@myclaw163/clawclaw-cli 0.6.58 → 0.6.60

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.
Files changed (195) hide show
  1. package/README.md +440 -440
  2. package/package.json +48 -48
  3. package/personas//347/220/206/346/231/272/346/270/251/345/222/214.md +23 -23
  4. package/personas//350/200/201/350/260/213/346/267/261/347/256/227.md +22 -22
  5. package/personas//350/257/232/346/201/263/347/233/264/347/216/207.md +22 -22
  6. package/personas//350/275/273/346/235/276/346/264/273/346/263/274.md +22 -22
  7. package/personas//351/207/216/346/200/247/345/217/233/351/200/206.md +23 -23
  8. package/scripts/postinstall.mjs +20 -20
  9. package/scripts/sync-bundled-skill.mjs +244 -244
  10. package/scripts/sync-bundled-skill.test.mjs +152 -152
  11. package/skills/clawclaw/SKILL.md +244 -244
  12. package/skills/clawclaw/references/CHATTERBOX.md +142 -142
  13. package/skills/clawclaw/references/COMMANDS.md +129 -132
  14. package/skills/clawclaw/references/GAME-MECHANICS.md +186 -186
  15. package/skills/clawclaw/references/HUB.md +48 -48
  16. package/skills/clawclaw/references/KNOWLEDGE.md +43 -43
  17. package/skills/clawclaw/references/STRATEGIES.md +57 -57
  18. package/skills/clawclaw/references/STREAM.md +59 -59
  19. package/skills/clawclaw/references/TACTICS.md +65 -65
  20. package/src/assets/clawclaw-ascii-map.txt +40 -40
  21. package/src/cli.ts +110 -110
  22. package/src/commands/_schema.ts +109 -109
  23. package/src/commands/account.ts +209 -209
  24. package/src/commands/config.ts +30 -30
  25. package/src/commands/do.test.ts +73 -37
  26. package/src/commands/do.ts +126 -95
  27. package/src/commands/events.ts +22 -22
  28. package/src/commands/game-map.test.ts +28 -28
  29. package/src/commands/game-start-plan.test.ts +84 -84
  30. package/src/commands/game.ts +1027 -1027
  31. package/src/commands/history-player.test.ts +102 -102
  32. package/src/commands/history.ts +573 -573
  33. package/src/commands/hub.test.ts +96 -96
  34. package/src/commands/hub.ts +234 -234
  35. package/src/commands/knowledge.test.ts +19 -19
  36. package/src/commands/knowledge.ts +168 -168
  37. package/src/commands/load.test.ts +51 -51
  38. package/src/commands/load.ts +13 -13
  39. package/src/commands/meeting-history.test.ts +106 -106
  40. package/src/commands/memory.ts +40 -40
  41. package/src/commands/peek.ts +45 -45
  42. package/src/commands/persona.ts +57 -57
  43. package/src/commands/setup/codex.ts +248 -248
  44. package/src/commands/setup/hermes.test.ts +96 -96
  45. package/src/commands/setup/hermes.ts +76 -76
  46. package/src/commands/setup/index.ts +13 -13
  47. package/src/commands/setup/openclaw.test.ts +114 -114
  48. package/src/commands/setup/openclaw.ts +147 -147
  49. package/src/commands/skill.ts +128 -128
  50. package/src/commands/state.ts +46 -46
  51. package/src/commands/strategy.test.ts +135 -135
  52. package/src/commands/strategy.ts +180 -180
  53. package/src/commands/tts.ts +128 -128
  54. package/src/commands/upgrade.test.ts +82 -82
  55. package/src/commands/upgrade.ts +148 -148
  56. package/src/commands/watch.test.ts +969 -969
  57. package/src/commands/watch.ts +720 -720
  58. package/src/lib/auth.test.ts +59 -59
  59. package/src/lib/auth.ts +186 -186
  60. package/src/lib/command-meta.ts +37 -37
  61. package/src/lib/game-client.ts +391 -391
  62. package/src/lib/host-config-patcher.test.ts +130 -130
  63. package/src/lib/host-config-patcher.ts +151 -151
  64. package/src/lib/http-keepalive.ts +15 -15
  65. package/src/lib/http-transport.test.ts +42 -42
  66. package/src/lib/http-transport.ts +113 -113
  67. package/src/lib/hub-client.test.ts +56 -56
  68. package/src/lib/hub-client.ts +88 -88
  69. package/src/lib/hub-install.test.ts +98 -98
  70. package/src/lib/hub-install.ts +121 -121
  71. package/src/lib/hub-reminder.ts +56 -56
  72. package/src/lib/hub-unzip.test.ts +69 -69
  73. package/src/lib/hub-unzip.ts +62 -62
  74. package/src/lib/init-command.test.ts +75 -75
  75. package/src/lib/init-command.ts +120 -120
  76. package/src/lib/knowledge-store.test.ts +180 -180
  77. package/src/lib/knowledge-store.ts +374 -374
  78. package/src/lib/load-context.test.ts +52 -52
  79. package/src/lib/load-context.ts +52 -52
  80. package/src/lib/match-state.test.ts +134 -134
  81. package/src/lib/match-state.ts +94 -94
  82. package/src/lib/netease-tts.ts +83 -83
  83. package/src/lib/normalize.ts +42 -42
  84. package/src/lib/persona.test.ts +41 -41
  85. package/src/lib/persona.ts +72 -72
  86. package/src/lib/server-registry.ts +152 -152
  87. package/src/lib/skill-version.test.ts +48 -48
  88. package/src/lib/skill-version.ts +19 -19
  89. package/src/lib/strategy-export.test.ts +232 -232
  90. package/src/lib/strategy-export.ts +242 -242
  91. package/src/lib/tts-keys.ts +7 -7
  92. package/src/lib/tts-speech.test.ts +63 -63
  93. package/src/lib/tts-speech.ts +76 -76
  94. package/src/lib/workspace-argv.test.ts +49 -49
  95. package/src/lib/workspace-argv.ts +44 -44
  96. package/src/perception/player-history-store.test.ts +87 -87
  97. package/src/perception/player-history-store.ts +194 -194
  98. package/src/pipeline/event-store.ts +124 -124
  99. package/src/pipeline/pipeline.ts +35 -35
  100. package/src/runtime/auto-upgrade.test.ts +66 -66
  101. package/src/runtime/auto-upgrade.ts +31 -31
  102. package/src/runtime/event-daemon.test.ts +107 -107
  103. package/src/runtime/event-daemon.ts +409 -409
  104. package/src/runtime/owner-control.ts +150 -150
  105. package/src/runtime/raw-ws-log.test.ts +33 -33
  106. package/src/runtime/raw-ws-log.ts +32 -32
  107. package/src/runtime/runtime-logger.ts +107 -107
  108. package/src/runtime/ws-client.test.ts +104 -104
  109. package/src/runtime/ws-client.ts +272 -272
  110. package/src/sdk/action.ts +166 -166
  111. package/src/sdk/index.ts +110 -110
  112. package/src/sdk/types.ts +146 -146
  113. package/src/strategies/avoid-lone.ts +11 -11
  114. package/src/strategies/avoid-players.knowledge.md +20 -20
  115. package/src/strategies/avoid-players.ts +15 -15
  116. package/src/strategies/corpse-patrol.ts +22 -22
  117. package/src/strategies/crab-sabotage.ts +21 -21
  118. package/src/strategies/custom-module.test.ts +269 -269
  119. package/src/strategies/find-player.ts +16 -16
  120. package/src/strategies/game-utils.test.ts +190 -190
  121. package/src/strategies/game-utils.ts +744 -744
  122. package/src/strategies/goals/avoid-lone-top.ts +168 -168
  123. package/src/strategies/goals/avoid-players-top.test.ts +83 -83
  124. package/src/strategies/goals/avoid-players-top.ts +121 -121
  125. package/src/strategies/goals/conversation-goal.ts +51 -51
  126. package/src/strategies/goals/corpse-patrol-top.ts +91 -91
  127. package/src/strategies/goals/crab-octopus-reflexes.ts +93 -93
  128. package/src/strategies/goals/crab-sabotage-top.ts +197 -197
  129. package/src/strategies/goals/emergency-hunt-goal.ts +28 -28
  130. package/src/strategies/goals/find-player-top.ts +93 -93
  131. package/src/strategies/goals/flee-players-goal.ts +53 -53
  132. package/src/strategies/goals/goal-manager.ts +41 -41
  133. package/src/strategies/goals/goal-root-strategy.ts +49 -49
  134. package/src/strategies/goals/goal.ts +28 -28
  135. package/src/strategies/goals/keep-away-goal.ts +209 -209
  136. package/src/strategies/goals/kill-frenzy-top.ts +80 -80
  137. package/src/strategies/goals/kill-lone-top.ts +160 -160
  138. package/src/strategies/goals/kill-target-goal.ts +59 -59
  139. package/src/strategies/goals/kill-target-top.ts +109 -109
  140. package/src/strategies/goals/leaf-goal.ts +25 -25
  141. package/src/strategies/goals/linger-corpse-goal.ts +79 -79
  142. package/src/strategies/goals/lone-kill-core.ts +82 -82
  143. package/src/strategies/goals/lone-kill-goal.ts +24 -24
  144. package/src/strategies/goals/lone-kill-task-top.test.ts +85 -85
  145. package/src/strategies/goals/lone-kill-task-top.ts +86 -86
  146. package/src/strategies/goals/move-room-goal.ts +60 -60
  147. package/src/strategies/goals/normal-shrimp-top.test.ts +80 -80
  148. package/src/strategies/goals/normal-shrimp-top.ts +242 -242
  149. package/src/strategies/goals/paradise-fish-top.test.ts +126 -126
  150. package/src/strategies/goals/paradise-fish-top.ts +219 -219
  151. package/src/strategies/goals/patrol-top.ts +57 -57
  152. package/src/strategies/goals/report-patrol-top.ts +80 -80
  153. package/src/strategies/goals/safe-task-goal.ts +102 -102
  154. package/src/strategies/goals/social-task-top.ts +161 -161
  155. package/src/strategies/goals/task-kill-report-top.ts +163 -163
  156. package/src/strategies/goals/task-only-top.ts +57 -57
  157. package/src/strategies/goals/task-or-patrol-goal.ts +41 -41
  158. package/src/strategies/goals/task-report-top.ts +57 -57
  159. package/src/strategies/goals/wander-task-goal.ts +33 -33
  160. package/src/strategies/goals/warrior-shrimp-top.test.ts +86 -86
  161. package/src/strategies/goals/warrior-shrimp-top.ts +248 -248
  162. package/src/strategies/greeting.ts +53 -53
  163. package/src/strategies/kill-frenzy.ts +12 -12
  164. package/src/strategies/kill-lone.knowledge.md +20 -20
  165. package/src/strategies/kill-lone.ts +13 -13
  166. package/src/strategies/kill-target.ts +18 -18
  167. package/src/strategies/loader.test.ts +678 -678
  168. package/src/strategies/loader.ts +172 -172
  169. package/src/strategies/lone-kill-task.ts +21 -21
  170. package/src/strategies/meeting-gate.test.ts +59 -59
  171. package/src/strategies/meeting-gate.ts +23 -23
  172. package/src/strategies/move-room.ts +15 -15
  173. package/src/strategies/new-events-backfill.ts +98 -98
  174. package/src/strategies/paradise-fish.knowledge.md +20 -20
  175. package/src/strategies/paradise-fish.ts +25 -25
  176. package/src/strategies/pathfind/distance-field.ts +150 -150
  177. package/src/strategies/pathfind/escape-planner.test.ts +197 -197
  178. package/src/strategies/pathfind/escape-planner.ts +355 -355
  179. package/src/strategies/pathfind/walkable-grid.ts +117 -117
  180. package/src/strategies/patrol.ts +11 -11
  181. package/src/strategies/player-targets.ts +13 -13
  182. package/src/strategies/report-patrol.ts +11 -11
  183. package/src/strategies/shrimp-memory.knowledge.md +20 -20
  184. package/src/strategies/shrimp-memory.ts +25 -25
  185. package/src/strategies/social-task.test.ts +28 -28
  186. package/src/strategies/social-task.ts +49 -49
  187. package/src/strategies/spawn.ts +82 -82
  188. package/src/strategies/speech-module.ts +123 -123
  189. package/src/strategies/strategy-loop.ts +763 -763
  190. package/src/strategies/task-kill-report.ts +17 -17
  191. package/src/strategies/task-only.ts +11 -11
  192. package/src/strategies/task-report.ts +22 -22
  193. package/src/strategies/types.ts +96 -96
  194. package/src/strategies/warrior-memory.knowledge.md +20 -20
  195. package/src/strategies/warrior-memory.ts +16 -16
