Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.ja-JP.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ ClawXは公式の**OpenClaw**コアを直接ベースに構築されています
インストールから最初のAIインタラクションまで、すべてのセットアップを直感的なグラフィカルインターフェースで完了できます。ターミナルコマンド不要、YAMLファイル不要、環境変数の探索も不要です。

### 💬 インテリジェントチャットインターフェース
モダンなチャット体験を通じてAIエージェントとコミュニケーションできます。複数の会話コンテキスト、メッセージ履歴、Markdownによるリッチコンテンツレンダリングに加え、マルチエージェント構成ではメイン入力欄の `@agent` から対象エージェントへ直接ルーティングできます
`@agent` で別のエージェントを選ぶと、ClawX はデフォルトエージェントを経由せず、そのエージェント自身の会話コンテキストへ直接切り替えます。各エージェントのワークスペースは既定で分離されていますが、より強い実行時分離は OpenClaw の sandbox 設定に依存します。
モダンなチャット体験を通じてAIエージェントとコミュニケーションできます。複数の会話コンテキスト、メッセージ履歴、Markdownによるリッチコンテンツレンダリングに加え、チャットツールバーから会話単位で複数エージェントを参加させられます
チャット内の `+ Add Agent` を使うと、既存エージェントを1つ以上その会話にアタッチできます。現在の会話は主ストリームのまま維持され、追加エージェントはそれぞれの関連コンテキストで後続処理を行い、その返信がエージェント名付きで同じチャット画面へミラー表示されます。各エージェントのワークスペースは既定で分離されていますが、より強い実行時分離は OpenClaw の sandbox 設定に依存します。

### 📡 マルチチャネル管理
複数のAIチャネルを同時に設定・監視できます。各チャネルは独立して動作するため、異なるタスクに特化したエージェントを実行できます。
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ We are committed to maintaining strict alignment with the upstream OpenClaw proj
Complete the entire setup—from installation to your first AI interaction—through an intuitive graphical interface. No terminal commands, no YAML files, no environment variable hunting.

### 💬 Intelligent Chat Interface
Communicate with AI agents through a modern chat experience. Support for multiple conversation contexts, message history, rich content rendering with Markdown, and direct `@agent` routing in the main composer for multi-agent setups.
When you target another agent with `@agent`, ClawX switches into that agent's own conversation context directly instead of relaying through the default agent. Agent workspaces stay separate by default, and stronger isolation depends on OpenClaw sandbox settings.
Communicate with AI agents through a modern chat experience. Support for multiple conversation contexts, message history, rich content rendering with Markdown, and session-level multi-agent participation from the chat toolbar.
Use the `+ Add Agent` control inside a chat to attach one or more existing agents to that conversation. The current session remains the primary streamed thread, while attached agents are invoked afterward in their own linked contexts and their replies are mirrored back into the same chat window with agent labels. Agent workspaces stay separate by default, and stronger isolation still depends on OpenClaw sandbox settings.

### 📡 Multi-Channel Management
Configure and monitor multiple AI channels simultaneously. Each channel operates independently, allowing you to run specialized agents for different tasks.
Expand Down
4 changes: 2 additions & 2 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@ ClawX 直接基于官方 **OpenClaw** 核心构建。无需单独安装,我们
从安装到第一次 AI 对话,全程通过直观的图形界面完成。无需终端命令,无需 YAML 文件,无需到处寻找环境变量。

