agileflow 2.92.1 → 2.94.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 (115) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +3 -3
  3. package/package.json +1 -1
  4. package/scripts/agileflow-statusline.sh +106 -0
  5. package/scripts/agileflow-welcome.js +54 -0
  6. package/scripts/document-repl.js +793 -0
  7. package/scripts/session-manager.js +230 -16
  8. package/scripts/spawn-parallel.js +53 -14
  9. package/src/core/agents/accessibility.md +19 -125
  10. package/src/core/agents/adr-writer.md +18 -1
  11. package/src/core/agents/analytics.md +19 -125
  12. package/src/core/agents/api.md +5 -130
  13. package/src/core/agents/ci.md +26 -131
  14. package/src/core/agents/compliance.md +21 -125
  15. package/src/core/agents/database.md +20 -125
  16. package/src/core/agents/datamigration.md +20 -125
  17. package/src/core/agents/design.md +19 -125
  18. package/src/core/agents/devops.md +12 -129
  19. package/src/core/agents/documentation.md +18 -1
  20. package/src/core/agents/epic-planner.md +31 -10
  21. package/src/core/agents/integrations.md +19 -125
  22. package/src/core/agents/mobile.md +19 -125
  23. package/src/core/agents/monitoring.md +19 -125
  24. package/src/core/agents/performance.md +19 -125
  25. package/src/core/agents/product.md +18 -1
  26. package/src/core/agents/qa.md +21 -125
  27. package/src/core/agents/readme-updater.md +18 -1
  28. package/src/core/agents/refactor.md +19 -125
  29. package/src/core/agents/research.md +3 -1
  30. package/src/core/agents/rlm-subcore.md +202 -0
  31. package/src/core/agents/security.md +7 -125
  32. package/src/core/agents/testing.md +20 -125
  33. package/src/core/agents/ui.md +14 -135
  34. package/src/core/commands/adr/list.md +20 -0
  35. package/src/core/commands/adr/update.md +24 -1
  36. package/src/core/commands/adr/view.md +23 -1
  37. package/src/core/commands/adr.md +2 -2
  38. package/src/core/commands/agent.md +11 -1
  39. package/src/core/commands/assign.md +15 -6
  40. package/src/core/commands/auto.md +11 -1
  41. package/src/core/commands/babysit.md +15 -4
  42. package/src/core/commands/baseline.md +11 -1
  43. package/src/core/commands/batch.md +11 -1
  44. package/src/core/commands/blockers.md +11 -1
  45. package/src/core/commands/board.md +11 -1
  46. package/src/core/commands/changelog.md +11 -0
  47. package/src/core/commands/choose.md +16 -1
  48. package/src/core/commands/ci.md +11 -1
  49. package/src/core/commands/configure.md +73 -2
  50. package/src/core/commands/context/export.md +8 -0
  51. package/src/core/commands/context/full.md +8 -0
  52. package/src/core/commands/context/note.md +8 -0
  53. package/src/core/commands/debt.md +11 -0
  54. package/src/core/commands/deploy.md +10 -0
  55. package/src/core/commands/deps.md +11 -1
  56. package/src/core/commands/diagnose.md +10 -0
  57. package/src/core/commands/docs.md +12 -2
  58. package/src/core/commands/epic/list.md +20 -0
  59. package/src/core/commands/epic/view.md +25 -0
  60. package/src/core/commands/epic.md +5 -6
  61. package/src/core/commands/feedback.md +11 -0
  62. package/src/core/commands/handoff.md +12 -2
  63. package/src/core/commands/help.md +10 -0
  64. package/src/core/commands/ideate.md +10 -0
  65. package/src/core/commands/impact.md +11 -1
  66. package/src/core/commands/metrics.md +11 -1
  67. package/src/core/commands/multi-expert.md +11 -1
  68. package/src/core/commands/packages.md +11 -0
  69. package/src/core/commands/pr.md +10 -0
  70. package/src/core/commands/readme-sync.md +10 -5
  71. package/src/core/commands/research/analyze.md +60 -3
  72. package/src/core/commands/research/ask.md +9 -1
  73. package/src/core/commands/research/import.md +8 -0
  74. package/src/core/commands/research/list.md +8 -0
  75. package/src/core/commands/research/synthesize.md +9 -1
  76. package/src/core/commands/research/view.md +8 -0
  77. package/src/core/commands/retro.md +12 -2
  78. package/src/core/commands/review.md +11 -1
  79. package/src/core/commands/rlm.md +363 -0
  80. package/src/core/commands/roadmap/analyze.md +1 -1
  81. package/src/core/commands/rpi.md +9 -1
  82. package/src/core/commands/session/cleanup.md +250 -0
  83. package/src/core/commands/session/end.md +10 -0
  84. package/src/core/commands/session/history.md +11 -1
  85. package/src/core/commands/session/init.md +10 -0
  86. package/src/core/commands/session/new.md +113 -13
  87. package/src/core/commands/session/resume.md +10 -0
  88. package/src/core/commands/session/spawn.md +8 -0
  89. package/src/core/commands/session/status.md +10 -0
  90. package/src/core/commands/skill/create.md +1 -1
  91. package/src/core/commands/skill/delete.md +11 -1
  92. package/src/core/commands/skill/edit.md +11 -1
  93. package/src/core/commands/skill/test.md +11 -1
  94. package/src/core/commands/skill/upgrade.md +11 -1
  95. package/src/core/commands/sprint.md +14 -3
  96. package/src/core/commands/status.md +15 -6
  97. package/src/core/commands/story/list.md +23 -0
  98. package/src/core/commands/story/view.md +24 -0
  99. package/src/core/commands/story.md +4 -5
  100. package/src/core/commands/template.md +10 -0
  101. package/src/core/commands/tests.md +10 -0
  102. package/src/core/commands/update.md +10 -0
  103. package/src/core/commands/validate-expertise.md +10 -1
  104. package/src/core/commands/velocity.md +11 -1
  105. package/src/core/commands/verify.md +13 -1
  106. package/src/core/commands/whats-new.md +8 -0
  107. package/src/core/commands/workflow.md +16 -1
  108. package/src/core/templates/agent-coordination-pattern.md +38 -0
  109. package/src/core/templates/agileflow-metadata.json +25 -0
  110. package/src/core/templates/preserve-rules-common.md +107 -0
  111. package/src/core/templates/preserve-rules.json +42 -0
  112. package/src/core/templates/proactive-action-spec.md +29 -0
  113. package/src/core/templates/quality-gate-priorities.md +34 -0
  114. package/src/core/templates/session-harness-protocol.md +128 -0
  115. package/tools/cli/lib/content-injector.js +338 -0
