@codemcp/workflows 5.0.1 → 5.1.1
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/package.json +6 -2
- package/skill/SKILL.md +23 -0
- package/.prettierignore +0 -2
- package/.turbo/turbo-build.log +0 -4
- package/.vibe/conversation-state.sqlite +0 -0
- package/src/components/beads/beads-instruction-generator.ts +0 -230
- package/src/components/beads/beads-plan-manager.ts +0 -333
- package/src/components/beads/beads-task-backend-client.ts +0 -229
- package/src/index.ts +0 -93
- package/src/notification-service.ts +0 -23
- package/src/plugin-system/beads-plugin.ts +0 -649
- package/src/plugin-system/commit-plugin.ts +0 -252
- package/src/plugin-system/index.ts +0 -20
- package/src/plugin-system/plugin-interfaces.ts +0 -153
- package/src/plugin-system/plugin-registry.ts +0 -190
- package/src/resource-handlers/conversation-state.ts +0 -55
- package/src/resource-handlers/development-plan.ts +0 -48
- package/src/resource-handlers/index.ts +0 -73
- package/src/resource-handlers/system-prompt.ts +0 -55
- package/src/resource-handlers/workflow-resource.ts +0 -132
- package/src/response-renderer.ts +0 -116
- package/src/server-config.ts +0 -760
- package/src/server-helpers.ts +0 -245
- package/src/server-implementation.ts +0 -277
- package/src/server.ts +0 -9
- package/src/tool-handlers/base-tool-handler.ts +0 -151
- package/src/tool-handlers/conduct-review.ts +0 -190
- package/src/tool-handlers/get-tool-info.ts +0 -273
- package/src/tool-handlers/index.ts +0 -115
- package/src/tool-handlers/list-workflows.ts +0 -78
- package/src/tool-handlers/no-idea.ts +0 -47
- package/src/tool-handlers/proceed-to-phase.ts +0 -296
- package/src/tool-handlers/reset-development.ts +0 -90
- package/src/tool-handlers/resume-workflow.ts +0 -378
- package/src/tool-handlers/setup-project-docs.ts +0 -232
- package/src/tool-handlers/start-development.ts +0 -746
- package/src/tool-handlers/whats-next.ts +0 -246
- package/src/types.ts +0 -135
- package/src/version-info.ts +0 -213
- package/test/e2e/beads-plugin-integration.test.ts +0 -1623
- package/test/e2e/commit-plugin-integration.test.ts +0 -222
- package/test/e2e/core-functionality.test.ts +0 -167
- package/test/e2e/git-branch-detection.test.ts +0 -351
- package/test/e2e/mcp-contract.test.ts +0 -509
- package/test/e2e/plan-management.test.ts +0 -334
- package/test/e2e/plugin-system-integration.test.ts +0 -1410
- package/test/e2e/state-management.test.ts +0 -387
- package/test/e2e/workflow-integration.test.ts +0 -498
- package/test/unit/beads-instruction-generator.test.ts +0 -979
- package/test/unit/beads-phase-task-id-integration.test.ts +0 -535
- package/test/unit/beads-plugin-behavioral.test.ts +0 -545
- package/test/unit/beads-plugin.test.ts +0 -117
- package/test/unit/commit-plugin.test.ts +0 -196
- package/test/unit/conduct-review.test.ts +0 -151
- package/test/unit/conversation-not-found-error.test.ts +0 -120
- package/test/unit/plugin-error-handling.test.ts +0 -240
- package/test/unit/proceed-to-phase-plugin-integration.test.ts +0 -150
- package/test/unit/reset-functionality.test.ts +0 -72
- package/test/unit/resume-workflow.test.ts +0 -193
- package/test/unit/server-config-plugin-registry.test.ts +0 -99
- package/test/unit/server-tools.test.ts +0 -310
- package/test/unit/setup-project-docs-handler.test.ts +0 -268
- package/test/unit/start-development-artifact-detection.test.ts +0 -387
- package/test/unit/start-development-gitignore.test.ts +0 -178
- package/test/unit/start-development-goal-extraction.test.ts +0 -226
- package/test/unit/system-prompt-resource.test.ts +0 -102
- package/test/unit/tool-handlers/no-idea.test.ts +0 -40
- package/test/utils/e2e-test-setup.ts +0 -451
- package/test/utils/run-server-in-dir.sh +0 -27
- package/test/utils/temp-files.ts +0 -320
- package/test/utils/test-access.ts +0 -79
- package/test/utils/test-helpers.ts +0 -288
- package/test/utils/test-setup.ts +0 -77
- package/tsconfig.build.json +0 -10
- package/tsconfig.build.tsbuildinfo +0 -1
- package/tsconfig.json +0 -12
- package/vitest.config.ts +0 -19
|
@@ -1,387 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for StartDevelopmentHandler dynamic artifact detection
|
|
3
|
-
*
|
|
4
|
-
* Tests the enhanced start_development functionality that dynamically analyzes workflows
|
|
5
|
-
* to detect which document variables are referenced and validates only those documents
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
9
|
-
import { TestAccess } from '../utils/test-access.js';
|
|
10
|
-
import { StartDevelopmentHandler } from '../../src/tool-handlers/start-development.js';
|
|
11
|
-
import type { YamlStateMachine } from './../../src/state-machine-types';
|
|
12
|
-
import { join } from 'node:path';
|
|
13
|
-
import {
|
|
14
|
-
MockContextFactory,
|
|
15
|
-
TEST_WORKFLOWS,
|
|
16
|
-
TestAssertions,
|
|
17
|
-
} from '../utils/test-helpers.js';
|
|
18
|
-
|
|
19
|
-
// Mock ProjectDocsManager
|
|
20
|
-
vi.mock('../../src/project-docs-manager.js');
|
|
21
|
-
|
|
22
|
-
// Mock other dependencies
|
|
23
|
-
vi.mock('../../src/git-manager.js', () => ({
|
|
24
|
-
GitManager: {
|
|
25
|
-
isGitRepository: vi.fn().mockReturnValue(true),
|
|
26
|
-
getCurrentCommitHash: vi.fn().mockReturnValue('abc123'),
|
|
27
|
-
},
|
|
28
|
-
}));
|
|
29
|
-
|
|
30
|
-
describe('StartDevelopmentHandler - Dynamic Artifact Detection', () => {
|
|
31
|
-
let handler: StartDevelopmentHandler;
|
|
32
|
-
let mockProjectDocsManager: ReturnType<
|
|
33
|
-
typeof MockContextFactory.createProjectDocsManagerMock
|
|
34
|
-
>;
|
|
35
|
-
let testProjectPath: string;
|
|
36
|
-
let mockContext: ReturnType<typeof MockContextFactory.createBasicContext>;
|
|
37
|
-
|
|
38
|
-
beforeEach(() => {
|
|
39
|
-
testProjectPath = '/test/project';
|
|
40
|
-
|
|
41
|
-
// Create mock project docs manager
|
|
42
|
-
mockProjectDocsManager =
|
|
43
|
-
MockContextFactory.createProjectDocsManagerMock(testProjectPath);
|
|
44
|
-
|
|
45
|
-
// Create handler and inject mock
|
|
46
|
-
handler = new StartDevelopmentHandler();
|
|
47
|
-
TestAccess.injectMock(
|
|
48
|
-
handler,
|
|
49
|
-
'projectDocsManager',
|
|
50
|
-
mockProjectDocsManager
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
// Create basic mock context
|
|
54
|
-
mockContext = MockContextFactory.createBasicContext(testProjectPath);
|
|
55
|
-
|
|
56
|
-
// Mock file system operations
|
|
57
|
-
vi.mock('fs', () => ({
|
|
58
|
-
readFileSync: vi.fn().mockReturnValue('main'),
|
|
59
|
-
writeFileSync: vi.fn(),
|
|
60
|
-
existsSync: vi.fn().mockReturnValue(false),
|
|
61
|
-
}));
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
describe('dynamic workflow analysis', () => {
|
|
65
|
-
it('should proceed normally when workflow contains no document variables', async () => {
|
|
66
|
-
mockContext.workflowManager.loadWorkflowForProject.mockReturnValue(
|
|
67
|
-
TEST_WORKFLOWS.simple
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
const result = await handler.executeHandler(
|
|
71
|
-
{ workflow: 'simple-workflow' },
|
|
72
|
-
mockContext
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
expect(mockProjectDocsManager.getProjectDocsInfo).not.toHaveBeenCalled();
|
|
76
|
-
TestAssertions.expectNormalPhase(result, 'requirements');
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('should detect and validate only referenced document variables', async () => {
|
|
80
|
-
mockContext.workflowManager.loadWorkflowForProject.mockReturnValue(
|
|
81
|
-
TEST_WORKFLOWS.requiredArchDoc
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
// Update mock context to match the workflow's initial state
|
|
85
|
-
mockContext.conversationManager.createConversationContext = vi
|
|
86
|
-
.fn()
|
|
87
|
-
.mockResolvedValue({
|
|
88
|
-
conversationId: 'test-conversation',
|
|
89
|
-
currentPhase: 'design',
|
|
90
|
-
planFilePath: join(testProjectPath, '.vibe', 'development-plan.md'),
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
mockProjectDocsManager.getProjectDocsInfo.mockResolvedValue({
|
|
94
|
-
architecture: {
|
|
95
|
-
exists: false,
|
|
96
|
-
path: join(testProjectPath, '.vibe', 'docs', 'architecture.md'),
|
|
97
|
-
},
|
|
98
|
-
requirements: {
|
|
99
|
-
exists: true,
|
|
100
|
-
path: join(testProjectPath, '.vibe', 'docs', 'requirements.md'),
|
|
101
|
-
},
|
|
102
|
-
design: {
|
|
103
|
-
exists: true,
|
|
104
|
-
path: join(testProjectPath, '.vibe', 'docs', 'design.md'),
|
|
105
|
-
},
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
const result = await handler.executeHandler(
|
|
109
|
-
{ workflow: 'arch-focused' },
|
|
110
|
-
mockContext
|
|
111
|
-
);
|
|
112
|
-
|
|
113
|
-
TestAssertions.expectArtifactSetupPhase(result);
|
|
114
|
-
expect(result.instructions).toContain(
|
|
115
|
-
'**Referenced Variables:** `$ARCHITECTURE_DOC`'
|
|
116
|
-
);
|
|
117
|
-
expect(result.instructions).toContain('architecture.md');
|
|
118
|
-
expect(result.instructions).toContain('✅ requirements.md');
|
|
119
|
-
expect(result.instructions).toContain('✅ design.md');
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it('should detect multiple document variables in workflow', async () => {
|
|
123
|
-
mockContext.workflowManager.loadWorkflowForProject.mockReturnValue(
|
|
124
|
-
TEST_WORKFLOWS.requiredMultipleDocs
|
|
125
|
-
);
|
|
126
|
-
|
|
127
|
-
// Update mock context to match the workflow's initial state
|
|
128
|
-
mockContext.conversationManager.createConversationContext = vi
|
|
129
|
-
.fn()
|
|
130
|
-
.mockResolvedValue({
|
|
131
|
-
conversationId: 'test-conversation',
|
|
132
|
-
currentPhase: 'implementation',
|
|
133
|
-
planFilePath: join(testProjectPath, '.vibe', 'development-plan.md'),
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
mockProjectDocsManager.getProjectDocsInfo.mockResolvedValue({
|
|
137
|
-
architecture: {
|
|
138
|
-
exists: false,
|
|
139
|
-
path: join(testProjectPath, '.vibe', 'docs', 'architecture.md'),
|
|
140
|
-
},
|
|
141
|
-
requirements: {
|
|
142
|
-
exists: false,
|
|
143
|
-
path: join(testProjectPath, '.vibe', 'docs', 'requirements.md'),
|
|
144
|
-
},
|
|
145
|
-
design: {
|
|
146
|
-
exists: false,
|
|
147
|
-
path: join(testProjectPath, '.vibe', 'docs', 'design.md'),
|
|
148
|
-
},
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
const result = await handler.executeHandler(
|
|
152
|
-
{ workflow: 'multi-doc' },
|
|
153
|
-
mockContext
|
|
154
|
-
);
|
|
155
|
-
|
|
156
|
-
TestAssertions.expectArtifactSetupPhase(result);
|
|
157
|
-
expect(result.instructions).toContain(
|
|
158
|
-
'**Referenced Variables:** `$ARCHITECTURE_DOC`, `$REQUIREMENTS_DOC`, `$DESIGN_DOC`'
|
|
159
|
-
);
|
|
160
|
-
expect(result.instructions).toContain('architecture.md');
|
|
161
|
-
expect(result.instructions).toContain('requirements.md');
|
|
162
|
-
expect(result.instructions).toContain('design.md');
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
it('should proceed normally when all referenced documents exist', async () => {
|
|
166
|
-
mockContext.workflowManager.loadWorkflowForProject.mockReturnValue(
|
|
167
|
-
TEST_WORKFLOWS.requiredMultipleDocs
|
|
168
|
-
);
|
|
169
|
-
|
|
170
|
-
// Mock conversation context to match initial state
|
|
171
|
-
mockContext.conversationManager.createConversationContext.mockResolvedValue(
|
|
172
|
-
{
|
|
173
|
-
conversationId: 'test-conversation',
|
|
174
|
-
currentPhase: 'implementation',
|
|
175
|
-
projectPath: testProjectPath,
|
|
176
|
-
planFilePath: join(testProjectPath, '.vibe', 'plan.md'),
|
|
177
|
-
gitBranch: 'feature-branch',
|
|
178
|
-
}
|
|
179
|
-
);
|
|
180
|
-
|
|
181
|
-
// Mock transition engine to return the correct phase
|
|
182
|
-
mockContext.transitionEngine.handleExplicitTransition.mockResolvedValue({
|
|
183
|
-
newPhase: 'implementation',
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
mockProjectDocsManager.getProjectDocsInfo.mockResolvedValue({
|
|
187
|
-
architecture: {
|
|
188
|
-
exists: true,
|
|
189
|
-
path: join(testProjectPath, '.vibe', 'docs', 'architecture.md'),
|
|
190
|
-
},
|
|
191
|
-
requirements: {
|
|
192
|
-
exists: true,
|
|
193
|
-
path: join(testProjectPath, '.vibe', 'docs', 'requirements.md'),
|
|
194
|
-
},
|
|
195
|
-
design: {
|
|
196
|
-
exists: true,
|
|
197
|
-
path: join(testProjectPath, '.vibe', 'docs', 'design.md'),
|
|
198
|
-
},
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
const result = await handler.executeHandler(
|
|
202
|
-
{ workflow: 'complete-docs' },
|
|
203
|
-
mockContext
|
|
204
|
-
);
|
|
205
|
-
|
|
206
|
-
expect(mockProjectDocsManager.getProjectDocsInfo).toHaveBeenCalledWith(
|
|
207
|
-
testProjectPath
|
|
208
|
-
);
|
|
209
|
-
TestAssertions.expectNormalPhase(result, 'implementation');
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
it('should handle partial document availability correctly', async () => {
|
|
213
|
-
// Mock workflow that references REQUIREMENTS_DOC and DESIGN_DOC
|
|
214
|
-
const partialWorkflow = {
|
|
215
|
-
name: 'req-design-focused',
|
|
216
|
-
description: 'Workflow focusing on requirements and design docs',
|
|
217
|
-
initial_state: 'development',
|
|
218
|
-
metadata: {
|
|
219
|
-
requiresDocumentation: true,
|
|
220
|
-
},
|
|
221
|
-
states: {
|
|
222
|
-
development: {
|
|
223
|
-
default_instructions:
|
|
224
|
-
'Implement features based on $REQUIREMENTS_DOC and follow $DESIGN_DOC patterns.',
|
|
225
|
-
},
|
|
226
|
-
},
|
|
227
|
-
} as Partial<YamlStateMachine>;
|
|
228
|
-
|
|
229
|
-
mockContext.workflowManager.loadWorkflowForProject.mockReturnValue(
|
|
230
|
-
partialWorkflow
|
|
231
|
-
);
|
|
232
|
-
|
|
233
|
-
// Update mock context to match the workflow's initial state
|
|
234
|
-
mockContext.conversationManager.createConversationContext = vi
|
|
235
|
-
.fn()
|
|
236
|
-
.mockResolvedValue({
|
|
237
|
-
conversationId: 'test-conversation',
|
|
238
|
-
currentPhase: 'development',
|
|
239
|
-
planFilePath: join(testProjectPath, '.vibe', 'development-plan.md'),
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
mockProjectDocsManager.getProjectDocsInfo.mockResolvedValue({
|
|
243
|
-
architecture: {
|
|
244
|
-
exists: true,
|
|
245
|
-
path: join(testProjectPath, '.vibe', 'docs', 'architecture.md'),
|
|
246
|
-
},
|
|
247
|
-
requirements: {
|
|
248
|
-
exists: true,
|
|
249
|
-
path: join(testProjectPath, '.vibe', 'docs', 'requirements.md'),
|
|
250
|
-
},
|
|
251
|
-
design: {
|
|
252
|
-
exists: false,
|
|
253
|
-
path: join(testProjectPath, '.vibe', 'docs', 'design.md'),
|
|
254
|
-
},
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
const result = await handler.executeHandler(
|
|
258
|
-
{ workflow: 'req-design-focused' },
|
|
259
|
-
mockContext
|
|
260
|
-
);
|
|
261
|
-
|
|
262
|
-
TestAssertions.expectArtifactSetupPhase(result);
|
|
263
|
-
expect(result.instructions).toContain(
|
|
264
|
-
'**Referenced Variables:** `$REQUIREMENTS_DOC`, `$DESIGN_DOC`'
|
|
265
|
-
);
|
|
266
|
-
expect(result.instructions).toContain('design.md'); // Only missing doc
|
|
267
|
-
expect(result.instructions).toContain('✅ architecture.md'); // Existing doc
|
|
268
|
-
expect(result.instructions).toContain('✅ requirements.md'); // Existing doc
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
it('should handle workflow loading errors gracefully', async () => {
|
|
272
|
-
// Mock workflow loading failure - this should happen during artifact check, not during normal workflow loading
|
|
273
|
-
let callCount = 0;
|
|
274
|
-
mockContext.workflowManager.loadWorkflowForProject.mockImplementation(
|
|
275
|
-
() => {
|
|
276
|
-
callCount++;
|
|
277
|
-
if (callCount === 1) {
|
|
278
|
-
// First call during artifact check - throw error
|
|
279
|
-
throw new Error('Workflow not found');
|
|
280
|
-
} else {
|
|
281
|
-
// Second call during normal flow - return valid workflow
|
|
282
|
-
return TEST_WORKFLOWS.simple;
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
);
|
|
286
|
-
|
|
287
|
-
const result = await handler.executeHandler(
|
|
288
|
-
{ workflow: 'invalid-workflow' },
|
|
289
|
-
mockContext
|
|
290
|
-
);
|
|
291
|
-
|
|
292
|
-
// Should proceed without artifact check when workflow analysis fails
|
|
293
|
-
TestAssertions.expectNormalPhase(result, 'requirements');
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
it('should include detected variables in setup guidance', async () => {
|
|
297
|
-
mockContext.workflowManager.loadWorkflowForProject.mockReturnValue(
|
|
298
|
-
TEST_WORKFLOWS.requiredArchDoc
|
|
299
|
-
);
|
|
300
|
-
|
|
301
|
-
// Update mock context to match the workflow's initial state
|
|
302
|
-
mockContext.conversationManager.createConversationContext = vi
|
|
303
|
-
.fn()
|
|
304
|
-
.mockResolvedValue({
|
|
305
|
-
conversationId: 'test-conversation',
|
|
306
|
-
currentPhase: 'design',
|
|
307
|
-
planFilePath: join(testProjectPath, '.vibe', 'development-plan.md'),
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
mockProjectDocsManager.getProjectDocsInfo.mockResolvedValue({
|
|
311
|
-
architecture: {
|
|
312
|
-
exists: false,
|
|
313
|
-
path: join(testProjectPath, '.vibe', 'docs', 'architecture.md'),
|
|
314
|
-
},
|
|
315
|
-
requirements: {
|
|
316
|
-
exists: true,
|
|
317
|
-
path: join(testProjectPath, '.vibe', 'docs', 'requirements.md'),
|
|
318
|
-
},
|
|
319
|
-
design: {
|
|
320
|
-
exists: true,
|
|
321
|
-
path: join(testProjectPath, '.vibe', 'docs', 'design.md'),
|
|
322
|
-
},
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
const result = await handler.executeHandler(
|
|
326
|
-
{ workflow: 'arch-only' },
|
|
327
|
-
mockContext
|
|
328
|
-
);
|
|
329
|
-
|
|
330
|
-
expect(result.instructions).toContain(
|
|
331
|
-
'**Referenced Variables:** `$ARCHITECTURE_DOC`'
|
|
332
|
-
);
|
|
333
|
-
expect(result.instructions).toContain(
|
|
334
|
-
'detected variables: `$ARCHITECTURE_DOC`'
|
|
335
|
-
);
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
it('should proceed normally for optional workflows with missing documents', async () => {
|
|
339
|
-
// Use a workflow that has document variables but NO requiresDocumentation flag
|
|
340
|
-
mockContext.workflowManager.loadWorkflowForProject.mockReturnValue(
|
|
341
|
-
TEST_WORKFLOWS.withArchDoc
|
|
342
|
-
);
|
|
343
|
-
|
|
344
|
-
// Mock conversation context to match initial state
|
|
345
|
-
mockContext.conversationManager.createConversationContext.mockResolvedValue(
|
|
346
|
-
{
|
|
347
|
-
conversationId: 'test-conversation',
|
|
348
|
-
currentPhase: 'design',
|
|
349
|
-
projectPath: testProjectPath,
|
|
350
|
-
planFilePath: join(testProjectPath, '.vibe', 'plan.md'),
|
|
351
|
-
gitBranch: 'feature-branch',
|
|
352
|
-
}
|
|
353
|
-
);
|
|
354
|
-
|
|
355
|
-
// Mock transition engine to return the correct phase
|
|
356
|
-
mockContext.transitionEngine.handleExplicitTransition.mockResolvedValue({
|
|
357
|
-
newPhase: 'design',
|
|
358
|
-
});
|
|
359
|
-
|
|
360
|
-
// Mock all documents as missing
|
|
361
|
-
mockProjectDocsManager.getProjectDocsInfo.mockResolvedValue({
|
|
362
|
-
architecture: {
|
|
363
|
-
exists: false,
|
|
364
|
-
path: join(testProjectPath, '.vibe', 'docs', 'architecture.md'),
|
|
365
|
-
},
|
|
366
|
-
requirements: {
|
|
367
|
-
exists: false,
|
|
368
|
-
path: join(testProjectPath, '.vibe', 'docs', 'requirements.md'),
|
|
369
|
-
},
|
|
370
|
-
design: {
|
|
371
|
-
exists: false,
|
|
372
|
-
path: join(testProjectPath, '.vibe', 'docs', 'design.md'),
|
|
373
|
-
},
|
|
374
|
-
});
|
|
375
|
-
|
|
376
|
-
const result = await handler.executeHandler(
|
|
377
|
-
{ workflow: 'optional-arch-workflow' },
|
|
378
|
-
mockContext
|
|
379
|
-
);
|
|
380
|
-
|
|
381
|
-
// Should proceed to normal phase instead of artifact-setup
|
|
382
|
-
// because requiresDocumentation is not set (defaults to false)
|
|
383
|
-
TestAssertions.expectNormalPhase(result, 'design');
|
|
384
|
-
expect(result.instructions).not.toContain('Referenced Variables');
|
|
385
|
-
});
|
|
386
|
-
});
|
|
387
|
-
});
|
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for StartDevelopment .gitignore management
|
|
3
|
-
*
|
|
4
|
-
* Tests the automatic .vibe/.gitignore creation functionality
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
8
|
-
import {
|
|
9
|
-
existsSync,
|
|
10
|
-
readFileSync,
|
|
11
|
-
writeFileSync,
|
|
12
|
-
mkdirSync,
|
|
13
|
-
rmSync,
|
|
14
|
-
} from 'node:fs';
|
|
15
|
-
import { resolve } from 'node:path';
|
|
16
|
-
import { TestAccess } from '../utils/test-access.js';
|
|
17
|
-
import { tmpdir } from 'node:os';
|
|
18
|
-
import { StartDevelopmentHandler } from '../../src/tool-handlers/start-development.js';
|
|
19
|
-
|
|
20
|
-
describe('StartDevelopment .gitignore management', () => {
|
|
21
|
-
let tempDir: string;
|
|
22
|
-
let handler: StartDevelopmentHandler;
|
|
23
|
-
let mockLogger: {
|
|
24
|
-
debug: ReturnType<typeof vi.fn>;
|
|
25
|
-
info: ReturnType<typeof vi.fn>;
|
|
26
|
-
warn: ReturnType<typeof vi.fn>;
|
|
27
|
-
error: ReturnType<typeof vi.fn>;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
beforeEach(() => {
|
|
31
|
-
// Create temporary directory for testing
|
|
32
|
-
tempDir = resolve(tmpdir(), `vibe-test-${Date.now()}`);
|
|
33
|
-
mkdirSync(tempDir, { recursive: true });
|
|
34
|
-
|
|
35
|
-
// Mock logger
|
|
36
|
-
mockLogger = {
|
|
37
|
-
debug: vi.fn(),
|
|
38
|
-
info: vi.fn(),
|
|
39
|
-
warn: vi.fn(),
|
|
40
|
-
error: vi.fn(),
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
// Create handler instance with mocked logger
|
|
44
|
-
handler = new StartDevelopmentHandler();
|
|
45
|
-
(handler as unknown as { logger: unknown }).logger = mockLogger;
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
afterEach(() => {
|
|
49
|
-
// Clean up temporary directory
|
|
50
|
-
if (existsSync(tempDir)) {
|
|
51
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
describe('ensureGitignoreEntry', () => {
|
|
56
|
-
it('should skip non-git repositories', () => {
|
|
57
|
-
// Call the private method cleanly
|
|
58
|
-
TestAccess.callMethod(handler, 'ensureGitignoreEntry', tempDir);
|
|
59
|
-
|
|
60
|
-
expect(mockLogger.debug).toHaveBeenCalledWith(
|
|
61
|
-
'Not a git repository, skipping .gitignore management',
|
|
62
|
-
{ projectPath: tempDir }
|
|
63
|
-
);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('should create .vibe/.gitignore when none exists in git repo', () => {
|
|
67
|
-
// Create .git directory to simulate git repo
|
|
68
|
-
mkdirSync(resolve(tempDir, '.git'));
|
|
69
|
-
|
|
70
|
-
// Call the method
|
|
71
|
-
TestAccess.callMethod(handler, 'ensureGitignoreEntry', tempDir);
|
|
72
|
-
|
|
73
|
-
// Check that .vibe/.gitignore was created with correct content
|
|
74
|
-
const vibeGitignorePath = resolve(tempDir, '.vibe', '.gitignore');
|
|
75
|
-
expect(existsSync(vibeGitignorePath)).toBe(true);
|
|
76
|
-
|
|
77
|
-
const content = readFileSync(vibeGitignorePath, 'utf-8');
|
|
78
|
-
expect(content).toContain('conversations/');
|
|
79
|
-
expect(content).toContain('*.sqlite');
|
|
80
|
-
|
|
81
|
-
expect(mockLogger.info).toHaveBeenCalledWith(
|
|
82
|
-
'Created .vibe/.gitignore to exclude conversation files',
|
|
83
|
-
{ projectPath: tempDir, gitignorePath: vibeGitignorePath }
|
|
84
|
-
);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it('should create .vibe directory if it does not exist', () => {
|
|
88
|
-
// Create .git directory to simulate git repo
|
|
89
|
-
mkdirSync(resolve(tempDir, '.git'));
|
|
90
|
-
|
|
91
|
-
// Ensure .vibe directory doesn't exist
|
|
92
|
-
const vibeDir = resolve(tempDir, '.vibe');
|
|
93
|
-
expect(existsSync(vibeDir)).toBe(false);
|
|
94
|
-
|
|
95
|
-
// Call the method
|
|
96
|
-
TestAccess.callMethod(handler, 'ensureGitignoreEntry', tempDir);
|
|
97
|
-
|
|
98
|
-
// Check that .vibe directory was created
|
|
99
|
-
expect(existsSync(vibeDir)).toBe(true);
|
|
100
|
-
|
|
101
|
-
// Check that .vibe/.gitignore was created
|
|
102
|
-
const vibeGitignorePath = resolve(vibeDir, '.gitignore');
|
|
103
|
-
expect(existsSync(vibeGitignorePath)).toBe(true);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it('should skip when .vibe/.gitignore already exists with conversation exclusions', () => {
|
|
107
|
-
// Create .git directory
|
|
108
|
-
mkdirSync(resolve(tempDir, '.git'));
|
|
109
|
-
|
|
110
|
-
// Create .vibe directory and .gitignore with existing conversation exclusions
|
|
111
|
-
const vibeDir = resolve(tempDir, '.vibe');
|
|
112
|
-
mkdirSync(vibeDir, { recursive: true });
|
|
113
|
-
const vibeGitignorePath = resolve(vibeDir, '.gitignore');
|
|
114
|
-
const existingContent = 'conversations/\n*.sqlite\n';
|
|
115
|
-
writeFileSync(vibeGitignorePath, existingContent);
|
|
116
|
-
|
|
117
|
-
// Call the method
|
|
118
|
-
TestAccess.callMethod(handler, 'ensureGitignoreEntry', tempDir);
|
|
119
|
-
|
|
120
|
-
// Check that content wasn't changed
|
|
121
|
-
const content = readFileSync(vibeGitignorePath, 'utf-8');
|
|
122
|
-
expect(content).toBe(existingContent);
|
|
123
|
-
|
|
124
|
-
expect(mockLogger.debug).toHaveBeenCalledWith(
|
|
125
|
-
'.vibe/.gitignore already exists with conversation exclusions',
|
|
126
|
-
{ gitignorePath: vibeGitignorePath }
|
|
127
|
-
);
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
it('should recreate .vibe/.gitignore if existing one is incomplete', () => {
|
|
131
|
-
// Create .git directory
|
|
132
|
-
mkdirSync(resolve(tempDir, '.git'));
|
|
133
|
-
|
|
134
|
-
// Create .vibe directory and incomplete .gitignore
|
|
135
|
-
const vibeDir = resolve(tempDir, '.vibe');
|
|
136
|
-
mkdirSync(vibeDir, { recursive: true });
|
|
137
|
-
const vibeGitignorePath = resolve(vibeDir, '.gitignore');
|
|
138
|
-
const incompleteContent = 'some-other-file\n';
|
|
139
|
-
writeFileSync(vibeGitignorePath, incompleteContent);
|
|
140
|
-
|
|
141
|
-
// Call the method
|
|
142
|
-
TestAccess.callMethod(handler, 'ensureGitignoreEntry', tempDir);
|
|
143
|
-
|
|
144
|
-
// Check that content was updated to include conversation exclusions
|
|
145
|
-
const content = readFileSync(vibeGitignorePath, 'utf-8');
|
|
146
|
-
expect(content).toContain('conversations/');
|
|
147
|
-
expect(content).toContain('*.sqlite');
|
|
148
|
-
|
|
149
|
-
expect(mockLogger.info).toHaveBeenCalledWith(
|
|
150
|
-
'Created .vibe/.gitignore to exclude conversation files',
|
|
151
|
-
{ projectPath: tempDir, gitignorePath: vibeGitignorePath }
|
|
152
|
-
);
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
it('should not affect parent directory .gitignore', () => {
|
|
156
|
-
// Create .git directory
|
|
157
|
-
mkdirSync(resolve(tempDir, '.git'));
|
|
158
|
-
|
|
159
|
-
// Create parent .gitignore with existing content
|
|
160
|
-
const parentGitignorePath = resolve(tempDir, '.gitignore');
|
|
161
|
-
const parentContent = 'node_modules/\n*.log\n';
|
|
162
|
-
writeFileSync(parentGitignorePath, parentContent);
|
|
163
|
-
|
|
164
|
-
// Call the method
|
|
165
|
-
TestAccess.callMethod(handler, 'ensureGitignoreEntry', tempDir);
|
|
166
|
-
|
|
167
|
-
// Check that parent .gitignore was not modified
|
|
168
|
-
const parentContentAfter = readFileSync(parentGitignorePath, 'utf-8');
|
|
169
|
-
expect(parentContentAfter).toBe(parentContent);
|
|
170
|
-
|
|
171
|
-
// Check that .vibe/.gitignore was created
|
|
172
|
-
const vibeGitignorePath = resolve(tempDir, '.vibe', '.gitignore');
|
|
173
|
-
expect(existsSync(vibeGitignorePath)).toBe(true);
|
|
174
|
-
const vibeContent = readFileSync(vibeGitignorePath, 'utf-8');
|
|
175
|
-
expect(vibeContent).toContain('*.sqlite');
|
|
176
|
-
});
|
|
177
|
-
});
|
|
178
|
-
});
|