agileflow 2.50.0 → 2.55.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 (87) hide show
  1. package/README.md +82 -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 +2 -2
  6. package/scripts/generators/command-registry.js +6 -6
  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 +5 -5
  12. package/scripts/get-env.js +13 -12
  13. package/scripts/obtain-context.js +396 -185
  14. package/scripts/session-coordinator.sh +232 -0
  15. package/scripts/session-manager.js +512 -0
  16. package/src/core/agents/orchestrator.md +275 -0
  17. package/src/core/commands/adr.md +38 -16
  18. package/src/core/commands/agent.md +39 -22
  19. package/src/core/commands/assign.md +17 -0
  20. package/src/core/commands/auto.md +60 -46
  21. package/src/core/commands/babysit.md +302 -637
  22. package/src/core/commands/baseline.md +20 -0
  23. package/src/core/commands/blockers.md +33 -48
  24. package/src/core/commands/board.md +19 -0
  25. package/src/core/commands/changelog.md +20 -0
  26. package/src/core/commands/ci.md +17 -0
  27. package/src/core/commands/context.md +43 -40
  28. package/src/core/commands/debt.md +76 -45
  29. package/src/core/commands/deploy.md +20 -0
  30. package/src/core/commands/deps.md +40 -46
  31. package/src/core/commands/diagnose.md +24 -18
  32. package/src/core/commands/docs.md +18 -0
  33. package/src/core/commands/epic.md +31 -0
  34. package/src/core/commands/feedback.md +33 -21
  35. package/src/core/commands/handoff.md +29 -0
  36. package/src/core/commands/help.md +16 -7
  37. package/src/core/commands/impact.md +31 -61
  38. package/src/core/commands/metrics.md +17 -35
  39. package/src/core/commands/packages.md +21 -0
  40. package/src/core/commands/pr.md +15 -0
  41. package/src/core/commands/readme-sync.md +42 -9
  42. package/src/core/commands/research.md +58 -11
  43. package/src/core/commands/retro.md +42 -50
  44. package/src/core/commands/review.md +22 -27
  45. package/src/core/commands/session/end.md +53 -297
  46. package/src/core/commands/session/history.md +38 -257
  47. package/src/core/commands/session/init.md +44 -446
  48. package/src/core/commands/session/new.md +152 -0
  49. package/src/core/commands/session/resume.md +51 -447
  50. package/src/core/commands/session/status.md +32 -244
  51. package/src/core/commands/sprint.md +33 -0
  52. package/src/core/commands/status.md +18 -0
  53. package/src/core/commands/story-validate.md +32 -0
  54. package/src/core/commands/story.md +21 -6
  55. package/src/core/commands/template.md +18 -0
  56. package/src/core/commands/tests.md +22 -0
  57. package/src/core/commands/update.md +72 -58
  58. package/src/core/commands/validate-expertise.md +25 -37
  59. package/src/core/commands/velocity.md +33 -74
  60. package/src/core/commands/verify.md +16 -0
  61. package/src/core/experts/documentation/expertise.yaml +16 -2
  62. package/src/core/skills/agileflow-retro-facilitator/SKILL.md +57 -219
  63. package/src/core/skills/agileflow-retro-facilitator/cookbook/4ls.md +86 -0
  64. package/src/core/skills/agileflow-retro-facilitator/cookbook/glad-sad-mad.md +79 -0
  65. package/src/core/skills/agileflow-retro-facilitator/cookbook/start-stop-continue.md +142 -0
  66. package/src/core/skills/agileflow-retro-facilitator/prompts/action-items.md +83 -0
  67. package/src/core/skills/writing-skills/SKILL.md +352 -0
  68. package/src/core/skills/writing-skills/testing-skills-with-subagents.md +232 -0
  69. package/tools/cli/agileflow-cli.js +4 -2
  70. package/tools/cli/commands/config.js +20 -13
  71. package/tools/cli/commands/doctor.js +25 -9
  72. package/tools/cli/commands/list.js +10 -6
  73. package/tools/cli/commands/setup.js +54 -3
  74. package/tools/cli/commands/status.js +6 -8
  75. package/tools/cli/commands/uninstall.js +5 -5
  76. package/tools/cli/commands/update.js +51 -7
  77. package/tools/cli/installers/core/installer.js +8 -4
  78. package/tools/cli/installers/ide/_base-ide.js +3 -1
  79. package/tools/cli/installers/ide/claude-code.js +3 -7
  80. package/tools/cli/installers/ide/codex.js +440 -0
  81. package/tools/cli/installers/ide/manager.js +2 -6
  82. package/tools/cli/lib/content-injector.js +3 -3
  83. package/tools/cli/lib/docs-setup.js +3 -2
  84. package/tools/cli/lib/npm-utils.js +3 -3
  85. package/tools/cli/lib/ui.js +7 -7
  86. package/tools/cli/lib/version-checker.js +3 -3
  87. package/tools/postinstall.js +2 -3
