agileflow 2.96.0 → 2.96.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 +10 -0
- package/lib/claude-cli-bridge.js +18 -24
- package/lib/dashboard-protocol.js +48 -48
- package/lib/drivers/claude-driver.ts +62 -60
- package/lib/drivers/codex-driver.ts +98 -88
- package/lib/drivers/driver-manager.ts +8 -7
- package/lib/drivers/gemini-driver.ts +121 -108
- package/lib/drivers/index.ts +4 -4
- package/lib/flag-detection.js +10 -9
- package/lib/merge-operations.js +8 -1
- package/lib/protocol/driver.ts +76 -82
- package/lib/protocol/index.ts +2 -2
- package/lib/protocol/ir.ts +40 -40
- package/lib/session-display.js +7 -6
- package/lib/yaml-utils.js +28 -1
- package/package.json +1 -1
- package/scripts/agileflow-welcome.js +2 -6
- package/scripts/automation-run-due.js +3 -1
- package/scripts/backfill-ideation-status.js +6 -2
- package/scripts/dashboard-serve.js +28 -15
- package/scripts/lib/concurrency-limiter.js +1 -3
- package/scripts/lib/context-formatter.js +3 -1
- package/scripts/lib/ideation-index.js +18 -6
- package/scripts/lib/process-cleanup.js +6 -1
- package/scripts/lib/sync-ideation-status.js +4 -4
- package/scripts/migrate-ideation-index.js +28 -13
- package/scripts/session-manager.js +471 -119
- package/scripts/spawn-parallel.js +3 -1
- package/tools/cli/commands/serve.js +54 -27
- package/tools/cli/installers/core/installer.js +9 -1
- package/tools/cli/installers/ide/claude-code.js +4 -2
- package/tools/cli/lib/content-injector.js +7 -5
- package/tools/cli/lib/docs-setup.js +27 -7
- package/tools/cli/lib/ui.js +12 -5
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
CLICommand,
|
|
22
22
|
IREventHandler,
|
|
23
23
|
GEMINI_CAPABILITIES,
|
|
24
|
-
} from
|
|
24
|
+
} from '../protocol/driver';
|
|
25
25
|
import {
|
|
26
26
|
IREnvelope,
|
|
27
27
|
IRSource,
|
|
@@ -34,28 +34,28 @@ import {
|
|
|
34
34
|
IRError,
|
|
35
35
|
IRTask,
|
|
36
36
|
IRNotification,
|
|
37
|
-
} from
|
|
37
|
+
} from '../protocol/ir';
|
|
38
38
|
|
|
39
39
|
// ============================================================================
|
|
40
40
|
// Tool Name Mapping (Gemini -> IR normalized names)
|
|
41
41
|
// ============================================================================
|
|
42
42
|
|
|
43
43
|
const GEMINI_TOOL_MAPPING: Record<string, string> = {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
44
|
+
read_file: 'file_read',
|
|
45
|
+
write_file: 'file_write',
|
|
46
|
+
replace: 'file_edit',
|
|
47
|
+
run_shell_command: 'shell_exec',
|
|
48
|
+
glob: 'file_glob',
|
|
49
|
+
search_file_content: 'file_grep',
|
|
50
|
+
google_web_search: 'web_search',
|
|
51
|
+
fetch_url: 'web_fetch',
|
|
52
|
+
create_checkpoint: 'checkpoint_create',
|
|
53
|
+
restore_checkpoint: 'checkpoint_restore',
|
|
54
|
+
list_checkpoints: 'checkpoint_list',
|
|
55
|
+
conductor_run: 'conductor_run',
|
|
56
|
+
task_create: 'todo_create',
|
|
57
|
+
task_update: 'todo_update',
|
|
58
|
+
task_list: 'todo_list',
|
|
59
59
|
};
|
|
60
60
|
|
|
61
61
|
/**
|
|
@@ -77,7 +77,7 @@ interface RateLimitState {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
const RATE_LIMITS = {
|
|
80
|
-
rpm: 60,
|
|
80
|
+
rpm: 60, // Requests per minute
|
|
81
81
|
rpd: 1000, // Requests per day
|
|
82
82
|
};
|
|
83
83
|
|
|
@@ -86,22 +86,25 @@ const RATE_LIMITS = {
|
|
|
86
86
|
// ============================================================================
|
|
87
87
|
|
|
88
88
|
export class GeminiDriver implements Driver {
|
|
89
|
-
readonly id: IRSource =
|
|
90
|
-
readonly name =
|
|
89
|
+
readonly id: IRSource = 'gemini';
|
|
90
|
+
readonly name = 'Gemini CLI';
|
|
91
91
|
|
|
92
92
|
private _status: DriverStatus = {
|
|
93
|
-
state:
|
|
94
|
-
provider:
|
|
93
|
+
state: 'stopped',
|
|
94
|
+
provider: 'gemini',
|
|
95
95
|
};
|
|
96
96
|
|
|
97
97
|
private _capabilities: Capability[] = [...GEMINI_CAPABILITIES];
|
|
98
98
|
private _eventHandlers: Set<IREventHandler> = new Set();
|
|
99
|
-
private _sessions: Map<
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
99
|
+
private _sessions: Map<
|
|
100
|
+
string,
|
|
101
|
+
{
|
|
102
|
+
config: DriverConfig;
|
|
103
|
+
seqCounter: number;
|
|
104
|
+
checkpoints: string[];
|
|
105
|
+
conductorWorkflow?: string;
|
|
106
|
+
}
|
|
107
|
+
> = new Map();
|
|
105
108
|
|
|
106
109
|
// Rate limiting state
|
|
107
110
|
private _rateLimit: RateLimitState = {
|
|
@@ -131,21 +134,21 @@ export class GeminiDriver implements Driver {
|
|
|
131
134
|
});
|
|
132
135
|
|
|
133
136
|
this._status = {
|
|
134
|
-
state:
|
|
135
|
-
provider:
|
|
136
|
-
model: config.model ||
|
|
137
|
+
state: 'ready',
|
|
138
|
+
provider: 'gemini',
|
|
139
|
+
model: config.model || 'gemini-2.0-flash',
|
|
137
140
|
contextMax: config.maxTokens || 1000000, // 1M context
|
|
138
141
|
};
|
|
139
142
|
|
|
140
143
|
// Emit init event
|
|
141
144
|
const initPayload: IRInit = {
|
|
142
|
-
provider:
|
|
143
|
-
version:
|
|
145
|
+
provider: 'gemini',
|
|
146
|
+
version: '1.0.0',
|
|
144
147
|
capabilities: this._capabilities.filter(c => c.available).map(c => c.name),
|
|
145
148
|
maxContext: config.maxTokens || 1000000,
|
|
146
149
|
};
|
|
147
150
|
|
|
148
|
-
this._emit(createIREnvelope(
|
|
151
|
+
this._emit(createIREnvelope('init', sessionId, 'gemini', initPayload));
|
|
149
152
|
}
|
|
150
153
|
|
|
151
154
|
async stop(sessionId: string): Promise<void> {
|
|
@@ -154,7 +157,7 @@ export class GeminiDriver implements Driver {
|
|
|
154
157
|
if (this._sessions.size === 0) {
|
|
155
158
|
this._status = {
|
|
156
159
|
...this._status,
|
|
157
|
-
state:
|
|
160
|
+
state: 'stopped',
|
|
158
161
|
};
|
|
159
162
|
}
|
|
160
163
|
}
|
|
@@ -223,26 +226,28 @@ export class GeminiDriver implements Driver {
|
|
|
223
226
|
// Check rate limits
|
|
224
227
|
const rateCheck = this._checkRateLimit();
|
|
225
228
|
if (!rateCheck.allowed) {
|
|
226
|
-
this._emit(
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
229
|
+
this._emit(
|
|
230
|
+
createIREnvelope<IRError>('error', sessionId, 'gemini', {
|
|
231
|
+
code: 'RATE_LIMITED',
|
|
232
|
+
message: `Rate limit exceeded. Retry after ${Math.ceil((rateCheck.retryAfterMs || 0) / 1000)} seconds.`,
|
|
233
|
+
details: {
|
|
234
|
+
rpm: this._rateLimit.requestsThisMinute,
|
|
235
|
+
rpd: this._rateLimit.requestsToday,
|
|
236
|
+
limits: RATE_LIMITS,
|
|
237
|
+
},
|
|
238
|
+
recoverable: true,
|
|
239
|
+
})
|
|
240
|
+
);
|
|
236
241
|
return;
|
|
237
242
|
}
|
|
238
243
|
|
|
239
244
|
this._recordRequest();
|
|
240
|
-
this._status = { ...this._status, state:
|
|
245
|
+
this._status = { ...this._status, state: 'busy' };
|
|
241
246
|
|
|
242
247
|
const sessionPayload: IRSession = {
|
|
243
|
-
state:
|
|
248
|
+
state: 'thinking',
|
|
244
249
|
};
|
|
245
|
-
this._emit(createIREnvelope(
|
|
250
|
+
this._emit(createIREnvelope('session', sessionId, 'gemini', sessionPayload));
|
|
246
251
|
}
|
|
247
252
|
|
|
248
253
|
// -------------------------------------------------------------------------
|
|
@@ -254,16 +259,18 @@ export class GeminiDriver implements Driver {
|
|
|
254
259
|
*/
|
|
255
260
|
async createCheckpoint(sessionId: string, name: string): Promise<string> {
|
|
256
261
|
const session = this._sessions.get(sessionId);
|
|
257
|
-
if (!session) throw new Error(
|
|
262
|
+
if (!session) throw new Error('Session not found');
|
|
258
263
|
|
|
259
264
|
const checkpointId = `ckpt_${Date.now()}_${name}`;
|
|
260
265
|
session.checkpoints.push(checkpointId);
|
|
261
266
|
|
|
262
|
-
this._emit(
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
+
this._emit(
|
|
268
|
+
createIREnvelope<IRNotification>('notification', sessionId, 'gemini', {
|
|
269
|
+
level: 'info',
|
|
270
|
+
title: 'Checkpoint Created',
|
|
271
|
+
message: `Checkpoint "${name}" created (${checkpointId})`,
|
|
272
|
+
})
|
|
273
|
+
);
|
|
267
274
|
|
|
268
275
|
return checkpointId;
|
|
269
276
|
}
|
|
@@ -273,16 +280,18 @@ export class GeminiDriver implements Driver {
|
|
|
273
280
|
*/
|
|
274
281
|
async restoreCheckpoint(sessionId: string, checkpointId: string): Promise<void> {
|
|
275
282
|
const session = this._sessions.get(sessionId);
|
|
276
|
-
if (!session) throw new Error(
|
|
283
|
+
if (!session) throw new Error('Session not found');
|
|
277
284
|
|
|
278
285
|
if (!session.checkpoints.includes(checkpointId)) {
|
|
279
286
|
throw new Error(`Checkpoint not found: ${checkpointId}`);
|
|
280
287
|
}
|
|
281
288
|
|
|
282
|
-
this._emit(
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
289
|
+
this._emit(
|
|
290
|
+
createIREnvelope<IRSession>('session', sessionId, 'gemini', {
|
|
291
|
+
state: 'idle',
|
|
292
|
+
message: `Restored checkpoint: ${checkpointId}`,
|
|
293
|
+
})
|
|
294
|
+
);
|
|
286
295
|
}
|
|
287
296
|
|
|
288
297
|
/**
|
|
@@ -302,15 +311,17 @@ export class GeminiDriver implements Driver {
|
|
|
302
311
|
*/
|
|
303
312
|
async startConductor(sessionId: string, workflowName: string): Promise<void> {
|
|
304
313
|
const session = this._sessions.get(sessionId);
|
|
305
|
-
if (!session) throw new Error(
|
|
314
|
+
if (!session) throw new Error('Session not found');
|
|
306
315
|
|
|
307
316
|
session.conductorWorkflow = workflowName;
|
|
308
317
|
|
|
309
|
-
this._emit(
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
318
|
+
this._emit(
|
|
319
|
+
createIREnvelope<IRNotification>('notification', sessionId, 'gemini', {
|
|
320
|
+
level: 'info',
|
|
321
|
+
title: 'Conductor Started',
|
|
322
|
+
message: `Starting workflow: ${workflowName}`,
|
|
323
|
+
})
|
|
324
|
+
);
|
|
314
325
|
}
|
|
315
326
|
|
|
316
327
|
// -------------------------------------------------------------------------
|
|
@@ -325,78 +336,80 @@ export class GeminiDriver implements Driver {
|
|
|
325
336
|
if (!session) return null;
|
|
326
337
|
|
|
327
338
|
switch (geminiMessage.type) {
|
|
328
|
-
case
|
|
329
|
-
return createIREnvelope<IRTextDelta>(
|
|
330
|
-
text: geminiMessage.content ||
|
|
339
|
+
case 'message':
|
|
340
|
+
return createIREnvelope<IRTextDelta>('text_delta', sessionId, 'gemini', {
|
|
341
|
+
text: geminiMessage.content || '',
|
|
331
342
|
done: !geminiMessage.delta,
|
|
332
343
|
});
|
|
333
344
|
|
|
334
|
-
case
|
|
335
|
-
return createIREnvelope<IRToolStart>(
|
|
345
|
+
case 'tool_use':
|
|
346
|
+
return createIREnvelope<IRToolStart>('tool_start', sessionId, 'gemini', {
|
|
336
347
|
id: geminiMessage.id || `tool_${Date.now()}`,
|
|
337
|
-
name: normalizeToolName(geminiMessage.tool ||
|
|
338
|
-
nativeName: geminiMessage.tool ||
|
|
348
|
+
name: normalizeToolName(geminiMessage.tool || ''),
|
|
349
|
+
nativeName: geminiMessage.tool || '',
|
|
339
350
|
input: geminiMessage.input || {},
|
|
340
351
|
});
|
|
341
352
|
|
|
342
|
-
case
|
|
343
|
-
return createIREnvelope<IRToolResult>(
|
|
344
|
-
id: geminiMessage.id ||
|
|
353
|
+
case 'tool_result':
|
|
354
|
+
return createIREnvelope<IRToolResult>('tool_result', sessionId, 'gemini', {
|
|
355
|
+
id: geminiMessage.id || '',
|
|
345
356
|
ok: !geminiMessage.error,
|
|
346
357
|
output: geminiMessage.output,
|
|
347
358
|
error: geminiMessage.error,
|
|
348
359
|
});
|
|
349
360
|
|
|
350
|
-
case
|
|
361
|
+
case 'checkpoint':
|
|
351
362
|
// Checkpoint created/restored
|
|
352
|
-
return createIREnvelope<IRNotification>(
|
|
353
|
-
level:
|
|
354
|
-
title: geminiMessage.action ===
|
|
363
|
+
return createIREnvelope<IRNotification>('notification', sessionId, 'gemini', {
|
|
364
|
+
level: 'info',
|
|
365
|
+
title: geminiMessage.action === 'create' ? 'Checkpoint Created' : 'Checkpoint Restored',
|
|
355
366
|
message: `Checkpoint: ${geminiMessage.checkpointId}`,
|
|
356
367
|
});
|
|
357
368
|
|
|
358
|
-
case
|
|
369
|
+
case 'conductor':
|
|
359
370
|
// Conductor workflow event
|
|
360
|
-
return createIREnvelope<IRNotification>(
|
|
361
|
-
level:
|
|
362
|
-
title:
|
|
371
|
+
return createIREnvelope<IRNotification>('notification', sessionId, 'gemini', {
|
|
372
|
+
level: 'info',
|
|
373
|
+
title: 'Conductor',
|
|
363
374
|
message: `Workflow ${geminiMessage.workflow}: ${geminiMessage.status}`,
|
|
364
375
|
});
|
|
365
376
|
|
|
366
|
-
case
|
|
367
|
-
return createIREnvelope<IRTask>(
|
|
368
|
-
action: geminiMessage.action as
|
|
369
|
-
task: geminiMessage.task
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
377
|
+
case 'task':
|
|
378
|
+
return createIREnvelope<IRTask>('task', sessionId, 'gemini', {
|
|
379
|
+
action: geminiMessage.action as 'create' | 'update' | 'delete' | 'list',
|
|
380
|
+
task: geminiMessage.task
|
|
381
|
+
? {
|
|
382
|
+
id: geminiMessage.task.id,
|
|
383
|
+
subject: geminiMessage.task.subject,
|
|
384
|
+
description: geminiMessage.task.description,
|
|
385
|
+
status: geminiMessage.task.status as 'pending' | 'in_progress' | 'completed',
|
|
386
|
+
}
|
|
387
|
+
: undefined,
|
|
388
|
+
tasks: geminiMessage.tasks?.map(t => ({
|
|
376
389
|
id: t.id,
|
|
377
390
|
subject: t.subject,
|
|
378
|
-
status: t.status as
|
|
391
|
+
status: t.status as 'pending' | 'in_progress' | 'completed',
|
|
379
392
|
})),
|
|
380
393
|
});
|
|
381
394
|
|
|
382
|
-
case
|
|
383
|
-
this._status = { ...this._status, state:
|
|
384
|
-
return createIREnvelope<IRSession>(
|
|
385
|
-
state:
|
|
395
|
+
case 'result':
|
|
396
|
+
this._status = { ...this._status, state: 'ready' };
|
|
397
|
+
return createIREnvelope<IRSession>('session', sessionId, 'gemini', {
|
|
398
|
+
state: 'idle',
|
|
386
399
|
});
|
|
387
400
|
|
|
388
|
-
case
|
|
389
|
-
return createIREnvelope<IRError>(
|
|
390
|
-
code: geminiMessage.code ||
|
|
391
|
-
message: geminiMessage.message ||
|
|
401
|
+
case 'error':
|
|
402
|
+
return createIREnvelope<IRError>('error', sessionId, 'gemini', {
|
|
403
|
+
code: geminiMessage.code || 'UNKNOWN',
|
|
404
|
+
message: geminiMessage.message || 'Unknown error',
|
|
392
405
|
details: geminiMessage.details,
|
|
393
406
|
recoverable: geminiMessage.recoverable ?? true,
|
|
394
407
|
});
|
|
395
408
|
|
|
396
|
-
case
|
|
397
|
-
return createIREnvelope<IRError>(
|
|
398
|
-
code:
|
|
399
|
-
message: geminiMessage.message ||
|
|
409
|
+
case 'rate_limit':
|
|
410
|
+
return createIREnvelope<IRError>('error', sessionId, 'gemini', {
|
|
411
|
+
code: 'RATE_LIMITED',
|
|
412
|
+
message: geminiMessage.message || 'Rate limit exceeded',
|
|
400
413
|
details: {
|
|
401
414
|
retryAfterSeconds: geminiMessage.retryAfter,
|
|
402
415
|
},
|
|
@@ -428,11 +441,11 @@ export class GeminiDriver implements Driver {
|
|
|
428
441
|
}
|
|
429
442
|
|
|
430
443
|
private _emit(envelope: IREnvelope): void {
|
|
431
|
-
Array.from(this._eventHandlers).forEach(
|
|
444
|
+
Array.from(this._eventHandlers).forEach(handler => {
|
|
432
445
|
try {
|
|
433
446
|
handler(envelope);
|
|
434
447
|
} catch (error) {
|
|
435
|
-
console.error(
|
|
448
|
+
console.error('[GeminiDriver] Event handler error:', error);
|
|
436
449
|
}
|
|
437
450
|
});
|
|
438
451
|
}
|
package/lib/drivers/index.ts
CHANGED
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
// Driver Manager
|
|
8
|
-
export { DefaultDriverManager, getDriverManager, resetDriverManager } from
|
|
8
|
+
export { DefaultDriverManager, getDriverManager, resetDriverManager } from './driver-manager';
|
|
9
9
|
|
|
10
10
|
// Claude Driver
|
|
11
|
-
export { ClaudeDriver, createClaudeDriver, type ClaudeNativeMessage } from
|
|
11
|
+
export { ClaudeDriver, createClaudeDriver, type ClaudeNativeMessage } from './claude-driver';
|
|
12
12
|
|
|
13
13
|
// Codex Driver
|
|
14
|
-
export { CodexDriver, createCodexDriver, type CodexJsonRpcMessage } from
|
|
14
|
+
export { CodexDriver, createCodexDriver, type CodexJsonRpcMessage } from './codex-driver';
|
|
15
15
|
|
|
16
16
|
// Gemini Driver
|
|
17
|
-
export { GeminiDriver, createGeminiDriver, type GeminiNdjsonMessage } from
|
|
17
|
+
export { GeminiDriver, createGeminiDriver, type GeminiNdjsonMessage } from './gemini-driver';
|
package/lib/flag-detection.js
CHANGED
|
@@ -61,8 +61,7 @@ function extractClaudeFlags(args) {
|
|
|
61
61
|
else if (arg === '--model' && args[i + 1]) {
|
|
62
62
|
flags.push(`${arg} ${args[i + 1]}`);
|
|
63
63
|
i++;
|
|
64
|
-
}
|
|
65
|
-
else if (arg.startsWith('--model=')) {
|
|
64
|
+
} else if (arg.startsWith('--model=')) {
|
|
66
65
|
flags.push(arg);
|
|
67
66
|
}
|
|
68
67
|
|
|
@@ -127,13 +126,15 @@ function isClaudeProcess(args) {
|
|
|
127
126
|
if (!args || args.length === 0) return false;
|
|
128
127
|
|
|
129
128
|
// Check if any arg contains 'claude' (handles various ways claude can be invoked)
|
|
130
|
-
for (const arg of args.slice(0, 3)) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
arg
|
|
134
|
-
arg.
|
|
135
|
-
|
|
136
|
-
|
|
129
|
+
for (const arg of args.slice(0, 3)) {
|
|
130
|
+
// Only check first few args
|
|
131
|
+
if (
|
|
132
|
+
arg &&
|
|
133
|
+
(arg.endsWith('/claude') ||
|
|
134
|
+
arg === 'claude' ||
|
|
135
|
+
arg.includes('claude-code') ||
|
|
136
|
+
arg.includes('@anthropic'))
|
|
137
|
+
) {
|
|
137
138
|
return true;
|
|
138
139
|
}
|
|
139
140
|
}
|
package/lib/merge-operations.js
CHANGED
|
@@ -732,7 +732,14 @@ function getMergeHistory() {
|
|
|
732
732
|
* @param {Function} unregisterSession - Session unregisterer
|
|
733
733
|
* @returns {Object} Smart merge result
|
|
734
734
|
*/
|
|
735
|
-
function smartMerge(
|
|
735
|
+
function smartMerge(
|
|
736
|
+
sessionId,
|
|
737
|
+
options = {},
|
|
738
|
+
loadRegistry,
|
|
739
|
+
saveRegistry,
|
|
740
|
+
removeLock,
|
|
741
|
+
unregisterSession
|
|
742
|
+
) {
|
|
736
743
|
const { c } = require('./colors');
|
|
737
744
|
const {
|
|
738
745
|
strategy = 'squash',
|