agileflow 2.33.1 → 2.35.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 (63) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +536 -0
  3. package/package.json +1 -1
  4. package/src/core/agents/adr-writer.md +3 -19
  5. package/src/core/agents/api.md +9 -43
  6. package/src/core/agents/ci.md +8 -40
  7. package/src/core/agents/configuration/archival.md +301 -0
  8. package/src/core/agents/configuration/attribution.md +318 -0
  9. package/src/core/agents/configuration/ci.md +1077 -0
  10. package/src/core/agents/configuration/git-config.md +511 -0
  11. package/src/core/agents/configuration/hooks.md +507 -0
  12. package/src/core/agents/configuration/verify.md +540 -0
  13. package/src/core/agents/devops.md +7 -35
  14. package/src/core/agents/documentation.md +0 -1
  15. package/src/core/agents/epic-planner.md +3 -22
  16. package/src/core/agents/mentor.md +8 -24
  17. package/src/core/agents/research.md +0 -7
  18. package/src/core/agents/security.md +0 -5
  19. package/src/core/agents/ui.md +8 -42
  20. package/src/core/commands/PATTERNS-AskUserQuestion.md +474 -0
  21. package/src/core/commands/adr.md +5 -0
  22. package/src/core/commands/agent.md +4 -0
  23. package/src/core/commands/assign.md +1 -0
  24. package/src/core/commands/auto.md +1 -1
  25. package/src/core/commands/babysit.md +147 -31
  26. package/src/core/commands/baseline.md +7 -0
  27. package/src/core/commands/blockers.md +2 -0
  28. package/src/core/commands/board.md +9 -0
  29. package/src/core/commands/configure.md +415 -0
  30. package/src/core/commands/context.md +1 -0
  31. package/src/core/commands/deps.md +2 -0
  32. package/src/core/commands/diagnose.md +0 -41
  33. package/src/core/commands/epic.md +8 -0
  34. package/src/core/commands/handoff.md +4 -0
  35. package/src/core/commands/impact.md +1 -1
  36. package/src/core/commands/metrics.md +10 -0
  37. package/src/core/commands/research.md +3 -0
  38. package/src/core/commands/retro.md +11 -1
  39. package/src/core/commands/sprint.md +2 -1
  40. package/src/core/commands/status.md +1 -0
  41. package/src/core/commands/story-validate.md +1 -1
  42. package/src/core/commands/story.md +29 -2
  43. package/src/core/commands/template.md +8 -0
  44. package/src/core/commands/update.md +1 -1
  45. package/src/core/commands/velocity.md +9 -0
  46. package/src/core/commands/verify.md +6 -0
  47. package/src/core/templates/validate-tokens.sh +0 -15
  48. package/src/core/templates/worktrees-guide.md +0 -4
  49. package/tools/agileflow-npx.js +21 -9
  50. package/tools/cli/commands/config.js +284 -0
  51. package/tools/cli/commands/doctor.js +221 -4
  52. package/tools/cli/commands/setup.js +4 -1
  53. package/tools/cli/commands/update.js +59 -15
  54. package/tools/cli/installers/core/installer.js +369 -37
  55. package/tools/cli/installers/ide/claude-code.js +1 -1
  56. package/tools/cli/installers/ide/cursor.js +1 -1
  57. package/tools/cli/installers/ide/windsurf.js +1 -1
  58. package/tools/cli/lib/docs-setup.js +52 -28
  59. package/tools/cli/lib/npm-utils.js +62 -0
  60. package/tools/cli/lib/ui.js +9 -2
  61. package/tools/postinstall.js +71 -13
  62. package/src/core/agents/context7.md +0 -164
  63. package/src/core/commands/setup.md +0 -708
