agileflow 2.99.8 → 3.0.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 +5 -0
- 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 +184 -133
- 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/auto-self-improve.js +3 -3
- package/scripts/claude-smart.sh +67 -0
- package/scripts/claude-tmux.sh +248 -161
- package/scripts/damage-control-multi-agent.js +227 -0
- package/scripts/lib/bus-utils.js +471 -0
- package/scripts/lib/configure-detect.js +5 -6
- package/scripts/lib/configure-features.js +44 -0
- 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/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 +105 -2
- 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
package/scripts/team-manager.js
CHANGED
|
@@ -54,6 +54,33 @@ function getFeatureFlags() {
|
|
|
54
54
|
return _featureFlags;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
// Lazy-load file-lock for atomic writes
|
|
58
|
+
let _fileLock;
|
|
59
|
+
function getFileLock() {
|
|
60
|
+
if (!_fileLock) {
|
|
61
|
+
try {
|
|
62
|
+
_fileLock = require('./lib/file-lock');
|
|
63
|
+
} catch (e) {
|
|
64
|
+
// Fall back to direct writes
|
|
65
|
+
_fileLock = null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return _fileLock;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Lazy-load messaging bridge for event logging
|
|
72
|
+
let _messagingBridge;
|
|
73
|
+
function getMessagingBridge() {
|
|
74
|
+
if (!_messagingBridge) {
|
|
75
|
+
try {
|
|
76
|
+
_messagingBridge = require('./messaging-bridge');
|
|
77
|
+
} catch (e) {
|
|
78
|
+
_messagingBridge = null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return _messagingBridge;
|
|
82
|
+
}
|
|
83
|
+
|
|
57
84
|
/**
|
|
58
85
|
* Find the teams directory
|
|
59
86
|
*/
|
|
@@ -124,7 +151,33 @@ function getTemplate(rootDir, name) {
|
|
|
124
151
|
}
|
|
125
152
|
|
|
126
153
|
/**
|
|
127
|
-
*
|
|
154
|
+
* Build the native TeamCreate payload from an AgileFlow template.
|
|
155
|
+
* This structures the template data in the format expected by Claude Code's
|
|
156
|
+
* experimental Agent Teams TeamCreate tool.
|
|
157
|
+
*
|
|
158
|
+
* @param {object} template - Parsed team template
|
|
159
|
+
* @param {string} templateName - Template name
|
|
160
|
+
* @returns {object} Native TeamCreate payload
|
|
161
|
+
*/
|
|
162
|
+
function buildNativeTeamPayload(template, templateName) {
|
|
163
|
+
return {
|
|
164
|
+
name: template.name || templateName,
|
|
165
|
+
description: template.description || `AgileFlow team: ${templateName}`,
|
|
166
|
+
teammates: (template.teammates || []).map(t => ({
|
|
167
|
+
name: t.agent,
|
|
168
|
+
role: t.role || t.domain,
|
|
169
|
+
instructions: t.instructions || `${t.role} agent for ${t.domain}`,
|
|
170
|
+
})),
|
|
171
|
+
delegate_mode: template.delegate_mode !== false,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Start a team from a template.
|
|
177
|
+
* When native Agent Teams is enabled, builds a TeamCreate-compatible payload.
|
|
178
|
+
* When disabled, falls back to subagent orchestration mode.
|
|
179
|
+
*
|
|
180
|
+
* Uses atomic writes for session-state.json to prevent concurrent write conflicts.
|
|
128
181
|
*/
|
|
129
182
|
function startTeam(rootDir, templateName) {
|
|
130
183
|
const ff = getFeatureFlags();
|
|
@@ -136,51 +189,100 @@ function startTeam(rootDir, templateName) {
|
|
|
136
189
|
|
|
137
190
|
const template = result.template;
|
|
138
191
|
|
|
139
|
-
//
|
|
192
|
+
// Build native payload when in native mode
|
|
193
|
+
let nativePayload = null;
|
|
194
|
+
if (mode === 'native') {
|
|
195
|
+
nativePayload = buildNativeTeamPayload(template, templateName);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Generate trace ID for correlating events across the team lifecycle
|
|
199
|
+
const traceId = `trace-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
200
|
+
|
|
201
|
+
// Record active team in session-state.json using atomic writes
|
|
140
202
|
try {
|
|
141
203
|
const paths = getPaths();
|
|
142
204
|
const sessionStatePath = paths.getSessionStatePath(rootDir);
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
205
|
+
const fileLock = getFileLock();
|
|
206
|
+
|
|
207
|
+
const updateState = (state) => {
|
|
208
|
+
state.active_team = {
|
|
209
|
+
template: templateName,
|
|
210
|
+
mode,
|
|
211
|
+
trace_id: traceId,
|
|
212
|
+
native_payload: nativePayload,
|
|
213
|
+
lead: template.lead,
|
|
214
|
+
teammates: template.teammates.map(t => ({
|
|
215
|
+
agent: t.agent,
|
|
216
|
+
role: t.role,
|
|
217
|
+
domain: t.domain,
|
|
218
|
+
status: 'pending',
|
|
219
|
+
})),
|
|
220
|
+
quality_gates: template.quality_gates,
|
|
221
|
+
started_at: new Date().toISOString(),
|
|
222
|
+
};
|
|
147
223
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
started_at: new Date().toISOString(),
|
|
160
|
-
};
|
|
224
|
+
state.team_metrics = {
|
|
225
|
+
started_at: new Date().toISOString(),
|
|
226
|
+
template: templateName,
|
|
227
|
+
mode,
|
|
228
|
+
trace_id: traceId,
|
|
229
|
+
teammate_count: template.teammates.length,
|
|
230
|
+
tasks_assigned: 0,
|
|
231
|
+
tasks_completed: 0,
|
|
232
|
+
messages_sent: 0,
|
|
233
|
+
gate_runs: [],
|
|
234
|
+
};
|
|
161
235
|
|
|
162
|
-
|
|
163
|
-
started_at: new Date().toISOString(),
|
|
164
|
-
template: templateName,
|
|
165
|
-
teammate_count: template.teammates.length,
|
|
166
|
-
tasks_assigned: 0,
|
|
167
|
-
tasks_completed: 0,
|
|
168
|
-
messages_sent: 0,
|
|
169
|
-
gate_runs: [],
|
|
236
|
+
return state;
|
|
170
237
|
};
|
|
171
238
|
|
|
172
|
-
|
|
239
|
+
if (fileLock) {
|
|
240
|
+
// Use atomic read-modify-write when available
|
|
241
|
+
if (fs.existsSync(sessionStatePath)) {
|
|
242
|
+
fileLock.atomicReadModifyWrite(sessionStatePath, updateState);
|
|
243
|
+
} else {
|
|
244
|
+
fileLock.atomicWriteJSON(sessionStatePath, updateState({}));
|
|
245
|
+
}
|
|
246
|
+
} else {
|
|
247
|
+
// Fallback to direct write
|
|
248
|
+
let state = {};
|
|
249
|
+
if (fs.existsSync(sessionStatePath)) {
|
|
250
|
+
state = JSON.parse(fs.readFileSync(sessionStatePath, 'utf8'));
|
|
251
|
+
}
|
|
252
|
+
state = updateState(state);
|
|
253
|
+
fs.writeFileSync(sessionStatePath, JSON.stringify(state, null, 2) + '\n');
|
|
254
|
+
}
|
|
173
255
|
} catch (e) {
|
|
174
256
|
// Non-critical - team can still function without state tracking
|
|
175
257
|
}
|
|
176
258
|
|
|
259
|
+
// Log team_created event to bus
|
|
260
|
+
try {
|
|
261
|
+
const bridge = getMessagingBridge();
|
|
262
|
+
if (bridge) {
|
|
263
|
+
bridge.sendMessage(rootDir, {
|
|
264
|
+
from: 'team-manager',
|
|
265
|
+
to: 'team-lead',
|
|
266
|
+
type: 'team_created',
|
|
267
|
+
template: templateName,
|
|
268
|
+
mode,
|
|
269
|
+
trace_id: traceId,
|
|
270
|
+
teammate_count: template.teammates.length,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
} catch (e) {
|
|
274
|
+
// Non-critical
|
|
275
|
+
}
|
|
276
|
+
|
|
177
277
|
return {
|
|
178
278
|
ok: true,
|
|
179
279
|
mode,
|
|
280
|
+
trace_id: traceId,
|
|
180
281
|
template: templateName,
|
|
181
282
|
lead: template.lead,
|
|
182
283
|
teammates: template.teammates,
|
|
183
284
|
quality_gates: template.quality_gates,
|
|
285
|
+
native_payload: nativePayload,
|
|
184
286
|
};
|
|
185
287
|
}
|
|
186
288
|
|
|
@@ -212,7 +314,9 @@ function getTeamStatus(rootDir) {
|
|
|
212
314
|
}
|
|
213
315
|
|
|
214
316
|
/**
|
|
215
|
-
* Stop active team
|
|
317
|
+
* Stop active team.
|
|
318
|
+
* Uses atomic writes for session-state.json.
|
|
319
|
+
* Logs team_stopped event to bus.
|
|
216
320
|
*/
|
|
217
321
|
function stopTeam(rootDir) {
|
|
218
322
|
try {
|
|
@@ -238,7 +342,46 @@ function stopTeam(rootDir) {
|
|
|
238
342
|
|
|
239
343
|
// Clear active team
|
|
240
344
|
delete state.active_team;
|
|
241
|
-
|
|
345
|
+
|
|
346
|
+
// Write atomically
|
|
347
|
+
const fileLock = getFileLock();
|
|
348
|
+
if (fileLock) {
|
|
349
|
+
fileLock.atomicWriteJSON(sessionStatePath, state);
|
|
350
|
+
} else {
|
|
351
|
+
fs.writeFileSync(sessionStatePath, JSON.stringify(state, null, 2) + '\n');
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Log team_stopped event
|
|
355
|
+
try {
|
|
356
|
+
const bridge = getMessagingBridge();
|
|
357
|
+
if (bridge) {
|
|
358
|
+
bridge.sendMessage(rootDir, {
|
|
359
|
+
from: 'team-manager',
|
|
360
|
+
to: 'system',
|
|
361
|
+
type: 'team_stopped',
|
|
362
|
+
template: team.template,
|
|
363
|
+
mode: team.mode,
|
|
364
|
+
trace_id: team.trace_id,
|
|
365
|
+
duration_ms: duration,
|
|
366
|
+
tasks_completed: state.team_metrics ? state.team_metrics.tasks_completed : 0,
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
} catch (e) {
|
|
370
|
+
// Non-critical
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Aggregate and save team metrics by trace_id
|
|
374
|
+
try {
|
|
375
|
+
if (team.trace_id) {
|
|
376
|
+
const teamEvents = require('./lib/team-events');
|
|
377
|
+
const metrics = teamEvents.aggregateTeamMetrics(rootDir, team.trace_id);
|
|
378
|
+
if (metrics.ok) {
|
|
379
|
+
teamEvents.saveAggregatedMetrics(rootDir, metrics);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
} catch (e) {
|
|
383
|
+
// Non-critical - metrics aggregation is best-effort
|
|
384
|
+
}
|
|
242
385
|
|
|
243
386
|
return {
|
|
244
387
|
ok: true,
|
|
@@ -314,6 +457,7 @@ module.exports = {
|
|
|
314
457
|
getTeamStatus,
|
|
315
458
|
stopTeam,
|
|
316
459
|
getTeamsDir,
|
|
460
|
+
buildNativeTeamPayload,
|
|
317
461
|
};
|
|
318
462
|
|
|
319
463
|
// Run CLI if invoked directly
|
|
@@ -312,18 +312,19 @@ NEXT STEPS:
|
|
|
312
312
|
|
|
313
313
|
### If PASS
|
|
314
314
|
|
|
315
|
-
Offer to mark story complete:
|
|
315
|
+
Offer to mark story complete with full context:
|
|
316
316
|
|
|
317
317
|
```xml
|
|
318
318
|
<invoke name="AskUserQuestion">
|
|
319
319
|
<parameter name="questions">[{
|
|
320
|
-
"question": "
|
|
321
|
-
"header": "
|
|
320
|
+
"question": "[STORY] passed audit: [test_count] tests passing, [ac_count]/[ac_total] AC verified. What next?",
|
|
321
|
+
"header": "Complete",
|
|
322
322
|
"multiSelect": false,
|
|
323
323
|
"options": [
|
|
324
|
-
{"label": "Mark complete (Recommended)", "description": "Update status to done"},
|
|
325
|
-
{"label": "
|
|
326
|
-
{"label": "
|
|
324
|
+
{"label": "Mark [STORY] complete (Recommended)", "description": "Update status to done - all AC verified, tests passing"},
|
|
325
|
+
{"label": "Run tests once more before completing", "description": "Re-run test suite to double-check [test_count] tests"},
|
|
326
|
+
{"label": "View [EPIC] progress", "description": "Check epic completion: [completed]/[total] stories done ([percent]%)"},
|
|
327
|
+
{"label": "Done without marking complete", "description": "Audit passed but don't update status yet"}
|
|
327
328
|
]
|
|
328
329
|
}]</parameter>
|
|
329
330
|
</invoke>
|
|
@@ -336,18 +337,19 @@ If "Mark complete":
|
|
|
336
337
|
|
|
337
338
|
### If FAIL
|
|
338
339
|
|
|
339
|
-
Offer to help fix issues:
|
|
340
|
+
Offer to help fix issues with specific context:
|
|
340
341
|
|
|
341
342
|
```xml
|
|
342
343
|
<invoke name="AskUserQuestion">
|
|
343
344
|
<parameter name="questions">[{
|
|
344
|
-
"question": "
|
|
345
|
+
"question": "[STORY] failed audit: [fail_count] tests failing, [unverified] AC unverified. How to proceed?",
|
|
345
346
|
"header": "Fix Issues",
|
|
346
347
|
"multiSelect": false,
|
|
347
348
|
"options": [
|
|
348
|
-
{"label": "Fix failing
|
|
349
|
-
{"label": "
|
|
350
|
-
{"label": "
|
|
349
|
+
{"label": "Fix [fail_count] failing test(s) (Recommended)", "description": "Debug: [first_failing_test_name] and [fail_count-1] other(s)"},
|
|
350
|
+
{"label": "Implement [unverified] missing AC item(s)", "description": "AC not met: [first_unverified_ac_text]"},
|
|
351
|
+
{"label": "Re-run audit after manual fixes", "description": "I'll fix issues myself, then re-audit"},
|
|
352
|
+
{"label": "Done for now", "description": "Audit failed - [STORY] stays in_progress"}
|
|
351
353
|
]
|
|
352
354
|
}]</parameter>
|
|
353
355
|
</invoke>
|