agileflow 2.61.0 → 2.63.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 (68) hide show
  1. package/README.md +9 -9
  2. package/package.json +1 -1
  3. package/scripts/lib/counter.js +103 -0
  4. package/src/core/commands/auto.md +1 -0
  5. package/src/core/commands/babysit.md +170 -29
  6. package/src/core/commands/board.md +1 -0
  7. package/src/core/commands/ci.md +1 -0
  8. package/src/core/commands/compress.md +1 -0
  9. package/src/core/commands/deploy.md +1 -0
  10. package/src/core/commands/help.md +1 -0
  11. package/src/core/commands/research.md +1 -0
  12. package/src/core/commands/skill/create.md +566 -0
  13. package/src/core/commands/skill/delete.md +189 -0
  14. package/src/core/commands/skill/edit.md +245 -0
  15. package/src/core/commands/skill/list.md +155 -0
  16. package/src/core/commands/skill/test.md +249 -0
  17. package/src/core/commands/template.md +1 -0
  18. package/src/core/commands/tests.md +1 -0
  19. package/src/core/commands/update.md +1 -0
  20. package/src/core/commands/velocity.md +1 -0
  21. package/src/core/experts/refactor/expertise.yaml +17 -12
  22. package/src/core/templates/claude-settings.advanced.example.json +1 -1
  23. package/src/core/templates/claude-settings.example.json +1 -1
  24. package/tools/cli/commands/list.js +8 -13
  25. package/tools/cli/commands/uninstall.js +70 -0
  26. package/tools/cli/commands/update.js +21 -4
  27. package/tools/cli/installers/core/installer.js +20 -19
  28. package/tools/cli/installers/ide/_base-ide.js +18 -4
  29. package/tools/cli/installers/ide/claude-code.js +4 -15
  30. package/tools/cli/installers/ide/codex.js +9 -13
  31. package/tools/cli/lib/content-injector.js +162 -31
  32. package/tools/cli/lib/utils.js +87 -0
  33. package/src/core/skills/acceptance-criteria-generator/SKILL.md +0 -46
  34. package/src/core/skills/adr-template/SKILL.md +0 -62
  35. package/src/core/skills/agileflow-acceptance-criteria/SKILL.md +0 -156
  36. package/src/core/skills/agileflow-adr/SKILL.md +0 -147
  37. package/src/core/skills/agileflow-adr/examples/database-choice-example.md +0 -122
  38. package/src/core/skills/agileflow-adr/templates/adr-template.md +0 -69
  39. package/src/core/skills/agileflow-commit-messages/SKILL.md +0 -130
  40. package/src/core/skills/agileflow-commit-messages/reference/bad-examples.md +0 -168
  41. package/src/core/skills/agileflow-commit-messages/reference/good-examples.md +0 -120
  42. package/src/core/skills/agileflow-commit-messages/scripts/check-attribution.sh +0 -15
  43. package/src/core/skills/agileflow-epic-planner/SKILL.md +0 -184
  44. package/src/core/skills/agileflow-retro-facilitator/SKILL.md +0 -119
  45. package/src/core/skills/agileflow-retro-facilitator/cookbook/4ls.md +0 -86
  46. package/src/core/skills/agileflow-retro-facilitator/cookbook/glad-sad-mad.md +0 -79
  47. package/src/core/skills/agileflow-retro-facilitator/cookbook/start-stop-continue.md +0 -142
  48. package/src/core/skills/agileflow-retro-facilitator/prompts/action-items.md +0 -83
  49. package/src/core/skills/agileflow-sprint-planner/SKILL.md +0 -212
  50. package/src/core/skills/agileflow-story-writer/SKILL.md +0 -163
  51. package/src/core/skills/agileflow-story-writer/examples/good-story-example.md +0 -63
  52. package/src/core/skills/agileflow-story-writer/templates/story-template.md +0 -44
  53. package/src/core/skills/agileflow-tech-debt/SKILL.md +0 -215
  54. package/src/core/skills/api-documentation-generator/SKILL.md +0 -65
  55. package/src/core/skills/changelog-entry/SKILL.md +0 -55
  56. package/src/core/skills/commit-message-formatter/SKILL.md +0 -50
  57. package/src/core/skills/deployment-guide-generator/SKILL.md +0 -84
  58. package/src/core/skills/diagram-generator/SKILL.md +0 -65
  59. package/src/core/skills/error-handler-template/SKILL.md +0 -78
  60. package/src/core/skills/migration-checklist/SKILL.md +0 -82
  61. package/src/core/skills/pr-description/SKILL.md +0 -65
  62. package/src/core/skills/sql-schema-generator/SKILL.md +0 -69
  63. package/src/core/skills/story-skeleton/SKILL.md +0 -34
  64. package/src/core/skills/test-case-generator/SKILL.md +0 -63
  65. package/src/core/skills/type-definitions/SKILL.md +0 -65
  66. package/src/core/skills/validation-schema-generator/SKILL.md +0 -64
  67. package/src/core/skills/writing-skills/SKILL.md +0 -352
  68. package/src/core/skills/writing-skills/testing-skills-with-subagents.md +0 -232