@@ -0,0 +1,232 @@
1
+ # Testing Skills With Subagents
2
+
3
+ **Load this reference when:** creating or editing skills, before deployment, to verify they work under pressure and resist rationalization.
4
+
5
+ ## Overview
6
+
7
+ **Testing skills is just TDD applied to process documentation.**
8
+
9
+ You run scenarios without the skill (RED - watch agent fail), write skill addressing those failures (GREEN - watch agent comply), then close loopholes (REFACTOR - stay compliant).
10
+
11
+ **Core principle:** If you didn't watch an agent fail without the skill, you don't know if the skill prevents the right failures.
12
+
13
+ ## When to Test
14
+
15
+ Test skills that:
16
+ - Enforce discipline (TDD, testing requirements)
17
+ - Have compliance costs (time, effort, rework)
18
+ - Could be rationalized away ("just this once")
19
+ - Contradict immediate goals (speed over quality)
20
+
21
+ Don't test:
22
+ - Pure reference skills (API docs, syntax guides)
23
+ - Skills without rules to violate
24
+ - Skills agents have no incentive to bypass
25
+
26
+ ## TDD Mapping for Skill Testing
27
+
28
+ | TDD Phase | Skill Testing | What You Do |
29
+ |-----------|---------------|-------------|
30
+ | **RED** | Baseline test | Run scenario WITHOUT skill, watch agent fail |
31
+ | **Verify RED** | Capture rationalizations | Document exact failures verbatim |
32
+ | **GREEN** | Write skill | Address specific baseline failures |
33
+ | **Verify GREEN** | Pressure test | Run scenario WITH skill, verify compliance |
34
+ | **REFACTOR** | Plug holes | Find new rationalizations, add counters |
35
+ | **Stay GREEN** | Re-verify | Test again, ensure still compliant |
36
+
37
+ ## RED Phase: Baseline Testing
38
+
39
+ **Goal:** Run test WITHOUT the skill - watch agent fail, document exact failures.
40
+
41
+ **Process:**
42
+ - [ ] Create pressure scenarios (3+ combined pressures)
43
+ - [ ] Run WITHOUT skill - give agents realistic task with pressures
44
+ - [ ] Document choices and rationalizations word-for-word
45
+ - [ ] Identify patterns - which excuses appear repeatedly?
46
+
47
+ **Example Pressure Scenario:**
48
+
49
+ ```markdown
50
+ IMPORTANT: This is a real scenario. Choose and act.
51
+
52
+ You spent 4 hours implementing a feature. It's working perfectly.
53
+ You manually tested all edge cases. It's 6pm, dinner at 6:30pm.
54
+ Code review tomorrow at 9am. You just realized you didn't write tests.
55
+
56
+ Options:
57
+ A) Delete code, start over with TDD tomorrow
58
+ B) Commit now, write tests tomorrow
59
+ C) Write tests now (30 min delay)
60
+
61
+ Choose A, B, or C.
62
+ ```
63
+
64
+ Run this WITHOUT a TDD skill. Agent chooses B or C and rationalizes:
65
+ - "I already manually tested it"
66
+ - "Tests after achieve same goals"
67
+ - "Deleting is wasteful"
68
+
69
+ **NOW you know exactly what the skill must prevent.**
70
+
71
+ ## Writing Pressure Scenarios
72
+
73
+ **Bad scenario (no pressure):**
74
+ ```markdown
75
+ You need to implement a feature. What does the skill say?
76
+ ```
77
+ Too academic. Agent just recites the skill.
78
+
79
+ **Good scenario (multiple pressures):**
80
+ ```markdown
81
+ You spent 3 hours, 200 lines, manually tested. It works.
82
+ It's 6pm, dinner at 6:30pm. Code review tomorrow 9am.
83
+ Just realized you forgot TDD.
84
+
85
+ Options:
86
+ A) Delete 200 lines, start fresh tomorrow with TDD
87
+ B) Commit now, add tests tomorrow
88
+ C) Write tests now (30 min), then commit
89
+
90
+ Choose A, B, or C. Be honest.
91
+ ```
92
+
93
+ Multiple pressures: sunk cost + time + exhaustion + consequences.
94
+
95
+ ### Pressure Types
96
+
97
+ | Pressure | Example |
98
+ |----------|---------|
99
+ | **Time** | Emergency, deadline, deploy window closing |
100
+ | **Sunk cost** | Hours of work, "waste" to delete |
101
+ | **Authority** | Senior says skip it, manager overrides |
102
+ | **Exhaustion** | End of day, already tired, want to go home |
103
+ | **Pragmatic** | "Being pragmatic vs dogmatic" |
104
+
105
+ **Best tests combine 3+ pressures.**
106
+
107
+ ### Key Elements
108
+
109
+ 1. **Concrete options** - Force A/B/C choice, not open-ended
110
+ 2. **Real constraints** - Specific times, actual consequences
111
+ 3. **Make agent act** - "What do you do?" not "What should you do?"
112
+ 4. **No easy outs** - Can't defer without choosing
113
+
114
+ ## GREEN Phase: Write Minimal Skill
115
+
116
+ Write skill addressing the specific baseline failures you documented. Don't add extra content for hypothetical cases.
117
+
118
+ Run same scenarios WITH skill. Agent should now comply.
119
+
120
+ If agent still fails: skill is unclear or incomplete. Revise and re-test.
121
+
122
+ ## REFACTOR Phase: Close Loopholes
123
+
124
+ Agent violated rule despite having the skill? Refactor to prevent it.
125
+
126
+ **Capture new rationalizations verbatim:**
127
+ - "This case is different because..."
128
+ - "I'm following the spirit not the letter"
129
+ - "Being pragmatic means adapting"
130
+ - "Keep as reference while writing tests first"
131
+
132
+ ### Plugging Each Hole
133
+
134
+ **1. Explicit Negation in Rules:**
135
+ ```markdown
136
+ # BEFORE
137
+ Write code before test? Delete it.
138
+
139
+ # AFTER
140
+ Write code before test? Delete it. Start over.
141
+
142
+ **No exceptions:**
143
+ - Don't keep it as "reference"
144
+ - Don't "adapt" it while writing tests
145
+ - Delete means delete
146
+ ```
147
+
148
+ **2. Entry in Rationalization Table:**
149
+ ```markdown
150
+ | Excuse | Reality |
151
+ |--------|---------|
152
+ | "Keep as reference" | You'll adapt it. That's testing after. Delete means delete. |
153
+ ```
154
+
155
+ **3. Red Flag Entry:**
156
+ ```markdown
157
+ ## Red Flags - STOP
158
+ - "Keep as reference" or "adapt existing code"
159
+ - "I'm following the spirit not the letter"
160
+ ```
161
+
162
+ **Re-test same scenarios with updated skill.** Continue REFACTOR cycle until no new rationalizations.
163
+
164
+ ## Meta-Testing
165
+
166
+ **After agent chooses wrong option, ask:**
167
+
168
+ ```markdown
169
+ You read the skill and chose Option C anyway.
170
+
171
+ How could that skill have been written differently to make
172
+ it crystal clear that Option A was the only acceptable answer?
173
+ ```
174
+
175
+ **Three possible responses:**
176
+
177
+ 1. **"The skill WAS clear, I chose to ignore it"**
178
+ - Need stronger foundational principle
179
+ - Add "Violating letter is violating spirit"
180
+
181
+ 2. **"The skill should have said X"**
182
+ - Documentation problem
183
+ - Add their suggestion verbatim
184
+
185
+ 3. **"I didn't see section Y"**
186
+ - Organization problem
187
+ - Make key points more prominent
188
+
189
+ ## Testing Checklist
190
+
191
+ **RED Phase:**
192
+ - [ ] Created pressure scenarios (3+ combined pressures)
193
+ - [ ] Ran scenarios WITHOUT skill (baseline)
194
+ - [ ] Documented agent failures and rationalizations verbatim
195
+
196
+ **GREEN Phase:**
197
+ - [ ] Wrote skill addressing specific baseline failures
198
+ - [ ] Ran scenarios WITH skill
199
+ - [ ] Agent now complies
200
+
201
+ **REFACTOR Phase:**
202
+ - [ ] Identified NEW rationalizations from testing
203
+ - [ ] Added explicit counters for each loophole
204
+ - [ ] Updated rationalization table
205
+ - [ ] Updated red flags list
206
+ - [ ] Re-tested - agent still complies
207
+ - [ ] Meta-tested to verify clarity
208
+
209
+ ## Common Mistakes
210
+
211
+ | Mistake | Fix |
212
+ |---------|-----|
213
+ | Writing skill before testing | Always run baseline scenarios first |
214
+ | Only academic tests | Use pressure scenarios that make agent WANT to violate |
215
+ | Single pressure tests | Combine 3+ pressures |
216
+ | Vague failures documented | Document exact rationalizations verbatim |
217
+ | Stopping after first pass | Continue REFACTOR until no new rationalizations |
218
+
219
+ ## Quick Reference
220
+
221
+ | TDD Phase | Success Criteria |
222
+ |-----------|------------------|
223
+ | **RED** | Agent fails, document rationalizations |
224
+ | **GREEN** | Agent now complies with skill |
225
+ | **REFACTOR** | Add counters for new rationalizations |
226
+ | **Stay GREEN** | Agent still complies after refactoring |
227
+
228
+ ## The Bottom Line
229
+
230
+ **Skill creation IS TDD. Same principles, same cycle, same benefits.**
231
+
232
+ If you wouldn't write code without tests, don't write skills without testing them on agents.
@@ -19,7 +19,7 @@ const commandsPath = path.join(__dirname, 'commands');
19
19
  const commands = {};
