agileflow 2.95.2 → 2.96.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.
Files changed (81) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +6 -6
  3. package/lib/api-routes.js +605 -0
  4. package/lib/api-server.js +260 -0
  5. package/lib/claude-cli-bridge.js +221 -0
  6. package/lib/dashboard-protocol.js +541 -0
  7. package/lib/dashboard-server.js +1601 -0
  8. package/lib/drivers/claude-driver.ts +310 -0
  9. package/lib/drivers/codex-driver.ts +454 -0
  10. package/lib/drivers/driver-manager.ts +158 -0
  11. package/lib/drivers/gemini-driver.ts +485 -0
  12. package/lib/drivers/index.ts +17 -0
  13. package/lib/flag-detection.js +350 -0
  14. package/lib/git-operations.js +267 -0
  15. package/lib/lock-file.js +144 -0
  16. package/lib/merge-operations.js +959 -0
  17. package/lib/protocol/driver.ts +360 -0
  18. package/lib/protocol/index.ts +12 -0
  19. package/lib/protocol/ir.ts +271 -0
  20. package/lib/session-display.js +330 -0
  21. package/lib/worktree-operations.js +221 -0
  22. package/package.json +2 -2
  23. package/scripts/agileflow-welcome.js +272 -24
  24. package/scripts/api-server-runner.js +177 -0
  25. package/scripts/archive-completed-stories.sh +22 -0
  26. package/scripts/automation-run-due.js +126 -0
  27. package/scripts/backfill-ideation-status.js +124 -0
  28. package/scripts/claude-tmux.sh +62 -1
  29. package/scripts/context-loader.js +292 -0
  30. package/scripts/dashboard-serve.js +323 -0
  31. package/scripts/lib/automation-registry.js +544 -0
  32. package/scripts/lib/automation-runner.js +476 -0
  33. package/scripts/lib/concurrency-limiter.js +513 -0
  34. package/scripts/lib/configure-features.js +46 -0
  35. package/scripts/lib/context-formatter.js +61 -0
  36. package/scripts/lib/damage-control-utils.js +29 -4
  37. package/scripts/lib/hook-metrics.js +324 -0
  38. package/scripts/lib/ideation-index.js +1196 -0
  39. package/scripts/lib/process-cleanup.js +359 -0
  40. package/scripts/lib/quality-gates.js +574 -0
  41. package/scripts/lib/status-task-bridge.js +522 -0
  42. package/scripts/lib/sync-ideation-status.js +292 -0
  43. package/scripts/lib/task-registry-cache.js +490 -0
  44. package/scripts/lib/task-registry.js +1181 -0
  45. package/scripts/migrate-ideation-index.js +515 -0
  46. package/scripts/precompact-context.sh +104 -0
  47. package/scripts/ralph-loop.js +2 -2
  48. package/scripts/session-manager.js +363 -2770
  49. package/scripts/spawn-parallel.js +45 -9
  50. package/src/core/agents/api-validator.md +180 -0
  51. package/src/core/agents/api.md +2 -0
  52. package/src/core/agents/code-reviewer.md +289 -0
  53. package/src/core/agents/configuration/damage-control.md +17 -0
  54. package/src/core/agents/database.md +2 -0
  55. package/src/core/agents/error-analyzer.md +203 -0
  56. package/src/core/agents/logic-analyzer-edge.md +171 -0
  57. package/src/core/agents/logic-analyzer-flow.md +254 -0
  58. package/src/core/agents/logic-analyzer-invariant.md +207 -0
  59. package/src/core/agents/logic-analyzer-race.md +267 -0
  60. package/src/core/agents/logic-analyzer-type.md +218 -0
  61. package/src/core/agents/logic-consensus.md +256 -0
  62. package/src/core/agents/orchestrator.md +89 -1
  63. package/src/core/agents/schema-validator.md +451 -0
  64. package/src/core/agents/team-coordinator.md +328 -0
  65. package/src/core/agents/ui-validator.md +328 -0
  66. package/src/core/agents/ui.md +2 -0
  67. package/src/core/commands/api.md +267 -0
  68. package/src/core/commands/automate.md +415 -0
  69. package/src/core/commands/babysit.md +290 -9
  70. package/src/core/commands/ideate/history.md +403 -0
  71. package/src/core/commands/{ideate.md → ideate/new.md} +244 -34
  72. package/src/core/commands/logic/audit.md +368 -0
  73. package/src/core/commands/roadmap/analyze.md +1 -1
  74. package/src/core/experts/documentation/expertise.yaml +29 -2
  75. package/src/core/templates/CONTEXT.md.example +49 -0
  76. package/src/core/templates/claude-settings.advanced.example.json +4 -0
  77. package/tools/cli/commands/serve.js +456 -0
  78. package/tools/cli/installers/core/installer.js +7 -2
  79. package/tools/cli/installers/ide/claude-code.js +85 -0
  80. package/tools/cli/lib/content-injector.js +27 -1
  81. package/tools/cli/lib/ui.js +26 -57