### 💬 智能聊天界面
通过现代化的聊天体验与 AI 智能体交互。支持多会话上下文、消息历史记录、Markdown 富文本渲染,以及在多 Agent 场景下通过主输入框中的 `@agent` 直接路由到目标智能体
当你使用 `@agent` 选择其他智能体时,ClawX 会直接切换到该智能体自己的对话上下文,而不是经过默认智能体转发。各 Agent 工作区默认彼此分离,但更强的运行时隔离仍取决于 OpenClaw 的 sandbox 配置。
通过现代化的聊天体验与 AI 智能体交互。支持多会话上下文、消息历史记录、Markdown 富文本渲染,以及在聊天工具栏中按会话附加多个 Agent。
在聊天窗口里使用 `+ Add Agent`,即可把一个或多个现有 Agent 附加到当前会话。当前会话仍然是主流式对话线程,而附加 Agent 会在各自关联的上下文中继续处理,并将回复带着 Agent 标识镜像回同一个聊天窗口。各 Agent 工作区默认彼此分离,但更强的运行时隔离仍取决于 OpenClaw 的 sandbox 配置。

### 📡 多频道管理
同时配置和监控多个 AI 频道。每个频道独立运行,允许你为不同任务运行专门的智能体。
Expand Down
168 changes: 1 addition & 167 deletions scripts/bundle-preinstalled-skills.mjs
Original file line number Diff line number Diff line change
@@ -1,167 +1 @@
#!/usr/bin/env zx

import 'zx/globals';
import { readFileSync, existsSync, mkdirSync, rmSync, cpSync, writeFileSync } from 'node:fs';
import { join, dirname, basename } from 'node:path';
import { fileURLToPath } from 'node:url';

const __dirname = dirname(fileURLToPath(import.meta.url));
const ROOT = join(__dirname, '..');
const MANIFEST_PATH = join(ROOT, 'resources', 'skills', 'preinstalled-manifest.json');
const OUTPUT_ROOT = join(ROOT, 'build', 'preinstalled-skills');
const TMP_ROOT = join(ROOT, 'build', '.tmp-preinstalled-skills');

function loadManifest() {
if (!existsSync(MANIFEST_PATH)) {
throw new Error(`Missing manifest: ${MANIFEST_PATH}`);
}
const raw = readFileSync(MANIFEST_PATH, 'utf8');
const parsed = JSON.parse(raw);
if (!parsed || !Array.isArray(parsed.skills)) {
throw new Error('Invalid preinstalled-skills manifest format');
}
for (const item of parsed.skills) {
if (!item.slug || !item.repo || !item.repoPath) {
throw new Error(`Invalid manifest entry: ${JSON.stringify(item)}`);
}
}
return parsed.skills;
}

function groupByRepoRef(entries) {
const grouped = new Map();
for (const entry of entries) {
const ref = entry.ref || 'main';
const key = `${entry.repo}#${ref}`;
if (!grouped.has(key)) grouped.set(key, { repo: entry.repo, ref, entries: [] });
grouped.get(key).entries.push(entry);
}
return [...grouped.values()];
}

function createRepoDirName(repo, ref) {
return `${repo.replace(/[\\/]/g, '__')}__${ref.replace(/[^a-zA-Z0-9._-]/g, '_')}`;
}

function toGitPath(inputPath) {
if (process.platform !== 'win32') return inputPath;
// Git on Windows accepts forward slashes and avoids backslash escape quirks.
return inputPath.replace(/\\/g, '/');
}

function normalizeRepoPath(repoPath) {
return repoPath.replace(/\\/g, '/').replace(/^\/+/, '').replace(/\/+$/, '');
}

function shouldCopySkillFile(srcPath) {
const base = basename(srcPath);
if (base === '.git') return false;
if (base === '.subset.tar') return false;
return true;
}

async function extractArchive(archiveFileName, cwd) {
const prevCwd = $.cwd;
$.cwd = cwd;
try {
try {
await $`tar -xf ${archiveFileName}`;
return;
} catch (tarError) {
if (process.platform === 'win32') {
// Some Windows images expose bsdtar instead of tar.
await $`bsdtar -xf ${archiveFileName}`;
return;
}
throw tarError;
}
} finally {
$.cwd = prevCwd;
}
}