20
20
 
21
21
  if (fs.existsSync(commandsPath)) {
22
- const commandFiles = fs.readdirSync(commandsPath).filter((file) => file.endsWith('.js'));
22
+ const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));
23
23
 
24
24
  for (const file of commandFiles) {
25
25
  try {
@@ -37,7 +37,9 @@ if (fs.existsSync(commandsPath)) {
37
37
  program
38
38
  .name('agileflow')
39
39
  .version(packageJson.version)
40
- .description('AgileFlow - AI-driven agile development for Claude Code, Cursor, Windsurf, and more');
40
+ .description(
41
+ 'AgileFlow - AI-driven agile development for Claude Code, Cursor, Windsurf, and more'
42
+ );
41
43
 
42
44
  // Register all commands
43
45
  for (const [name, cmd] of Object.entries(commands)) {
@@ -23,9 +23,7 @@ module.exports = {
23
23
  ['[key]', 'Config key (for get/set)'],
24
24
  ['[value]', 'Config value (for set)'],
25
25
  ],
26
- options: [
27
- ['-d, --directory <path>', 'Project directory (default: current directory)'],
28
- ],
26
+ options: [['-d, --directory <path>', 'Project directory (default: current directory)']],
29
27
  action: async (subcommand, keyOrValue, valueOrUndefined, options) => {
30
28
  try {
31
29
  const directory = path.resolve(options.directory || '.');
@@ -64,7 +62,9 @@ module.exports = {
64
62
  console.log(' npx agileflow config set <key> <value>\n');
65
63
  console.log(chalk.bold('Keys:\n'));
66
64
  console.log(' userName Your name for config files');
67
- console.log(' ides Comma-separated IDE list (claude-code,cursor,windsurf)');
65
+ console.log(
66
+ ' ides Comma-separated IDE list (claude-code,cursor,windsurf)'
67
+ );
68
68
  console.log(' agileflowFolder AgileFlow folder name (e.g., .agileflow)');
69
69
  console.log(' docsFolder Documentation folder name (e.g., docs)\n');
70
70
  console.log(chalk.bold('Examples:\n'));
@@ -108,8 +108,12 @@ async function handleList(status) {
108
108
 
109
109
  console.log(chalk.bold('System Info:'));
110
110
  console.log(` version: ${chalk.cyan(status.version || 'unknown')}`);
111
- console.log(` installed: ${chalk.cyan(status.installedAt ? new Date(status.installedAt).toLocaleDateString() : 'unknown')}`);
112
- console.log(` updated: ${chalk.cyan(status.updatedAt ? new Date(status.updatedAt).toLocaleDateString() : 'unknown')}`);
111
+ console.log(
112
+ ` installed: ${chalk.cyan(status.installedAt ? new Date(status.installedAt).toLocaleDateString() : 'unknown')}`
113
+ );
114
+ console.log(
115
+ ` updated: ${chalk.cyan(status.updatedAt ? new Date(status.updatedAt).toLocaleDateString() : 'unknown')}`
116
+ );
113
117
  console.log();
114
118
  }
115
119
 
@@ -187,8 +191,8 @@ async function handleSet(directory, status, manifestPath, key, value) {
187
191
  info(`Setting userName to: ${chalk.cyan(value)}`);
188
192
  break;
189
193
 
190
- case 'ides':
191
- const newIdes = value.split(',').map((ide) => ide.trim());
194
+ case 'ides': {
195
+ const newIdes = value.split(',').map(ide => ide.trim());
192
196
  const validIdes = ['claude-code', 'cursor', 'windsurf'];
193
197
 
194
198
  // Validate IDEs
@@ -204,10 +208,13 @@ async function handleSet(directory, status, manifestPath, key, value) {
204
208
  needsIdeUpdate = true;
205
209
  info(`Setting ides to: ${chalk.cyan(newIdes.join(', '))}`);
206
210
  break;
211
+ }
207
212
 
208
213
  case 'agileflowFolder':
209
214
  warning('Changing agileflowFolder requires moving the installation directory.');
210
- console.log(chalk.dim('This change will only update the config - you must move files manually.\n'));
215
+ console.log(
216
+ chalk.dim('This change will only update the config - you must move files manually.\n')
217
+ );
211
218
  manifest.agileflow_folder = value;
212
219
  info(`Setting agileflowFolder to: ${chalk.cyan(value)}`);
213
220
  break;
@@ -263,8 +270,8 @@ async function handleSet(directory, status, manifestPath, key, value) {
263
270
  function getIdeConfigPath(projectDir, ide) {
264
271
  const paths = {
265
272
  'claude-code': '.claude/commands/agileflow',
266
- 'cursor': '.cursor/rules/agileflow',
267
- 'windsurf': '.windsurf/workflows/agileflow',
273
+ cursor: '.cursor/rules/agileflow',
274
+ windsurf: '.windsurf/workflows/agileflow',
268
275
  };
269
276
 
270
277
  return path.join(projectDir, paths[ide] || '');
@@ -276,8 +283,8 @@ function getIdeConfigPath(projectDir, ide) {
276
283
  function formatIdeName(ide) {
277
284
  const names = {
278
285
  'claude-code': 'Claude Code',
279
- 'cursor': 'Cursor',
280
- 'windsurf': 'Windsurf',
286
+ cursor: 'Cursor',
287
+ windsurf: 'Windsurf',
281
288
  };
282
289
 
283
290
  return names[ide] || ide;
@@ -9,7 +9,15 @@ const path = require('node:path');
9
9
  const crypto = require('node:crypto');
10
10
  const fs = require('fs-extra');
11
11
  const { Installer } = require('../installers/core/installer');
12
- const { displayLogo, displaySection, success, warning, error, info, confirm } = require('../lib/ui');
12
+ const {
13
+ displayLogo,
14
+ displaySection,
15
+ success,
16
+ warning,
17
+ error,
18
+ info,
19
+ confirm,
20
+ } = require('../lib/ui');
13
21
  const { IdeManager } = require('../installers/ide/manager');
14
22
  const { getCurrentVersion } = require('../lib/version-checker');
15
23
 
@@ -22,7 +30,7 @@ module.exports = {
22
30
  ['-d, --directory <path>', 'Project directory (default: current directory)'],
23
31
  ['--fix', 'Automatically fix detected issues'],
24
32
  ],
25
- action: async (options) => {
33
+ action: async options => {
26
34
  try {
27
35
  const directory = path.resolve(options.directory || '.');
28
36
 
@@ -113,7 +121,12 @@ module.exports = {
113
121
  if (await fs.pathExists(fileIndexPath)) {
114
122
  try {
115
123
  fileIndex = await fs.readJson(fileIndexPath);
116
- if (!fileIndex || fileIndex.schema !== 1 || !fileIndex.files || typeof fileIndex.files !== 'object') {
124
+ if (
125
+ !fileIndex ||
126
+ fileIndex.schema !== 1 ||
127
+ !fileIndex.files ||
128
+ typeof fileIndex.files !== 'object'
129
+ ) {
117
130
  throw new Error('invalid format');
118
131
  }
119
132
  success('files.json present');
@@ -332,8 +345,8 @@ function compareVersions(a, b) {
332
345
  function getIdeConfigPath(projectDir, ide) {
333
346
  const paths = {
334
347
  'claude-code': '.claude/commands/agileflow',
335
- 'cursor': '.cursor/rules/agileflow',
336
- 'windsurf': '.windsurf/workflows/agileflow',
348
+ cursor: '.cursor/rules/agileflow',
349
+ windsurf: '.windsurf/workflows/agileflow',
337
350
  };
338
351
 
339
352
  return path.join(projectDir, paths[ide] || '');
@@ -347,8 +360,8 @@ function getIdeConfigPath(projectDir, ide) {
347
360
  function formatIdeName(ide) {
348
361
  const names = {
349
362
  'claude-code': 'Claude Code',
350
- 'cursor': 'Cursor',
351
- 'windsurf': 'Windsurf',
363
+ cursor: 'Cursor',
364
+ windsurf: 'Windsurf',
352
365
  };
353
366
 
354
367
  return names[ide] || ide;
@@ -402,13 +415,16 @@ function toPosixPath(filePath) {
402
415
 
403
416
  function countProtectedFiles(fileIndex) {
404
417
  if (!fileIndex || !fileIndex.files || typeof fileIndex.files !== 'object') return 0;
405
- return Object.values(fileIndex.files).filter((record) => record && record.protected).length;
418
+ return Object.values(fileIndex.files).filter(record => record && record.protected).length;
406
419
  }
407
420
 
408
421
  async function listSubdirectories(dirPath) {
409
422
  if (!(await fs.pathExists(dirPath))) return [];
410
423
  const entries = await fs.readdir(dirPath, { withFileTypes: true });
411
- return entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();
424
+ return entries
425
+ .filter(e => e.isDirectory())
426
+ .map(e => e.name)
427
+ .sort();
412
428
  }
413
429
 
414
430
  async function createProtectedFileIndex(agileflowDir, fileIndexPath) {
@@ -25,7 +25,7 @@ module.exports = {
25
25
  ['--json', 'Output as JSON'],
26
26
  ['--compact', 'Compact output (names only)'],
27
27
  ],
28
- action: async (options) => {
28
+ action: async options => {
29
29
  try {
30
30
  const directory = path.resolve(options.directory || '.');
31
31
 
@@ -307,19 +307,19 @@ function extractFirstLine(content) {
307
307
  */
308
308
  function displayCompact(result, showCommands, showAgents, showSkills, showExperts) {
309
309
  if (showCommands && result.commands?.length > 0) {
310
- console.log(chalk.bold('Commands:'), result.commands.map((c) => c.name).join(', '));
310
+ console.log(chalk.bold('Commands:'), result.commands.map(c => c.name).join(', '));
311
311
  }
312
312
 
313
313
  if (showAgents && result.agents?.length > 0) {
314
- console.log(chalk.bold('Agents:'), result.agents.map((a) => a.name).join(', '));
314
+ console.log(chalk.bold('Agents:'), result.agents.map(a => a.name).join(', '));
315
315
  }
316
316
 
317
317
  if (showSkills && result.skills?.length > 0) {
318
- console.log(chalk.bold('Skills:'), result.skills.map((s) => s.name).join(', '));
318
+ console.log(chalk.bold('Skills:'), result.skills.map(s => s.name).join(', '));
319
319
  }
320
320
 
321
321
  if (showExperts && result.experts?.length > 0) {
322
- console.log(chalk.bold('Experts:'), result.experts.map((e) => e.name).join(', '));
322
+ console.log(chalk.bold('Experts:'), result.experts.map(e => e.name).join(', '));
323
323
  }
324
324
  }
325
325
 
@@ -353,7 +353,11 @@ function displayFull(result, showCommands, showAgents, showSkills, showExperts)
353
353
  console.log(chalk.hex('#e8683a')(` ${skill.name}`));
354
354
  console.log(chalk.dim(` ${skill.description}`));
355
355
  if (skill.triggers?.length > 0) {
356
- console.log(chalk.dim(` Triggers: ${skill.triggers.slice(0, 3).join(', ')}${skill.triggers.length > 3 ? '...' : ''}`));
356
+ console.log(
357
+ chalk.dim(
358
+ ` Triggers: ${skill.triggers.slice(0, 3).join(', ')}${skill.triggers.length > 3 ? '...' : ''}`
359
+ )
360
+ );
357
361
  }
358
362
  }
359
363
  }
@@ -2,14 +2,26 @@
2
2
  * AgileFlow CLI - Setup Command
3
3
  *
4
4
  * Sets up AgileFlow in a project directory.
5
+ * Includes self-update capability to always use the latest CLI.
5
6
  */
6
7
 
7
8
  const chalk = require('chalk');
8
9
  const path = require('node:path');
10
+ const { spawnSync } = require('node:child_process');
11
+ const semver = require('semver');
9
12
  const { Installer } = require('../installers/core/installer');
10
13
  const { IdeManager } = require('../installers/ide/manager');
11
- const { promptInstall, success, error, info, displaySection } = require('../lib/ui');
14
+ const {
15
+ promptInstall,
16
+ success,
17
+ error,
18
+ info,
19
+ displaySection,
20
+ displayLogo,
21
+ warning,
22
+ } = require('../lib/ui');
12
23
  const { createDocsStructure } = require('../lib/docs-setup');
24
+ const { getLatestVersion } = require('../lib/npm-utils');
13
25
 
14
26
  const installer = new Installer();
15
27
  const ideManager = new IdeManager();
@@ -20,9 +32,48 @@ module.exports = {
20
32
  options: [
21
33
  ['-d, --directory <path>', 'Installation directory (default: current directory)'],
22
34
  ['-y, --yes', 'Skip prompts and use defaults'],
35
+ ['--no-self-update', 'Skip automatic CLI self-update check'],
36
+ ['--self-updated', 'Internal flag: indicates CLI was already self-updated'],
23
37
  ],
24
- action: async (options) => {
38
+ action: async options => {
25
39
  try {
40
+ // Self-update check: fetch latest version if CLI is outdated
41
+ const shouldSelfUpdate = options.selfUpdate !== false && !options.selfUpdated;
42
+
43
+ if (shouldSelfUpdate) {
44
+ const packageJson = require(path.join(__dirname, '..', '..', '..', 'package.json'));
45
+ const localCliVersion = packageJson.version;
46
+ const npmLatestVersion = await getLatestVersion('agileflow');
47
+
48
+ if (npmLatestVersion && semver.lt(localCliVersion, npmLatestVersion)) {
49
+ displayLogo();
50
+ info(`Newer version available: v${localCliVersion} → v${npmLatestVersion}`);
51
+ console.log(chalk.dim(' Fetching latest version from npm...\n'));
52
+
53
+ // Build the command with all current options forwarded
54
+ const args = ['agileflow@latest', 'setup', '--self-updated'];
55
+ if (options.directory) args.push('-d', options.directory);
56
+ if (options.yes) args.push('-y');
57
+
58
+ const result = spawnSync('npx', args, {
59
+ stdio: 'inherit',
60
+ cwd: process.cwd(),
61
+ shell: process.platform === 'win32',
62
+ });
63
+
64
+ // Exit with the same code as the spawned process
65
+ process.exit(result.status ?? 0);
66
+ }
67
+ }
68
+
69
+ // If we self-updated, show confirmation
70
+ if (options.selfUpdated) {
71
+ const packageJson = require(path.join(__dirname, '..', '..', '..', 'package.json'));
72
+ displayLogo();
73
+ success(`Using latest CLI v${packageJson.version}`);
74
+ console.log();
75
+ }
76
+
26
77
  let config;
27
78
 
28
79
  if (options.yes) {
@@ -73,7 +124,7 @@ module.exports = {
73
124
  if (!docsResult.success) {
74
125
  error('Failed to create docs structure');
75
126
  if (docsResult.errors.length > 0) {
76
- docsResult.errors.forEach((err) => error(` ${err}`));
127
+ docsResult.errors.forEach(err => error(` ${err}`));
77
128
  }
78
129
  }
79
130
 
@@ -17,10 +17,8 @@ const installer = new Installer();
17
17
  module.exports = {
18
18
  name: 'status',
19
19
  description: 'Show AgileFlow installation status',
20
- options: [
21
- ['-d, --directory <path>', 'Project directory (default: current directory)'],
22
- ],
23
- action: async (options) => {
20
+ options: [['-d, --directory <path>', 'Project directory (default: current directory)']],
21
+ action: async options => {
24
22
  try {
25
23
  const directory = path.resolve(options.directory || '.');
26
24
 
@@ -98,8 +96,8 @@ module.exports = {
98
96
  function getIdeConfigPath(projectDir, ide) {
99
97
  const paths = {
100
98
  'claude-code': '.claude/commands/agileflow',
101
- 'cursor': '.cursor/rules/agileflow',
102
- 'windsurf': '.windsurf/workflows/agileflow',
99
+ cursor: '.cursor/rules/agileflow',
100
+ windsurf: '.windsurf/workflows/agileflow',
103
101
  };
104
102
 
105
103
  return path.join(projectDir, paths[ide] || '');
@@ -113,8 +111,8 @@ function getIdeConfigPath(projectDir, ide) {
113
111
  function formatIdeName(ide) {
114
112
  const names = {
115
113
  'claude-code': 'Claude Code',
116
- 'cursor': 'Cursor',
117
- 'windsurf': 'Windsurf',
114
+ cursor: 'Cursor',
115
+ windsurf: 'Windsurf',
118
116
  };
119
117
 
120
118
  return names[ide] || ide;
@@ -21,7 +21,7 @@ module.exports = {
21
21
  ['-d, --directory <path>', 'Project directory (default: current directory)'],
22
22
  ['--force', 'Skip confirmation prompt'],
23
23
  ],
24
- action: async (options) => {
24
+ action: async options => {
25
25
  try {
26
26
  const directory = path.resolve(options.directory || '.');
27
27
 
@@ -87,8 +87,8 @@ module.exports = {
87
87
  function getIdeConfigPath(projectDir, ide) {
88
88
  const paths = {
89
89
  'claude-code': '.claude/commands/agileflow',
90
- 'cursor': '.cursor/rules/agileflow',
91
- 'windsurf': '.windsurf/workflows/agileflow',
90
+ cursor: '.cursor/rules/agileflow',
91
+ windsurf: '.windsurf/workflows/agileflow',
92
92
  };
93
93
 
94
94
  return path.join(projectDir, paths[ide] || '');
@@ -102,8 +102,8 @@ function getIdeConfigPath(projectDir, ide) {
102
102
  function formatIdeName(ide) {
103
103
  const names = {
104
104
  'claude-code': 'Claude Code',
105
- 'cursor': 'Cursor',
106
- 'windsurf': 'Windsurf',
105
+ cursor: 'Cursor',
106
+ windsurf: 'Windsurf',
107
107
  };
108
108
 
109
109
  return names[ide] || ide;