agileflow 2.92.1 → 2.93.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.
- package/CHANGELOG.md +5 -0
- package/README.md +3 -3
- package/package.json +1 -1
- package/scripts/agileflow-statusline.sh +106 -0
- package/scripts/agileflow-welcome.js +57 -0
- package/scripts/document-repl.js +793 -0
- package/scripts/session-manager.js +195 -12
- package/scripts/spawn-parallel.js +41 -14
- package/src/core/agents/accessibility.md +19 -125
- package/src/core/agents/adr-writer.md +18 -1
- package/src/core/agents/analytics.md +19 -125
- package/src/core/agents/api.md +5 -130
- package/src/core/agents/ci.md +26 -131
- package/src/core/agents/compliance.md +21 -125
- package/src/core/agents/database.md +20 -125
- package/src/core/agents/datamigration.md +20 -125
- package/src/core/agents/design.md +19 -125
- package/src/core/agents/devops.md +12 -129
- package/src/core/agents/documentation.md +18 -1
- package/src/core/agents/epic-planner.md +31 -10
- package/src/core/agents/integrations.md +19 -125
- package/src/core/agents/mobile.md +19 -125
- package/src/core/agents/monitoring.md +19 -125
- package/src/core/agents/performance.md +19 -125
- package/src/core/agents/product.md +18 -1
- package/src/core/agents/qa.md +21 -125
- package/src/core/agents/readme-updater.md +18 -1
- package/src/core/agents/refactor.md +19 -125
- package/src/core/agents/research.md +3 -1
- package/src/core/agents/rlm-subcore.md +202 -0
- package/src/core/agents/security.md +7 -125
- package/src/core/agents/testing.md +20 -125
- package/src/core/agents/ui.md +14 -135
- package/src/core/commands/adr/list.md +20 -0
- package/src/core/commands/adr/update.md +24 -1
- package/src/core/commands/adr/view.md +23 -1
- package/src/core/commands/adr.md +2 -2
- package/src/core/commands/agent.md +11 -1
- package/src/core/commands/assign.md +15 -6
- package/src/core/commands/auto.md +11 -1
- package/src/core/commands/babysit.md +15 -4
- package/src/core/commands/baseline.md +11 -1
- package/src/core/commands/batch.md +11 -1
- package/src/core/commands/blockers.md +11 -1
- package/src/core/commands/board.md +11 -1
- package/src/core/commands/changelog.md +11 -0
- package/src/core/commands/choose.md +16 -1
- package/src/core/commands/ci.md +11 -1
- package/src/core/commands/configure.md +73 -2
- package/src/core/commands/context/export.md +8 -0
- package/src/core/commands/context/full.md +8 -0
- package/src/core/commands/context/note.md +8 -0
- package/src/core/commands/debt.md +11 -0
- package/src/core/commands/deploy.md +10 -0
- package/src/core/commands/deps.md +11 -1
- package/src/core/commands/diagnose.md +10 -0
- package/src/core/commands/docs.md +12 -2
- package/src/core/commands/epic/list.md +20 -0
- package/src/core/commands/epic/view.md +25 -0
- package/src/core/commands/epic.md +5 -6
- package/src/core/commands/feedback.md +11 -0
- package/src/core/commands/handoff.md +12 -2
- package/src/core/commands/help.md +10 -0
- package/src/core/commands/ideate.md +10 -0
- package/src/core/commands/impact.md +11 -1
- package/src/core/commands/metrics.md +11 -1
- package/src/core/commands/multi-expert.md +11 -1
- package/src/core/commands/packages.md +11 -0
- package/src/core/commands/pr.md +10 -0
- package/src/core/commands/readme-sync.md +10 -5
- package/src/core/commands/research/analyze.md +60 -3
- package/src/core/commands/research/ask.md +9 -1
- package/src/core/commands/research/import.md +8 -0
- package/src/core/commands/research/list.md +8 -0
- package/src/core/commands/research/synthesize.md +9 -1
- package/src/core/commands/research/view.md +8 -0
- package/src/core/commands/retro.md +12 -2
- package/src/core/commands/review.md +11 -1
- package/src/core/commands/rlm.md +363 -0
- package/src/core/commands/roadmap/analyze.md +1 -1
- package/src/core/commands/rpi.md +9 -1
- package/src/core/commands/session/cleanup.md +250 -0
- package/src/core/commands/session/end.md +10 -0
- package/src/core/commands/session/history.md +11 -1
- package/src/core/commands/session/init.md +10 -0
- package/src/core/commands/session/new.md +113 -13
- package/src/core/commands/session/resume.md +10 -0
- package/src/core/commands/session/spawn.md +8 -0
- package/src/core/commands/session/status.md +10 -0
- package/src/core/commands/skill/create.md +1 -1
- package/src/core/commands/skill/delete.md +11 -1
- package/src/core/commands/skill/edit.md +11 -1
- package/src/core/commands/skill/test.md +11 -1
- package/src/core/commands/skill/upgrade.md +11 -1
- package/src/core/commands/sprint.md +14 -3
- package/src/core/commands/status.md +15 -6
- package/src/core/commands/story/list.md +23 -0
- package/src/core/commands/story/view.md +24 -0
- package/src/core/commands/story.md +4 -5
- package/src/core/commands/template.md +10 -0
- package/src/core/commands/tests.md +10 -0
- package/src/core/commands/update.md +10 -0
- package/src/core/commands/validate-expertise.md +10 -1
- package/src/core/commands/velocity.md +11 -1
- package/src/core/commands/verify.md +13 -1
- package/src/core/commands/whats-new.md +8 -0
- package/src/core/commands/workflow.md +16 -1
- package/src/core/templates/agent-coordination-pattern.md +38 -0
- package/src/core/templates/agileflow-metadata.json +25 -0
- package/src/core/templates/preserve-rules-common.md +107 -0
- package/src/core/templates/preserve-rules.json +42 -0
- package/src/core/templates/proactive-action-spec.md +29 -0
- package/src/core/templates/quality-gate-priorities.md +34 -0
- package/src/core/templates/session-harness-protocol.md +128 -0
- package/tools/cli/lib/content-injector.js +336 -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(
|
|
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',
|
|
617
|
+
proc.stdout.on('data', data => {
|
|
459
618
|
stdout += data.toString();
|
|
460
619
|
});
|
|
461
620
|
|
|
462
|
-
proc.stderr.on('data',
|
|
621
|
+
proc.stderr.on('data', data => {
|
|
463
622
|
stderr += data.toString();
|
|
464
623
|
});
|
|
465
624
|
|
|
466
|
-
proc.on('error',
|
|
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(
|
|
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(
|
|
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();
|
|
@@ -1300,16 +1465,23 @@ function main() {
|
|
|
1300
1465
|
if (options.timeout) {
|
|
1301
1466
|
options.timeout = parseInt(options.timeout, 10);
|
|
1302
1467
|
if (isNaN(options.timeout) || options.timeout < 1000) {
|
|
1303
|
-
console.log(
|
|
1468
|
+
console.log(
|
|
1469
|
+
JSON.stringify({
|
|
1470
|
+
success: false,
|
|
1471
|
+
error: 'Timeout must be a number >= 1000 (milliseconds)',
|
|
1472
|
+
})
|
|
1473
|
+
);
|
|
1304
1474
|
return;
|
|
1305
1475
|
}
|
|
1306
1476
|
}
|
|
1307
1477
|
// Handle async createSession
|
|
1308
|
-
createSession(options)
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1478
|
+
createSession(options)
|
|
1479
|
+
.then(result => {
|
|
1480
|
+
console.log(JSON.stringify(result));
|
|
1481
|
+
})
|
|
1482
|
+
.catch(err => {
|
|
1483
|
+
console.log(JSON.stringify({ success: false, error: err.message }));
|
|
1484
|
+
});
|
|
1313
1485
|
break;
|
|
1314
1486
|
}
|
|
1315
1487
|
|
|
@@ -1361,6 +1533,17 @@ function main() {
|
|
|
1361
1533
|
break;
|
|
1362
1534
|
}
|
|
1363
1535
|
|
|
1536
|
+
case 'health': {
|
|
1537
|
+
// Get health status for all sessions
|
|
1538
|
+
// Usage: health [staleDays] [--detailed]
|
|
1539
|
+
const staleDaysArg = args.find((a) => /^\d+$/.test(a));
|
|
1540
|
+
const staleDays = staleDaysArg ? parseInt(staleDaysArg, 10) : 7;
|
|
1541
|
+
const detailed = args.includes('--detailed');
|
|
1542
|
+
const health = getSessionsHealth({ staleDays, detailed });
|
|
1543
|
+
console.log(JSON.stringify(health));
|
|
1544
|
+
break;
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1364
1547
|
case 'get': {
|
|
1365
1548
|
const sessionId = args[1];
|
|
1366
1549
|
if (!sessionId) {
|
|
@@ -64,16 +64,24 @@ 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 { init = false, dangerous = false, prompt = null, claudeArgs = null, noClaude = false } = options;
|
|
68
68
|
const parts = [`cd "${sessionPath}"`];
|
|
69
69
|
|
|
70
70
|
if (init) {
|
|
71
71
|
parts.push('claude init --yes 2>/dev/null || true');
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
// If noClaude is true, just return cd command (no claude startup)
|
|
75
|
+
if (noClaude) {
|
|
76
|
+
return parts.join(' && ');
|
|
77
|
+
}
|
|
78
|
+
|
|
74
79
|
let claudeCmd = 'claude';
|
|
75
80
|
if (dangerous) {
|
|
76
|
-
claudeCmd = 'claude --
|
|
81
|
+
claudeCmd = 'claude --dangerously-skip-permissions';
|
|
82
|
+
} else if (claudeArgs) {
|
|
83
|
+
// Custom claude arguments (e.g., --permission-mode acceptEdits)
|
|
84
|
+
claudeCmd = `claude ${claudeArgs}`;
|
|
77
85
|
}
|
|
78
86
|
|
|
79
87
|
if (prompt) {
|
|
@@ -215,7 +223,7 @@ function getReadyStoriesFromEpic(epicId) {
|
|
|
215
223
|
/**
|
|
216
224
|
* Main spawn command
|
|
217
225
|
*/
|
|
218
|
-
function spawn(args) {
|
|
226
|
+
async function spawn(args) {
|
|
219
227
|
const count = args.count ? parseInt(args.count, 10) : null;
|
|
220
228
|
const branches = args.branches ? args.branches.split(',').map(b => b.trim()) : null;
|
|
221
229
|
const fromEpic = args['from-epic'] || args.fromEpic;
|
|
@@ -223,6 +231,8 @@ function spawn(args) {
|
|
|
223
231
|
const init = args.init || false;
|
|
224
232
|
const dangerous = args.dangerous || false;
|
|
225
233
|
const prompt = args.prompt || null;
|
|
234
|
+
const claudeArgs = args['claude-args'] || args.claudeArgs || null;
|
|
235
|
+
const noClaude = args['no-claude'] || args.noClaude || false;
|
|
226
236
|
|
|
227
237
|
// Determine what to create
|
|
228
238
|
let sessionsToCreate = [];
|
|
@@ -276,7 +286,7 @@ function spawn(args) {
|
|
|
276
286
|
|
|
277
287
|
const createdSessions = [];
|
|
278
288
|
for (const sessionSpec of sessionsToCreate) {
|
|
279
|
-
const result = sessionManager.createSession({
|
|
289
|
+
const result = await sessionManager.createSession({
|
|
280
290
|
nickname: sessionSpec.nickname,
|
|
281
291
|
branch: sessionSpec.branch,
|
|
282
292
|
});
|
|
@@ -311,10 +321,10 @@ function spawn(args) {
|
|
|
311
321
|
// Spawn in tmux or output commands
|
|
312
322
|
if (noTmux) {
|
|
313
323
|
// User explicitly requested manual mode
|
|
314
|
-
outputCommands(createdSessions, { init, dangerous, prompt });
|
|
324
|
+
outputCommands(createdSessions, { init, dangerous, prompt, claudeArgs, noClaude });
|
|
315
325
|
} else if (hasTmux()) {
|
|
316
326
|
// Tmux available - use it
|
|
317
|
-
const tmuxResult = spawnInTmux(createdSessions, { init, dangerous, prompt });
|
|
327
|
+
const tmuxResult = spawnInTmux(createdSessions, { init, dangerous, prompt, claudeArgs, noClaude });
|
|
318
328
|
|
|
319
329
|
if (tmuxResult.success) {
|
|
320
330
|
console.log(success(`\n✅ Tmux session created: ${tmuxResult.sessionName}`));
|
|
@@ -326,7 +336,7 @@ function spawn(args) {
|
|
|
326
336
|
console.log('');
|
|
327
337
|
} else {
|
|
328
338
|
console.error(error(`Failed to create tmux session: ${tmuxResult.error}`));
|
|
329
|
-
outputCommands(createdSessions, { init, dangerous, prompt });
|
|
339
|
+
outputCommands(createdSessions, { init, dangerous, prompt, claudeArgs, noClaude });
|
|
330
340
|
}
|
|
331
341
|
} else {
|
|
332
342
|
// Tmux NOT available - require it or use --no-tmux
|
|
@@ -392,9 +402,12 @@ function list() {
|
|
|
392
402
|
/**
|
|
393
403
|
* Add a new window to an existing tmux session
|
|
394
404
|
*/
|
|
395
|
-
function addWindow(args) {
|
|
405
|
+
async function addWindow(args) {
|
|
396
406
|
const nickname = args.nickname || args.name || null;
|
|
397
407
|
const branch = args.branch || null;
|
|
408
|
+
const dangerous = args.dangerous || false;
|
|
409
|
+
const claudeArgs = args['claude-args'] || args.claudeArgs || null;
|
|
410
|
+
const noClaude = args['no-claude'] || args.noClaude || false;
|
|
398
411
|
|
|
399
412
|
// Check if we're inside a tmux session
|
|
400
413
|
const tmuxEnv = process.env.TMUX;
|
|
@@ -427,7 +440,7 @@ function addWindow(args) {
|
|
|
427
440
|
branch: branch || `parallel-${Date.now()}`,
|
|
428
441
|
};
|
|
429
442
|
|
|
430
|
-
const result = sessionManager.createSession({
|
|
443
|
+
const result = await sessionManager.createSession({
|
|
431
444
|
nickname: sessionSpec.nickname,
|
|
432
445
|
branch: sessionSpec.branch,
|
|
433
446
|
});
|
|
@@ -438,7 +451,7 @@ function addWindow(args) {
|
|
|
438
451
|
}
|
|
439
452
|
|
|
440
453
|
const windowName = sessionSpec.nickname;
|
|
441
|
-
const cmd = buildClaudeCommand(result.path, {});
|
|
454
|
+
const cmd = buildClaudeCommand(result.path, { dangerous, claudeArgs, noClaude });
|
|
442
455
|
|
|
443
456
|
// Create new window in current tmux session
|
|
444
457
|
const newWindowResult = spawnSync(
|
|
@@ -551,7 +564,9 @@ ${c.cyan}SPAWN OPTIONS:${c.reset}
|
|
|
551
564
|
--branches "a,b,c" Create worktrees for specific branch names
|
|
552
565
|
--from-epic EP-XXX Create worktrees for ready stories in epic
|
|
553
566
|
--init Run 'claude init' in each worktree
|
|
554
|
-
--dangerous Use --
|
|
567
|
+
--dangerous Use --dangerously-skip-permissions
|
|
568
|
+
--claude-args "..." Custom arguments for claude command
|
|
569
|
+
--no-claude Create worktree but don't start claude
|
|
555
570
|
--no-tmux Output commands without spawning in tmux
|
|
556
571
|
--prompt "TEXT" Initial prompt to send to each Claude instance
|
|
557
572
|
|
|
@@ -575,6 +590,9 @@ ${c.cyan}ADD-WINDOW OPTIONS:${c.reset}
|
|
|
575
590
|
--name NAME Name for the new session/window
|
|
576
591
|
--nickname NAME Alias for --name
|
|
577
592
|
--branch BRANCH Use specific branch name
|
|
593
|
+
--dangerous Use --dangerously-skip-permissions
|
|
594
|
+
--claude-args "..." Custom arguments for claude command
|
|
595
|
+
--no-claude Create worktree but don't start claude
|
|
578
596
|
|
|
579
597
|
${c.cyan}ADD-WINDOW EXAMPLES:${c.reset}
|
|
580
598
|
${dim('# Add window with auto-generated name (when in tmux)')}
|
|
@@ -582,6 +600,15 @@ ${c.cyan}ADD-WINDOW EXAMPLES:${c.reset}
|
|
|
582
600
|
|
|
583
601
|
${dim('# Add named window')}
|
|
584
602
|
node scripts/spawn-parallel.js add-window --name auth
|
|
603
|
+
|
|
604
|
+
${dim('# Add window without starting claude')}
|
|
605
|
+
node scripts/spawn-parallel.js add-window --name research --no-claude
|
|
606
|
+
|
|
607
|
+
${dim('# Add window with skip permissions')}
|
|
608
|
+
node scripts/spawn-parallel.js add-window --name trusted --dangerous
|
|
609
|
+
|
|
610
|
+
${dim('# Add window with custom claude args')}
|
|
611
|
+
node scripts/spawn-parallel.js add-window --name safe --claude-args "--permission-mode acceptEdits"
|
|
585
612
|
`);
|
|
586
613
|
}
|
|
587
614
|
|
|
@@ -616,16 +643,16 @@ function parseArgs(argv) {
|
|
|
616
643
|
/**
|
|
617
644
|
* Main entry point
|
|
618
645
|
*/
|
|
619
|
-
function main() {
|
|
646
|
+
async function main() {
|
|
620
647
|
const { command, args } = parseArgs(process.argv.slice(2));
|
|
621
648
|
|
|
622
649
|
switch (command) {
|
|
623
650
|
case 'spawn':
|
|
624
|
-
spawn(args);
|
|
651
|
+
await spawn(args);
|
|
625
652
|
break;
|
|
626
653
|
case 'add-window':
|
|
627
654
|
case 'add':
|
|
628
|
-
addWindow(args);
|
|
655
|
+
await addWindow(args);
|
|
629
656
|
break;
|
|
630
657
|
case 'list':
|
|
631
658
|
list();
|
|
@@ -189,131 +189,8 @@ BOUNDARIES
|
|
|
189
189
|
- Always prioritize real user needs over technical convenience
|
|
190
190
|
|
|
191
191
|
|
|
192
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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)**
|