agileflow 2.51.0 → 2.56.0

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 (90) hide show
  1. package/README.md +80 -460
  2. package/package.json +18 -3
  3. package/scripts/agileflow-configure.js +134 -63
  4. package/scripts/agileflow-welcome.js +161 -31
  5. package/scripts/generators/agent-registry.js +45 -57
  6. package/scripts/generators/command-registry.js +48 -32
  7. package/scripts/generators/index.js +2 -6
  8. package/scripts/generators/inject-babysit.js +9 -2
  9. package/scripts/generators/inject-help.js +3 -1
  10. package/scripts/generators/inject-readme.js +7 -3
  11. package/scripts/generators/skill-registry.js +60 -33
  12. package/scripts/get-env.js +13 -12
  13. package/scripts/lib/frontmatter-parser.js +82 -0
  14. package/scripts/obtain-context.js +79 -26
  15. package/scripts/session-coordinator.sh +232 -0
  16. package/scripts/session-manager.js +512 -0
  17. package/src/core/agents/orchestrator.md +275 -0
  18. package/src/core/commands/adr.md +38 -16
  19. package/src/core/commands/agent.md +39 -22
  20. package/src/core/commands/assign.md +17 -0
  21. package/src/core/commands/auto.md +60 -46
  22. package/src/core/commands/babysit.md +302 -637
  23. package/src/core/commands/baseline.md +20 -0
  24. package/src/core/commands/blockers.md +33 -48
  25. package/src/core/commands/board.md +19 -0
  26. package/src/core/commands/changelog.md +20 -0
  27. package/src/core/commands/ci.md +17 -0
  28. package/src/core/commands/context.md +43 -40
  29. package/src/core/commands/debt.md +76 -45
  30. package/src/core/commands/deploy.md +20 -0
  31. package/src/core/commands/deps.md +40 -46
  32. package/src/core/commands/diagnose.md +24 -18
  33. package/src/core/commands/docs.md +18 -0
  34. package/src/core/commands/epic.md +31 -0
  35. package/src/core/commands/feedback.md +33 -21
  36. package/src/core/commands/handoff.md +29 -0
  37. package/src/core/commands/help.md +16 -7
  38. package/src/core/commands/impact.md +31 -61
  39. package/src/core/commands/metrics.md +17 -35
  40. package/src/core/commands/packages.md +21 -0
  41. package/src/core/commands/pr.md +15 -0
  42. package/src/core/commands/readme-sync.md +42 -9
  43. package/src/core/commands/research.md +58 -11
  44. package/src/core/commands/retro.md +42 -50
  45. package/src/core/commands/review.md +22 -27
  46. package/src/core/commands/session/end.md +53 -297
  47. package/src/core/commands/session/history.md +38 -257
  48. package/src/core/commands/session/init.md +44 -446
  49. package/src/core/commands/session/new.md +152 -0
  50. package/src/core/commands/session/resume.md +51 -447
  51. package/src/core/commands/session/status.md +32 -244
  52. package/src/core/commands/sprint.md +33 -0
  53. package/src/core/commands/status.md +18 -0
  54. package/src/core/commands/story-validate.md +32 -0
  55. package/src/core/commands/story.md +21 -6
  56. package/src/core/commands/template.md +18 -0
  57. package/src/core/commands/tests.md +22 -0
  58. package/src/core/commands/update.md +72 -58
  59. package/src/core/commands/validate-expertise.md +25 -37
  60. package/src/core/commands/velocity.md +33 -74
  61. package/src/core/commands/verify.md +16 -0
  62. package/src/core/experts/documentation/expertise.yaml +16 -2
  63. package/src/core/skills/agileflow-retro-facilitator/SKILL.md +57 -219
  64. package/src/core/skills/agileflow-retro-facilitator/cookbook/4ls.md +86 -0
  65. package/src/core/skills/agileflow-retro-facilitator/cookbook/glad-sad-mad.md +79 -0
  66. package/src/core/skills/agileflow-retro-facilitator/cookbook/start-stop-continue.md +142 -0
  67. package/src/core/skills/agileflow-retro-facilitator/prompts/action-items.md +83 -0
  68. package/src/core/skills/writing-skills/SKILL.md +352 -0
  69. package/src/core/skills/writing-skills/testing-skills-with-subagents.md +232 -0
  70. package/tools/cli/agileflow-cli.js +4 -2
  71. package/tools/cli/commands/config.js +20 -13
  72. package/tools/cli/commands/doctor.js +25 -9
  73. package/tools/cli/commands/list.js +10 -6
  74. package/tools/cli/commands/setup.js +54 -3
  75. package/tools/cli/commands/status.js +6 -8
  76. package/tools/cli/commands/uninstall.js +5 -5
  77. package/tools/cli/commands/update.js +51 -7
  78. package/tools/cli/installers/core/installer.js +8 -4
  79. package/tools/cli/installers/ide/_base-ide.js +58 -1
  80. package/tools/cli/installers/ide/claude-code.js +3 -61
  81. package/tools/cli/installers/ide/codex.js +440 -0
  82. package/tools/cli/installers/ide/cursor.js +21 -51
  83. package/tools/cli/installers/ide/manager.js +2 -6
  84. package/tools/cli/installers/ide/windsurf.js +20 -50
  85. package/tools/cli/lib/content-injector.js +26 -49
  86. package/tools/cli/lib/docs-setup.js +3 -2
  87. package/tools/cli/lib/npm-utils.js +39 -12
  88. package/tools/cli/lib/ui.js +31 -10
  89. package/tools/cli/lib/version-checker.js +3 -3
  90. package/tools/postinstall.js +2 -3