@@ -0,0 +1,249 @@
1
+ ---
2
+ description: Verify a skill works correctly by testing its activation and functionality
3
+ argument-hint: [SKILL_NAME] (optional)
4
+ ---
5
+
6
+ # /agileflow:skill:test
7
+
8
+ Test a skill to verify it activates correctly and produces expected results.
9
+
10
+ ---
11
+
12
+ ## Workflow
13
+
14
+ ### STEP 1: Select skill to test
15
+
16
+ If SKILL_NAME not provided, list available skills:
17
+
18
+ ```bash
19
+ ls -d .claude/skills/*/ 2>/dev/null | xargs -I {} basename {}
20
+ ```
21
+
22
+ Then ask user:
23
+
24
+ ```xml
25
+ <invoke name="AskUserQuestion">
26
+ <parameter name="questions">[{
27
+ "question": "Which skill would you like to test?",
28
+ "header": "Select Skill",
29
+ "multiSelect": false,
30
+ "options": [
31
+ {"label": "<skill-1>", "description": "<description from frontmatter>"},
32
+ {"label": "<skill-2>", "description": "<description from frontmatter>"},
33
+ {"label": "<skill-3>", "description": "<description from frontmatter>"}
34
+ ]
35
+ }]</parameter>
36
+ </invoke>
37
+ ```
38
+
39
+ ### STEP 2: Read skill metadata
40
+
41
+ Read SKILL.md and extract:
42
+ - Name and description
43
+ - "When to Use" section (activation triggers)
44
+ - Cookbook entries (if any)
45
+ - Quick reference section
46
+
47
+ ### STEP 3: Run validation checks
48
+
49
+ ```
50
+ 🧪 Testing: supabase-swift
51
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
52
+
53
+ Structure Validation:
54
+ ✅ SKILL.md exists
55
+ ✅ Frontmatter has name and description
56
+ ✅ references.md exists
57
+ ✅ cookbook/ directory found (2 entries)
58
+ ⚠️ No .mcp.json (MCP not configured)
59
+
60
+ Content Validation:
61
+ ✅ "When to Use" section present
62
+ ✅ Description under 1024 characters
63
+ ✅ SKILL.md under 500 lines (current: 287)
64
+ ✅ All cookbook files referenced in SKILL.md
65
+
66
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
67
+ ```
68
+
69
+ ### STEP 4: Test activation (optional)
70
+
71
+ ```xml
72
+ <invoke name="AskUserQuestion">
73
+ <parameter name="questions">[{
74
+ "question": "Would you like to test skill activation with a sample prompt?",
75
+ "header": "Activation Test",
76
+ "multiSelect": false,
77
+ "options": [
78
+ {"label": "Yes, test activation (Recommended)", "description": "I'll simulate using the skill"},
79
+ {"label": "Skip activation test", "description": "Structure validation is enough"},
80
+ {"label": "View SKILL.md content", "description": "Read the full skill instructions"}
81
+ ]
82
+ }]</parameter>
83
+ </invoke>
84
+ ```
85
+
86
+ If user wants activation test:
87
+
88
+ 1. **Extract sample triggers** from "When to Use" section
89
+ 2. **Present test prompts** based on triggers:
90
+ ```
91
+ Sample prompts that should activate this skill:
92
+
93
+ 1. "Help me set up Supabase authentication in Swift"
94
+ 2. "Create CRUD operations for my database"
95
+ 3. "How do I query Supabase from iOS?"
96
+ ```
97
+ 3. **Ask user to pick one** or provide custom prompt
98
+ 4. **Execute the skill** by reading SKILL.md and following instructions
99
+ 5. **Report results**
100
+
101
+ ### STEP 5: Show test results
102
+
103
+ ```
104
+ 🧪 Test Results: supabase-swift
105
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
106
+
107
+ Structure: ✅ PASS (5/5 checks)
108
+ Content: ✅ PASS (4/4 checks)
109
+ Activation: ✅ PASS (skill triggered correctly)
110
+ MCP Config: ⚠️ NOT CONFIGURED
111
+
112
+ Overall: ✅ SKILL IS FUNCTIONAL
113
+
114
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
115
+ ```
116
+
117
+ ### STEP 6: Offer next actions
118
+
119
+ ```xml
120
+ <invoke name="AskUserQuestion">
121
+ <parameter name="questions">[{
122
+ "question": "What would you like to do?",
123
+ "header": "Next Action",
124
+ "multiSelect": false,
125
+ "options": [
126
+ {"label": "Test another skill", "description": "Validate a different skill"},
127
+ {"label": "Edit this skill", "description": "Fix issues or improve"},
128
+ {"label": "Use this skill now", "description": "Start working with it"},
129
+ {"label": "Done", "description": "Exit skill testing"}
130
+ ]
131
+ }]</parameter>
132
+ </invoke>
133
+ ```
134
+
135
+ ---
136
+
137
+ ## Validation Checks
138
+
139
+ ### Structure Checks
140
+
141
+ | Check | Pass Condition |
142
+ |-------|----------------|
143
+ | SKILL.md exists | File present in skill directory |
144
+ | Frontmatter valid | Has `name:` and `description:` |
145
+ | references.md | File exists (optional but recommended) |
146
+ | cookbook/ | Directory exists if referenced in SKILL.md |
147
+ | .mcp.json | Valid JSON if present |
148
+
149
+ ### Content Checks
150
+
151
+ | Check | Pass Condition |
152
+ |-------|----------------|
153
+ | When to Use | Section present with activation triggers |
154
+ | Description length | Under 1024 characters |
155
+ | SKILL.md size | Under 500 lines |
156
+ | Cookbook references | All referenced files exist |
157
+ | No broken links | All local file references valid |
158
+
159
+ ### MCP Checks (if .mcp.json exists)
160
+
161
+ | Check | Pass Condition |
162
+ |-------|----------------|
163
+ | Valid JSON | Parses without errors |
164
+ | Has mcpServers | Contains mcpServers object |
165
+ | Command exists | Command is npx or valid executable |
166
+ | Env vars documented | Any ${VAR} has comments |
167
+
168
+ ---
169
+
170
+ ## Test Report Format
171
+
172
+ ```
173
+ 🧪 Skill Test Report: <skill-name>
174
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
175
+
176
+ STRUCTURE VALIDATION
177
+ [✅|❌] SKILL.md exists
178
+ [✅|❌] Frontmatter valid (name, description)
179
+ [✅|⚠️] references.md exists
180
+ [✅|⚠️] cookbook/ directory
181
+ [✅|⚠️] .mcp.json present
182
+
183
+ CONTENT VALIDATION
184
+ [✅|❌] "When to Use" section
185
+ [✅|❌] Description < 1024 chars (<current> chars)
186
+ [✅|❌] SKILL.md < 500 lines (<current> lines)
187
+ [✅|❌] All cookbook files exist
188
+
189
+ ACTIVATION TEST
190
+ [✅|❌] Skill triggered on test prompt
191
+ [✅|❌] Produced expected output format
192
+
193
+ ISSUES FOUND
194
+ - <issue 1>
195
+ - <issue 2>
196
+
197
+ RECOMMENDATIONS
198
+ - <suggestion 1>
199
+ - <suggestion 2>
200
+
201
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
202
+ Overall: [✅ PASS | ⚠️ WARNINGS | ❌ FAIL]
203
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
204
+ ```
205
+
206
+ ---
207
+
208
+ ## Error Handling
209
+
210
+ ### Skill Not Found
211
+ ```
212
+ ❌ Skill "<name>" not found.
213
+
214
+ Available skills:
215
+ - ui-components
216
+ - api-integration
217
+
218
+ Use /agileflow:skill:list to see all skills.
219
+ ```
220
+
221
+ ### No Skills Installed
222
+ ```
223
+ ❌ No skills to test.
224
+
225
+ Create a skill first: /agileflow:skill:create
226
+ ```
227
+
228
+ ### Validation Failed
229
+ ```
230
+ ❌ Skill "<name>" has issues:
231
+
232
+ ❌ Missing SKILL.md frontmatter
233
+ ❌ Description exceeds 1024 characters (1523)
234
+ ⚠️ No "When to Use" section
235
+
236
+ Fix with: /agileflow:skill:edit <name>
237
+ ```
238
+
239
+ ---
240
+
241
+ ## Usage
242
+
243
+ ```bash
244
+ # Interactive mode
245
+ /agileflow:skill:test
246
+
247
+ # Test specific skill
248
+ /agileflow:skill:test supabase-swift
249
+ ```
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  description: Create and manage custom document templates
3
+ argument-hint: (no arguments)
3
4
  ---
