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.
Files changed (69) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +3 -3
  3. package/lib/cache-provider.js +155 -0
  4. package/lib/codebase-indexer.js +1 -1
  5. package/lib/content-sanitizer.js +1 -0
  6. package/lib/dashboard-protocol.js +25 -0
  7. package/lib/dashboard-server.js +282 -150
  8. package/lib/errors.js +18 -0
  9. package/lib/file-cache.js +1 -1
  10. package/lib/flag-detection.js +11 -20
  11. package/lib/git-operations.js +15 -33
  12. package/lib/merge-operations.js +40 -34
  13. package/lib/process-executor.js +199 -0
  14. package/lib/registry-cache.js +13 -47
  15. package/lib/skill-loader.js +206 -0
  16. package/lib/smart-json-file.js +2 -4
  17. package/package.json +1 -1
  18. package/scripts/agileflow-configure.js +13 -12
  19. package/scripts/agileflow-statusline.sh +30 -0
  20. package/scripts/agileflow-welcome.js +181 -212
  21. package/scripts/archive-completed-stories.sh +3 -0
  22. package/scripts/auto-self-improve.js +3 -3
  23. package/scripts/ci-summary.js +294 -0
  24. package/scripts/claude-smart.sh +85 -0
  25. package/scripts/claude-tmux.sh +272 -161
  26. package/scripts/damage-control-multi-agent.js +227 -0
  27. package/scripts/lib/bus-utils.js +471 -0
  28. package/scripts/lib/configure-detect.js +87 -10
  29. package/scripts/lib/configure-features.js +110 -4
  30. package/scripts/lib/configure-repair.js +5 -6
  31. package/scripts/lib/configure-utils.js +2 -3
  32. package/scripts/lib/context-formatter.js +87 -8
  33. package/scripts/lib/damage-control-utils.js +37 -3
  34. package/scripts/lib/file-lock.js +392 -0
  35. package/scripts/lib/ideation-index.js +2 -5
  36. package/scripts/lib/lifecycle-detector.js +123 -0
  37. package/scripts/lib/process-cleanup.js +55 -81
  38. package/scripts/lib/scale-detector.js +357 -0
  39. package/scripts/lib/signal-detectors.js +779 -0
  40. package/scripts/lib/story-state-machine.js +1 -1
  41. package/scripts/lib/sync-ideation-status.js +2 -3
  42. package/scripts/lib/task-registry.js +7 -1
  43. package/scripts/lib/team-events.js +357 -0
  44. package/scripts/messaging-bridge.js +79 -36
  45. package/scripts/migrate-ideation-index.js +37 -14
  46. package/scripts/obtain-context.js +37 -19
  47. package/scripts/precompact-context.sh +3 -0
  48. package/scripts/ralph-loop.js +3 -4
  49. package/scripts/smart-detect.js +390 -0
  50. package/scripts/team-manager.js +174 -30
  51. package/src/core/commands/audit.md +13 -11
  52. package/src/core/commands/babysit.md +162 -115
  53. package/src/core/commands/changelog.md +21 -4
  54. package/src/core/commands/configure.md +141 -21
  55. package/src/core/commands/debt.md +12 -2
  56. package/src/core/commands/feedback.md +7 -6
  57. package/src/core/commands/ideate/history.md +1 -1
  58. package/src/core/commands/ideate/new.md +5 -5
  59. package/src/core/commands/logic/audit.md +2 -2
  60. package/src/core/commands/pr.md +7 -6
  61. package/src/core/commands/research/analyze.md +28 -20
  62. package/src/core/commands/research/ask.md +43 -0
  63. package/src/core/commands/research/import.md +29 -21
  64. package/src/core/commands/research/list.md +8 -7
  65. package/src/core/commands/research/synthesize.md +356 -20
  66. package/src/core/commands/research/view.md +8 -5
  67. package/src/core/commands/review.md +24 -6
  68. package/src/core/commands/skill/create.md +34 -0
  69. 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