@@ -0,0 +1,440 @@
1
+ /**
2
+ * AgileFlow CLI - OpenAI Codex CLI Installer
3
+ *
4
+ * Installs AgileFlow for OpenAI Codex CLI:
5
+ * - Agents become Codex Skills (.codex/skills/agileflow-NAME/)
6
+ * - Commands become Codex Prompts (~/.codex/prompts/agileflow-NAME.md)
7
+ * - AGENTS.md provides project instructions at repo root
8
+ *
9
+ * @see https://developers.openai.com/codex/
10
+ * @see ADR-0002: Codex CLI Integration Strategy
11
+ */
12
+
13
+ const path = require('node:path');
14
+ const os = require('node:os');
15
+ const fs = require('fs-extra');
16
+ const chalk = require('chalk');
17
+ const yaml = require('js-yaml');
18
+ const { BaseIdeSetup } = require('./_base-ide');
19
+
20
+ /**
21
+ * OpenAI Codex CLI setup handler
22
+ */
23
+ class CodexSetup extends BaseIdeSetup {
24
+ constructor() {
25
+ super('codex', 'OpenAI Codex CLI', false);
26
+ // Per-repo config directory
27
+ this.configDir = '.codex';
28
+ // User-level Codex home (can be overridden by $CODEX_HOME)
29
+ this.codexHome = process.env.CODEX_HOME || path.join(os.homedir(), '.codex');
30
+ }
31
+
32
+ /**
33
+ * Get the Codex home directory
34
+ * @returns {string} Path to ~/.codex or $CODEX_HOME
35
+ */
36
+ getCodexHome() {
37
+ return this.codexHome;
38
+ }
39
+
40
+ /**
41
+ * Detect if Codex CLI is installed/configured
42
+ * @param {string} projectDir - Project directory
43
+ * @returns {Promise<boolean>}
44
+ */
45
+ async detect(projectDir) {
46
+ // Check if Codex home exists (user has Codex CLI)
47
+ const codexHomeExists = await this.exists(this.codexHome);
48
+ // Check if project has .codex/ or AGENTS.md
49
+ const projectCodexExists = await this.exists(path.join(projectDir, this.configDir));
50
+ const agentsMdExists = await this.exists(path.join(projectDir, 'AGENTS.md'));
51
+
52
+ return codexHomeExists || projectCodexExists || agentsMdExists;
53
+ }
54
+
55
+ /**
56
+ * Convert an AgileFlow agent markdown file to Codex SKILL.md format
57
+ * @param {string} content - Original agent markdown content
58
+ * @param {string} agentName - Agent name (e.g., 'database')
59
+ * @returns {string} Codex SKILL.md content
60
+ */
61
+ convertAgentToSkill(content, agentName) {
62
+ // Extract frontmatter if present
63
+ let description = `AgileFlow ${agentName} agent`;
64
+ let model = 'default';
65
+
66
+ const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
67
+ if (frontmatterMatch) {
68
+ try {
69
+ const frontmatter = yaml.load(frontmatterMatch[1]);
70
+ if (frontmatter.description) {
71
+ description = frontmatter.description;
72
+ }
73
+ if (frontmatter.model) {
74
+ model = frontmatter.model;
75
+ }
76
+ } catch (e) {
77
+ // Ignore YAML parse errors
78
+ }
79
+ }
80
+
81
+ // Create SKILL.md with YAML frontmatter
82
+ const skillFrontmatter = yaml
83
+ .dump({
84
+ name: `agileflow-${agentName}`,
85
+ description: description,
86
+ version: '1.0.0',
87
+ })
88
+ .trim();
89
+
90
+ // Remove original frontmatter from content
91
+ let bodyContent = content.replace(/^---\n[\s\S]*?\n---\n*/, '');
92
+
93
+ // Add Codex-specific header
94
+ const codexHeader = `# AgileFlow: ${agentName.charAt(0).toUpperCase() + agentName.slice(1)} Agent
95
+
96
+ > Invoke with \`$agileflow-${agentName}\` or via \`/skills\`
97
+
98
+ `;
99
+
100
+ // Replace Claude-specific references
101
+ bodyContent = bodyContent
102
+ .replace(/Claude Code/gi, 'Codex CLI')
103
+ .replace(/CLAUDE\.md/g, 'AGENTS.md')
104
+ .replace(/\.claude\//g, '.codex/')
105
+ .replace(/Task tool/gi, 'skill invocation');
106
+
107
+ return `---
108
+ ${skillFrontmatter}
109
+ ---
110
+
111
+ ${codexHeader}${bodyContent}`;
112
+ }
113
+
114
+ /**
115
+ * Convert an AgileFlow command markdown file to Codex prompt format
116
+ * @param {string} content - Original command markdown content
117
+ * @param {string} commandName - Command name (e.g., 'board')
118
+ * @returns {string} Codex prompt content
119
+ */
120
+ convertCommandToPrompt(content, commandName) {
121
+ // Extract description from frontmatter if present
122
+ let description = `AgileFlow ${commandName} command`;
123
+
124
+ const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
125
+ if (frontmatterMatch) {
126
+ try {
127
+ const frontmatter = yaml.load(frontmatterMatch[1]);
128
+ if (frontmatter.description) {
129
+ description = frontmatter.description;
130
+ }
131
+ } catch (e) {
132
+ // Ignore YAML parse errors
133
+ }
134
+ }
135
+
136
+ // Remove original frontmatter from content
137
+ let bodyContent = content.replace(/^---\n[\s\S]*?\n---\n*/, '');
138
+
139
+ // Replace Claude-specific references
140
+ bodyContent = bodyContent
141
+ .replace(/Claude Code/gi, 'Codex CLI')
142
+ .replace(/CLAUDE\.md/g, 'AGENTS.md')
143
+ .replace(/\.claude\//g, '.codex/')
144
+ .replace(/\/agileflow:/g, '$agileflow-');
145
+
146
+ // Add Codex prompt header
147
+ const header = `# AgileFlow: ${commandName}
148
+
149
+ > ${description}
150
+
151
+ ## Instructions
152
+
153
+ `;
154
+
155
+ // Add input placeholder at the end
156
+ const footer = `
157
+
158
+ ## Context
159
+
160
+ {{input}}
161
+ `;
162
+
163
+ return `${header}${bodyContent}${footer}`;
164
+ }
165
+
166
+ /**
167
+ * Install AgileFlow agents as Codex skills
168
+ * @param {string} projectDir - Project directory
169
+ * @param {string} agileflowDir - AgileFlow installation directory
170
+ * @returns {Promise<number>} Number of skills installed
171
+ */
172
+ async installSkills(projectDir, agileflowDir) {
173
+ const agentsSource = path.join(agileflowDir, 'agents');
174
+ const skillsTarget = path.join(projectDir, this.configDir, 'skills');
175
+
176
+ if (!(await this.exists(agentsSource))) {
177
+ return 0;
178
+ }
179
+
180
+ let skillCount = 0;
181
+ const agents = await this.scanDirectory(agentsSource, '.md');
182
+
183
+ for (const agent of agents) {
184
+ const content = await this.readFile(agent.path);
185
+ const skillContent = this.convertAgentToSkill(content, agent.name);
186
+
187
+ // Create skill directory: .codex/skills/agileflow-{name}/
188
+ const skillDir = path.join(skillsTarget, `agileflow-${agent.name}`);
189
+ await this.ensureDir(skillDir);
190
+
191
+ // Write SKILL.md
192
+ await this.writeFile(path.join(skillDir, 'SKILL.md'), skillContent);
193
+ skillCount++;
194
+ }
195
+
196
+ return skillCount;
197
+ }
198
+
199
+ /**
200
+ * Install AgileFlow commands as Codex prompts (user-level)
201
+ * @param {string} agileflowDir - AgileFlow installation directory
202
+ * @returns {Promise<number>} Number of prompts installed
203
+ */
204
+ async installPrompts(agileflowDir) {
205
+ const commandsSource = path.join(agileflowDir, 'commands');
206
+ const promptsTarget = path.join(this.codexHome, 'prompts');
207
+
208
+ if (!(await this.exists(commandsSource))) {
209
+ return 0;
210
+ }
211
+
212
+ await this.ensureDir(promptsTarget);
213
+
214
+ let promptCount = 0;
215
+ const commands = await this.scanDirectory(commandsSource, '.md');
216
+
217
+ for (const command of commands) {
218
+ const content = await this.readFile(command.path);
219
+ const promptContent = this.convertCommandToPrompt(content, command.name);
220
+
221
+ // Write prompt: ~/.codex/prompts/agileflow-{name}.md
222
+ const promptPath = path.join(promptsTarget, `agileflow-${command.name}.md`);
223
+ await this.writeFile(promptPath, promptContent);
224
+ promptCount++;
225
+ }
226
+
227
+ return promptCount;
228
+ }
229
+
230
+ /**
231
+ * Generate AGENTS.md scaffolding for the project
232
+ * @param {string} projectDir - Project directory
233
+ * @returns {Promise<boolean>} Whether AGENTS.md was created
234
+ */
235
+ async generateAgentsMd(projectDir) {
236
+ const agentsMdPath = path.join(projectDir, 'AGENTS.md');
237
+
238
+ // Don't overwrite existing AGENTS.md
239
+ if (await this.exists(agentsMdPath)) {
240
+ console.log(chalk.dim(` - AGENTS.md already exists (preserved)`));
241
+ return false;
242
+ }
243
+
244
+ const content = `# AGENTS.md
245
+
246
+ > Project instructions for Codex CLI with AgileFlow integration
247
+
248
+ ## Project Commands
249
+
250
+ \`\`\`bash
251
+ # Run tests
252
+ npm test
253
+
254
+ # Lint code
255
+ npm run lint
256
+
257
+ # Build project
258
+ npm run build
259
+ \`\`\`
260
+
261
+ ## Conventions
262
+
263
+ - Prefer running tests before claiming success
264
+ - Keep changes small and reviewable
265
+ - Use AgileFlow skills when relevant (see below)
266
+ - Check \`${this.docsFolder}/09-agents/status.json\` for current work status
267
+
268
+ ## When to Use AgileFlow Skills
269
+
270
+ Invoke skills with \`$skill-name\` or list available skills with \`/skills\`.
271
+
272
+ | Task Type | Skill to Use |
273
+ |-----------|--------------|
274
+ | Architecture decisions | \`$agileflow-adr-writer\` |
275
+ | Database work | \`$agileflow-database\` |
276
+ | API endpoints | \`$agileflow-api\` |
277
+ | UI components | \`$agileflow-ui\` |
278
+ | Testing | \`$agileflow-testing\` |
279
+ | CI/CD setup | \`$agileflow-ci\` |
280
+ | Security review | \`$agileflow-security\` |
281
+ | Performance | \`$agileflow-performance\` |
282
+ | Documentation | \`$agileflow-documentation\` |
283
+
284
+ ## AgileFlow Status
285
+
286
+ - **Stories**: \`${this.docsFolder}/09-agents/status.json\`
287
+ - **Research**: \`${this.docsFolder}/10-research/\`
288
+ - **Decisions**: \`${this.docsFolder}/03-decisions/\`
289
+ - **Epics**: \`${this.docsFolder}/05-epics/\`
290
+
291
+ ## Security Recommendations
292
+
293
+ For safe operation, configure Codex with:
294
+
295
+ \`\`\`toml
296
+ # ~/.codex/config.toml
297
+ approval_policy = "on-request"
298
+ sandbox_mode = "workspace-write"
299
+ \`\`\`
300
+
301
+ ---
302
+
303
+ *Generated by AgileFlow - https://github.com/projectquestorg/AgileFlow*
304
+ `;
305
+
306
+ await this.writeFile(agentsMdPath, content);
307
+ return true;
308
+ }
309
+
310
+ /**
311
+ * Generate AGENTS.override.md template for subdirectories
312
+ * @param {string} targetDir - Directory to create override in
313
+ * @returns {Promise<boolean>} Whether file was created
314
+ */
315
+ async generateAgentsOverride(targetDir) {
316
+ const overridePath = path.join(targetDir, 'AGENTS.override.md');
317
+
318
+ // Don't overwrite existing override
319
+ if (await this.exists(overridePath)) {
320
+ return false;
321
+ }
322
+
323
+ const content = `# AGENTS.override.md
324
+
325
+ > Override instructions for this subdirectory
326
+
327
+ ## Directory-Specific Context
328
+
329
+ This directory contains: [describe purpose]
330
+
331
+ ## Additional Commands
332
+
333
+ \`\`\`bash
334
+ # Directory-specific commands here
335
+ \`\`\`
336
+
337
+ ## Directory Conventions
338
+
339
+ - [List any patterns specific to this directory]
340
+
341
+ ---
342
+
343
+ *This file overrides AGENTS.md for this subdirectory*
344
+ `;
345
+
346
+ await this.writeFile(overridePath, content);
347
+ return true;
348
+ }
349
+
350
+ /**
351
+ * Setup Codex CLI configuration
352
+ * @param {string} projectDir - Project directory
353
+ * @param {string} agileflowDir - AgileFlow installation directory
354
+ * @param {Object} options - Setup options
355
+ */
356
+ async setup(projectDir, agileflowDir, options = {}) {
357
+ console.log(chalk.hex('#e8683a')(` Setting up ${this.displayName}...`));
358
+
359
+ // Clean up old installation first
360
+ await this.cleanup(projectDir);
361
+
362
+ // Ensure .codex directory exists
363
+ await this.ensureDir(path.join(projectDir, this.configDir));
364
+
365
+ // 1. Install agents as skills (per-repo)
366
+ const skillCount = await this.installSkills(projectDir, agileflowDir);
367
+ if (skillCount > 0) {
368
+ console.log(chalk.dim(` - ${skillCount} skills installed to .codex/skills/`));
369
+ }
370
+
371
+ // 2. Install commands as prompts (user-level)
372
+ const promptCount = await this.installPrompts(agileflowDir);
373
+ if (promptCount > 0) {
374
+ console.log(chalk.dim(` - ${promptCount} prompts installed to ~/.codex/prompts/`));
375
+ }
376
+
377
+ // 3. Generate AGENTS.md if it doesn't exist
378
+ const agentsMdCreated = await this.generateAgentsMd(projectDir);
379
+ if (agentsMdCreated) {
380
+ console.log(chalk.dim(` - AGENTS.md created at project root`));
381
+ }
382
+
383
+ console.log(chalk.green(` ✓ ${this.displayName} configured`));
384
+ console.log(chalk.dim(` - Skills: .codex/skills/agileflow-*/`));
385
+ console.log(chalk.dim(` - Prompts: ~/.codex/prompts/agileflow-*.md`));
386
+ console.log(chalk.dim(` - Instructions: AGENTS.md`));
387
+
388
+ return {
389
+ success: true,
390
+ skills: skillCount,
391
+ prompts: promptCount,
392
+ agentsMd: agentsMdCreated,
393
+ };
394
+ }
395
+
396
+ /**
397
+ * Cleanup Codex configuration
398
+ * @param {string} projectDir - Project directory
399
+ * @param {Object} options - Cleanup options
400
+ * @param {boolean} options.removeAgentsMd - Also remove AGENTS.md (requires confirmation)
401
+ */
402
+ async cleanup(projectDir, options = {}) {
403
+ // Clean up per-repo skills
404
+ const skillsDir = path.join(projectDir, this.configDir, 'skills');
405
+ if (await this.exists(skillsDir)) {
406
+ const entries = await fs.readdir(skillsDir);
407
+ for (const entry of entries) {
408
+ if (entry.startsWith('agileflow-')) {
409
+ await fs.remove(path.join(skillsDir, entry));
410
+ }
411
+ }
412
+ console.log(chalk.dim(` Cleaned up AgileFlow skills from .codex/skills/`));
413
+ }
414
+
415
+ // Clean up user-level prompts
416
+ const promptsDir = path.join(this.codexHome, 'prompts');
417
+ if (await this.exists(promptsDir)) {
418
+ const entries = await fs.readdir(promptsDir);
419
+ for (const entry of entries) {
420
+ if (entry.startsWith('agileflow-')) {
421
+ await fs.remove(path.join(promptsDir, entry));
422
+ }
423
+ }
424
+ console.log(chalk.dim(` Cleaned up AgileFlow prompts from ~/.codex/prompts/`));
425
+ }
426
+
427
+ // Optionally remove AGENTS.md if explicitly requested
428
+ if (options.removeAgentsMd) {
429
+ const agentsMdPath = path.join(projectDir, 'AGENTS.md');
430
+ if (await this.exists(agentsMdPath)) {
431
+ await fs.remove(agentsMdPath);
432
+ console.log(chalk.dim(` Removed AGENTS.md`));
433
+ }
434
+ } else {
435
+ console.log(chalk.dim(` Note: AGENTS.md preserved (use removeAgentsMd option to delete)`));
436
+ }
437
+ }
438
+ }
439
+
440
+ module.exports = { CodexSetup };
@@ -32,69 +32,39 @@ class CursorSetup extends BaseIdeSetup {
32
32
  // Clean up old installation first
33
33
  await this.cleanup(projectDir);
34
34
 
35
- // Create .cursor/commands/agileflow directory
35
+ // Create .cursor/commands/AgileFlow directory
36
36
  const cursorDir = path.join(projectDir, this.configDir);
37
37
  const commandsDir = path.join(cursorDir, this.commandsDir);
38
38
  const agileflowCommandsDir = path.join(commandsDir, 'AgileFlow');
39
39
 
40
- await this.ensureDir(agileflowCommandsDir);
41
-
42
- // Get commands from AgileFlow installation
40
+ // Install commands using shared recursive method
43
41
  const commandsSource = path.join(agileflowDir, 'commands');
44
- let commandCount = 0;
45
-
46
- if (await this.exists(commandsSource)) {
47
- const commands = await this.scanDirectory(commandsSource, '.md');
48
-
49
- for (const command of commands) {
50
- // Read the original command content
51
- let content = await this.readFile(command.path);
52
-
53
- // Inject dynamic content (agent lists, command lists)
54
- content = this.injectDynamicContent(content, agileflowDir);
55
-
56
- // Replace docs/ references with custom folder name
57
- content = this.replaceDocsReferences(content);
58
-
59
- const targetPath = path.join(agileflowCommandsDir, `${command.name}.md`);
60
- await this.writeFile(targetPath, content);
61
- commandCount++;
62
- }
63
- }
64
-
65
- // Create agents subdirectory
66
- const agileflowAgentsDir = path.join(agileflowCommandsDir, 'agents');
67
- await this.ensureDir(agileflowAgentsDir);
68
-
69
- // Get agents from AgileFlow installation
42
+ const commandResult = await this.installCommandsRecursive(
43
+ commandsSource,
44
+ agileflowCommandsDir,
45
+ agileflowDir,
46
+ true // Inject dynamic content
47
+ );
48
+
49
+ // Install agents as subdirectory
70
50
  const agentsSource = path.join(agileflowDir, 'agents');
71
- let agentCount = 0;
72
-
73
- if (await this.exists(agentsSource)) {
74
- const agents = await this.scanDirectory(agentsSource, '.md');
75
-
76
- for (const agent of agents) {
77
- // Read the original agent content
78
- let content = await this.readFile(agent.path);
79
-
80
- // Replace docs/ references with custom folder name
81
- content = this.replaceDocsReferences(content);
82
-
83
- const targetPath = path.join(agileflowAgentsDir, `${agent.name}.md`);
84
- await this.writeFile(targetPath, content);
85
- agentCount++;
86
- }
87
- }
51
+ const agentsTargetDir = path.join(agileflowCommandsDir, 'agents');
52
+ const agentResult = await this.installCommandsRecursive(
53
+ agentsSource,
54
+ agentsTargetDir,
55
+ agileflowDir,
56
+ false // No dynamic content for agents
57
+ );
88
58
 
89
59
  console.log(chalk.green(` ✓ ${this.displayName} configured:`));
90
- console.log(chalk.dim(` - ${commandCount} commands installed`));
91
- console.log(chalk.dim(` - ${agentCount} agents installed`));
60
+ console.log(chalk.dim(` - ${commandResult.commands} commands installed`));
61
+ console.log(chalk.dim(` - ${agentResult.commands} agents installed`));
92
62
  console.log(chalk.dim(` - Path: ${path.relative(projectDir, agileflowCommandsDir)}`));
93
63
 
94
64
  return {
95
65
  success: true,
96
- commands: commandCount,
97
- agents: agentCount,
66
+ commands: commandResult.commands,
67
+ agents: agentResult.commands,
98
68
  };
99
69
  }
100
70
 
@@ -52,12 +52,8 @@ class IdeManager {
52
52
  const ideDir = __dirname;
53
53
 
54
54
  try {
55
- const files = fs.readdirSync(ideDir).filter((file) => {
56
- return (
57
- file.endsWith('.js') &&
58
- !file.startsWith('_') &&
59
- file !== 'manager.js'
60
- );
55
+ const files = fs.readdirSync(ideDir).filter(file => {
56
+ return file.endsWith('.js') && !file.startsWith('_') && file !== 'manager.js';
61
57
  });
62
58
 
63
59
  files.sort();
@@ -37,64 +37,34 @@ class WindsurfSetup extends BaseIdeSetup {
37
37
  const workflowsDir = path.join(windsurfDir, this.workflowsDir);
38
38
  const agileflowWorkflowsDir = path.join(workflowsDir, 'agileflow');
39
39
 
40
- await this.ensureDir(agileflowWorkflowsDir);
41
-
42
- // Get commands from AgileFlow installation
40
+ // Install commands using shared recursive method
43
41
  const commandsSource = path.join(agileflowDir, 'commands');
44
- let commandCount = 0;
45
-
46
- if (await this.exists(commandsSource)) {
47
- const commands = await this.scanDirectory(commandsSource, '.md');
48
-
49
- for (const command of commands) {
50
- // Read the original command content
51
- let content = await this.readFile(command.path);
52
-
53
- // Inject dynamic content (agent lists, command lists)
54
- content = this.injectDynamicContent(content, agileflowDir);
55
-
56
- // Replace docs/ references with custom folder name
57
- content = this.replaceDocsReferences(content);
58
-
59
- const targetPath = path.join(agileflowWorkflowsDir, `${command.name}.md`);
60
- await this.writeFile(targetPath, content);
61
- commandCount++;
62
- }
63
- }
64
-
65
- // Create agents subdirectory
66
- const agileflowAgentsDir = path.join(agileflowWorkflowsDir, 'agents');
67
- await this.ensureDir(agileflowAgentsDir);
68
-
69
- // Get agents from AgileFlow installation
42
+ const commandResult = await this.installCommandsRecursive(
43
+ commandsSource,
44
+ agileflowWorkflowsDir,
45
+ agileflowDir,
46
+ true // Inject dynamic content
47
+ );
48
+
49
+ // Install agents as subdirectory
70
50
  const agentsSource = path.join(agileflowDir, 'agents');
71
- let agentCount = 0;
72
-
73
- if (await this.exists(agentsSource)) {
74
- const agents = await this.scanDirectory(agentsSource, '.md');
75
-
76
- for (const agent of agents) {
77
- // Read the original agent content
78
- let content = await this.readFile(agent.path);
79
-
80
- // Replace docs/ references with custom folder name
81
- content = this.replaceDocsReferences(content);
82
-
83
- const targetPath = path.join(agileflowAgentsDir, `${agent.name}.md`);
84
- await this.writeFile(targetPath, content);
85
- agentCount++;
86
- }
87
- }
51
+ const agentsTargetDir = path.join(agileflowWorkflowsDir, 'agents');
52
+ const agentResult = await this.installCommandsRecursive(
53
+ agentsSource,
54
+ agentsTargetDir,
55
+ agileflowDir,
56
+ false // No dynamic content for agents
57
+ );
88
58
 
89
59
  console.log(chalk.green(` ✓ ${this.displayName} configured:`));
90
- console.log(chalk.dim(` - ${commandCount} workflows installed`));
91
- console.log(chalk.dim(` - ${agentCount} agent workflows installed`));
60
+ console.log(chalk.dim(` - ${commandResult.commands} workflows installed`));
61
+ console.log(chalk.dim(` - ${agentResult.commands} agent workflows installed`));
92
62
  console.log(chalk.dim(` - Path: ${path.relative(projectDir, agileflowWorkflowsDir)}`));
93
63
 
94
64
  return {
95
65
  success: true,
96
- commands: commandCount,
97
- agents: agentCount,
66
+ commands: commandResult.commands,
67
+ agents: agentResult.commands,
98
68
  };
99
69
  }
100
70