create-walle 0.9.3 → 0.9.4
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 +2 -1
- package/package.json +1 -1
- package/template/claude-task-manager/db.js +5 -1
- package/template/claude-task-manager/public/css/walle.css +317 -0
- package/template/claude-task-manager/public/index.html +404 -101
- package/template/claude-task-manager/public/js/walle.js +1256 -86
- package/template/claude-task-manager/server.js +189 -14
- package/template/docs/site/api/README.md +146 -0
- package/template/docs/site/skills/README.md +99 -5
- package/template/package.json +1 -1
- package/template/wall-e/agent.js +54 -0
- package/template/wall-e/api-walle.js +452 -3
- package/template/wall-e/brain.js +45 -1
- package/template/wall-e/channels/telegram-channel.js +96 -0
- package/template/wall-e/chat.js +61 -2
- package/template/wall-e/coding-context.js +252 -0
- package/template/wall-e/coding-orchestrator.js +625 -0
- package/template/wall-e/coding-review.js +189 -0
- package/template/wall-e/core-tasks.js +12 -3
- package/template/wall-e/deploy.sh +4 -4
- package/template/wall-e/fly.toml +2 -2
- package/template/wall-e/package.json +4 -1
- package/template/wall-e/skills/_bundled/coding-agent/SKILL.md +17 -0
- package/template/wall-e/skills/_bundled/coding-agent/run.js +142 -0
- package/template/wall-e/skills/_bundled/email-sync/SKILL.md +12 -7
- package/template/wall-e/skills/_bundled/email-sync/mail-reader.jxa +76 -46
- package/template/wall-e/skills/_bundled/email-sync/run.js +42 -2
- package/template/wall-e/skills/_bundled/glean-team-sync/SKILL.md +57 -0
- package/template/wall-e/skills/_bundled/glean-team-sync/run.js +254 -0
- package/template/wall-e/skills/_bundled/slack-mentions/SKILL.md +1 -1
- package/template/wall-e/skills/_bundled/slack-mentions/run.js +268 -121
- package/template/wall-e/skills/_templates/data-fetcher.md +27 -0
- package/template/wall-e/skills/_templates/manual-action.md +19 -0
- package/template/wall-e/skills/_templates/periodic-checker.md +29 -0
- package/template/wall-e/skills/_templates/script-runner.md +21 -0
- package/template/wall-e/skills/claude-code-reader.js +16 -4
- package/template/wall-e/skills/skill-executor.js +23 -1
- package/template/wall-e/skills/skill-validator.js +73 -0
- package/template/wall-e/tests/brain.test.js +3 -3
- package/template/wall-e/tests/coding-agent-integration.test.js +240 -0
- package/template/wall-e/tests/coding-context.test.js +212 -0
- package/template/wall-e/tests/coding-orchestrator.test.js +303 -0
- package/template/wall-e/tests/coding-review.test.js +141 -0
- package/template/claude-task-manager/package-lock.json +0 -1607
- package/template/claude-task-manager/tests/test-ai-search.js +0 -61
- package/template/claude-task-manager/tests/test-editor-ux.js +0 -76
- package/template/claude-task-manager/tests/test-editor-ux2.js +0 -51
- package/template/claude-task-manager/tests/test-features-v2.js +0 -127
- package/template/claude-task-manager/tests/test-insights-cached.js +0 -78
- package/template/claude-task-manager/tests/test-insights.js +0 -124
- package/template/claude-task-manager/tests/test-permissions-v2.js +0 -127
- package/template/claude-task-manager/tests/test-permissions.js +0 -122
- package/template/claude-task-manager/tests/test-pin.js +0 -51
- package/template/claude-task-manager/tests/test-prompts.js +0 -164
- package/template/claude-task-manager/tests/test-recent-sessions.js +0 -96
- package/template/claude-task-manager/tests/test-review.js +0 -104
- package/template/claude-task-manager/tests/test-send-dropdown.js +0 -76
- package/template/claude-task-manager/tests/test-send-final.js +0 -30
- package/template/claude-task-manager/tests/test-send-fixes.js +0 -76
- package/template/claude-task-manager/tests/test-send-integration.js +0 -107
- package/template/claude-task-manager/tests/test-send-visual.js +0 -34
- package/template/claude-task-manager/tests/test-session-create.js +0 -147
- package/template/claude-task-manager/tests/test-sidebar-ux.js +0 -83
- package/template/claude-task-manager/tests/test-url-hash.js +0 -68
- package/template/claude-task-manager/tests/test-ux-crop.js +0 -34
- package/template/claude-task-manager/tests/test-ux-review.js +0 -130
- package/template/claude-task-manager/tests/test-zoom-card.js +0 -76
- package/template/claude-task-manager/tests/test-zoom.js +0 -92
- package/template/claude-task-manager/tests/test-zoom2.js +0 -67
- package/template/docs/openclaw-vs-walle-comparison.md +0 -103
- package/template/docs/ux-improvement-plan.md +0 -84
- package/template/wall-e/docs/specs/2026-04-01-publish-plan.md +0 -112
- package/template/wall-e/docs/specs/SKILL-FORMAT.md +0 -326
- package/template/wall-e/package-lock.json +0 -533
- package/template/wall-e/skills/_bundled/slack-mentions/.watermark.json +0 -4
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { describe, it, before, after } = require('node:test');
|
|
3
|
+
const assert = require('node:assert/strict');
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
const os = require('node:os');
|
|
7
|
+
|
|
8
|
+
const {
|
|
9
|
+
getDefaultConfig,
|
|
10
|
+
buildPlanningPrompt,
|
|
11
|
+
parsePlan,
|
|
12
|
+
buildSubtaskPrompt,
|
|
13
|
+
writeCheckpoint,
|
|
14
|
+
readCheckpoint,
|
|
15
|
+
formatReport,
|
|
16
|
+
} = require('../coding-orchestrator');
|
|
17
|
+
|
|
18
|
+
describe('coding-orchestrator', () => {
|
|
19
|
+
let tmpDir;
|
|
20
|
+
|
|
21
|
+
before(() => {
|
|
22
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'coding-orch-'));
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
after(() => {
|
|
26
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe('getDefaultConfig', () => {
|
|
30
|
+
it('returns object with all expected keys and correct defaults', () => {
|
|
31
|
+
const cfg = getDefaultConfig();
|
|
32
|
+
assert.equal(cfg.delivery, 'commit');
|
|
33
|
+
assert.equal(cfg.max_retries, 3);
|
|
34
|
+
assert.equal(cfg.max_subtasks, 15);
|
|
35
|
+
assert.equal(cfg.guardrail_threshold, 10);
|
|
36
|
+
assert.equal(cfg.subtask_timeout_ms, 300000);
|
|
37
|
+
assert.equal(cfg.subtask_budget_usd, 0.50);
|
|
38
|
+
assert.equal(cfg.total_budget_usd, 5.00);
|
|
39
|
+
assert.equal(cfg.test_command, 'auto');
|
|
40
|
+
assert.equal(cfg.review, true);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('returns a new object each time', () => {
|
|
44
|
+
const a = getDefaultConfig();
|
|
45
|
+
const b = getDefaultConfig();
|
|
46
|
+
assert.notStrictEqual(a, b);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe('buildPlanningPrompt', () => {
|
|
51
|
+
it('includes request text', () => {
|
|
52
|
+
const prompt = buildPlanningPrompt('Add user authentication', {
|
|
53
|
+
claudeMd: '# Rules',
|
|
54
|
+
fileTree: 'src/\nsrc/index.js',
|
|
55
|
+
gitLog: 'abc123 initial commit',
|
|
56
|
+
relevantFiles: { 'src/index.js': 'console.log("hi")' },
|
|
57
|
+
brainMemories: [{ content: 'User prefers TypeScript' }],
|
|
58
|
+
mcpResults: [{ tool: 'search', result: 'spec doc' }],
|
|
59
|
+
testCommand: 'npm test',
|
|
60
|
+
});
|
|
61
|
+
assert.ok(prompt.includes('Add user authentication'));
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('includes CLAUDE.md conventions section', () => {
|
|
65
|
+
const prompt = buildPlanningPrompt('Fix bug', {
|
|
66
|
+
claudeMd: '# My Conventions\nUse strict mode.',
|
|
67
|
+
fileTree: '',
|
|
68
|
+
gitLog: '',
|
|
69
|
+
relevantFiles: {},
|
|
70
|
+
brainMemories: [],
|
|
71
|
+
mcpResults: [],
|
|
72
|
+
testCommand: null,
|
|
73
|
+
});
|
|
74
|
+
assert.ok(prompt.includes('My Conventions'));
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('includes file tree section', () => {
|
|
78
|
+
const prompt = buildPlanningPrompt('Fix bug', {
|
|
79
|
+
claudeMd: '',
|
|
80
|
+
fileTree: 'src/\nsrc/app.js',
|
|
81
|
+
gitLog: '',
|
|
82
|
+
relevantFiles: {},
|
|
83
|
+
brainMemories: [],
|
|
84
|
+
mcpResults: [],
|
|
85
|
+
testCommand: null,
|
|
86
|
+
});
|
|
87
|
+
assert.ok(prompt.includes('src/app.js'));
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('includes JSON output instruction', () => {
|
|
91
|
+
const prompt = buildPlanningPrompt('Fix bug', {
|
|
92
|
+
claudeMd: '',
|
|
93
|
+
fileTree: '',
|
|
94
|
+
gitLog: '',
|
|
95
|
+
relevantFiles: {},
|
|
96
|
+
brainMemories: [],
|
|
97
|
+
mcpResults: [],
|
|
98
|
+
testCommand: null,
|
|
99
|
+
});
|
|
100
|
+
assert.ok(prompt.includes('subtasks'));
|
|
101
|
+
assert.ok(prompt.includes('JSON'));
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('includes recent commits when present', () => {
|
|
105
|
+
const prompt = buildPlanningPrompt('Fix bug', {
|
|
106
|
+
claudeMd: '',
|
|
107
|
+
fileTree: '',
|
|
108
|
+
gitLog: 'abc123 feat: added login\ndef456 fix: typo',
|
|
109
|
+
relevantFiles: {},
|
|
110
|
+
brainMemories: [],
|
|
111
|
+
mcpResults: [],
|
|
112
|
+
testCommand: null,
|
|
113
|
+
});
|
|
114
|
+
assert.ok(prompt.includes('abc123 feat: added login'));
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('includes test command when present', () => {
|
|
118
|
+
const prompt = buildPlanningPrompt('Fix bug', {
|
|
119
|
+
claudeMd: '',
|
|
120
|
+
fileTree: '',
|
|
121
|
+
gitLog: '',
|
|
122
|
+
relevantFiles: {},
|
|
123
|
+
brainMemories: [],
|
|
124
|
+
mcpResults: [],
|
|
125
|
+
testCommand: 'npm test',
|
|
126
|
+
});
|
|
127
|
+
assert.ok(prompt.includes('npm test'));
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('respects options for max_subtasks', () => {
|
|
131
|
+
const prompt = buildPlanningPrompt('Fix bug', {
|
|
132
|
+
claudeMd: '',
|
|
133
|
+
fileTree: '',
|
|
134
|
+
gitLog: '',
|
|
135
|
+
relevantFiles: {},
|
|
136
|
+
brainMemories: [],
|
|
137
|
+
mcpResults: [],
|
|
138
|
+
testCommand: null,
|
|
139
|
+
}, { max_subtasks: 5 });
|
|
140
|
+
assert.ok(prompt.includes('5'));
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
describe('parsePlan', () => {
|
|
145
|
+
it('parses valid plan JSON from text', () => {
|
|
146
|
+
const output = `Here is the plan:
|
|
147
|
+
\`\`\`json
|
|
148
|
+
{
|
|
149
|
+
"subtasks": [
|
|
150
|
+
{ "title": "Create auth module", "prompt": "Create src/auth.js with login/logout" },
|
|
151
|
+
{ "title": "Add tests", "prompt": "Write tests for auth module" }
|
|
152
|
+
],
|
|
153
|
+
"branch_name": "feat/auth",
|
|
154
|
+
"estimated_scope": "medium"
|
|
155
|
+
}
|
|
156
|
+
\`\`\`
|
|
157
|
+
That's the plan.`;
|
|
158
|
+
const plan = parsePlan(output);
|
|
159
|
+
assert.equal(plan.subtasks.length, 2);
|
|
160
|
+
assert.equal(plan.subtasks[0].title, 'Create auth module');
|
|
161
|
+
assert.equal(plan.branch_name, 'feat/auth');
|
|
162
|
+
assert.equal(plan.estimated_scope, 'medium');
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('parses plan without code fences', () => {
|
|
166
|
+
const output = `{"subtasks": [{"title": "Fix bug", "prompt": "Fix the null check"}], "branch_name": "fix/null", "estimated_scope": "small"}`;
|
|
167
|
+
const plan = parsePlan(output);
|
|
168
|
+
assert.equal(plan.subtasks.length, 1);
|
|
169
|
+
assert.equal(plan.branch_name, 'fix/null');
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('throws on output with no JSON', () => {
|
|
173
|
+
assert.throws(() => parsePlan('No JSON here at all'), /Failed to parse plan/);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('throws on empty subtasks array', () => {
|
|
177
|
+
assert.throws(
|
|
178
|
+
() => parsePlan('{"subtasks": [], "branch_name": "x"}'),
|
|
179
|
+
/Failed to parse plan/,
|
|
180
|
+
);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('throws on missing subtasks key', () => {
|
|
184
|
+
assert.throws(
|
|
185
|
+
() => parsePlan('{"branch_name": "x", "tasks": []}'),
|
|
186
|
+
/Failed to parse plan/,
|
|
187
|
+
);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('throws on empty input', () => {
|
|
191
|
+
assert.throws(() => parsePlan(''), /Failed to parse plan/);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
describe('buildSubtaskPrompt', () => {
|
|
196
|
+
it('includes subtask title and prompt', () => {
|
|
197
|
+
const prompt = buildSubtaskPrompt(
|
|
198
|
+
{ title: 'Create auth module', prompt: 'Build src/auth.js' },
|
|
199
|
+
'Previous step created src/utils.js',
|
|
200
|
+
'# Rules\nUse strict.',
|
|
201
|
+
);
|
|
202
|
+
assert.ok(prompt.includes('Create auth module'));
|
|
203
|
+
assert.ok(prompt.includes('Build src/auth.js'));
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('includes cumulative context', () => {
|
|
207
|
+
const prompt = buildSubtaskPrompt(
|
|
208
|
+
{ title: 'Step 2', prompt: 'Do something' },
|
|
209
|
+
'Step 1 created foo.js and bar.js',
|
|
210
|
+
'',
|
|
211
|
+
);
|
|
212
|
+
assert.ok(prompt.includes('Step 1 created foo.js and bar.js'));
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('includes CLAUDE.md conventions', () => {
|
|
216
|
+
const prompt = buildSubtaskPrompt(
|
|
217
|
+
{ title: 'Step 1', prompt: 'Do something' },
|
|
218
|
+
'',
|
|
219
|
+
'# Conventions\nAlways use ESM imports.',
|
|
220
|
+
);
|
|
221
|
+
assert.ok(prompt.includes('Always use ESM imports'));
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('handles empty cumulative context', () => {
|
|
225
|
+
const prompt = buildSubtaskPrompt(
|
|
226
|
+
{ title: 'Step 1', prompt: 'First task' },
|
|
227
|
+
'',
|
|
228
|
+
'# Rules',
|
|
229
|
+
);
|
|
230
|
+
assert.ok(prompt.includes('First task'));
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
describe('writeCheckpoint / readCheckpoint', () => {
|
|
235
|
+
it('round-trips state through file', () => {
|
|
236
|
+
const filePath = path.join(tmpDir, 'checkpoint.json');
|
|
237
|
+
const state = {
|
|
238
|
+
plan: { subtasks: [{ title: 'A' }] },
|
|
239
|
+
completed: 1,
|
|
240
|
+
session_ids: ['abc-123'],
|
|
241
|
+
budget_used: 0.50,
|
|
242
|
+
};
|
|
243
|
+
writeCheckpoint(filePath, state);
|
|
244
|
+
const loaded = readCheckpoint(filePath);
|
|
245
|
+
assert.deepStrictEqual(loaded, state);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it('overwrites existing checkpoint', () => {
|
|
249
|
+
const filePath = path.join(tmpDir, 'checkpoint2.json');
|
|
250
|
+
writeCheckpoint(filePath, { step: 1 });
|
|
251
|
+
writeCheckpoint(filePath, { step: 2 });
|
|
252
|
+
const loaded = readCheckpoint(filePath);
|
|
253
|
+
assert.equal(loaded.step, 2);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it('readCheckpoint throws on missing file', () => {
|
|
257
|
+
assert.throws(() => readCheckpoint(path.join(tmpDir, 'nonexistent.json')));
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
describe('formatReport', () => {
|
|
262
|
+
it('formats report with status, branch, commit', () => {
|
|
263
|
+
const text = formatReport({
|
|
264
|
+
success: true,
|
|
265
|
+
branch: 'feat/auth',
|
|
266
|
+
commit: 'abc1234',
|
|
267
|
+
concerns: [],
|
|
268
|
+
});
|
|
269
|
+
assert.ok(text.includes('feat/auth'));
|
|
270
|
+
assert.ok(text.includes('abc1234'));
|
|
271
|
+
assert.ok(/success/i.test(text));
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it('formats report with concerns', () => {
|
|
275
|
+
const text = formatReport({
|
|
276
|
+
success: true,
|
|
277
|
+
branch: 'feat/x',
|
|
278
|
+
commit: 'def5678',
|
|
279
|
+
concerns: ['Missing error handling', 'No integration tests'],
|
|
280
|
+
});
|
|
281
|
+
assert.ok(text.includes('Missing error handling'));
|
|
282
|
+
assert.ok(text.includes('No integration tests'));
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it('formats failed report', () => {
|
|
286
|
+
const text = formatReport({
|
|
287
|
+
success: false,
|
|
288
|
+
branch: 'fix/bug',
|
|
289
|
+
commit: null,
|
|
290
|
+
concerns: ['Build failed'],
|
|
291
|
+
error: 'Tests did not pass after 3 retries',
|
|
292
|
+
});
|
|
293
|
+
assert.ok(/fail/i.test(text));
|
|
294
|
+
assert.ok(text.includes('Tests did not pass'));
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('handles missing optional fields', () => {
|
|
298
|
+
const text = formatReport({ success: true });
|
|
299
|
+
assert.ok(/success/i.test(text));
|
|
300
|
+
assert.ok(typeof text === 'string');
|
|
301
|
+
});
|
|
302
|
+
});
|
|
303
|
+
});
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { describe, it } = require('node:test');
|
|
3
|
+
const assert = require('node:assert/strict');
|
|
4
|
+
|
|
5
|
+
const {
|
|
6
|
+
buildSubtaskReviewPrompt,
|
|
7
|
+
buildFinalReviewPrompt,
|
|
8
|
+
parseVerdict,
|
|
9
|
+
} = require('../coding-review');
|
|
10
|
+
|
|
11
|
+
describe('coding-review', () => {
|
|
12
|
+
describe('buildSubtaskReviewPrompt', () => {
|
|
13
|
+
it('includes subtask title and prompt in the output', () => {
|
|
14
|
+
const subtask = { title: 'Add login endpoint', prompt: 'Create POST /login that validates credentials' };
|
|
15
|
+
const diff = '+ app.post("/login", handler);';
|
|
16
|
+
const result = buildSubtaskReviewPrompt(subtask, diff);
|
|
17
|
+
|
|
18
|
+
assert.ok(result.includes('Add login endpoint'), 'should include subtask title');
|
|
19
|
+
assert.ok(result.includes('Create POST /login that validates credentials'), 'should include subtask prompt');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('includes the diff content', () => {
|
|
23
|
+
const subtask = { title: 'Fix bug', prompt: 'Fix the null check' };
|
|
24
|
+
const diff = '- if (x) {\n+ if (x != null) {';
|
|
25
|
+
const result = buildSubtaskReviewPrompt(subtask, diff);
|
|
26
|
+
|
|
27
|
+
assert.ok(result.includes(diff), 'should include the diff');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('asks for JSON reply', () => {
|
|
31
|
+
const subtask = { title: 'Task', prompt: 'Do something' };
|
|
32
|
+
const diff = '+ code';
|
|
33
|
+
const result = buildSubtaskReviewPrompt(subtask, diff);
|
|
34
|
+
|
|
35
|
+
assert.ok(result.includes('Reply with JSON'), 'should ask for JSON reply');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('mentions pass, issues, and severity in expected output format', () => {
|
|
39
|
+
const subtask = { title: 'Task', prompt: 'Do something' };
|
|
40
|
+
const diff = '+ code';
|
|
41
|
+
const result = buildSubtaskReviewPrompt(subtask, diff);
|
|
42
|
+
|
|
43
|
+
assert.ok(result.includes('pass'), 'should mention pass field');
|
|
44
|
+
assert.ok(result.includes('issues'), 'should mention issues field');
|
|
45
|
+
assert.ok(result.includes('severity'), 'should mention severity field');
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe('buildFinalReviewPrompt', () => {
|
|
50
|
+
it('includes the original request', () => {
|
|
51
|
+
const request = 'Build a REST API for user management';
|
|
52
|
+
const diff = '+ const express = require("express");';
|
|
53
|
+
const result = buildFinalReviewPrompt(request, diff);
|
|
54
|
+
|
|
55
|
+
assert.ok(result.includes(request), 'should include the original request');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('includes the diff', () => {
|
|
59
|
+
const request = 'Build something';
|
|
60
|
+
const diff = '+ new code line\n- old code line';
|
|
61
|
+
const result = buildFinalReviewPrompt(request, diff);
|
|
62
|
+
|
|
63
|
+
assert.ok(result.includes(diff), 'should include the diff');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('mentions cross-file consistency', () => {
|
|
67
|
+
const result = buildFinalReviewPrompt('request', 'diff');
|
|
68
|
+
|
|
69
|
+
assert.ok(
|
|
70
|
+
result.toLowerCase().includes('cross-file consistency'),
|
|
71
|
+
'should mention cross-file consistency'
|
|
72
|
+
);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('asks for JSON reply with summary field', () => {
|
|
76
|
+
const result = buildFinalReviewPrompt('request', 'diff');
|
|
77
|
+
|
|
78
|
+
assert.ok(result.includes('Reply with JSON'), 'should ask for JSON reply');
|
|
79
|
+
assert.ok(result.includes('summary'), 'should mention summary field');
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
describe('parseVerdict', () => {
|
|
84
|
+
it('parses valid JSON from mixed text', () => {
|
|
85
|
+
const text = 'Here is my review:\n\n```json\n{"pass": true, "issues": [], "severity": "minor"}\n```\nDone.';
|
|
86
|
+
const result = parseVerdict(text);
|
|
87
|
+
|
|
88
|
+
assert.strictEqual(result.pass, true);
|
|
89
|
+
assert.deepStrictEqual(result.issues, []);
|
|
90
|
+
assert.strictEqual(result.severity, 'minor');
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('parses JSON without code fences', () => {
|
|
94
|
+
const text = 'The code looks good. {"pass": true, "issues": [], "severity": "minor"} That is all.';
|
|
95
|
+
const result = parseVerdict(text);
|
|
96
|
+
|
|
97
|
+
assert.strictEqual(result.pass, true);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('parses a failing verdict with issues', () => {
|
|
101
|
+
const text = '{"pass": false, "issues": ["Missing input validation", "SQL injection risk"], "severity": "blocker"}';
|
|
102
|
+
const result = parseVerdict(text);
|
|
103
|
+
|
|
104
|
+
assert.strictEqual(result.pass, false);
|
|
105
|
+
assert.strictEqual(result.issues.length, 2);
|
|
106
|
+
assert.strictEqual(result.severity, 'blocker');
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('parses verdict with summary field', () => {
|
|
110
|
+
const text = '{"pass": true, "issues": [], "severity": "minor", "summary": "All good"}';
|
|
111
|
+
const result = parseVerdict(text);
|
|
112
|
+
|
|
113
|
+
assert.strictEqual(result.summary, 'All good');
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('returns blocker on unparseable text', () => {
|
|
117
|
+
const result = parseVerdict('No JSON here at all');
|
|
118
|
+
|
|
119
|
+
assert.strictEqual(result.pass, false);
|
|
120
|
+
assert.strictEqual(result.severity, 'blocker');
|
|
121
|
+
assert.ok(result.issues.length > 0, 'should have at least one issue');
|
|
122
|
+
assert.ok(result.issues[0].includes('parse'), 'should mention parse failure');
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('returns blocker on empty input', () => {
|
|
126
|
+
const result = parseVerdict('');
|
|
127
|
+
|
|
128
|
+
assert.strictEqual(result.pass, false);
|
|
129
|
+
assert.strictEqual(result.severity, 'blocker');
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('ignores JSON objects without a pass field', () => {
|
|
133
|
+
const text = '{"foo": "bar"} and then {"pass": false, "issues": ["bug"], "severity": "major"}';
|
|
134
|
+
const result = parseVerdict(text);
|
|
135
|
+
|
|
136
|
+
assert.strictEqual(result.pass, false);
|
|
137
|
+
assert.deepStrictEqual(result.issues, ['bug']);
|
|
138
|
+
assert.strictEqual(result.severity, 'major');
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
});
|