opencode-conductor-cdd-plugin 1.0.0-beta.18 → 1.0.0-beta.20

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 (37) hide show
  1. package/README.md +19 -3
  2. package/dist/prompts/cdd/setup.json +2 -2
  3. package/dist/prompts/cdd/setup.test.js +40 -118
  4. package/dist/prompts/cdd/setup.test.ts +40 -143
  5. package/dist/utils/codebaseAnalysis.d.ts +61 -0
  6. package/dist/utils/codebaseAnalysis.js +429 -0
  7. package/dist/utils/codebaseAnalysis.test.d.ts +1 -0
  8. package/dist/utils/codebaseAnalysis.test.js +556 -0
  9. package/dist/utils/configDetection.d.ts +12 -0
  10. package/dist/utils/configDetection.js +23 -9
  11. package/dist/utils/configDetection.test.js +204 -7
  12. package/dist/utils/documentGeneration.d.ts +97 -0
  13. package/dist/utils/documentGeneration.js +301 -0
  14. package/dist/utils/documentGeneration.test.d.ts +1 -0
  15. package/dist/utils/documentGeneration.test.js +380 -0
  16. package/dist/utils/interactiveMenu.d.ts +56 -0
  17. package/dist/utils/interactiveMenu.js +144 -0
  18. package/dist/utils/interactiveMenu.test.d.ts +1 -0
  19. package/dist/utils/interactiveMenu.test.js +231 -0
  20. package/dist/utils/interactiveSetup.d.ts +43 -0
  21. package/dist/utils/interactiveSetup.js +131 -0
  22. package/dist/utils/interactiveSetup.test.d.ts +1 -0
  23. package/dist/utils/interactiveSetup.test.js +124 -0
  24. package/dist/utils/projectMaturity.d.ts +53 -0
  25. package/dist/utils/projectMaturity.js +179 -0
  26. package/dist/utils/projectMaturity.test.d.ts +1 -0
  27. package/dist/utils/projectMaturity.test.js +298 -0
  28. package/dist/utils/questionGenerator.d.ts +51 -0
  29. package/dist/utils/questionGenerator.js +535 -0
  30. package/dist/utils/questionGenerator.test.d.ts +1 -0
  31. package/dist/utils/questionGenerator.test.js +328 -0
  32. package/dist/utils/setupIntegration.d.ts +72 -0
  33. package/dist/utils/setupIntegration.js +179 -0
  34. package/dist/utils/setupIntegration.test.d.ts +1 -0
  35. package/dist/utils/setupIntegration.test.js +344 -0
  36. package/dist/utils/synergyState.test.js +17 -3
  37. package/package.json +2 -1
