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.
Files changed (210) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +6 -6
  3. package/lib/skill-loader.js +0 -1
  4. package/package.json +1 -1
  5. package/scripts/agileflow-statusline.sh +81 -0
  6. package/scripts/agileflow-welcome.js +79 -0
  7. package/scripts/claude-tmux.sh +90 -23
  8. package/scripts/claude-watchdog.sh +225 -0
  9. package/scripts/generators/agent-registry.js +14 -1
  10. package/scripts/generators/inject-babysit.js +22 -9
  11. package/scripts/generators/inject-help.js +19 -9
  12. package/scripts/lib/ac-test-matcher.js +452 -0
  13. package/scripts/lib/audit-cleanup.js +250 -0
  14. package/scripts/lib/audit-registry.js +304 -0
  15. package/scripts/lib/configure-features.js +35 -0
  16. package/scripts/lib/feature-catalog.js +3 -3
  17. package/scripts/lib/gate-enforcer.js +295 -0
  18. package/scripts/lib/model-profiles.js +118 -0
  19. package/scripts/lib/quality-gates.js +163 -0
  20. package/scripts/lib/signal-detectors.js +44 -1
  21. package/scripts/lib/skill-catalog.js +557 -0
  22. package/scripts/lib/skill-recommender.js +311 -0
  23. package/scripts/lib/status-writer.js +255 -0
  24. package/scripts/lib/story-claiming.js +128 -45
  25. package/scripts/lib/task-sync.js +32 -38
  26. package/scripts/lib/tdd-phase-manager.js +455 -0
  27. package/scripts/lib/team-events.js +34 -3
  28. package/scripts/lib/tmux-audit-monitor.js +611 -0
  29. package/scripts/lib/tmux-group-colors.js +113 -0
  30. package/scripts/lib/tool-registry.yaml +241 -0
  31. package/scripts/lib/tool-shed.js +441 -0
  32. package/scripts/messaging-bridge.js +209 -1
  33. package/scripts/native-team-observer.js +219 -0
  34. package/scripts/obtain-context.js +14 -0
  35. package/scripts/ralph-loop.js +30 -5
  36. package/scripts/smart-detect.js +21 -0
  37. package/scripts/spawn-audit-sessions.js +877 -0
  38. package/scripts/team-manager.js +56 -16
  39. package/scripts/tmux-close-windows.sh +180 -0
  40. package/src/core/agents/a11y-analyzer-aria.md +155 -0
  41. package/src/core/agents/a11y-analyzer-forms.md +162 -0
  42. package/src/core/agents/a11y-analyzer-keyboard.md +175 -0
  43. package/src/core/agents/a11y-analyzer-semantic.md +153 -0
  44. package/src/core/agents/a11y-analyzer-visual.md +158 -0
  45. package/src/core/agents/a11y-consensus.md +248 -0
  46. package/src/core/agents/ads-audit-budget.md +181 -0
  47. package/src/core/agents/ads-audit-compliance.md +169 -0
  48. package/src/core/agents/ads-audit-creative.md +164 -0
  49. package/src/core/agents/ads-audit-google.md +226 -0
  50. package/src/core/agents/ads-audit-meta.md +183 -0
  51. package/src/core/agents/ads-audit-tracking.md +197 -0
  52. package/src/core/agents/ads-consensus.md +396 -0
  53. package/src/core/agents/ads-generate.md +145 -0
  54. package/src/core/agents/ads-performance-tracker.md +197 -0
  55. package/src/core/agents/api-quality-analyzer-conventions.md +148 -0
  56. package/src/core/agents/api-quality-analyzer-docs.md +176 -0
  57. package/src/core/agents/api-quality-analyzer-errors.md +183 -0
  58. package/src/core/agents/api-quality-analyzer-pagination.md +171 -0
  59. package/src/core/agents/api-quality-analyzer-versioning.md +143 -0
  60. package/src/core/agents/api-quality-consensus.md +214 -0
  61. package/src/core/agents/arch-analyzer-circular.md +148 -0
  62. package/src/core/agents/arch-analyzer-complexity.md +171 -0
  63. package/src/core/agents/arch-analyzer-coupling.md +146 -0
  64. package/src/core/agents/arch-analyzer-layering.md +151 -0
  65. package/src/core/agents/arch-analyzer-patterns.md +162 -0
  66. package/src/core/agents/arch-consensus.md +227 -0
  67. package/src/core/agents/brainstorm-analyzer-features.md +169 -0
  68. package/src/core/agents/brainstorm-analyzer-growth.md +161 -0
  69. package/src/core/agents/brainstorm-analyzer-integration.md +172 -0
  70. package/src/core/agents/brainstorm-analyzer-market.md +147 -0
  71. package/src/core/agents/brainstorm-analyzer-ux.md +167 -0
  72. package/src/core/agents/brainstorm-consensus.md +237 -0
  73. package/src/core/agents/completeness-consensus.md +5 -5
  74. package/src/core/agents/perf-consensus.md +2 -2
  75. package/src/core/agents/security-consensus.md +2 -2
  76. package/src/core/agents/seo-analyzer-content.md +167 -0
  77. package/src/core/agents/seo-analyzer-images.md +187 -0
  78. package/src/core/agents/seo-analyzer-performance.md +206 -0
  79. package/src/core/agents/seo-analyzer-schema.md +176 -0
  80. package/src/core/agents/seo-analyzer-sitemap.md +172 -0
  81. package/src/core/agents/seo-analyzer-technical.md +144 -0
  82. package/src/core/agents/seo-consensus.md +289 -0
  83. package/src/core/agents/test-consensus.md +2 -2
  84. package/src/core/commands/adr.md +1 -0
  85. package/src/core/commands/ads/audit.md +375 -0
  86. package/src/core/commands/ads/budget.md +97 -0
  87. package/src/core/commands/ads/competitor.md +112 -0
  88. package/src/core/commands/ads/creative.md +85 -0
  89. package/src/core/commands/ads/generate.md +238 -0
  90. package/src/core/commands/ads/google.md +112 -0
  91. package/src/core/commands/ads/health.md +327 -0
  92. package/src/core/commands/ads/landing.md +119 -0
  93. package/src/core/commands/ads/linkedin.md +112 -0
  94. package/src/core/commands/ads/meta.md +91 -0
  95. package/src/core/commands/ads/microsoft.md +115 -0
  96. package/src/core/commands/ads/plan.md +321 -0
  97. package/src/core/commands/ads/test-plan.md +317 -0
  98. package/src/core/commands/ads/tiktok.md +129 -0
  99. package/src/core/commands/ads/track.md +288 -0
  100. package/src/core/commands/ads/youtube.md +124 -0
  101. package/src/core/commands/ads.md +140 -0
  102. package/src/core/commands/assign.md +1 -0
  103. package/src/core/commands/audit.md +43 -6
  104. package/src/core/commands/babysit.md +315 -1266
  105. package/src/core/commands/baseline.md +1 -0
  106. package/src/core/commands/blockers.md +1 -0
  107. package/src/core/commands/board.md +1 -0
  108. package/src/core/commands/changelog.md +1 -0
  109. package/src/core/commands/choose.md +1 -0
  110. package/src/core/commands/ci.md +1 -0
  111. package/src/core/commands/code/accessibility.md +347 -0
  112. package/src/core/commands/code/api.md +297 -0
  113. package/src/core/commands/code/architecture.md +297 -0
  114. package/src/core/commands/{audit → code}/completeness.md +72 -25
  115. package/src/core/commands/{audit → code}/legal.md +63 -16
  116. package/src/core/commands/{audit → code}/logic.md +64 -16
  117. package/src/core/commands/{audit → code}/performance.md +67 -20
  118. package/src/core/commands/{audit → code}/security.md +69 -19
  119. package/src/core/commands/{audit → code}/test.md +67 -20
  120. package/src/core/commands/configure.md +1 -0
  121. package/src/core/commands/council.md +1 -0
  122. package/src/core/commands/deploy.md +1 -0
  123. package/src/core/commands/diagnose.md +1 -0
  124. package/src/core/commands/docs.md +1 -0
  125. package/src/core/commands/epic/edit.md +213 -0
  126. package/src/core/commands/epic.md +1 -0
  127. package/src/core/commands/export.md +238 -0
  128. package/src/core/commands/help.md +16 -1
  129. package/src/core/commands/{discovery → ideate}/brief.md +12 -12
  130. package/src/core/commands/{discovery/new.md → ideate/discover.md} +20 -16
  131. package/src/core/commands/ideate/features.md +496 -0
  132. package/src/core/commands/ideate/new.md +158 -124
  133. package/src/core/commands/impact.md +1 -0
  134. package/src/core/commands/learn/explain.md +118 -0
  135. package/src/core/commands/learn/glossary.md +135 -0
  136. package/src/core/commands/learn/patterns.md +138 -0
  137. package/src/core/commands/learn/tour.md +126 -0
  138. package/src/core/commands/migrate/codemods.md +151 -0
  139. package/src/core/commands/migrate/plan.md +131 -0
  140. package/src/core/commands/migrate/scan.md +114 -0
  141. package/src/core/commands/migrate/validate.md +119 -0
  142. package/src/core/commands/multi-expert.md +1 -0
  143. package/src/core/commands/pr.md +1 -0
  144. package/src/core/commands/review.md +1 -0
  145. package/src/core/commands/seo/audit.md +373 -0
  146. package/src/core/commands/seo/competitor.md +174 -0
  147. package/src/core/commands/seo/content.md +107 -0
  148. package/src/core/commands/seo/geo.md +229 -0
  149. package/src/core/commands/seo/hreflang.md +140 -0
  150. package/src/core/commands/seo/images.md +96 -0
  151. package/src/core/commands/seo/page.md +198 -0
  152. package/src/core/commands/seo/plan.md +163 -0
  153. package/src/core/commands/seo/programmatic.md +131 -0
  154. package/src/core/commands/seo/references/cwv-thresholds.md +64 -0
  155. package/src/core/commands/seo/references/eeat-framework.md +110 -0
  156. package/src/core/commands/seo/references/quality-gates.md +91 -0
  157. package/src/core/commands/seo/references/schema-types.md +102 -0
  158. package/src/core/commands/seo/schema.md +183 -0
  159. package/src/core/commands/seo/sitemap.md +97 -0
  160. package/src/core/commands/seo/technical.md +100 -0
  161. package/src/core/commands/seo.md +107 -0
  162. package/src/core/commands/skill/list.md +68 -212
  163. package/src/core/commands/skill/recommend.md +216 -0
  164. package/src/core/commands/sprint.md +1 -0
  165. package/src/core/commands/status/undo.md +191 -0
  166. package/src/core/commands/status.md +1 -0
  167. package/src/core/commands/story/edit.md +204 -0
  168. package/src/core/commands/story/view.md +29 -7
  169. package/src/core/commands/story-validate.md +1 -0
  170. package/src/core/commands/story.md +1 -0
  171. package/src/core/commands/tdd-next.md +238 -0
  172. package/src/core/commands/tdd.md +211 -0
  173. package/src/core/commands/team/start.md +10 -6
  174. package/src/core/commands/tests.md +1 -0
  175. package/src/core/commands/verify.md +27 -1
  176. package/src/core/commands/workflow.md +2 -0
  177. package/src/core/experts/_core-expertise.yaml +105 -0
  178. package/src/core/experts/analytics/expertise.yaml +5 -99
  179. package/src/core/experts/codebase-query/expertise.yaml +3 -72
  180. package/src/core/experts/compliance/expertise.yaml +6 -72
  181. package/src/core/experts/database/expertise.yaml +9 -52
  182. package/src/core/experts/documentation/expertise.yaml +7 -140
  183. package/src/core/experts/integrations/expertise.yaml +7 -127
  184. package/src/core/experts/mentor/expertise.yaml +8 -35
  185. package/src/core/experts/monitoring/expertise.yaml +7 -49
  186. package/src/core/experts/performance/expertise.yaml +1 -26
  187. package/src/core/experts/security/expertise.yaml +9 -34
  188. package/src/core/experts/ui/expertise.yaml +6 -36
  189. package/src/core/knowledge/ads/ad-audit-checklist-scoring.md +424 -0
  190. package/src/core/knowledge/ads/ad-optimization-logic.md +590 -0
  191. package/src/core/knowledge/ads/ad-technical-specifications.md +385 -0
  192. package/src/core/knowledge/ads/definitive-advertising-reference-2026.md +506 -0
  193. package/src/core/knowledge/ads/paid-advertising-research-2026.md +445 -0
  194. package/src/core/teams/backend.json +41 -0
  195. package/src/core/teams/frontend.json +41 -0
  196. package/src/core/teams/qa.json +41 -0
  197. package/src/core/teams/solo.json +35 -0
  198. package/src/core/templates/agileflow-metadata.json +20 -1
  199. package/tools/cli/commands/setup.js +85 -3
  200. package/tools/cli/commands/update.js +42 -0
  201. package/tools/cli/installers/ide/_base-ide.js +42 -5
  202. package/tools/cli/installers/ide/claude-code.js +71 -3
  203. package/tools/cli/lib/content-injector.js +160 -12
  204. package/tools/cli/lib/docs-setup.js +1 -1
  205. package/src/core/commands/skill/create.md +0 -698
  206. package/src/core/commands/skill/delete.md +0 -316
  207. package/src/core/commands/skill/edit.md +0 -359
  208. package/src/core/commands/skill/test.md +0 -394
  209. package/src/core/commands/skill/upgrade.md +0 -552
  210. 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
 
@@ -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 or assume AC are verified if tests pass
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 allVerified = ac.every((_, i) => acStatus[i] === 'verified' || acStatus[i] === true);
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
  /**
@@ -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 {