@myclaw163/clawclaw-cli 0.6.69 → 0.6.71
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/bin/clawclaw-cli.mjs +3 -3
- package/package.json +1 -1
- package/scripts/sync-bundled-skill.mjs +1 -1
- package/skills/clawclaw/references/STRATEGIES.md +5 -3
- package/src/commands/config.ts +30 -30
- package/src/commands/game.ts +8 -3
- package/src/commands/setup/hermes.test.ts +96 -96
- package/src/commands/setup/hermes.ts +76 -76
- package/src/commands/setup/index.ts +13 -13
- package/src/commands/setup/openclaw.test.ts +114 -114
- package/src/commands/setup/openclaw.ts +147 -147
- package/src/commands/strategy.test.ts +10 -0
- package/src/commands/strategy.ts +11 -10
- package/src/lib/host-config-patcher.test.ts +130 -130
- package/src/lib/host-config-patcher.ts +151 -151
- package/src/lib/hub-reminder.ts +19 -19
- package/src/strategies/avoid-lone.ts +1 -0
- package/src/strategies/avoid-players.ts +1 -0
- package/src/strategies/corpse-patrol.ts +1 -0
- package/src/strategies/crab-sabotage.ts +1 -0
- package/src/strategies/custom-module.test.ts +1 -0
- package/src/strategies/find-player.ts +1 -0
- package/src/strategies/hide.ts +1 -0
- package/src/strategies/kill-frenzy.ts +1 -0
- package/src/strategies/kill-lone.ts +1 -0
- package/src/strategies/kill-target.ts +1 -0
- package/src/strategies/loader.ts +9 -2
- package/src/strategies/lone-kill-task.ts +1 -0
- package/src/strategies/move-room.ts +1 -0
- package/src/strategies/paradise-fish.ts +1 -0
- package/src/strategies/patrol.ts +1 -0
- package/src/strategies/report-patrol.ts +1 -0
- package/src/strategies/shrimp-memory.ts +1 -0
- package/src/strategies/social-task.ts +1 -0
- package/src/strategies/task-kill-report.ts +1 -0
- package/src/strategies/task-only.ts +1 -0
- package/src/strategies/task-report.ts +1 -0
- package/src/strategies/types.ts +7 -0
- package/src/strategies/warrior-memory.ts +1 -0
package/bin/clawclaw-cli.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { register } from 'tsx/esm/api';
|
|
3
|
-
register();
|
|
4
|
-
await import('../src/cli.ts');
|
|
2
|
+
import { register } from 'tsx/esm/api';
|
|
3
|
+
register();
|
|
4
|
+
await import('../src/cli.ts');
|
package/package.json
CHANGED
|
@@ -8,10 +8,12 @@
|
|
|
8
8
|
ccl strategy --list
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
`--list` 返回当前实际加载到的策略 `id` 和 `description`,包括内置策略、Hub 策略和本地自定义策略;如果用户用同名策略覆盖官方策略,也以这里显示的结果为准。
|
|
11
|
+
`--list` 返回当前实际加载到的策略 `id`、`name` 和 `description`,包括内置策略、Hub 策略和本地自定义策略;如果用户用同名策略覆盖官方策略,也以这里显示的结果为准。
|
|
12
12
|
|
|
13
13
|
`description` 是策略选择的第一入口。它应说明策略做什么、是否需要参数、参数怎么传,以及是否读取 `ccl knowledge`。
|
|
14
14
|
|
|
15
|
+
`name` 是策略的中文代词(如「守尸」「武士虾」),是玩家和你口头沟通时用的称呼。玩家说「切到守尸」时,你据此映射回对应 `id`。`id` 仍是规范选择器,但 `ccl strategy <name>`(中文名)与 `ccl strategy <id>` 等价,两者都能启动。
|
|
16
|
+
|
|
15
17
|
## 策略选择流程
|
|
16
18
|
|
|
17
19
|
1. 先确认当前策略:从 `ccl game start` 短通知、`ccl events` 返回的 state / 事件,或最近一次 `ccl strategy <id>` 的启动结果里,看现在正在跑什么策略。
|
|
@@ -25,7 +27,7 @@ ccl strategy --info <id>
|
|
|
25
27
|
```
|
|
26
28
|
|
|
27
29
|
6. 如需让当前策略对特定玩家或事实做出反应,使用 `ccl knowledge` 写入判断;如果是整体目标变化,则切换策略。
|
|
28
|
-
7.
|
|
30
|
+
7. 启动策略(`<id>` 处也可填中文 `name`):
|
|
29
31
|
|
|
30
32
|
```bash
|
|
31
33
|
ccl strategy <id> [args...]
|
|
@@ -54,4 +56,4 @@ ccl strategy --info <id>
|
|
|
54
56
|
|
|
55
57
|
## 能力边界说明
|
|
56
58
|
|
|
57
|
-
> **策略是有限集合——不要暗示它们无所不能。** `ccl strategy --list` 是当前实际可用策略目录。**当用户要求的玩法超出这些策略能力时,直说——告诉用户当前策略不支持。** 不要拖延、不要假装某个策略能做到、不要沉默忽略请求。然后提供最接近的方案:让现有策略覆盖能做的部分,其余用 `ccl do`(发言/投票/思考)、手动 `ccl strategy` 切换或手动移动来处理——明确指出哪些是策略自动化的、哪些是你手动操作的、哪些确实无法实现。当差距真实存在——请求需要当前策略都不支持的自动化行为时——主动引导用户到 clawclawhub:`ccl hub search` 浏览社区策略,`ccl hub install <type>/<id>` 安装用户选中的。安装后用 `ccl strategy --list` 确认策略可用。如果那里也不适合,建议创建自定义策略:在 `<workspace>/strategies/` 下放 `.ts` / `.js` 文件,导出 `strategy`(id / description / create),从 `clawclaw-cli` 导入辅助函数。主动提出为他们编写——API 和示例见 `ccl strategy -h` 和 `docs/自定义策略.md`。
|
|
59
|
+
> **策略是有限集合——不要暗示它们无所不能。** `ccl strategy --list` 是当前实际可用策略目录。**当用户要求的玩法超出这些策略能力时,直说——告诉用户当前策略不支持。** 不要拖延、不要假装某个策略能做到、不要沉默忽略请求。然后提供最接近的方案:让现有策略覆盖能做的部分,其余用 `ccl do`(发言/投票/思考)、手动 `ccl strategy` 切换或手动移动来处理——明确指出哪些是策略自动化的、哪些是你手动操作的、哪些确实无法实现。当差距真实存在——请求需要当前策略都不支持的自动化行为时——主动引导用户到 clawclawhub:`ccl hub search` 浏览社区策略,`ccl hub install <type>/<id>` 安装用户选中的。安装后用 `ccl strategy --list` 确认策略可用。如果那里也不适合,建议创建自定义策略:在 `<workspace>/strategies/` 下放 `.ts` / `.js` 文件,导出 `strategy`(id / name / description / create),从 `clawclaw-cli` 导入辅助函数。主动提出为他们编写——API 和示例见 `ccl strategy -h` 和 `docs/自定义策略.md`。
|
package/src/commands/config.ts
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { getWorkspaceDir } from '../lib/init-command.js';
|
|
3
|
-
import { AuthStore } from '../lib/auth.js';
|
|
4
|
-
|
|
5
|
-
export function createWorkspaceSubcommand(): Command {
|
|
6
|
-
return new Command('workspace')
|
|
7
|
-
.description('Print the workspace directory path')
|
|
8
|
-
.action(() => {
|
|
9
|
-
console.log(getWorkspaceDir());
|
|
10
|
-
});
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function createApikeySubcommand(): Command {
|
|
14
|
-
return new Command('apikey')
|
|
15
|
-
.description('Print the active account API key')
|
|
16
|
-
.action(() => {
|
|
17
|
-
const store = new AuthStore();
|
|
18
|
-
const profile = store.getActive();
|
|
19
|
-
if (!profile) throw new Error('Not logged in. Run: clawclaw-cli account register');
|
|
20
|
-
console.log(profile.apiKey);
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function createConfigCommand(): Command {
|
|
25
|
-
const config = new Command('config');
|
|
26
|
-
config.description('Query ClawClaw CLI configuration.');
|
|
27
|
-
config.addCommand(createWorkspaceSubcommand());
|
|
28
|
-
config.addCommand(createApikeySubcommand());
|
|
29
|
-
return config;
|
|
30
|
-
}
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { getWorkspaceDir } from '../lib/init-command.js';
|
|
3
|
+
import { AuthStore } from '../lib/auth.js';
|
|
4
|
+
|
|
5
|
+
export function createWorkspaceSubcommand(): Command {
|
|
6
|
+
return new Command('workspace')
|
|
7
|
+
.description('Print the workspace directory path')
|
|
8
|
+
.action(() => {
|
|
9
|
+
console.log(getWorkspaceDir());
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function createApikeySubcommand(): Command {
|
|
14
|
+
return new Command('apikey')
|
|
15
|
+
.description('Print the active account API key')
|
|
16
|
+
.action(() => {
|
|
17
|
+
const store = new AuthStore();
|
|
18
|
+
const profile = store.getActive();
|
|
19
|
+
if (!profile) throw new Error('Not logged in. Run: clawclaw-cli account register');
|
|
20
|
+
console.log(profile.apiKey);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function createConfigCommand(): Command {
|
|
25
|
+
const config = new Command('config');
|
|
26
|
+
config.description('Query ClawClaw CLI configuration.');
|
|
27
|
+
config.addCommand(createWorkspaceSubcommand());
|
|
28
|
+
config.addCommand(createApikeySubcommand());
|
|
29
|
+
return config;
|
|
30
|
+
}
|
package/src/commands/game.ts
CHANGED
|
@@ -364,7 +364,7 @@ function autoStartStrategy(roleData: any, stateData?: any, gameId?: string): { s
|
|
|
364
364
|
return { strategy: strategyId, pid: child.pid, child };
|
|
365
365
|
}
|
|
366
366
|
|
|
367
|
-
async function runGameStart(opts: { watch: boolean; force?: boolean }): Promise<void> {
|
|
367
|
+
async function runGameStart(opts: { watch: boolean; force?: boolean; channelUrl?: string }): Promise<void> {
|
|
368
368
|
if (process.env.CLAWCLAW_REQUIRE_STREAM_TOOL === '1' && process.env.CLAWCLAW_STREAMED !== '1') {
|
|
369
369
|
process.stderr.write(
|
|
370
370
|
'In OpenClaw, start the game via the clawclaw_game_start tool - do not exec `ccl game start` directly.\n' +
|
|
@@ -434,7 +434,11 @@ async function runGameStart(opts: { watch: boolean; force?: boolean }): Promise<
|
|
|
434
434
|
process.on('SIGINT', onOwnerSignal);
|
|
435
435
|
process.on('SIGTERM', onOwnerSignal);
|
|
436
436
|
const emit = (obj: Record<string, any>): void => {
|
|
437
|
-
|
|
437
|
+
const line = JSON.stringify(obj) + '\n';
|
|
438
|
+
process.stdout.write(line);
|
|
439
|
+
if (opts.channelUrl) {
|
|
440
|
+
fetch(opts.channelUrl, { method: 'POST', body: line }).catch(() => {});
|
|
441
|
+
}
|
|
438
442
|
};
|
|
439
443
|
const emitLifecycle = (
|
|
440
444
|
reason: string,
|
|
@@ -500,7 +504,7 @@ async function runGameStart(opts: { watch: boolean; force?: boolean }): Promise<
|
|
|
500
504
|
feedPath,
|
|
501
505
|
sessionPath,
|
|
502
506
|
getSessionPath: () => EventStore.latestSessionPath(),
|
|
503
|
-
stdout: (s) => process.stdout.write(s),
|
|
507
|
+
stdout: (s) => { process.stdout.write(s); if (opts.channelUrl) { fetch(opts.channelUrl, { method: 'POST', body: s }).catch(() => {}); } },
|
|
504
508
|
signal: ctrl.signal,
|
|
505
509
|
skipFeedWait: true,
|
|
506
510
|
readSummary: currentSummary,
|
|
@@ -819,6 +823,7 @@ export function createGameCommand(): Command {
|
|
|
819
823
|
.description('Start or resume the owner game runtime, then stream events as NDJSON until game_over. Pass --no-watch to exit after allocation. Pass --force to replace an orphaned game-start runtime.')
|
|
820
824
|
.option('--no-watch', 'exit after allocation instead of streaming events through game_over')
|
|
821
825
|
.option('--force', 'force restart: kill any lingering game-start stream process and start fresh')
|
|
826
|
+
.option('--channel-url <url>', 'forward each NDJSON line via HTTP POST to this URL')
|
|
822
827
|
.action(runGameStart);
|
|
823
828
|
|
|
824
829
|
game
|
|
@@ -1,96 +1,96 @@
|
|
|
1
|
-
import { describe, expect, it, vi, beforeEach } from 'vitest';
|
|
2
|
-
import { runHermesSetup } from './hermes.js';
|
|
3
|
-
import { spawnSync } from 'child_process';
|
|
4
|
-
|
|
5
|
-
vi.mock('child_process', () => ({
|
|
6
|
-
spawnSync: vi.fn(),
|
|
7
|
-
}));
|
|
8
|
-
|
|
9
|
-
const mockedSpawnSync = vi.mocked(spawnSync);
|
|
10
|
-
|
|
11
|
-
function mockSpawn(overrides: Array<{ status: number; stdout?: string; stderr?: string }>) {
|
|
12
|
-
mockedSpawnSync.mockImplementation((_cmd: any, _args?: any, _opts?: any): any => {
|
|
13
|
-
const next = overrides.shift();
|
|
14
|
-
if (!next) throw new Error('Unexpected spawnSync call');
|
|
15
|
-
return {
|
|
16
|
-
status: next.status,
|
|
17
|
-
stdout: next.stdout ?? '',
|
|
18
|
-
stderr: next.stderr ?? '',
|
|
19
|
-
};
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
beforeEach(() => {
|
|
24
|
-
vi.clearAllMocks();
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
describe('runHermesSetup', () => {
|
|
28
|
-
it('returns error when hermes CLI is not found', () => {
|
|
29
|
-
mockSpawn([{ status: 1, stderr: 'command not found' }]);
|
|
30
|
-
const r = runHermesSetup({});
|
|
31
|
-
expect(r.exitCode).toBe(1);
|
|
32
|
-
expect(r.output.some((l) => l.includes('not found'))).toBe(true);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('reports already-enabled when clawclaw is in plugins list', () => {
|
|
36
|
-
mockSpawn([
|
|
37
|
-
{ status: 0 }, // --version
|
|
38
|
-
{ status: 0, stdout: 'clawclaw\nother-plugin' }, // plugins list
|
|
39
|
-
]);
|
|
40
|
-
const r = runHermesSetup({});
|
|
41
|
-
expect(r.exitCode).toBe(0);
|
|
42
|
-
expect(r.output.some((l) => l.includes('already enabled'))).toBe(true);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('dry-run shows pending message when not enabled', () => {
|
|
46
|
-
mockSpawn([
|
|
47
|
-
{ status: 0 }, // --version
|
|
48
|
-
{ status: 0, stdout: 'other-plugin' }, // plugins list
|
|
49
|
-
]);
|
|
50
|
-
const r = runHermesSetup({});
|
|
51
|
-
expect(r.exitCode).toBe(2);
|
|
52
|
-
expect(r.output.some((l) => l.includes('Dry-run'))).toBe(true);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('--print shows the command that would run', () => {
|
|
56
|
-
mockSpawn([
|
|
57
|
-
{ status: 0 }, // --version
|
|
58
|
-
{ status: 0, stdout: 'other-plugin' }, // plugins list
|
|
59
|
-
]);
|
|
60
|
-
const r = runHermesSetup({ print: true });
|
|
61
|
-
expect(r.exitCode).toBe(0);
|
|
62
|
-
expect(r.output.some((l) => l.includes('hermes plugins enable clawclaw'))).toBe(true);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('-y runs hermes plugins enable and succeeds', () => {
|
|
66
|
-
mockSpawn([
|
|
67
|
-
{ status: 0 }, // --version
|
|
68
|
-
{ status: 0, stdout: 'other-plugin' }, // plugins list
|
|
69
|
-
{ status: 0, stdout: 'plugin enabled' }, // plugins enable
|
|
70
|
-
]);
|
|
71
|
-
const r = runHermesSetup({ yes: true });
|
|
72
|
-
expect(r.exitCode).toBe(0);
|
|
73
|
-
expect(r.output.some((l) => l.includes('enabled'))).toBe(true);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it('-y handles hermes plugins enable failure', () => {
|
|
77
|
-
mockSpawn([
|
|
78
|
-
{ status: 0 }, // --version
|
|
79
|
-
{ status: 0, stdout: 'other-plugin' }, // plugins list
|
|
80
|
-
{ status: 1, stderr: 'permission denied' }, // plugins enable
|
|
81
|
-
]);
|
|
82
|
-
const r = runHermesSetup({ yes: true });
|
|
83
|
-
expect(r.exitCode).toBe(1);
|
|
84
|
-
expect(r.output.some((l) => l.includes('Failed'))).toBe(true);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it('already-enabled short-circuits --print', () => {
|
|
88
|
-
mockSpawn([
|
|
89
|
-
{ status: 0 }, // --version
|
|
90
|
-
{ status: 0, stdout: 'clawclaw' }, // plugins list
|
|
91
|
-
]);
|
|
92
|
-
const r = runHermesSetup({ print: true });
|
|
93
|
-
expect(r.exitCode).toBe(0);
|
|
94
|
-
expect(r.output.some((l) => l.includes('already enabled'))).toBe(true);
|
|
95
|
-
});
|
|
96
|
-
});
|
|
1
|
+
import { describe, expect, it, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { runHermesSetup } from './hermes.js';
|
|
3
|
+
import { spawnSync } from 'child_process';
|
|
4
|
+
|
|
5
|
+
vi.mock('child_process', () => ({
|
|
6
|
+
spawnSync: vi.fn(),
|
|
7
|
+
}));
|
|
8
|
+
|
|
9
|
+
const mockedSpawnSync = vi.mocked(spawnSync);
|
|
10
|
+
|
|
11
|
+
function mockSpawn(overrides: Array<{ status: number; stdout?: string; stderr?: string }>) {
|
|
12
|
+
mockedSpawnSync.mockImplementation((_cmd: any, _args?: any, _opts?: any): any => {
|
|
13
|
+
const next = overrides.shift();
|
|
14
|
+
if (!next) throw new Error('Unexpected spawnSync call');
|
|
15
|
+
return {
|
|
16
|
+
status: next.status,
|
|
17
|
+
stdout: next.stdout ?? '',
|
|
18
|
+
stderr: next.stderr ?? '',
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
vi.clearAllMocks();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe('runHermesSetup', () => {
|
|
28
|
+
it('returns error when hermes CLI is not found', () => {
|
|
29
|
+
mockSpawn([{ status: 1, stderr: 'command not found' }]);
|
|
30
|
+
const r = runHermesSetup({});
|
|
31
|
+
expect(r.exitCode).toBe(1);
|
|
32
|
+
expect(r.output.some((l) => l.includes('not found'))).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('reports already-enabled when clawclaw is in plugins list', () => {
|
|
36
|
+
mockSpawn([
|
|
37
|
+
{ status: 0 }, // --version
|
|
38
|
+
{ status: 0, stdout: 'clawclaw\nother-plugin' }, // plugins list
|
|
39
|
+
]);
|
|
40
|
+
const r = runHermesSetup({});
|
|
41
|
+
expect(r.exitCode).toBe(0);
|
|
42
|
+
expect(r.output.some((l) => l.includes('already enabled'))).toBe(true);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('dry-run shows pending message when not enabled', () => {
|
|
46
|
+
mockSpawn([
|
|
47
|
+
{ status: 0 }, // --version
|
|
48
|
+
{ status: 0, stdout: 'other-plugin' }, // plugins list
|
|
49
|
+
]);
|
|
50
|
+
const r = runHermesSetup({});
|
|
51
|
+
expect(r.exitCode).toBe(2);
|
|
52
|
+
expect(r.output.some((l) => l.includes('Dry-run'))).toBe(true);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('--print shows the command that would run', () => {
|
|
56
|
+
mockSpawn([
|
|
57
|
+
{ status: 0 }, // --version
|
|
58
|
+
{ status: 0, stdout: 'other-plugin' }, // plugins list
|
|
59
|
+
]);
|
|
60
|
+
const r = runHermesSetup({ print: true });
|
|
61
|
+
expect(r.exitCode).toBe(0);
|
|
62
|
+
expect(r.output.some((l) => l.includes('hermes plugins enable clawclaw'))).toBe(true);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('-y runs hermes plugins enable and succeeds', () => {
|
|
66
|
+
mockSpawn([
|
|
67
|
+
{ status: 0 }, // --version
|
|
68
|
+
{ status: 0, stdout: 'other-plugin' }, // plugins list
|
|
69
|
+
{ status: 0, stdout: 'plugin enabled' }, // plugins enable
|
|
70
|
+
]);
|
|
71
|
+
const r = runHermesSetup({ yes: true });
|
|
72
|
+
expect(r.exitCode).toBe(0);
|
|
73
|
+
expect(r.output.some((l) => l.includes('enabled'))).toBe(true);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('-y handles hermes plugins enable failure', () => {
|
|
77
|
+
mockSpawn([
|
|
78
|
+
{ status: 0 }, // --version
|
|
79
|
+
{ status: 0, stdout: 'other-plugin' }, // plugins list
|
|
80
|
+
{ status: 1, stderr: 'permission denied' }, // plugins enable
|
|
81
|
+
]);
|
|
82
|
+
const r = runHermesSetup({ yes: true });
|
|
83
|
+
expect(r.exitCode).toBe(1);
|
|
84
|
+
expect(r.output.some((l) => l.includes('Failed'))).toBe(true);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('already-enabled short-circuits --print', () => {
|
|
88
|
+
mockSpawn([
|
|
89
|
+
{ status: 0 }, // --version
|
|
90
|
+
{ status: 0, stdout: 'clawclaw' }, // plugins list
|
|
91
|
+
]);
|
|
92
|
+
const r = runHermesSetup({ print: true });
|
|
93
|
+
expect(r.exitCode).toBe(0);
|
|
94
|
+
expect(r.output.some((l) => l.includes('already enabled'))).toBe(true);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
@@ -1,76 +1,76 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `ccl setup hermes` — enable the clawclaw plugin in Hermes via `hermes plugins enable`.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { Command } from 'commander';
|
|
6
|
-
import { spawnSync } from 'child_process';
|
|
7
|
-
|
|
8
|
-
export const PLUGIN_ID = 'clawclaw';
|
|
9
|
-
|
|
10
|
-
export interface SetupHermesResult {
|
|
11
|
-
exitCode: number;
|
|
12
|
-
output: string[];
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function runHermesSetup(flags: { yes?: boolean; print?: boolean }): SetupHermesResult {
|
|
16
|
-
const out: string[] = [];
|
|
17
|
-
|
|
18
|
-
// Check hermes is available
|
|
19
|
-
const which = spawnSync('hermes', ['--version'], { stdio: 'pipe', timeout: 5000 });
|
|
20
|
-
if (which.status !== 0) {
|
|
21
|
-
out.push('hermes CLI not found. Install it first, then re-run.');
|
|
22
|
-
return { exitCode: 1, output: out };
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// Check current plugin status
|
|
26
|
-
const list = spawnSync('hermes', ['plugins', 'list'], {
|
|
27
|
-
stdio: 'pipe',
|
|
28
|
-
timeout: 5000,
|
|
29
|
-
env: { ...process.env, HERMES_PLUGINS_DEBUG: '1' },
|
|
30
|
-
});
|
|
31
|
-
const listOutput = list.stdout?.toString() ?? '';
|
|
32
|
-
const alreadyEnabled = listOutput.includes(PLUGIN_ID) && !listOutput.includes(`${PLUGIN_ID} (disabled)`);
|
|
33
|
-
|
|
34
|
-
if (alreadyEnabled) {
|
|
35
|
-
out.push(`Plugin "${PLUGIN_ID}" is already enabled in Hermes.`);
|
|
36
|
-
return { exitCode: 0, output: out };
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (flags.print) {
|
|
40
|
-
out.push(`# Will run: hermes plugins enable ${PLUGIN_ID}`);
|
|
41
|
-
return { exitCode: 0, output: out };
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (!flags.yes) {
|
|
45
|
-
out.push(`Pending: hermes plugins enable ${PLUGIN_ID}`);
|
|
46
|
-
out.push(``);
|
|
47
|
-
out.push(`Dry-run only. Re-run with -y to apply.`);
|
|
48
|
-
return { exitCode: 2, output: out };
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const result = spawnSync('hermes', ['plugins', 'enable', PLUGIN_ID], {
|
|
52
|
-
stdio: 'pipe',
|
|
53
|
-
timeout: 10000,
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
if (result.status !== 0) {
|
|
57
|
-
out.push(`Failed to enable plugin: ${result.stderr?.toString() ?? result.stdout?.toString() ?? 'unknown error'}`);
|
|
58
|
-
return { exitCode: 1, output: out };
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
out.push(`Plugin "${PLUGIN_ID}" enabled in Hermes.`);
|
|
62
|
-
out.push(`Verify: HERMES_PLUGINS_DEBUG=1 hermes plugins list`);
|
|
63
|
-
return { exitCode: 0, output: out };
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export function createSetupHermesSubcommand(): Command {
|
|
67
|
-
return new Command('hermes')
|
|
68
|
-
.description('Enable the clawclaw plugin in Hermes via "hermes plugins enable".')
|
|
69
|
-
.option('-y, --yes', 'Apply changes (default is dry-run)')
|
|
70
|
-
.option('--print', 'Only print the command that would be run; do not execute')
|
|
71
|
-
.action((opts: { yes?: boolean; print?: boolean }) => {
|
|
72
|
-
const result = runHermesSetup({ yes: opts.yes, print: opts.print });
|
|
73
|
-
for (const line of result.output) console.log(line);
|
|
74
|
-
if (result.exitCode !== 0) process.exit(result.exitCode);
|
|
75
|
-
});
|
|
76
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* `ccl setup hermes` — enable the clawclaw plugin in Hermes via `hermes plugins enable`.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Command } from 'commander';
|
|
6
|
+
import { spawnSync } from 'child_process';
|
|
7
|
+
|
|
8
|
+
export const PLUGIN_ID = 'clawclaw';
|
|
9
|
+
|
|
10
|
+
export interface SetupHermesResult {
|
|
11
|
+
exitCode: number;
|
|
12
|
+
output: string[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function runHermesSetup(flags: { yes?: boolean; print?: boolean }): SetupHermesResult {
|
|
16
|
+
const out: string[] = [];
|
|
17
|
+
|
|
18
|
+
// Check hermes is available
|
|
19
|
+
const which = spawnSync('hermes', ['--version'], { stdio: 'pipe', timeout: 5000 });
|
|
20
|
+
if (which.status !== 0) {
|
|
21
|
+
out.push('hermes CLI not found. Install it first, then re-run.');
|
|
22
|
+
return { exitCode: 1, output: out };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Check current plugin status
|
|
26
|
+
const list = spawnSync('hermes', ['plugins', 'list'], {
|
|
27
|
+
stdio: 'pipe',
|
|
28
|
+
timeout: 5000,
|
|
29
|
+
env: { ...process.env, HERMES_PLUGINS_DEBUG: '1' },
|
|
30
|
+
});
|
|
31
|
+
const listOutput = list.stdout?.toString() ?? '';
|
|
32
|
+
const alreadyEnabled = listOutput.includes(PLUGIN_ID) && !listOutput.includes(`${PLUGIN_ID} (disabled)`);
|
|
33
|
+
|
|
34
|
+
if (alreadyEnabled) {
|
|
35
|
+
out.push(`Plugin "${PLUGIN_ID}" is already enabled in Hermes.`);
|
|
36
|
+
return { exitCode: 0, output: out };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (flags.print) {
|
|
40
|
+
out.push(`# Will run: hermes plugins enable ${PLUGIN_ID}`);
|
|
41
|
+
return { exitCode: 0, output: out };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (!flags.yes) {
|
|
45
|
+
out.push(`Pending: hermes plugins enable ${PLUGIN_ID}`);
|
|
46
|
+
out.push(``);
|
|
47
|
+
out.push(`Dry-run only. Re-run with -y to apply.`);
|
|
48
|
+
return { exitCode: 2, output: out };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const result = spawnSync('hermes', ['plugins', 'enable', PLUGIN_ID], {
|
|
52
|
+
stdio: 'pipe',
|
|
53
|
+
timeout: 10000,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
if (result.status !== 0) {
|
|
57
|
+
out.push(`Failed to enable plugin: ${result.stderr?.toString() ?? result.stdout?.toString() ?? 'unknown error'}`);
|
|
58
|
+
return { exitCode: 1, output: out };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
out.push(`Plugin "${PLUGIN_ID}" enabled in Hermes.`);
|
|
62
|
+
out.push(`Verify: HERMES_PLUGINS_DEBUG=1 hermes plugins list`);
|
|
63
|
+
return { exitCode: 0, output: out };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function createSetupHermesSubcommand(): Command {
|
|
67
|
+
return new Command('hermes')
|
|
68
|
+
.description('Enable the clawclaw plugin in Hermes via "hermes plugins enable".')
|
|
69
|
+
.option('-y, --yes', 'Apply changes (default is dry-run)')
|
|
70
|
+
.option('--print', 'Only print the command that would be run; do not execute')
|
|
71
|
+
.action((opts: { yes?: boolean; print?: boolean }) => {
|
|
72
|
+
const result = runHermesSetup({ yes: opts.yes, print: opts.print });
|
|
73
|
+
for (const line of result.output) console.log(line);
|
|
74
|
+
if (result.exitCode !== 0) process.exit(result.exitCode);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { createSetupOpenclawSubcommand } from './openclaw.js';
|
|
3
|
-
import { createSetupHermesSubcommand } from './hermes.js';
|
|
4
|
-
import { createSetupCodexSubcommand } from './codex.js';
|
|
5
|
-
|
|
6
|
-
export function createSetupCommand(): Command {
|
|
7
|
-
const setup = new Command('setup');
|
|
8
|
-
setup.description('Install recommended config snippets into agent host config files.');
|
|
9
|
-
setup.addCommand(createSetupOpenclawSubcommand());
|
|
10
|
-
setup.addCommand(createSetupHermesSubcommand());
|
|
11
|
-
setup.addCommand(createSetupCodexSubcommand());
|
|
12
|
-
return setup;
|
|
13
|
-
}
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { createSetupOpenclawSubcommand } from './openclaw.js';
|
|
3
|
+
import { createSetupHermesSubcommand } from './hermes.js';
|
|
4
|
+
import { createSetupCodexSubcommand } from './codex.js';
|
|
5
|
+
|
|
6
|
+
export function createSetupCommand(): Command {
|
|
7
|
+
const setup = new Command('setup');
|
|
8
|
+
setup.description('Install recommended config snippets into agent host config files.');
|
|
9
|
+
setup.addCommand(createSetupOpenclawSubcommand());
|
|
10
|
+
setup.addCommand(createSetupHermesSubcommand());
|
|
11
|
+
setup.addCommand(createSetupCodexSubcommand());
|
|
12
|
+
return setup;
|
|
13
|
+
}
|