4
5
 
5
6
  # custom-template
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  description: Set up automated testing infrastructure
3
+ argument-hint: (no arguments)
3
4
  ---
4
5
 
5
6
  # setup-tests
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  description: Generate stakeholder progress report
3
+ argument-hint: (no arguments)
3
4
  ---
4
5
 
5
6
  # stakeholder-update
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  description: Track velocity and forecast sprint capacity
3
+ argument-hint: (no arguments)
3
4
  model: haiku
4
5
  ---
5
6
 
@@ -26,22 +26,22 @@ files:
26
26
  core_content:
27
27
  - path: packages/cli/src/core/commands/
28
28
  purpose: Slash command markdown files
29
- count: 41
29
+ count: "{{COMMAND_COUNT}}"
30
30
  pattern: "<command>.md with frontmatter"
31
31
 
32
32
  - path: packages/cli/src/core/agents/
33
33
  purpose: Agent prompt markdown files
34
- count: 26
34
+ count: "{{AGENT_COUNT}}"
35
35
  pattern: "<agent>.md with frontmatter (name, description, tools, model)"
36
36
 
37
37
  - path: packages/cli/src/core/experts/
38
38
  purpose: Expert knowledge files
39
- count: 25
39
+ count: "{{SKILL_COUNT}}"
40
40
  pattern: "<domain>/expertise.yaml, question.md, self-improve.md, workflow.md"
