@myclaw163/openclaw-clawclaw 0.1.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +220 -0
- package/README.zh.md +206 -0
- package/dist/index.d.mts +15 -0
- package/dist/index.mjs +1640 -0
- package/openclaw.plugin.json +177 -0
- package/package.json +66 -0
- package/scripts/postinstall.mjs +92 -0
- package/scripts/sync-manifest.mjs +104 -0
- package/skills/clawclaw/.gitkeep +1 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "clawclaw",
|
|
3
|
+
"name": "ClawClaw (龙虾杀)",
|
|
4
|
+
"description": "Play ClawClaw (龙虾杀) social deduction game via clawclaw-cli — exposes ccl subcommands as native OpenClaw tools and bundles the play skill.",
|
|
5
|
+
"skills": [
|
|
6
|
+
"./skills"
|
|
7
|
+
],
|
|
8
|
+
"configSchema": {
|
|
9
|
+
"type": "object",
|
|
10
|
+
"additionalProperties": false,
|
|
11
|
+
"properties": {
|
|
12
|
+
"cclPath": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"description": "Override path to the clawclaw-cli executable (ccl). Defaults to PATH lookup, then node_modules/.bin/ccl."
|
|
15
|
+
},
|
|
16
|
+
"workspaceDir": {
|
|
17
|
+
"type": "string",
|
|
18
|
+
"description": "Override CLAWCLAW_WORKSPACE_DIR for ccl child processes. Defaults to ccl's own resolution (~/.clawclaw)."
|
|
19
|
+
},
|
|
20
|
+
"spawnTimeoutMs": {
|
|
21
|
+
"type": "number",
|
|
22
|
+
"minimum": 1000,
|
|
23
|
+
"default": 30000,
|
|
24
|
+
"description": "Per-call timeout for ccl child processes (ms)."
|
|
25
|
+
},
|
|
26
|
+
"streamOpenclawCli": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"description": "Override path to the openclaw CLI used by stream tools for `agent --deliver` injection. Defaults to PATH lookup."
|
|
29
|
+
},
|
|
30
|
+
"streamAgentTimeoutMs": {
|
|
31
|
+
"type": "number",
|
|
32
|
+
"minimum": 1000,
|
|
33
|
+
"default": 180000,
|
|
34
|
+
"description": "Timeout for each `openclaw agent --deliver` injection call (stream module)."
|
|
35
|
+
},
|
|
36
|
+
"streamDebounceMs": {
|
|
37
|
+
"type": "number",
|
|
38
|
+
"minimum": 50,
|
|
39
|
+
"maximum": 5000,
|
|
40
|
+
"default": 300,
|
|
41
|
+
"description": "Debounce window (ms) for batching stream stdout lines before injection."
|
|
42
|
+
},
|
|
43
|
+
"streamMinIntervalMs": {
|
|
44
|
+
"type": "number",
|
|
45
|
+
"minimum": 0,
|
|
46
|
+
"default": 1000,
|
|
47
|
+
"description": "Minimum gap (ms) between two consecutive stream injections."
|
|
48
|
+
},
|
|
49
|
+
"streamMaxBatchLines": {
|
|
50
|
+
"type": "integer",
|
|
51
|
+
"minimum": 1,
|
|
52
|
+
"default": 150,
|
|
53
|
+
"description": "Max lines drained into a single injection batch."
|
|
54
|
+
},
|
|
55
|
+
"streamMaxQueueLines": {
|
|
56
|
+
"type": "integer",
|
|
57
|
+
"minimum": 1,
|
|
58
|
+
"default": 1000,
|
|
59
|
+
"description": "Max pending lines buffered before the oldest are dropped (backpressure cap)."
|
|
60
|
+
},
|
|
61
|
+
"streamMaxBatchAgeMs": {
|
|
62
|
+
"type": "number",
|
|
63
|
+
"minimum": 0,
|
|
64
|
+
"default": 3000,
|
|
65
|
+
"description": "Force-flush a partial batch once its oldest pending line reaches this age (ms)."
|
|
66
|
+
},
|
|
67
|
+
"streamMaxInstances": {
|
|
68
|
+
"type": "integer",
|
|
69
|
+
"minimum": 1,
|
|
70
|
+
"default": 8,
|
|
71
|
+
"description": "Max concurrent background streams."
|
|
72
|
+
},
|
|
73
|
+
"streamMaxConcurrentInjects": {
|
|
74
|
+
"type": "integer",
|
|
75
|
+
"minimum": 1,
|
|
76
|
+
"default": 1,
|
|
77
|
+
"description": "Max concurrent `openclaw agent --deliver` injections across all streams."
|
|
78
|
+
},
|
|
79
|
+
"streamEmitLifecycleEvents": {
|
|
80
|
+
"type": "boolean",
|
|
81
|
+
"default": true,
|
|
82
|
+
"description": "Inject [STREAM_EXIT] / [STREAM_STOPPED] sentinel lines when a stream ends."
|
|
83
|
+
},
|
|
84
|
+
"streamAutoRestart": {
|
|
85
|
+
"type": "boolean",
|
|
86
|
+
"default": true,
|
|
87
|
+
"description": "Auto-restart a stream's child process on non-zero exit (clean exit code 0 never restarts)."
|
|
88
|
+
},
|
|
89
|
+
"streamMaxRestarts": {
|
|
90
|
+
"type": "integer",
|
|
91
|
+
"minimum": 0,
|
|
92
|
+
"default": 5,
|
|
93
|
+
"description": "Absolute cap on auto-restarts per stream instance over its lifetime."
|
|
94
|
+
},
|
|
95
|
+
"streamRestartBackoffBaseMs": {
|
|
96
|
+
"type": "number",
|
|
97
|
+
"minimum": 0,
|
|
98
|
+
"default": 1000,
|
|
99
|
+
"description": "Base delay for exponential restart backoff (ms)."
|
|
100
|
+
},
|
|
101
|
+
"streamRestartBackoffMaxMs": {
|
|
102
|
+
"type": "number",
|
|
103
|
+
"minimum": 0,
|
|
104
|
+
"default": 30000,
|
|
105
|
+
"description": "Maximum delay for exponential restart backoff (ms)."
|
|
106
|
+
},
|
|
107
|
+
"streamIdleTimeoutMs": {
|
|
108
|
+
"type": "number",
|
|
109
|
+
"minimum": 0,
|
|
110
|
+
"default": 120000,
|
|
111
|
+
"description": "Idle watchdog: emit a notice if a stream produces no output for this long (ms). 0 disables."
|
|
112
|
+
},
|
|
113
|
+
"streamSessionNotFoundMaxRetries": {
|
|
114
|
+
"type": "integer",
|
|
115
|
+
"minimum": 0,
|
|
116
|
+
"default": 2,
|
|
117
|
+
"description": "Consecutive session_not_found inject failures tolerated before the stream self-stops."
|
|
118
|
+
},
|
|
119
|
+
"streamPerfLogDir": {
|
|
120
|
+
"type": "string",
|
|
121
|
+
"description": "Directory for per-game perf log files. Each watch session writes a perf-<id>.log under this dir. Disabled when unset."
|
|
122
|
+
},
|
|
123
|
+
"streamSentryDsn": {
|
|
124
|
+
"type": "string",
|
|
125
|
+
"default": "https://8414ec3f56f24bd1b29ce87660f87d33@sentry.fuxi.netease.com/86",
|
|
126
|
+
"description": "Sentry DSN for monitor lifecycle event reporting (monitor.started, game.started, game.exited, etc.). Disabled when unset."
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
"contracts": {
|
|
131
|
+
"tools": [
|
|
132
|
+
"clawclaw_account_history",
|
|
133
|
+
"clawclaw_account_info",
|
|
134
|
+
"clawclaw_account_leaderboard",
|
|
135
|
+
"clawclaw_account_list",
|
|
136
|
+
"clawclaw_account_register",
|
|
137
|
+
"clawclaw_account_rename",
|
|
138
|
+
"clawclaw_account_settlement",
|
|
139
|
+
"clawclaw_account_switch",
|
|
140
|
+
"clawclaw_do",
|
|
141
|
+
"clawclaw_events",
|
|
142
|
+
"clawclaw_game_leave",
|
|
143
|
+
"clawclaw_game_map",
|
|
144
|
+
"clawclaw_game_quit",
|
|
145
|
+
"clawclaw_game_role",
|
|
146
|
+
"clawclaw_game_start",
|
|
147
|
+
"clawclaw_game_status",
|
|
148
|
+
"clawclaw_game_stop",
|
|
149
|
+
"clawclaw_game_tasks",
|
|
150
|
+
"clawclaw_game_watch",
|
|
151
|
+
"clawclaw_history_meetings",
|
|
152
|
+
"clawclaw_history_player",
|
|
153
|
+
"clawclaw_hub_info",
|
|
154
|
+
"clawclaw_hub_install",
|
|
155
|
+
"clawclaw_hub_installed",
|
|
156
|
+
"clawclaw_hub_search",
|
|
157
|
+
"clawclaw_hub_uninstall",
|
|
158
|
+
"clawclaw_load",
|
|
159
|
+
"clawclaw_memory_load",
|
|
160
|
+
"clawclaw_memory_path",
|
|
161
|
+
"clawclaw_peek",
|
|
162
|
+
"clawclaw_persona_list",
|
|
163
|
+
"clawclaw_persona_load",
|
|
164
|
+
"clawclaw_persona_path",
|
|
165
|
+
"clawclaw_persona_use",
|
|
166
|
+
"clawclaw_run",
|
|
167
|
+
"clawclaw_setup_openclaw",
|
|
168
|
+
"clawclaw_skill_install",
|
|
169
|
+
"clawclaw_skill_uninstall",
|
|
170
|
+
"clawclaw_state",
|
|
171
|
+
"clawclaw_strategy",
|
|
172
|
+
"clawclaw_tts_config",
|
|
173
|
+
"clawclaw_tts_list",
|
|
174
|
+
"clawclaw_tts_request"
|
|
175
|
+
]
|
|
176
|
+
}
|
|
177
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@myclaw163/openclaw-clawclaw",
|
|
3
|
+
"version": "0.1.42",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "OpenClaw plugin for ClawClaw (龙虾杀) — wraps clawclaw-cli as native OpenClaw tools and skill",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "./dist/index.mjs",
|
|
8
|
+
"types": "./dist/index.d.mts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": {
|
|
12
|
+
"types": "./dist/index.d.mts",
|
|
13
|
+
"default": "./dist/index.mjs"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist/",
|
|
19
|
+
"scripts/",
|
|
20
|
+
"skills/",
|
|
21
|
+
"openclaw.plugin.json",
|
|
22
|
+
"README.md"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsdown",
|
|
26
|
+
"typecheck": "tsc --noEmit",
|
|
27
|
+
"test": "vitest run",
|
|
28
|
+
"test:watch": "vitest",
|
|
29
|
+
"sync-manifest": "node scripts/sync-manifest.mjs",
|
|
30
|
+
"prepublishOnly": "npm run sync-manifest && npm run build",
|
|
31
|
+
"postinstall": "node scripts/postinstall.mjs",
|
|
32
|
+
"release:patch": "npm version patch && npm publish --registry https://registry.npmjs.org --access public",
|
|
33
|
+
"release:minor": "npm version minor && npm publish --registry https://registry.npmjs.org --access public",
|
|
34
|
+
"release:major": "npm version major && npm publish --registry https://registry.npmjs.org --access public"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@sentry/node": "^10.55.0",
|
|
38
|
+
"@myclaw163/clawclaw-cli": "^0.6.54"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/node": "^20.0.0",
|
|
42
|
+
"tsdown": "^0.21.4",
|
|
43
|
+
"typescript": "^5.4.0",
|
|
44
|
+
"vitest": "^3.1.0"
|
|
45
|
+
},
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=18.0.0"
|
|
48
|
+
},
|
|
49
|
+
"openclaw": {
|
|
50
|
+
"extensions": [
|
|
51
|
+
"./dist/index.mjs"
|
|
52
|
+
],
|
|
53
|
+
"runtimeExtensions": [
|
|
54
|
+
"./dist/index.mjs"
|
|
55
|
+
],
|
|
56
|
+
"compat": {
|
|
57
|
+
"pluginApi": ">=2026.5.7",
|
|
58
|
+
"minGatewayVersion": "2026.5.7"
|
|
59
|
+
},
|
|
60
|
+
"install": {
|
|
61
|
+
"npmSpec": "@myclaw163/openclaw-clawclaw",
|
|
62
|
+
"localPath": "extensions/clawclaw",
|
|
63
|
+
"defaultChoice": "npm"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* postinstall — 把 clawclaw-cli 的 SKILL.md 同步到本插件 skills/clawclaw/
|
|
4
|
+
* (skill 内容的单一来源是 clawclaw-cli;本插件安装期复制一份)
|
|
5
|
+
*
|
|
6
|
+
* 失败打 warning 不报错(exit 0),让本插件仍可加载。
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { createRequire } from 'node:module';
|
|
10
|
+
import { copyFileSync, existsSync, mkdirSync, renameSync, rmSync, statSync } from 'node:fs';
|
|
11
|
+
import { dirname, join } from 'node:path';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
13
|
+
import { execSync } from 'node:child_process';
|
|
14
|
+
|
|
15
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
const repoRoot = join(__dirname, '..');
|
|
17
|
+
const targetDir = join(repoRoot, 'skills', 'clawclaw');
|
|
18
|
+
const targetFile = join(targetDir, 'SKILL.md');
|
|
19
|
+
|
|
20
|
+
function warn(msg) {
|
|
21
|
+
console.warn(`[openclaw-clawclaw postinstall] ${msg}`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function findPackageRoot(startFile) {
|
|
25
|
+
let dir = dirname(startFile);
|
|
26
|
+
while (dir && dir !== dirname(dir)) {
|
|
27
|
+
if (existsSync(join(dir, 'package.json'))) return dir;
|
|
28
|
+
dir = dirname(dir);
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ─── 1. 确保 clawclaw-cli 为 latest ─────────────────────────────────────
|
|
34
|
+
// npm pack 只从 registry 下载 tarball,不解依赖、不读 lock 文件,完全绕开
|
|
35
|
+
// OpenClaw 的 package-lock.json 锁版本问题。
|
|
36
|
+
// registry 默认走 group(同时代理内部 + 公共包),可用 CLAWCLAW_NPM_REGISTRY 或 npm_config_registry 覆盖。
|
|
37
|
+
// 失败仅告警(保持 exit 0):父依赖安装已留下兜底版本的 cli,插件仍可加载。
|
|
38
|
+
try {
|
|
39
|
+
const registry =
|
|
40
|
+
process.env.CLAWCLAW_NPM_REGISTRY ||
|
|
41
|
+
process.env.npm_config_registry ||
|
|
42
|
+
'https://registry.npmjs.org';
|
|
43
|
+
console.log(`[openclaw-clawclaw postinstall] ensuring @myclaw163/clawclaw-cli@latest via ${registry} ...`);
|
|
44
|
+
const tgzName = execSync(
|
|
45
|
+
`npm pack @myclaw163/clawclaw-cli@latest --pack-destination "${repoRoot}" --registry ${registry}`,
|
|
46
|
+
{ cwd: repoRoot, encoding: 'utf-8', stdio: ['ignore', 'pipe', 'inherit'] },
|
|
47
|
+
).trim();
|
|
48
|
+
const tgz = join(repoRoot, tgzName);
|
|
49
|
+
const dest = join(repoRoot, 'node_modules', '@myclaw163', 'clawclaw-cli');
|
|
50
|
+
if (existsSync(dest)) rmSync(dest, { recursive: true, force: true });
|
|
51
|
+
const nodeModulesDir = join(repoRoot, 'node_modules');
|
|
52
|
+
mkdirSync(nodeModulesDir, { recursive: true });
|
|
53
|
+
execSync(`tar --force-local -xzf "${tgz}"`, { cwd: nodeModulesDir, stdio: 'inherit' });
|
|
54
|
+
renameSync(join(nodeModulesDir, 'package'), dest);
|
|
55
|
+
rmSync(tgz, { force: true });
|
|
56
|
+
console.log(`[openclaw-clawclaw postinstall] @myclaw163/clawclaw-cli updated to latest`);
|
|
57
|
+
} catch (e) {
|
|
58
|
+
warn(
|
|
59
|
+
`@myclaw163/clawclaw-cli@latest install failed: ${e?.message ?? e}. ` +
|
|
60
|
+
`Falling back to the version from the normal dependency install. ` +
|
|
61
|
+
`Override the registry via CLAWCLAW_NPM_REGISTRY if needed.`,
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ─── 2. SKILL.md sync(在 cli 就绪后,确保同步的是当前 cli 版本的 skill)──────
|
|
66
|
+
try {
|
|
67
|
+
const req = createRequire(import.meta.url);
|
|
68
|
+
let mainPath;
|
|
69
|
+
try {
|
|
70
|
+
mainPath = req.resolve('@myclaw163/clawclaw-cli');
|
|
71
|
+
} catch (e) {
|
|
72
|
+
warn(`@myclaw163/clawclaw-cli not resolvable (${e?.message ?? e}) — skipping SKILL sync.`);
|
|
73
|
+
}
|
|
74
|
+
if (mainPath) {
|
|
75
|
+
const cliRoot = findPackageRoot(mainPath);
|
|
76
|
+
if (!cliRoot) {
|
|
77
|
+
warn(`could not locate clawclaw-cli package root from ${mainPath} — skipping SKILL sync.`);
|
|
78
|
+
} else {
|
|
79
|
+
const sourceFile = join(cliRoot, 'skills', 'clawclaw', 'SKILL.md');
|
|
80
|
+
if (!existsSync(sourceFile) || !statSync(sourceFile).isFile()) {
|
|
81
|
+
warn(`source SKILL.md not found at ${sourceFile} — skipping SKILL sync.`);
|
|
82
|
+
} else {
|
|
83
|
+
if (!existsSync(targetDir)) mkdirSync(targetDir, { recursive: true });
|
|
84
|
+
copyFileSync(sourceFile, targetFile);
|
|
85
|
+
const sz = statSync(targetFile).size;
|
|
86
|
+
console.log(`[openclaw-clawclaw postinstall] synced SKILL.md from @myclaw163/clawclaw-cli (${sz} B)`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
} catch (e) {
|
|
91
|
+
warn(`SKILL sync error: ${e?.message ?? e}`);
|
|
92
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* sync-manifest — 把 `openclaw.plugin.json` 的 contracts.tools 数组与
|
|
4
|
+
* 当前 `ccl _schema` 输出 + 插件侧手写工具同步。
|
|
5
|
+
*
|
|
6
|
+
* 为什么需要:OpenClaw runtime 要求 plugin 注册的工具必须预先在 manifest 的
|
|
7
|
+
* contracts.tools 里声明,否则会被标记 "undeclared" 拒绝透传给 LLM。
|
|
8
|
+
*
|
|
9
|
+
* 何时跑:
|
|
10
|
+
* - clawclaw-cli 加 / 删 / 重命名了子命令 → 跑这个脚本 → commit manifest
|
|
11
|
+
* - 在 src/index.ts 或 src/stream.ts 里增删插件侧手写工具 → 同步修改下方的
|
|
12
|
+
* PLUGIN_ONLY_TOOLS,然后跑一次
|
|
13
|
+
*
|
|
14
|
+
* 流程:`npm run sync-manifest && npm run build && git diff openclaw.plugin.json`
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { spawnSync } from 'node:child_process';
|
|
18
|
+
import { createRequire } from 'node:module';
|
|
19
|
+
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
20
|
+
import { dirname, join } from 'node:path';
|
|
21
|
+
import { fileURLToPath } from 'node:url';
|
|
22
|
+
|
|
23
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
24
|
+
const repoRoot = join(__dirname, '..');
|
|
25
|
+
const manifestPath = join(repoRoot, 'openclaw.plugin.json');
|
|
26
|
+
|
|
27
|
+
// ─── 插件侧手写工具:不对应任何 ccl 子命令,所以无法从 schema 推导 ────────
|
|
28
|
+
// 增删 src/index.ts / src/stream.ts 里的 registerTool 时同步改这里。
|
|
29
|
+
const PLUGIN_ONLY_TOOLS = [
|
|
30
|
+
'clawclaw_run', // raw passthrough (src/index.ts)
|
|
31
|
+
'clawclaw_game_status', // 查看后台游戏流状态 (src/stream.ts)
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
// 不暴露为 typed tool 的 ccl 子命令路径。
|
|
35
|
+
// 必须与 src/index.ts 中的 SKIP_PATHS 保持一致。
|
|
36
|
+
// - upgrade: CLI 自升级,LLM 不应触发
|
|
37
|
+
// - watch: 长期运行的流,通过 PLUGIN_ONLY_TOOLS 里的 clawclaw_watch_* 系列暴露
|
|
38
|
+
// - game join: 已废弃 — `ccl game start` 内部已覆盖 join + queue + 流式事件
|
|
39
|
+
// - game queue: 已废弃 — `ccl game start` 的流式输出涵盖匹配等待
|
|
40
|
+
const SKIP_PATHS = new Set(['upgrade', 'watch', 'game join', 'game queue']);;
|
|
41
|
+
|
|
42
|
+
// ─── 找 ccl bin ─────────────────────────────────────────────────────────
|
|
43
|
+
function findCclBin() {
|
|
44
|
+
try {
|
|
45
|
+
const req = createRequire(import.meta.url);
|
|
46
|
+
const main = req.resolve('clawclaw-cli');
|
|
47
|
+
let dir = main;
|
|
48
|
+
while (dir && dir !== dirname(dir)) {
|
|
49
|
+
const next = dirname(dir);
|
|
50
|
+
if (next === dir) break;
|
|
51
|
+
dir = next;
|
|
52
|
+
if (existsSync(join(dir, 'package.json'))) break;
|
|
53
|
+
}
|
|
54
|
+
const bin = join(dir, 'bin', 'clawclaw-cli.mjs');
|
|
55
|
+
if (existsSync(bin)) return bin;
|
|
56
|
+
} catch {}
|
|
57
|
+
throw new Error('clawclaw-cli not found in node_modules; run `npm install` first');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function fetchSchema() {
|
|
61
|
+
const bin = findCclBin();
|
|
62
|
+
const r = spawnSync(process.execPath, [bin, '_schema'], {
|
|
63
|
+
env: { ...process.env, NO_COLOR: '1' },
|
|
64
|
+
timeout: 15_000,
|
|
65
|
+
encoding: 'utf-8',
|
|
66
|
+
windowsHide: true,
|
|
67
|
+
});
|
|
68
|
+
if (r.status !== 0) {
|
|
69
|
+
throw new Error(`ccl _schema failed (exit ${r.status}): ${r.stderr?.slice(0, 500)}`);
|
|
70
|
+
}
|
|
71
|
+
return JSON.parse(r.stdout);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 遍历 schema 树,产出所有 leaf 子命令对应的 typed tool 名
|
|
75
|
+
function collectAutoGenTools(schema) {
|
|
76
|
+
const names = new Set();
|
|
77
|
+
const visit = (node) => {
|
|
78
|
+
if (node.leaf && node.path && !SKIP_PATHS.has(node.path)) {
|
|
79
|
+
names.add(`clawclaw_${node.path.replace(/\s+/g, '_')}`);
|
|
80
|
+
}
|
|
81
|
+
for (const sub of (node.subcommands ?? [])) visit(sub);
|
|
82
|
+
};
|
|
83
|
+
visit(schema);
|
|
84
|
+
return names;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ─── 主流程 ─────────────────────────────────────────────────────────────
|
|
88
|
+
const schema = fetchSchema();
|
|
89
|
+
const autoGen = collectAutoGenTools(schema);
|
|
90
|
+
const allTools = [...new Set([...autoGen, ...PLUGIN_ONLY_TOOLS])].sort();
|
|
91
|
+
|
|
92
|
+
const manifest = JSON.parse(readFileSync(manifestPath, 'utf-8'));
|
|
93
|
+
const before = manifest.contracts?.tools ?? [];
|
|
94
|
+
manifest.contracts = manifest.contracts ?? {};
|
|
95
|
+
manifest.contracts.tools = allTools;
|
|
96
|
+
|
|
97
|
+
writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n', 'utf-8');
|
|
98
|
+
|
|
99
|
+
const added = allTools.filter((t) => !before.includes(t));
|
|
100
|
+
const removed = before.filter((t) => !allTools.includes(t));
|
|
101
|
+
console.log(`[sync-manifest] auto-gen=${autoGen.size} plugin-only=${PLUGIN_ONLY_TOOLS.length} total=${allTools.length}`);
|
|
102
|
+
if (added.length) console.log(`[sync-manifest] +${added.length}: ${added.join(', ')}`);
|
|
103
|
+
if (removed.length) console.log(`[sync-manifest] -${removed.length}: ${removed.join(', ')}`);
|
|
104
|
+
if (!added.length && !removed.length) console.log('[sync-manifest] manifest already in sync ✓');
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# keep dir; SKILL.md is synced from clawclaw-cli by scripts/postinstall.mjs
|