@iservu-inc/adf-cli 0.17.1 → 0.17.5

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/CLAUDE.md CHANGED
@@ -72,7 +72,7 @@ npm publish --dry-run
72
72
  - Routes to command handlers in `lib/commands/`
73
73
 
74
74
  2. **Commands** (`lib/commands/`)
75
- - `init.js` - Initialize framework, detect project, start interview session
75
+ - `init.js` - Initialize framework, detect project (brownfield detection: agent-native, openspec, specification-driven, gemini-conductor), start interview session
76
76
  - `config.js` - Configure AI provider, analysis settings, learning system
77
77
  - `deploy.js` - Deploy to development tools (Windsurf, Cursor, VS Code, etc.)
78
78
  - `update.js` - Check for and install CLI updates
@@ -38,7 +38,8 @@ async function init(options) {
38
38
  const frameworkNames = {
39
39
  'agent-native': 'Agent-Native',
40
40
  'openspec': 'OpenSpec',
41
- 'specification-driven': 'Specification-Driven'
41
+ 'specification-driven': 'Specification-Driven',
42
+ 'gemini-conductor': 'Gemini CLI Conductor'
42
43
  };
43
44
 
44
45
  const displayNames = detectedFrameworks
@@ -106,6 +106,7 @@ class DocumentAnalyzer {
106
106
  path.join(this.projectRoot, 'stories'),
107
107
  path.join(this.projectRoot, 'specs'),
108
108
  path.join(this.projectRoot, 'shared'),
109
+ path.join(this.projectRoot, 'conductor'),
109
110
  this.projectRoot // Root level files
110
111
  ];
111
112
 
@@ -206,6 +207,11 @@ class DocumentAnalyzer {
206
207
  if (content.match(/qa\s+agent/i)) keywords.add('qa-agent');
207
208
  if (content.match(/sm\s+agent/i) || content.match(/scrum\s+master/i)) keywords.add('sm-agent');
208
209
 
210
+ // Conductor indicators
211
+ if (content.includes('conductor')) keywords.add('gemini-conductor');
212
+ if (content.includes('Gemini CLI')) keywords.add('gemini-conductor');
213
+ if (content.includes('gemini conductor')) keywords.add('gemini-conductor');
214
+
209
215
  // Workflow indicators
210
216
  if (content.includes('TDD') || content.includes('Test-Driven')) keywords.add('tdd');
211
217
  if (content.includes('RED-GREEN-REFACTOR') || content.includes('red → green → refactor')) keywords.add('tdd-workflow');
@@ -231,7 +237,8 @@ class DocumentAnalyzer {
231
237
  bmad: 0,
232
238
  openSpec: 0,
233
239
  contextEngineering: 0,
234
- adf: 0
240
+ adf: 0,
241
+ conductor: 0
235
242
  };
236
243
 
237
244
  for (const doc of this.analysis.documents) {
@@ -262,6 +269,10 @@ class DocumentAnalyzer {
262
269
  // ADF indicators
263
270
  if (doc.keywords.includes('adf-framework')) frameworkIndicators.adf += 5;
264
271
  if (fs.existsSync(path.join(this.projectRoot, '.adf-context.json'))) frameworkIndicators.adf += 10;
272
+
273
+ // Conductor indicators
274
+ if (doc.keywords.includes('gemini-conductor')) frameworkIndicators.conductor += 5;
275
+ if (doc.path && doc.path.includes('conductor')) frameworkIndicators.conductor += 3;
265
276
  }
266
277
 
267
278
  // Determine detected framework
@@ -63,6 +63,10 @@ class ContextExtractor {
63
63
  context = await this.extractFromOpenSpec2(projectDir, context);
64
64
  }
65
65
 
66
+ if (frameworks.includes('gemini-conductor')) {
67
+ context = await this.extractFromConductor(projectDir, context);
68
+ }
69
+
66
70
  return context;
67
71
  }
68
72
 
@@ -120,6 +124,50 @@ class ContextExtractor {
120
124
  return context;
121
125
  }
122
126
 
127
+ static async extractFromConductor(projectDir, context) {
128
+ const conductorDir = path.join(projectDir, 'conductor');
129
+
130
+ // Extract from product.md (project overview, features, vision)
131
+ const productFile = path.join(conductorDir, 'product.md');
132
+ if (await fs.pathExists(productFile)) {
133
+ const content = await fs.readFile(productFile, 'utf-8');
134
+
135
+ // Try to extract project name from first heading
136
+ const nameMatch = content.match(/^#\s+(.+?)$/m);
137
+ if (nameMatch && !context.name) {
138
+ context.name = nameMatch[1].trim();
139
+ }
140
+
141
+ // Extract overview from product description or first section
142
+ const overview = this.extractSection(content, 'Overview')
143
+ || this.extractSection(content, 'Vision')
144
+ || this.extractSection(content, 'Description');
145
+ if (overview) {
146
+ context.overview += (context.overview ? '\n\n' : '') + overview;
147
+ }
148
+ }
149
+
150
+ // Extract from tech-stack.md (languages, frameworks, infrastructure)
151
+ const techStackFile = path.join(conductorDir, 'tech-stack.md');
152
+ if (await fs.pathExists(techStackFile)) {
153
+ const content = await fs.readFile(techStackFile, 'utf-8');
154
+ context.techStack += (context.techStack ? '\n\n' : '') + content.trim();
155
+ }
156
+
157
+ // Extract architecture from workflow.md (patterns, conventions)
158
+ const workflowFile = path.join(conductorDir, 'workflow.md');
159
+ if (await fs.pathExists(workflowFile)) {
160
+ const content = await fs.readFile(workflowFile, 'utf-8');
161
+ const architecture = this.extractSection(content, 'Architecture')
162
+ || this.extractSection(content, 'Quality Gates');
163
+ if (architecture) {
164
+ context.architecture += (context.architecture ? '\n\n' : '') + architecture;
165
+ }
166
+ }
167
+
168
+ return context;
169
+ }
170
+
123
171
  /**
124
172
  * Extracts content under a specific heading.
125
173
  * Supports both # and ## and emojis.
@@ -9,7 +9,7 @@ class FrameworkDetector {
9
9
  /**
10
10
  * Detects which frameworks are present in the given project directory.
11
11
  * @param {string} projectDir - The project directory to scan.
12
- * @returns {Promise<string[]>} Array of detected framework IDs ('adf', 'agent-native', 'openspec', 'specification-driven').
12
+ * @returns {Promise<string[]>} Array of detected framework IDs ('adf', 'agent-native', 'openspec', 'specification-driven', 'gemini-conductor').
13
13
  */
14
14
  static async detect(projectDir) {
15
15
  const detected = [];
@@ -47,6 +47,15 @@ class FrameworkDetector {
47
47
  detected.push('specification-driven');
48
48
  }
49
49
 
50
+ // 5. Detect Gemini CLI Conductor
51
+ // Indicators: conductor/product.md, conductor/workflow.md
52
+ if (
53
+ await fs.pathExists(path.join(projectDir, 'conductor', 'product.md')) ||
54
+ await fs.pathExists(path.join(projectDir, 'conductor', 'workflow.md'))
55
+ ) {
56
+ detected.push('gemini-conductor');
57
+ }
58
+
50
59
  return detected;
51
60
  }
52
61
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iservu-inc/adf-cli",
3
- "version": "0.17.1",
3
+ "version": "0.17.5",
4
4
  "description": "CLI tool for AgentDevFramework - Agent-Native development framework with multi-provider AI support",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -50,6 +50,51 @@ Add many cool features.
50
50
  expect(context.proposedChanges).toContain('Add many cool features');
51
51
  });
52
52
 
53
+ test('should extract context from Gemini CLI Conductor', async () => {
54
+ await fs.ensureDir(path.join(tempDir, 'conductor'));
55
+ await fs.writeFile(path.join(tempDir, 'conductor', 'product.md'), `# My Product
56
+
57
+ ## Overview
58
+ An AI-powered task management system.
59
+
60
+ ## Features
61
+ - Smart task prioritization
62
+ - Team collaboration
63
+ `);
64
+ await fs.writeFile(path.join(tempDir, 'conductor', 'tech-stack.md'), `# Tech Stack
65
+
66
+ - **Language**: TypeScript
67
+ - **Runtime**: Node.js
68
+ - **Framework**: Express
69
+ `);
70
+ await fs.writeFile(path.join(tempDir, 'conductor', 'workflow.md'), `# Development Workflow
71
+
72
+ ## Quality Gates
73
+ All PRs must pass linting, tests, and review.
74
+ `);
75
+
76
+ const context = await ContextExtractor.extract(tempDir, ['gemini-conductor']);
77
+ expect(context.name).toBe('My Product');
78
+ expect(context.overview).toContain('AI-powered task management');
79
+ expect(context.techStack).toContain('TypeScript');
80
+ expect(context.architecture).toContain('linting, tests, and review');
81
+ });
82
+
83
+ test('should extract Conductor context with partial files', async () => {
84
+ await fs.ensureDir(path.join(tempDir, 'conductor'));
85
+ await fs.writeFile(path.join(tempDir, 'conductor', 'product.md'), `# TaskFlow
86
+
87
+ ## Vision
88
+ A next-gen project management tool.
89
+ `);
90
+
91
+ const context = await ContextExtractor.extract(tempDir, ['gemini-conductor']);
92
+ expect(context.name).toBe('TaskFlow');
93
+ expect(context.overview).toContain('next-gen project management');
94
+ expect(context.techStack).toBe('');
95
+ expect(context.architecture).toBe('');
96
+ });
97
+
53
98
  test('should extract tech stack and architecture from specification-driven', async () => {
54
99
  const specContent = `# Specification: App Core
55
100
 
@@ -48,6 +48,34 @@ describe('Framework Detector', () => {
48
48
  expect(detected).toContain('openspec');
49
49
  });
50
50
 
51
+ test('should detect Gemini CLI Conductor project', async () => {
52
+ await fs.ensureDir(path.join(tempDir, 'conductor'));
53
+ await fs.writeFile(path.join(tempDir, 'conductor', 'product.md'), '# My Product');
54
+ await fs.writeFile(path.join(tempDir, 'conductor', 'workflow.md'), '# Workflow');
55
+ const detected = await FrameworkDetector.detect(tempDir);
56
+ expect(detected).toContain('gemini-conductor');
57
+ });
58
+
59
+ test('should detect Conductor with only product.md', async () => {
60
+ await fs.ensureDir(path.join(tempDir, 'conductor'));
61
+ await fs.writeFile(path.join(tempDir, 'conductor', 'product.md'), '# My Product');
62
+ const detected = await FrameworkDetector.detect(tempDir);
63
+ expect(detected).toContain('gemini-conductor');
64
+ });
65
+
66
+ test('should detect Conductor with only workflow.md', async () => {
67
+ await fs.ensureDir(path.join(tempDir, 'conductor'));
68
+ await fs.writeFile(path.join(tempDir, 'conductor', 'workflow.md'), '# Workflow');
69
+ const detected = await FrameworkDetector.detect(tempDir);
70
+ expect(detected).toContain('gemini-conductor');
71
+ });
72
+
73
+ test('should not detect Conductor for empty conductor directory', async () => {
74
+ await fs.ensureDir(path.join(tempDir, 'conductor'));
75
+ const detected = await FrameworkDetector.detect(tempDir);
76
+ expect(detected).not.toContain('gemini-conductor');
77
+ });
78
+
51
79
  test('should return empty array for no frameworks', async () => {
52
80
  const detected = await FrameworkDetector.detect(tempDir);
53
81
  expect(detected).toEqual([]);