@@ -0,0 +1,344 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
+ import * as fs from 'fs';
3
+ import * as path from 'path';
4
+ import { setupProductGuide, setupGuidelines, setupTechStack, setupStyleguides, setupWorkflow, runFullSetup, } from './setupIntegration.js';
5
+ describe('setupIntegration', () => {
6
+ const testOutputDir = path.join(process.cwd(), 'test-output-setup-integration');
7
+ const testProjectDir = path.join(testOutputDir, 'test-project');
8
+ beforeEach(() => {
9
+ // Create test directories
10
+ if (!fs.existsSync(testOutputDir)) {
11
+ fs.mkdirSync(testOutputDir, { recursive: true });
12
+ }
13
+ if (!fs.existsSync(testProjectDir)) {
14
+ fs.mkdirSync(testProjectDir, { recursive: true });
15
+ }
16
+ });
17
+ afterEach(() => {
18
+ // Clean up test files
19
+ if (fs.existsSync(testOutputDir)) {
20
+ fs.rmSync(testOutputDir, { recursive: true, force: true });
21
+ }
22
+ });
23
+ describe('setupProductGuide - End-to-End Integration', () => {
24
+ it('should generate product.md for greenfield project with user selections', async () => {
25
+ // Mock user responses: Select A, B, C for 3 questions
26
+ const mockResponder = vi.fn()
27
+ .mockResolvedValueOnce(['A']) // Question 1: Primary goal
28
+ .mockResolvedValueOnce(['B']) // Question 2: Target users
29
+ .mockResolvedValueOnce(['A']); // Question 3: Key features
30
+ const mockApproval = vi.fn()
31
+ .mockResolvedValueOnce({ approved: true, finalContent: undefined });
32
+ const options = {
33
+ projectPath: testProjectDir,
34
+ outputDir: path.join(testProjectDir, 'conductor-cdd'),
35
+ responder: mockResponder,
36
+ approvalFlow: mockApproval,
37
+ };
38
+ const result = await setupProductGuide(options);
39
+ expect(result.success).toBe(true);
40
+ expect(result.checkpoint).toBe('2.1_product_guide');
41
+ expect(result.filePath).toContain('product.md');
42
+ // Verify file was created
43
+ const productPath = path.join(testProjectDir, 'conductor-cdd', 'product.md');
44
+ expect(fs.existsSync(productPath)).toBe(true);
45
+ // Verify state file was created
46
+ const statePath = path.join(testProjectDir, 'conductor-cdd', 'setup_state.json');
47
+ expect(fs.existsSync(statePath)).toBe(true);
48
+ const state = JSON.parse(fs.readFileSync(statePath, 'utf-8'));
49
+ expect(state.last_successful_step).toBe('2.1_product_guide');
50
+ });
51
+ it('should generate product.md for brownfield project with context awareness', async () => {
52
+ // Create brownfield project structure
53
+ const packageJson = {
54
+ name: 'test-app',
55
+ version: '1.0.0',
56
+ dependencies: {
57
+ 'react': '^18.0.0',
58
+ 'express': '^4.18.0',
59
+ },
60
+ };
61
+ fs.writeFileSync(path.join(testProjectDir, 'package.json'), JSON.stringify(packageJson, null, 2));
62
+ fs.mkdirSync(path.join(testProjectDir, 'src'), { recursive: true });
63
+ fs.writeFileSync(path.join(testProjectDir, 'src', 'index.ts'), 'console.log("test");');
64
+ const mockResponder = vi.fn()
65
+ .mockResolvedValueOnce(['A']) // Question 1
66
+ .mockResolvedValueOnce(['B']) // Question 2
67
+ .mockResolvedValueOnce(['A']); // Question 3
68
+ const mockApproval = vi.fn()
69
+ .mockResolvedValueOnce({ approved: true });
70
+ const options = {
71
+ projectPath: testProjectDir,
72
+ outputDir: path.join(testProjectDir, 'conductor-cdd'),
73
+ responder: mockResponder,
74
+ approvalFlow: mockApproval,
75
+ };
76
+ const result = await setupProductGuide(options);
77
+ expect(result.success).toBe(true);
78
+ expect(result.isBrownfield).toBe(true);
79
+ // Verify content includes detected technologies
80
+ const productPath = path.join(testProjectDir, 'conductor-cdd', 'product.md');
81
+ const content = fs.readFileSync(productPath, 'utf-8');
82
+ // Should detect context from package.json
83
+ expect(content.length).toBeGreaterThan(50);
84
+ });
85
+ it('should handle option E (auto-generate) early exit', async () => {
86
+ const mockResponder = vi.fn()
87
+ .mockResolvedValueOnce(['E']); // Auto-generate immediately
88
+ const mockApproval = vi.fn()
89
+ .mockResolvedValueOnce({ approved: true });
90
+ const options = {
91
+ projectPath: testProjectDir,
92
+ outputDir: path.join(testProjectDir, 'conductor-cdd'),
93
+ responder: mockResponder,
94
+ approvalFlow: mockApproval,
95
+ };
96
+ const result = await setupProductGuide(options);
97
+ if (!result.success) {
98
+ console.error('[Test Debug] Option E test failed:', result.error);
99
+ }
100
+ expect(result.success).toBe(true);
101
+ expect(result.autoGenerated).toBe(true);
102
+ expect(mockResponder).toHaveBeenCalledTimes(1); // Only asked once
103
+ // Verify file was still created
104
+ const productPath = path.join(testProjectDir, 'conductor-cdd', 'product.md');
105
+ expect(fs.existsSync(productPath)).toBe(true);
106
+ });
107
+ it('should handle option D (custom input) with user text', async () => {
108
+ const mockResponder = vi.fn()
109
+ .mockResolvedValueOnce(['D']) // Question 1: Custom input
110
+ .mockResolvedValueOnce(['A']) // Question 2
111
+ .mockResolvedValueOnce(['B']); // Question 3
112
+ const mockCustomInput = vi.fn()
113
+ .mockResolvedValueOnce('A revolutionary AI-powered task management platform');
114
+ const mockApproval = vi.fn()
115
+ .mockResolvedValueOnce({ approved: true });
116
+ const options = {
117
+ projectPath: testProjectDir,
118
+ outputDir: path.join(testProjectDir, 'conductor-cdd'),
119
+ responder: mockResponder,
120
+ approvalFlow: mockApproval,
121
+ customInputPrompt: mockCustomInput,
122
+ };
123
+ const result = await setupProductGuide(options);
124
+ expect(result.success).toBe(true);
125
+ expect(mockCustomInput).toHaveBeenCalled();
126
+ const productPath = path.join(testProjectDir, 'conductor-cdd', 'product.md');
127
+ const content = fs.readFileSync(productPath, 'utf-8');
128
+ expect(content).toContain('AI-powered task management platform');
129
+ });
130
+ it('should handle approval rejection and revision loop', async () => {
131
+ const mockResponder = vi.fn()
132
+ .mockResolvedValueOnce(['A']) // Question 1
133
+ .mockResolvedValueOnce(['B']) // Question 2
134
+ .mockResolvedValueOnce(['A']); // Question 3
135
+ const mockApproval = vi.fn()
136
+ .mockResolvedValueOnce({ approved: false, revisionGuidance: 'Add more details about users' })
137
+ .mockResolvedValueOnce({ approved: true, finalContent: 'Revised content with more details' });
138
+ const options = {
139
+ projectPath: testProjectDir,
140
+ outputDir: path.join(testProjectDir, 'conductor-cdd'),
141
+ responder: mockResponder,
142
+ approvalFlow: mockApproval,
143
+ };
144
+ const result = await setupProductGuide(options);
145
+ expect(result.success).toBe(true);
146
+ expect(result.revisionCount).toBe(1);
147
+ expect(mockApproval).toHaveBeenCalledTimes(2);
148
+ const productPath = path.join(testProjectDir, 'conductor-cdd', 'product.md');
149
+ const content = fs.readFileSync(productPath, 'utf-8');
150
+ expect(content).toContain('Revised content with more details');
151
+ });
152
+ it('should enforce max 5 questions limit', async () => {
153
+ // Setup to answer 10 times, but should only be asked 5
154
+ const mockResponder = vi.fn()
155
+ .mockResolvedValue(['A']);
156
+ const mockApproval = vi.fn()
157
+ .mockResolvedValueOnce({ approved: true });
158
+ const options = {
159
+ projectPath: testProjectDir,
160
+ outputDir: path.join(testProjectDir, 'conductor-cdd'),
161
+ responder: mockResponder,
162
+ approvalFlow: mockApproval,
163
+ };
164
+ const result = await setupProductGuide(options);
165
+ expect(result.success).toBe(true);
166
+ expect(mockResponder.mock.calls.length).toBeLessThanOrEqual(5);
167
+ });
168
+ it('should NOT include unselected options in final document', async () => {
169
+ const mockResponder = vi.fn()
170
+ .mockResolvedValueOnce(['A']) // Question 1: Select only A
171
+ .mockResolvedValueOnce(['A']) // Question 2
172
+ .mockResolvedValueOnce(['A']); // Question 3
173
+ const mockApproval = vi.fn()
174
+ .mockResolvedValueOnce({ approved: true });
175
+ const options = {
176
+ projectPath: testProjectDir,
177
+ outputDir: path.join(testProjectDir, 'conductor-cdd'),
178
+ responder: mockResponder,
179
+ approvalFlow: mockApproval,
180
+ };
181
+ const result = await setupProductGuide(options);
182
+ expect(result.success).toBe(true);
183
+ const productPath = path.join(testProjectDir, 'conductor-cdd', 'product.md');
184
+ const content = fs.readFileSync(productPath, 'utf-8');
185
+ // Should NOT contain option markers
186
+ expect(content).not.toMatch(/\bA\)/);
187
+ expect(content).not.toMatch(/\bB\)/);
188
+ expect(content).not.toMatch(/\bC\)/);
189
+ expect(content).not.toMatch(/\bD\)/);
190
+ expect(content).not.toMatch(/\bE\)/);
191
+ });
192
+ });
193
+ describe('runFullSetup - Complete Workflow Integration', () => {
194
+ it('should generate all 5 documents in sequence', async () => {
195
+ const mockResponder = vi.fn()
196
+ .mockResolvedValue(['A']); // Simple answer for all questions
197
+ const mockApproval = vi.fn()
198
+ .mockResolvedValue({ approved: true });
199
+ const options = {
200
+ projectPath: testProjectDir,
201
+ outputDir: path.join(testProjectDir, 'conductor-cdd'),
202
+ responder: mockResponder,
203
+ approvalFlow: mockApproval,
204
+ };
205
+ const result = await runFullSetup(options);
206
+ expect(result.success).toBe(true);
207
+ expect(result.documentsCreated).toHaveLength(5);
208
+ // Verify all 5 documents were created
209
+ const cddDir = path.join(testProjectDir, 'conductor-cdd');
210
+ expect(fs.existsSync(path.join(cddDir, 'product.md'))).toBe(true);
211
+ expect(fs.existsSync(path.join(cddDir, 'guidelines.md'))).toBe(true);
212
+ expect(fs.existsSync(path.join(cddDir, 'tech-stack.md'))).toBe(true);
213
+ expect(fs.existsSync(path.join(cddDir, 'styleguides.md'))).toBe(true);
214
+ expect(fs.existsSync(path.join(cddDir, 'workflow.md'))).toBe(true);
215
+ // Verify final state
216
+ const statePath = path.join(cddDir, 'setup_state.json');
217
+ const state = JSON.parse(fs.readFileSync(statePath, 'utf-8'));
218
+ expect(state.last_successful_step).toBe('2.5_workflow');
219
+ });
220
+ it('should resume from checkpoint if interrupted', async () => {
221
+ // First, create partial state (stopped after product.md)
222
+ const cddDir = path.join(testProjectDir, 'conductor-cdd');
223
+ fs.mkdirSync(cddDir, { recursive: true });
224
+ const partialState = {
225
+ last_successful_step: '2.1_product_guide',
226
+ product_timestamp: new Date().toISOString(),
227
+ };
228
+ fs.writeFileSync(path.join(cddDir, 'setup_state.json'), JSON.stringify(partialState, null, 2));
229
+ // Mock responder should only be called for remaining sections
230
+ const mockResponder = vi.fn()
231
+ .mockResolvedValue(['A']);
232
+ const mockApproval = vi.fn()
233
+ .mockResolvedValue({ approved: true });
234
+ const options = {
235
+ projectPath: testProjectDir,
236
+ outputDir: cddDir,
237
+ responder: mockResponder,
238
+ approvalFlow: mockApproval,
239
+ resume: true,
240
+ };
241
+ const result = await runFullSetup(options);
242
+ expect(result.success).toBe(true);
243
+ expect(result.resumed).toBe(true);
244
+ expect(result.resumedFrom).toBe('2.1_product_guide');
245
+ // Should have created remaining 4 documents
246
+ expect(result.documentsCreated).toHaveLength(4);
247
+ });
248
+ it('should handle failures and report partial completion', async () => {
249
+ const mockResponder = vi.fn()
250
+ .mockResolvedValue(['A']);
251
+ // Fail on guidelines approval
252
+ const mockApproval = vi.fn()
253
+ .mockResolvedValueOnce({ approved: true }) // product: success
254
+ .mockResolvedValue({ approved: false, revisionGuidance: 'Keep rejecting' }); // guidelines: fail
255
+ const options = {
256
+ projectPath: testProjectDir,
257
+ outputDir: path.join(testProjectDir, 'conductor-cdd'),
258
+ responder: mockResponder,
259
+ approvalFlow: mockApproval,
260
+ maxRevisions: 2,
261
+ };
262
+ const result = await runFullSetup(options);
263
+ expect(result.success).toBe(false);
264
+ expect(result.documentsCreated.length).toBeLessThan(5);
265
+ expect(result.error).toContain('revision');
266
+ // Product should be created, but not all others
267
+ const cddDir = path.join(testProjectDir, 'conductor-cdd');
268
+ expect(fs.existsSync(path.join(cddDir, 'product.md'))).toBe(true);
269
+ });
270
+ });
271
+ describe('Individual Section Setup Functions', () => {
272
+ it('should setup guidelines.md independently', async () => {
273
+ const mockResponder = vi.fn().mockResolvedValue(['A']);
274
+ const mockApproval = vi.fn().mockResolvedValue({ approved: true });
275
+ const options = {
276
+ projectPath: testProjectDir,
277
+ outputDir: path.join(testProjectDir, 'conductor-cdd'),
278
+ responder: mockResponder,
279
+ approvalFlow: mockApproval,
280
+ };
281
+ const result = await setupGuidelines(options);
282
+ expect(result.success).toBe(true);
283
+ expect(result.checkpoint).toBe('2.2_product_guidelines');
284
+ const guidelinesPath = path.join(testProjectDir, 'conductor-cdd', 'guidelines.md');
285
+ expect(fs.existsSync(guidelinesPath)).toBe(true);
286
+ });
287
+ it('should setup tech-stack.md with auto-detected dependencies', async () => {
288
+ // Create package.json for detection
289
+ const packageJson = {
290
+ name: 'test-app',
291
+ dependencies: {
292
+ 'react': '^18.0.0',
293
+ 'typescript': '^5.0.0',
294
+ },
295
+ };
296
+ fs.writeFileSync(path.join(testProjectDir, 'package.json'), JSON.stringify(packageJson, null, 2));
297
+ const mockResponder = vi.fn().mockResolvedValue(['E']); // Auto-generate
298
+ const mockApproval = vi.fn().mockResolvedValue({ approved: true });
299
+ const options = {
300
+ projectPath: testProjectDir,
301
+ outputDir: path.join(testProjectDir, 'conductor-cdd'),
302
+ responder: mockResponder,
303
+ approvalFlow: mockApproval,
304
+ };
305
+ const result = await setupTechStack(options);
306
+ expect(result.success).toBe(true);
307
+ expect(result.checkpoint).toBe('2.3_tech_stack');
308
+ const techStackPath = path.join(testProjectDir, 'conductor-cdd', 'tech-stack.md');
309
+ const content = fs.readFileSync(techStackPath, 'utf-8');
310
+ // Should detect React and TypeScript
311
+ expect(content.toLowerCase()).toMatch(/react|typescript/);
312
+ });
313
+ it('should setup styleguides.md', async () => {
314
+ const mockResponder = vi.fn().mockResolvedValue(['A']);
315
+ const mockApproval = vi.fn().mockResolvedValue({ approved: true });
316
+ const options = {
317
+ projectPath: testProjectDir,
318
+ outputDir: path.join(testProjectDir, 'conductor-cdd'),
319
+ responder: mockResponder,
320
+ approvalFlow: mockApproval,
321
+ };
322
+ const result = await setupStyleguides(options);
323
+ expect(result.success).toBe(true);
324
+ expect(result.checkpoint).toBe('2.4_code_styleguides');
325
+ const styleguidesPath = path.join(testProjectDir, 'conductor-cdd', 'styleguides.md');
326
+ expect(fs.existsSync(styleguidesPath)).toBe(true);
327
+ });
328
+ it('should setup workflow.md', async () => {
329
+ const mockResponder = vi.fn().mockResolvedValue(['A']);
330
+ const mockApproval = vi.fn().mockResolvedValue({ approved: true });
331
+ const options = {
332
+ projectPath: testProjectDir,
333
+ outputDir: path.join(testProjectDir, 'conductor-cdd'),
334
+ responder: mockResponder,
335
+ approvalFlow: mockApproval,
336
+ };
337
+ const result = await setupWorkflow(options);
338
+ expect(result.success).toBe(true);
339
+ expect(result.checkpoint).toBe('2.5_workflow');
340
+ const workflowPath = path.join(testProjectDir, 'conductor-cdd', 'workflow.md');
341
+ expect(fs.existsSync(workflowPath)).toBe(true);
342
+ });
343
+ });
344
+ });
@@ -15,6 +15,7 @@ describe("synergyState", () => {
15
15
  vi.mocked(detectCDDConfig).mockReturnValue({
16
16
  hasCDDInOpenCode: false,
17
17
  hasCDDInOMO: false,
18
+ hasCDDInSlim: true,
18
19
  synergyActive: true,
19
20
  synergyFramework: 'oh-my-opencode-slim',
20
21
  slimAgents: ['explorer', 'librarian', 'oracle', 'designer'],
@@ -28,6 +29,7 @@ describe("synergyState", () => {
28
29
  vi.mocked(detectCDDConfig).mockReturnValue({
29
30
  hasCDDInOpenCode: false,
30
31
  hasCDDInOMO: false,
32
+ hasCDDInSlim: true,
31
33
  synergyActive: true,
32
34
  synergyFramework: 'oh-my-opencode-slim',
33
35
  slimAgents: ['explorer', 'librarian'],
@@ -42,6 +44,7 @@ describe("synergyState", () => {
42
44
  .mockReturnValueOnce({
43
45
  hasCDDInOpenCode: false,
44
46
  hasCDDInOMO: false,
47
+ hasCDDInSlim: true,
45
48
  synergyActive: true,
46
49
  synergyFramework: 'oh-my-opencode-slim',
47
50
  slimAgents: ['explorer'],
@@ -49,6 +52,7 @@ describe("synergyState", () => {
49
52
  .mockReturnValueOnce({
50
53
  hasCDDInOpenCode: false,
51
54
  hasCDDInOMO: true,
55
+ hasCDDInSlim: false,
52
56
  synergyActive: true,
53
57
  synergyFramework: 'oh-my-opencode',
54
58
  cddModel: 'model-1',
@@ -64,13 +68,15 @@ describe("synergyState", () => {
64
68
  .mockReturnValueOnce({
65
69
  hasCDDInOpenCode: false,
66
70
  hasCDDInOMO: false,
71
+ hasCDDInSlim: true,
67
72
  synergyActive: true,
68
73
  synergyFramework: 'oh-my-opencode-slim',
69
74
  slimAgents: ['explorer'],
70
75
  })
71
76
  .mockReturnValueOnce({
72
77
  hasCDDInOpenCode: false,
73
- hasCDDInOMO: false,
78
+ hasCDDInOMO: true,
79
+ hasCDDInSlim: false,
74
80
  synergyActive: true,
75
81
  synergyFramework: 'oh-my-opencode',
76
82
  cddModel: 'model-changed',
@@ -85,6 +91,7 @@ describe("synergyState", () => {
85
91
  vi.mocked(detectCDDConfig).mockReturnValue({
86
92
  hasCDDInOpenCode: true,
87
93
  hasCDDInOMO: false,
94
+ hasCDDInSlim: false,
88
95
  synergyActive: false,
89
96
  synergyFramework: 'none',
90
97
  cddModel: 'model-1',
@@ -99,13 +106,15 @@ describe("synergyState", () => {
99
106
  .mockReturnValueOnce({
100
107
  hasCDDInOpenCode: false,
101
108
  hasCDDInOMO: false,
109
+ hasCDDInSlim: true,
102
110
  synergyActive: true,
103
111
  synergyFramework: 'oh-my-opencode-slim',
104
112
  slimAgents: ['explorer'],
105
113
  })
106
114
  .mockReturnValueOnce({
107
115
  hasCDDInOpenCode: false,
108
- hasCDDInOMO: false,
116
+ hasCDDInOMO: true,
117
+ hasCDDInSlim: false,
109
118
  synergyActive: true,
110
119
  synergyFramework: 'oh-my-opencode',
111
120
  cddModel: 'model-1',
@@ -124,6 +133,7 @@ describe("synergyState", () => {
124
133
  vi.mocked(detectCDDConfig).mockReturnValue({
125
134
  hasCDDInOpenCode: false,
126
135
  hasCDDInOMO: false,
136
+ hasCDDInSlim: false,
127
137
  synergyActive: false,
128
138
  synergyFramework: 'none',
129
139
  });
@@ -136,6 +146,7 @@ describe("synergyState", () => {
136
146
  vi.mocked(detectCDDConfig).mockReturnValue({
137
147
  hasCDDInOpenCode: false,
138
148
  hasCDDInOMO: false,
149
+ hasCDDInSlim: true,
139
150
  synergyActive: true,
140
151
  synergyFramework: 'oh-my-opencode-slim',
141
152
  slimAgents: ['explorer', 'oracle'],
@@ -147,6 +158,7 @@ describe("synergyState", () => {
147
158
  vi.mocked(detectCDDConfig).mockReturnValue({
148
159
  hasCDDInOpenCode: false,
149
160
  hasCDDInOMO: true,
161
+ hasCDDInSlim: false,
150
162
  synergyActive: true,
151
163
  synergyFramework: 'oh-my-opencode',
152
164
  cddModel: 'model-1',
@@ -163,13 +175,15 @@ describe("synergyState", () => {
163
175
  .mockReturnValueOnce({
164
176
  hasCDDInOpenCode: false,
165
177
  hasCDDInOMO: false,
178
+ hasCDDInSlim: true,
166
179
  synergyActive: true,
167
180
  synergyFramework: 'oh-my-opencode-slim',
168
181
  slimAgents: ['explorer'],
169
182
  })
170
183
  .mockReturnValueOnce({
171
184
  hasCDDInOpenCode: false,
172
- hasCDDInOMO: false,
185
+ hasCDDInOMO: true,
186
+ hasCDDInSlim: false,
173
187
  synergyActive: true,
174
188
  synergyFramework: 'oh-my-opencode',
175
189
  cddModel: 'changed',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-conductor-cdd-plugin",
3
- "version": "1.0.0-beta.18",
3
+ "version": "1.0.0-beta.20",
4
4
  "description": "Context-Driven Development (CDD) plugin for OpenCode - Transform your AI coding workflow with structured specifications, plans, and implementation tracking",
5
5
  "type": "module",
6
6
  "repository": {
@@ -53,6 +53,7 @@
53
53
  "@semantic-release/npm": "^12.0.1",
54
54
  "@semantic-release/release-notes-generator": "^14.0.0",
55
55
  "@types/node": "^20.0.0",
56
+ "@vitest/coverage-v8": "^4.0.17",
56
57
  "semantic-release": "^24.2.1",
57
58
  "typescript": "^5.0.0",
58
59
  "vitest": "^4.0.16"