@@ -231,6 +231,161 @@ async function cleanupStaleLocksAsync(registry, options = {}) {
231
231
  return { count: cleanedSessions.length, sessions: cleanedSessions };
232
232
  }
233
233
 
234
+ /**
235
+ * Get detailed file information for a session's changes
236
+ * @param {string} sessionPath - Path to session worktree
237
+ * @param {string[]} changes - Array of git status lines
238
+ * @returns {Object[]} Array of file details with analysis
239
+ */
240
+ function getFileDetails(sessionPath, changes) {
241
+ return changes.map(change => {
242
+ const status = change.substring(0, 2).trim();
243
+ const file = change.substring(3);
244
+
245
+ const detail = { status, file, trivial: false, existsInMain: false, diffLines: 0 };
246
+
247
+ // For modified files, get diff stats
248
+ if (status === 'M') {
249
+ try {
250
+ const diffStat = spawnSync('git', ['diff', '--numstat', file], {
251
+ cwd: sessionPath,
252
+ encoding: 'utf8',
253
+ timeout: 3000,
254
+ });
255
+ if (diffStat.stdout) {
256
+ const parts = diffStat.stdout.trim().split('\t');
257
+ const added = parseInt(parts[0], 10) || 0;
258
+ const removed = parseInt(parts[1], 10) || 0;
259
+ detail.diffLines = added + removed;
260
+ // Trivial if only 1-2 lines changed (likely whitespace)
261
+ detail.trivial = detail.diffLines <= 2;
262
+ }
263
+ } catch (e) {
264
+ // Can't get diff, assume not trivial
265
+ }
266
+ }
267
+
268
+ // For untracked files, check if exists in main
269
+ if (status === '??') {
270
+ detail.existsInMain = fs.existsSync(path.join(ROOT, file));
271
+ // Trivial if it's a duplicate
272
+ detail.trivial = detail.existsInMain;
273
+ }
274
+
275
+ // Config/cache files are trivial
276
+ if (file.includes('.claude/') || file.includes('.agileflow/cache')) {
277
+ detail.trivial = true;
278
+ }
279
+
280
+ return detail;
281
+ });
282
+ }
283
+
284
+ /**
285
+ * Get health status for all sessions
286
+ * Detects: stale sessions, uncommitted changes, orphaned entries
287
+ * @param {Object} options - { staleDays: 7, detailed: false }
288
+ * @returns {Object} Health report
289
+ */
290
+ function getSessionsHealth(options = {}) {
291
+ const { staleDays = 7, detailed = false } = options;
292
+ const registry = loadRegistry();
293
+ const now = Date.now();
294
+ const staleThreshold = staleDays * 24 * 60 * 60 * 1000;
295
+
296
+ const health = {
297
+ stale: [], // Sessions with no activity > staleDays
298
+ uncommitted: [], // Sessions with uncommitted git changes
299
+ orphanedRegistry: [], // Registry entries where path doesn't exist
300
+ orphanedWorktrees: [], // Worktrees not in registry
301
+ healthy: 0,
302
+ };
303
+
304
+ // Check each registered session
305
+ for (const [id, session] of Object.entries(registry.sessions)) {
306
+ if (session.is_main) continue; // Skip main session
307
+
308
+ const age = now - new Date(session.last_active).getTime();
309
+ const pathExists = fs.existsSync(session.path);
310
+
311
+ // Check for orphaned registry entry (path missing)
312
+ if (!pathExists) {
313
+ health.orphanedRegistry.push({ id, ...session, reason: 'path_missing' });
314
+ continue;
315
+ }
316
+
317
+ // Check for stale session
318
+ if (age > staleThreshold) {
319
+ health.stale.push({
320
+ id,
321
+ ...session,
322
+ ageDays: Math.floor(age / (24 * 60 * 60 * 1000)),
323
+ });
324
+ }
325
+
326
+ // Check for uncommitted changes
327
+ try {
328
+ const result = spawnSync('git', ['status', '--porcelain'], {
329
+ cwd: session.path,
330
+ encoding: 'utf8',
331
+ timeout: 5000,
332
+ });
333
+ if (result.stdout && result.stdout.trim()) {
334
+ // Don't use trim() on the whole string - it removes leading space from first status
335
+ // Split by newline and filter empty lines instead
336
+ const changes = result.stdout.split('\n').filter(line => line.length > 0);
337
+ const sessionData = {
338
+ id,
339
+ ...session,
340
+ changeCount: changes.length,
341
+ changes: detailed ? changes : changes.slice(0, 5), // All or first 5
342
+ };
343
+
344
+ // Add detailed file analysis if requested
345
+ if (detailed) {
346
+ sessionData.fileDetails = getFileDetails(session.path, changes);
347
+ // Calculate if session is safe to delete (all changes trivial)
348
+ sessionData.allTrivial = sessionData.fileDetails.every(f => f.trivial);
349
+ }
350
+
351
+ health.uncommitted.push(sessionData);
352
+ } else {
353
+ health.healthy++;
354
+ }
355
+ } catch (e) {
356
+ // Can't check, skip
357
+ }
358
+ }
359
+
360
+ // Check for orphaned worktrees (directories not in registry)
361
+ try {
362
+ const worktreeList = spawnSync('git', ['worktree', 'list', '--porcelain'], {
363
+ encoding: 'utf8',
364
+ });
365
+ if (worktreeList.stdout) {
366
+ const worktrees = worktreeList.stdout
367
+ .split('\n')
368
+ .filter(line => line.startsWith('worktree '))
369
+ .map(line => line.replace('worktree ', ''));
370
+
371
+ const mainPath = ROOT;
372
+ for (const wtPath of worktrees) {
373
+ const inRegistry = Object.values(registry.sessions).some(s => s.path === wtPath);
374
+ if (!inRegistry && wtPath !== mainPath) {
375
+ // Check if it's an AgileFlow worktree (has .agileflow folder)
376
+ if (fs.existsSync(path.join(wtPath, '.agileflow'))) {
377
+ health.orphanedWorktrees.push({ path: wtPath });
378
+ }
379
+ }
380
+ }
381
+ }
382
+ } catch (e) {
383
+ // Can't list worktrees, skip
384
+ }
385
+
386
+ return health;
387
+ }
388
+
234
389
  // Git command cache (10 second TTL to avoid stale data)
