@codemcp/workflows-core 3.1.16
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 +4 -0
- package/LICENSE +674 -0
- package/dist/config-manager.d.ts +24 -0
- package/dist/config-manager.js +68 -0
- package/dist/config-manager.js.map +1 -0
- package/dist/conversation-manager.d.ts +97 -0
- package/dist/conversation-manager.js +367 -0
- package/dist/conversation-manager.js.map +1 -0
- package/dist/database.d.ts +73 -0
- package/dist/database.js +500 -0
- package/dist/database.js.map +1 -0
- package/dist/file-detection-manager.d.ts +53 -0
- package/dist/file-detection-manager.js +221 -0
- package/dist/file-detection-manager.js.map +1 -0
- package/dist/git-manager.d.ts +14 -0
- package/dist/git-manager.js +59 -0
- package/dist/git-manager.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/instruction-generator.d.ts +69 -0
- package/dist/instruction-generator.js +133 -0
- package/dist/instruction-generator.js.map +1 -0
- package/dist/interaction-logger.d.ts +37 -0
- package/dist/interaction-logger.js +87 -0
- package/dist/interaction-logger.js.map +1 -0
- package/dist/logger.d.ts +64 -0
- package/dist/logger.js +283 -0
- package/dist/logger.js.map +1 -0
- package/dist/path-validation-utils.d.ts +51 -0
- package/dist/path-validation-utils.js +202 -0
- package/dist/path-validation-utils.js.map +1 -0
- package/dist/plan-manager.d.ts +65 -0
- package/dist/plan-manager.js +256 -0
- package/dist/plan-manager.js.map +1 -0
- package/dist/project-docs-manager.d.ts +119 -0
- package/dist/project-docs-manager.js +357 -0
- package/dist/project-docs-manager.js.map +1 -0
- package/dist/state-machine-loader.d.ts +60 -0
- package/dist/state-machine-loader.js +235 -0
- package/dist/state-machine-loader.js.map +1 -0
- package/dist/state-machine-types.d.ts +58 -0
- package/dist/state-machine-types.js +7 -0
- package/dist/state-machine-types.js.map +1 -0
- package/dist/state-machine.d.ts +52 -0
- package/dist/state-machine.js +256 -0
- package/dist/state-machine.js.map +1 -0
- package/dist/system-prompt-generator.d.ts +17 -0
- package/dist/system-prompt-generator.js +113 -0
- package/dist/system-prompt-generator.js.map +1 -0
- package/dist/template-manager.d.ts +61 -0
- package/dist/template-manager.js +229 -0
- package/dist/template-manager.js.map +1 -0
- package/dist/transition-engine.d.ts +70 -0
- package/dist/transition-engine.js +240 -0
- package/dist/transition-engine.js.map +1 -0
- package/dist/types.d.ts +56 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/workflow-manager.d.ts +89 -0
- package/dist/workflow-manager.js +466 -0
- package/dist/workflow-manager.js.map +1 -0
- package/package.json +27 -0
- package/src/config-manager.ts +96 -0
- package/src/conversation-manager.ts +492 -0
- package/src/database.ts +685 -0
- package/src/file-detection-manager.ts +302 -0
- package/src/git-manager.ts +64 -0
- package/src/index.ts +28 -0
- package/src/instruction-generator.ts +210 -0
- package/src/interaction-logger.ts +109 -0
- package/src/logger.ts +353 -0
- package/src/path-validation-utils.ts +261 -0
- package/src/plan-manager.ts +323 -0
- package/src/project-docs-manager.ts +522 -0
- package/src/state-machine-loader.ts +308 -0
- package/src/state-machine-types.ts +72 -0
- package/src/state-machine.ts +370 -0
- package/src/system-prompt-generator.ts +122 -0
- package/src/template-manager.ts +321 -0
- package/src/transition-engine.ts +386 -0
- package/src/types.ts +60 -0
- package/src/workflow-manager.ts +601 -0
- package/test/unit/conversation-manager.test.ts +179 -0
- package/test/unit/custom-workflow-loading.test.ts +174 -0
- package/test/unit/directory-linking-and-extensions.test.ts +338 -0
- package/test/unit/file-linking-integration.test.ts +256 -0
- package/test/unit/git-commit-integration.test.ts +91 -0
- package/test/unit/git-manager.test.ts +86 -0
- package/test/unit/install-workflow.test.ts +138 -0
- package/test/unit/instruction-generator.test.ts +247 -0
- package/test/unit/list-workflows-filtering.test.ts +68 -0
- package/test/unit/none-template-functionality.test.ts +224 -0
- package/test/unit/project-docs-manager.test.ts +337 -0
- package/test/unit/state-machine-loader.test.ts +234 -0
- package/test/unit/template-manager.test.ts +217 -0
- package/test/unit/validate-workflow-name.test.ts +150 -0
- package/test/unit/workflow-domain-filtering.test.ts +75 -0
- package/test/unit/workflow-enum-generation.test.ts +92 -0
- package/test/unit/workflow-manager-enhanced-path-resolution.test.ts +369 -0
- package/test/unit/workflow-manager-path-resolution.test.ts +150 -0
- package/test/unit/workflow-migration.test.ts +155 -0
- package/test/unit/workflow-override-by-name.test.ts +116 -0
- package/test/unit/workflow-prioritization.test.ts +38 -0
- package/test/unit/workflow-validation.test.ts +303 -0
- package/test/utils/e2e-test-setup.ts +453 -0
- package/test/utils/run-server-in-dir.sh +27 -0
- package/test/utils/temp-files.ts +308 -0
- package/test/utils/test-access.ts +79 -0
- package/test/utils/test-helpers.ts +286 -0
- package/test/utils/test-setup.ts +78 -0
- package/tsconfig.build.json +21 -0
- package/tsconfig.json +8 -0
- package/vitest.config.ts +18 -0
@@ -0,0 +1,247 @@
|
|
1
|
+
/**
|
2
|
+
* Unit tests for InstructionGenerator
|
3
|
+
*
|
4
|
+
* Tests instruction generation and variable substitution functionality
|
5
|
+
*/
|
6
|
+
|
7
|
+
import { describe, it, expect, beforeEach, Mocked, vi } from 'vitest';
|
8
|
+
import { TestAccess } from '../utils/test-access.js';
|
9
|
+
import { InstructionGenerator } from '@codemcp/workflows-core';
|
10
|
+
import { PlanManager } from '@codemcp/workflows-core';
|
11
|
+
import { ProjectDocsManager } from '@codemcp/workflows-core';
|
12
|
+
import type { ConversationContext } from '../../src/types.js';
|
13
|
+
import type { InstructionContext } from '../../src/instruction-generator.js';
|
14
|
+
import { join } from 'node:path';
|
15
|
+
|
16
|
+
// Mock PlanManager
|
17
|
+
vi.mock('../../src/plan-manager.js');
|
18
|
+
|
19
|
+
// Mock ProjectDocsManager
|
20
|
+
vi.mock('../../src/project-docs-manager.js');
|
21
|
+
|
22
|
+
describe('InstructionGenerator', () => {
|
23
|
+
let instructionGenerator: InstructionGenerator;
|
24
|
+
let mockPlanManager: Mocked<PlanManager>;
|
25
|
+
let mockProjectDocsManager: Mocked<ProjectDocsManager>;
|
26
|
+
let testProjectPath: string;
|
27
|
+
let mockConversationContext: ConversationContext;
|
28
|
+
let mockInstructionContext: InstructionContext;
|
29
|
+
|
30
|
+
beforeEach(() => {
|
31
|
+
testProjectPath = '/test/project';
|
32
|
+
|
33
|
+
// Mock PlanManager
|
34
|
+
mockPlanManager = {
|
35
|
+
generatePlanFileGuidance: vi
|
36
|
+
.fn()
|
37
|
+
.mockReturnValue('Test plan file guidance'),
|
38
|
+
} as unknown;
|
39
|
+
|
40
|
+
// Mock ProjectDocsManager
|
41
|
+
mockProjectDocsManager = {
|
42
|
+
getVariableSubstitutions: vi.fn().mockReturnValue({
|
43
|
+
$ARCHITECTURE_DOC: join(
|
44
|
+
testProjectPath,
|
45
|
+
'.vibe',
|
46
|
+
'docs',
|
47
|
+
'architecture.md'
|
48
|
+
),
|
49
|
+
$REQUIREMENTS_DOC: join(
|
50
|
+
testProjectPath,
|
51
|
+
'.vibe',
|
52
|
+
'docs',
|
53
|
+
'requirements.md'
|
54
|
+
),
|
55
|
+
$DESIGN_DOC: join(testProjectPath, '.vibe', 'docs', 'design.md'),
|
56
|
+
}),
|
57
|
+
} as unknown;
|
58
|
+
|
59
|
+
// Create instruction generator and inject mocks
|
60
|
+
instructionGenerator = new InstructionGenerator(mockPlanManager);
|
61
|
+
TestAccess.injectMock(
|
62
|
+
instructionGenerator,
|
63
|
+
'projectDocsManager',
|
64
|
+
mockProjectDocsManager
|
65
|
+
);
|
66
|
+
|
67
|
+
// Mock conversation context
|
68
|
+
mockConversationContext = {
|
69
|
+
projectPath: testProjectPath,
|
70
|
+
planFilePath: join(testProjectPath, '.vibe', 'plan.md'),
|
71
|
+
gitBranch: 'main',
|
72
|
+
conversationId: 'test-conversation',
|
73
|
+
} as ConversationContext;
|
74
|
+
|
75
|
+
// Mock instruction context
|
76
|
+
mockInstructionContext = {
|
77
|
+
phase: 'design',
|
78
|
+
conversationContext: mockConversationContext,
|
79
|
+
transitionReason: 'Test transition',
|
80
|
+
isModeled: false,
|
81
|
+
planFileExists: true,
|
82
|
+
};
|
83
|
+
});
|
84
|
+
|
85
|
+
describe('generateInstructions', () => {
|
86
|
+
it('should apply variable substitution to base instructions', async () => {
|
87
|
+
const baseInstructions =
|
88
|
+
'Review the architecture in $ARCHITECTURE_DOC and update requirements in $REQUIREMENTS_DOC.';
|
89
|
+
|
90
|
+
const result = await instructionGenerator.generateInstructions(
|
91
|
+
baseInstructions,
|
92
|
+
mockInstructionContext
|
93
|
+
);
|
94
|
+
|
95
|
+
expect(result.instructions).toContain(
|
96
|
+
join(testProjectPath, '.vibe', 'docs', 'architecture.md')
|
97
|
+
);
|
98
|
+
expect(result.instructions).toContain(
|
99
|
+
join(testProjectPath, '.vibe', 'docs', 'requirements.md')
|
100
|
+
);
|
101
|
+
expect(result.instructions).not.toContain('$ARCHITECTURE_DOC');
|
102
|
+
expect(result.instructions).not.toContain('$REQUIREMENTS_DOC');
|
103
|
+
});
|
104
|
+
|
105
|
+
it('should handle multiple occurrences of the same variable', async () => {
|
106
|
+
const baseInstructions =
|
107
|
+
'Check $DESIGN_DOC for details. Update $DESIGN_DOC with new information.';
|
108
|
+
|
109
|
+
const result = await instructionGenerator.generateInstructions(
|
110
|
+
baseInstructions,
|
111
|
+
mockInstructionContext
|
112
|
+
);
|
113
|
+
|
114
|
+
const designDocPath = join(testProjectPath, '.vibe', 'docs', 'design.md');
|
115
|
+
const occurrences = (
|
116
|
+
result.instructions.match(
|
117
|
+
new RegExp(designDocPath.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g')
|
118
|
+
) || []
|
119
|
+
).length;
|
120
|
+
expect(occurrences).toBe(2);
|
121
|
+
expect(result.instructions).not.toContain('$DESIGN_DOC');
|
122
|
+
});
|
123
|
+
|
124
|
+
it('should handle instructions with no variables', async () => {
|
125
|
+
const baseInstructions = 'Continue with the current phase tasks.';
|
126
|
+
|
127
|
+
const result = await instructionGenerator.generateInstructions(
|
128
|
+
baseInstructions,
|
129
|
+
mockInstructionContext
|
130
|
+
);
|
131
|
+
|
132
|
+
expect(result.instructions).toContain(
|
133
|
+
'Continue with the current phase tasks.'
|
134
|
+
);
|
135
|
+
expect(
|
136
|
+
mockProjectDocsManager.getVariableSubstitutions
|
137
|
+
).toHaveBeenCalledWith(testProjectPath);
|
138
|
+
});
|
139
|
+
|
140
|
+
it('should handle all three document variables', async () => {
|
141
|
+
const baseInstructions =
|
142
|
+
'Review $ARCHITECTURE_DOC, check $REQUIREMENTS_DOC, and update $DESIGN_DOC.';
|
143
|
+
|
144
|
+
const result = await instructionGenerator.generateInstructions(
|
145
|
+
baseInstructions,
|
146
|
+
mockInstructionContext
|
147
|
+
);
|
148
|
+
|
149
|
+
expect(result.instructions).toContain(
|
150
|
+
join(testProjectPath, '.vibe', 'docs', 'architecture.md')
|
151
|
+
);
|
152
|
+
expect(result.instructions).toContain(
|
153
|
+
join(testProjectPath, '.vibe', 'docs', 'requirements.md')
|
154
|
+
);
|
155
|
+
expect(result.instructions).toContain(
|
156
|
+
join(testProjectPath, '.vibe', 'docs', 'design.md')
|
157
|
+
);
|
158
|
+
expect(result.instructions).not.toContain('$ARCHITECTURE_DOC');
|
159
|
+
expect(result.instructions).not.toContain('$REQUIREMENTS_DOC');
|
160
|
+
expect(result.instructions).not.toContain('$DESIGN_DOC');
|
161
|
+
});
|
162
|
+
|
163
|
+
it('should preserve enhanced instruction structure', async () => {
|
164
|
+
const baseInstructions = 'Work on design tasks using $DESIGN_DOC.';
|
165
|
+
|
166
|
+
const result = await instructionGenerator.generateInstructions(
|
167
|
+
baseInstructions,
|
168
|
+
mockInstructionContext
|
169
|
+
);
|
170
|
+
|
171
|
+
// Should contain enhanced instruction elements
|
172
|
+
expect(result.instructions).toContain('Check your plan file');
|
173
|
+
expect(result.instructions).toContain('**Plan File Guidance:**');
|
174
|
+
expect(result.instructions).toContain('**Project Context:**');
|
175
|
+
expect(result.instructions).toContain('Project: /test/project');
|
176
|
+
expect(result.instructions).toContain('Branch: main');
|
177
|
+
expect(result.instructions).toContain('Current Phase: design');
|
178
|
+
|
179
|
+
// Should contain substituted variable
|
180
|
+
expect(result.instructions).toContain(
|
181
|
+
join(testProjectPath, '.vibe', 'docs', 'design.md')
|
182
|
+
);
|
183
|
+
});
|
184
|
+
|
185
|
+
it('should return correct metadata', async () => {
|
186
|
+
const baseInstructions = 'Test instructions with $ARCHITECTURE_DOC.';
|
187
|
+
|
188
|
+
const result = await instructionGenerator.generateInstructions(
|
189
|
+
baseInstructions,
|
190
|
+
mockInstructionContext
|
191
|
+
);
|
192
|
+
|
193
|
+
expect(result.metadata).toEqual({
|
194
|
+
phase: 'design',
|
195
|
+
planFilePath: join(testProjectPath, '.vibe', 'plan.md'),
|
196
|
+
transitionReason: 'Test transition',
|
197
|
+
isModeled: false,
|
198
|
+
});
|
199
|
+
});
|
200
|
+
|
201
|
+
it('should handle special regex characters in variables', async () => {
|
202
|
+
// Mock a variable with special characters (though unlikely in practice)
|
203
|
+
mockProjectDocsManager.getVariableSubstitutions.mockReturnValue({
|
204
|
+
'$TEST[DOC]': '/test/path/doc.md',
|
205
|
+
});
|
206
|
+
|
207
|
+
const baseInstructions = 'Check $TEST[DOC] for information.';
|
208
|
+
|
209
|
+
const result = await instructionGenerator.generateInstructions(
|
210
|
+
baseInstructions,
|
211
|
+
mockInstructionContext
|
212
|
+
);
|
213
|
+
|
214
|
+
expect(result.instructions).toContain('/test/path/doc.md');
|
215
|
+
expect(result.instructions).not.toContain('$TEST[DOC]');
|
216
|
+
});
|
217
|
+
});
|
218
|
+
|
219
|
+
describe('variable substitution edge cases', () => {
|
220
|
+
it('should handle empty substitutions', async () => {
|
221
|
+
mockProjectDocsManager.getVariableSubstitutions.mockReturnValue({});
|
222
|
+
|
223
|
+
const baseInstructions = 'Work on tasks without variables.';
|
224
|
+
|
225
|
+
const result = await instructionGenerator.generateInstructions(
|
226
|
+
baseInstructions,
|
227
|
+
mockInstructionContext
|
228
|
+
);
|
229
|
+
|
230
|
+
expect(result.instructions).toContain('Work on tasks without variables.');
|
231
|
+
});
|
232
|
+
|
233
|
+
it('should handle variables that do not exist in instructions', async () => {
|
234
|
+
const baseInstructions = 'Work on general tasks.';
|
235
|
+
|
236
|
+
const result = await instructionGenerator.generateInstructions(
|
237
|
+
baseInstructions,
|
238
|
+
mockInstructionContext
|
239
|
+
);
|
240
|
+
|
241
|
+
expect(result.instructions).toContain('Work on general tasks.');
|
242
|
+
expect(
|
243
|
+
mockProjectDocsManager.getVariableSubstitutions
|
244
|
+
).toHaveBeenCalledWith(testProjectPath);
|
245
|
+
});
|
246
|
+
});
|
247
|
+
});
|
@@ -0,0 +1,68 @@
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
2
|
+
import { ListWorkflowsHandler } from '../../packages/mcp-server/src/tool-handlers/list-workflows.js';
|
3
|
+
import { WorkflowManager } from '@codemcp/workflows-core';
|
4
|
+
|
5
|
+
describe('List Workflows Filtering', () => {
|
6
|
+
let originalEnv: string | undefined;
|
7
|
+
|
8
|
+
beforeEach(() => {
|
9
|
+
originalEnv = process.env.VIBE_WORKFLOW_DOMAINS;
|
10
|
+
});
|
11
|
+
|
12
|
+
afterEach(() => {
|
13
|
+
if (originalEnv !== undefined) {
|
14
|
+
process.env.VIBE_WORKFLOW_DOMAINS = originalEnv;
|
15
|
+
} else {
|
16
|
+
delete process.env.VIBE_WORKFLOW_DOMAINS;
|
17
|
+
}
|
18
|
+
});
|
19
|
+
|
20
|
+
it('should return only loaded workflows by default', async () => {
|
21
|
+
process.env.VIBE_WORKFLOW_DOMAINS = 'code';
|
22
|
+
|
23
|
+
const handler = new ListWorkflowsHandler();
|
24
|
+
const workflowManager = new WorkflowManager();
|
25
|
+
|
26
|
+
const context = {
|
27
|
+
workflowManager,
|
28
|
+
projectPath: process.cwd(),
|
29
|
+
} as { workflowManager: WorkflowManager; projectPath: string };
|
30
|
+
|
31
|
+
const result = await handler.executeHandler({}, context);
|
32
|
+
|
33
|
+
// Should only include code workflows
|
34
|
+
const hasCodeWorkflows = result.workflows.some(
|
35
|
+
w => w.name === 'waterfall' || w.name === 'epcc'
|
36
|
+
);
|
37
|
+
const hasOfficeWorkflows = result.workflows.some(w => w.name === 'posts');
|
38
|
+
|
39
|
+
expect(hasCodeWorkflows).toBe(true);
|
40
|
+
expect(hasOfficeWorkflows).toBe(false);
|
41
|
+
});
|
42
|
+
|
43
|
+
it('should return all workflows when include_unloaded is true', async () => {
|
44
|
+
process.env.VIBE_WORKFLOW_DOMAINS = 'code';
|
45
|
+
|
46
|
+
const handler = new ListWorkflowsHandler();
|
47
|
+
const workflowManager = new WorkflowManager();
|
48
|
+
|
49
|
+
const context = {
|
50
|
+
workflowManager,
|
51
|
+
projectPath: process.cwd(),
|
52
|
+
} as { workflowManager: WorkflowManager; projectPath: string };
|
53
|
+
|
54
|
+
const result = await handler.executeHandler(
|
55
|
+
{ include_unloaded: true },
|
56
|
+
context
|
57
|
+
);
|
58
|
+
|
59
|
+
// Should include workflows from all domains
|
60
|
+
const hasCodeWorkflows = result.workflows.some(
|
61
|
+
w => w.name === 'waterfall' || w.name === 'epcc'
|
62
|
+
);
|
63
|
+
const hasOfficeWorkflows = result.workflows.some(w => w.name === 'posts');
|
64
|
+
|
65
|
+
expect(hasCodeWorkflows).toBe(true);
|
66
|
+
expect(hasOfficeWorkflows).toBe(true);
|
67
|
+
});
|
68
|
+
});
|
@@ -0,0 +1,224 @@
|
|
1
|
+
/**
|
2
|
+
* Tests for "none" template functionality
|
3
|
+
*
|
4
|
+
* Tests the ability to disable specific document types using "none" templates
|
5
|
+
*/
|
6
|
+
|
7
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
8
|
+
import { ProjectDocsManager } from '@codemcp/workflows-core';
|
9
|
+
import { join } from 'node:path';
|
10
|
+
import { tmpdir } from 'node:os';
|
11
|
+
import { mkdir, rmdir, readFile } from 'node:fs/promises';
|
12
|
+
|
13
|
+
describe('None Template Functionality', () => {
|
14
|
+
let testProjectPath: string;
|
15
|
+
let projectDocsManager: ProjectDocsManager;
|
16
|
+
|
17
|
+
beforeEach(async () => {
|
18
|
+
// Create test project directory
|
19
|
+
testProjectPath = join(tmpdir(), `none-template-test-${Date.now()}`);
|
20
|
+
await mkdir(testProjectPath, { recursive: true });
|
21
|
+
|
22
|
+
projectDocsManager = new ProjectDocsManager();
|
23
|
+
});
|
24
|
+
|
25
|
+
afterEach(async () => {
|
26
|
+
// Clean up test directory
|
27
|
+
try {
|
28
|
+
await rmdir(testProjectPath, { recursive: true });
|
29
|
+
} catch {
|
30
|
+
// Ignore cleanup errors
|
31
|
+
}
|
32
|
+
});
|
33
|
+
|
34
|
+
describe('None Template Creation', () => {
|
35
|
+
it('should create none template for architecture', async () => {
|
36
|
+
const result = await projectDocsManager.createOrLinkProjectDocs(
|
37
|
+
testProjectPath,
|
38
|
+
{
|
39
|
+
architecture: 'none',
|
40
|
+
requirements: 'freestyle',
|
41
|
+
design: 'freestyle',
|
42
|
+
},
|
43
|
+
{}
|
44
|
+
);
|
45
|
+
|
46
|
+
expect(result.created).toContain('architecture.md');
|
47
|
+
expect(result.created).toContain('requirements.md');
|
48
|
+
expect(result.created).toContain('design.md');
|
49
|
+
|
50
|
+
// Verify the none template content
|
51
|
+
const archPath = await projectDocsManager.readDocument(
|
52
|
+
testProjectPath,
|
53
|
+
'architecture'
|
54
|
+
);
|
55
|
+
expect(archPath).toContain('architecture.md');
|
56
|
+
|
57
|
+
// Read the actual file to verify placeholder content
|
58
|
+
const archContent = await readFile(archPath, 'utf-8');
|
59
|
+
expect(archContent).toContain('Architecture Placeholder');
|
60
|
+
expect(archContent).toContain('DO NOT EDIT THIS FILE');
|
61
|
+
expect(archContent).toContain('plan file');
|
62
|
+
});
|
63
|
+
|
64
|
+
it('should create none template for requirements', async () => {
|
65
|
+
const result = await projectDocsManager.createOrLinkProjectDocs(
|
66
|
+
testProjectPath,
|
67
|
+
{
|
68
|
+
architecture: 'freestyle',
|
69
|
+
requirements: 'none',
|
70
|
+
design: 'freestyle',
|
71
|
+
},
|
72
|
+
{}
|
73
|
+
);
|
74
|
+
|
75
|
+
expect(result.created).toContain('requirements.md');
|
76
|
+
|
77
|
+
// Verify the none template content
|
78
|
+
const reqPath = await projectDocsManager.readDocument(
|
79
|
+
testProjectPath,
|
80
|
+
'requirements'
|
81
|
+
);
|
82
|
+
expect(reqPath).toContain('requirements.md');
|
83
|
+
|
84
|
+
// Read the actual file to verify placeholder content
|
85
|
+
const reqContent = await readFile(reqPath, 'utf-8');
|
86
|
+
expect(reqContent).toContain('Requirements Placeholder');
|
87
|
+
expect(reqContent).toContain('DO NOT EDIT THIS FILE');
|
88
|
+
expect(reqContent).toContain('plan file');
|
89
|
+
});
|
90
|
+
|
91
|
+
it('should create none template for design', async () => {
|
92
|
+
const result = await projectDocsManager.createOrLinkProjectDocs(
|
93
|
+
testProjectPath,
|
94
|
+
{
|
95
|
+
architecture: 'freestyle',
|
96
|
+
requirements: 'freestyle',
|
97
|
+
design: 'none',
|
98
|
+
},
|
99
|
+
{}
|
100
|
+
);
|
101
|
+
|
102
|
+
expect(result.created).toContain('design.md');
|
103
|
+
|
104
|
+
// Verify the none template content
|
105
|
+
const designPath = await projectDocsManager.readDocument(
|
106
|
+
testProjectPath,
|
107
|
+
'design'
|
108
|
+
);
|
109
|
+
expect(designPath).toContain('design.md');
|
110
|
+
|
111
|
+
// Read the actual file to verify placeholder content
|
112
|
+
const designContent = await readFile(designPath, 'utf-8');
|
113
|
+
expect(designContent).toContain('Design Placeholder');
|
114
|
+
expect(designContent).toContain('DO NOT EDIT THIS FILE');
|
115
|
+
expect(designContent).toContain('plan file');
|
116
|
+
});
|
117
|
+
|
118
|
+
it('should support mixed usage with none templates', async () => {
|
119
|
+
// Create a test README file
|
120
|
+
const readmePath = join(testProjectPath, 'README.md');
|
121
|
+
await mkdir(testProjectPath, { recursive: true });
|
122
|
+
const fs = await import('node:fs/promises');
|
123
|
+
await fs.writeFile(
|
124
|
+
readmePath,
|
125
|
+
'# Test Project\n\nThis is a test project.'
|
126
|
+
);
|
127
|
+
|
128
|
+
const result = await projectDocsManager.createOrLinkProjectDocs(
|
129
|
+
testProjectPath,
|
130
|
+
{
|
131
|
+
architecture: 'freestyle', // Template
|
132
|
+
design: 'none', // None template
|
133
|
+
},
|
134
|
+
{
|
135
|
+
requirements: readmePath, // File link
|
136
|
+
}
|
137
|
+
);
|
138
|
+
|
139
|
+
expect(result.created).toContain('architecture.md');
|
140
|
+
expect(result.created).toContain('design.md');
|
141
|
+
expect(result.linked).toContain('requirements.md');
|
142
|
+
|
143
|
+
// Verify each document type
|
144
|
+
const archPath = await projectDocsManager.readDocument(
|
145
|
+
testProjectPath,
|
146
|
+
'architecture'
|
147
|
+
);
|
148
|
+
const archContent = await readFile(archPath, 'utf-8');
|
149
|
+
expect(archContent).toContain('INSTRUCTIONS FOR ARCHITECTURE');
|
150
|
+
|
151
|
+
const reqPath = await projectDocsManager.readDocument(
|
152
|
+
testProjectPath,
|
153
|
+
'requirements'
|
154
|
+
);
|
155
|
+
const reqContent = await readFile(reqPath, 'utf-8');
|
156
|
+
expect(reqContent).toContain('This is a test project');
|
157
|
+
|
158
|
+
const designPath = await projectDocsManager.readDocument(
|
159
|
+
testProjectPath,
|
160
|
+
'design'
|
161
|
+
);
|
162
|
+
const designContent = await readFile(designPath, 'utf-8');
|
163
|
+
expect(designContent).toContain('Design Placeholder');
|
164
|
+
expect(designContent).toContain('DO NOT EDIT THIS FILE');
|
165
|
+
});
|
166
|
+
|
167
|
+
it('should create all none templates when all are disabled', async () => {
|
168
|
+
const result = await projectDocsManager.createOrLinkProjectDocs(
|
169
|
+
testProjectPath,
|
170
|
+
{
|
171
|
+
architecture: 'none',
|
172
|
+
requirements: 'none',
|
173
|
+
design: 'none',
|
174
|
+
},
|
175
|
+
{}
|
176
|
+
);
|
177
|
+
|
178
|
+
expect(result.created).toEqual([
|
179
|
+
'architecture.md',
|
180
|
+
'requirements.md',
|
181
|
+
'design.md',
|
182
|
+
]);
|
183
|
+
expect(result.linked).toEqual([]);
|
184
|
+
|
185
|
+
// Verify all contain placeholder content
|
186
|
+
const archPath = await projectDocsManager.readDocument(
|
187
|
+
testProjectPath,
|
188
|
+
'architecture'
|
189
|
+
);
|
190
|
+
const reqPath = await projectDocsManager.readDocument(
|
191
|
+
testProjectPath,
|
192
|
+
'requirements'
|
193
|
+
);
|
194
|
+
const designPath = await projectDocsManager.readDocument(
|
195
|
+
testProjectPath,
|
196
|
+
'design'
|
197
|
+
);
|
198
|
+
|
199
|
+
const archContent = await readFile(archPath, 'utf-8');
|
200
|
+
const reqContent = await readFile(reqPath, 'utf-8');
|
201
|
+
const designContent = await readFile(designPath, 'utf-8');
|
202
|
+
|
203
|
+
expect(archContent).toContain('Architecture Placeholder');
|
204
|
+
expect(reqContent).toContain('Requirements Placeholder');
|
205
|
+
expect(designContent).toContain('Design Placeholder');
|
206
|
+
|
207
|
+
// All should contain the DO NOT EDIT instruction
|
208
|
+
expect(archContent).toContain('DO NOT EDIT THIS FILE');
|
209
|
+
expect(reqContent).toContain('DO NOT EDIT THIS FILE');
|
210
|
+
expect(designContent).toContain('DO NOT EDIT THIS FILE');
|
211
|
+
});
|
212
|
+
});
|
213
|
+
|
214
|
+
describe('Template Discovery', () => {
|
215
|
+
it('should include none in available templates', async () => {
|
216
|
+
const availableTemplates =
|
217
|
+
await projectDocsManager.templateManager.getAvailableTemplates();
|
218
|
+
|
219
|
+
expect(availableTemplates.architecture).toContain('none');
|
220
|
+
expect(availableTemplates.requirements).toContain('none');
|
221
|
+
expect(availableTemplates.design).toContain('none');
|
222
|
+
});
|
223
|
+
});
|
224
|
+
});
|