agileflow 2.99.8 → 3.0.1
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 +10 -0
- package/README.md +3 -3
- 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 +282 -150
- 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/archive-completed-stories.sh +3 -0
- package/scripts/auto-self-improve.js +3 -3
- package/scripts/ci-summary.js +294 -0
- package/scripts/claude-smart.sh +85 -0
- package/scripts/claude-tmux.sh +272 -161
- package/scripts/damage-control-multi-agent.js +227 -0
- package/scripts/lib/bus-utils.js +471 -0
- package/scripts/lib/configure-detect.js +87 -10
- package/scripts/lib/configure-features.js +110 -4
- 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/precompact-context.sh +3 -0
- 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 +141 -21
- 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
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* AgileFlow CLI - CI Summary Script
|
|
5
|
+
*
|
|
6
|
+
* Summarizes CI/CD workflow failures from the past 24 hours.
|
|
7
|
+
* Uses GitHub CLI (gh) to fetch workflow run data.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* node scripts/ci-summary.js [options]
|
|
11
|
+
*
|
|
12
|
+
* Options:
|
|
13
|
+
* --json Output results as JSON
|
|
14
|
+
* --hours=N Look back N hours (default: 24)
|
|
15
|
+
* --quiet Only show failures
|
|
16
|
+
* --help Show help
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const { execFileSync } = require('child_process');
|
|
20
|
+
const path = require('path');
|
|
21
|
+
|
|
22
|
+
// ANSI colors
|
|
23
|
+
const c = {
|
|
24
|
+
reset: '\x1b[0m',
|
|
25
|
+
bold: '\x1b[1m',
|
|
26
|
+
dim: '\x1b[2m',
|
|
27
|
+
red: '\x1b[31m',
|
|
28
|
+
green: '\x1b[32m',
|
|
29
|
+
yellow: '\x1b[33m',
|
|
30
|
+
cyan: '\x1b[36m',
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Parse command line arguments
|
|
35
|
+
*/
|
|
36
|
+
function parseArgs(args) {
|
|
37
|
+
const options = {
|
|
38
|
+
json: false,
|
|
39
|
+
hours: 24,
|
|
40
|
+
quiet: false,
|
|
41
|
+
help: false,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
for (const arg of args) {
|
|
45
|
+
if (arg === '--json') options.json = true;
|
|
46
|
+
else if (arg === '--quiet') options.quiet = true;
|
|
47
|
+
else if (arg === '--help' || arg === '-h') options.help = true;
|
|
48
|
+
else if (arg.startsWith('--hours=')) {
|
|
49
|
+
const hours = parseInt(arg.split('=')[1], 10);
|
|
50
|
+
if (!isNaN(hours) && hours > 0) {
|
|
51
|
+
options.hours = hours;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return options;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Show help message
|
|
61
|
+
*/
|
|
62
|
+
function showHelp() {
|
|
63
|
+
console.log(`
|
|
64
|
+
${c.bold}AgileFlow CI Summary${c.reset}
|
|
65
|
+
|
|
66
|
+
${c.cyan}Usage:${c.reset}
|
|
67
|
+
node scripts/ci-summary.js [options]
|
|
68
|
+
|
|
69
|
+
${c.cyan}Options:${c.reset}
|
|
70
|
+
--json Output results as JSON
|
|
71
|
+
--hours=N Look back N hours (default: 24)
|
|
72
|
+
--quiet Only show failures
|
|
73
|
+
--help, -h Show this help message
|
|
74
|
+
`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Check if gh CLI is available
|
|
79
|
+
*/
|
|
80
|
+
function hasGhCli() {
|
|
81
|
+
try {
|
|
82
|
+
execFileSync('gh', ['--version'], { stdio: 'pipe' });
|
|
83
|
+
return true;
|
|
84
|
+
} catch {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Check if we're in a git repo with a GitHub remote
|
|
91
|
+
*/
|
|
92
|
+
function hasGitHubRemote() {
|
|
93
|
+
try {
|
|
94
|
+
const remote = execFileSync('git', ['remote', 'get-url', 'origin'], {
|
|
95
|
+
stdio: 'pipe',
|
|
96
|
+
encoding: 'utf8',
|
|
97
|
+
}).trim();
|
|
98
|
+
return remote.includes('github.com');
|
|
99
|
+
} catch {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Fetch workflow runs from GitHub
|
|
106
|
+
*/
|
|
107
|
+
function fetchWorkflowRuns(hours) {
|
|
108
|
+
try {
|
|
109
|
+
const output = execFileSync(
|
|
110
|
+
'gh',
|
|
111
|
+
[
|
|
112
|
+
'run',
|
|
113
|
+
'list',
|
|
114
|
+
'--limit',
|
|
115
|
+
'50',
|
|
116
|
+
'--json',
|
|
117
|
+
'databaseId,name,status,conclusion,createdAt,headBranch,event,url',
|
|
118
|
+
],
|
|
119
|
+
{ stdio: 'pipe', encoding: 'utf8', timeout: 30000 }
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
const runs = JSON.parse(output);
|
|
123
|
+
const cutoff = new Date(Date.now() - hours * 60 * 60 * 1000);
|
|
124
|
+
|
|
125
|
+
return runs.filter(run => new Date(run.createdAt) >= cutoff);
|
|
126
|
+
} catch (e) {
|
|
127
|
+
throw new Error(`Failed to fetch workflow runs: ${e.message}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Generate summary from runs
|
|
133
|
+
*/
|
|
134
|
+
function generateSummary(runs, hours) {
|
|
135
|
+
const summary = {
|
|
136
|
+
period_hours: hours,
|
|
137
|
+
total: runs.length,
|
|
138
|
+
successful: 0,
|
|
139
|
+
failed: 0,
|
|
140
|
+
cancelled: 0,
|
|
141
|
+
in_progress: 0,
|
|
142
|
+
failures: [],
|
|
143
|
+
workflows: {},
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
for (const run of runs) {
|
|
147
|
+
const workflowName = run.name || 'Unknown';
|
|
148
|
+
|
|
149
|
+
if (!summary.workflows[workflowName]) {
|
|
150
|
+
summary.workflows[workflowName] = { total: 0, passed: 0, failed: 0 };
|
|
151
|
+
}
|
|
152
|
+
summary.workflows[workflowName].total++;
|
|
153
|
+
|
|
154
|
+
if (run.conclusion === 'success') {
|
|
155
|
+
summary.successful++;
|
|
156
|
+
summary.workflows[workflowName].passed++;
|
|
157
|
+
} else if (run.conclusion === 'failure') {
|
|
158
|
+
summary.failed++;
|
|
159
|
+
summary.workflows[workflowName].failed++;
|
|
160
|
+
summary.failures.push({
|
|
161
|
+
workflow: workflowName,
|
|
162
|
+
branch: run.headBranch,
|
|
163
|
+
event: run.event,
|
|
164
|
+
created: run.createdAt,
|
|
165
|
+
url: run.url,
|
|
166
|
+
});
|
|
167
|
+
} else if (run.conclusion === 'cancelled') {
|
|
168
|
+
summary.cancelled++;
|
|
169
|
+
} else if (run.status === 'in_progress' || run.status === 'queued') {
|
|
170
|
+
summary.in_progress++;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return summary;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Format summary for console output
|
|
179
|
+
*/
|
|
180
|
+
function formatSummary(summary, quiet) {
|
|
181
|
+
const lines = [];
|
|
182
|
+
|
|
183
|
+
if (!quiet) {
|
|
184
|
+
lines.push(`${c.bold}CI Summary (last ${summary.period_hours}h)${c.reset}`);
|
|
185
|
+
lines.push('');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (summary.total === 0) {
|
|
189
|
+
lines.push(`${c.dim}No workflow runs in the past ${summary.period_hours} hours.${c.reset}`);
|
|
190
|
+
return lines.join('\n');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (!quiet) {
|
|
194
|
+
lines.push(` Total runs: ${summary.total}`);
|
|
195
|
+
lines.push(` ${c.green}Successful:${c.reset} ${summary.successful}`);
|
|
196
|
+
if (summary.failed > 0) {
|
|
197
|
+
lines.push(` ${c.red}Failed:${c.reset} ${summary.failed}`);
|
|
198
|
+
}
|
|
199
|
+
if (summary.cancelled > 0) {
|
|
200
|
+
lines.push(` ${c.yellow}Cancelled:${c.reset} ${summary.cancelled}`);
|
|
201
|
+
}
|
|
202
|
+
if (summary.in_progress > 0) {
|
|
203
|
+
lines.push(` ${c.cyan}In progress:${c.reset} ${summary.in_progress}`);
|
|
204
|
+
}
|
|
205
|
+
lines.push('');
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Show failures
|
|
209
|
+
if (summary.failures.length > 0) {
|
|
210
|
+
lines.push(`${c.red}${c.bold}Failures:${c.reset}`);
|
|
211
|
+
for (const failure of summary.failures) {
|
|
212
|
+
lines.push(
|
|
213
|
+
` ${c.red}x${c.reset} ${failure.workflow} ${c.dim}(${failure.branch}, ${failure.event})${c.reset}`
|
|
214
|
+
);
|
|
215
|
+
if (failure.url) {
|
|
216
|
+
lines.push(` ${c.dim}${failure.url}${c.reset}`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
lines.push('');
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Workflow breakdown (non-quiet)
|
|
223
|
+
if (!quiet) {
|
|
224
|
+
const workflowNames = Object.keys(summary.workflows);
|
|
225
|
+
if (workflowNames.length > 0) {
|
|
226
|
+
lines.push(`${c.bold}Workflows:${c.reset}`);
|
|
227
|
+
for (const name of workflowNames) {
|
|
228
|
+
const w = summary.workflows[name];
|
|
229
|
+
const status = w.failed > 0 ? c.red : c.green;
|
|
230
|
+
lines.push(` ${status}${name}${c.reset}: ${w.passed}/${w.total} passed`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return lines.join('\n');
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Main entry point
|
|
240
|
+
*/
|
|
241
|
+
function main() {
|
|
242
|
+
const options = parseArgs(process.argv.slice(2));
|
|
243
|
+
|
|
244
|
+
if (options.help) {
|
|
245
|
+
showHelp();
|
|
246
|
+
process.exit(0);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Pre-flight checks
|
|
250
|
+
if (!hasGhCli()) {
|
|
251
|
+
if (options.json) {
|
|
252
|
+
console.log(JSON.stringify({ error: 'GitHub CLI (gh) not installed', runs: [] }));
|
|
253
|
+
} else {
|
|
254
|
+
console.log(
|
|
255
|
+
`${c.dim}CI Summary: GitHub CLI (gh) not available. Install from https://cli.github.com/${c.reset}`
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
process.exit(0);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (!hasGitHubRemote()) {
|
|
262
|
+
if (options.json) {
|
|
263
|
+
console.log(JSON.stringify({ error: 'No GitHub remote found', runs: [] }));
|
|
264
|
+
} else {
|
|
265
|
+
console.log(`${c.dim}CI Summary: No GitHub remote detected. Skipping.${c.reset}`);
|
|
266
|
+
}
|
|
267
|
+
process.exit(0);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Fetch and summarize
|
|
271
|
+
try {
|
|
272
|
+
const runs = fetchWorkflowRuns(options.hours);
|
|
273
|
+
const summary = generateSummary(runs, options.hours);
|
|
274
|
+
|
|
275
|
+
if (options.json) {
|
|
276
|
+
console.log(JSON.stringify(summary, null, 2));
|
|
277
|
+
} else {
|
|
278
|
+
console.log(formatSummary(summary, options.quiet));
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Always exit 0 when summary was generated successfully.
|
|
282
|
+
// The automation runner treats non-zero as script failure.
|
|
283
|
+
process.exit(0);
|
|
284
|
+
} catch (e) {
|
|
285
|
+
if (options.json) {
|
|
286
|
+
console.log(JSON.stringify({ error: e.message, runs: [] }));
|
|
287
|
+
} else {
|
|
288
|
+
console.error(`${c.red}CI Summary error:${c.reset} ${e.message}`);
|
|
289
|
+
}
|
|
290
|
+
process.exit(1);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
main();
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# claude-smart.sh - Smart resume wrapper for Claude in tmux
|
|
3
|
+
#
|
|
4
|
+
# Uses per-pane tmux option @claude_uuid to track conversation identity.
|
|
5
|
+
# New windows start fresh; re-running in the same pane resumes the conversation.
|
|
6
|
+
#
|
|
7
|
+
# Usage:
|
|
8
|
+
# claude-smart.sh # Resume if UUID stored, else fresh
|
|
9
|
+
# claude-smart.sh --fresh # Force fresh start (ignore stored UUID)
|
|
10
|
+
# claude-smart.sh [flags...] # Pass-through flags to claude
|
|
11
|
+
#
|
|
12
|
+
# NO set -e: UUID capture must run even after Ctrl+C / non-zero exit
|
|
13
|
+
|
|
14
|
+
FRESH=false
|
|
15
|
+
ARGS=()
|
|
16
|
+
|
|
17
|
+
for arg in "$@"; do
|
|
18
|
+
case $arg in
|
|
19
|
+
--fresh)
|
|
20
|
+
FRESH=true
|
|
21
|
+
;;
|
|
22
|
+
*)
|
|
23
|
+
ARGS+=("$arg")
|
|
24
|
+
;;
|
|
25
|
+
esac
|
|
26
|
+
done
|
|
27
|
+
|
|
28
|
+
# ── Resolve conversation UUID ──────────────────────────────────────────────
|
|
29
|
+
STORED_UUID=""
|
|
30
|
+
if [ "$FRESH" = false ] && [ -n "$TMUX" ]; then
|
|
31
|
+
STORED_UUID=$(tmux show-options -pqv @claude_uuid 2>/dev/null || true)
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
# Validate: the .jsonl file must still exist
|
|
35
|
+
if [ -n "$STORED_UUID" ]; then
|
|
36
|
+
PROJ_DIR=$(pwd | sed 's|/|-|g' | sed 's|^-||')
|
|
37
|
+
SESSIONS_DIR="$HOME/.claude/projects/-$PROJ_DIR"
|
|
38
|
+
if [ ! -f "$SESSIONS_DIR/$STORED_UUID.jsonl" ]; then
|
|
39
|
+
STORED_UUID="" # File gone, start fresh
|
|
40
|
+
fi
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
# ── Build and run Claude command ───────────────────────────────────────────
|
|
44
|
+
CMD=(claude)
|
|
45
|
+
if [ -n "$STORED_UUID" ]; then
|
|
46
|
+
CMD+=(--resume "$STORED_UUID")
|
|
47
|
+
fi
|
|
48
|
+
CMD+=("${ARGS[@]}")
|
|
49
|
+
|
|
50
|
+
"${CMD[@]}"
|
|
51
|
+
EXIT_CODE=$?
|
|
52
|
+
|
|
53
|
+
# ── Auto-retry on expired session ────────────────────────────────────────
|
|
54
|
+
# Exit code 1 with a stored UUID means Claude couldn't find the session
|
|
55
|
+
# in its internal index (even though the .jsonl file exists on disk).
|
|
56
|
+
if [ $EXIT_CODE -eq 1 ] && [ -n "$STORED_UUID" ]; then
|
|
57
|
+
echo ""
|
|
58
|
+
echo "Session expired. Starting fresh..."
|
|
59
|
+
echo ""
|
|
60
|
+
# Clear stale UUID from tmux pane
|
|
61
|
+
if [ -n "$TMUX" ]; then
|
|
62
|
+
tmux set-option -p -u @claude_uuid 2>/dev/null || true
|
|
63
|
+
fi
|
|
64
|
+
# Retry without --resume
|
|
65
|
+
STORED_UUID=""
|
|
66
|
+
CMD=(claude "${ARGS[@]}")
|
|
67
|
+
"${CMD[@]}"
|
|
68
|
+
EXIT_CODE=$?
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
# ── Capture UUID after exit ────────────────────────────────────────────────
|
|
72
|
+
# Store the most recent non-agent .jsonl as the pane's conversation UUID
|
|
73
|
+
if [ -n "$TMUX" ]; then
|
|
74
|
+
PROJ_DIR=${PROJ_DIR:-$(pwd | sed 's|/|-|g' | sed 's|^-||')}
|
|
75
|
+
SESSIONS_DIR=${SESSIONS_DIR:-"$HOME/.claude/projects/-$PROJ_DIR"}
|
|
76
|
+
if [ -d "$SESSIONS_DIR" ]; then
|
|
77
|
+
NEWEST=$(ls -t "$SESSIONS_DIR"/*.jsonl 2>/dev/null | grep -v "agent-" | head -1)
|
|
78
|
+
if [ -n "$NEWEST" ]; then
|
|
79
|
+
NEW_UUID=$(basename "$NEWEST" .jsonl)
|
|
80
|
+
tmux set-option -p @claude_uuid "$NEW_UUID" 2>/dev/null || true
|
|
81
|
+
fi
|
|
82
|
+
fi
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
exit $EXIT_CODE
|