agileflow 3.0.1 → 3.0.2
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/lib/api-server.js +3 -2
- package/lib/flag-detection.js +4 -2
- package/lib/git-operations.js +4 -2
- package/lib/process-executor.js +24 -9
- package/lib/skill-loader.js +11 -3
- package/package.json +1 -1
- package/scripts/agileflow-welcome.js +65 -25
- package/scripts/claude-tmux.sh +9 -3
- package/scripts/damage-control-multi-agent.js +14 -10
- package/scripts/lib/bus-utils.js +3 -1
- package/scripts/lib/configure-detect.js +12 -9
- package/scripts/lib/configure-features.js +12 -7
- package/scripts/lib/configure-repair.js +6 -5
- package/scripts/lib/context-formatter.js +13 -3
- package/scripts/lib/damage-control-utils.js +5 -1
- package/scripts/lib/lifecycle-detector.js +5 -3
- package/scripts/lib/process-cleanup.js +8 -4
- package/scripts/lib/scale-detector.js +47 -8
- package/scripts/lib/signal-detectors.js +117 -59
- package/scripts/lib/task-registry.js +5 -1
- package/scripts/lib/team-events.js +4 -4
- package/scripts/messaging-bridge.js +7 -1
- package/scripts/ralph-loop.js +10 -8
- package/scripts/smart-detect.js +32 -11
- package/scripts/team-manager.js +1 -1
- package/scripts/tmux-task-name.sh +75 -0
- package/scripts/tmux-task-watcher.sh +177 -0
- package/src/core/commands/babysit.md +75 -42
- package/src/core/commands/blockers.md +7 -7
- package/src/core/commands/configure.md +5 -36
- package/src/core/commands/discovery/brief.md +363 -0
- package/src/core/commands/discovery/new.md +395 -0
- package/src/core/commands/ideate/new.md +5 -5
- package/src/core/commands/logic/audit.md +5 -5
- package/src/core/commands/review.md +7 -1
- package/src/core/commands/rpi.md +61 -26
- package/src/core/commands/sprint.md +7 -6
- package/src/core/templates/product-brief.md +136 -0
- package/tools/cli/installers/ide/claude-code.js +67 -2
- package/src/core/agents/configuration/archival.md +0 -350
- package/src/core/agents/configuration/attribution.md +0 -343
- package/src/core/agents/configuration/ci.md +0 -1103
- package/src/core/agents/configuration/damage-control.md +0 -375
- package/src/core/agents/configuration/git-config.md +0 -537
- package/src/core/agents/configuration/hooks.md +0 -623
- package/src/core/agents/configuration/precompact.md +0 -302
- package/src/core/agents/configuration/status-line.md +0 -557
- package/src/core/agents/configuration/verify.md +0 -618
- package/src/core/agents/configuration-damage-control.md +0 -259
- package/src/core/agents/configuration-visual-e2e.md +0 -339
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [3.0.2] - 2026-02-14
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Automatic tmux window naming and configuration agent consolidation
|
|
14
|
+
|
|
10
15
|
## [3.0.1] - 2026-02-13
|
|
11
16
|
|
|
12
17
|
### Fixed
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/agileflow)
|
|
6
6
|
[](docs/04-architecture/commands.md)
|
|
7
|
-
[](docs/04-architecture/subagents.md)
|
|
8
8
|
[](docs/04-architecture/skills.md)
|
|
9
9
|
|
|
10
10
|
**AI-driven agile development for Claude Code, Cursor, Windsurf, OpenAI Codex CLI, and more.** Combining Scrum, Kanban, ADRs, and docs-as-code principles into one framework-agnostic system.
|
|
@@ -66,7 +66,7 @@ AgileFlow combines three proven methodologies:
|
|
|
66
66
|
| Component | Count | Description |
|
|
67
67
|
|-----------|-------|-------------|
|
|
68
68
|
| [Commands](docs/04-architecture/commands.md) | 93 | Slash commands for agile workflows |
|
|
69
|
-
| [Agents/Experts](docs/04-architecture/subagents.md) |
|
|
69
|
+
| [Agents/Experts](docs/04-architecture/subagents.md) | 45 | Specialized agents with self-improving knowledge bases |
|
|
70
70
|
| [Skills](docs/04-architecture/skills.md) | Dynamic | Generated on-demand with `/agileflow:skill:create` |
|
|
71
71
|
|
|
72
72
|
---
|
|
@@ -77,7 +77,7 @@ Full documentation lives in [`docs/04-architecture/`](docs/04-architecture/):
|
|
|
77
77
|
|
|
78
78
|
### Reference
|
|
79
79
|
- [Commands](docs/04-architecture/commands.md) - All 93 slash commands
|
|
80
|
-
- [Agents/Experts](docs/04-architecture/subagents.md) -
|
|
80
|
+
- [Agents/Experts](docs/04-architecture/subagents.md) - 45 specialized agents with self-improving knowledge
|
|
81
81
|
- [Skills](docs/04-architecture/skills.md) - Dynamic skill generator with MCP integration
|
|
82
82
|
|
|
83
83
|
### Architecture
|
package/lib/api-server.js
CHANGED
|
@@ -71,7 +71,7 @@ class ApiCache {
|
|
|
71
71
|
* @returns {{ server: http.Server, options: Object, cache: ApiCache }}
|
|
72
72
|
*/
|
|
73
73
|
function createApiServer(options = {}) {
|
|
74
|
-
const port = options.port
|
|
74
|
+
const port = options.port != null ? options.port : DEFAULT_PORT;
|
|
75
75
|
const host = options.host || DEFAULT_HOST;
|
|
76
76
|
const rootDir = options.rootDir || getProjectRoot();
|
|
77
77
|
const cacheTTL = options.cacheTTL || 2000;
|
|
@@ -238,7 +238,8 @@ function startApiServer(serverInstance) {
|
|
|
238
238
|
});
|
|
239
239
|
|
|
240
240
|
server.listen(port, host, () => {
|
|
241
|
-
const
|
|
241
|
+
const actualPort = server.address().port;
|
|
242
|
+
const url = `http://${host}:${actualPort}`;
|
|
242
243
|
console.log(`[AgileFlow API] Server running at ${url}`);
|
|
243
244
|
console.log(`[AgileFlow API] Project root: ${options.rootDir}`);
|
|
244
245
|
resolve({
|
package/lib/flag-detection.js
CHANGED
|
@@ -219,7 +219,8 @@ function detectFromPs() {
|
|
|
219
219
|
// Get command line for this PID
|
|
220
220
|
let cmdline;
|
|
221
221
|
const cmdResult = executeCommandSync('ps', ['-p', String(pid), '-o', 'args='], {
|
|
222
|
-
timeout: 1000,
|
|
222
|
+
timeout: 1000,
|
|
223
|
+
fallback: null,
|
|
223
224
|
});
|
|
224
225
|
if (!cmdResult.ok || cmdResult.data === null) break;
|
|
225
226
|
cmdline = cmdResult.data;
|
|
@@ -239,7 +240,8 @@ function detectFromPs() {
|
|
|
239
240
|
|
|
240
241
|
// Get parent PID
|
|
241
242
|
const ppidResult = executeCommandSync('ps', ['-p', String(pid), '-o', 'ppid='], {
|
|
242
|
-
timeout: 1000,
|
|
243
|
+
timeout: 1000,
|
|
244
|
+
fallback: null,
|
|
243
245
|
});
|
|
244
246
|
if (!ppidResult.ok || ppidResult.data === null) break;
|
|
245
247
|
pid = parseInt(ppidResult.data, 10);
|
package/lib/git-operations.js
CHANGED
|
@@ -176,12 +176,14 @@ function getSessionPhase(session) {
|
|
|
176
176
|
try {
|
|
177
177
|
const mainBranch = getMainBranch(sessionPath);
|
|
178
178
|
const commitResult = git(['rev-list', '--count', `${mainBranch}..HEAD`], {
|
|
179
|
-
cwd: sessionPath,
|
|
179
|
+
cwd: sessionPath,
|
|
180
|
+
fallback: '0',
|
|
180
181
|
});
|
|
181
182
|
const commits = parseInt(commitResult.data, 10);
|
|
182
183
|
|
|
183
184
|
const statusResult = git(['status', '--porcelain'], {
|
|
184
|
-
cwd: sessionPath,
|
|
185
|
+
cwd: sessionPath,
|
|
186
|
+
fallback: '',
|
|
185
187
|
});
|
|
186
188
|
const status = statusResult.data;
|
|
187
189
|
|
package/lib/process-executor.js
CHANGED
|
@@ -91,13 +91,20 @@ function executeCommand(cmd, args = [], opts = {}) {
|
|
|
91
91
|
let stderr = '';
|
|
92
92
|
let timedOut = false;
|
|
93
93
|
|
|
94
|
-
const timer =
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
94
|
+
const timer =
|
|
95
|
+
timeout > 0
|
|
96
|
+
? setTimeout(() => {
|
|
97
|
+
timedOut = true;
|
|
98
|
+
proc.kill('SIGTERM');
|
|
99
|
+
}, timeout)
|
|
100
|
+
: null;
|
|
101
|
+
|
|
102
|
+
proc.stdout.on('data', chunk => {
|
|
103
|
+
stdout += chunk;
|
|
104
|
+
});
|
|
105
|
+
proc.stderr.on('data', chunk => {
|
|
106
|
+
stderr += chunk;
|
|
107
|
+
});
|
|
101
108
|
|
|
102
109
|
proc.on('error', err => {
|
|
103
110
|
if (timer) clearTimeout(timer);
|
|
@@ -115,7 +122,11 @@ function executeCommand(cmd, args = [], opts = {}) {
|
|
|
115
122
|
if (fallback !== undefined) {
|
|
116
123
|
resolve({ ok: true, data: fallback });
|
|
117
124
|
} else {
|
|
118
|
-
resolve({
|
|
125
|
+
resolve({
|
|
126
|
+
ok: false,
|
|
127
|
+
error: `Command timed out after ${timeout}ms: ${cmd}`,
|
|
128
|
+
exitCode: null,
|
|
129
|
+
});
|
|
119
130
|
}
|
|
120
131
|
return;
|
|
121
132
|
}
|
|
@@ -124,7 +135,11 @@ function executeCommand(cmd, args = [], opts = {}) {
|
|
|
124
135
|
if (fallback !== undefined) {
|
|
125
136
|
resolve({ ok: true, data: fallback });
|
|
126
137
|
} else {
|
|
127
|
-
const result = {
|
|
138
|
+
const result = {
|
|
139
|
+
ok: false,
|
|
140
|
+
error: `Command failed: ${cmd} ${args.join(' ')} (exit ${code})`,
|
|
141
|
+
exitCode: code,
|
|
142
|
+
};
|
|
128
143
|
if (captureStderr) {
|
|
129
144
|
result.stderr = trim ? stderr.trim() : stderr;
|
|
130
145
|
}
|
package/lib/skill-loader.js
CHANGED
|
@@ -57,13 +57,21 @@ function parseSkillFrontmatter(content) {
|
|
|
57
57
|
let value = trimmed.substring(colonIndex + 1).trim();
|
|
58
58
|
|
|
59
59
|
// Remove surrounding quotes
|
|
60
|
-
if (
|
|
61
|
-
|
|
60
|
+
if (
|
|
61
|
+
(value.startsWith('"') && value.endsWith('"')) ||
|
|
62
|
+
(value.startsWith("'") && value.endsWith("'"))
|
|
63
|
+
) {
|
|
62
64
|
value = value.slice(1, -1);
|
|
63
65
|
}
|
|
64
66
|
|
|
65
67
|
// Type coercion for known fields
|
|
66
|
-
if (
|
|
68
|
+
if (
|
|
69
|
+
key === 'version' ||
|
|
70
|
+
key === 'model' ||
|
|
71
|
+
key === 'category' ||
|
|
72
|
+
key === 'name' ||
|
|
73
|
+
key === 'type'
|
|
74
|
+
) {
|
|
67
75
|
result.metadata[key] = value;
|
|
68
76
|
} else if (value === 'true') {
|
|
69
77
|
result.metadata[key] = true;
|
package/package.json
CHANGED
|
@@ -416,7 +416,8 @@ function checkParallelSessions(rootDir) {
|
|
|
416
416
|
|
|
417
417
|
// PERFORMANCE: Single subprocess call instead of 3 (register + count + status)
|
|
418
418
|
const fullStatusResult = executeCommandSync('node', [scriptPath, 'full-status'], {
|
|
419
|
-
cwd: rootDir,
|
|
419
|
+
cwd: rootDir,
|
|
420
|
+
fallback: null,
|
|
420
421
|
});
|
|
421
422
|
|
|
422
423
|
if (fullStatusResult.data) {
|
|
@@ -443,7 +444,8 @@ function checkParallelSessions(rootDir) {
|
|
|
443
444
|
if (!fullStatusResult.data) {
|
|
444
445
|
// Fall back to individual calls if full-status not available (older version)
|
|
445
446
|
const registerResult = executeCommandSync('node', [scriptPath, 'register'], {
|
|
446
|
-
cwd: rootDir,
|
|
447
|
+
cwd: rootDir,
|
|
448
|
+
fallback: null,
|
|
447
449
|
});
|
|
448
450
|
if (registerResult.data) {
|
|
449
451
|
try {
|
|
@@ -454,7 +456,8 @@ function checkParallelSessions(rootDir) {
|
|
|
454
456
|
}
|
|
455
457
|
|
|
456
458
|
const countResult = executeCommandSync('node', [scriptPath, 'count'], {
|
|
457
|
-
cwd: rootDir,
|
|
459
|
+
cwd: rootDir,
|
|
460
|
+
fallback: null,
|
|
458
461
|
});
|
|
459
462
|
if (countResult.data) {
|
|
460
463
|
try {
|
|
@@ -464,7 +467,8 @@ function checkParallelSessions(rootDir) {
|
|
|
464
467
|
}
|
|
465
468
|
|
|
466
469
|
const statusCmdResult = executeCommandSync('node', [scriptPath, 'status'], {
|
|
467
|
-
cwd: rootDir,
|
|
470
|
+
cwd: rootDir,
|
|
471
|
+
fallback: null,
|
|
468
472
|
});
|
|
469
473
|
if (statusCmdResult.data) {
|
|
470
474
|
try {
|
|
@@ -930,7 +934,9 @@ async function checkUpdates() {
|
|
|
930
934
|
};
|
|
931
935
|
|
|
932
936
|
let updateChecker;
|
|
933
|
-
try {
|
|
937
|
+
try {
|
|
938
|
+
updateChecker = require('./check-update.js');
|
|
939
|
+
} catch (e) {}
|
|
934
940
|
if (!updateChecker) return result;
|
|
935
941
|
|
|
936
942
|
try {
|
|
@@ -1000,7 +1006,8 @@ function getChangelogEntries(version) {
|
|
|
1000
1006
|
async function runAutoUpdate(rootDir, fromVersion, toVersion) {
|
|
1001
1007
|
const runUpdate = () => {
|
|
1002
1008
|
return executeCommandSync('npx', ['agileflow@latest', 'update', '--force'], {
|
|
1003
|
-
cwd: rootDir,
|
|
1009
|
+
cwd: rootDir,
|
|
1010
|
+
timeout: 120000,
|
|
1004
1011
|
});
|
|
1005
1012
|
};
|
|
1006
1013
|
|
|
@@ -1528,17 +1535,26 @@ function formatTable(
|
|
|
1528
1535
|
// Scale detection (EP-0033)
|
|
1529
1536
|
if (scaleDetection && scaleDetection.scale) {
|
|
1530
1537
|
const scaleColors = {
|
|
1531
|
-
micro: c.cyan,
|
|
1532
|
-
|
|
1538
|
+
micro: c.cyan,
|
|
1539
|
+
small: c.teal,
|
|
1540
|
+
medium: c.mintGreen,
|
|
1541
|
+
large: c.peach,
|
|
1542
|
+
enterprise: c.coral,
|
|
1533
1543
|
};
|
|
1534
1544
|
const scaleIcons = {
|
|
1535
|
-
micro: '◦',
|
|
1545
|
+
micro: '◦',
|
|
1546
|
+
small: '○',
|
|
1547
|
+
medium: '◎',
|
|
1548
|
+
large: '●',
|
|
1549
|
+
enterprise: '◉',
|
|
1536
1550
|
};
|
|
1537
1551
|
const scale = scaleDetection.scale;
|
|
1538
1552
|
const icon = scaleIcons[scale] || '◎';
|
|
1539
1553
|
const label = scale.charAt(0).toUpperCase() + scale.slice(1);
|
|
1540
1554
|
const cacheNote = scaleDetection.fromCache ? '' : ` (${scaleDetection.detection_ms}ms)`;
|
|
1541
|
-
lines.push(
|
|
1555
|
+
lines.push(
|
|
1556
|
+
row('Scale', `${icon} ${label}${cacheNote}`, c.lavender, scaleColors[scale] || c.dim)
|
|
1557
|
+
);
|
|
1542
1558
|
}
|
|
1543
1559
|
|
|
1544
1560
|
lines.push(divider());
|
|
@@ -1634,13 +1650,20 @@ async function main() {
|
|
|
1634
1650
|
// Scale detection not available
|
|
1635
1651
|
}
|
|
1636
1652
|
|
|
1637
|
-
const scaleRecommendations = earlyScale
|
|
1638
|
-
|
|
1639
|
-
|
|
1653
|
+
const scaleRecommendations = earlyScale
|
|
1654
|
+
? (() => {
|
|
1655
|
+
try {
|
|
1656
|
+
return require('./lib/scale-detector').getScaleRecommendations(earlyScale.scale);
|
|
1657
|
+
} catch {
|
|
1658
|
+
return null;
|
|
1659
|
+
}
|
|
1660
|
+
})()
|
|
1661
|
+
: null;
|
|
1640
1662
|
|
|
1641
|
-
const archival =
|
|
1642
|
-
|
|
1643
|
-
|
|
1663
|
+
const archival =
|
|
1664
|
+
scaleRecommendations && scaleRecommendations.skipArchival
|
|
1665
|
+
? { ran: false, threshold: 0, archived: 0, remaining: 0, skippedByScale: true }
|
|
1666
|
+
: runArchival(rootDir, cache);
|
|
1644
1667
|
const session = clearActiveCommands(rootDir, cache);
|
|
1645
1668
|
const precompact = checkPreCompact(rootDir, cache);
|
|
1646
1669
|
const parallelSessions = checkParallelSessions(rootDir);
|
|
@@ -1654,7 +1677,9 @@ async function main() {
|
|
|
1654
1677
|
|
|
1655
1678
|
// Agent Teams feature flag detection
|
|
1656
1679
|
let featureFlags;
|
|
1657
|
-
try {
|
|
1680
|
+
try {
|
|
1681
|
+
featureFlags = require('../lib/feature-flags');
|
|
1682
|
+
} catch (e) {}
|
|
1658
1683
|
let agentTeamsInfo = {};
|
|
1659
1684
|
if (featureFlags) {
|
|
1660
1685
|
try {
|
|
@@ -1776,14 +1801,18 @@ async function main() {
|
|
|
1776
1801
|
|
|
1777
1802
|
// Mark current version as seen to track for next update
|
|
1778
1803
|
let updateChecker;
|
|
1779
|
-
try {
|
|
1804
|
+
try {
|
|
1805
|
+
updateChecker = require('./check-update.js');
|
|
1806
|
+
} catch (e) {}
|
|
1780
1807
|
if (freshUpdateInfo.justUpdated && updateChecker) {
|
|
1781
1808
|
updateChecker.markVersionSeen(info.version);
|
|
1782
1809
|
}
|
|
1783
1810
|
} else {
|
|
1784
1811
|
// Mark current version as seen (for "just updated" case)
|
|
1785
1812
|
let updateChecker;
|
|
1786
|
-
try {
|
|
1813
|
+
try {
|
|
1814
|
+
updateChecker = require('./check-update.js');
|
|
1815
|
+
} catch (e) {}
|
|
1787
1816
|
if (updateChecker) {
|
|
1788
1817
|
updateChecker.markVersionSeen(info.version);
|
|
1789
1818
|
}
|
|
@@ -1870,7 +1899,8 @@ async function main() {
|
|
|
1870
1899
|
// Check for forgotten sessions with uncommitted changes, stale sessions, orphaned entries
|
|
1871
1900
|
try {
|
|
1872
1901
|
const healthResult = executeCommandSync('node', [SESSION_MANAGER_PATH, 'health'], {
|
|
1873
|
-
timeout: 10000,
|
|
1902
|
+
timeout: 10000,
|
|
1903
|
+
fallback: null,
|
|
1874
1904
|
});
|
|
1875
1905
|
|
|
1876
1906
|
if (healthResult.data) {
|
|
@@ -1922,7 +1952,9 @@ async function main() {
|
|
|
1922
1952
|
// === DUPLICATE CLAUDE PROCESS DETECTION ===
|
|
1923
1953
|
// Check for multiple Claude processes in the same working directory
|
|
1924
1954
|
let processCleanup;
|
|
1925
|
-
try {
|
|
1955
|
+
try {
|
|
1956
|
+
processCleanup = require('./lib/process-cleanup.js');
|
|
1957
|
+
} catch (e) {}
|
|
1926
1958
|
if (processCleanup) {
|
|
1927
1959
|
try {
|
|
1928
1960
|
// Auto-kill is explicitly opt-in at runtime.
|
|
@@ -1976,7 +2008,9 @@ async function main() {
|
|
|
1976
2008
|
|
|
1977
2009
|
// Story claiming: cleanup stale claims and show warnings
|
|
1978
2010
|
let storyClaiming;
|
|
1979
|
-
try {
|
|
2011
|
+
try {
|
|
2012
|
+
storyClaiming = require('./lib/story-claiming.js');
|
|
2013
|
+
} catch (e) {}
|
|
1980
2014
|
if (storyClaiming) {
|
|
1981
2015
|
try {
|
|
1982
2016
|
// Clean up stale claims (dead PIDs, expired TTL)
|
|
@@ -2003,7 +2037,9 @@ async function main() {
|
|
|
2003
2037
|
|
|
2004
2038
|
// File tracking: cleanup stale touches and show overlap warnings
|
|
2005
2039
|
let fileTracking;
|
|
2006
|
-
try {
|
|
2040
|
+
try {
|
|
2041
|
+
fileTracking = require('./lib/file-tracking.js');
|
|
2042
|
+
} catch (e) {}
|
|
2007
2043
|
if (fileTracking) {
|
|
2008
2044
|
try {
|
|
2009
2045
|
// Clean up stale file touches (dead PIDs, expired TTL)
|
|
@@ -2028,7 +2064,9 @@ async function main() {
|
|
|
2028
2064
|
|
|
2029
2065
|
// Epic completion check: auto-complete epics where all stories are done
|
|
2030
2066
|
let storyStateMachine;
|
|
2031
|
-
try {
|
|
2067
|
+
try {
|
|
2068
|
+
storyStateMachine = require('./lib/story-state-machine.js');
|
|
2069
|
+
} catch (e) {}
|
|
2032
2070
|
if (storyStateMachine && cache.status) {
|
|
2033
2071
|
try {
|
|
2034
2072
|
const statusPath = getStatusPath(rootDir);
|
|
@@ -2058,7 +2096,9 @@ async function main() {
|
|
|
2058
2096
|
|
|
2059
2097
|
// Ideation sync: mark ideas as implemented when linked epics complete
|
|
2060
2098
|
let syncIdeationStatus;
|
|
2061
|
-
try {
|
|
2099
|
+
try {
|
|
2100
|
+
syncIdeationStatus = require('./lib/sync-ideation-status.js');
|
|
2101
|
+
} catch (e) {}
|
|
2062
2102
|
if (syncIdeationStatus) {
|
|
2063
2103
|
try {
|
|
2064
2104
|
const syncResult = syncIdeationStatus.syncImplementedIdeas(rootDir);
|
package/scripts/claude-tmux.sh
CHANGED
|
@@ -165,6 +165,9 @@ configure_tmux_session() {
|
|
|
165
165
|
# Enable mouse support
|
|
166
166
|
tmux set-option -t "$target_session" mouse on
|
|
167
167
|
|
|
168
|
+
# Automatically renumber windows when one is closed (no gaps)
|
|
169
|
+
tmux set-option -t "$target_session" renumber-windows on
|
|
170
|
+
|
|
168
171
|
# Fix colors - proper terminal support
|
|
169
172
|
tmux set-option -t "$target_session" default-terminal "xterm-256color"
|
|
170
173
|
tmux set-option -t "$target_session" -ga terminal-overrides ",xterm-256color:Tc"
|
|
@@ -245,7 +248,8 @@ configure_tmux_session() {
|
|
|
245
248
|
|
|
246
249
|
# ─── Session Creation Keybindings ──────────────────────────────────────────
|
|
247
250
|
# Alt+s to create a new Claude window (starts fresh, future re-runs in same pane resume)
|
|
248
|
-
|
|
251
|
+
# Window gets sequential name (claude-2, claude-3, ...) so windows are distinguishable
|
|
252
|
+
tmux bind-key -n M-s run-shell "N=\$(( \$(tmux list-windows -F '#{window_name}' 2>/dev/null | grep -c '^claude') + 1 )); tmux new-window -n \"claude-\$N\" -c '#{pane_current_path}' && tmux send-keys '\"\$AGILEFLOW_SCRIPTS/claude-smart.sh\" --fresh \$CLAUDE_SESSION_FLAGS' Enter"
|
|
249
253
|
|
|
250
254
|
# ─── Freeze Recovery Keybindings ───────────────────────────────────────────
|
|
251
255
|
# Alt+k to send Ctrl+C twice (soft interrupt for frozen processes)
|
|
@@ -253,7 +257,7 @@ configure_tmux_session() {
|
|
|
253
257
|
|
|
254
258
|
# ─── Help Panel ──────────────────────────────────────────────────────────
|
|
255
259
|
# Alt+h to show all Alt keybindings in a popup
|
|
256
|
-
tmux bind-key -n M-h display-popup -E -w 52 -h
|
|
260
|
+
tmux bind-key -n M-h display-popup -E -w 52 -h 30 "\
|
|
257
261
|
printf '\\n';\
|
|
258
262
|
printf ' \\033[1;38;5;208mSESSIONS\\033[0m\\n';\
|
|
259
263
|
printf ' Alt+s New Claude session\\n';\
|
|
@@ -272,7 +276,9 @@ configure_tmux_session() {
|
|
|
272
276
|
printf ' Alt+v Split top / bottom\\n';\
|
|
273
277
|
printf ' Alt+arrows Navigate panes\\n';\
|
|
274
278
|
printf ' Alt+z Zoom / unzoom\\n';\
|
|
275
|
-
printf ' Alt+x Close pane\\n';\
|
|
279
|
+
printf ' Alt+x Close pane (confirm)\\n';\
|
|
280
|
+
printf ' Alt+K Kill pane (no confirm)\\n';\
|
|
281
|
+
printf ' Alt+R Restart pane\\n';\
|
|
276
282
|
printf '\\n';\
|
|
277
283
|
printf ' \\033[1;38;5;208mOTHER\\033[0m\\n';\
|
|
278
284
|
printf ' Alt+[ Scroll mode\\n';\
|
|
@@ -48,8 +48,12 @@ if (!utils || typeof utils.runDamageControlHook !== 'function') {
|
|
|
48
48
|
|
|
49
49
|
// Tools this hook handles
|
|
50
50
|
const MULTI_AGENT_TOOLS = [
|
|
51
|
-
'TeamCreate',
|
|
52
|
-
'
|
|
51
|
+
'TeamCreate',
|
|
52
|
+
'TeamDelete',
|
|
53
|
+
'TaskCreate',
|
|
54
|
+
'TaskUpdate',
|
|
55
|
+
'TaskGet',
|
|
56
|
+
'TaskList',
|
|
53
57
|
'SendMessage',
|
|
54
58
|
];
|
|
55
59
|
|
|
@@ -65,10 +69,10 @@ const MAX_MESSAGE_SIZE = 10240;
|
|
|
65
69
|
// Blocked patterns in SendMessage content
|
|
66
70
|
const BLOCKED_MESSAGE_PATTERNS = [
|
|
67
71
|
// Command injection attempts
|
|
68
|
-
/\$\{.*\}/,
|
|
69
|
-
/`[^`]*`/,
|
|
70
|
-
/\bexec\s*\(/,
|
|
71
|
-
/\beval\s*\(/,
|
|
72
|
+
/\$\{.*\}/, // Template injection ${...}
|
|
73
|
+
/`[^`]*`/, // Backtick execution
|
|
74
|
+
/\bexec\s*\(/, // exec() calls
|
|
75
|
+
/\beval\s*\(/, // eval() calls
|
|
72
76
|
// Dangerous git operations
|
|
73
77
|
/\bgit\s+push\s+--force\b/i,
|
|
74
78
|
/\bgit\s+reset\s+--hard\b/i,
|
|
@@ -143,9 +147,9 @@ function validateTaskOperation(input) {
|
|
|
143
147
|
// Check for secrets in task descriptions
|
|
144
148
|
const secretPatterns = [
|
|
145
149
|
/\b(?:API_KEY|SECRET|PASSWORD|TOKEN|CREDENTIALS)\s*[:=]\s*\S+/i,
|
|
146
|
-
/\bsk-[a-zA-Z0-9]{20,}/,
|
|
147
|
-
/\bghp_[a-zA-Z0-9]{36}/,
|
|
148
|
-
/\bnpm_[a-zA-Z0-9]{36}/,
|
|
150
|
+
/\bsk-[a-zA-Z0-9]{20,}/, // API keys starting with sk-
|
|
151
|
+
/\bghp_[a-zA-Z0-9]{36}/, // GitHub personal access tokens
|
|
152
|
+
/\bnpm_[a-zA-Z0-9]{36}/, // npm tokens
|
|
149
153
|
];
|
|
150
154
|
|
|
151
155
|
for (const pattern of secretPatterns) {
|
|
@@ -163,7 +167,7 @@ function validateTaskOperation(input) {
|
|
|
163
167
|
|
|
164
168
|
try {
|
|
165
169
|
utils.runDamageControlHook({
|
|
166
|
-
getInputValue:
|
|
170
|
+
getInputValue: input => {
|
|
167
171
|
// Check if this is a multi-agent tool
|
|
168
172
|
const toolName = input.tool_name || '';
|
|
169
173
|
if (!MULTI_AGENT_TOOLS.includes(toolName)) {
|
package/scripts/lib/bus-utils.js
CHANGED
|
@@ -341,7 +341,9 @@ function formatLogStats(stats) {
|
|
|
341
341
|
lines.push('Archives:');
|
|
342
342
|
if (stats.archives && stats.archives.length > 0) {
|
|
343
343
|
for (const archive of stats.archives) {
|
|
344
|
-
lines.push(
|
|
344
|
+
lines.push(
|
|
345
|
+
` ${archive.filename}: ${archive.lineCount} lines (${Math.round(archive.size / 1024)} KB)`
|
|
346
|
+
);
|
|
345
347
|
}
|
|
346
348
|
}
|
|
347
349
|
|
|
@@ -107,10 +107,15 @@ function detectConfig(version) {
|
|
|
107
107
|
// Git detection
|
|
108
108
|
if (fs.existsSync('.git')) {
|
|
109
109
|
status.git.initialized = true;
|
|
110
|
-
status.git.remote =
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
110
|
+
status.git.remote =
|
|
111
|
+
tryOptional(
|
|
112
|
+
() =>
|
|
113
|
+
execFileSync('git', ['remote', 'get-url', 'origin'], {
|
|
114
|
+
encoding: 'utf8',
|
|
115
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
116
|
+
}).trim(),
|
|
117
|
+
'git remote'
|
|
118
|
+
) ?? null;
|
|
114
119
|
}
|
|
115
120
|
|
|
116
121
|
// Settings file detection
|
|
@@ -312,17 +317,15 @@ function detectMetadata(status, version) {
|
|
|
312
317
|
|
|
313
318
|
// Content-based outdated detection
|
|
314
319
|
const featureConfig = FEATURES[statusKey];
|
|
315
|
-
const scriptsToCheck =
|
|
316
|
-
|| (featureConfig?.script ? [featureConfig.script] : []);
|
|
320
|
+
const scriptsToCheck =
|
|
321
|
+
featureConfig?.scripts || (featureConfig?.script ? [featureConfig.script] : []);
|
|
317
322
|
|
|
318
323
|
if (scriptsToCheck.length > 0 && packageScriptDir) {
|
|
319
324
|
// Compare installed scripts against package source
|
|
320
325
|
let isOutdated = false;
|
|
321
326
|
for (const scriptName of scriptsToCheck) {
|
|
322
327
|
const packageScript = path.join(packageScriptDir, scriptName);
|
|
323
|
-
const installedScript = path.join(
|
|
324
|
-
process.cwd(), '.agileflow', 'scripts', scriptName
|
|
325
|
-
);
|
|
328
|
+
const installedScript = path.join(process.cwd(), '.agileflow', 'scripts', scriptName);
|
|
326
329
|
const packageHash = hashFile(packageScript);
|
|
327
330
|
const installedHash = hashFile(installedScript);
|
|
328
331
|
|
|
@@ -55,7 +55,8 @@ const FEATURES = {
|
|
|
55
55
|
},
|
|
56
56
|
claudeflags: {
|
|
57
57
|
metadataOnly: false,
|
|
58
|
-
description:
|
|
58
|
+
description:
|
|
59
|
+
'Default flags for Claude CLI (sets permissions.defaultMode in .claude/settings.json)',
|
|
59
60
|
},
|
|
60
61
|
agentteams: {
|
|
61
62
|
metadataOnly: false,
|
|
@@ -459,12 +460,16 @@ function enableFeature(feature, options = {}, version) {
|
|
|
459
460
|
: null;
|
|
460
461
|
writeJSON('.claude/settings.json', settings);
|
|
461
462
|
updateMetadata(
|
|
462
|
-
{
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
463
|
+
{
|
|
464
|
+
features: {
|
|
465
|
+
[feature]: {
|
|
466
|
+
enabled: true,
|
|
467
|
+
version,
|
|
468
|
+
...(contentHash ? { contentHash } : {}),
|
|
469
|
+
at: new Date().toISOString(),
|
|
470
|
+
},
|
|
471
|
+
},
|
|
472
|
+
},
|
|
468
473
|
version
|
|
469
474
|
);
|
|
470
475
|
updateGitignore();
|
|
@@ -116,11 +116,12 @@ function listScripts() {
|
|
|
116
116
|
// Check if modified
|
|
117
117
|
let isModified = false;
|
|
118
118
|
if (exists && fileIndex?.files?.[`scripts/${script}`]) {
|
|
119
|
-
isModified =
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
119
|
+
isModified =
|
|
120
|
+
tryOptional(() => {
|
|
121
|
+
const currentHash = sha256(fs.readFileSync(scriptPath));
|
|
122
|
+
const indexHash = fileIndex.files[`scripts/${script}`].sha256;
|
|
123
|
+
return currentHash !== indexHash;
|
|
124
|
+
}, 'hash check') ?? false;
|
|
124
125
|
}
|
|
125
126
|
|
|
126
127
|
// Print status
|
|
@@ -323,7 +323,11 @@ function generateSummary(prefetched = null, options = {}) {
|
|
|
323
323
|
|
|
324
324
|
// Scale indicator (EP-0033)
|
|
325
325
|
let scaleDetectorSummary;
|
|
326
|
-
try {
|
|
326
|
+
try {
|
|
327
|
+
scaleDetectorSummary = require('./scale-detector');
|
|
328
|
+
} catch {
|
|
329
|
+
/* not available */
|
|
330
|
+
}
|
|
327
331
|
if (scaleDetectorSummary) {
|
|
328
332
|
try {
|
|
329
333
|
const scaleResult = scaleDetectorSummary.detectScale({
|
|
@@ -503,7 +507,11 @@ function generateFullContent(prefetched = null, options = {}) {
|
|
|
503
507
|
|
|
504
508
|
// SCALE DETECTION (EP-0033)
|
|
505
509
|
let scaleDetector;
|
|
506
|
-
try {
|
|
510
|
+
try {
|
|
511
|
+
scaleDetector = require('./scale-detector');
|
|
512
|
+
} catch {
|
|
513
|
+
/* not available */
|
|
514
|
+
}
|
|
507
515
|
if (scaleDetector) {
|
|
508
516
|
try {
|
|
509
517
|
const scaleResult = scaleDetector.detectScale({
|
|
@@ -886,7 +894,9 @@ function generateRemainingContent(prefetched, options = {}) {
|
|
|
886
894
|
|
|
887
895
|
// Show auto-enabled modes
|
|
888
896
|
const modes = auto_enabled || {};
|
|
889
|
-
const enabledModes = Object.entries(modes)
|
|
897
|
+
const enabledModes = Object.entries(modes)
|
|
898
|
+
.filter(([, v]) => v)
|
|
899
|
+
.map(([k]) => k.replace('_', ' '));
|
|
890
900
|
if (enabledModes.length > 0) {
|
|
891
901
|
content += `\n${C.mintGreen}Auto-enabled:${C.reset} ${enabledModes.join(', ')}\n`;
|
|
892
902
|
}
|
|
@@ -96,7 +96,11 @@ function loadPatterns(projectRoot, parseYAML, defaultConfig = {}) {
|
|
|
96
96
|
const stat = fs.statSync(fullPath);
|
|
97
97
|
const mtime = stat.mtimeMs;
|
|
98
98
|
|
|
99
|
-
if (
|
|
99
|
+
if (
|
|
100
|
+
_patternCache.filePath === fullPath &&
|
|
101
|
+
_patternCache.mtime === mtime &&
|
|
102
|
+
_patternCache.config
|
|
103
|
+
) {
|
|
100
104
|
return _patternCache.config;
|
|
101
105
|
}
|
|
102
106
|
|