agileflow 3.3.0 → 3.4.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.
- package/CHANGELOG.md +10 -0
- package/README.md +6 -6
- package/lib/skill-loader.js +0 -1
- package/package.json +1 -1
- package/scripts/agileflow-statusline.sh +81 -0
- package/scripts/agileflow-welcome.js +79 -0
- package/scripts/claude-tmux.sh +90 -23
- package/scripts/claude-watchdog.sh +225 -0
- package/scripts/generators/agent-registry.js +14 -1
- package/scripts/generators/inject-babysit.js +22 -9
- package/scripts/generators/inject-help.js +19 -9
- package/scripts/lib/ac-test-matcher.js +452 -0
- package/scripts/lib/audit-cleanup.js +250 -0
- package/scripts/lib/audit-registry.js +304 -0
- package/scripts/lib/configure-features.js +35 -0
- package/scripts/lib/feature-catalog.js +3 -3
- package/scripts/lib/gate-enforcer.js +295 -0
- package/scripts/lib/model-profiles.js +118 -0
- package/scripts/lib/quality-gates.js +163 -0
- package/scripts/lib/signal-detectors.js +44 -1
- package/scripts/lib/skill-catalog.js +557 -0
- package/scripts/lib/skill-recommender.js +311 -0
- package/scripts/lib/status-writer.js +255 -0
- package/scripts/lib/story-claiming.js +128 -45
- package/scripts/lib/task-sync.js +32 -38
- package/scripts/lib/tdd-phase-manager.js +455 -0
- package/scripts/lib/team-events.js +34 -3
- package/scripts/lib/tmux-audit-monitor.js +611 -0
- package/scripts/lib/tmux-group-colors.js +113 -0
- package/scripts/lib/tool-registry.yaml +241 -0
- package/scripts/lib/tool-shed.js +441 -0
- package/scripts/messaging-bridge.js +209 -1
- package/scripts/native-team-observer.js +219 -0
- package/scripts/obtain-context.js +14 -0
- package/scripts/ralph-loop.js +30 -5
- package/scripts/smart-detect.js +21 -0
- package/scripts/spawn-audit-sessions.js +877 -0
- package/scripts/team-manager.js +56 -16
- package/scripts/tmux-close-windows.sh +180 -0
- package/src/core/agents/a11y-analyzer-aria.md +155 -0
- package/src/core/agents/a11y-analyzer-forms.md +162 -0
- package/src/core/agents/a11y-analyzer-keyboard.md +175 -0
- package/src/core/agents/a11y-analyzer-semantic.md +153 -0
- package/src/core/agents/a11y-analyzer-visual.md +158 -0
- package/src/core/agents/a11y-consensus.md +248 -0
- package/src/core/agents/ads-audit-budget.md +181 -0
- package/src/core/agents/ads-audit-compliance.md +169 -0
- package/src/core/agents/ads-audit-creative.md +164 -0
- package/src/core/agents/ads-audit-google.md +226 -0
- package/src/core/agents/ads-audit-meta.md +183 -0
- package/src/core/agents/ads-audit-tracking.md +197 -0
- package/src/core/agents/ads-consensus.md +396 -0
- package/src/core/agents/ads-generate.md +145 -0
- package/src/core/agents/ads-performance-tracker.md +197 -0
- package/src/core/agents/api-quality-analyzer-conventions.md +148 -0
- package/src/core/agents/api-quality-analyzer-docs.md +176 -0
- package/src/core/agents/api-quality-analyzer-errors.md +183 -0
- package/src/core/agents/api-quality-analyzer-pagination.md +171 -0
- package/src/core/agents/api-quality-analyzer-versioning.md +143 -0
- package/src/core/agents/api-quality-consensus.md +214 -0
- package/src/core/agents/arch-analyzer-circular.md +148 -0
- package/src/core/agents/arch-analyzer-complexity.md +171 -0
- package/src/core/agents/arch-analyzer-coupling.md +146 -0
- package/src/core/agents/arch-analyzer-layering.md +151 -0
- package/src/core/agents/arch-analyzer-patterns.md +162 -0
- package/src/core/agents/arch-consensus.md +227 -0
- package/src/core/agents/brainstorm-analyzer-features.md +169 -0
- package/src/core/agents/brainstorm-analyzer-growth.md +161 -0
- package/src/core/agents/brainstorm-analyzer-integration.md +172 -0
- package/src/core/agents/brainstorm-analyzer-market.md +147 -0
- package/src/core/agents/brainstorm-analyzer-ux.md +167 -0
- package/src/core/agents/brainstorm-consensus.md +237 -0
- package/src/core/agents/completeness-consensus.md +5 -5
- package/src/core/agents/perf-consensus.md +2 -2
- package/src/core/agents/security-consensus.md +2 -2
- package/src/core/agents/seo-analyzer-content.md +167 -0
- package/src/core/agents/seo-analyzer-images.md +187 -0
- package/src/core/agents/seo-analyzer-performance.md +206 -0
- package/src/core/agents/seo-analyzer-schema.md +176 -0
- package/src/core/agents/seo-analyzer-sitemap.md +172 -0
- package/src/core/agents/seo-analyzer-technical.md +144 -0
- package/src/core/agents/seo-consensus.md +289 -0
- package/src/core/agents/test-consensus.md +2 -2
- package/src/core/commands/adr.md +1 -0
- package/src/core/commands/ads/audit.md +375 -0
- package/src/core/commands/ads/budget.md +97 -0
- package/src/core/commands/ads/competitor.md +112 -0
- package/src/core/commands/ads/creative.md +85 -0
- package/src/core/commands/ads/generate.md +238 -0
- package/src/core/commands/ads/google.md +112 -0
- package/src/core/commands/ads/health.md +327 -0
- package/src/core/commands/ads/landing.md +119 -0
- package/src/core/commands/ads/linkedin.md +112 -0
- package/src/core/commands/ads/meta.md +91 -0
- package/src/core/commands/ads/microsoft.md +115 -0
- package/src/core/commands/ads/plan.md +321 -0
- package/src/core/commands/ads/test-plan.md +317 -0
- package/src/core/commands/ads/tiktok.md +129 -0
- package/src/core/commands/ads/track.md +288 -0
- package/src/core/commands/ads/youtube.md +124 -0
- package/src/core/commands/ads.md +140 -0
- package/src/core/commands/assign.md +1 -0
- package/src/core/commands/audit.md +43 -6
- package/src/core/commands/babysit.md +315 -1266
- package/src/core/commands/baseline.md +1 -0
- package/src/core/commands/blockers.md +1 -0
- package/src/core/commands/board.md +1 -0
- package/src/core/commands/changelog.md +1 -0
- package/src/core/commands/choose.md +1 -0
- package/src/core/commands/ci.md +1 -0
- package/src/core/commands/code/accessibility.md +347 -0
- package/src/core/commands/code/api.md +297 -0
- package/src/core/commands/code/architecture.md +297 -0
- package/src/core/commands/{audit → code}/completeness.md +72 -25
- package/src/core/commands/{audit → code}/legal.md +63 -16
- package/src/core/commands/{audit → code}/logic.md +64 -16
- package/src/core/commands/{audit → code}/performance.md +67 -20
- package/src/core/commands/{audit → code}/security.md +69 -19
- package/src/core/commands/{audit → code}/test.md +67 -20
- package/src/core/commands/configure.md +1 -0
- package/src/core/commands/council.md +1 -0
- package/src/core/commands/deploy.md +1 -0
- package/src/core/commands/diagnose.md +1 -0
- package/src/core/commands/docs.md +1 -0
- package/src/core/commands/epic/edit.md +213 -0
- package/src/core/commands/epic.md +1 -0
- package/src/core/commands/export.md +238 -0
- package/src/core/commands/help.md +16 -1
- package/src/core/commands/{discovery → ideate}/brief.md +12 -12
- package/src/core/commands/{discovery/new.md → ideate/discover.md} +20 -16
- package/src/core/commands/ideate/features.md +496 -0
- package/src/core/commands/ideate/new.md +158 -124
- package/src/core/commands/impact.md +1 -0
- package/src/core/commands/learn/explain.md +118 -0
- package/src/core/commands/learn/glossary.md +135 -0
- package/src/core/commands/learn/patterns.md +138 -0
- package/src/core/commands/learn/tour.md +126 -0
- package/src/core/commands/migrate/codemods.md +151 -0
- package/src/core/commands/migrate/plan.md +131 -0
- package/src/core/commands/migrate/scan.md +114 -0
- package/src/core/commands/migrate/validate.md +119 -0
- package/src/core/commands/multi-expert.md +1 -0
- package/src/core/commands/pr.md +1 -0
- package/src/core/commands/review.md +1 -0
- package/src/core/commands/seo/audit.md +373 -0
- package/src/core/commands/seo/competitor.md +174 -0
- package/src/core/commands/seo/content.md +107 -0
- package/src/core/commands/seo/geo.md +229 -0
- package/src/core/commands/seo/hreflang.md +140 -0
- package/src/core/commands/seo/images.md +96 -0
- package/src/core/commands/seo/page.md +198 -0
- package/src/core/commands/seo/plan.md +163 -0
- package/src/core/commands/seo/programmatic.md +131 -0
- package/src/core/commands/seo/references/cwv-thresholds.md +64 -0
- package/src/core/commands/seo/references/eeat-framework.md +110 -0
- package/src/core/commands/seo/references/quality-gates.md +91 -0
- package/src/core/commands/seo/references/schema-types.md +102 -0
- package/src/core/commands/seo/schema.md +183 -0
- package/src/core/commands/seo/sitemap.md +97 -0
- package/src/core/commands/seo/technical.md +100 -0
- package/src/core/commands/seo.md +107 -0
- package/src/core/commands/skill/list.md +68 -212
- package/src/core/commands/skill/recommend.md +216 -0
- package/src/core/commands/sprint.md +1 -0
- package/src/core/commands/status/undo.md +191 -0
- package/src/core/commands/status.md +1 -0
- package/src/core/commands/story/edit.md +204 -0
- package/src/core/commands/story/view.md +29 -7
- package/src/core/commands/story-validate.md +1 -0
- package/src/core/commands/story.md +1 -0
- package/src/core/commands/tdd-next.md +238 -0
- package/src/core/commands/tdd.md +211 -0
- package/src/core/commands/team/start.md +10 -6
- package/src/core/commands/tests.md +1 -0
- package/src/core/commands/verify.md +27 -1
- package/src/core/commands/workflow.md +2 -0
- package/src/core/experts/_core-expertise.yaml +105 -0
- package/src/core/experts/analytics/expertise.yaml +5 -99
- package/src/core/experts/codebase-query/expertise.yaml +3 -72
- package/src/core/experts/compliance/expertise.yaml +6 -72
- package/src/core/experts/database/expertise.yaml +9 -52
- package/src/core/experts/documentation/expertise.yaml +7 -140
- package/src/core/experts/integrations/expertise.yaml +7 -127
- package/src/core/experts/mentor/expertise.yaml +8 -35
- package/src/core/experts/monitoring/expertise.yaml +7 -49
- package/src/core/experts/performance/expertise.yaml +1 -26
- package/src/core/experts/security/expertise.yaml +9 -34
- package/src/core/experts/ui/expertise.yaml +6 -36
- package/src/core/knowledge/ads/ad-audit-checklist-scoring.md +424 -0
- package/src/core/knowledge/ads/ad-optimization-logic.md +590 -0
- package/src/core/knowledge/ads/ad-technical-specifications.md +385 -0
- package/src/core/knowledge/ads/definitive-advertising-reference-2026.md +506 -0
- package/src/core/knowledge/ads/paid-advertising-research-2026.md +445 -0
- package/src/core/teams/backend.json +41 -0
- package/src/core/teams/frontend.json +41 -0
- package/src/core/teams/qa.json +41 -0
- package/src/core/teams/solo.json +35 -0
- package/src/core/templates/agileflow-metadata.json +20 -1
- package/tools/cli/commands/setup.js +85 -3
- package/tools/cli/commands/update.js +42 -0
- package/tools/cli/installers/ide/_base-ide.js +42 -5
- package/tools/cli/installers/ide/claude-code.js +71 -3
- package/tools/cli/lib/content-injector.js +160 -12
- package/tools/cli/lib/docs-setup.js +1 -1
- package/src/core/commands/skill/create.md +0 -698
- package/src/core/commands/skill/delete.md +0 -316
- package/src/core/commands/skill/edit.md +0 -359
- package/src/core/commands/skill/test.md +0 -394
- package/src/core/commands/skill/upgrade.md +0 -552
- package/src/core/templates/skill-template.md +0 -117
|
@@ -49,6 +49,18 @@ function getFeatureFlags() {
|
|
|
49
49
|
return _featureFlags;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
let _teamEvents;
|
|
53
|
+
function getTeamEvents() {
|
|
54
|
+
if (!_teamEvents) {
|
|
55
|
+
try {
|
|
56
|
+
_teamEvents = require('./lib/team-events');
|
|
57
|
+
} catch (e) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return _teamEvents;
|
|
62
|
+
}
|
|
63
|
+
|
|
52
64
|
let _busUtils;
|
|
53
65
|
function getBusUtils() {
|
|
54
66
|
if (!_busUtils) {
|
|
@@ -277,6 +289,168 @@ function sendPlanDecision(rootDir, from, to, taskId, approved, reason, traceId)
|
|
|
277
289
|
return sendMessage(rootDir, msg);
|
|
278
290
|
}
|
|
279
291
|
|
|
292
|
+
/**
|
|
293
|
+
* Send an inter-agent team message to the JSONL bus.
|
|
294
|
+
* Logs communication between teammates with optional trace_id for correlation.
|
|
295
|
+
*
|
|
296
|
+
* @param {string} rootDir - Project root
|
|
297
|
+
* @param {string} from - Sending agent name
|
|
298
|
+
* @param {string} to - Receiving agent name
|
|
299
|
+
* @param {string} content - Message content
|
|
300
|
+
* @param {string} [traceId] - Trace ID for team lifecycle correlation
|
|
301
|
+
* @returns {{ ok: boolean, native?: boolean }}
|
|
302
|
+
*/
|
|
303
|
+
function sendTeamMessage(rootDir, from, to, content, traceId) {
|
|
304
|
+
const msg = { from, to, type: 'team_message', content };
|
|
305
|
+
if (traceId) msg.trace_id = traceId;
|
|
306
|
+
const result = sendMessage(rootDir, msg);
|
|
307
|
+
|
|
308
|
+
// Always track in session-state for observability parity (both native and subagent modes)
|
|
309
|
+
try {
|
|
310
|
+
const teamEvents = getTeamEvents();
|
|
311
|
+
if (teamEvents) {
|
|
312
|
+
teamEvents.trackEvent(rootDir, 'team_message', {
|
|
313
|
+
from,
|
|
314
|
+
to,
|
|
315
|
+
trace_id: traceId,
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
} catch (e) {
|
|
319
|
+
// Non-critical
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return result;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Send a team_completed event to the JSONL bus.
|
|
327
|
+
* Signals that a team run has finished all work.
|
|
328
|
+
*
|
|
329
|
+
* @param {string} rootDir - Project root
|
|
330
|
+
* @param {string} templateName - Team template name
|
|
331
|
+
* @param {string} [traceId] - Trace ID for team lifecycle correlation
|
|
332
|
+
* @param {object} [summary] - Optional summary data (duration_ms, tasks_completed, etc.)
|
|
333
|
+
* @returns {{ ok: boolean, native?: boolean }}
|
|
334
|
+
*/
|
|
335
|
+
function sendTeamCompleted(rootDir, templateName, traceId, summary) {
|
|
336
|
+
const msg = {
|
|
337
|
+
from: 'team-manager',
|
|
338
|
+
to: 'system',
|
|
339
|
+
type: 'team_completed',
|
|
340
|
+
template: templateName,
|
|
341
|
+
...summary,
|
|
342
|
+
};
|
|
343
|
+
if (traceId) msg.trace_id = traceId;
|
|
344
|
+
return sendMessage(rootDir, msg);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Log a native SendMessage tool call to the JSONL bus.
|
|
349
|
+
* Used by agents to record native API calls for observability parity.
|
|
350
|
+
*
|
|
351
|
+
* @param {string} rootDir - Project root
|
|
352
|
+
* @param {string} from - Sending agent name
|
|
353
|
+
* @param {string} to - Receiving agent name
|
|
354
|
+
* @param {string} content - Message content
|
|
355
|
+
* @param {string} [traceId] - Trace ID for correlation
|
|
356
|
+
* @returns {{ ok: boolean }}
|
|
357
|
+
*/
|
|
358
|
+
function logNativeSend(rootDir, from, to, content, traceId) {
|
|
359
|
+
const msg = { from, to, type: 'native_send', content };
|
|
360
|
+
if (traceId) msg.trace_id = traceId;
|
|
361
|
+
const result = sendMessage(rootDir, msg);
|
|
362
|
+
|
|
363
|
+
try {
|
|
364
|
+
const teamEvents = getTeamEvents();
|
|
365
|
+
if (teamEvents) {
|
|
366
|
+
teamEvents.trackEvent(rootDir, 'team_message', {
|
|
367
|
+
from,
|
|
368
|
+
to,
|
|
369
|
+
trace_id: traceId,
|
|
370
|
+
native: true,
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
} catch (e) {
|
|
374
|
+
// Non-critical
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return { ok: result.ok };
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Log a native TeamCreate tool call to the JSONL bus.
|
|
382
|
+
*
|
|
383
|
+
* @param {string} rootDir - Project root
|
|
384
|
+
* @param {string} templateName - Team template name
|
|
385
|
+
* @param {string} [traceId] - Trace ID for correlation
|
|
386
|
+
* @param {number} [teammateCount] - Number of teammates created
|
|
387
|
+
* @returns {{ ok: boolean }}
|
|
388
|
+
*/
|
|
389
|
+
function logNativeTeamCreate(rootDir, templateName, traceId, teammateCount) {
|
|
390
|
+
const msg = {
|
|
391
|
+
from: 'team-manager',
|
|
392
|
+
to: 'system',
|
|
393
|
+
type: 'native_team_create',
|
|
394
|
+
template: templateName,
|
|
395
|
+
};
|
|
396
|
+
if (traceId) msg.trace_id = traceId;
|
|
397
|
+
if (typeof teammateCount === 'number') msg.teammate_count = teammateCount;
|
|
398
|
+
const result = sendMessage(rootDir, msg);
|
|
399
|
+
|
|
400
|
+
try {
|
|
401
|
+
const teamEvents = getTeamEvents();
|
|
402
|
+
if (teamEvents) {
|
|
403
|
+
teamEvents.trackEvent(rootDir, 'team_created', {
|
|
404
|
+
template: templateName,
|
|
405
|
+
trace_id: traceId,
|
|
406
|
+
teammate_count: teammateCount,
|
|
407
|
+
native: true,
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
} catch (e) {
|
|
411
|
+
// Non-critical
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
return { ok: result.ok };
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Log a native team completion to the JSONL bus.
|
|
419
|
+
*
|
|
420
|
+
* @param {string} rootDir - Project root
|
|
421
|
+
* @param {string} templateName - Team template name
|
|
422
|
+
* @param {string} [traceId] - Trace ID for correlation
|
|
423
|
+
* @param {string} [summary] - Summary of completed work
|
|
424
|
+
* @returns {{ ok: boolean }}
|
|
425
|
+
*/
|
|
426
|
+
function logNativeTeamCompleted(rootDir, templateName, traceId, summary) {
|
|
427
|
+
const msg = {
|
|
428
|
+
from: 'team-manager',
|
|
429
|
+
to: 'system',
|
|
430
|
+
type: 'native_team_completed',
|
|
431
|
+
template: templateName,
|
|
432
|
+
};
|
|
433
|
+
if (traceId) msg.trace_id = traceId;
|
|
434
|
+
if (summary) msg.summary = summary;
|
|
435
|
+
const result = sendMessage(rootDir, msg);
|
|
436
|
+
|
|
437
|
+
try {
|
|
438
|
+
const teamEvents = getTeamEvents();
|
|
439
|
+
if (teamEvents) {
|
|
440
|
+
teamEvents.trackEvent(rootDir, 'team_completed', {
|
|
441
|
+
template: templateName,
|
|
442
|
+
trace_id: traceId,
|
|
443
|
+
summary,
|
|
444
|
+
native: true,
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
} catch (e) {
|
|
448
|
+
// Non-critical
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
return { ok: result.ok };
|
|
452
|
+
}
|
|
453
|
+
|
|
280
454
|
/**
|
|
281
455
|
* Send a validation result message.
|
|
282
456
|
*/
|
|
@@ -324,10 +498,39 @@ function main() {
|
|
|
324
498
|
break;
|
|
325
499
|
}
|
|
326
500
|
|
|
501
|
+
case 'log-native-send': {
|
|
502
|
+
const [, from, to, ...contentParts] = args;
|
|
503
|
+
const content = contentParts.filter(p => !p.startsWith('--')).join(' ');
|
|
504
|
+
const traceArg = args.find(a => a.startsWith('--trace-id='));
|
|
505
|
+
const traceId = traceArg ? traceArg.slice(11) : undefined;
|
|
506
|
+
result = logNativeSend(rootDir, from, to, content, traceId);
|
|
507
|
+
break;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
case 'log-native-create': {
|
|
511
|
+
const [, templateName] = args;
|
|
512
|
+
const traceArg = args.find(a => a.startsWith('--trace-id='));
|
|
513
|
+
const traceId = traceArg ? traceArg.slice(11) : undefined;
|
|
514
|
+
const countArg = args.find(a => a.startsWith('--count='));
|
|
515
|
+
const teammateCount = countArg ? parseInt(countArg.slice(8), 10) : undefined;
|
|
516
|
+
result = logNativeTeamCreate(rootDir, templateName, traceId, teammateCount);
|
|
517
|
+
break;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
case 'log-native-completed': {
|
|
521
|
+
const [, templateName] = args;
|
|
522
|
+
const traceArg = args.find(a => a.startsWith('--trace-id='));
|
|
523
|
+
const traceId = traceArg ? traceArg.slice(11) : undefined;
|
|
524
|
+
const summaryArg = args.find(a => a.startsWith('--summary='));
|
|
525
|
+
const summary = summaryArg ? summaryArg.slice(10) : undefined;
|
|
526
|
+
result = logNativeTeamCompleted(rootDir, templateName, traceId, summary);
|
|
527
|
+
break;
|
|
528
|
+
}
|
|
529
|
+
|
|
327
530
|
default:
|
|
328
531
|
result = {
|
|
329
532
|
ok: false,
|
|
330
|
-
error: `Unknown command: ${command}\nUsage: messaging-bridge.js <send|read|context>`,
|
|
533
|
+
error: `Unknown command: ${command}\nUsage: messaging-bridge.js <send|read|context|log-native-send|log-native-create|log-native-completed>`,
|
|
331
534
|
};
|
|
332
535
|
}
|
|
333
536
|
|
|
@@ -343,7 +546,12 @@ module.exports = {
|
|
|
343
546
|
sendTaskAssignment,
|
|
344
547
|
sendPlanProposal,
|
|
345
548
|
sendPlanDecision,
|
|
549
|
+
sendTeamMessage,
|
|
550
|
+
sendTeamCompleted,
|
|
346
551
|
sendValidationResult,
|
|
552
|
+
logNativeSend,
|
|
553
|
+
logNativeTeamCreate,
|
|
554
|
+
logNativeTeamCompleted,
|
|
347
555
|
getBusLogPath,
|
|
348
556
|
formatForNative,
|
|
349
557
|
};
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* native-team-observer.js - PostToolUse hook for native Agent Teams observability
|
|
4
|
+
*
|
|
5
|
+
* Logs native TeamCreate, SendMessage, and ListTeams tool calls to the
|
|
6
|
+
* AgileFlow JSONL bus so that native mode has observability parity with
|
|
7
|
+
* subagent mode (where team-manager.js handles dual-write).
|
|
8
|
+
*
|
|
9
|
+
* Exit codes:
|
|
10
|
+
* 0 - Always (observability hook, never blocks)
|
|
11
|
+
*
|
|
12
|
+
* Usage: Configured as PostToolUse hook in .claude/settings.json
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
|
|
18
|
+
// Tools this hook observes
|
|
19
|
+
const NATIVE_TEAM_TOOLS = ['TeamCreate', 'SendMessage', 'ListTeams'];
|
|
20
|
+
|
|
21
|
+
// Lazy-load modules to minimize startup cost
|
|
22
|
+
let _featureFlags;
|
|
23
|
+
function getFeatureFlags() {
|
|
24
|
+
if (!_featureFlags) {
|
|
25
|
+
const candidates = [
|
|
26
|
+
path.join(__dirname, '..', 'lib', 'feature-flags.js'),
|
|
27
|
+
path.join(__dirname, 'lib', 'feature-flags.js'),
|
|
28
|
+
path.join(process.cwd(), '.agileflow', 'lib', 'feature-flags.js'),
|
|
29
|
+
];
|
|
30
|
+
for (const candidate of candidates) {
|
|
31
|
+
try {
|
|
32
|
+
if (fs.existsSync(candidate)) {
|
|
33
|
+
_featureFlags = require(candidate);
|
|
34
|
+
return _featureFlags;
|
|
35
|
+
}
|
|
36
|
+
} catch (e) {
|
|
37
|
+
// Try next
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
return _featureFlags;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let _paths;
|
|
46
|
+
function getPaths() {
|
|
47
|
+
if (!_paths) {
|
|
48
|
+
const candidates = [
|
|
49
|
+
path.join(__dirname, '..', 'lib', 'paths.js'),
|
|
50
|
+
path.join(__dirname, 'lib', 'paths.js'),
|
|
51
|
+
path.join(process.cwd(), '.agileflow', 'lib', 'paths.js'),
|
|
52
|
+
];
|
|
53
|
+
for (const candidate of candidates) {
|
|
54
|
+
try {
|
|
55
|
+
if (fs.existsSync(candidate)) {
|
|
56
|
+
_paths = require(candidate);
|
|
57
|
+
return _paths;
|
|
58
|
+
}
|
|
59
|
+
} catch (e) {
|
|
60
|
+
// Try next
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
return _paths;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let _messagingBridge;
|
|
69
|
+
function getMessagingBridge() {
|
|
70
|
+
if (!_messagingBridge) {
|
|
71
|
+
const candidates = [
|
|
72
|
+
path.join(__dirname, 'messaging-bridge.js'),
|
|
73
|
+
path.join(process.cwd(), '.agileflow', 'scripts', 'messaging-bridge.js'),
|
|
74
|
+
];
|
|
75
|
+
for (const candidate of candidates) {
|
|
76
|
+
try {
|
|
77
|
+
if (fs.existsSync(candidate)) {
|
|
78
|
+
_messagingBridge = require(candidate);
|
|
79
|
+
return _messagingBridge;
|
|
80
|
+
}
|
|
81
|
+
} catch (e) {
|
|
82
|
+
// Try next
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
return _messagingBridge;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Read trace_id from session-state.json active_team.
|
|
92
|
+
* Returns undefined if not available.
|
|
93
|
+
*/
|
|
94
|
+
function getTraceId(rootDir) {
|
|
95
|
+
try {
|
|
96
|
+
const paths = getPaths();
|
|
97
|
+
if (!paths) return undefined;
|
|
98
|
+
const sessionStatePath = paths.getSessionStatePath(rootDir);
|
|
99
|
+
if (!fs.existsSync(sessionStatePath)) return undefined;
|
|
100
|
+
const state = JSON.parse(fs.readFileSync(sessionStatePath, 'utf8'));
|
|
101
|
+
return state.active_team?.trace_id;
|
|
102
|
+
} catch (e) {
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Get the active_team template name from session-state.json.
|
|
109
|
+
*/
|
|
110
|
+
function getActiveTeamTemplate(rootDir) {
|
|
111
|
+
try {
|
|
112
|
+
const paths = getPaths();
|
|
113
|
+
if (!paths) return undefined;
|
|
114
|
+
const sessionStatePath = paths.getSessionStatePath(rootDir);
|
|
115
|
+
if (!fs.existsSync(sessionStatePath)) return undefined;
|
|
116
|
+
const state = JSON.parse(fs.readFileSync(sessionStatePath, 'utf8'));
|
|
117
|
+
return state.active_team?.template;
|
|
118
|
+
} catch (e) {
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Check if session-state has an active_team.
|
|
125
|
+
*/
|
|
126
|
+
function hasActiveTeam(rootDir) {
|
|
127
|
+
try {
|
|
128
|
+
const paths = getPaths();
|
|
129
|
+
if (!paths) return false;
|
|
130
|
+
const sessionStatePath = paths.getSessionStatePath(rootDir);
|
|
131
|
+
if (!fs.existsSync(sessionStatePath)) return false;
|
|
132
|
+
const state = JSON.parse(fs.readFileSync(sessionStatePath, 'utf8'));
|
|
133
|
+
return !!state.active_team;
|
|
134
|
+
} catch (e) {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Read stdin and process
|
|
140
|
+
let input = '';
|
|
141
|
+
process.stdin.on('data', chunk => (input += chunk));
|
|
142
|
+
process.stdin.on('end', () => {
|
|
143
|
+
try {
|
|
144
|
+
const context = JSON.parse(input);
|
|
145
|
+
const toolName = context.tool_name;
|
|
146
|
+
|
|
147
|
+
// Skip non-matching tools
|
|
148
|
+
if (!NATIVE_TEAM_TOOLS.includes(toolName)) {
|
|
149
|
+
process.exit(0);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Check if native Agent Teams is enabled
|
|
153
|
+
const featureFlags = getFeatureFlags();
|
|
154
|
+
if (!featureFlags || !featureFlags.isAgentTeamsEnabled({ rootDir: process.cwd() })) {
|
|
155
|
+
process.exit(0);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const rootDir = process.cwd();
|
|
159
|
+
const bridge = getMessagingBridge();
|
|
160
|
+
if (!bridge) {
|
|
161
|
+
process.exit(0);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const traceId = getTraceId(rootDir);
|
|
165
|
+
const toolInput = context.tool_input || {};
|
|
166
|
+
const toolOutput = context.tool_output;
|
|
167
|
+
|
|
168
|
+
switch (toolName) {
|
|
169
|
+
case 'TeamCreate': {
|
|
170
|
+
const teamName = toolInput.name || 'unknown';
|
|
171
|
+
const teammateCount = Array.isArray(toolInput.teammates)
|
|
172
|
+
? toolInput.teammates.length
|
|
173
|
+
: undefined;
|
|
174
|
+
bridge.logNativeTeamCreate(rootDir, teamName, traceId, teammateCount);
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
case 'SendMessage': {
|
|
179
|
+
const to = toolInput.to || 'unknown';
|
|
180
|
+
const content = toolInput.message || toolInput.content || '';
|
|
181
|
+
bridge.logNativeSend(rootDir, 'lead', to, content, traceId);
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
case 'ListTeams': {
|
|
186
|
+
// Detect team completion: ListTeams returns no active teams but
|
|
187
|
+
// session-state still has an active_team
|
|
188
|
+
if (hasActiveTeam(rootDir)) {
|
|
189
|
+
let noActiveTeams = false;
|
|
190
|
+
try {
|
|
191
|
+
if (typeof toolOutput === 'string') {
|
|
192
|
+
const parsed = JSON.parse(toolOutput);
|
|
193
|
+
noActiveTeams = Array.isArray(parsed) ? parsed.length === 0 : !parsed.teams?.length;
|
|
194
|
+
} else if (typeof toolOutput === 'object' && toolOutput !== null) {
|
|
195
|
+
noActiveTeams = Array.isArray(toolOutput)
|
|
196
|
+
? toolOutput.length === 0
|
|
197
|
+
: !toolOutput.teams?.length;
|
|
198
|
+
}
|
|
199
|
+
} catch (e) {
|
|
200
|
+
// Can't parse output - skip completion detection
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (noActiveTeams) {
|
|
204
|
+
const template = getActiveTeamTemplate(rootDir) || 'unknown';
|
|
205
|
+
bridge.logNativeTeamCompleted(rootDir, template, traceId, 'completed');
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
} catch (e) {
|
|
212
|
+
// Fail-open: observability hook should never block
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
process.exit(0);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
// Handle empty stdin (timeout safety)
|
|
219
|
+
setTimeout(() => process.exit(0), 4000);
|
|
@@ -158,6 +158,20 @@ function executeQueryMode(query) {
|
|
|
158
158
|
// =============================================================================
|
|
159
159
|
|
|
160
160
|
async function main() {
|
|
161
|
+
// Guard: Check .agileflow directory exists (covers all 94+ commands)
|
|
162
|
+
if (!fs.existsSync('.agileflow') && !fs.existsSync('docs/09-agents/status.json')) {
|
|
163
|
+
const red = '\x1b[38;5;203m';
|
|
164
|
+
const bold = '\x1b[1m';
|
|
165
|
+
const reset = '\x1b[0m';
|
|
166
|
+
const dim = '\x1b[2m';
|
|
167
|
+
console.error(`${red}${bold}AgileFlow is not initialized in this directory.${reset}`);
|
|
168
|
+
console.error(`${dim}Run: ${reset}${bold}npx agileflow setup${reset}`);
|
|
169
|
+
console.error(
|
|
170
|
+
`${dim}This will create the .agileflow/ directory with commands, hooks, and configuration.${reset}`
|
|
171
|
+
);
|
|
172
|
+
process.exit(1);
|
|
173
|
+
}
|
|
174
|
+
|
|
161
175
|
// Register command for PreCompact
|
|
162
176
|
registerCommand();
|
|
163
177
|
|
package/scripts/ralph-loop.js
CHANGED
|
@@ -279,16 +279,41 @@ const DISCRETION_CONDITIONS = {
|
|
|
279
279
|
if (!Array.isArray(ac) || ac.length === 0) {
|
|
280
280
|
return { passed: true, message: 'No AC defined (assuming complete)' };
|
|
281
281
|
}
|
|
282
|
-
// Check for ac_status field
|
|
282
|
+
// Check for ac_status field (supports auto-verified and likely-covered from ac-test-matcher)
|
|
283
283
|
const acStatus = story.ac_status || {};
|
|
284
|
-
const
|
|
284
|
+
const verifiedStatuses = ['verified', 'auto-verified', 'likely-covered'];
|
|
285
|
+
const verifiedCount = ac.filter(
|
|
286
|
+
(_, i) => verifiedStatuses.includes(acStatus[i]) || acStatus[i] === true
|
|
287
|
+
).length;
|
|
288
|
+
const allVerified = verifiedCount === ac.length;
|
|
285
289
|
return {
|
|
286
290
|
passed: allVerified,
|
|
287
|
-
message: allVerified
|
|
288
|
-
? 'All AC verified'
|
|
289
|
-
: `${Object.values(acStatus).filter(v => v === 'verified' || v === true).length}/${ac.length} AC verified`,
|
|
291
|
+
message: allVerified ? 'All AC verified' : `${verifiedCount}/${ac.length} AC verified`,
|
|
290
292
|
};
|
|
291
293
|
},
|
|
294
|
+
|
|
295
|
+
// AC test coverage condition (uses ac-test-matcher for automated checks)
|
|
296
|
+
'ac test coverage sufficient': (rootDir, ctx) => {
|
|
297
|
+
const storyId = ctx.currentStoryId;
|
|
298
|
+
if (!storyId) {
|
|
299
|
+
return { passed: false, message: 'No story ID in context' };
|
|
300
|
+
}
|
|
301
|
+
try {
|
|
302
|
+
const { matchACToTests } = require(path.join(__dirname, 'lib', 'ac-test-matcher'));
|
|
303
|
+
const result = matchACToTests(storyId, rootDir);
|
|
304
|
+
if (result.error) {
|
|
305
|
+
return { passed: false, message: result.error };
|
|
306
|
+
}
|
|
307
|
+
const threshold = ctx.coverageThreshold || 0.5;
|
|
308
|
+
const passed = result.coverage >= threshold;
|
|
309
|
+
return {
|
|
310
|
+
passed,
|
|
311
|
+
message: `AC test coverage: ${Math.round(result.coverage * 100)}% (${result.matched.length}/${result.total} matched, threshold: ${Math.round(threshold * 100)}%)`,
|
|
312
|
+
};
|
|
313
|
+
} catch (e) {
|
|
314
|
+
return { passed: false, message: `AC matcher error: ${e.message}` };
|
|
315
|
+
}
|
|
316
|
+
},
|
|
292
317
|
};
|
|
293
318
|
|
|
294
319
|
/**
|
package/scripts/smart-detect.js
CHANGED
|
@@ -24,6 +24,7 @@ const path = require('path');
|
|
|
24
24
|
const { detectLifecyclePhase, getRelevantPhases } = require('./lib/lifecycle-detector');
|
|
25
25
|
const { runDetectorsForPhases } = require('./lib/signal-detectors');
|
|
26
26
|
const { buildCatalogWithStatus } = require('./lib/feature-catalog');
|
|
27
|
+
const { detectScale, getScaleRecommendations } = require('./lib/scale-detector');
|
|
27
28
|
|
|
28
29
|
let safeReadJSON, safeWriteJSON, tryOptional;
|
|
29
30
|
try {
|
|
@@ -158,6 +159,24 @@ function extractSignals(prefetched, sessionState, metadata) {
|
|
|
158
159
|
// Thresholds from metadata
|
|
159
160
|
const thresholds = metadata?.smart_detect?.thresholds || {};
|
|
160
161
|
|
|
162
|
+
// Scale detection (cached, <200ms)
|
|
163
|
+
let scale = null;
|
|
164
|
+
try {
|
|
165
|
+
const scaleResult = detectScale({
|
|
166
|
+
rootDir: process.cwd(),
|
|
167
|
+
statusJson,
|
|
168
|
+
sessionState,
|
|
169
|
+
});
|
|
170
|
+
scale = {
|
|
171
|
+
tier: scaleResult.scale,
|
|
172
|
+
metrics: scaleResult.metrics,
|
|
173
|
+
recommendations: getScaleRecommendations(scaleResult.scale),
|
|
174
|
+
fromCache: scaleResult.fromCache,
|
|
175
|
+
};
|
|
176
|
+
} catch {
|
|
177
|
+
// Scale detection failure is non-critical
|
|
178
|
+
}
|
|
179
|
+
|
|
161
180
|
return {
|
|
162
181
|
statusJson,
|
|
163
182
|
sessionState,
|
|
@@ -178,6 +197,7 @@ function extractSignals(prefetched, sessionState, metadata) {
|
|
|
178
197
|
counts,
|
|
179
198
|
storyCount,
|
|
180
199
|
thresholds,
|
|
200
|
+
scale,
|
|
181
201
|
session: {
|
|
182
202
|
planModeActive,
|
|
183
203
|
activeCommands: sessionState?.active_commands || [],
|
|
@@ -300,6 +320,7 @@ function analyze(prefetched, sessionState, metadata) {
|
|
|
300
320
|
tests_passing: signals.tests.passing,
|
|
301
321
|
on_feature_branch: signals.git.onFeatureBranch,
|
|
302
322
|
story_counts: signals.counts,
|
|
323
|
+
scale: signals.scale ? signals.scale.tier : null,
|
|
303
324
|
};
|
|
304
325
|
|
|
305
326
|
return {
|