opencode-conductor-cdd-plugin 1.0.0-beta.17 → 1.0.0-beta.19

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.
Files changed (55) hide show
  1. package/dist/prompts/agent/cdd.md +16 -16
  2. package/dist/prompts/agent/implementer.md +5 -5
  3. package/dist/prompts/agent.md +7 -7
  4. package/dist/prompts/cdd/implement.json +1 -1
  5. package/dist/prompts/cdd/revert.json +1 -1
  6. package/dist/prompts/cdd/setup.json +2 -2
  7. package/dist/prompts/cdd/setup.test.js +40 -118
  8. package/dist/prompts/cdd/setup.test.ts +40 -143
  9. package/dist/test/integration/rebrand.test.js +15 -14
  10. package/dist/utils/agentMapping.js +2 -0
  11. package/dist/utils/archive-tracks.d.ts +28 -0
  12. package/dist/utils/archive-tracks.js +154 -1
  13. package/dist/utils/archive-tracks.test.d.ts +1 -0
  14. package/dist/utils/archive-tracks.test.js +495 -0
  15. package/dist/utils/codebaseAnalysis.d.ts +61 -0
  16. package/dist/utils/codebaseAnalysis.js +429 -0
  17. package/dist/utils/codebaseAnalysis.test.d.ts +1 -0
  18. package/dist/utils/codebaseAnalysis.test.js +556 -0
  19. package/dist/utils/documentGeneration.d.ts +97 -0
  20. package/dist/utils/documentGeneration.js +301 -0
  21. package/dist/utils/documentGeneration.test.d.ts +1 -0
  22. package/dist/utils/documentGeneration.test.js +380 -0
  23. package/dist/utils/interactiveMenu.d.ts +56 -0
  24. package/dist/utils/interactiveMenu.js +144 -0
  25. package/dist/utils/interactiveMenu.test.d.ts +1 -0
  26. package/dist/utils/interactiveMenu.test.js +231 -0
  27. package/dist/utils/interactiveSetup.d.ts +43 -0
  28. package/dist/utils/interactiveSetup.js +131 -0
  29. package/dist/utils/interactiveSetup.test.d.ts +1 -0
  30. package/dist/utils/interactiveSetup.test.js +124 -0
  31. package/dist/utils/metadataTracker.d.ts +39 -0
  32. package/dist/utils/metadataTracker.js +105 -0
  33. package/dist/utils/metadataTracker.test.d.ts +1 -0
  34. package/dist/utils/metadataTracker.test.js +265 -0
  35. package/dist/utils/planParser.d.ts +25 -0
  36. package/dist/utils/planParser.js +107 -0
  37. package/dist/utils/planParser.test.d.ts +1 -0
  38. package/dist/utils/planParser.test.js +119 -0
  39. package/dist/utils/projectMaturity.d.ts +53 -0
  40. package/dist/utils/projectMaturity.js +179 -0
  41. package/dist/utils/projectMaturity.test.d.ts +1 -0
  42. package/dist/utils/projectMaturity.test.js +298 -0
  43. package/dist/utils/questionGenerator.d.ts +51 -0
  44. package/dist/utils/questionGenerator.js +535 -0
  45. package/dist/utils/questionGenerator.test.d.ts +1 -0
  46. package/dist/utils/questionGenerator.test.js +328 -0
  47. package/dist/utils/setupIntegration.d.ts +72 -0
  48. package/dist/utils/setupIntegration.js +179 -0
  49. package/dist/utils/setupIntegration.test.d.ts +1 -0
  50. package/dist/utils/setupIntegration.test.js +344 -0
  51. package/dist/utils/statusDisplay.d.ts +35 -0
  52. package/dist/utils/statusDisplay.js +81 -0
  53. package/dist/utils/statusDisplay.test.d.ts +1 -0
  54. package/dist/utils/statusDisplay.test.js +102 -0
  55. package/package.json +1 -1