@@ -0,0 +1,284 @@
1
+ /**
2
+ * AgileFlow CLI - Config Command
3
+ *
4
+ * Manage AgileFlow configuration without re-running setup.
5
+ */
6
+
7
+ const chalk = require('chalk');
8
+ const path = require('node:path');
9
+ const fs = require('fs-extra');
10
+ const yaml = require('js-yaml');
11
+ const { Installer } = require('../installers/core/installer');
12
+ const { IdeManager } = require('../installers/ide/manager');
13
+ const { displayLogo, displaySection, success, warning, error, info } = require('../lib/ui');
14
+
15
+ const installer = new Installer();
16
+ const ideManager = new IdeManager();
17
+
18
+ module.exports = {
19
+ name: 'config',
20
+ description: 'Manage AgileFlow configuration',
21
+ arguments: [
22
+ ['<subcommand>', 'Subcommand: list, get, set'],
23
+ ['[key]', 'Config key (for get/set)'],
24
+ ['[value]', 'Config value (for set)'],
25
+ ],
26
+ options: [
27
+ ['-d, --directory <path>', 'Project directory (default: current directory)'],
28
+ ],
29
+ action: async (subcommand, keyOrValue, valueOrUndefined, options) => {
30
+ try {
31
+ const directory = path.resolve(options.directory || '.');
32
+
33
+ // Get installation status
34
+ const status = await installer.getStatus(directory);
35
+
36
+ if (!status.installed) {
37
+ displayLogo();
38
+ warning('No AgileFlow installation found');
39
+ console.log(chalk.dim(`\nRun 'npx agileflow setup' to set up AgileFlow\n`));
40
+ process.exit(1);
41
+ }
42
+
43
+ const manifestPath = path.join(status.path, '_cfg', 'manifest.yaml');
44
+
45
+ // Handle subcommands
46
+ switch (subcommand) {
47
+ case 'list':
48
+ await handleList(status);
49
+ break;
50
+
51
+ case 'get':
52
+ await handleGet(status, keyOrValue);
53
+ break;
54
+
55
+ case 'set':
56
+ await handleSet(directory, status, manifestPath, keyOrValue, valueOrUndefined);
57
+ break;
58
+
59
+ default:
60
+ displayLogo();
61
+ console.log(chalk.bold('Usage:\n'));
62
+ console.log(' npx agileflow config list');
63
+ console.log(' npx agileflow config get <key>');
64
+ console.log(' npx agileflow config set <key> <value>\n');
65
+ console.log(chalk.bold('Keys:\n'));
66
+ console.log(' userName Your name for config files');
67
+ console.log(' ides Comma-separated IDE list (claude-code,cursor,windsurf)');
68
+ console.log(' agileflowFolder AgileFlow folder name (e.g., .agileflow)');
69
+ console.log(' docsFolder Documentation folder name (e.g., docs)\n');
70
+ console.log(chalk.bold('Examples:\n'));
71
+ console.log(' npx agileflow config get userName');
72
+ console.log(' npx agileflow config set userName "Jane Developer"');
73
+ console.log(' npx agileflow config set ides "claude-code,cursor"\n');
74
+ process.exit(0);
75
+ }
76
+
77
+ process.exit(0);
78
+ } catch (err) {
79
+ console.error(chalk.red('Error:'), err.message);
80
+ if (process.env.DEBUG) {
81
+ console.error(err.stack);
82
+ }
83
+ process.exit(1);
84
+ }
85
+ },
86
+ };
87
+
88
+ /**
89
+ * Handle list subcommand
90
+ */
91
+ async function handleList(status) {
92
+ displayLogo();
93
+ displaySection('AgileFlow Configuration');
94
+
95
+ console.log(chalk.bold('User Settings:'));
96
+ console.log(` userName: ${chalk.cyan(status.userName || 'Developer')}`);
97
+ console.log();
98
+
99
+ console.log(chalk.bold('IDE Settings:'));
100
+ const ides = status.ides || ['claude-code'];
101
+ console.log(` ides: ${chalk.cyan(ides.join(', '))}`);
102
+ console.log();
103
+
104
+ console.log(chalk.bold('Folder Settings:'));
105
+ console.log(` agileflowFolder: ${chalk.cyan(status.agileflowFolder || '.agileflow')}`);
106
+ console.log(` docsFolder: ${chalk.cyan(status.docsFolder || 'docs')}`);
107
+ console.log();
108
+
109
+ console.log(chalk.bold('System Info:'));
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')}`);
113
+ console.log();
114
+ }
115
+
116
+ /**
117
+ * Handle get subcommand
118
+ */
119
+ async function handleGet(status, key) {
120
+ if (!key) {
121
+ error('Missing key. Usage: npx agileflow config get <key>');
122
+ process.exit(1);
123
+ }
124
+
125
+ const validKeys = ['userName', 'ides', 'agileflowFolder', 'docsFolder', 'version'];
126
+
127
+ if (!validKeys.includes(key)) {
128
+ error(`Invalid key: ${key}`);
129
+ console.log(chalk.dim(`Valid keys: ${validKeys.join(', ')}\n`));
130
+ process.exit(1);
131
+ }
132
+
133
+ let value;
134
+ switch (key) {
135
+ case 'userName':
136
+ value = status.userName || 'Developer';
137
+ break;
138
+ case 'ides':
139
+ value = (status.ides || ['claude-code']).join(',');
140
+ break;
141
+ case 'agileflowFolder':
142
+ value = status.agileflowFolder || '.agileflow';
143
+ break;
144
+ case 'docsFolder':
145
+ value = status.docsFolder || 'docs';
146
+ break;
147
+ case 'version':
148
+ value = status.version || 'unknown';
149
+ break;
150
+ }
151
+
152
+ console.log(value);
153
+ }
154
+
155
+ /**
156
+ * Handle set subcommand
157
+ */
158
+ async function handleSet(directory, status, manifestPath, key, value) {
159
+ if (!key || value === undefined) {
160
+ error('Missing arguments. Usage: npx agileflow config set <key> <value>');
161
+ process.exit(1);
162
+ }
163
+
164
+ const validKeys = ['userName', 'ides', 'agileflowFolder', 'docsFolder'];
165
+
166
+ if (!validKeys.includes(key)) {
167
+ error(`Invalid key: ${key}`);
168
+ console.log(chalk.dim(`Valid keys: ${validKeys.join(', ')}\n`));
169
+ process.exit(1);
170
+ }
171
+
172
+ displayLogo();
173
+ displaySection('Updating Configuration');
174
+
175
+ // Read current manifest
176
+ const manifestContent = await fs.readFile(manifestPath, 'utf8');
177
+ const manifest = yaml.load(manifestContent);
178
+
179
+ // Track if we need to update IDE configs
180
+ let needsIdeUpdate = false;
181
+ const oldIdes = manifest.ides || [];
182
+
183
+ // Update the value
184
+ switch (key) {
185
+ case 'userName':
186
+ manifest.user_name = value;
187
+ info(`Setting userName to: ${chalk.cyan(value)}`);
188
+ break;
189
+
190
+ case 'ides':
191
+ const newIdes = value.split(',').map((ide) => ide.trim());
192
+ const validIdes = ['claude-code', 'cursor', 'windsurf'];
193
+
194
+ // Validate IDEs
195
+ for (const ide of newIdes) {
196
+ if (!validIdes.includes(ide)) {
197
+ error(`Invalid IDE: ${ide}`);
198
+ console.log(chalk.dim(`Valid IDEs: ${validIdes.join(', ')}\n`));
199
+ process.exit(1);
200
+ }
201
+ }
202
+
203
+ manifest.ides = newIdes;
204
+ needsIdeUpdate = true;
205
+ info(`Setting ides to: ${chalk.cyan(newIdes.join(', '))}`);
206
+ break;
207
+
208
+ case 'agileflowFolder':
209
+ 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'));
211
+ manifest.agileflow_folder = value;
212
+ info(`Setting agileflowFolder to: ${chalk.cyan(value)}`);
213
+ break;
214
+
215
+ case 'docsFolder':
216
+ manifest.docs_folder = value;
217
+ info(`Setting docsFolder to: ${chalk.cyan(value)}`);
218
+ break;
219
+ }
220
+
221
+ // Update timestamp
222
+ manifest.updated_at = new Date().toISOString();
223
+
224
+ // Write manifest
225
+ await fs.writeFile(manifestPath, yaml.dump(manifest), 'utf8');
226
+ success('Configuration updated');
227
+
228
+ // Update IDE configs if needed
229
+ if (needsIdeUpdate) {
230
+ console.log();
231
+ info('Updating IDE configurations...');
232
+
233
+ ideManager.setAgileflowFolder(manifest.agileflow_folder || '.agileflow');
234
+ ideManager.setDocsFolder(manifest.docs_folder || 'docs');
235
+
236
+ // Remove old IDE configs
237
+ for (const ide of oldIdes) {
238
+ if (!manifest.ides.includes(ide)) {
239
+ const configPath = getIdeConfigPath(directory, ide);
240
+ if (await fs.pathExists(configPath)) {
241
+ await fs.remove(configPath);
242
+ info(`Removed ${formatIdeName(ide)} configuration`);
243
+ }
244
+ }
245
+ }
246
+
247
+ // Add new IDE configs
248
+ for (const ide of manifest.ides) {
249
+ await ideManager.setup(ide, directory, status.path);
250
+ success(`Updated ${formatIdeName(ide)} configuration`);
251
+ }
252
+
253
+ console.log();
254
+ success('IDE configurations updated');
255
+ }
256
+
257
+ console.log();
258
+ }
259
+
260
+ /**
261
+ * Get IDE config path
262
+ */
263
+ function getIdeConfigPath(projectDir, ide) {
264
+ const paths = {
265
+ 'claude-code': '.claude/commands/AgileFlow',
266
+ 'cursor': '.cursor/rules/agileflow',
267
+ 'windsurf': '.windsurf/workflows/agileflow',
268
+ };
269
+
270
+ return path.join(projectDir, paths[ide] || '');
271
+ }
272
+
273
+ /**
274
+ * Format IDE name for display
275
+ */
276
+ function formatIdeName(ide) {
277
+ const names = {
278
+ 'claude-code': 'Claude Code',
279
+ 'cursor': 'Cursor',
280
+ 'windsurf': 'Windsurf',
281
+ };
282
+
283
+ return names[ide] || ide;
284
+ }
@@ -6,9 +6,11 @@
6
6
 
