@codemcp/workflows 4.7.0 → 4.9.0
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/.turbo/turbo-build.log +1 -1
- package/dist/components/beads/beads-instruction-generator.d.ts +48 -0
- package/dist/components/beads/beads-instruction-generator.d.ts.map +1 -0
- package/dist/components/beads/beads-instruction-generator.js +182 -0
- package/dist/components/beads/beads-instruction-generator.js.map +1 -0
- package/dist/components/beads/beads-plan-manager.d.ts +66 -0
- package/dist/components/beads/beads-plan-manager.d.ts.map +1 -0
- package/dist/components/beads/beads-plan-manager.js +288 -0
- package/dist/components/beads/beads-plan-manager.js.map +1 -0
- package/dist/components/beads/beads-task-backend-client.d.ts +43 -0
- package/dist/components/beads/beads-task-backend-client.d.ts.map +1 -0
- package/dist/components/beads/beads-task-backend-client.js +178 -0
- package/dist/components/beads/beads-task-backend-client.js.map +1 -0
- package/dist/components/server-components-factory.d.ts +39 -0
- package/dist/components/server-components-factory.d.ts.map +1 -0
- package/dist/components/server-components-factory.js +62 -0
- package/dist/components/server-components-factory.js.map +1 -0
- package/dist/server-config.d.ts.map +1 -1
- package/dist/server-config.js +8 -4
- package/dist/server-config.js.map +1 -1
- package/dist/server-implementation.d.ts +1 -1
- package/dist/tool-handlers/get-tool-info.d.ts.map +1 -1
- package/dist/tool-handlers/get-tool-info.js +2 -1
- package/dist/tool-handlers/get-tool-info.js.map +1 -1
- package/dist/tool-handlers/proceed-to-phase.d.ts +5 -0
- package/dist/tool-handlers/proceed-to-phase.d.ts.map +1 -1
- package/dist/tool-handlers/proceed-to-phase.js +95 -0
- package/dist/tool-handlers/proceed-to-phase.js.map +1 -1
- package/dist/tool-handlers/start-development.d.ts.map +1 -1
- package/dist/tool-handlers/start-development.js +7 -3
- package/dist/tool-handlers/start-development.js.map +1 -1
- package/dist/tool-handlers/whats-next.d.ts +0 -12
- package/dist/tool-handlers/whats-next.d.ts.map +1 -1
- package/dist/tool-handlers/whats-next.js +1 -88
- package/dist/tool-handlers/whats-next.js.map +1 -1
- package/dist/types.d.ts +7 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/version-info.d.ts +30 -0
- package/dist/version-info.d.ts.map +1 -0
- package/dist/version-info.js +178 -0
- package/dist/version-info.js.map +1 -0
- package/package.json +2 -2
- package/src/components/beads/beads-instruction-generator.ts +261 -0
- package/src/components/beads/beads-plan-manager.ts +358 -0
- package/src/components/beads/beads-task-backend-client.ts +232 -0
- package/src/components/server-components-factory.ts +86 -0
- package/src/server-config.ts +9 -4
- package/src/tool-handlers/get-tool-info.ts +2 -1
- package/src/tool-handlers/proceed-to-phase.ts +140 -0
- package/src/tool-handlers/start-development.ts +14 -3
- package/src/tool-handlers/whats-next.ts +4 -117
- package/src/types.ts +7 -4
- package/src/version-info.ts +213 -0
- package/test/e2e/component-substitution.test.ts +208 -0
- package/test/unit/beads-instruction-generator.test.ts +847 -0
- package/test/unit/beads-phase-task-id-integration.test.ts +557 -0
- package/test/unit/server-components-factory.test.ts +279 -0
- package/test/unit/setup-project-docs-handler.test.ts +3 -2
- package/test/utils/e2e-test-setup.ts +0 -1
- package/test/utils/temp-files.ts +12 -0
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -0,0 +1,557 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase-Specific Task ID Integration Tests for BeadsInstructionGenerator
|
|
3
|
+
*
|
|
4
|
+
* Tests that validate BeadsInstructionGenerator's ability to extract phase task IDs
|
|
5
|
+
* from plan files and integrate them properly into BD CLI commands.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
9
|
+
import { BeadsInstructionGenerator } from '../../src/components/beads/beads-instruction-generator.js';
|
|
10
|
+
import type {
|
|
11
|
+
InstructionContext,
|
|
12
|
+
ConversationContext,
|
|
13
|
+
} from '@codemcp/workflows-core';
|
|
14
|
+
import { mkdir, writeFile, rm } from 'node:fs/promises';
|
|
15
|
+
import { join } from 'node:path';
|
|
16
|
+
|
|
17
|
+
describe('Phase-Specific Task ID Integration Tests', () => {
|
|
18
|
+
let beadsInstructionGenerator: BeadsInstructionGenerator;
|
|
19
|
+
let mockInstructionContext: InstructionContext;
|
|
20
|
+
let mockConversationContext: ConversationContext;
|
|
21
|
+
let testTempDir: string;
|
|
22
|
+
let testPlanFilePath: string;
|
|
23
|
+
|
|
24
|
+
beforeEach(async () => {
|
|
25
|
+
beadsInstructionGenerator = new BeadsInstructionGenerator();
|
|
26
|
+
|
|
27
|
+
// Create temporary directory for test files
|
|
28
|
+
testTempDir = join(process.cwd(), 'temp-test-' + Date.now());
|
|
29
|
+
await mkdir(testTempDir, { recursive: true });
|
|
30
|
+
|
|
31
|
+
testPlanFilePath = join(testTempDir, 'plan.md');
|
|
32
|
+
|
|
33
|
+
// Set up mock contexts with temp directory
|
|
34
|
+
mockConversationContext = {
|
|
35
|
+
conversationId: 'test-conversation',
|
|
36
|
+
projectPath: testTempDir,
|
|
37
|
+
planFilePath: testPlanFilePath,
|
|
38
|
+
gitBranch: 'main',
|
|
39
|
+
currentPhase: 'design',
|
|
40
|
+
workflowName: 'epcc',
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
mockInstructionContext = {
|
|
44
|
+
phase: 'design',
|
|
45
|
+
conversationContext: mockConversationContext,
|
|
46
|
+
transitionReason: 'test transition',
|
|
47
|
+
isModeled: false,
|
|
48
|
+
planFileExists: true,
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
afterEach(async () => {
|
|
53
|
+
// Clean up temp files
|
|
54
|
+
try {
|
|
55
|
+
await rm(testTempDir, { recursive: true, force: true });
|
|
56
|
+
} catch {
|
|
57
|
+
// Ignore cleanup errors
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('Phase Task ID Extraction from Plan Files', () => {
|
|
62
|
+
it('should extract phase task ID from properly formatted plan file', async () => {
|
|
63
|
+
const planContent = `# Project Plan
|
|
64
|
+
|
|
65
|
+
## Explore
|
|
66
|
+
Some exploration tasks here.
|
|
67
|
+
|
|
68
|
+
## Design
|
|
69
|
+
<!-- beads-phase-id: project-epic-1.2 -->
|
|
70
|
+
- Design the system architecture
|
|
71
|
+
- Create wireframes
|
|
72
|
+
- Review requirements
|
|
73
|
+
|
|
74
|
+
## Implementation
|
|
75
|
+
Some implementation tasks here.
|
|
76
|
+
`;
|
|
77
|
+
|
|
78
|
+
await writeFile(testPlanFilePath, planContent);
|
|
79
|
+
|
|
80
|
+
const result = await beadsInstructionGenerator.generateInstructions(
|
|
81
|
+
'Work on design tasks.',
|
|
82
|
+
mockInstructionContext
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
// Should include specific phase task ID in commands
|
|
86
|
+
expect(result.instructions).toContain(
|
|
87
|
+
'bd list --parent project-epic-1.2 --status open'
|
|
88
|
+
);
|
|
89
|
+
expect(result.instructions).toContain(
|
|
90
|
+
"bd create 'Task description' --parent project-epic-1.2 -p 2"
|
|
91
|
+
);
|
|
92
|
+
expect(result.instructions).toContain('bd show project-epic-1.2');
|
|
93
|
+
expect(result.instructions).toContain(
|
|
94
|
+
'All work items should be created as children of project-epic-1.2'
|
|
95
|
+
);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should handle phase task IDs with various formats', async () => {
|
|
99
|
+
const testCases = [
|
|
100
|
+
{ id: 'epic-123', phase: 'design' },
|
|
101
|
+
{ id: 'project-1.2.3', phase: 'design' },
|
|
102
|
+
{ id: 'feature-456.1', phase: 'design' },
|
|
103
|
+
{ id: 'milestone-x', phase: 'design' },
|
|
104
|
+
];
|
|
105
|
+
|
|
106
|
+
for (const testCase of testCases) {
|
|
107
|
+
const planContent = `# Project Plan
|
|
108
|
+
|
|
109
|
+
## Design
|
|
110
|
+
<!-- beads-phase-id: ${testCase.id} -->
|
|
111
|
+
- Task 1
|
|
112
|
+
- Task 2
|
|
113
|
+
`;
|
|
114
|
+
|
|
115
|
+
await writeFile(testPlanFilePath, planContent);
|
|
116
|
+
|
|
117
|
+
const result = await beadsInstructionGenerator.generateInstructions(
|
|
118
|
+
'Work on tasks.',
|
|
119
|
+
{ ...mockInstructionContext, phase: testCase.phase }
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
expect(
|
|
123
|
+
result.instructions,
|
|
124
|
+
`Should extract ID: ${testCase.id}`
|
|
125
|
+
).toContain(`bd list --parent ${testCase.id} --status open`);
|
|
126
|
+
expect(
|
|
127
|
+
result.instructions,
|
|
128
|
+
`Should use ID in create command: ${testCase.id}`
|
|
129
|
+
).toContain(
|
|
130
|
+
`bd create 'Task description' --parent ${testCase.id} -p 2`
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('should handle different phase names with underscore formatting', async () => {
|
|
136
|
+
const phaseMappings = [
|
|
137
|
+
{ phase: 'design', header: 'Design' },
|
|
138
|
+
{ phase: 'implementation', header: 'Implementation' },
|
|
139
|
+
{ phase: 'code_review', header: 'Code Review' },
|
|
140
|
+
{ phase: 'system_test', header: 'System Test' },
|
|
141
|
+
];
|
|
142
|
+
|
|
143
|
+
for (const mapping of phaseMappings) {
|
|
144
|
+
const planContent = `# Project Plan
|
|
145
|
+
|
|
146
|
+
## ${mapping.header}
|
|
147
|
+
<!-- beads-phase-id: phase-${mapping.phase}-123 -->
|
|
148
|
+
- Some tasks here
|
|
149
|
+
`;
|
|
150
|
+
|
|
151
|
+
await writeFile(testPlanFilePath, planContent);
|
|
152
|
+
|
|
153
|
+
const result = await beadsInstructionGenerator.generateInstructions(
|
|
154
|
+
'Work on phase tasks.',
|
|
155
|
+
{ ...mockInstructionContext, phase: mapping.phase }
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
expect(
|
|
159
|
+
result.instructions,
|
|
160
|
+
`Should find task ID for phase: ${mapping.phase}`
|
|
161
|
+
).toContain(
|
|
162
|
+
`bd list --parent phase-${mapping.phase}-123 --status open`
|
|
163
|
+
);
|
|
164
|
+
expect(
|
|
165
|
+
result.instructions,
|
|
166
|
+
`Should capitalize phase name correctly: ${mapping.phase}`
|
|
167
|
+
).toContain(`You are currently in the ${mapping.header} phase`);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('should handle multiple phases and extract correct phase task ID', async () => {
|
|
172
|
+
const planContent = `# Project Plan
|
|
173
|
+
|
|
174
|
+
## Explore
|
|
175
|
+
<!-- beads-phase-id: explore-task-1 -->
|
|
176
|
+
- Research requirements
|
|
177
|
+
- Analyze existing solutions
|
|
178
|
+
|
|
179
|
+
## Design
|
|
180
|
+
<!-- beads-phase-id: design-task-2 -->
|
|
181
|
+
- Create system design
|
|
182
|
+
- Design database schema
|
|
183
|
+
|
|
184
|
+
## Implementation
|
|
185
|
+
<!-- beads-phase-id: impl-task-3 -->
|
|
186
|
+
- Write core functionality
|
|
187
|
+
- Implement API endpoints
|
|
188
|
+
`;
|
|
189
|
+
|
|
190
|
+
await writeFile(testPlanFilePath, planContent);
|
|
191
|
+
|
|
192
|
+
// Test design phase extraction
|
|
193
|
+
const designResult = await beadsInstructionGenerator.generateInstructions(
|
|
194
|
+
'Work on design.',
|
|
195
|
+
{ ...mockInstructionContext, phase: 'design' }
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
expect(designResult.instructions).toContain(
|
|
199
|
+
'bd list --parent design-task-2 --status open'
|
|
200
|
+
);
|
|
201
|
+
expect(designResult.instructions).not.toContain('explore-task-1');
|
|
202
|
+
expect(designResult.instructions).not.toContain('impl-task-3');
|
|
203
|
+
|
|
204
|
+
// Test implementation phase extraction
|
|
205
|
+
const implResult = await beadsInstructionGenerator.generateInstructions(
|
|
206
|
+
'Work on implementation.',
|
|
207
|
+
{ ...mockInstructionContext, phase: 'implementation' }
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
expect(implResult.instructions).toContain(
|
|
211
|
+
'bd list --parent impl-task-3 --status open'
|
|
212
|
+
);
|
|
213
|
+
expect(implResult.instructions).not.toContain('design-task-2');
|
|
214
|
+
expect(implResult.instructions).not.toContain('explore-task-1');
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
describe('Graceful Handling of Missing Phase Task IDs', () => {
|
|
219
|
+
it('should provide generic commands when no phase task ID is found', async () => {
|
|
220
|
+
const planContent = `# Project Plan
|
|
221
|
+
|
|
222
|
+
## Design
|
|
223
|
+
- Some tasks without beads-phase-id
|
|
224
|
+
- More tasks here
|
|
225
|
+
`;
|
|
226
|
+
|
|
227
|
+
await writeFile(testPlanFilePath, planContent);
|
|
228
|
+
|
|
229
|
+
const result = await beadsInstructionGenerator.generateInstructions(
|
|
230
|
+
'Work on design tasks.',
|
|
231
|
+
mockInstructionContext
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
// Should fall back to generic placeholder commands
|
|
235
|
+
expect(result.instructions).toContain(
|
|
236
|
+
'bd list --parent <phase-task-id> --status open'
|
|
237
|
+
);
|
|
238
|
+
expect(result.instructions).toContain(
|
|
239
|
+
"bd create 'Task title' --parent <phase-task-id> -p 2"
|
|
240
|
+
);
|
|
241
|
+
expect(result.instructions).toContain('Use bd CLI tool exclusively');
|
|
242
|
+
expect(result.instructions).not.toContain('bd list --parent design-');
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('should handle malformed beads-phase-id comments gracefully', async () => {
|
|
246
|
+
// Test cases that should fall back to generic commands (don't match regex)
|
|
247
|
+
const genericFallbackCases = [
|
|
248
|
+
'<!-- beads-phase-id -->', // No colon
|
|
249
|
+
'<!-- beads-phase-id: @#$% -->', // Invalid characters (should not match regex)
|
|
250
|
+
];
|
|
251
|
+
|
|
252
|
+
for (const malformedComment of genericFallbackCases) {
|
|
253
|
+
const planContent = `# Project Plan
|
|
254
|
+
|
|
255
|
+
## Design
|
|
256
|
+
${malformedComment}
|
|
257
|
+
- Some tasks here
|
|
258
|
+
`;
|
|
259
|
+
|
|
260
|
+
await writeFile(testPlanFilePath, planContent);
|
|
261
|
+
|
|
262
|
+
const result = await beadsInstructionGenerator.generateInstructions(
|
|
263
|
+
'Work on tasks.',
|
|
264
|
+
mockInstructionContext
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
expect(
|
|
268
|
+
result.instructions,
|
|
269
|
+
`Should fall back to generic for malformed comment: ${malformedComment}`
|
|
270
|
+
).toContain('bd list --parent <phase-task-id> --status open');
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Test cases that extract unexpected values due to regex matching behavior
|
|
274
|
+
const edgeCases = [
|
|
275
|
+
{ comment: '<!-- beads-phase-id: -->', extracted: '--' },
|
|
276
|
+
{ comment: '<!-- beads-phase-id:no-space -->', extracted: 'no-space' },
|
|
277
|
+
];
|
|
278
|
+
|
|
279
|
+
for (const edgeCase of edgeCases) {
|
|
280
|
+
const planContent = `# Project Plan
|
|
281
|
+
|
|
282
|
+
## Design
|
|
283
|
+
${edgeCase.comment}
|
|
284
|
+
- Some tasks here
|
|
285
|
+
`;
|
|
286
|
+
|
|
287
|
+
await writeFile(testPlanFilePath, planContent);
|
|
288
|
+
|
|
289
|
+
const result = await beadsInstructionGenerator.generateInstructions(
|
|
290
|
+
'Work on tasks.',
|
|
291
|
+
mockInstructionContext
|
|
292
|
+
);
|
|
293
|
+
|
|
294
|
+
// Note: These cases currently extract values due to regex behavior
|
|
295
|
+
// This could be considered edge case behavior that should be improved
|
|
296
|
+
expect(
|
|
297
|
+
result.instructions,
|
|
298
|
+
`Should extract value from edge case: ${edgeCase.comment}`
|
|
299
|
+
).toContain(`bd list --parent ${edgeCase.extracted} --status open`);
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it('should handle non-existent plan file gracefully', async () => {
|
|
304
|
+
const contextWithMissingFile = {
|
|
305
|
+
...mockInstructionContext,
|
|
306
|
+
planFileExists: false,
|
|
307
|
+
conversationContext: {
|
|
308
|
+
...mockConversationContext,
|
|
309
|
+
planFilePath: '/non/existent/plan.md',
|
|
310
|
+
},
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
const result = await beadsInstructionGenerator.generateInstructions(
|
|
314
|
+
'Work on tasks.',
|
|
315
|
+
contextWithMissingFile
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
// Should provide generic guidance without crashing
|
|
319
|
+
expect(result.instructions).toContain(
|
|
320
|
+
'bd list --parent <phase-task-id> --status open'
|
|
321
|
+
);
|
|
322
|
+
expect(result.instructions).toContain('Use bd CLI tool exclusively');
|
|
323
|
+
expect(result.instructions).toContain(
|
|
324
|
+
'Plan file will be created when you first update it'
|
|
325
|
+
);
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it('should handle plan file with no matching phase section', async () => {
|
|
329
|
+
const planContent = `# Project Plan
|
|
330
|
+
|
|
331
|
+
## Explore
|
|
332
|
+
<!-- beads-phase-id: explore-123 -->
|
|
333
|
+
- Exploration tasks
|
|
334
|
+
|
|
335
|
+
## Implementation
|
|
336
|
+
<!-- beads-phase-id: impl-456 -->
|
|
337
|
+
- Implementation tasks
|
|
338
|
+
`;
|
|
339
|
+
|
|
340
|
+
await writeFile(testPlanFilePath, planContent);
|
|
341
|
+
|
|
342
|
+
// Request instructions for a phase not in the plan file
|
|
343
|
+
const result = await beadsInstructionGenerator.generateInstructions(
|
|
344
|
+
'Work on design tasks.',
|
|
345
|
+
{ ...mockInstructionContext, phase: 'design' }
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
// Should fall back to generic commands
|
|
349
|
+
expect(result.instructions).toContain(
|
|
350
|
+
'bd list --parent <phase-task-id> --status open'
|
|
351
|
+
);
|
|
352
|
+
expect(result.instructions).not.toContain('explore-123');
|
|
353
|
+
expect(result.instructions).not.toContain('impl-456');
|
|
354
|
+
});
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
describe('BD CLI Command Integration', () => {
|
|
358
|
+
it('should integrate extracted phase task ID into all relevant BD CLI commands', async () => {
|
|
359
|
+
const planContent = `# Project Plan
|
|
360
|
+
|
|
361
|
+
## Design
|
|
362
|
+
<!-- beads-phase-id: design-epic-789 -->
|
|
363
|
+
- Design system architecture
|
|
364
|
+
`;
|
|
365
|
+
|
|
366
|
+
await writeFile(testPlanFilePath, planContent);
|
|
367
|
+
|
|
368
|
+
const result = await beadsInstructionGenerator.generateInstructions(
|
|
369
|
+
'Work on design.',
|
|
370
|
+
mockInstructionContext
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
// Check all BD CLI commands contain the extracted ID
|
|
374
|
+
const expectedCommands = [
|
|
375
|
+
'bd list --parent design-epic-789 --status open',
|
|
376
|
+
"bd create 'Task description' --parent design-epic-789 -p 2",
|
|
377
|
+
'bd show design-epic-789',
|
|
378
|
+
];
|
|
379
|
+
|
|
380
|
+
for (const command of expectedCommands) {
|
|
381
|
+
expect(
|
|
382
|
+
result.instructions,
|
|
383
|
+
`Should contain command: ${command}`
|
|
384
|
+
).toContain(command);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Should mention the specific task ID in context
|
|
388
|
+
expect(result.instructions).toContain(
|
|
389
|
+
'All work items should be created as children of design-epic-789'
|
|
390
|
+
);
|
|
391
|
+
expect(result.instructions).toContain('subtasks of `design-epic-789`');
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
it('should provide immediate action guidance with extracted task ID', async () => {
|
|
395
|
+
const planContent = `# Project Plan
|
|
396
|
+
|
|
397
|
+
## Implementation
|
|
398
|
+
<!-- beads-phase-id: feature-impl-999 -->
|
|
399
|
+
- Implement core features
|
|
400
|
+
`;
|
|
401
|
+
|
|
402
|
+
await writeFile(testPlanFilePath, planContent);
|
|
403
|
+
|
|
404
|
+
const result = await beadsInstructionGenerator.generateInstructions(
|
|
405
|
+
'Start implementation.',
|
|
406
|
+
{ ...mockInstructionContext, phase: 'implementation' }
|
|
407
|
+
);
|
|
408
|
+
|
|
409
|
+
// Should provide specific immediate action
|
|
410
|
+
expect(result.instructions).toContain(
|
|
411
|
+
'Run `bd list --parent feature-impl-999 --status open` to see ready tasks'
|
|
412
|
+
);
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
it('should handle phase task ID extraction consistently across multiple calls', async () => {
|
|
416
|
+
const planContent = `# Project Plan
|
|
417
|
+
|
|
418
|
+
## Code
|
|
419
|
+
<!-- beads-phase-id: consistent-id-123 -->
|
|
420
|
+
- Write tests
|
|
421
|
+
- Implement features
|
|
422
|
+
`;
|
|
423
|
+
|
|
424
|
+
await writeFile(testPlanFilePath, planContent);
|
|
425
|
+
|
|
426
|
+
// Generate instructions multiple times
|
|
427
|
+
const results = await Promise.all([
|
|
428
|
+
beadsInstructionGenerator.generateInstructions('Call 1', {
|
|
429
|
+
...mockInstructionContext,
|
|
430
|
+
phase: 'code',
|
|
431
|
+
}),
|
|
432
|
+
beadsInstructionGenerator.generateInstructions('Call 2', {
|
|
433
|
+
...mockInstructionContext,
|
|
434
|
+
phase: 'code',
|
|
435
|
+
}),
|
|
436
|
+
beadsInstructionGenerator.generateInstructions('Call 3', {
|
|
437
|
+
...mockInstructionContext,
|
|
438
|
+
phase: 'code',
|
|
439
|
+
}),
|
|
440
|
+
]);
|
|
441
|
+
|
|
442
|
+
// All results should contain the same extracted task ID
|
|
443
|
+
for (const result of results) {
|
|
444
|
+
expect(result.instructions).toContain(
|
|
445
|
+
'bd list --parent consistent-id-123 --status open'
|
|
446
|
+
);
|
|
447
|
+
expect(result.instructions).toContain('consistent-id-123');
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
describe('Phase Name Capitalization and Matching', () => {
|
|
453
|
+
it('should correctly capitalize phase names for header matching', async () => {
|
|
454
|
+
const testCases = [
|
|
455
|
+
{ input: 'design', expected: 'Design' },
|
|
456
|
+
{ input: 'code_review', expected: 'Code Review' },
|
|
457
|
+
{ input: 'system_test', expected: 'System Test' },
|
|
458
|
+
{ input: 'integration_testing', expected: 'Integration Testing' },
|
|
459
|
+
];
|
|
460
|
+
|
|
461
|
+
for (const testCase of testCases) {
|
|
462
|
+
const planContent = `# Project Plan
|
|
463
|
+
|
|
464
|
+
## ${testCase.expected}
|
|
465
|
+
<!-- beads-phase-id: test-id-${testCase.input} -->
|
|
466
|
+
- Some tasks
|
|
467
|
+
`;
|
|
468
|
+
|
|
469
|
+
await writeFile(testPlanFilePath, planContent);
|
|
470
|
+
|
|
471
|
+
const result = await beadsInstructionGenerator.generateInstructions(
|
|
472
|
+
'Work on phase.',
|
|
473
|
+
{ ...mockInstructionContext, phase: testCase.input }
|
|
474
|
+
);
|
|
475
|
+
|
|
476
|
+
expect(
|
|
477
|
+
result.instructions,
|
|
478
|
+
`Should extract ID for phase: ${testCase.input} -> ${testCase.expected}`
|
|
479
|
+
).toContain(`bd list --parent test-id-${testCase.input} --status open`);
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
it('should handle case-insensitive phase header matching', async () => {
|
|
484
|
+
const planContent = `# Project Plan
|
|
485
|
+
|
|
486
|
+
## design
|
|
487
|
+
<!-- beads-phase-id: lowercase-header-123 -->
|
|
488
|
+
- Tasks with lowercase header
|
|
489
|
+
`;
|
|
490
|
+
|
|
491
|
+
await writeFile(testPlanFilePath, planContent);
|
|
492
|
+
|
|
493
|
+
// Should still find the phase despite case difference
|
|
494
|
+
const result = await beadsInstructionGenerator.generateInstructions(
|
|
495
|
+
'Work on design.',
|
|
496
|
+
mockInstructionContext
|
|
497
|
+
);
|
|
498
|
+
|
|
499
|
+
// Note: The current implementation is case-sensitive, so this tests the expected behavior
|
|
500
|
+
// If the implementation should be case-insensitive, this test would need to be updated
|
|
501
|
+
expect(result.instructions).toContain(
|
|
502
|
+
'bd list --parent <phase-task-id> --status open'
|
|
503
|
+
);
|
|
504
|
+
});
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
describe('Error Recovery and Robustness', () => {
|
|
508
|
+
it('should handle plan files with multiple beads-phase-id comments in same section', async () => {
|
|
509
|
+
const planContent = `# Project Plan
|
|
510
|
+
|
|
511
|
+
## Design
|
|
512
|
+
<!-- beads-phase-id: first-id-123 -->
|
|
513
|
+
<!-- beads-phase-id: second-id-456 -->
|
|
514
|
+
- Tasks with multiple IDs
|
|
515
|
+
`;
|
|
516
|
+
|
|
517
|
+
await writeFile(testPlanFilePath, planContent);
|
|
518
|
+
|
|
519
|
+
const result = await beadsInstructionGenerator.generateInstructions(
|
|
520
|
+
'Work on design.',
|
|
521
|
+
mockInstructionContext
|
|
522
|
+
);
|
|
523
|
+
|
|
524
|
+
// Should use the first found ID
|
|
525
|
+
expect(result.instructions).toContain(
|
|
526
|
+
'bd list --parent first-id-123 --status open'
|
|
527
|
+
);
|
|
528
|
+
expect(result.instructions).not.toContain('second-id-456');
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
it('should handle plan files with beads-phase-id in wrong sections', async () => {
|
|
532
|
+
const planContent = `# Project Plan
|
|
533
|
+
|
|
534
|
+
## Design
|
|
535
|
+
- Design tasks
|
|
536
|
+
|
|
537
|
+
## Implementation
|
|
538
|
+
<!-- beads-phase-id: impl-id-789 -->
|
|
539
|
+
- Implementation tasks
|
|
540
|
+
`;
|
|
541
|
+
|
|
542
|
+
await writeFile(testPlanFilePath, planContent);
|
|
543
|
+
|
|
544
|
+
// Request design phase but ID is in implementation section
|
|
545
|
+
const result = await beadsInstructionGenerator.generateInstructions(
|
|
546
|
+
'Work on design.',
|
|
547
|
+
mockInstructionContext
|
|
548
|
+
);
|
|
549
|
+
|
|
550
|
+
// Should not use the ID from wrong section
|
|
551
|
+
expect(result.instructions).toContain(
|
|
552
|
+
'bd list --parent <phase-task-id> --status open'
|
|
553
|
+
);
|
|
554
|
+
expect(result.instructions).not.toContain('impl-id-789');
|
|
555
|
+
});
|
|
556
|
+
});
|
|
557
|
+
});
|