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.
- package/CHANGELOG.md +10 -0
- package/README.md +6 -6
- package/lib/api-routes.js +605 -0
- package/lib/api-server.js +260 -0
- package/lib/claude-cli-bridge.js +221 -0
- package/lib/dashboard-protocol.js +541 -0
- package/lib/dashboard-server.js +1601 -0
- package/lib/drivers/claude-driver.ts +310 -0
- package/lib/drivers/codex-driver.ts +454 -0
- package/lib/drivers/driver-manager.ts +158 -0
- package/lib/drivers/gemini-driver.ts +485 -0
- package/lib/drivers/index.ts +17 -0
- package/lib/flag-detection.js +350 -0
- package/lib/git-operations.js +267 -0
- package/lib/lock-file.js +144 -0
- package/lib/merge-operations.js +959 -0
- package/lib/protocol/driver.ts +360 -0
- package/lib/protocol/index.ts +12 -0
- package/lib/protocol/ir.ts +271 -0
- package/lib/session-display.js +330 -0
- package/lib/worktree-operations.js +221 -0
- package/package.json +2 -2
- package/scripts/agileflow-welcome.js +272 -24
- package/scripts/api-server-runner.js +177 -0
- package/scripts/archive-completed-stories.sh +22 -0
- package/scripts/automation-run-due.js +126 -0
- package/scripts/backfill-ideation-status.js +124 -0
- package/scripts/claude-tmux.sh +62 -1
- package/scripts/context-loader.js +292 -0
- package/scripts/dashboard-serve.js +323 -0
- package/scripts/lib/automation-registry.js +544 -0
- package/scripts/lib/automation-runner.js +476 -0
- package/scripts/lib/concurrency-limiter.js +513 -0
- package/scripts/lib/configure-features.js +46 -0
- package/scripts/lib/context-formatter.js +61 -0
- package/scripts/lib/damage-control-utils.js +29 -4
- package/scripts/lib/hook-metrics.js +324 -0
- package/scripts/lib/ideation-index.js +1196 -0
- package/scripts/lib/process-cleanup.js +359 -0
- package/scripts/lib/quality-gates.js +574 -0
- package/scripts/lib/status-task-bridge.js +522 -0
- package/scripts/lib/sync-ideation-status.js +292 -0
- package/scripts/lib/task-registry-cache.js +490 -0
- package/scripts/lib/task-registry.js +1181 -0
- package/scripts/migrate-ideation-index.js +515 -0
- package/scripts/precompact-context.sh +104 -0
- package/scripts/ralph-loop.js +2 -2
- package/scripts/session-manager.js +363 -2770
- package/scripts/spawn-parallel.js +45 -9
- package/src/core/agents/api-validator.md +180 -0
- package/src/core/agents/api.md +2 -0
- package/src/core/agents/code-reviewer.md +289 -0
- package/src/core/agents/configuration/damage-control.md +17 -0
- package/src/core/agents/database.md +2 -0
- package/src/core/agents/error-analyzer.md +203 -0
- package/src/core/agents/logic-analyzer-edge.md +171 -0
- package/src/core/agents/logic-analyzer-flow.md +254 -0
- package/src/core/agents/logic-analyzer-invariant.md +207 -0
- package/src/core/agents/logic-analyzer-race.md +267 -0
- package/src/core/agents/logic-analyzer-type.md +218 -0
- package/src/core/agents/logic-consensus.md +256 -0
- package/src/core/agents/orchestrator.md +89 -1
- package/src/core/agents/schema-validator.md +451 -0
- package/src/core/agents/team-coordinator.md +328 -0
- package/src/core/agents/ui-validator.md +328 -0
- package/src/core/agents/ui.md +2 -0
- package/src/core/commands/api.md +267 -0
- package/src/core/commands/automate.md +415 -0
- package/src/core/commands/babysit.md +290 -9
- package/src/core/commands/ideate/history.md +403 -0
- package/src/core/commands/{ideate.md → ideate/new.md} +244 -34
- package/src/core/commands/logic/audit.md +368 -0
- package/src/core/commands/roadmap/analyze.md +1 -1
- package/src/core/experts/documentation/expertise.yaml +29 -2
- package/src/core/templates/CONTEXT.md.example +49 -0
- package/src/core/templates/claude-settings.advanced.example.json +4 -0
- package/tools/cli/commands/serve.js +456 -0
- package/tools/cli/installers/core/installer.js +7 -2
- package/tools/cli/installers/ide/claude-code.js +85 -0
- package/tools/cli/lib/content-injector.js +27 -1
- 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);
|