agileflow 2.99.8 → 3.0.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/lib/cache-provider.js +155 -0
- package/lib/codebase-indexer.js +1 -1
- package/lib/content-sanitizer.js +1 -0
- package/lib/dashboard-protocol.js +25 -0
- package/lib/dashboard-server.js +184 -133
- package/lib/errors.js +18 -0
- package/lib/file-cache.js +1 -1
- package/lib/flag-detection.js +11 -20
- package/lib/git-operations.js +15 -33
- package/lib/merge-operations.js +40 -34
- package/lib/process-executor.js +199 -0
- package/lib/registry-cache.js +13 -47
- package/lib/skill-loader.js +206 -0
- package/lib/smart-json-file.js +2 -4
- package/package.json +1 -1
- package/scripts/agileflow-configure.js +13 -12
- package/scripts/agileflow-statusline.sh +30 -0
- package/scripts/agileflow-welcome.js +181 -212
- package/scripts/auto-self-improve.js +3 -3
- package/scripts/claude-smart.sh +67 -0
- package/scripts/claude-tmux.sh +248 -161
- package/scripts/damage-control-multi-agent.js +227 -0
- package/scripts/lib/bus-utils.js +471 -0
- package/scripts/lib/configure-detect.js +5 -6
- package/scripts/lib/configure-features.js +44 -0
- package/scripts/lib/configure-repair.js +5 -6
- package/scripts/lib/configure-utils.js +2 -3
- package/scripts/lib/context-formatter.js +87 -8
- package/scripts/lib/damage-control-utils.js +37 -3
- package/scripts/lib/file-lock.js +392 -0
- package/scripts/lib/ideation-index.js +2 -5
- package/scripts/lib/lifecycle-detector.js +123 -0
- package/scripts/lib/process-cleanup.js +55 -81
- package/scripts/lib/scale-detector.js +357 -0
- package/scripts/lib/signal-detectors.js +779 -0
- package/scripts/lib/story-state-machine.js +1 -1
- package/scripts/lib/sync-ideation-status.js +2 -3
- package/scripts/lib/task-registry.js +7 -1
- package/scripts/lib/team-events.js +357 -0
- package/scripts/messaging-bridge.js +79 -36
- package/scripts/migrate-ideation-index.js +37 -14
- package/scripts/obtain-context.js +37 -19
- package/scripts/ralph-loop.js +3 -4
- package/scripts/smart-detect.js +390 -0
- package/scripts/team-manager.js +174 -30
- package/src/core/commands/audit.md +13 -11
- package/src/core/commands/babysit.md +162 -115
- package/src/core/commands/changelog.md +21 -4
- package/src/core/commands/configure.md +105 -2
- package/src/core/commands/debt.md +12 -2
- package/src/core/commands/feedback.md +7 -6
- package/src/core/commands/ideate/history.md +1 -1
- package/src/core/commands/ideate/new.md +5 -5
- package/src/core/commands/logic/audit.md +2 -2
- package/src/core/commands/pr.md +7 -6
- package/src/core/commands/research/analyze.md +28 -20
- package/src/core/commands/research/ask.md +43 -0
- package/src/core/commands/research/import.md +29 -21
- package/src/core/commands/research/list.md +8 -7
- package/src/core/commands/research/synthesize.md +356 -20
- package/src/core/commands/research/view.md +8 -5
- package/src/core/commands/review.md +24 -6
- package/src/core/commands/skill/create.md +34 -0
- package/tools/cli/lib/docs-setup.js +4 -0
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
const fs = require('fs');
|
|
15
15
|
const path = require('path');
|
|
16
|
-
const {
|
|
16
|
+
const { executeCommandSync, git, spawnBackground } = require('../lib/process-executor');
|
|
17
17
|
|
|
18
18
|
// Shared utilities
|
|
19
19
|
const { c, box } = require('../lib/colors');
|
|
@@ -30,23 +30,7 @@ const { readJSONCached, readFileCached } = require('../lib/file-cache');
|
|
|
30
30
|
// Session manager path (relative to script location)
|
|
31
31
|
const SESSION_MANAGER_PATH = path.join(__dirname, 'session-manager.js');
|
|
32
32
|
|
|
33
|
-
//
|
|
34
|
-
let storyClaiming;
|
|
35
|
-
try {
|
|
36
|
-
storyClaiming = require('./lib/story-claiming.js');
|
|
37
|
-
} catch (e) {
|
|
38
|
-
// Story claiming not available
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Story state machine (for epic completion check)
|
|
42
|
-
let storyStateMachine;
|
|
43
|
-
try {
|
|
44
|
-
storyStateMachine = require('./lib/story-state-machine.js');
|
|
45
|
-
} catch (e) {
|
|
46
|
-
// Story state machine not available
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Hook metrics module
|
|
33
|
+
// Hook metrics module (kept at top level - needed early for timer)
|
|
50
34
|
let hookMetrics;
|
|
51
35
|
try {
|
|
52
36
|
hookMetrics = require('./lib/hook-metrics.js');
|
|
@@ -54,58 +38,9 @@ try {
|
|
|
54
38
|
// Hook metrics not available
|
|
55
39
|
}
|
|
56
40
|
|
|
57
|
-
// File tracking module
|
|
58
|
-
let fileTracking;
|
|
59
|
-
try {
|
|
60
|
-
fileTracking = require('./lib/file-tracking.js');
|
|
61
|
-
} catch (e) {
|
|
62
|
-
// File tracking not available
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Ideation sync module
|
|
66
|
-
let syncIdeationStatus;
|
|
67
|
-
try {
|
|
68
|
-
syncIdeationStatus = require('./lib/sync-ideation-status.js');
|
|
69
|
-
} catch (e) {
|
|
70
|
-
// Ideation sync not available
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Automation registry and runner
|
|
74
|
-
let automationRegistry, automationRunner;
|
|
75
|
-
try {
|
|
76
|
-
automationRegistry = require('./lib/automation-registry.js');
|
|
77
|
-
automationRunner = require('./lib/automation-runner.js');
|
|
78
|
-
} catch (e) {
|
|
79
|
-
// Automation system not available
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Update checker module
|
|
83
|
-
let updateChecker;
|
|
84
|
-
try {
|
|
85
|
-
updateChecker = require('./check-update.js');
|
|
86
|
-
} catch (e) {
|
|
87
|
-
// Update checker not available
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Process cleanup module (duplicate Claude detection)
|
|
91
|
-
let processCleanup;
|
|
92
|
-
try {
|
|
93
|
-
processCleanup = require('./lib/process-cleanup.js');
|
|
94
|
-
} catch (e) {
|
|
95
|
-
// Process cleanup not available
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Feature flags module (Agent Teams detection)
|
|
99
|
-
let featureFlags;
|
|
100
|
-
try {
|
|
101
|
-
featureFlags = require('../lib/feature-flags');
|
|
102
|
-
} catch (e) {
|
|
103
|
-
// Feature flags not available
|
|
104
|
-
}
|
|
105
|
-
|
|
106
41
|
/**
|
|
107
42
|
* PERFORMANCE OPTIMIZATION: Load all project files using LRU cache
|
|
108
|
-
* Uses file-cache module for automatic caching with
|
|
43
|
+
* Uses file-cache module for automatic caching with 15s TTL.
|
|
109
44
|
* Files are cached across script invocations within TTL window.
|
|
110
45
|
* Estimated savings: 60-120ms on cache hits
|
|
111
46
|
*/
|
|
@@ -177,17 +112,16 @@ function detectPlatform() {
|
|
|
177
112
|
* Returns object with availability info and platform-specific install suggestion
|
|
178
113
|
*/
|
|
179
114
|
function checkTmuxAvailability() {
|
|
180
|
-
|
|
181
|
-
|
|
115
|
+
const result = executeCommandSync('which', ['tmux'], { fallback: null });
|
|
116
|
+
if (result.data !== null) {
|
|
182
117
|
return { available: true };
|
|
183
|
-
} catch (e) {
|
|
184
|
-
const platform = detectPlatform();
|
|
185
|
-
return {
|
|
186
|
-
available: false,
|
|
187
|
-
platform,
|
|
188
|
-
noSudoCmd: 'conda install -c conda-forge tmux',
|
|
189
|
-
};
|
|
190
118
|
}
|
|
119
|
+
const platform = detectPlatform();
|
|
120
|
+
return {
|
|
121
|
+
available: false,
|
|
122
|
+
platform,
|
|
123
|
+
noSudoCmd: 'conda install -c conda-forge tmux',
|
|
124
|
+
};
|
|
191
125
|
}
|
|
192
126
|
|
|
193
127
|
/**
|
|
@@ -196,21 +130,11 @@ function checkTmuxAvailability() {
|
|
|
196
130
|
* Estimated savings: 20-40ms
|
|
197
131
|
*/
|
|
198
132
|
function getGitInfo(rootDir) {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
);
|
|
205
|
-
const lines = output.trim().split('\n');
|
|
206
|
-
return {
|
|
207
|
-
branch: lines[0] || 'unknown',
|
|
208
|
-
commit: lines[1] || 'unknown',
|
|
209
|
-
lastCommit: lines[2] || '',
|
|
210
|
-
};
|
|
211
|
-
} catch (e) {
|
|
212
|
-
return { branch: 'unknown', commit: 'unknown', lastCommit: '' };
|
|
213
|
-
}
|
|
133
|
+
const opts = { cwd: rootDir, timeout: 5000, fallback: 'unknown' };
|
|
134
|
+
const branch = git(['branch', '--show-current'], opts).data;
|
|
135
|
+
const commit = git(['rev-parse', '--short', 'HEAD'], opts).data;
|
|
136
|
+
const lastCommit = git(['log', '-1', '--format=%s'], { ...opts, fallback: '' }).data;
|
|
137
|
+
return { branch, commit, lastCommit };
|
|
214
138
|
}
|
|
215
139
|
|
|
216
140
|
function getProjectInfo(rootDir, cache = null) {
|
|
@@ -376,15 +300,13 @@ function runArchival(rootDir, cache = null) {
|
|
|
376
300
|
|
|
377
301
|
if (toArchiveCount > 0) {
|
|
378
302
|
// Run archival
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
stdio: 'pipe',
|
|
384
|
-
});
|
|
303
|
+
const archiveResult = executeCommandSync('bash', ['scripts/archive-completed-stories.sh'], {
|
|
304
|
+
cwd: rootDir,
|
|
305
|
+
});
|
|
306
|
+
if (archiveResult.ok) {
|
|
385
307
|
result.archived = toArchiveCount;
|
|
386
308
|
result.remaining -= toArchiveCount;
|
|
387
|
-
}
|
|
309
|
+
}
|
|
388
310
|
}
|
|
389
311
|
} catch (e) {}
|
|
390
312
|
|
|
@@ -492,69 +414,68 @@ function checkParallelSessions(rootDir) {
|
|
|
492
414
|
// Try to use combined full-status command (saves ~200ms vs 3 separate calls)
|
|
493
415
|
const scriptPath = fs.existsSync(managerPath) ? managerPath : SESSION_MANAGER_PATH;
|
|
494
416
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
});
|
|
502
|
-
const data = JSON.parse(fullStatusOutput);
|
|
503
|
-
|
|
504
|
-
result.registered = data.registered;
|
|
505
|
-
result.currentId = data.id;
|
|
506
|
-
result.otherActive = data.otherActive || 0;
|
|
507
|
-
result.cleaned = data.cleaned || 0;
|
|
508
|
-
result.cleanedSessions = data.cleanedSessions || [];
|
|
509
|
-
|
|
510
|
-
if (data.current) {
|
|
511
|
-
result.isMain = data.current.is_main === true;
|
|
512
|
-
result.nickname = data.current.nickname;
|
|
513
|
-
result.branch = data.current.branch;
|
|
514
|
-
result.sessionPath = data.current.path;
|
|
515
|
-
}
|
|
516
|
-
} catch (e) {
|
|
517
|
-
// Fall back to individual calls if full-status not available (older version)
|
|
417
|
+
// PERFORMANCE: Single subprocess call instead of 3 (register + count + status)
|
|
418
|
+
const fullStatusResult = executeCommandSync('node', [scriptPath, 'full-status'], {
|
|
419
|
+
cwd: rootDir, fallback: null,
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
if (fullStatusResult.data) {
|
|
518
423
|
try {
|
|
519
|
-
const
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
424
|
+
const data = JSON.parse(fullStatusResult.data);
|
|
425
|
+
result.registered = data.registered;
|
|
426
|
+
result.currentId = data.id;
|
|
427
|
+
result.otherActive = data.otherActive || 0;
|
|
428
|
+
result.cleaned = data.cleaned || 0;
|
|
429
|
+
result.cleanedSessions = data.cleanedSessions || [];
|
|
430
|
+
|
|
431
|
+
if (data.current) {
|
|
432
|
+
result.isMain = data.current.is_main === true;
|
|
433
|
+
result.nickname = data.current.nickname;
|
|
434
|
+
result.branch = data.current.branch;
|
|
435
|
+
result.sessionPath = data.current.path;
|
|
436
|
+
}
|
|
527
437
|
} catch (e) {
|
|
528
|
-
//
|
|
438
|
+
// JSON parse failed, fall through to individual calls
|
|
439
|
+
fullStatusResult.data = null;
|
|
529
440
|
}
|
|
441
|
+
}
|
|
530
442
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
443
|
+
if (!fullStatusResult.data) {
|
|
444
|
+
// Fall back to individual calls if full-status not available (older version)
|
|
445
|
+
const registerResult = executeCommandSync('node', [scriptPath, 'register'], {
|
|
446
|
+
cwd: rootDir, fallback: null,
|
|
447
|
+
});
|
|
448
|
+
if (registerResult.data) {
|
|
449
|
+
try {
|
|
450
|
+
const registerData = JSON.parse(registerResult.data);
|
|
451
|
+
result.registered = true;
|
|
452
|
+
result.currentId = registerData.id;
|
|
453
|
+
} catch (e) {}
|
|
541
454
|
}
|
|
542
455
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
456
|
+
const countResult = executeCommandSync('node', [scriptPath, 'count'], {
|
|
457
|
+
cwd: rootDir, fallback: null,
|
|
458
|
+
});
|
|
459
|
+
if (countResult.data) {
|
|
460
|
+
try {
|
|
461
|
+
const countData = JSON.parse(countResult.data);
|
|
462
|
+
result.otherActive = countData.count || 0;
|
|
463
|
+
} catch (e) {}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const statusCmdResult = executeCommandSync('node', [scriptPath, 'status'], {
|
|
467
|
+
cwd: rootDir, fallback: null,
|
|
468
|
+
});
|
|
469
|
+
if (statusCmdResult.data) {
|
|
470
|
+
try {
|
|
471
|
+
const statusData = JSON.parse(statusCmdResult.data);
|
|
472
|
+
if (statusData.current) {
|
|
473
|
+
result.isMain = statusData.current.is_main === true;
|
|
474
|
+
result.nickname = statusData.current.nickname;
|
|
475
|
+
result.branch = statusData.current.branch;
|
|
476
|
+
result.sessionPath = statusData.current.path;
|
|
477
|
+
}
|
|
478
|
+
} catch (e) {}
|
|
558
479
|
}
|
|
559
480
|
}
|
|
560
481
|
} catch (e) {
|
|
@@ -1008,6 +929,8 @@ async function checkUpdates() {
|
|
|
1008
929
|
changelog: [],
|
|
1009
930
|
};
|
|
1010
931
|
|
|
932
|
+
let updateChecker;
|
|
933
|
+
try { updateChecker = require('./check-update.js'); } catch (e) {}
|
|
1011
934
|
if (!updateChecker) return result;
|
|
1012
935
|
|
|
1013
936
|
try {
|
|
@@ -1076,42 +999,36 @@ function getChangelogEntries(version) {
|
|
|
1076
999
|
// DEPRECATED: Use spawnAutoUpdateInBackground() instead for non-blocking updates
|
|
1077
1000
|
async function runAutoUpdate(rootDir, fromVersion, toVersion) {
|
|
1078
1001
|
const runUpdate = () => {
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
cwd: rootDir,
|
|
1082
|
-
encoding: 'utf8',
|
|
1083
|
-
stdio: 'pipe',
|
|
1084
|
-
timeout: 120000, // 2 minute timeout
|
|
1002
|
+
return executeCommandSync('npx', ['agileflow@latest', 'update', '--force'], {
|
|
1003
|
+
cwd: rootDir, timeout: 120000,
|
|
1085
1004
|
});
|
|
1086
1005
|
};
|
|
1087
1006
|
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
runUpdate();
|
|
1007
|
+
console.log(
|
|
1008
|
+
`${c.skyBlue}Updating AgileFlow${c.reset} ${c.dim}v${fromVersion} → v${toVersion}${c.reset}`
|
|
1009
|
+
);
|
|
1010
|
+
const result = runUpdate();
|
|
1011
|
+
if (result.ok) {
|
|
1094
1012
|
console.log(`${c.mintGreen}✓ Update complete${c.reset}`);
|
|
1095
1013
|
return true;
|
|
1096
|
-
}
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
console.log(`${c.peach}Auto-update failed after cache clean${c.reset}`);
|
|
1107
|
-
console.log(`${c.dim} Run manually: npx agileflow update${c.reset}`);
|
|
1108
|
-
return false;
|
|
1109
|
-
}
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
// Check if this is a stale npm cache issue (ETARGET = version not found)
|
|
1017
|
+
if (result.error && (result.error.includes('ETARGET') || result.error.includes('notarget'))) {
|
|
1018
|
+
console.log(`${c.dim} Clearing npm cache and retrying...${c.reset}`);
|
|
1019
|
+
executeCommandSync('npm', ['cache', 'clean', '--force'], { timeout: 30000 });
|
|
1020
|
+
const retryResult = runUpdate();
|
|
1021
|
+
if (retryResult.ok) {
|
|
1022
|
+
console.log(`${c.mintGreen}✓ Update complete${c.reset}`);
|
|
1023
|
+
return true;
|
|
1110
1024
|
}
|
|
1111
|
-
console.log(`${c.peach}Auto-update failed${c.reset}`);
|
|
1025
|
+
console.log(`${c.peach}Auto-update failed after cache clean${c.reset}`);
|
|
1112
1026
|
console.log(`${c.dim} Run manually: npx agileflow update${c.reset}`);
|
|
1113
1027
|
return false;
|
|
1114
1028
|
}
|
|
1029
|
+
console.log(`${c.peach}Auto-update failed${c.reset}`);
|
|
1030
|
+
console.log(`${c.dim} Run manually: npx agileflow update${c.reset}`);
|
|
1031
|
+
return false;
|
|
1115
1032
|
}
|
|
1116
1033
|
|
|
1117
1034
|
/**
|
|
@@ -1123,8 +1040,6 @@ async function runAutoUpdate(rootDir, fromVersion, toVersion) {
|
|
|
1123
1040
|
* @param {string} toVersion - Target version
|
|
1124
1041
|
*/
|
|
1125
1042
|
function spawnAutoUpdateInBackground(rootDir, fromVersion, toVersion) {
|
|
1126
|
-
const { spawn } = require('child_process');
|
|
1127
|
-
|
|
1128
1043
|
// Track pending update in session-state.json
|
|
1129
1044
|
try {
|
|
1130
1045
|
const sessionStatePath = getSessionStatePath(rootDir);
|
|
@@ -1143,16 +1058,7 @@ function spawnAutoUpdateInBackground(rootDir, fromVersion, toVersion) {
|
|
|
1143
1058
|
}
|
|
1144
1059
|
|
|
1145
1060
|
// Create detached subprocess that survives parent exit
|
|
1146
|
-
|
|
1147
|
-
const child = spawn('npx', ['agileflow@latest', 'update', '--force'], {
|
|
1148
|
-
cwd: rootDir,
|
|
1149
|
-
detached: true,
|
|
1150
|
-
stdio: 'ignore', // Don't inherit stdout/stderr - prevents output after hook returns
|
|
1151
|
-
shell: true,
|
|
1152
|
-
});
|
|
1153
|
-
|
|
1154
|
-
// Allow parent to exit independently
|
|
1155
|
-
child.unref();
|
|
1061
|
+
spawnBackground('npx', ['agileflow@latest', 'update', '--force'], { cwd: rootDir });
|
|
1156
1062
|
|
|
1157
1063
|
console.log(`${c.dim} Auto-update starting in background...${c.reset}`);
|
|
1158
1064
|
}
|
|
@@ -1387,7 +1293,8 @@ function formatTable(
|
|
|
1387
1293
|
updateInfo = {},
|
|
1388
1294
|
expertise = {},
|
|
1389
1295
|
damageControl = {},
|
|
1390
|
-
agentTeamsInfo = {}
|
|
1296
|
+
agentTeamsInfo = {},
|
|
1297
|
+
scaleDetection = {}
|
|
1391
1298
|
) {
|
|
1392
1299
|
const W = 58; // inner width (total table = W + 2 = 60)
|
|
1393
1300
|
const R = W - 25; // right column width (33 chars) to match total of 60
|
|
@@ -1528,6 +1435,8 @@ function formatTable(
|
|
|
1528
1435
|
// System section (colorful labels like obtain-context)
|
|
1529
1436
|
if (archival.disabled) {
|
|
1530
1437
|
lines.push(row('Auto-archival', 'disabled', c.lavender, c.slate));
|
|
1438
|
+
} else if (archival.skippedByScale) {
|
|
1439
|
+
lines.push(row('Auto-archival', 'skipped (small project)', c.lavender, c.dim));
|
|
1531
1440
|
} else {
|
|
1532
1441
|
const archivalStatus =
|
|
1533
1442
|
archival.archived > 0 ? `archived ${archival.archived} stories` : `nothing to archive`;
|
|
@@ -1616,6 +1525,22 @@ function formatTable(
|
|
|
1616
1525
|
lines.push(row('Agent Teams', `${agentTeamsInfo.value}`, c.lavender, c.dim));
|
|
1617
1526
|
}
|
|
1618
1527
|
|
|
1528
|
+
// Scale detection (EP-0033)
|
|
1529
|
+
if (scaleDetection && scaleDetection.scale) {
|
|
1530
|
+
const scaleColors = {
|
|
1531
|
+
micro: c.cyan, small: c.teal, medium: c.mintGreen,
|
|
1532
|
+
large: c.peach, enterprise: c.coral,
|
|
1533
|
+
};
|
|
1534
|
+
const scaleIcons = {
|
|
1535
|
+
micro: '◦', small: '○', medium: '◎', large: '●', enterprise: '◉',
|
|
1536
|
+
};
|
|
1537
|
+
const scale = scaleDetection.scale;
|
|
1538
|
+
const icon = scaleIcons[scale] || '◎';
|
|
1539
|
+
const label = scale.charAt(0).toUpperCase() + scale.slice(1);
|
|
1540
|
+
const cacheNote = scaleDetection.fromCache ? '' : ` (${scaleDetection.detection_ms}ms)`;
|
|
1541
|
+
lines.push(row('Scale', `${icon} ${label}${cacheNote}`, c.lavender, scaleColors[scale] || c.dim));
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1619
1544
|
lines.push(divider());
|
|
1620
1545
|
|
|
1621
1546
|
// Current story (colorful like obtain-context)
|
|
@@ -1693,7 +1618,29 @@ async function main() {
|
|
|
1693
1618
|
// All fast operations - no network, no auto-update
|
|
1694
1619
|
// ============================================
|
|
1695
1620
|
const info = getProjectInfo(rootDir, cache);
|
|
1696
|
-
|
|
1621
|
+
|
|
1622
|
+
// Smart hook scheduling: skip archival for micro/small projects (EP-0033)
|
|
1623
|
+
// Scale detection is done early to inform hook scheduling
|
|
1624
|
+
let earlyScale = null;
|
|
1625
|
+
try {
|
|
1626
|
+
const scaleDetector = require('./lib/scale-detector');
|
|
1627
|
+
// Check cache only (fast path, no full detection yet)
|
|
1628
|
+
earlyScale = scaleDetector.detectScale({
|
|
1629
|
+
rootDir,
|
|
1630
|
+
statusJson: cache?.status,
|
|
1631
|
+
sessionState: cache?.sessionState,
|
|
1632
|
+
});
|
|
1633
|
+
} catch (e) {
|
|
1634
|
+
// Scale detection not available
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
const scaleRecommendations = earlyScale ? (() => {
|
|
1638
|
+
try { return require('./lib/scale-detector').getScaleRecommendations(earlyScale.scale); } catch { return null; }
|
|
1639
|
+
})() : null;
|
|
1640
|
+
|
|
1641
|
+
const archival = (scaleRecommendations && scaleRecommendations.skipArchival)
|
|
1642
|
+
? { ran: false, threshold: 0, archived: 0, remaining: 0, skippedByScale: true }
|
|
1643
|
+
: runArchival(rootDir, cache);
|
|
1697
1644
|
const session = clearActiveCommands(rootDir, cache);
|
|
1698
1645
|
const precompact = checkPreCompact(rootDir, cache);
|
|
1699
1646
|
const parallelSessions = checkParallelSessions(rootDir);
|
|
@@ -1702,7 +1649,12 @@ async function main() {
|
|
|
1702
1649
|
const expertise = getExpertiseCountFast(rootDir);
|
|
1703
1650
|
const damageControl = checkDamageControl(rootDir, cache);
|
|
1704
1651
|
|
|
1652
|
+
// Use early scale detection result (already computed for hook scheduling)
|
|
1653
|
+
const scaleDetection = earlyScale || { scale: 'medium' };
|
|
1654
|
+
|
|
1705
1655
|
// Agent Teams feature flag detection
|
|
1656
|
+
let featureFlags;
|
|
1657
|
+
try { featureFlags = require('../lib/feature-flags'); } catch (e) {}
|
|
1706
1658
|
let agentTeamsInfo = {};
|
|
1707
1659
|
if (featureFlags) {
|
|
1708
1660
|
try {
|
|
@@ -1794,7 +1746,8 @@ async function main() {
|
|
|
1794
1746
|
updateInfo,
|
|
1795
1747
|
expertise,
|
|
1796
1748
|
damageControl,
|
|
1797
|
-
agentTeamsInfo
|
|
1749
|
+
agentTeamsInfo,
|
|
1750
|
+
scaleDetection
|
|
1798
1751
|
)
|
|
1799
1752
|
);
|
|
1800
1753
|
|
|
@@ -1822,12 +1775,18 @@ async function main() {
|
|
|
1822
1775
|
}
|
|
1823
1776
|
|
|
1824
1777
|
// Mark current version as seen to track for next update
|
|
1778
|
+
let updateChecker;
|
|
1779
|
+
try { updateChecker = require('./check-update.js'); } catch (e) {}
|
|
1825
1780
|
if (freshUpdateInfo.justUpdated && updateChecker) {
|
|
1826
1781
|
updateChecker.markVersionSeen(info.version);
|
|
1827
1782
|
}
|
|
1828
|
-
} else
|
|
1783
|
+
} else {
|
|
1829
1784
|
// Mark current version as seen (for "just updated" case)
|
|
1830
|
-
updateChecker
|
|
1785
|
+
let updateChecker;
|
|
1786
|
+
try { updateChecker = require('./check-update.js'); } catch (e) {}
|
|
1787
|
+
if (updateChecker) {
|
|
1788
|
+
updateChecker.markVersionSeen(info.version);
|
|
1789
|
+
}
|
|
1831
1790
|
}
|
|
1832
1791
|
} catch (e) {
|
|
1833
1792
|
// Update check failed - continue without it (non-critical)
|
|
@@ -1910,13 +1869,12 @@ async function main() {
|
|
|
1910
1869
|
// === SESSION HEALTH WARNINGS ===
|
|
1911
1870
|
// Check for forgotten sessions with uncommitted changes, stale sessions, orphaned entries
|
|
1912
1871
|
try {
|
|
1913
|
-
const healthResult =
|
|
1914
|
-
|
|
1915
|
-
timeout: 10000,
|
|
1872
|
+
const healthResult = executeCommandSync('node', [SESSION_MANAGER_PATH, 'health'], {
|
|
1873
|
+
timeout: 10000, fallback: null,
|
|
1916
1874
|
});
|
|
1917
1875
|
|
|
1918
|
-
if (healthResult.
|
|
1919
|
-
const health = JSON.parse(healthResult.
|
|
1876
|
+
if (healthResult.data) {
|
|
1877
|
+
const health = JSON.parse(healthResult.data);
|
|
1920
1878
|
const hasIssues =
|
|
1921
1879
|
health.uncommitted.length > 0 ||
|
|
1922
1880
|
health.stale.length > 0 ||
|
|
@@ -1963,6 +1921,8 @@ async function main() {
|
|
|
1963
1921
|
|
|
1964
1922
|
// === DUPLICATE CLAUDE PROCESS DETECTION ===
|
|
1965
1923
|
// Check for multiple Claude processes in the same working directory
|
|
1924
|
+
let processCleanup;
|
|
1925
|
+
try { processCleanup = require('./lib/process-cleanup.js'); } catch (e) {}
|
|
1966
1926
|
if (processCleanup) {
|
|
1967
1927
|
try {
|
|
1968
1928
|
// Auto-kill is explicitly opt-in at runtime.
|
|
@@ -2015,6 +1975,8 @@ async function main() {
|
|
|
2015
1975
|
}
|
|
2016
1976
|
|
|
2017
1977
|
// Story claiming: cleanup stale claims and show warnings
|
|
1978
|
+
let storyClaiming;
|
|
1979
|
+
try { storyClaiming = require('./lib/story-claiming.js'); } catch (e) {}
|
|
2018
1980
|
if (storyClaiming) {
|
|
2019
1981
|
try {
|
|
2020
1982
|
// Clean up stale claims (dead PIDs, expired TTL)
|
|
@@ -2040,6 +2002,8 @@ async function main() {
|
|
|
2040
2002
|
}
|
|
2041
2003
|
|
|
2042
2004
|
// File tracking: cleanup stale touches and show overlap warnings
|
|
2005
|
+
let fileTracking;
|
|
2006
|
+
try { fileTracking = require('./lib/file-tracking.js'); } catch (e) {}
|
|
2043
2007
|
if (fileTracking) {
|
|
2044
2008
|
try {
|
|
2045
2009
|
// Clean up stale file touches (dead PIDs, expired TTL)
|
|
@@ -2063,6 +2027,8 @@ async function main() {
|
|
|
2063
2027
|
}
|
|
2064
2028
|
|
|
2065
2029
|
// Epic completion check: auto-complete epics where all stories are done
|
|
2030
|
+
let storyStateMachine;
|
|
2031
|
+
try { storyStateMachine = require('./lib/story-state-machine.js'); } catch (e) {}
|
|
2066
2032
|
if (storyStateMachine && cache.status) {
|
|
2067
2033
|
try {
|
|
2068
2034
|
const statusPath = getStatusPath(rootDir);
|
|
@@ -2091,6 +2057,8 @@ async function main() {
|
|
|
2091
2057
|
}
|
|
2092
2058
|
|
|
2093
2059
|
// Ideation sync: mark ideas as implemented when linked epics complete
|
|
2060
|
+
let syncIdeationStatus;
|
|
2061
|
+
try { syncIdeationStatus = require('./lib/sync-ideation-status.js'); } catch (e) {}
|
|
2094
2062
|
if (syncIdeationStatus) {
|
|
2095
2063
|
try {
|
|
2096
2064
|
const syncResult = syncIdeationStatus.syncImplementedIdeas(rootDir);
|
|
@@ -2105,6 +2073,13 @@ async function main() {
|
|
|
2105
2073
|
|
|
2106
2074
|
// === SCHEDULED AUTOMATIONS ===
|
|
2107
2075
|
// Check for and run due automations (non-blocking)
|
|
2076
|
+
let automationRegistry, automationRunner;
|
|
2077
|
+
try {
|
|
2078
|
+
automationRegistry = require('./lib/automation-registry.js');
|
|
2079
|
+
automationRunner = require('./lib/automation-runner.js');
|
|
2080
|
+
} catch (e) {
|
|
2081
|
+
// Automation system not available
|
|
2082
|
+
}
|
|
2108
2083
|
if (automationRegistry && automationRunner) {
|
|
2109
2084
|
try {
|
|
2110
2085
|
const registry = automationRegistry.getAutomationRegistry({ rootDir });
|
|
@@ -2125,17 +2100,11 @@ async function main() {
|
|
|
2125
2100
|
|
|
2126
2101
|
// Run due automations in background (spawn detached process)
|
|
2127
2102
|
// This prevents blocking the welcome hook
|
|
2128
|
-
const { spawn } = require('child_process');
|
|
2129
2103
|
const runnerScriptPath = path.join(__dirname, 'automation-run-due.js');
|
|
2130
2104
|
|
|
2131
2105
|
// Only spawn if the runner script exists
|
|
2132
2106
|
if (fs.existsSync(runnerScriptPath)) {
|
|
2133
|
-
|
|
2134
|
-
cwd: rootDir,
|
|
2135
|
-
detached: true,
|
|
2136
|
-
stdio: 'ignore',
|
|
2137
|
-
});
|
|
2138
|
-
child.unref();
|
|
2107
|
+
spawnBackground('node', [runnerScriptPath], { cwd: rootDir });
|
|
2139
2108
|
console.log(`${c.dim} Running in background...${c.reset}`);
|
|
2140
2109
|
} else {
|
|
2141
2110
|
console.log(`${c.slate} Run: ${c.skyBlue}/agileflow:automate ACTION=run-due${c.reset}`);
|
|
@@ -24,7 +24,7 @@ const { execFileSync } = require('child_process');
|
|
|
24
24
|
// Shared utilities
|
|
25
25
|
const { c } = require('../lib/colors');
|
|
26
26
|
const { getProjectRoot } = require('../lib/paths');
|
|
27
|
-
const { safeReadJSON, safeReadFile, safeWriteFile } = require('../lib/errors');
|
|
27
|
+
const { safeReadJSON, safeReadFile, safeWriteFile, tryOptional } = require('../lib/errors');
|
|
28
28
|
|
|
29
29
|
// Agents that have expertise files
|
|
30
30
|
const AGENTS_WITH_EXPERTISE = [
|
|
@@ -114,7 +114,7 @@ function getGitDiff(rootDir) {
|
|
|
114
114
|
// Get diff stats
|
|
115
115
|
let additions = 0;
|
|
116
116
|
let deletions = 0;
|
|
117
|
-
|
|
117
|
+
tryOptional(() => {
|
|
118
118
|
const stats = execFileSync('git', ['diff', '--shortstat', 'HEAD'], {
|
|
119
119
|
cwd: rootDir,
|
|
120
120
|
encoding: 'utf8',
|
|
@@ -124,7 +124,7 @@ function getGitDiff(rootDir) {
|
|
|
124
124
|
const delMatch = stats.match(/(\d+) deletion/);
|
|
125
125
|
if (addMatch) additions = parseInt(addMatch[1]);
|
|
126
126
|
if (delMatch) deletions = parseInt(delMatch[1]);
|
|
127
|
-
}
|
|
127
|
+
}, 'git diff stats');
|
|
128
128
|
|
|
129
129
|
return {
|
|
130
130
|
files: allFiles,
|