@@ -0,0 +1,328 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { classifyQuestionType, addQuestionSuffix, generateBrownfieldQuestions, generateGreenfieldQuestions, generateAnswerOptions, formatQuestion, generateQuestionsForSection, } from './questionGenerator.js';
3
+ /**
4
+ * Question Generation Engine Tests
5
+ *
6
+ * This module is responsible for generating context-aware questions for setup:
7
+ * - Classification: Additive (multiple answers) vs Exclusive Choice (single answer)
8
+ * - Context-aware generation for brownfield projects (based on codebase analysis)
9
+ * - Pattern-based generation for greenfield projects (common best practices)
10
+ * - Answer option generation (3 contextual options + D/E standard options)
11
+ *
12
+ * Based on reference implementations from:
13
+ * - derekbar90/opencode-conductor
14
+ * - gemini-cli-extensions/conductor
15
+ */
16
+ const mockBrownfieldAnalysis = {
17
+ languages: { TypeScript: 70, JavaScript: 30 },
18
+ frameworks: {
19
+ frontend: ['React', 'Next.js'],
20
+ backend: ['Express'],
21
+ },
22
+ databases: ['PostgreSQL', 'Redis'],
23
+ architecture: ['monorepo', 'layered-architecture'],
24
+ manifests: [
25
+ {
26
+ type: 'package.json',
27
+ path: '/test/package.json',
28
+ metadata: { name: 'test-app', version: '1.0.0', description: 'A test application' },
29
+ dependencies: { react: '^18.0.0', express: '^4.18.0' },
30
+ },
31
+ ],
32
+ projectGoal: 'A test application for demonstrating brownfield analysis',
33
+ };
34
+ describe('Question Generation Engine', () => {
35
+ describe('classifyQuestionType', () => {
36
+ it('should classify as additive when question asks for multiple selections', () => {
37
+ const result = classifyQuestionType('What are the key features?');
38
+ expect(result).toBe('additive');
39
+ });
40
+ it('should classify as exclusive when question asks for single choice', () => {
41
+ const result = classifyQuestionType('What is the primary goal?');
42
+ expect(result).toBe('exclusive');
43
+ });
44
+ it('should add "(Select all that apply)" suffix for additive questions', () => {
45
+ const result = addQuestionSuffix('What are the features?', 'additive');
46
+ expect(result).toContain('(Select all that apply)');
47
+ });
48
+ it('should NOT add suffix for exclusive questions', () => {
49
+ const result = addQuestionSuffix('What is the goal?', 'exclusive');
50
+ expect(result).not.toContain('(Select all that apply)');
51
+ });
52
+ it('should handle edge case: questions with "or" are exclusive', () => {
53
+ const result = classifyQuestionType('Is this A or B?');
54
+ expect(result).toBe('exclusive');
55
+ });
56
+ it('should handle edge case: questions with "and" might be additive', () => {
57
+ const result = classifyQuestionType('What features and capabilities do you need?');
58
+ expect(result).toBe('additive');
59
+ });
60
+ });
61
+ describe('generateBrownfieldQuestions', () => {
62
+ it('should generate product questions based on project goal from README', () => {
63
+ const questions = generateBrownfieldQuestions('product', mockBrownfieldAnalysis);
64
+ expect(questions.length).toBeGreaterThan(0);
65
+ expect(questions[0].section).toBe('product');
66
+ expect(questions[0].text).toContain('goal');
67
+ });
68
+ it('should generate tech-stack questions based on detected languages', () => {
69
+ const questions = generateBrownfieldQuestions('tech-stack', mockBrownfieldAnalysis);
70
+ const langQuestion = questions.find(q => q.id === 'techstack_languages');
71
+ expect(langQuestion).toBeDefined();
72
+ expect(langQuestion?.options).toContain('TypeScript');
73
+ });
74
+ it('should generate tech-stack questions based on detected frameworks', () => {
75
+ const questions = generateBrownfieldQuestions('tech-stack', mockBrownfieldAnalysis);
76
+ const fwQuestion = questions.find(q => q.id === 'techstack_frameworks');
77
+ expect(fwQuestion).toBeDefined();
78
+ expect(fwQuestion?.options).toContain('React');
79
+ });
80
+ it('should generate questions based on detected architecture', () => {
81
+ const questions = generateBrownfieldQuestions('tech-stack', mockBrownfieldAnalysis);
82
+ const archQuestion = questions.find(q => q.id === 'techstack_architecture');
83
+ expect(archQuestion).toBeDefined();
84
+ });
85
+ it('should generate 3 contextual answer options from codebase analysis', () => {
86
+ const questions = generateBrownfieldQuestions('product', mockBrownfieldAnalysis);
87
+ questions.forEach(q => {
88
+ expect(q.options.length).toBe(5); // A-C contextual + D (Custom) + E (Auto-generate)
89
+ });
90
+ });
91
+ it('should always include "Type your own answer" as option D', () => {
92
+ const questions = generateBrownfieldQuestions('product', mockBrownfieldAnalysis);
93
+ const options = generateAnswerOptions(questions[0].options, 'product');
94
+ expect(options[3]).toBe('Type your own answer');
95
+ });
96
+ it('should always include "Autogenerate and review [section].md" as option E', () => {
97
+ const questions = generateBrownfieldQuestions('product', mockBrownfieldAnalysis);
98
+ const options = generateAnswerOptions(questions[0].options, 'product');
99
+ expect(options[4]).toBe('Autogenerate and review product.md');
100
+ });
101
+ it('should limit to max 5 questions per section', () => {
102
+ const questions = generateBrownfieldQuestions('tech-stack', mockBrownfieldAnalysis);
103
+ expect(questions.length).toBeLessThanOrEqual(5);
104
+ });
105
+ it('should prioritize questions based on project complexity', () => {
106
+ const questions = generateBrownfieldQuestions('tech-stack', mockBrownfieldAnalysis);
107
+ // Complex projects should have more specific questions
108
+ expect(questions.length).toBeGreaterThan(0);
109
+ });
110
+ });
111
+ describe('generateGreenfieldQuestions', () => {
112
+ it('should generate product questions using common patterns', () => {
113
+ const questions = generateGreenfieldQuestions('product');
114
+ expect(questions.length).toBeGreaterThan(0);
115
+ expect(questions[0].section).toBe('product');
116
+ });
117
+ it('should generate tech-stack questions with popular framework options', () => {
118
+ const questions = generateGreenfieldQuestions('tech-stack');
119
+ const frontendQ = questions.find(q => q.id === 'techstack_frontend');
120
+ expect(frontendQ?.options).toContain('React');
121
+ expect(frontendQ?.options).toContain('Vue');
122
+ });
123
+ it('should generate 3 pattern-based answer options', () => {
124
+ const questions = generateGreenfieldQuestions('product');
125
+ questions.forEach(q => {
126
+ expect(q.options.length).toBe(5); // A-C contextual + D (Custom) + E (Auto-generate)
127
+ });
128
+ });
129
+ it('should always include "Type your own answer" as option D', () => {
130
+ const questions = generateGreenfieldQuestions('product');
131
+ const options = generateAnswerOptions(questions[0].options, 'product');
132
+ expect(options[3]).toBe('Type your own answer');
133
+ });
134
+ it('should always include "Autogenerate and review [section].md" as option E', () => {
135
+ const questions = generateGreenfieldQuestions('product');
136
+ const options = generateAnswerOptions(questions[0].options, 'product');
137
+ expect(options[4]).toBe('Autogenerate and review product.md');
138
+ });
139
+ it('should limit to max 5 questions per section', () => {
140
+ const questions = generateGreenfieldQuestions('tech-stack');
141
+ expect(questions.length).toBeLessThanOrEqual(5);
142
+ });
143
+ it('should cover all major decision points for new projects', () => {
144
+ const sections = ['product', 'guidelines', 'tech-stack', 'styleguides', 'workflow'];
145
+ sections.forEach(section => {
146
+ const questions = generateGreenfieldQuestions(section);
147
+ expect(questions.length).toBeGreaterThan(0);
148
+ });
149
+ });
150
+ });
151
+ describe('generateAnswerOptions', () => {
152
+ it('should generate 3 contextual options for brownfield projects', () => {
153
+ const options = generateAnswerOptions(['Option 1', 'Option 2', 'Option 3'], 'product');
154
+ expect(options.slice(0, 3)).toEqual(['Option 1', 'Option 2', 'Option 3']);
155
+ });
156
+ it('should generate 3 pattern-based options for greenfield projects', () => {
157
+ const options = generateAnswerOptions(['Pattern A', 'Pattern B', 'Pattern C'], 'product');
158
+ expect(options.slice(0, 3)).toEqual(['Pattern A', 'Pattern B', 'Pattern C']);
159
+ });
160
+ it('should make options mutually exclusive for exclusive questions', () => {
161
+ // This is enforced by question design, not by code
162
+ const options = generateAnswerOptions(['Option 1', 'Option 2', 'Option 3'], 'product');
163
+ expect(options.length).toBe(5);
164
+ });
165
+ it('should make options complementary for additive questions', () => {
166
+ // This is enforced by question design, not by code
167
+ const options = generateAnswerOptions(['Feature 1', 'Feature 2', 'Feature 3'], 'product');
168
+ expect(options.length).toBe(5);
169
+ });
170
+ it('should keep options concise (max 80 characters)', () => {
171
+ const longOption = 'This is a very long option that exceeds eighty characters and should be truncated automatically';
172
+ const options = generateAnswerOptions([longOption, 'Short', 'Another'], 'product');
173
+ expect(options[0].length).toBeLessThanOrEqual(80);
174
+ });
175
+ it('should order options by relevance/popularity', () => {
176
+ // Options are already ordered by caller
177
+ const options = generateAnswerOptions(['Most relevant', 'Second', 'Third'], 'product');
178
+ expect(options[0]).toBe('Most relevant');
179
+ });
180
+ });
181
+ describe('formatQuestion', () => {
182
+ it('should format question with lettered options (A, B, C, D, E)', () => {
183
+ const question = {
184
+ id: 'test',
185
+ text: 'What is your choice?',
186
+ type: 'exclusive',
187
+ section: 'product',
188
+ options: ['Option 1', 'Option 2', 'Option 3'],
189
+ };
190
+ const formatted = formatQuestion(question, 1);
191
+ expect(formatted).toContain('A)');
192
+ expect(formatted).toContain('B)');
193
+ expect(formatted).toContain('C)');
194
+ expect(formatted).toContain('D)');
195
+ expect(formatted).toContain('E)');
196
+ });
197
+ it('should include additive suffix when type is additive', () => {
198
+ const question = {
199
+ id: 'test',
200
+ text: 'What are your choices?',
201
+ type: 'additive',
202
+ section: 'product',
203
+ options: ['Option 1', 'Option 2', 'Option 3'],
204
+ };
205
+ const formatted = formatQuestion(question, 1);
206
+ expect(formatted).toContain('(Select all that apply)');
207
+ });
208
+ it('should format option D as "Type your own answer"', () => {
209
+ const question = {
210
+ id: 'test',
211
+ text: 'Test question',
212
+ type: 'exclusive',
213
+ section: 'product',
214
+ options: ['A', 'B', 'C'],
215
+ };
216
+ const formatted = formatQuestion(question, 1);
217
+ expect(formatted).toContain('D) Type your own answer');
218
+ });
219
+ it('should format option E with section-specific document name', () => {
220
+ const question = {
221
+ id: 'test',
222
+ text: 'Test question',
223
+ type: 'exclusive',
224
+ section: 'product',
225
+ options: ['A', 'B', 'C'],
226
+ };
227
+ const formatted = formatQuestion(question, 1);
228
+ expect(formatted).toContain('E) Autogenerate and review product.md');
229
+ });
230
+ it('should number questions sequentially within section', () => {
231
+ const question = {
232
+ id: 'test',
233
+ text: 'Test question',
234
+ type: 'exclusive',
235
+ section: 'product',
236
+ options: ['A', 'B', 'C'],
237
+ };
238
+ const formatted = formatQuestion(question, 3);
239
+ expect(formatted).toContain('Question 3:');
240
+ });
241
+ });
242
+ describe('generateQuestionsForSection', () => {
243
+ it('should generate questions for product.md section', () => {
244
+ const questions = generateQuestionsForSection('product', 'greenfield');
245
+ expect(questions.length).toBeGreaterThan(0);
246
+ expect(questions[0].section).toBe('product');
247
+ });
248
+ it('should generate questions for guidelines.md section', () => {
249
+ const questions = generateQuestionsForSection('guidelines', 'greenfield');
250
+ expect(questions.length).toBeGreaterThan(0);
251
+ expect(questions[0].section).toBe('guidelines');
252
+ });
253
+ it('should generate questions for tech-stack.md section', () => {
254
+ const questions = generateQuestionsForSection('tech-stack', 'greenfield');
255
+ expect(questions.length).toBeGreaterThan(0);
256
+ expect(questions[0].section).toBe('tech-stack');
257
+ });
258
+ it('should generate questions for styleguides.md section', () => {
259
+ const questions = generateQuestionsForSection('styleguides', 'greenfield');
260
+ expect(questions.length).toBeGreaterThan(0);
261
+ expect(questions[0].section).toBe('styleguides');
262
+ });
263
+ it('should generate questions for workflow.md section', () => {
264
+ const questions = generateQuestionsForSection('workflow', 'greenfield');
265
+ expect(questions.length).toBeGreaterThan(0);
266
+ expect(questions[0].section).toBe('workflow');
267
+ });
268
+ it('should adapt questions based on project maturity', () => {
269
+ const greenfieldQuestions = generateQuestionsForSection('product', 'greenfield');
270
+ const brownfieldQuestions = generateQuestionsForSection('product', 'brownfield', mockBrownfieldAnalysis);
271
+ // Both should generate questions, but content may differ
272
+ expect(greenfieldQuestions.length).toBeGreaterThan(0);
273
+ expect(brownfieldQuestions.length).toBeGreaterThan(0);
274
+ });
275
+ it('should respect max 5 questions per section', () => {
276
+ const questions = generateQuestionsForSection('tech-stack', 'greenfield');
277
+ expect(questions.length).toBeLessThanOrEqual(5);
278
+ });
279
+ });
280
+ describe('Integration: Question Generation Flow', () => {
281
+ it('should generate complete question set for brownfield project', () => {
282
+ const sections = ['product', 'guidelines', 'tech-stack', 'styleguides', 'workflow'];
283
+ sections.forEach(section => {
284
+ const questions = generateQuestionsForSection(section, 'brownfield', mockBrownfieldAnalysis);
285
+ expect(questions.length).toBeGreaterThan(0);
286
+ expect(questions.length).toBeLessThanOrEqual(5);
287
+ questions.forEach(q => {
288
+ expect(q.section).toBe(section);
289
+ expect(q.options.length).toBe(5); // A-C contextual + D (Custom) + E (Auto-generate)
290
+ expect(['additive', 'exclusive']).toContain(q.type);
291
+ const fullOptions = generateAnswerOptions(q.options, section);
292
+ expect(fullOptions[3]).toBe('Type your own answer');
293
+ expect(fullOptions[4]).toBe(`Autogenerate and review ${section}.md`);
294
+ });
295
+ });
296
+ });
297
+ it('should generate complete question set for greenfield project', () => {
298
+ const sections = ['product', 'guidelines', 'tech-stack', 'styleguides', 'workflow'];
299
+ sections.forEach(section => {
300
+ const questions = generateQuestionsForSection(section, 'greenfield');
301
+ expect(questions.length).toBeGreaterThan(0);
302
+ expect(questions.length).toBeLessThanOrEqual(5);
303
+ questions.forEach(q => {
304
+ expect(q.section).toBe(section);
305
+ expect(q.options.length).toBe(5); // A-C contextual + D (Custom) + E (Auto-generate)
306
+ expect(['additive', 'exclusive']).toContain(q.type);
307
+ });
308
+ });
309
+ });
310
+ it('should maintain consistency across all sections', () => {
311
+ const sections = ['product', 'guidelines', 'tech-stack', 'styleguides', 'workflow'];
312
+ const allQuestions = [];
313
+ sections.forEach(section => {
314
+ const questions = generateQuestionsForSection(section, 'greenfield');
315
+ allQuestions.push(...questions);
316
+ });
317
+ // All questions should have consistent structure
318
+ allQuestions.forEach(q => {
319
+ expect(q.id).toBeTruthy();
320
+ expect(q.text).toBeTruthy();
321
+ expect(q.type).toBeTruthy();
322
+ expect(q.section).toBeTruthy();
323
+ expect(Array.isArray(q.options)).toBe(true);
324
+ expect(q.options.length).toBe(5); // A-C contextual + D (Custom) + E (Auto-generate)
325
+ });
326
+ });
327
+ });
328
+ });
@@ -0,0 +1,72 @@
1
+ import { Question } from './questionGenerator.js';
2
+ import { ApprovalResult, DocumentDraft } from './documentGeneration.js';
3
+ /**
4
+ * Setup Integration Module
5
+ *
6
+ * Orchestrates the complete setup workflow by integrating:
7
+ * - Project maturity detection (Phase 2)
8
+ * - Codebase analysis (Phase 2)
9
+ * - Question generation (Phase 3)
10
+ * - Interactive menu (Phase 4)
11
+ * - Document generation (Phase 5)
12
+ *
13
+ * Provides high-level functions for setting up each section:
14
+ * - setupProductGuide()
15
+ * - setupGuidelines()
16
+ * - setupTechStack()
17
+ * - setupStyleguides()
18
+ * - setupWorkflow()
19
+ * - runFullSetup() - All 5 documents in sequence
20
+ */
21
+ export interface SetupOptions {
22
+ projectPath: string;
23
+ outputDir: string;
24
+ responder: (question: Question, questionNumber: number) => Promise<string[]>;
25
+ approvalFlow: (draft: DocumentDraft) => Promise<ApprovalResult>;
26
+ customInputPrompt?: (question: Question) => Promise<string>;
27
+ maxRevisions?: number;
28
+ resume?: boolean;
29
+ }
30
+ export interface SetupResult {
31
+ success: boolean;
32
+ checkpoint?: string;
33
+ filePath?: string;
34
+ isBrownfield?: boolean;
35
+ autoGenerated?: boolean;
36
+ revisionCount?: number;
37
+ error?: string;
38
+ resumed?: boolean;
39
+ resumedFrom?: string;
40
+ }
41
+ export interface FullSetupResult {
42
+ success: boolean;
43
+ documentsCreated: string[];
44
+ finalCheckpoint?: string;
45
+ error?: string;
46
+ resumed?: boolean;
47
+ resumedFrom?: string;
48
+ }
49
+ /**
50
+ * Setup product.md
51
+ */
52
+ export declare function setupProductGuide(options: SetupOptions): Promise<SetupResult>;
53
+ /**
54
+ * Setup guidelines.md
55
+ */
56
+ export declare function setupGuidelines(options: SetupOptions): Promise<SetupResult>;
57
+ /**
58
+ * Setup tech-stack.md
59
+ */
60
+ export declare function setupTechStack(options: SetupOptions): Promise<SetupResult>;
61
+ /**
62
+ * Setup styleguides.md
63
+ */
64
+ export declare function setupStyleguides(options: SetupOptions): Promise<SetupResult>;
65
+ /**
66
+ * Setup workflow.md
67
+ */
68
+ export declare function setupWorkflow(options: SetupOptions): Promise<SetupResult>;
69
+ /**
70
+ * Run full setup - generate all 5 documents in sequence
71
+ */
72
+ export declare function runFullSetup(options: SetupOptions): Promise<FullSetupResult>;
@@ -0,0 +1,179 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import { detectProjectMaturity } from './projectMaturity.js';
4
+ import { analyzeCodebase } from './codebaseAnalysis.js';
5
+ import { generateBrownfieldQuestions, generateGreenfieldQuestions, } from './questionGenerator.js';
6
+ import { generateDocument, } from './documentGeneration.js';
7
+ const SECTION_ORDER = ['product', 'guidelines', 'tech-stack', 'styleguides', 'workflow'];
8
+ const CHECKPOINT_TO_SECTION = {
9
+ '2.1_product_guide': 'product',
10
+ '2.2_product_guidelines': 'guidelines',
11
+ '2.3_tech_stack': 'tech-stack',
12
+ '2.4_code_styleguides': 'styleguides',
13
+ '2.5_workflow': 'workflow',
14
+ };
15
+ const SECTION_TO_FILENAME = {
16
+ 'product': 'product.md',
17
+ 'guidelines': 'guidelines.md',
18
+ 'tech-stack': 'tech-stack.md',
19
+ 'styleguides': 'styleguides.md',
20
+ 'workflow': 'workflow.md',
21
+ };
22
+ /**
23
+ * Get current setup state from state file
24
+ */
25
+ function getSetupState(outputDir) {
26
+ const statePath = path.join(outputDir, 'setup_state.json');
27
+ if (fs.existsSync(statePath)) {
28
+ const content = fs.readFileSync(statePath, 'utf-8');
29
+ return JSON.parse(content);
30
+ }
31
+ return null;
32
+ }
33
+ /**
34
+ * Determine which sections need to be completed
35
+ */
36
+ function getSectionsToComplete(state, resume) {
37
+ if (!resume || !state || !state.last_successful_step) {
38
+ return SECTION_ORDER;
39
+ }
40
+ const lastCheckpoint = state.last_successful_step;
41
+ const lastSection = CHECKPOINT_TO_SECTION[lastCheckpoint];
42
+ if (!lastSection) {
43
+ return SECTION_ORDER;
44
+ }
45
+ const lastIndex = SECTION_ORDER.indexOf(lastSection);
46
+ return SECTION_ORDER.slice(lastIndex + 1);
47
+ }
48
+ /**
49
+ * Setup a single section
50
+ */
51
+ async function setupSection(section, options, maturity, analysis) {
52
+ try {
53
+ const isBrownfield = maturity === 'brownfield';
54
+ // Generate questions based on project maturity
55
+ const questions = isBrownfield
56
+ ? generateBrownfieldQuestions(section, analysis)
57
+ : generateGreenfieldQuestions(section);
58
+ // Prepare document generation options
59
+ const docOptions = {
60
+ section,
61
+ questions,
62
+ analysis,
63
+ responder: options.responder,
64
+ approvalFlow: options.approvalFlow,
65
+ outputPath: path.join(options.outputDir, SECTION_TO_FILENAME[section]),
66
+ maxRevisions: options.maxRevisions,
67
+ customInputPrompt: options.customInputPrompt,
68
+ };
69
+ // Generate document
70
+ const result = await generateDocument(docOptions);
71
+ return {
72
+ success: result.success,
73
+ checkpoint: result.checkpoint,
74
+ filePath: docOptions.outputPath,
75
+ isBrownfield,
76
+ autoGenerated: result.autoGenerated,
77
+ revisionCount: result.revisionCount,
78
+ error: result.error,
79
+ };
80
+ }
81
+ catch (error) {
82
+ return {
83
+ success: false,
84
+ error: error instanceof Error ? error.message : 'Unknown error',
85
+ };
86
+ }
87
+ }
88
+ /**
89
+ * Setup product.md
90
+ */
91
+ export async function setupProductGuide(options) {
92
+ const maturity = detectProjectMaturity(options.projectPath);
93
+ const analysis = analyzeCodebase(options.projectPath);
94
+ return setupSection('product', options, maturity, analysis);
95
+ }
96
+ /**
97
+ * Setup guidelines.md
98
+ */
99
+ export async function setupGuidelines(options) {
100
+ const maturity = detectProjectMaturity(options.projectPath);
101
+ const analysis = analyzeCodebase(options.projectPath);
102
+ return setupSection('guidelines', options, maturity, analysis);
103
+ }
104
+ /**
105
+ * Setup tech-stack.md
106
+ */
107
+ export async function setupTechStack(options) {
108
+ const maturity = detectProjectMaturity(options.projectPath);
109
+ const analysis = analyzeCodebase(options.projectPath);
110
+ return setupSection('tech-stack', options, maturity, analysis);
111
+ }
112
+ /**
113
+ * Setup styleguides.md
114
+ */
115
+ export async function setupStyleguides(options) {
116
+ const maturity = detectProjectMaturity(options.projectPath);
117
+ const analysis = analyzeCodebase(options.projectPath);
118
+ return setupSection('styleguides', options, maturity, analysis);
119
+ }
120
+ /**
121
+ * Setup workflow.md
122
+ */
123
+ export async function setupWorkflow(options) {
124
+ const maturity = detectProjectMaturity(options.projectPath);
125
+ const analysis = analyzeCodebase(options.projectPath);
126
+ return setupSection('workflow', options, maturity, analysis);
127
+ }
128
+ /**
129
+ * Run full setup - generate all 5 documents in sequence
130
+ */
131
+ export async function runFullSetup(options) {
132
+ try {
133
+ // Ensure output directory exists
134
+ if (!fs.existsSync(options.outputDir)) {
135
+ fs.mkdirSync(options.outputDir, { recursive: true });
136
+ }
137
+ // Detect project maturity once
138
+ const maturity = detectProjectMaturity(options.projectPath);
139
+ const analysis = analyzeCodebase(options.projectPath);
140
+ // Check for resume
141
+ const state = getSetupState(options.outputDir);
142
+ const sectionsToComplete = getSectionsToComplete(state, options.resume || false);
143
+ const resumed = options.resume && state && sectionsToComplete.length < SECTION_ORDER.length;
144
+ const resumedFrom = resumed ? state.last_successful_step : undefined;
145
+ const documentsCreated = [];
146
+ let finalCheckpoint;
147
+ // Setup each section in order
148
+ for (const section of sectionsToComplete) {
149
+ const result = await setupSection(section, options, maturity, analysis);
150
+ if (!result.success) {
151
+ return {
152
+ success: false,
153
+ documentsCreated,
154
+ error: result.error || `Failed to setup ${section}`,
155
+ resumed,
156
+ resumedFrom,
157
+ };
158
+ }
159
+ if (result.filePath) {
160
+ documentsCreated.push(result.filePath);
161
+ }
162
+ finalCheckpoint = result.checkpoint;
163
+ }
164
+ return {
165
+ success: true,
166
+ documentsCreated,
167
+ finalCheckpoint,
168
+ resumed,
169
+ resumedFrom,
170
+ };
171
+ }
172
+ catch (error) {
173
+ return {
174
+ success: false,
175
+ documentsCreated: [],
176
+ error: error instanceof Error ? error.message : 'Unknown error',
177
+ };
178
+ }
179
+ }
@@ -0,0 +1 @@
1
+ export {};