235
390
  const gitCache = {
236
391
  data: new Map(),
@@ -432,7 +587,11 @@ function progressIndicator(message) {
432
587
  * @param {number} timeoutMs - Timeout in milliseconds
433
588
  * @returns {Promise<{stdout: string, stderr: string}>}
434
589
  */
435
- function createWorktreeWithTimeout(worktreePath, branchName, timeoutMs = DEFAULT_WORKTREE_TIMEOUT_MS) {
590
+ function createWorktreeWithTimeout(
591
+ worktreePath,
592
+ branchName,
593
+ timeoutMs = DEFAULT_WORKTREE_TIMEOUT_MS
594
+ ) {
436
595
  return new Promise((resolve, reject) => {
437
596
  let stdout = '';
438
597
  let stderr = '';
@@ -455,15 +614,15 @@ function createWorktreeWithTimeout(worktreePath, branchName, timeoutMs = DEFAULT
455
614
  }, 1000);
456
615
  }, timeoutMs);
457
616
 
458
- proc.stdout.on('data', (data) => {
617
+ proc.stdout.on('data', data => {
459
618
  stdout += data.toString();
460
619
  });
461
620
 
462
- proc.stderr.on('data', (data) => {
621
+ proc.stderr.on('data', data => {
463
622
  stderr += data.toString();
464
623
  });
465
624
 
466
- proc.on('error', (err) => {
625
+ proc.on('error', err => {
467
626
  clearTimeout(timer);
468
627
  reject(new Error(`Failed to spawn git: ${err.message}`));
469
628
  });
@@ -472,7 +631,11 @@ function createWorktreeWithTimeout(worktreePath, branchName, timeoutMs = DEFAULT
472
631
  clearTimeout(timer);
473
632
 
474
633
  if (timedOut) {
475
- reject(new Error(`Worktree creation timed out after ${timeoutMs / 1000}s. Try increasing timeout or check disk space.`));
634
+ reject(
635
+ new Error(
636
+ `Worktree creation timed out after ${timeoutMs / 1000}s. Try increasing timeout or check disk space.`
637
+ )
638
+ );
476
639
  return;
477
640
  }
478
641
 
@@ -601,7 +764,9 @@ async function createSession(options = {}) {
601
764
  const timeoutMs = options.timeout || DEFAULT_WORKTREE_TIMEOUT_MS;
602
765
 
603
766
  // Create worktree with timeout and progress feedback
604
- const stopProgress = progressIndicator('Creating worktree (this may take a while for large repos)');
767
+ const stopProgress = progressIndicator(
768
+ 'Creating worktree (this may take a while for large repos)'
769
+ );
605
770
  try {
606
771
  await createWorktreeWithTimeout(worktreePath, branchName, timeoutMs);
607
772
  stopProgress();
@@ -633,12 +798,11 @@ async function createSession(options = {}) {
633
798
  }
634
799
  }
635
800
 
636
- // Copy Claude Code, AgileFlow config, and docs folders (gitignored contents won't copy with worktree)
801
+ // Copy Claude Code and AgileFlow config folders (gitignored contents won't copy with worktree)
637
802
  // Note: The folder may exist with some tracked files, but gitignored subfolders (commands/, agents/) won't be there
638
- // docs/ contains gitignored state files like status.json, session-state.json that need to be shared
639
- const configFolders = ['.claude', '.agileflow', 'docs'];
803
+ const configFoldersToCopy = ['.claude', '.agileflow'];
640
804
  const copiedFolders = [];
641
- for (const folder of configFolders) {
805
+ for (const folder of configFoldersToCopy) {
642
806
  const src = path.join(ROOT, folder);
643
807
  const dest = path.join(worktreePath, folder);
644
808
  if (fs.existsSync(src)) {
@@ -653,6 +817,37 @@ async function createSession(options = {}) {
653
817
  }
654
818
  }
655
819
 
820
+ // Symlink docs/ to main project docs (shared state: status.json, session-state.json, bus/)
821
+ // This enables story claiming, status bus, and session coordination across worktrees
822
+ const foldersToSymlink = ['docs'];
823
+ const symlinkedFolders = [];
824
+ for (const folder of foldersToSymlink) {
825
+ const src = path.join(ROOT, folder);
826
+ const dest = path.join(worktreePath, folder);
827
+ if (fs.existsSync(src)) {
828
+ try {
829
+ // Remove if exists (worktree may have empty/partial tracked folder)
830
+ if (fs.existsSync(dest)) {
831
+ fs.rmSync(dest, { recursive: true, force: true });
832
+ }
833
+
834
+ // Create relative symlink (works across project moves)
835
+ const relPath = path.relative(worktreePath, src);
836
+ fs.symlinkSync(relPath, dest, 'dir');
837
+ symlinkedFolders.push(folder);
838
+ } catch (e) {
839
+ // Fallback to copy if symlink fails (e.g., Windows without dev mode)
840
+ console.warn(`Warning: Could not symlink ${folder}, copying instead: ${e.message}`);
841
+ try {
842
+ fs.cpSync(src, dest, { recursive: true, force: true });
843
+ copiedFolders.push(folder);
844
+ } catch (copyErr) {
845
+ console.warn(`Warning: Could not copy ${folder}: ${copyErr.message}`);
846
+ }
847
+ }
848
+ }
849
+ }
850
+
656
851
  // Register session - worktree sessions are always parallel threads
657
852
  registry.next_id++;
658
853
  registry.sessions[sessionId] = {
@@ -677,6 +872,7 @@ async function createSession(options = {}) {
677
872
  command: `cd "${worktreePath}" && claude`,
678
873
  envFilesCopied: copiedEnvFiles,
679
874
  foldersCopied: copiedFolders,
875
+ foldersSymlinked: symlinkedFolders,
680
876
  };
681
877
  }
682
878
 
@@ -1300,16 +1496,23 @@ function main() {
1300
1496
  if (options.timeout) {
1301
1497
  options.timeout = parseInt(options.timeout, 10);
1302
1498
  if (isNaN(options.timeout) || options.timeout < 1000) {
1303
- console.log(JSON.stringify({ success: false, error: 'Timeout must be a number >= 1000 (milliseconds)' }));
1499
+ console.log(
1500
+ JSON.stringify({
1501
+ success: false,
1502
+ error: 'Timeout must be a number >= 1000 (milliseconds)',
1503
+ })
1504
+ );
1304
1505
  return;
1305
1506
  }
1306
1507
  }
1307
1508
  // Handle async createSession
1308
- createSession(options).then(result => {
1309
- console.log(JSON.stringify(result));
1310
- }).catch(err => {
1311
- console.log(JSON.stringify({ success: false, error: err.message }));
1312
- });
1509
+ createSession(options)
1510
+ .then(result => {
1511
+ console.log(JSON.stringify(result));
1512
+ })
1513
+ .catch(err => {
1514
+ console.log(JSON.stringify({ success: false, error: err.message }));
1515
+ });
1313
1516
  break;
1314
1517
  }
1315
1518
 
@@ -1361,6 +1564,17 @@ function main() {
1361
1564
  break;
1362
1565
  }
1363
1566
 
1567
+ case 'health': {
1568
+ // Get health status for all sessions
1569
+ // Usage: health [staleDays] [--detailed]
1570
+ const staleDaysArg = args.find(a => /^\d+$/.test(a));
1571
+ const staleDays = staleDaysArg ? parseInt(staleDaysArg, 10) : 7;
1572
+ const detailed = args.includes('--detailed');
1573
+ const health = getSessionsHealth({ staleDays, detailed });
1574
+ console.log(JSON.stringify(health));
1575
+ break;
1576
+ }
1577
+
1364
1578
  case 'get': {
1365
1579
  const sessionId = args[1];
1366
1580
  if (!sessionId) {
@@ -64,16 +64,30 @@ function hasScreen() {
64
64
  * Build the Claude command for a session
65
65
  */
66
66
  function buildClaudeCommand(sessionPath, options = {}) {
67
- const { init = false, dangerous = false, prompt = null } = options;
67
+ const {
68
+ init = false,
69
+ dangerous = false,
70
+ prompt = null,
71
+ claudeArgs = null,
72
+ noClaude = false,
73
+ } = options;
68
74
  const parts = [`cd "${sessionPath}"`];
69
75
 
70
76
  if (init) {
71
77
  parts.push('claude init --yes 2>/dev/null || true');
72
78
  }
73
79
 
80
+ // If noClaude is true, just return cd command (no claude startup)
81
+ if (noClaude) {
82
+ return parts.join(' && ');
83
+ }
84
+
74
85
  let claudeCmd = 'claude';
75
86
  if (dangerous) {
76
- claudeCmd = 'claude --dangerouslySkipPermissions';
87
+ claudeCmd = 'claude --dangerously-skip-permissions';
88
+ } else if (claudeArgs) {
89
+ // Custom claude arguments (e.g., --permission-mode acceptEdits)
90
+ claudeCmd = `claude ${claudeArgs}`;
77
91
  }
78
92
 
79
93
  if (prompt) {
@@ -215,7 +229,7 @@ function getReadyStoriesFromEpic(epicId) {
215
229
  /**
216
230
  * Main spawn command
217
231
  */
218
- function spawn(args) {
232
+ async function spawn(args) {
219
233
  const count = args.count ? parseInt(args.count, 10) : null;
220
234
  const branches = args.branches ? args.branches.split(',').map(b => b.trim()) : null;
221
235
  const fromEpic = args['from-epic'] || args.fromEpic;
@@ -223,6 +237,8 @@ function spawn(args) {
223
237
  const init = args.init || false;
224
238
  const dangerous = args.dangerous || false;
225
239
  const prompt = args.prompt || null;
240
+ const claudeArgs = args['claude-args'] || args.claudeArgs || null;
241
+ const noClaude = args['no-claude'] || args.noClaude || false;
226
242
 
227
243
  // Determine what to create
228
244
  let sessionsToCreate = [];
@@ -276,7 +292,7 @@ function spawn(args) {
276
292
 
277
293
  const createdSessions = [];
278
294
  for (const sessionSpec of sessionsToCreate) {
279
- const result = sessionManager.createSession({
295
+ const result = await sessionManager.createSession({
280
296
  nickname: sessionSpec.nickname,
281
297
  branch: sessionSpec.branch,
282
298
  });
@@ -311,10 +327,16 @@ function spawn(args) {
311
327
  // Spawn in tmux or output commands
312
328
  if (noTmux) {
313
329
  // User explicitly requested manual mode
314
- outputCommands(createdSessions, { init, dangerous, prompt });
330
+ outputCommands(createdSessions, { init, dangerous, prompt, claudeArgs, noClaude });
315
331
  } else if (hasTmux()) {
316
332
  // Tmux available - use it
317
- const tmuxResult = spawnInTmux(createdSessions, { init, dangerous, prompt });
333
+ const tmuxResult = spawnInTmux(createdSessions, {
334
+ init,
335
+ dangerous,
336
+ prompt,
337
+ claudeArgs,
338
+ noClaude,
339
+ });
318
340
 
319
341
  if (tmuxResult.success) {
320
342
  console.log(success(`\n✅ Tmux session created: ${tmuxResult.sessionName}`));
@@ -326,7 +348,7 @@ function spawn(args) {
326
348
  console.log('');
327
349
  } else {
328
350
  console.error(error(`Failed to create tmux session: ${tmuxResult.error}`));
329
- outputCommands(createdSessions, { init, dangerous, prompt });
351
+ outputCommands(createdSessions, { init, dangerous, prompt, claudeArgs, noClaude });
330
352
  }
331
353
  } else {
332
354
  // Tmux NOT available - require it or use --no-tmux
@@ -392,9 +414,12 @@ function list() {
392
414
  /**
393
415
  * Add a new window to an existing tmux session
394
416
  */
395
- function addWindow(args) {
417
+ async function addWindow(args) {
396
418
  const nickname = args.nickname || args.name || null;
397
419
  const branch = args.branch || null;
420
+ const dangerous = args.dangerous || false;
421
+ const claudeArgs = args['claude-args'] || args.claudeArgs || null;
422
+ const noClaude = args['no-claude'] || args.noClaude || false;
398
423
 
399
424
  // Check if we're inside a tmux session
400
425
  const tmuxEnv = process.env.TMUX;
@@ -427,7 +452,7 @@ function addWindow(args) {
427
452
  branch: branch || `parallel-${Date.now()}`,
428
453
  };
429
454
 
430
- const result = sessionManager.createSession({
455
+ const result = await sessionManager.createSession({
431
456
  nickname: sessionSpec.nickname,
432
457
  branch: sessionSpec.branch,
433
458
  });
@@ -438,7 +463,7 @@ function addWindow(args) {
438
463
  }
439
464
 
440
465
  const windowName = sessionSpec.nickname;
441
- const cmd = buildClaudeCommand(result.path, {});
466
+ const cmd = buildClaudeCommand(result.path, { dangerous, claudeArgs, noClaude });
442
467
 
443
468
  // Create new window in current tmux session
444
469
  const newWindowResult = spawnSync(
@@ -551,7 +576,9 @@ ${c.cyan}SPAWN OPTIONS:${c.reset}
551
576
  --branches "a,b,c" Create worktrees for specific branch names
552
577
  --from-epic EP-XXX Create worktrees for ready stories in epic
553
578
  --init Run 'claude init' in each worktree
554
- --dangerous Use --dangerouslySkipPermissions
579
+ --dangerous Use --dangerously-skip-permissions
580
+ --claude-args "..." Custom arguments for claude command
581
+ --no-claude Create worktree but don't start claude
555
582
  --no-tmux Output commands without spawning in tmux
556
583
  --prompt "TEXT" Initial prompt to send to each Claude instance
557
584
 
@@ -575,6 +602,9 @@ ${c.cyan}ADD-WINDOW OPTIONS:${c.reset}
575
602
  --name NAME Name for the new session/window
576
603
  --nickname NAME Alias for --name
577
604
  --branch BRANCH Use specific branch name
605
+ --dangerous Use --dangerously-skip-permissions
606
+ --claude-args "..." Custom arguments for claude command
607
+ --no-claude Create worktree but don't start claude
578
608
 
579
609
  ${c.cyan}ADD-WINDOW EXAMPLES:${c.reset}
580
610
  ${dim('# Add window with auto-generated name (when in tmux)')}
@@ -582,6 +612,15 @@ ${c.cyan}ADD-WINDOW EXAMPLES:${c.reset}
582
612
 
583
613
  ${dim('# Add named window')}
584
614
  node scripts/spawn-parallel.js add-window --name auth
615
+
616
+ ${dim('# Add window without starting claude')}
617
+ node scripts/spawn-parallel.js add-window --name research --no-claude
618
+
619
+ ${dim('# Add window with skip permissions')}
620
+ node scripts/spawn-parallel.js add-window --name trusted --dangerous
621
+
622
+ ${dim('# Add window with custom claude args')}
623
+ node scripts/spawn-parallel.js add-window --name safe --claude-args "--permission-mode acceptEdits"
585
624
  `);
586
625
  }
587
626
 
@@ -616,16 +655,16 @@ function parseArgs(argv) {
616
655
  /**
617
656
  * Main entry point
618
657
  */
619
- function main() {
658
+ async function main() {
620
659
  const { command, args } = parseArgs(process.argv.slice(2));
621
660
 
622
661
  switch (command) {
623
662
  case 'spawn':
624
- spawn(args);
663
+ await spawn(args);
625
664
  break;
626
665
  case 'add-window':
627
666
  case 'add':
628
- addWindow(args);
667
+ await addWindow(args);
629
668
  break;
630
669
  case 'list':
631
670
  list();
@@ -189,131 +189,8 @@ BOUNDARIES
189
189
  - Always prioritize real user needs over technical convenience
190
190
 
191
191
 
192
- SESSION HARNESS & VERIFICATION PROTOCOL (v2.25.0+)
192
+ <!-- {{SESSION_HARNESS}} -->
193
193
 
194
- **CRITICAL**: Session Harness System prevents agents from breaking functionality, claiming work is done when tests fail, or losing context between sessions.
195
-
196
- **PRE-IMPLEMENTATION VERIFICATION**
197
-
198
- Before starting work on ANY story:
199
-
200
- 1. **Check Session Harness**:
201
- - Look for `docs/00-meta/environment.json`
202
- - If exists → Session harness is active ✅
203
- - If missing → Suggest `/agileflow:session:init` to user
204
-
205
- 2. **Test Baseline Check**:
206
- - Read `test_status` from story in `docs/09-agents/status.json`
207
- - If `"passing"` → Proceed with implementation ✅
208
- - If `"failing"` → STOP. Cannot start new work with failing baseline ⚠️
209
- - If `"not_run"` → Run `/agileflow:verify` first to establish baseline
210
- - If `"skipped"` → Check why tests are skipped, document override decision
211
-
212
- 3. **Environment Verification** (if session harness active):
213
- - Run `/agileflow:session:resume` to verify environment and load context
214
- - Check for regressions (tests were passing, now failing)
215
- - If regression detected → Fix before proceeding with new story
216
-
217
- **DURING IMPLEMENTATION**
218
-
219
- 1. **Incremental Testing**:
220
- - Run tests frequently during development (not just at end)
221
- - Fix test failures immediately (don't accumulate debt)
222
- - Use `/agileflow:verify US-XXXX` to check specific story tests
223
-
224
- 2. **Real-time Status Updates**:
225
- - Update `test_status` in status.json as tests are written/fixed
226
- - Append bus messages when tests pass milestone checkpoints
227
-
228
- **POST-IMPLEMENTATION VERIFICATION**
229
-
230
- After completing ANY changes:
231
-
232
- 1. **Run Full Test Suite**:
233
- - Execute `/agileflow:verify US-XXXX` to run tests for the story
234
- - Check exit code (0 = success required for completion)
235
- - Review test output for warnings or flaky tests
236
-
237
- 2. **Update Test Status**:
238
- - `/agileflow:verify` automatically updates `test_status` in status.json
239
- - Verify the update was successful
240
- - Expected: `test_status: "passing"` with test results metadata
241
-
242
- 3. **Regression Check**:
243
- - Compare test results to baseline (initial test status)
244
- - If new failures introduced → Fix before marking complete
245
- - If test count decreased → Investigate deleted tests
246
-
247
- 4. **Story Completion Requirements**:
248
- - Story can ONLY be marked `"in-review"` if `test_status: "passing"` ✅
249
- - If tests failing → Story remains `"in-progress"` until fixed ⚠️
250
- - No exceptions unless documented override (see below)
251
-
252
- **OVERRIDE PROTOCOL** (Use with extreme caution)
253
-
254
- If tests are failing but you need to proceed:
255
-
256
- 1. **Document Override Decision**:
257
- - Append bus message with full explanation (include agent ID, story ID, reason, tracking issue)
258
-
259
- 2. **Update Story Dev Agent Record**:
260
- - Add note to "Issues Encountered" section explaining override
261
- - Link to tracking issue for the failing test
262
- - Document risk and mitigation plan
263
-
264
- 3. **Create Follow-up Story**:
265
- - If test failure is real but out of scope → Create new story
266
- - Link dependency in status.json
267
- - Notify user of the override and follow-up story
268
-
269
- **BASELINE MANAGEMENT**
270
-
271
- After completing major milestones (epic complete, sprint end):
272
-
273
- 1. **Establish Baseline**:
274
- - Suggest `/agileflow:baseline "Epic EP-XXXX complete"` to user
275
- - Requires: All tests passing, git working tree clean
276
- - Creates git tag + metadata for reset point
277
-
278
- 2. **Baseline Benefits**:
279
- - Known-good state to reset to if needed
280
- - Regression detection reference point
281
- - Deployment readiness checkpoint
282
- - Sprint/epic completion marker
283
-
284
- **INTEGRATION WITH WORKFLOW**
285
-
286
- The verification protocol integrates into the standard workflow:
287
-
288
- 1. **Before creating feature branch**: Run pre-implementation verification
289
- 2. **Before marking in-review**: Run post-implementation verification
290
- 3. **After merge**: Verify baseline is still passing
291
-
292
- **ERROR HANDLING**
293
-
294
- If `/agileflow:verify` fails:
295
- - Read error output carefully
296
- - Check if test command is configured in `docs/00-meta/environment.json`
297
- - Verify test dependencies are installed
298
- - If project has no tests → Suggest `/agileflow:session:init` to set up testing
299
- - If tests are misconfigured → Coordinate with AG-CI
300
-
301
- **SESSION RESUME PROTOCOL**
302
-
303
- When resuming work after context loss:
304
-
305
- 1. **Run Resume Command**: `/agileflow:session:resume` loads context automatically
306
- 2. **Check Session State**: Review `docs/09-agents/session-state.json`
307
- 3. **Verify Test Status**: Ensure no regressions occurred
308
- 4. **Load Previous Insights**: Check Dev Agent Record from previous stories
309
-
310
- **KEY PRINCIPLES**
311
-
312
- - **Tests are the contract**: Passing tests = feature works as specified
313
- - **Fail fast**: Catch regressions immediately, not at PR review
314
- - **Context preservation**: Session harness maintains progress across context windows
315
- - **Transparency**: Document all override decisions fully
316
- - **Accountability**: test_status field creates audit trail
317
194
 
318
195
  WCAG 2.1 STANDARDS
319
196
 
@@ -554,7 +431,9 @@ WORKFLOW
554
431
 
555
432
  10. Sync externally if enabled
556
433
 
557
- QUALITY CHECKLIST
434
+ <!-- {{QUALITY_GATE_PRIORITIES}} -->
435
+
436
+ QUALITY CHECKLIST (AG-ACCESSIBILITY Specific)
558
437
 
559
438
  Before approval:
560
439
  - [ ] WCAG 2.1 Level AA compliance verified
@@ -568,6 +447,21 @@ Before approval:
568
447
  - [ ] Motion respects prefers-reduced-motion
569
448
  - [ ] Accessibility documentation complete
570
449
 
450
+ AGENT COORDINATION
451
+
452
+ **Coordinates with**:
453
+ - **AG-UI**: Accessibility in UI components (send a11y findings, receive implementation questions)
454
+ - **AG-DESIGN**: Design system accessibility (send contrast/focus requirements, coordinate on design tokens)
455
+ - **AG-QA**: Accessibility testing (send test criteria, receive test results)
456
+
457
+ **Bus Messages** (append to `docs/09-agents/bus/log.jsonl`):
458
+ ```jsonl
459
+ {"ts":"<ISO>","from":"AG-ACCESSIBILITY","type":"finding","story":"<US-ID>","text":"Finding: Component [X] missing keyboard navigation"}
460
+ {"ts":"<ISO>","from":"AG-ACCESSIBILITY","type":"status","story":"<US-ID>","text":"A11y audit complete: WCAG AA compliance verified"}
461
+ ```
462
+
463
+ **On invocation**: Check bus for AG-UI requests for accessibility review.
464
+
571
465
  FIRST ACTION
572
466
 
573
467
  **CRITICAL: Load Expertise First (Agent Expert Protocol)**
@@ -362,7 +362,9 @@ ADR TEMPLATE STRUCTURE
362
362
  - [Title](URL) - Description
363
363
  ```
364
364
 
365
- QUALITY CHECKLIST
365
+ <!-- {{QUALITY_GATE_PRIORITIES}} -->
366
+
367
+ QUALITY CHECKLIST (AG-ADR-WRITER Specific)
366
368
  Before creating ADR:
367
369
  - [ ] Context explains why decision is needed now
368
370
  - [ ] At least 2 alternatives documented
@@ -388,6 +390,21 @@ TONE
388
390
  - Avoid advocacy (document, don't persuade)
389
391
  - Focus on context and reasoning, not implementation details
390
392
 
393
+ AGENT COORDINATION
394
+
395
+ **Coordinates with**:
396
+ - **AG-RESEARCH**: Technical research (receive research findings, document decisions)
397
+ - **AG-PRODUCT**: Product decisions (receive feature requirements, document trade-offs)
398
+ - **AG-EPIC-PLANNER**: Epic planning (send architectural constraints, receive epic context)
399
+
400
+ **Bus Messages** (append to `docs/09-agents/bus/log.jsonl`):
401
+ ```jsonl
402
+ {"ts":"<ISO>","from":"AG-ADR-WRITER","type":"status","story":"<US-ID>","text":"ADR-XXXX created: [decision title]"}
403
+ {"ts":"<ISO>","from":"AG-ADR-WRITER","type":"finding","story":"<US-ID>","text":"Finding: Existing ADR-YYYY conflicts with proposed approach"}
404
+ ```
405
+
406
+ **On invocation**: Check bus for architectural decisions that need documentation.
407
+
391
408
  FIRST ACTION
392
409
 
393
410
  **CRITICAL: Load Expertise First (Agent Expert Protocol)**