41
41
 
42
42
  - path: packages/cli/src/core/skills/
43
43
  purpose: Auto-activating skills
44
- count: 23
44
+ count: "{{SKILL_COUNT}}"
45
45
  pattern: "<skill>/SKILL.md"
46
46
 
47
47
  scripts:
@@ -67,10 +67,10 @@ relationships:
67
67
 
68
68
  - name: content_structure
69
69
  locations:
70
- - "commands/ - 41 .md files"
71
- - "agents/ - 26 .md files"
72
- - "experts/ - 25 directories"
73
- - "skills/ - 23 directories"
70
+ - "commands/ - {{COMMAND_COUNT}} .md files"
71
+ - "agents/ - {{AGENT_COUNT}} .md files"
72
+ - "experts/ - {{SKILL_COUNT}} directories"
73
+ - "skills/ - {{SKILL_COUNT}} directories"
74
74
  observation: "Consistent patterns across all content types"
75
75
 
76
76
  patterns:
@@ -78,9 +78,14 @@ patterns:
78
78
  description: "Placeholders replaced at install time"
79
79
  location: "lib/content-injector.js"
80
80
  placeholders:
81
- - "<!-- {{AGENT_LIST}} -->"
82
- - "<!-- {{COMMAND_LIST}} -->"
83
- benefit: "Zero maintenance for lists"
81
+ - "{{COMMAND_COUNT}} - Number of commands"
82
+ - "{{AGENT_COUNT}} - Number of agents"
83
+ - "{{SKILL_COUNT}} - Number of skills"
84
+ - "{{VERSION}} - AgileFlow version"
85
+ - "{{INSTALL_DATE}} - Installation date"
86
+ - "<!-- {{AGENT_LIST}} --> - Full agent list"
87
+ - "<!-- {{COMMAND_LIST}} --> - Full command list"
88
+ benefit: "Zero maintenance for lists and counts"
84
89
 