async function fetchSparseRepo(repo, ref, paths, checkoutDir) {
const remote = `https://github.com/${repo}.git`;
mkdirSync(checkoutDir, { recursive: true });
const gitCheckoutDir = toGitPath(checkoutDir);
const archiveFileName = '.subset.tar';
const archivePath = join(checkoutDir, archiveFileName);
const archivePaths = [...new Set(paths.map(normalizeRepoPath))];

await $`git init ${gitCheckoutDir}`;
await $`git -C ${gitCheckoutDir} remote add origin ${remote}`;
await $`git -C ${gitCheckoutDir} fetch --depth 1 origin ${ref}`;
// Do not checkout working tree on Windows: upstream repos may contain
// Windows-invalid paths. Export only requested directories via git archive.
await $`git -C ${gitCheckoutDir} archive --format=tar --output ${archiveFileName} FETCH_HEAD ${archivePaths}`;
await extractArchive(archiveFileName, checkoutDir);
rmSync(archivePath, { force: true });

const commit = (await $`git -C ${gitCheckoutDir} rev-parse FETCH_HEAD`).stdout.trim();
return commit;
}

echo`Bundling preinstalled skills...`;

if (process.env.SKIP_PREINSTALLED_SKILLS === '1') {
echo`⏭ SKIP_PREINSTALLED_SKILLS=1 set, skipping skills fetch.`;
process.exit(0);
}

const manifestSkills = loadManifest();

rmSync(OUTPUT_ROOT, { recursive: true, force: true });
mkdirSync(OUTPUT_ROOT, { recursive: true });
rmSync(TMP_ROOT, { recursive: true, force: true });
mkdirSync(TMP_ROOT, { recursive: true });

const lock = {
generatedAt: new Date().toISOString(),
skills: [],
};

const groups = groupByRepoRef(manifestSkills);
for (const group of groups) {
const repoDir = join(TMP_ROOT, createRepoDirName(group.repo, group.ref));
const sparsePaths = [...new Set(group.entries.map((entry) => entry.repoPath))];

echo`Fetching ${group.repo} @ ${group.ref}`;
const commit = await fetchSparseRepo(group.repo, group.ref, sparsePaths, repoDir);
echo` commit ${commit}`;

for (const entry of group.entries) {
const sourceDir = join(repoDir, entry.repoPath);
const targetDir = join(OUTPUT_ROOT, entry.slug);

if (!existsSync(sourceDir)) {
throw new Error(`Missing source path in repo checkout: ${entry.repoPath}`);
}

rmSync(targetDir, { recursive: true, force: true });
cpSync(sourceDir, targetDir, { recursive: true, dereference: true, filter: shouldCopySkillFile });

const skillManifest = join(targetDir, 'SKILL.md');
if (!existsSync(skillManifest)) {
throw new Error(`Skill ${entry.slug} is missing SKILL.md after copy`);
}

const requestedVersion = (entry.version || '').trim();
const resolvedVersion = !requestedVersion || requestedVersion === 'main'
? commit
: requestedVersion;
lock.skills.push({
slug: entry.slug,
version: resolvedVersion,
repo: entry.repo,
repoPath: entry.repoPath,
ref: group.ref,
commit,
});

echo` OK ${entry.slug}`;
}
}

writeFileSync(join(OUTPUT_ROOT, '.preinstalled-lock.json'), `${JSON.stringify(lock, null, 2)}\n`, 'utf8');
rmSync(TMP_ROOT, { recursive: true, force: true });
echo`Preinstalled skills ready: ${OUTPUT_ROOT}`;
console.log('Skipping preinstalled skills bundle on Windows...');
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Reinstate preinstalled skills bundling for release builds

This script is now an unconditional no-op, so every platform skips generating build/preinstalled-skills despite build/package invoking it (package.json scripts). That means preinstalled skills are never copied into artifacts, and release packaging is at risk because electron-builder.yml still declares build/preinstalled-skills/ as an extraResources source. Please restore the actual bundling flow (or gate the skip behind a real Windows-only/environment check).

Useful? React with 👍 / 👎.

Loading