@@ -0,0 +1,323 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * dashboard-serve.js - AgileFlow Dashboard WebSocket Server CLI
5
+ *
6
+ * Starts a WebSocket server that the AgileFlow Dashboard can connect to
7
+ * for real-time communication with Claude Code.
8
+ *
9
+ * Usage:
10
+ * agileflow serve [options]
11
+ * node scripts/dashboard-serve.js [options]
12
+ *
13
+ * Options:
14
+ * --port, -p Port to listen on (default: 8765)
15
+ * --host, -h Host to bind to (default: 0.0.0.0)
16
+ * --api-key, -k API key for authentication
17
+ * --require-auth Require API key for connections
18
+ * --tunnel, -t Start ngrok tunnel (if installed)
19
+ */
20
+
21
+ 'use strict';
22
+
23
+ const path = require('path');
24
+ const { createDashboardServer, startDashboardServer, stopDashboardServer } = require('../lib/dashboard-server');
25
+ const { createNotification, createTextDelta, createToolStart, createToolResult, createAskUserQuestion } = require('../lib/dashboard-protocol');
26
+ const { createClaudeBridge } = require('../lib/claude-cli-bridge');
27
+
28
+ // Parse command line arguments
29
+ function parseArgs() {
30
+ const args = process.argv.slice(2);
31
+ const options = {
32
+ port: 8765,
33
+ host: '0.0.0.0',
34
+ apiKey: null,
35
+ requireAuth: false,
36
+ tunnel: false,
37
+ };
38
+
39
+ for (let i = 0; i < args.length; i++) {
40
+ const arg = args[i];
41
+ const next = args[i + 1];
42
+
43
+ switch (arg) {
44
+ case '--port':
45
+ case '-p':
46
+ options.port = parseInt(next, 10);
47
+ i++;
48
+ break;
49
+ case '--host':
50
+ case '-h':
51
+ options.host = next;
52
+ i++;
53
+ break;
54
+ case '--api-key':
55
+ case '-k':
56
+ options.apiKey = next;
57
+ options.requireAuth = true;
58
+ i++;
59
+ break;
60
+ case '--require-auth':
61
+ options.requireAuth = true;
62
+ break;
63
+ case '--tunnel':
64
+ case '-t':
65
+ options.tunnel = true;
66
+ break;
67
+ case '--help':
68
+ printHelp();
69
+ process.exit(0);
70
+ }
71
+ }
72
+
73
+ return options;
74
+ }
75
+
76
+ function printHelp() {
77
+ console.log(`
78
+ AgileFlow Dashboard Server
79
+
80
+ Starts a WebSocket server for the AgileFlow Dashboard to connect to.
81
+
82
+ Usage:
83
+ agileflow serve [options]
84
+ node scripts/dashboard-serve.js [options]
85
+
86
+ Options:
87
+ --port, -p <port> Port to listen on (default: 8765)
88
+ --host, -h <host> Host to bind to (default: 0.0.0.0)
89
+ --api-key, -k <key> API key for authentication
90
+ --require-auth Require API key for connections
91
+ --tunnel, -t Start ngrok tunnel (requires ngrok)
92
+ --help Show this help message
93
+
94
+ Examples:
95
+ # Start with default settings
96
+ agileflow serve
97
+
98
+ # Start on custom port with API key
99
+ agileflow serve --port 9000 --api-key agf_secret123
100
+
101
+ # Start with ngrok tunnel
102
+ agileflow serve --tunnel
103
+
104
+ Dashboard Connection:
105
+ The dashboard should connect to ws://localhost:<port>
106
+ Or use the tunnel URL if --tunnel is enabled.
107
+ `);
108
+ }
109
+
110
+ function printBanner() {
111
+ console.log(`
112
+ ╔═══════════════════════════════════════════════════════════╗
113
+ ║ ║
114
+ ║ █████╗ ██████╗ ██╗██╗ ███████╗███████╗██╗ ║
115
+ ║ ██╔══██╗██╔════╝ ██║██║ ██╔════╝██╔════╝██║ ║
116
+ ║ ███████║██║ ███╗██║██║ █████╗ █████╗ ██║ ║
117
+ ║ ██╔══██║██║ ██║██║██║ ██╔══╝ ██╔══╝ ██║ ║
118
+ ║ ██║ ██║╚██████╔╝██║███████╗███████╗██║ ███████╗ ║
119
+ ║ ╚═╝ ╚═╝ ╚═════╝ ╚═╝╚══════╝╚══════╝╚═╝ ╚══════╝ ║
120
+ ║ ║
121
+ ║ Dashboard WebSocket Server ║
122
+ ║ ║
123
+ ╚═══════════════════════════════════════════════════════════╝
124
+ `);
125
+ }
126
+
127
+ async function startTunnel(port) {
128
+ try {
129
+ const { exec } = require('child_process');
130
+
131
+ return new Promise((resolve, reject) => {
132
+ // Check if ngrok is installed
133
+ exec('which ngrok', (error) => {
134
+ if (error) {
135
+ console.log(' Tunnel: ngrok not found. Install with: npm install -g ngrok');
136
+ resolve(null);
137
+ return;
138
+ }
139
+
140
+ // Start ngrok tunnel
141
+ const ngrok = exec(`ngrok http ${port} --log stdout`, { encoding: 'utf8' });
142
+
143
+ ngrok.stdout.on('data', (data) => {
144
+ // Parse ngrok output for public URL
145
+ const urlMatch = data.match(/url=(https?:\/\/[^\s]+)/);
146
+ if (urlMatch) {
147
+ const tunnelUrl = urlMatch[1].replace('https://', 'wss://').replace('http://', 'ws://');
148
+ console.log(` Tunnel: ${tunnelUrl}`);
149
+ resolve(tunnelUrl);
150
+ }
151
+ });
152
+
153
+ ngrok.stderr.on('data', (data) => {
154
+ console.error(' Tunnel error:', data);
155
+ });
156
+
157
+ // Give ngrok a moment to start
158
+ setTimeout(() => {
159
+ if (!ngrok.killed) {
160
+ console.log(' Tunnel: Starting... check ngrok dashboard');
161
+ resolve(null);
162
+ }
163
+ }, 5000);
164
+ });
165
+ });
166
+ } catch (error) {
167
+ console.log(' Tunnel: Failed to start -', error.message);
168
+ return null;
169
+ }
170
+ }
171
+
172
+ async function main() {
173
+ const options = parseArgs();
174
+
175
+ printBanner();
176
+
177
+ console.log('Starting server...\n');
178
+
179
+ try {
180
+ // Create server
181
+ const server = createDashboardServer({
182
+ port: options.port,
183
+ host: options.host,
184
+ apiKey: options.apiKey,
185
+ requireAuth: options.requireAuth,
186
+ });
187
+
188
+ // Set up event handlers
189
+ setupEventHandlers(server);
190
+
191
+ // Start server
192
+ const { wsUrl } = await startDashboardServer(server);
193
+
194
+ // Start tunnel if requested
195
+ if (options.tunnel) {
196
+ await startTunnel(options.port);
197
+ }
198
+
199
+ console.log('─────────────────────────────────────────────────────────────');
200
+ console.log('');
201
+ console.log(' Ready! Connect your dashboard to:');
202
+ console.log(` ${wsUrl}`);
203
+ console.log('');
204
+ if (options.apiKey) {
205
+ console.log(` API Key: ${options.apiKey.slice(0, 8)}...`);
206
+ console.log('');
207
+ }
208
+ console.log(' Press Ctrl+C to stop.');
209
+ console.log('');
210
+ console.log('─────────────────────────────────────────────────────────────');
211
+ console.log('');
212
+
213
+ // Handle shutdown
214
+ const shutdown = async () => {
215
+ console.log('\nShutting down...');
216
+ await stopDashboardServer(server);
217
+ process.exit(0);
218
+ };
219
+
220
+ process.on('SIGINT', shutdown);
221
+ process.on('SIGTERM', shutdown);
222
+
223
+ } catch (error) {
224
+ console.error('Failed to start server:', error.message);
225
+ process.exit(1);
226
+ }
227
+ }
228
+
229
+ /**
230
+ * Set up event handlers for the dashboard server
231
+ */
232
+ function setupEventHandlers(server) {
233
+ // Session events
234
+ server.on('session:connected', (sessionId, session) => {
235
+ console.log(`[${new Date().toISOString()}] Session connected: ${sessionId}`);
236
+ });
237
+
238
+ server.on('session:disconnected', (sessionId) => {
239
+ console.log(`[${new Date().toISOString()}] Session disconnected: ${sessionId}`);
240
+ });
241
+
242
+ // User message handler - use Claude CLI bridge
243
+ server.on('user:message', async (session, content) => {
244
+ console.log(`[${new Date().toISOString()}] Message from ${session.id}: ${content.slice(0, 50)}...`);
245
+
246
+ try {
247
+ await handleClaudeMessage(session, content, server.projectRoot);
248
+ } catch (error) {
249
+ console.error(`[${new Date().toISOString()}] Claude error:`, error.message);
250
+ session.send(createNotification('error', 'Error', error.message));
251
+ session.setState('error');
252
+ }
253
+ });
254
+
255
+ // Cancel handler
256
+ server.on('user:cancel', (session) => {
257
+ console.log(`[${new Date().toISOString()}] Cancel from ${session.id}`);
258
+ });
259
+
260
+ // Refresh handlers
261
+ server.on('refresh:tasks', (session) => {
262
+ // Send task list update
263
+ console.log(`[${new Date().toISOString()}] Task refresh for ${session.id}`);
264
+ });
265
+
266
+ server.on('refresh:status', (session) => {
267
+ // Send status update
268
+ console.log(`[${new Date().toISOString()}] Status refresh for ${session.id}`);
269
+ });
270
+ }
271
+
272
+ /**
273
+ * Handle message by calling Claude CLI
274
+ */
275
+ async function handleClaudeMessage(session, content, projectRoot) {
276
+ let fullResponse = '';
277
+
278
+ const bridge = createClaudeBridge({
279
+ cwd: projectRoot,
280
+ onInit: (info) => {
281
+ console.log(`[${new Date().toISOString()}] Claude session: ${info.sessionId}, model: ${info.model}`);
282
+ },
283
+ onText: (text, done) => {
284
+ if (text) {
285
+ fullResponse += text;
286
+ session.send(createTextDelta(text, done));
287
+ }
288
+ if (done) {
289
+ session.addMessage('assistant', fullResponse);
290
+ session.setState('idle');
291
+ console.log(`[${new Date().toISOString()}] Response complete`);
292
+ }
293
+ },
294
+ onToolStart: (id, name, input) => {
295
+ // Special handling for AskUserQuestion - send to dashboard for UI
296
+ if (name === 'AskUserQuestion' && input?.questions) {
297
+ session.send(createAskUserQuestion(id, input.questions));
298
+ }
299
+ session.send(createToolStart(id, name, input));
300
+ },
301
+ onToolResult: (id, output, isError, toolName) => {
302
+ session.send(createToolResult(id, { content: output, error: isError }, toolName));
303
+ },
304
+ onError: (error) => {
305
+ console.error(`[${new Date().toISOString()}] Claude error:`, error);
306
+ session.send(createNotification('error', 'Claude Error', error));
307
+ },
308
+ onComplete: (response) => {
309
+ // Already handled in onText with done=true
310
+ },
311
+ });
312
+
313
+ try {
314
+ await bridge.sendMessage(content);
315
+ } catch (error) {
316
+ session.send(createNotification('error', 'Error', error.message));
317
+ session.setState('error');
318
+ throw error;
319
+ }
320
+ }
321
+
322
+ // Run main
323
+ main().catch(console.error);