7
7
  const chalk = require('chalk');
8
8
  const path = require('node:path');
9
+ const crypto = require('node:crypto');
9
10
  const fs = require('fs-extra');
10
11
  const { Installer } = require('../installers/core/installer');
11
- const { displayLogo, displaySection, success, warning, error, info } = require('../lib/ui');
12
+ const { displayLogo, displaySection, success, warning, error, info, confirm } = require('../lib/ui');
13
+ const { IdeManager } = require('../installers/ide/manager');
12
14
  const { getCurrentVersion } = require('../lib/version-checker');
13
15
 
14
16
  const installer = new Installer();
@@ -18,16 +20,18 @@ module.exports = {
18
20
  description: 'Diagnose AgileFlow installation issues',
19
21
  options: [
20
22
  ['-d, --directory <path>', 'Project directory (default: current directory)'],
23
+ ['--fix', 'Automatically fix detected issues'],
21
24
  ],
22
25
  action: async (options) => {
23
26
  try {
24
27
  const directory = path.resolve(options.directory || '.');
25
28
 
26
29
  displayLogo();
27
- displaySection('AgileFlow Diagnostics');
30
+ displaySection(options.fix ? 'AgileFlow Auto-Repair' : 'AgileFlow Diagnostics');
28
31
 
29
32
  let issues = 0;
30
33
  let warnings = 0;
34
+ const repairs = []; // Track fixable issues
31
35
 
32
36
  // Check Node.js version
33
37
  console.log(chalk.bold('Environment:'));
@@ -73,16 +77,98 @@ module.exports = {
73
77
  } else {
74
78
  error('manifest.yaml missing');
75
79
  issues++;
80
+ repairs.push({
81
+ type: 'missing-manifest',
82
+ message: 'Recreate missing manifest.yaml',
83
+ fix: async () => {
84
+ info('Recreating manifest.yaml...');
85
+ const packageJson = require(path.join(__dirname, '..', '..', '..', 'package.json'));
86
+ const cfgDir = path.join(status.path, '_cfg');
87
+ await fs.ensureDir(cfgDir);
88
+
89
+ const yaml = require('js-yaml');
90
+ const manifest = {
91
+ version: packageJson.version,
92
+ installed_at: new Date().toISOString(),
93
+ updated_at: new Date().toISOString(),
94
+ ides: status.ides || ['claude-code'],
95
+ modules: ['core'],
96
+ user_name: status.userName || 'Developer',
97
+ agileflow_folder: path.basename(status.path),
98
+ docs_folder: 'docs',
99
+ };
100
+
101
+ await fs.writeFile(manifestPath, yaml.dump(manifest), 'utf8');
102
+ success('Created manifest.yaml');
103
+ },
104
+ });
105
+ }
106
+
107
+ // Check safe update tracking and pending preserved updates
108
+ console.log(chalk.bold('\nSafe Updates:'));
109
+ const cfgDir = path.join(status.path, '_cfg');
110
+ const fileIndexPath = path.join(cfgDir, 'files.json');
111
+ let fileIndex = null;
112
+
113
+ if (await fs.pathExists(fileIndexPath)) {
114
+ try {
115
+ fileIndex = await fs.readJson(fileIndexPath);
116
+ if (!fileIndex || fileIndex.schema !== 1 || !fileIndex.files || typeof fileIndex.files !== 'object') {
117
+ throw new Error('invalid format');
118
+ }
119
+ success('files.json present');
120
+
121
+ const protectedCount = countProtectedFiles(fileIndex);
122
+ if (protectedCount > 0) {
123
+ warning(`Protected files: ${protectedCount} (local changes preserved)`);
124
+ warnings++;
125
+ }
126
+ } catch (err) {
127
+ warning(`files.json invalid (${err.message})`);
128
+ warnings++;
129
+ repairs.push({
130
+ type: 'invalid-file-index',
131
+ message: 'Recreate files.json safe-update index',
132
+ fix: async () => {
133
+ await createProtectedFileIndex(status.path, fileIndexPath);
134
+ success('Recreated files.json (all files protected)');
135
+ },
136
+ });
137
+ }
138
+ } else {
139
+ warning('files.json missing (safe updates disabled)');
140
+ warnings++;
141
+ repairs.push({
142
+ type: 'missing-file-index',
143
+ message: 'Create files.json safe-update index',
144
+ fix: async () => {
145
+ await createProtectedFileIndex(status.path, fileIndexPath);
146
+ success('Created files.json (all files protected)');
147
+ },
148
+ });
149
+ }
150
+
151
+ const updatesDir = path.join(cfgDir, 'updates');
152
+ const updateSets = await listSubdirectories(updatesDir);
153
+ if (updateSets.length > 0) {
154
+ warning(`Pending preserved updates: ${updateSets.length} set(s)`);
155
+ warnings++;
156
+ info(`Review: ${updatesDir}/`);
157
+ } else {
158
+ success('No pending preserved updates');
76
159
  }
77
160
 
78
161
  // Check core content
79
162
  const counts = await installer.countInstalledItems(status.path);
80
163
 
164
+ let missingCore = false;
165
+
81
166
  if (counts.agents > 0) {
82
167
  success(`Core agents: ${counts.agents} files`);
83
168
  } else {
84
169
  error('Core agents: Missing');
85
170
  issues++;
171
+ missingCore = true;
86
172
  }
87
173
 
88
174
  if (counts.commands > 0) {
@@ -90,6 +176,27 @@ module.exports = {
90
176
  } else {
91
177
  error('Commands: Missing');
92
178
  issues++;
179
+ missingCore = true;
180
+ }
181
+
182
+ if (missingCore) {
183
+ repairs.push({
184
+ type: 'missing-core',
185
+ message: 'Reinstall missing core content',
186
+ fix: async () => {
187
+ info('Reinstalling core content...');
188
+ const config = {
189
+ directory,
190
+ ides: status.ides || ['claude-code'],
191
+ userName: status.userName || 'Developer',
192
+ agileflowFolder: path.basename(status.path),
193
+ docsFolder: status.docsFolder || 'docs',
194
+ };
195
+
196
+ await installer.install(config);
197
+ success('Reinstalled core content');
198
+ },
199
+ });
93
200
  }
94
201
 
95
202
  if (counts.skills > 0) {
@@ -103,6 +210,10 @@ module.exports = {
103
210
  if (status.ides && status.ides.length > 0) {
104
211
  console.log(chalk.bold('\nIDE Configurations:'));
105
212
 
213
+ const ideManager = new IdeManager();
214
+ ideManager.setAgileflowFolder(path.basename(status.path));
215
+ ideManager.setDocsFolder(status.docsFolder || 'docs');
216
+
106
217
  for (const ide of status.ides) {
107
218
  const configPath = getIdeConfigPath(directory, ide);
108
219
  const ideName = formatIdeName(ide);
@@ -112,8 +223,17 @@ module.exports = {
112
223
  const files = await countFilesInDir(configPath);
113
224
  success(`${ideName}: ${files} files`);
114
225
  } else {
115
- warning(`${ideName}: Config missing (run 'npx agileflow update')`);
226
+ warning(`${ideName}: Config missing`);
116
227
  warnings++;
228
+ repairs.push({
229
+ type: 'missing-ide-config',
230
+ message: `Reinstall ${ideName} configuration`,
231
+ fix: async () => {
232
+ info(`Reinstalling ${ideName} configuration...`);
233
+ await ideManager.setup(ide, directory, status.path);
234
+ success(`Reinstalled ${ideName} configuration`);
235
+ },
236
+ });
117
237
  }
118
238
  }
119
239
  }
@@ -127,9 +247,19 @@ module.exports = {
127
247
  if (!status.ides || !status.ides.includes(ide)) {
128
248
  const configPath = getIdeConfigPath(directory, ide);
129
249
  if (await fs.pathExists(configPath)) {
130
- warning(`${formatIdeName(ide)}: Config exists but not in manifest`);
250
+ const ideName = formatIdeName(ide);
251
+ warning(`${ideName}: Config exists but not in manifest`);
131
252
  orphansFound = true;
132
253
  warnings++;
254
+ repairs.push({
255
+ type: 'orphaned-config',
256
+ message: `Remove orphaned ${ideName} configuration`,
257
+ fix: async () => {
258
+ info(`Removing orphaned ${ideName} configuration...`);
259
+ await fs.remove(configPath);
260
+ success(`Removed ${ideName} configuration`);
261
+ },
262
+ });
133
263
  }
134
264
  }
135
265
  }
@@ -138,6 +268,26 @@ module.exports = {
138
268
  success('No orphaned configurations');
139
269
  }
140
270
 
271
+ // Execute repairs if --fix is enabled
272
+ if (options.fix && repairs.length > 0) {
273
+ console.log();
274
+ displaySection('Applying Fixes');
275
+
276
+ for (const repair of repairs) {
277
+ try {
278
+ await repair.fix();
279
+ } catch (err) {
280
+ error(`Failed to ${repair.message.toLowerCase()}: ${err.message}`);
281
+ }
282
+ }
283
+
284
+ console.log();
285
+ success(`Applied ${repairs.length} fix(es)`);
286
+ } else if (repairs.length > 0 && !options.fix) {
287
+ console.log();
288
+ info(`Found ${repairs.length} fixable issue(s). Run with --fix to auto-repair.`);
289
+ }
290
+
141
291
  // Print summary
142
292
  printSummary(issues, warnings);
143
293
 
@@ -241,3 +391,70 @@ function printSummary(issues, warnings) {
241
391
  console.log(chalk.red(`${issues} issue(s), ${warnings} warning(s) found.\n`));
242
392
  }
243
393
  }
394
+
395
+ function sha256Hex(data) {
396
+ return crypto.createHash('sha256').update(data).digest('hex');
397
+ }
398
+
399
+ function toPosixPath(filePath) {
400
+ return filePath.split(path.sep).join('/');
401
+ }
402
+
403
+ function countProtectedFiles(fileIndex) {
404
+ if (!fileIndex || !fileIndex.files || typeof fileIndex.files !== 'object') return 0;
405
+ return Object.values(fileIndex.files).filter((record) => record && record.protected).length;
406
+ }
407
+
408
+ async function listSubdirectories(dirPath) {
409
+ if (!(await fs.pathExists(dirPath))) return [];
410
+ const entries = await fs.readdir(dirPath, { withFileTypes: true });
411
+ return entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();
412
+ }
413
+
414
+ async function createProtectedFileIndex(agileflowDir, fileIndexPath) {
415
+ const candidates = ['agents', 'commands', 'skills', 'templates', 'config.yaml'];
416
+ const files = {};
417
+
418
+ for (const candidate of candidates) {
419
+ const candidatePath = path.join(agileflowDir, candidate);
420
+ if (!(await fs.pathExists(candidatePath))) continue;
421
+
422
+ const stat = await fs.stat(candidatePath);
423
+ if (stat.isFile()) {
424
+ const data = await fs.readFile(candidatePath);
425
+ const relativePath = toPosixPath(path.relative(agileflowDir, candidatePath));
426
+ files[relativePath] = { sha256: sha256Hex(data), protected: true };
427
+ continue;
428
+ }
429
+
430
+ await indexDirectory(candidatePath, agileflowDir, files);
431
+ }
432
+
433
+ const index = {
434
+ schema: 1,
435
+ generated_at: new Date().toISOString(),
436
+ version: getCurrentVersion(),
437
+ files,
438
+ };
439
+
440
+ await fs.ensureDir(path.dirname(fileIndexPath));
441
+ await fs.writeJson(fileIndexPath, index, { spaces: 2 });
442
+ }
443
+
444
+ async function indexDirectory(dirPath, rootDir, filesOut) {
445
+ const entries = await fs.readdir(dirPath, { withFileTypes: true });
446
+
447
+ for (const entry of entries) {
448
+ const entryPath = path.join(dirPath, entry.name);
449
+ if (entry.isDirectory()) {
450
+ await indexDirectory(entryPath, rootDir, filesOut);
451
+ continue;
452
+ }
453
+
454
+ if (entry.isFile()) {
455
+ const data = await fs.readFile(entryPath);
456
+ const relativePath = toPosixPath(path.relative(rootDir, entryPath));
457
+ filesOut[relativePath] = { sha256: sha256Hex(data), protected: true };
458
+ }
459
+ }
460
+ }
@@ -33,6 +33,7 @@ module.exports = {
33
33
  userName: 'Developer',
34
34
  agileflowFolder: '.agileflow',
35
35
  docsFolder: 'docs',
36
+ updateGitignore: true,
36
37
  };
37
38
  } else {
38
39
  // Interactive prompts
@@ -65,7 +66,9 @@ module.exports = {
65
66
 
66
67
  // Create docs structure
67
68
  displaySection('Creating Documentation Structure', `Folder: ${config.docsFolder}/`);
68
- const docsResult = await createDocsStructure(config.directory, config.docsFolder);
69
+ const docsResult = await createDocsStructure(config.directory, config.docsFolder, {
70
+ updateGitignore: config.updateGitignore,
71
+ });
69
72
 
70
73
  if (!docsResult.success) {
71
74
  error('Failed to create docs structure');