85
90
  - name: IDE-Specific Installers
86
91
  description: "Separate installer per IDE"
@@ -116,7 +121,7 @@ learnings:
116
121
 
117
122
  - date: 2025-12-21
118
123
  context: "Analyzed content structure"
119
- insight: "Consistent patterns: 41 commands, 26 agents, 25 experts, 23 skills - all use frontmatter markdown"
124
+ insight: "Consistent patterns: {{COMMAND_COUNT}} commands, {{AGENT_COUNT}} agents, {{SKILL_COUNT}} experts, {{SKILL_COUNT}} skills - all use frontmatter markdown"
120
125
  source: "packages/cli/src/core/"
121
126
 
122
127
  - date: 2025-12-21
@@ -6,7 +6,7 @@
6
6
  "hooks": [
7
7
  {
8
8
  "type": "command",
9
- "command": "echo '🚀 AgileFlow v2.19.0 loaded - Type /agileflow:help for commands'"
9
+ "command": "echo '🚀 AgileFlow v{{VERSION}} loaded - Type /agileflow:help for commands'"
10
10
  },
11
11
  {
12
12
  "type": "command",
@@ -6,7 +6,7 @@
6
6
  "hooks": [
7
7
  {
8
8
  "type": "command",
9
- "command": "echo '🚀 AgileFlow v2.19.0 loaded - Use /agileflow:help to see available commands'"
9
+ "command": "echo '🚀 AgileFlow v{{VERSION}} loaded - Use /agileflow:help to see available commands'"
10
10
  }
11
11
  ]
12
12
  },
@@ -10,6 +10,7 @@ const fs = require('fs-extra');
10
10
  const yaml = require('js-yaml');
11
11
  const { Installer } = require('../installers/core/installer');
12
12
  const { displayLogo, displaySection, success, warning, info } = require('../lib/ui');
13
+ const { parseFrontmatter: parseYamlFrontmatter } = require('../../../scripts/lib/frontmatter-parser');
13
14
 
14
15
  const installer = new Installer();
15
16
 
@@ -255,23 +256,17 @@ async function listExperts(agileflowPath) {
255
256
  }
256
257
 
257
258
  /**
258
- * Parse YAML frontmatter from markdown
259
+ * Parse YAML frontmatter from markdown (wrapper for shared parser)
259
260
  * @param {string} content - File content
260
261
  * @returns {{ frontmatter: Object|null, content: string }}
261
262
  */
262
263
  function parseFrontmatter(content) {
263
- const match = content.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
264
-
265
- if (!match) {
266
- return { frontmatter: null, content };
267
- }
268
-
269
- try {
270
- const frontmatter = yaml.load(match[1]);
271
- return { frontmatter, content: match[2] };
272
- } catch {
273
- return { frontmatter: null, content };
274
- }
264
+ const frontmatter = parseYamlFrontmatter(content);
265
+ const body = content.replace(/^---\n[\s\S]*?\n---\n?/, '');
266
+ return {
267
+ frontmatter: Object.keys(frontmatter).length > 0 ? frontmatter : null,
268
+ content: body,
269
+ };
275
270
  }
276
271
 
277
272
  /**
@@ -19,6 +19,7 @@ module.exports = {
19
19
  description: 'Remove AgileFlow from a project',
20
20
  options: [
21
21
  ['-d, --directory <path>', 'Project directory (default: current directory)'],
22
+ ['--ide <name>', 'Remove only a specific IDE (e.g., windsurf, cursor)'],
22
23
  ['--force', 'Skip confirmation prompt'],
23
24
  ],
24
25
  action: async options => {
@@ -35,6 +36,68 @@ module.exports = {
35
36
  process.exit(0);
36
37
  }
37
38
 
39
+ // Check if removing just one IDE
40
+ if (options.ide) {
41
+ const ideName = options.ide.toLowerCase();
42
+ displaySection('Removing IDE Configuration', `IDE: ${formatIdeName(ideName)}`);
43
+
44
+ if (!status.ides || !status.ides.includes(ideName)) {
45
+ warning(`${formatIdeName(ideName)} is not configured in this installation`);
46
+ console.log(chalk.dim(`Configured IDEs: ${(status.ides || []).join(', ') || 'none'}\n`));
47
+ process.exit(0);
48
+ }
49
+
50
+ // Confirm removal
51
+ if (!options.force) {
52
+ const proceed = await confirm(
53
+ `Remove ${formatIdeName(ideName)} configuration?`,
54
+ false
55
+ );
56
+ if (!proceed) {
57
+ console.log(chalk.dim('\nCancelled\n'));
58
+ process.exit(0);
59
+ }
60
+ }
61
+
62
+ console.log();
63
+
64
+ // Remove the IDE configuration
65
+ const configPath = getIdeConfigPath(directory, ideName);
66
+ if (await fs.pathExists(configPath)) {
67
+ await fs.remove(configPath);
68
+ success(`Removed ${formatIdeName(ideName)} configuration`);
69
+ }
70
+
71
+ // Also remove spawnable agents for claude-code
72
+ if (ideName === 'claude-code') {
73
+ const agentsPath = path.join(directory, '.claude', 'agents', 'agileflow');
74
+ if (await fs.pathExists(agentsPath)) {
75
+ await fs.remove(agentsPath);
76
+ success('Removed spawnable agents');
77
+ }
78
+ }
79
+
80
+ // Update the manifest to remove this IDE
81
+ const manifestPath = path.join(status.path, '_cfg', 'manifest.yaml');
82
+ if (await fs.pathExists(manifestPath)) {
83
+ const yaml = require('js-yaml');
84
+ const manifestContent = await fs.readFile(manifestPath, 'utf8');
85
+ const manifest = yaml.load(manifestContent);
86
+ manifest.ides = (manifest.ides || []).filter(ide => ide !== ideName);
87
+ manifest.updated_at = new Date().toISOString();
88
+ await fs.writeFile(manifestPath, yaml.dump(manifest), 'utf8');
89
+ success('Updated manifest');
90
+ }
91
+
92
+ console.log(chalk.green(`\n${formatIdeName(ideName)} has been removed.\n`));
93
+ if (status.ides.length > 1) {
94
+ console.log(chalk.dim(`Remaining IDEs: ${status.ides.filter(i => i !== ideName).join(', ')}\n`));
95
+ }
96
+
97
+ process.exit(0);
98
+ }
99
+
100
+ // Full uninstall
38
101
  displaySection('Uninstalling AgileFlow', `Location: ${status.path}`);
39
102
 
40
103
  // Confirm uninstall
@@ -56,6 +119,13 @@ module.exports = {
56
119
  await fs.remove(configPath);
57
120
  success(`Removed ${formatIdeName(ide)} configuration`);
58
121
  }
122
+ // Also remove spawnable agents for claude-code
123
+ if (ide === 'claude-code') {
124
+ const agentsPath = path.join(directory, '.claude', 'agents', 'agileflow');
125
+ if (await fs.pathExists(agentsPath)) {
126
+ await fs.remove(agentsPath);
127
+ }
128
+ }
59
129
  }
60
130
  }
61
131
 
@@ -32,6 +32,7 @@ module.exports = {
32
32
  options: [
33
33
  ['-d, --directory <path>', 'Project directory (default: current directory)'],
34
34
  ['--force', 'Force reinstall (skip prompts; overwrites local changes)'],
35
+ ['--ides <list>', 'Comma-separated list of IDEs to update (overrides manifest)'],
35
36
  ['--no-self-update', 'Skip automatic CLI self-update check'],
36
37
  ['--self-updated', 'Internal flag: indicates CLI was already self-updated'],
37
38
  ],
@@ -56,6 +57,7 @@ module.exports = {
56
57
  const args = ['agileflow@latest', 'update', '--self-updated'];
57
58
  if (options.directory) args.push('-d', options.directory);
58
59
  if (options.force) args.push('--force');
60
+ if (options.ides) args.push('--ides', options.ides);
59
61
 
60
62
  const result = spawnSync('npx', args, {
61
63
  stdio: 'inherit',
@@ -88,10 +90,10 @@ module.exports = {
88
90
 
89
91
  const latestVersion = npmLatestVersion || localCliVersion;
90
92
 
91
- console.log(chalk.bold('Installed: '), status.version);
92
- console.log(chalk.bold('CLI version: '), localCliVersion);
93
+ console.log(chalk.bold('Currently installed:'), status.version);
94
+ console.log(chalk.bold('CLI version: '), localCliVersion);
93
95
  if (npmLatestVersion) {
94
- console.log(chalk.bold('Latest (npm):'), npmLatestVersion);
96
+ console.log(chalk.bold('Latest (npm): '), npmLatestVersion);
95
97
  }
96
98
 
97
99
  // If we self-updated, show confirmation
@@ -136,10 +138,25 @@ module.exports = {
136
138
  // Get docs folder name from metadata (or default to 'docs')
137
139
  const docsFolder = await getDocsFolderName(directory);
138
140
 
141
+ // Determine which IDEs to update
142
+ let idesToUpdate;
143
+ if (options.ides) {
144
+ // User explicitly specified IDEs via --ides flag
145
+ idesToUpdate = options.ides.split(',').map(ide => ide.trim().toLowerCase());
146
+ console.log(chalk.dim(`Updating specified IDEs: ${idesToUpdate.join(', ')}`));
147
+ } else {
148
+ // Use IDEs from manifest
149
+ idesToUpdate = status.ides || ['claude-code'];
150
+ if (idesToUpdate.length > 1) {
151
+ console.log(chalk.dim(`IDEs to update (from manifest): ${idesToUpdate.join(', ')}`));
152
+ console.log(chalk.dim(` Tip: Use --ides=claude-code to update only specific IDEs\n`));
153
+ }
154
+ }
155
+
139
156
  // Re-run installation with existing config from manifest
140
157
  const config = {
141
158
  directory,
142
- ides: status.ides || ['claude-code'],
159
+ ides: idesToUpdate,
143
160
  userName: status.userName || 'Developer',
144
161
  agileflowFolder: status.agileflowFolder || path.basename(status.path),
145
162
  docsFolder: status.docsFolder || docsFolder,
@@ -5,26 +5,15 @@
5
5
  */
6
6
 
7
7
  const path = require('node:path');
8
- const crypto = require('node:crypto');
9
8
  const fs = require('fs-extra');
10
9
  const chalk = require('chalk');
11
10
  const ora = require('ora');
12
11
  const yaml = require('js-yaml');
12
+ const { injectContent } = require('../../lib/content-injector');
13
+ const { sha256Hex, toPosixPath, safeTimestampForPath } = require('../../lib/utils');
13
14
 
14
15
  const TEXT_EXTENSIONS = new Set(['.md', '.yaml', '.yml', '.txt', '.json']);
15
16
 
16
- function sha256Hex(data) {
17
- return crypto.createHash('sha256').update(data).digest('hex');
18
- }
19
-
20
- function toPosixPath(filePath) {
21
- return filePath.split(path.sep).join('/');
22
- }
23
-
24
- function safeTimestampForPath(date = new Date()) {
25
- return date.toISOString().replace(/[:.]/g, '-');
26
- }
27
-
28
17
  /**
29
18
  * Get the source path for AgileFlow content
30
19
  * @returns {string} Path to src directory
@@ -48,6 +37,11 @@ class Installer {
48
37
  constructor() {
49
38
  this.sourcePath = getSourcePath();
50
39
  this.packageRoot = getPackageRoot();
40
+ this.coreDir = path.join(this.sourcePath, 'core');
41
+
42
+ // Load version from package.json
43
+ const packageJson = require(path.join(this.packageRoot, 'package.json'));
44
+ this.version = packageJson.version;
51
45
  }
52
46
 
53
47
  /**
@@ -260,7 +254,7 @@ class Installer {
260
254
  }
261
255
 
262
256
  /**
263
- * Copy a file with placeholder replacements
257
+ * Copy a file with placeholder replacements using content injector
264
258
  * @param {string} source - Source file path
265
259
  * @param {string} dest - Destination file path
266
260
  * @param {string} agileflowFolder - AgileFlow folder name
@@ -271,9 +265,12 @@ class Installer {
271
265
  if (TEXT_EXTENSIONS.has(ext)) {
272
266
  let content = await fs.readFile(source, 'utf8');
273
267
 
274
- // Replace placeholders
275
- content = content.replace(/\{agileflow_folder\}/g, agileflowFolder);
276
- content = content.replace(/\{project-root\}/g, '{project-root}'); // Keep as-is for runtime
268
+ // Use content injector for all placeholder replacements
269
+ content = injectContent(content, {
270
+ coreDir: this.coreDir,
271
+ agileflowFolder,
272
+ version: this.version,
273
+ });
277
274
 
278
275
  await fs.writeFile(dest, content, 'utf8');
279
276
  } else {
@@ -304,8 +301,12 @@ class Installer {
304
301
  let newContent;
305
302
  if (isText) {
306
303
  let content = await fs.readFile(source, 'utf8');
307
- content = content.replace(/\{agileflow_folder\}/g, agileflowFolder);
308
- content = content.replace(/\{project-root\}/g, '{project-root}');
304
+ // Use content injector for all placeholder replacements
305
+ content = injectContent(content, {
306
+ coreDir: this.coreDir,
307
+ agileflowFolder,
308
+ version: this.version,
309
+ });
309
310
  newContent = content;
310
311
  } else {
311
312
  newContent = await fs.readFile(source);
@@ -68,11 +68,25 @@ class BaseIdeSetup {
68
68
  injectDynamicContent(content, agileflowDir) {
69
69
  const { injectContent } = require('../../lib/content-injector');
70
70
  // agileflowDir is the user's .agileflow installation directory
71
- // which has agents/ and commands/ at the root level (not src/core/)
72
- const agentsDir = path.join(agileflowDir, 'agents');
73
- const commandsDir = path.join(agileflowDir, 'commands');
71
+ // which has agents/, commands/, skills/ at the root level
72
+ return injectContent(content, {
73
+ coreDir: agileflowDir,
74
+ agileflowFolder: this.agileflowFolder,
75
+ version: this.getVersion(),
76
+ });
77
+ }
74
78
 
75
- return injectContent(content, agentsDir, commandsDir);
79
+ /**
80
+ * Get the current AgileFlow version
81
+ * @returns {string} Version string
82
+ */
83
+ getVersion() {
84
+ try {
85
+ const packageJson = require('../../../../package.json');
86
+ return packageJson.version || 'unknown';
87
+ } catch {
88
+ return 'unknown';
89
+ }
76
90
  }
77
91
 
78
92
  /**