@@ -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
+ }
@@ -1,114 +1,114 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { computeOpenclawPatch, resolveOpenclawConfigPath } from './openclaw.js';
3
-
4
- describe('computeOpenclawPatch', () => {
5
- it('on empty config creates plugins.allow, entries.enabled, tools.alsoAllow', () => {
6
- const r = computeOpenclawPatch({}, {});
7
- expect(r.warnings).toEqual([]);
8
- expect(r.next).toEqual({
9
- plugins: { allow: ['clawclaw'], entries: { clawclaw: { enabled: true } } },
10
- tools: { alsoAllow: ['clawclaw'] },
11
- });
12
- expect(r.changes).toHaveLength(3);
13
- });
14
-
15
- it('null/non-object current is treated as empty', () => {
16
- const r1 = computeOpenclawPatch(null, {});
17
- expect(r1.warnings).toEqual([]);
18
- expect((r1.next as any).plugins.allow).toEqual(['clawclaw']);
19
- const r2 = computeOpenclawPatch(['unexpected'], {});
20
- expect(r2.warnings).toEqual([]);
21
- expect((r2.next as any).plugins.allow).toEqual(['clawclaw']);
22
- });
23
-
24
- it('no-op when everything already present', () => {
25
- const current = {
26
- plugins: { allow: ['clawclaw'], entries: { clawclaw: { enabled: true } } },
27
- tools: { profile: 'coding', alsoAllow: ['clawclaw'] },
28
- };
29
- const r = computeOpenclawPatch(current, {});
30
- expect(r.changes).toEqual([]);
31
- expect(r.warnings).toEqual([]);
32
- expect(r.next).toEqual(current);
33
- });
34
-
35
- it('Set union into existing plugins.allow without dupes', () => {
36
- const r = computeOpenclawPatch({ plugins: { allow: ['other'] } }, {});
37
- expect((r.next as any).plugins.allow).toEqual(['other', 'clawclaw']);
38
- expect(r.changes).toContain('add "clawclaw" to plugins.allow');
39
- });
40
-
41
- it('Set union into existing tools.alsoAllow without dupes', () => {
42
- const r = computeOpenclawPatch({ tools: { profile: 'coding', alsoAllow: ['somethingElse'] } }, {});
43
- expect((r.next as any).tools.alsoAllow).toEqual(['somethingElse', 'clawclaw']);
44
- expect(r.changes).toContain('add "clawclaw" to tools.alsoAllow');
45
- });
46
-
47
- it('does not touch tools.profile, even when not coding', () => {
48
- const r = computeOpenclawPatch({ tools: { profile: 'full' } }, {});
49
- expect((r.next as any).tools.profile).toBe('full');
50
- });
51
-
52
- it('does not touch tools.profile when missing (no opinion-setting)', () => {
53
- const r = computeOpenclawPatch({}, {});
54
- expect((r.next as any).tools.profile).toBeUndefined();
55
- });
56
-
57
- it('warns and skips when plugins.allow is not an array', () => {
58
- const r = computeOpenclawPatch({ plugins: { allow: 'oops' } }, {});
59
- expect(r.warnings.some((w) => w.includes('plugins.allow'))).toBe(true);
60
- // unchanged value
61
- expect((r.next as any).plugins.allow).toBe('oops');
62
- });
63
-
64
- it('warns and skips when tools.alsoAllow is not an array', () => {
65
- const r = computeOpenclawPatch({ tools: { alsoAllow: 'oops' } }, {});
66
- expect(r.warnings.some((w) => w.includes('tools.alsoAllow'))).toBe(true);
67
- expect((r.next as any).tools.alsoAllow).toBe('oops');
68
- });
69
-
70
- it('respects explicit enabled:false on entries.clawclaw', () => {
71
- const r = computeOpenclawPatch(
72
- { plugins: { entries: { clawclaw: { enabled: false } } } },
73
- {},
74
- );
75
- expect((r.next as any).plugins.entries.clawclaw.enabled).toBe(false);
76
- expect(r.warnings.some((w) => w.includes('explicitly false'))).toBe(true);
77
- // and we did not mark a change for it
78
- expect(r.changes.some((c) => c.includes('enabled = true'))).toBe(false);
79
- });
80
-
81
- it('--skip-plugins-allow leaves plugins untouched, still patches tools', () => {
82
- const r = computeOpenclawPatch({}, { skipPluginsAllow: true });
83
- expect((r.next as any).plugins).toBeUndefined();
84
- expect((r.next as any).tools.alsoAllow).toEqual(['clawclaw']);
85
- });
86
-
87
- it('--skip-tools-allow leaves tools untouched, still patches plugins', () => {
88
- const r = computeOpenclawPatch({}, { skipToolsAllow: true });
89
- expect((r.next as any).tools).toBeUndefined();
90
- expect((r.next as any).plugins.allow).toEqual(['clawclaw']);
91
- });
92
-
93
- it('preserves unrelated top-level keys', () => {
94
- const r = computeOpenclawPatch(
95
- { models: { mode: 'replace' }, channels: { telegram: { enabled: true } } },
96
- {},
97
- );
98
- expect((r.next as any).models).toEqual({ mode: 'replace' });
99
- expect((r.next as any).channels).toEqual({ telegram: { enabled: true } });
100
- });
101
- });
102
-
103
- describe('resolveOpenclawConfigPath', () => {
104
- it('uses OPENCLAW_HOME when set', () => {
105
- const p = resolveOpenclawConfigPath({ OPENCLAW_HOME: '/tmp/oc' });
106
- expect(p.replace(/\\/g, '/')).toBe('/tmp/oc/openclaw.json');
107
- });
108
-
109
- it('falls back to ~/.openclaw/openclaw.json', () => {
110
- const p = resolveOpenclawConfigPath({});
111
- expect(p.endsWith('openclaw.json')).toBe(true);
112
- expect(p.includes('.openclaw')).toBe(true);
113
- });
114
- });
1
+ import { describe, expect, it } from 'vitest';
2
+ import { computeOpenclawPatch, resolveOpenclawConfigPath } from './openclaw.js';
3
+
4
+ describe('computeOpenclawPatch', () => {
5
+ it('on empty config creates plugins.allow, entries.enabled, tools.alsoAllow', () => {
6
+ const r = computeOpenclawPatch({}, {});
7
+ expect(r.warnings).toEqual([]);
8
+ expect(r.next).toEqual({
9
+ plugins: { allow: ['clawclaw'], entries: { clawclaw: { enabled: true } } },
10
+ tools: { alsoAllow: ['clawclaw'] },
11
+ });
12
+ expect(r.changes).toHaveLength(3);
13
+ });
14
+
15
+ it('null/non-object current is treated as empty', () => {
16
+ const r1 = computeOpenclawPatch(null, {});
17
+ expect(r1.warnings).toEqual([]);
18
+ expect((r1.next as any).plugins.allow).toEqual(['clawclaw']);
19
+ const r2 = computeOpenclawPatch(['unexpected'], {});
20
+ expect(r2.warnings).toEqual([]);
21
+ expect((r2.next as any).plugins.allow).toEqual(['clawclaw']);
22
+ });
23
+
24
+ it('no-op when everything already present', () => {
25
+ const current = {
26
+ plugins: { allow: ['clawclaw'], entries: { clawclaw: { enabled: true } } },
27
+ tools: { profile: 'coding', alsoAllow: ['clawclaw'] },
28
+ };
29
+ const r = computeOpenclawPatch(current, {});
30
+ expect(r.changes).toEqual([]);
31
+ expect(r.warnings).toEqual([]);
32
+ expect(r.next).toEqual(current);
33
+ });
34
+
35
+ it('Set union into existing plugins.allow without dupes', () => {
36
+ const r = computeOpenclawPatch({ plugins: { allow: ['other'] } }, {});
37
+ expect((r.next as any).plugins.allow).toEqual(['other', 'clawclaw']);
38
+ expect(r.changes).toContain('add "clawclaw" to plugins.allow');
39
+ });
40
+
41
+ it('Set union into existing tools.alsoAllow without dupes', () => {
42
+ const r = computeOpenclawPatch({ tools: { profile: 'coding', alsoAllow: ['somethingElse'] } }, {});
43
+ expect((r.next as any).tools.alsoAllow).toEqual(['somethingElse', 'clawclaw']);
44
+ expect(r.changes).toContain('add "clawclaw" to tools.alsoAllow');
45
+ });
46
+
47
+ it('does not touch tools.profile, even when not coding', () => {
48
+ const r = computeOpenclawPatch({ tools: { profile: 'full' } }, {});
49
+ expect((r.next as any).tools.profile).toBe('full');
50
+ });
51
+
52
+ it('does not touch tools.profile when missing (no opinion-setting)', () => {
53
+ const r = computeOpenclawPatch({}, {});
54
+ expect((r.next as any).tools.profile).toBeUndefined();
55
+ });
56
+
57
+ it('warns and skips when plugins.allow is not an array', () => {
58
+ const r = computeOpenclawPatch({ plugins: { allow: 'oops' } }, {});
59
+ expect(r.warnings.some((w) => w.includes('plugins.allow'))).toBe(true);
60
+ // unchanged value
61
+ expect((r.next as any).plugins.allow).toBe('oops');
62
+ });
63
+
64
+ it('warns and skips when tools.alsoAllow is not an array', () => {
65
+ const r = computeOpenclawPatch({ tools: { alsoAllow: 'oops' } }, {});
66
+ expect(r.warnings.some((w) => w.includes('tools.alsoAllow'))).toBe(true);
67
+ expect((r.next as any).tools.alsoAllow).toBe('oops');
68
+ });
69
+
70
+ it('respects explicit enabled:false on entries.clawclaw', () => {
71
+ const r = computeOpenclawPatch(
72
+ { plugins: { entries: { clawclaw: { enabled: false } } } },
73
+ {},
74
+ );
75
+ expect((r.next as any).plugins.entries.clawclaw.enabled).toBe(false);
76
+ expect(r.warnings.some((w) => w.includes('explicitly false'))).toBe(true);
77
+ // and we did not mark a change for it
78
+ expect(r.changes.some((c) => c.includes('enabled = true'))).toBe(false);
79
+ });
80
+
81
+ it('--skip-plugins-allow leaves plugins untouched, still patches tools', () => {
82
+ const r = computeOpenclawPatch({}, { skipPluginsAllow: true });
83
+ expect((r.next as any).plugins).toBeUndefined();
84
+ expect((r.next as any).tools.alsoAllow).toEqual(['clawclaw']);
85
+ });
86
+
87
+ it('--skip-tools-allow leaves tools untouched, still patches plugins', () => {
88
+ const r = computeOpenclawPatch({}, { skipToolsAllow: true });
89
+ expect((r.next as any).tools).toBeUndefined();
90
+ expect((r.next as any).plugins.allow).toEqual(['clawclaw']);
91
+ });
92
+
93
+ it('preserves unrelated top-level keys', () => {
94
+ const r = computeOpenclawPatch(
95
+ { models: { mode: 'replace' }, channels: { telegram: { enabled: true } } },
96
+ {},
97
+ );
98
+ expect((r.next as any).models).toEqual({ mode: 'replace' });
99
+ expect((r.next as any).channels).toEqual({ telegram: { enabled: true } });
100
+ });
101
+ });
102
+
103
+ describe('resolveOpenclawConfigPath', () => {
104
+ it('uses OPENCLAW_HOME when set', () => {
105
+ const p = resolveOpenclawConfigPath({ OPENCLAW_HOME: '/tmp/oc' });
106
+ expect(p.replace(/\\/g, '/')).toBe('/tmp/oc/openclaw.json');
107
+ });
108
+
109
+ it('falls back to ~/.openclaw/openclaw.json', () => {
110
+ const p = resolveOpenclawConfigPath({});
111
+ expect(p.endsWith('openclaw.json')).toBe(true);
112
+ expect(p.includes('.openclaw')).toBe(true);
113
+ });
114
+ });