@myclaw163/clawclaw-cli 0.6.74 → 0.6.77
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 +387 -377
- package/bin/clawclaw-cli.mjs +3 -3
- package/package.json +48 -48
- package/personas//347/220/206/346/231/272/346/270/251/345/222/214.md +23 -23
- package/personas//350/200/201/350/260/213/346/267/261/347/256/227.md +22 -22
- package/personas//350/257/232/346/201/263/347/233/264/347/216/207.md +22 -22
- package/personas//350/275/273/346/235/276/346/264/273/346/263/274.md +22 -22
- package/personas//351/207/216/346/200/247/345/217/233/351/200/206.md +23 -23
- package/scripts/check-skill-command-surface.mjs +116 -116
- package/scripts/find-hide-spots.py +157 -157
- package/scripts/postinstall.mjs +20 -20
- package/scripts/sync-bundled-skill.mjs +254 -245
- package/scripts/sync-bundled-skill.test.mjs +152 -152
- package/skills/clawclaw/SKILL.md +248 -246
- package/skills/clawclaw/references/CHATTERBOX.md +141 -141
- package/skills/clawclaw/references/COMMANDS.md +160 -155
- package/skills/clawclaw/references/GAME-MECHANICS.md +188 -188
- package/skills/clawclaw/references/HUB.md +48 -48
- package/skills/clawclaw/references/KNOWLEDGE.md +42 -42
- package/skills/clawclaw/references/STRATEGIES.md +59 -59
- package/skills/clawclaw/references/STREAM.md +93 -93
- package/skills/clawclaw/references/TACTICS.md +65 -65
- package/src/assets/clawclaw-ascii-map.txt +40 -40
- package/src/cli.ts +112 -110
- package/src/commands/_schema.ts +124 -124
- package/src/commands/account.ts +209 -209
- package/src/commands/data.test.ts +33 -0
- package/src/commands/data.ts +22 -0
- package/src/commands/do.test.ts +84 -84
- package/src/commands/do.ts +130 -130
- package/src/commands/events.test.ts +100 -71
- package/src/commands/events.ts +250 -221
- package/src/commands/game-map.test.ts +28 -28
- package/src/commands/game-start-plan.test.ts +84 -84
- package/src/commands/game.ts +1113 -1113
- package/src/commands/history-player.test.ts +102 -102
- package/src/commands/history.ts +573 -573
- package/src/commands/hub.test.ts +96 -96
- package/src/commands/hub.ts +234 -234
- package/src/commands/knowledge.test.ts +13 -13
- package/src/commands/knowledge.ts +139 -139
- package/src/commands/load.test.ts +51 -51
- package/src/commands/load.ts +13 -13
- package/src/commands/meeting-history.test.ts +106 -106
- package/src/commands/memory.ts +40 -40
- package/src/commands/peek.ts +45 -45
- package/src/commands/persona.ts +57 -57
- package/src/commands/setup/codex.ts +266 -266
- package/src/commands/skill.ts +128 -128
- package/src/commands/state.ts +46 -46
- package/src/commands/strategy.test.ts +153 -145
- package/src/commands/strategy.ts +183 -181
- package/src/commands/tts.ts +128 -128
- package/src/commands/upgrade.test.ts +82 -82
- package/src/commands/upgrade.ts +148 -148
- package/src/commands/watch.test.ts +999 -999
- package/src/commands/watch.ts +660 -660
- package/src/lib/auth.test.ts +86 -74
- package/src/lib/auth.ts +223 -186
- package/src/lib/command-meta.ts +37 -37
- package/src/lib/game-client.ts +403 -403
- package/src/lib/game-context.ts +92 -92
- package/src/lib/http-keepalive.ts +15 -15
- package/src/lib/http-transport.test.ts +42 -42
- package/src/lib/http-transport.ts +113 -113
- package/src/lib/hub-client.test.ts +56 -56
- package/src/lib/hub-client.ts +88 -88
- package/src/lib/hub-install.test.ts +98 -98
- package/src/lib/hub-install.ts +160 -121
- package/src/lib/hub-reminder.ts +78 -75
- package/src/lib/hub-unzip.test.ts +69 -69
- package/src/lib/hub-unzip.ts +62 -62
- package/src/lib/init-command.test.ts +75 -75
- package/src/lib/init-command.ts +130 -120
- package/src/lib/knowledge-store.test.ts +170 -170
- package/src/lib/knowledge-store.ts +369 -369
- package/src/lib/load-context.test.ts +52 -52
- package/src/lib/load-context.ts +52 -52
- package/src/lib/match-state.test.ts +134 -134
- package/src/lib/match-state.ts +94 -94
- package/src/lib/netease-tts.ts +83 -83
- package/src/lib/normalize.ts +42 -42
- package/src/lib/persona.test.ts +41 -41
- package/src/lib/persona.ts +72 -72
- package/src/lib/server-registry.ts +152 -152
- package/src/lib/skill-version.test.ts +48 -48
- package/src/lib/skill-version.ts +19 -19
- package/src/lib/strategy-export.test.ts +240 -232
- package/src/lib/strategy-export.ts +247 -242
- package/src/lib/tts-keys.ts +7 -7
- package/src/lib/tts-speech.test.ts +63 -63
- package/src/lib/tts-speech.ts +76 -76
- package/src/lib/user-data.test.ts +96 -0
- package/src/lib/user-data.ts +400 -0
- package/src/lib/workspace-argv.test.ts +49 -49
- package/src/lib/workspace-argv.ts +44 -44
- package/src/perception/player-history-store.test.ts +87 -87
- package/src/perception/player-history-store.ts +194 -194
- package/src/pipeline/event-format.test.ts +243 -243
- package/src/pipeline/event-format.ts +501 -501
- package/src/pipeline/event-hints.ts +195 -195
- package/src/pipeline/event-store.test.ts +28 -28
- package/src/pipeline/event-store.ts +193 -193
- package/src/pipeline/pipeline.ts +35 -35
- package/src/pipeline/player-projection.test.ts +168 -119
- package/src/pipeline/player-projection.ts +370 -380
- package/src/runtime/auto-upgrade.test.ts +66 -66
- package/src/runtime/auto-upgrade.ts +31 -31
- package/src/runtime/event-daemon.test.ts +209 -209
- package/src/runtime/event-daemon.ts +519 -519
- package/src/runtime/owner-control.ts +150 -150
- package/src/runtime/raw-ws-log.test.ts +33 -33
- package/src/runtime/raw-ws-log.ts +32 -32
- package/src/runtime/runtime-logger.ts +107 -107
- package/src/runtime/ws-client.test.ts +125 -125
- package/src/runtime/ws-client.ts +287 -287
- package/src/sdk/action.ts +166 -166
- package/src/sdk/index.ts +110 -110
- package/src/sdk/types.ts +161 -161
- package/src/strategies/avoid-lone.ts +12 -12
- package/src/strategies/avoid-players.knowledge.md +19 -19
- package/src/strategies/avoid-players.ts +16 -16
- package/src/strategies/corpse-patrol.ts +23 -23
- package/src/strategies/crab-sabotage.ts +22 -22
- package/src/strategies/custom-module.test.ts +270 -270
- package/src/strategies/find-player.ts +17 -17
- package/src/strategies/game-utils.test.ts +242 -242
- package/src/strategies/game-utils.ts +846 -846
- package/src/strategies/goals/anchor-linger.ts +77 -77
- package/src/strategies/goals/avoid-lone-top.ts +168 -168
- package/src/strategies/goals/avoid-players-top.test.ts +83 -83
- package/src/strategies/goals/avoid-players-top.ts +121 -121
- package/src/strategies/goals/conversation-goal.ts +51 -51
- package/src/strategies/goals/corpse-patrol-top.ts +113 -113
- package/src/strategies/goals/crab-octopus-reflexes.ts +101 -101
- package/src/strategies/goals/crab-sabotage-top.ts +197 -197
- package/src/strategies/goals/emergency-hunt-goal.ts +28 -28
- package/src/strategies/goals/find-player-top.ts +93 -93
- package/src/strategies/goals/flee-players-goal.ts +53 -53
- package/src/strategies/goals/follow-companion-goal.ts +106 -106
- package/src/strategies/goals/goal-manager.ts +41 -41
- package/src/strategies/goals/goal-root-strategy.ts +49 -49
- package/src/strategies/goals/goal.ts +28 -28
- package/src/strategies/goals/hide-top.ts +197 -197
- package/src/strategies/goals/keep-away-goal.ts +221 -221
- package/src/strategies/goals/kill-frenzy-top.ts +80 -80
- package/src/strategies/goals/kill-lone-top.ts +160 -160
- package/src/strategies/goals/kill-target-goal.ts +59 -59
- package/src/strategies/goals/kill-target-top.ts +109 -109
- package/src/strategies/goals/leaf-goal.ts +27 -27
- package/src/strategies/goals/linger-corpse-goal.ts +35 -35
- package/src/strategies/goals/lone-kill-core.ts +82 -82
- package/src/strategies/goals/lone-kill-goal.ts +24 -24
- package/src/strategies/goals/lone-kill-task-top.test.ts +85 -85
- package/src/strategies/goals/lone-kill-task-top.ts +133 -133
- package/src/strategies/goals/move-room-goal.ts +60 -60
- package/src/strategies/goals/normal-shrimp-top.test.ts +80 -80
- package/src/strategies/goals/normal-shrimp-top.ts +242 -242
- package/src/strategies/goals/paradise-fish-top.test.ts +126 -126
- package/src/strategies/goals/paradise-fish-top.ts +224 -224
- package/src/strategies/goals/patrol-top.ts +57 -57
- package/src/strategies/goals/report-patrol-top.ts +80 -80
- package/src/strategies/goals/safe-task-goal.ts +102 -102
- package/src/strategies/goals/social-task-top.ts +161 -161
- package/src/strategies/goals/task-kill-report-top.ts +163 -163
- package/src/strategies/goals/task-only-top.ts +57 -57
- package/src/strategies/goals/task-or-patrol-goal.ts +41 -41
- package/src/strategies/goals/task-report-top.ts +57 -57
- package/src/strategies/goals/wander-task-goal.ts +33 -33
- package/src/strategies/goals/warrior-shrimp-top.test.ts +87 -87
- package/src/strategies/goals/warrior-shrimp-top.ts +267 -267
- package/src/strategies/greeting.ts +53 -53
- package/src/strategies/hide-spots.ts +59 -59
- package/src/strategies/hide.ts +24 -24
- package/src/strategies/kill-frenzy.ts +13 -13
- package/src/strategies/kill-lone.knowledge.md +17 -17
- package/src/strategies/kill-lone.ts +14 -14
- package/src/strategies/kill-target.ts +19 -19
- package/src/strategies/loader.test.ts +678 -678
- package/src/strategies/loader.ts +181 -179
- package/src/strategies/lone-kill-task.ts +22 -22
- package/src/strategies/meeting-gate.test.ts +59 -59
- package/src/strategies/meeting-gate.ts +23 -23
- package/src/strategies/move-room.ts +16 -16
- package/src/strategies/new-events-backfill.ts +98 -98
- package/src/strategies/off-route-points.ts +105 -105
- package/src/strategies/paradise-fish.knowledge.md +19 -19
- package/src/strategies/paradise-fish.ts +26 -26
- package/src/strategies/pathfind/distance-field.ts +150 -150
- package/src/strategies/pathfind/escape-planner.test.ts +197 -197
- package/src/strategies/pathfind/escape-planner.ts +355 -355
- package/src/strategies/pathfind/walkable-grid.ts +117 -117
- package/src/strategies/patrol.ts +12 -12
- package/src/strategies/player-targets.ts +13 -13
- package/src/strategies/report-patrol.ts +12 -12
- package/src/strategies/shrimp-memory.knowledge.md +19 -19
- package/src/strategies/shrimp-memory.ts +26 -26
- package/src/strategies/social-task.test.ts +28 -28
- package/src/strategies/social-task.ts +50 -50
- package/src/strategies/spawn.ts +82 -82
- package/src/strategies/speech-module.ts +123 -123
- package/src/strategies/strategy-loop.test.ts +15 -15
- package/src/strategies/strategy-loop.ts +776 -776
- package/src/strategies/task-kill-report.ts +18 -18
- package/src/strategies/task-only.ts +12 -12
- package/src/strategies/task-report.ts +23 -23
- package/src/strategies/types.ts +109 -109
- package/src/strategies/warrior-memory.knowledge.md +21 -21
- package/src/strategies/warrior-memory.ts +17 -17
|
@@ -1,232 +1,240 @@
|
|
|
1
|
-
// src/lib/strategy-export.test.ts
|
|
2
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
3
|
-
import { existsSync, mkdirSync, writeFileSync, rmSync, readFileSync } from 'fs';
|
|
4
|
-
import { join } from 'path';
|
|
5
|
-
import { tmpdir } from 'os';
|
|
6
|
-
import {
|
|
7
|
-
rewriteBuiltinImports,
|
|
8
|
-
exportStrategy,
|
|
9
|
-
detectStaleOfficialStrategies,
|
|
10
|
-
readManifest,
|
|
11
|
-
} from './strategy-export.js';
|
|
12
|
-
|
|
13
|
-
describe('strategy-export', () => {
|
|
14
|
-
let testWorkspace: string;
|
|
15
|
-
let originalEnv: string | undefined;
|
|
16
|
-
|
|
17
|
-
beforeEach(() => {
|
|
18
|
-
testWorkspace = join(tmpdir(), `ccl-test-${Date.now()}`);
|
|
19
|
-
mkdirSync(testWorkspace, { recursive: true });
|
|
20
|
-
originalEnv = process.env.CLAWCLAW_WORKSPACE_DIR;
|
|
21
|
-
process.env.CLAWCLAW_WORKSPACE_DIR = testWorkspace;
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
afterEach(() => {
|
|
25
|
-
if (originalEnv !== undefined) {
|
|
26
|
-
process.env.CLAWCLAW_WORKSPACE_DIR = originalEnv;
|
|
27
|
-
} else {
|
|
28
|
-
delete process.env.CLAWCLAW_WORKSPACE_DIR;
|
|
29
|
-
}
|
|
30
|
-
rmSync(testWorkspace, { recursive: true, force: true });
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
expect(rewritten).
|
|
57
|
-
//
|
|
58
|
-
expect(rewritten).toContain("
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
it('should export a
|
|
84
|
-
await exportStrategy('
|
|
85
|
-
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
expect(
|
|
98
|
-
expect(
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
//
|
|
124
|
-
const {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
await exportStrategy('task-only');
|
|
157
|
-
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
it('should
|
|
180
|
-
expect(
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
1
|
+
// src/lib/strategy-export.test.ts
|
|
2
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
3
|
+
import { existsSync, mkdirSync, writeFileSync, rmSync, readFileSync } from 'fs';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
import { tmpdir } from 'os';
|
|
6
|
+
import {
|
|
7
|
+
rewriteBuiltinImports,
|
|
8
|
+
exportStrategy,
|
|
9
|
+
detectStaleOfficialStrategies,
|
|
10
|
+
readManifest,
|
|
11
|
+
} from './strategy-export.js';
|
|
12
|
+
|
|
13
|
+
describe('strategy-export', () => {
|
|
14
|
+
let testWorkspace: string;
|
|
15
|
+
let originalEnv: string | undefined;
|
|
16
|
+
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
testWorkspace = join(tmpdir(), `ccl-test-${Date.now()}`);
|
|
19
|
+
mkdirSync(testWorkspace, { recursive: true });
|
|
20
|
+
originalEnv = process.env.CLAWCLAW_WORKSPACE_DIR;
|
|
21
|
+
process.env.CLAWCLAW_WORKSPACE_DIR = testWorkspace;
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
afterEach(() => {
|
|
25
|
+
if (originalEnv !== undefined) {
|
|
26
|
+
process.env.CLAWCLAW_WORKSPACE_DIR = originalEnv;
|
|
27
|
+
} else {
|
|
28
|
+
delete process.env.CLAWCLAW_WORKSPACE_DIR;
|
|
29
|
+
}
|
|
30
|
+
rmSync(testWorkspace, { recursive: true, force: true });
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
function userStrategiesDir(): string {
|
|
34
|
+
return join(testWorkspace, 'user-data', 'strategies');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function officialManifestFile(): string {
|
|
38
|
+
return join(testWorkspace, 'runtime', 'official-strategies', 'manifest.json');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
describe('rewriteBuiltinImports', () => {
|
|
42
|
+
it('should rewrite ALL relative imports to @myclaw163/clawclaw-cli', () => {
|
|
43
|
+
const source = `
|
|
44
|
+
import type { GameState } from '../sdk/types.js';
|
|
45
|
+
import { Action } from '../sdk/action.js';
|
|
46
|
+
import { dist, killCommitRange } from './game-utils.js';
|
|
47
|
+
import { GoalRootStrategy } from './goals/goal-root-strategy.js';
|
|
48
|
+
import { TaskOnlyTop } from './goals/task-only-top.js';
|
|
49
|
+
import { parseGreetingArgs } from './greeting.js';
|
|
50
|
+
import { TTS_TEXT_MAX_LENGTH } from '../lib/tts-keys.js';
|
|
51
|
+
|
|
52
|
+
export const strategy = { id: 'test', description: 'test', create: () => ({ name: 'test', decide: () => [] }) };
|
|
53
|
+
`;
|
|
54
|
+
const rewritten = rewriteBuiltinImports(source, 'test', '0.6.31');
|
|
55
|
+
|
|
56
|
+
expect(rewritten).toContain('// Exported from test@0.6.31');
|
|
57
|
+
// All relative imports should be rewritten
|
|
58
|
+
expect(rewritten).not.toContain("'../sdk/types.js'");
|
|
59
|
+
expect(rewritten).not.toContain("'../sdk/action.js'");
|
|
60
|
+
expect(rewritten).not.toContain("'./game-utils.js'");
|
|
61
|
+
expect(rewritten).not.toContain("'./goals/goal-root-strategy.js'");
|
|
62
|
+
expect(rewritten).not.toContain("'./goals/task-only-top.js'");
|
|
63
|
+
expect(rewritten).not.toContain("'./greeting.js'");
|
|
64
|
+
expect(rewritten).not.toContain("'../lib/tts-keys.js'");
|
|
65
|
+
// Verify rewritten imports use @myclaw163/clawclaw-cli
|
|
66
|
+
expect(rewritten).toContain("from '@myclaw163/clawclaw-cli'");
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should preserve user imports from clawclaw-cli', () => {
|
|
70
|
+
const source = `
|
|
71
|
+
import { Action } from 'clawclaw-cli';
|
|
72
|
+
import type { GameState } from '../sdk/types.js';
|
|
73
|
+
|
|
74
|
+
export const strategy = { id: 'test', description: 'test', create: () => ({ name: 'test', decide: () => [] }) };
|
|
75
|
+
`;
|
|
76
|
+
const rewritten = rewriteBuiltinImports(source, 'test', '0.6.31');
|
|
77
|
+
expect(rewritten).toContain("import { Action } from 'clawclaw-cli'");
|
|
78
|
+
expect(rewritten).toContain("from 'clawclaw-cli'");
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe('exportStrategy', () => {
|
|
83
|
+
it('should export a formerly goal-based strategy with its knowledge sidecar', async () => {
|
|
84
|
+
await exportStrategy('kill-lone');
|
|
85
|
+
|
|
86
|
+
const strategiesDir = userStrategiesDir();
|
|
87
|
+
expect(existsSync(join(strategiesDir, 'kill-lone.ts'))).toBe(true);
|
|
88
|
+
expect(existsSync(join(strategiesDir, 'kill-lone.knowledge.md'))).toBe(true);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('should export a valid strategy', async () => {
|
|
92
|
+
await exportStrategy('task-only');
|
|
93
|
+
|
|
94
|
+
const strategyFile = join(userStrategiesDir(), 'task-only.ts');
|
|
95
|
+
const manifestFile = officialManifestFile();
|
|
96
|
+
|
|
97
|
+
expect(existsSync(strategyFile)).toBe(true);
|
|
98
|
+
expect(existsSync(manifestFile)).toBe(true);
|
|
99
|
+
|
|
100
|
+
const strategy = readFileSync(strategyFile, 'utf8');
|
|
101
|
+
expect(strategy).toContain('// Exported from task-only@');
|
|
102
|
+
expect(strategy).toContain("from '@myclaw163/clawclaw-cli'");
|
|
103
|
+
|
|
104
|
+
const manifest = readManifest();
|
|
105
|
+
expect(manifest['task-only']).toBeDefined();
|
|
106
|
+
expect(manifest['task-only'].sourceHash).toBeDefined();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('should reject strategy with unexported SDK symbol', async () => {
|
|
110
|
+
// Create a fake builtin strategy that imports a symbol not in the SDK
|
|
111
|
+
const fakeDir = join(testWorkspace, 'fake-builtin');
|
|
112
|
+
mkdirSync(fakeDir, { recursive: true });
|
|
113
|
+
const fakeFile = join(fakeDir, 'broken-strategy.ts');
|
|
114
|
+
writeFileSync(fakeFile, `
|
|
115
|
+
import { TotallyMissingSymbol } from 'clawclaw-cli';
|
|
116
|
+
export const strategy = {
|
|
117
|
+
id: 'broken-strategy',
|
|
118
|
+
description: 'test',
|
|
119
|
+
create: () => ({ name: 'broken', decide: () => [TotallyMissingSymbol] }),
|
|
120
|
+
};
|
|
121
|
+
`, 'utf8');
|
|
122
|
+
|
|
123
|
+
// Temporarily override locateBuiltinSource to point to our fake
|
|
124
|
+
const { rewriteBuiltinImports } = await import('./strategy-export.js');
|
|
125
|
+
const rewritten = rewriteBuiltinImports(
|
|
126
|
+
readFileSync(fakeFile, 'utf8'),
|
|
127
|
+
'broken-strategy',
|
|
128
|
+
'0.0.0'
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
// Validate should catch the missing symbol
|
|
132
|
+
const { validateExportable } = await import('./strategy-export.js') as any;
|
|
133
|
+
if (typeof validateExportable === 'function') {
|
|
134
|
+
const result = await validateExportable(rewritten);
|
|
135
|
+
expect(result.ok).toBe(false);
|
|
136
|
+
expect(result.error).toBeDefined();
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('should refuse to overwrite untracked files without --force', async () => {
|
|
141
|
+
const strategiesDir = userStrategiesDir();
|
|
142
|
+
mkdirSync(strategiesDir, { recursive: true });
|
|
143
|
+
writeFileSync(join(strategiesDir, 'task-only.ts'), '// user file', 'utf8');
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
await expect(exportStrategy('task-only')).rejects.toThrow(
|
|
147
|
+
'already exists and is not tracked. Use --force to overwrite.'
|
|
148
|
+
);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('should overwrite untracked files with --force', async () => {
|
|
152
|
+
const strategiesDir = userStrategiesDir();
|
|
153
|
+
mkdirSync(strategiesDir, { recursive: true });
|
|
154
|
+
writeFileSync(join(strategiesDir, 'task-only.ts'), '// user file', 'utf8');
|
|
155
|
+
|
|
156
|
+
await exportStrategy('task-only', { force: true });
|
|
157
|
+
|
|
158
|
+
const strategy = readFileSync(join(strategiesDir, 'task-only.ts'), 'utf8');
|
|
159
|
+
expect(strategy).toContain('// Exported from task-only@');
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('should allow re-exporting tracked strategies without --force', async () => {
|
|
163
|
+
// First export
|
|
164
|
+
await exportStrategy('task-only');
|
|
165
|
+
|
|
166
|
+
const strategiesDir = userStrategiesDir();
|
|
167
|
+
const strategyFile = join(strategiesDir, 'task-only.ts');
|
|
168
|
+
|
|
169
|
+
// Modify the file
|
|
170
|
+
writeFileSync(strategyFile, '// user customized', 'utf8');
|
|
171
|
+
|
|
172
|
+
// Re-export without --force (should succeed because it's tracked)
|
|
173
|
+
await exportStrategy('task-only');
|
|
174
|
+
|
|
175
|
+
const strategy = readFileSync(strategyFile, 'utf8');
|
|
176
|
+
expect(strategy).toContain('// Exported from task-only@');
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('should reject non-existent strategies', async () => {
|
|
180
|
+
await expect(exportStrategy('non-existent-strategy')).rejects.toThrow(
|
|
181
|
+
"Built-in strategy 'non-existent-strategy' not found."
|
|
182
|
+
);
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
describe('detectStaleOfficialStrategies', () => {
|
|
187
|
+
it('should return empty array when no strategies are exported', () => {
|
|
188
|
+
expect(detectStaleOfficialStrategies()).toEqual([]);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('should return empty array when all strategies are up to date', async () => {
|
|
192
|
+
await exportStrategy('task-only');
|
|
193
|
+
expect(detectStaleOfficialStrategies()).toEqual([]);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('should return stale IDs when source hash mismatch', async () => {
|
|
197
|
+
await exportStrategy('task-only');
|
|
198
|
+
|
|
199
|
+
// Manually modify the manifest to simulate a stale hash
|
|
200
|
+
const manifestFile = officialManifestFile();
|
|
201
|
+
const manifest = JSON.parse(readFileSync(manifestFile, 'utf8'));
|
|
202
|
+
manifest['task-only'].sourceHash = 'stale-hash-value';
|
|
203
|
+
writeFileSync(manifestFile, JSON.stringify(manifest, null, 2), 'utf8');
|
|
204
|
+
|
|
205
|
+
expect(detectStaleOfficialStrategies()).toEqual(['task-only']);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('should return multiple stale IDs', async () => {
|
|
209
|
+
await exportStrategy('task-only');
|
|
210
|
+
await exportStrategy('kill-lone');
|
|
211
|
+
|
|
212
|
+
// Make both stale
|
|
213
|
+
const manifestFile = officialManifestFile();
|
|
214
|
+
const manifest = JSON.parse(readFileSync(manifestFile, 'utf8'));
|
|
215
|
+
manifest['task-only'].sourceHash = 'stale-1';
|
|
216
|
+
manifest['kill-lone'].sourceHash = 'stale-2';
|
|
217
|
+
writeFileSync(manifestFile, JSON.stringify(manifest, null, 2), 'utf8');
|
|
218
|
+
|
|
219
|
+
const stale = detectStaleOfficialStrategies();
|
|
220
|
+
expect(stale).toContain('task-only');
|
|
221
|
+
expect(stale).toContain('kill-lone');
|
|
222
|
+
expect(stale).toHaveLength(2);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('should detect stale when sidecar hash changes', async () => {
|
|
226
|
+
await exportStrategy('kill-lone');
|
|
227
|
+
|
|
228
|
+
// Verify kill-lone has a sidecar hash (it has a .knowledge.md)
|
|
229
|
+
const manifestFile = officialManifestFile();
|
|
230
|
+
const manifest = JSON.parse(readFileSync(manifestFile, 'utf8'));
|
|
231
|
+
expect(manifest['kill-lone'].sidecarHash).toBeDefined();
|
|
232
|
+
|
|
233
|
+
// Make sidecar stale
|
|
234
|
+
manifest['kill-lone'].sidecarHash = 'stale-sidecar';
|
|
235
|
+
writeFileSync(manifestFile, JSON.stringify(manifest, null, 2), 'utf8');
|
|
236
|
+
|
|
237
|
+
expect(detectStaleOfficialStrategies()).toEqual(['kill-lone']);
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
});
|