@aiwg/cli 0.0.0-bootstrap.0 → 2026.7.17

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 (466) hide show
  1. package/README.md +21 -1
  2. package/bin/aiwg.mjs +349 -0
  3. package/dist/src/a2a/agent-card.js +145 -0
  4. package/dist/src/a2a/client.js +411 -0
  5. package/dist/src/a2a/hitl-cli.js +171 -0
  6. package/dist/src/a2a/hitl-driver.js +370 -0
  7. package/dist/src/a2a/hitl.js +181 -0
  8. package/dist/src/a2a/http.js +193 -0
  9. package/dist/src/a2a/jcs.js +128 -0
  10. package/dist/src/a2a/jws.js +187 -0
  11. package/dist/src/a2a/types.js +24 -0
  12. package/dist/src/a2a/webhook.js +240 -0
  13. package/dist/src/activity-log/cli.js +348 -0
  14. package/dist/src/activity-log/parser.js +81 -0
  15. package/dist/src/activity-log/types.js +50 -0
  16. package/dist/src/agents/agent-deployer.js +367 -0
  17. package/dist/src/agents/agent-packager.js +271 -0
  18. package/dist/src/agents/agent-validator.js +271 -0
  19. package/dist/src/agents/grounding/knowledge-base.js +161 -0
  20. package/dist/src/agents/grounding/types.js +12 -0
  21. package/dist/src/agents/index.js +10 -0
  22. package/dist/src/agents/packaged-agent-inventory.js +75 -0
  23. package/dist/src/agents/types.js +7 -0
  24. package/dist/src/api/index.d.ts +10 -0
  25. package/dist/src/api/index.js +10 -0
  26. package/dist/src/artifacts/address-parser.js +73 -0
  27. package/dist/src/artifacts/audit/cli.js +183 -0
  28. package/dist/src/artifacts/audit/drift.js +122 -0
  29. package/dist/src/artifacts/audit/types.js +12 -0
  30. package/dist/src/artifacts/backends/graphology-backend.js +160 -0
  31. package/dist/src/artifacts/backends/json-backend.js +129 -0
  32. package/dist/src/artifacts/backends/sqlite-backend.js +199 -0
  33. package/dist/src/artifacts/browser-export.js +622 -0
  34. package/dist/src/artifacts/capability-resolver.js +94 -0
  35. package/dist/src/artifacts/checksum-manifest.js +91 -0
  36. package/dist/src/artifacts/citation-parser.js +162 -0
  37. package/dist/src/artifacts/cli.js +1462 -0
  38. package/dist/src/artifacts/corpus-tools/citation-densify.js +311 -0
  39. package/dist/src/artifacts/corpus-tools/cli.js +393 -0
  40. package/dist/src/artifacts/corpus-tools/corpus-graph.js +225 -0
  41. package/dist/src/artifacts/corpus-tools/curator.js +186 -0
  42. package/dist/src/artifacts/corpus-tools/discovery-log.js +70 -0
  43. package/dist/src/artifacts/corpus-tools/funder-network.js +141 -0
  44. package/dist/src/artifacts/corpus-tools/induction-audit.js +233 -0
  45. package/dist/src/artifacts/corpus-tools/integrity-scan.js +202 -0
  46. package/dist/src/artifacts/corpus-tools/profile-communities.js +166 -0
  47. package/dist/src/artifacts/corpus-tools/profile-edges.js +72 -0
  48. package/dist/src/artifacts/corpus-tools/profile-embed.js +113 -0
  49. package/dist/src/artifacts/corpus-tools/profile-generate-fm.js +167 -0
  50. package/dist/src/artifacts/corpus-tools/profile-generate.js +181 -0
  51. package/dist/src/artifacts/corpus-tools/profile-metrics.js +98 -0
  52. package/dist/src/artifacts/corpus-tools/profile-status.js +86 -0
  53. package/dist/src/artifacts/corpus-tools/profile-temporal.js +139 -0
  54. package/dist/src/artifacts/corpus-tools/radar-init.js +93 -0
  55. package/dist/src/artifacts/corpus-tools/radar-report.js +118 -0
  56. package/dist/src/artifacts/corpus-tools/radar-shared.js +189 -0
  57. package/dist/src/artifacts/corpus-tools/radar-status.js +68 -0
  58. package/dist/src/artifacts/corpus-tools/sidecar-lint.js +233 -0
  59. package/dist/src/artifacts/corpus-tools/sidecar-repair.js +277 -0
  60. package/dist/src/artifacts/corpus-tools/snapshot.js +591 -0
  61. package/dist/src/artifacts/corpus-tools/source-types.js +164 -0
  62. package/dist/src/artifacts/corpus-tools/vision-extract.js +243 -0
  63. package/dist/src/artifacts/corpus-views/build.js +126 -0
  64. package/dist/src/artifacts/corpus-views/corpus-config.js +124 -0
  65. package/dist/src/artifacts/corpus-views/ref-parser.js +437 -0
  66. package/dist/src/artifacts/corpus-views/renderers.js +353 -0
  67. package/dist/src/artifacts/corpus-views/taxonomies.js +131 -0
  68. package/dist/src/artifacts/dep-graph.js +173 -0
  69. package/dist/src/artifacts/discover-facets.js +375 -0
  70. package/dist/src/artifacts/embedding-index.js +354 -0
  71. package/dist/src/artifacts/enrichment/cli.js +240 -0
  72. package/dist/src/artifacts/enrichment/prompt.js +47 -0
  73. package/dist/src/artifacts/enrichment/store.js +90 -0
  74. package/dist/src/artifacts/enrichment/types.js +12 -0
  75. package/dist/src/artifacts/fortemi-core-query-adapter.js +475 -0
  76. package/dist/src/artifacts/fortemi-core-sync.js +251 -0
  77. package/dist/src/artifacts/fortemi-shard-export.js +44 -0
  78. package/dist/src/artifacts/fulltext.js +108 -0
  79. package/dist/src/artifacts/graph-backend.js +51 -0
  80. package/dist/src/artifacts/graph-query.js +247 -0
  81. package/dist/src/artifacts/hybrid-query.js +225 -0
  82. package/dist/src/artifacts/index-builder.js +1089 -0
  83. package/dist/src/artifacts/index-reader.js +112 -0
  84. package/dist/src/artifacts/index-status.js +193 -0
  85. package/dist/src/artifacts/legacy-index-migration.js +169 -0
  86. package/dist/src/artifacts/move.js +154 -0
  87. package/dist/src/artifacts/operational-state.js +239 -0
  88. package/dist/src/artifacts/prebuilt-build-lock.js +49 -0
  89. package/dist/src/artifacts/query-engine.js +1955 -0
  90. package/dist/src/artifacts/source-graph.js +529 -0
  91. package/dist/src/artifacts/stats.js +169 -0
  92. package/dist/src/artifacts/types.js +606 -0
  93. package/dist/src/artifacts/views/cli.js +293 -0
  94. package/dist/src/artifacts/views/definition.js +170 -0
  95. package/dist/src/artifacts/views/store.js +149 -0
  96. package/dist/src/artifacts/views/types.js +8 -0
  97. package/dist/src/artifacts/watcher.js +255 -0
  98. package/dist/src/catalog/builtin-models.json +636 -0
  99. package/dist/src/catalog/cli.js +228 -0
  100. package/dist/src/catalog/cli.mjs +289 -0
  101. package/dist/src/catalog/index.js +15 -0
  102. package/dist/src/catalog/loader.js +217 -0
  103. package/dist/src/catalog/loader.mjs +251 -0
  104. package/dist/src/catalog/sources.json +41 -0
  105. package/dist/src/catalog/types.js +8 -0
  106. package/dist/src/channel/manager.mjs +504 -0
  107. package/dist/src/cli/agent-spawn.js +178 -0
  108. package/dist/src/cli/cli-extension-loader.js +245 -0
  109. package/dist/src/cli/command-log.js +275 -0
  110. package/dist/src/cli/config-loader.js +232 -0
  111. package/dist/src/cli/env.js +82 -0
  112. package/dist/src/cli/errors.js +150 -0
  113. package/dist/src/cli/find-package-root.js +40 -0
  114. package/dist/src/cli/git-hooks.js +250 -0
  115. package/dist/src/cli/handlers/agentcard.js +206 -0
  116. package/dist/src/cli/handlers/artifacts.js +76 -0
  117. package/dist/src/cli/handlers/best-practices-audit.js +280 -0
  118. package/dist/src/cli/handlers/cockpit.js +137 -0
  119. package/dist/src/cli/handlers/command-log.js +37 -0
  120. package/dist/src/cli/handlers/daemon.js +59 -0
  121. package/dist/src/cli/handlers/diagnose.js +300 -0
  122. package/dist/src/cli/handlers/execution-mode.js +105 -0
  123. package/dist/src/cli/handlers/feedback.js +341 -0
  124. package/dist/src/cli/handlers/help.js +147 -0
  125. package/dist/src/cli/handlers/index.js +302 -0
  126. package/dist/src/cli/handlers/init.js +179 -0
  127. package/dist/src/cli/handlers/install.js +129 -0
  128. package/dist/src/cli/handlers/issues.js +46 -0
  129. package/dist/src/cli/handlers/lint.js +42 -0
  130. package/dist/src/cli/handlers/local-executor.js +315 -0
  131. package/dist/src/cli/handlers/marketplace.js +168 -0
  132. package/dist/src/cli/handlers/mc.js +1010 -0
  133. package/dist/src/cli/handlers/models.js +352 -0
  134. package/dist/src/cli/handlers/packages.js +155 -0
  135. package/dist/src/cli/handlers/ralph-launcher.js +717 -0
  136. package/dist/src/cli/handlers/ralph.js +575 -0
  137. package/dist/src/cli/handlers/refresh.js +432 -0
  138. package/dist/src/cli/handlers/regenerate.js +336 -0
  139. package/dist/src/cli/handlers/repo-access.js +109 -0
  140. package/dist/src/cli/handlers/run.js +165 -0
  141. package/dist/src/cli/handlers/runtime-info.js +301 -0
  142. package/dist/src/cli/handlers/sandbox.js +197 -0
  143. package/dist/src/cli/handlers/scaffolding.js +149 -0
  144. package/dist/src/cli/handlers/script-runner.js +121 -0
  145. package/dist/src/cli/handlers/sdlc-accelerate.js +148 -0
  146. package/dist/src/cli/handlers/serve.js +1835 -0
  147. package/dist/src/cli/handlers/session.js +348 -0
  148. package/dist/src/cli/handlers/setup.js +440 -0
  149. package/dist/src/cli/handlers/skill-lint.js +367 -0
  150. package/dist/src/cli/handlers/skill-usage.js +74 -0
  151. package/dist/src/cli/handlers/steward.js +567 -0
  152. package/dist/src/cli/handlers/subcommands.js +1702 -0
  153. package/dist/src/cli/handlers/team.js +337 -0
  154. package/dist/src/cli/handlers/types.js +11 -0
  155. package/dist/src/cli/handlers/use.js +2772 -0
  156. package/dist/src/cli/handlers/utilities.js +703 -0
  157. package/dist/src/cli/handlers/version.js +134 -0
  158. package/dist/src/cli/handlers/workspace-context.js +93 -0
  159. package/dist/src/cli/handlers/workspace.js +126 -0
  160. package/dist/src/cli/help-generator.js +178 -0
  161. package/dist/src/cli/hooks/builtin/activity-log-hook.js +126 -0
  162. package/dist/src/cli/hooks/executor.js +96 -0
  163. package/dist/src/cli/hooks/index.js +15 -0
  164. package/dist/src/cli/hooks/registry.js +143 -0
  165. package/dist/src/cli/hooks/types.js +13 -0
  166. package/dist/src/cli/log.js +492 -0
  167. package/dist/src/cli/project-isolation/detect.js +70 -0
  168. package/dist/src/cli/project-isolation/index.js +7 -0
  169. package/dist/src/cli/project-isolation/signals.js +17 -0
  170. package/dist/src/cli/project-isolation/warning.js +102 -0
  171. package/dist/src/cli/prompt-utils.js +155 -0
  172. package/dist/src/cli/provider-deployment-plan.js +126 -0
  173. package/dist/src/cli/provider-resolution.js +119 -0
  174. package/dist/src/cli/router.js +305 -0
  175. package/dist/src/cli/scope-resolver.js +392 -0
  176. package/dist/src/cli/skill-usage.js +616 -0
  177. package/dist/src/cli/ui.js +224 -0
  178. package/dist/src/cli/watch-service.js +203 -0
  179. package/dist/src/cli/workflow-orchestrator.js +478 -0
  180. package/dist/src/cli/workspace-signals.js +321 -0
  181. package/dist/src/community/footer.js +14 -0
  182. package/dist/src/community/links.js +123 -0
  183. package/dist/src/community/milestones.js +46 -0
  184. package/dist/src/community/nudge-policy.js +93 -0
  185. package/dist/src/config/aiwg-config.js +883 -0
  186. package/dist/src/config/cli.js +703 -0
  187. package/dist/src/config/gitignore.js +156 -0
  188. package/dist/src/config/project-artifacts.js +76 -0
  189. package/dist/src/config/user-config.js +428 -0
  190. package/dist/src/config/user-registry.js +127 -0
  191. package/dist/src/config/workspace.js +276 -0
  192. package/dist/src/contributors/detect.js +39 -0
  193. package/dist/src/contributors/discover.js +172 -0
  194. package/dist/src/contributors/heuristic.js +186 -0
  195. package/dist/src/contributors/index.js +11 -0
  196. package/dist/src/contributors/types.js +13 -0
  197. package/dist/src/contributors/validation.js +116 -0
  198. package/dist/src/data/community.schema.json +43 -0
  199. package/dist/src/data/community.yaml +16 -0
  200. package/dist/src/extensions/capability-index.js +270 -0
  201. package/dist/src/extensions/claude-hooks-installer.js +268 -0
  202. package/dist/src/extensions/commands/definitions.js +3485 -0
  203. package/dist/src/extensions/deployment-registration.js +463 -0
  204. package/dist/src/extensions/index.js +22 -0
  205. package/dist/src/extensions/loader.js +132 -0
  206. package/dist/src/extensions/managed-marker.js +100 -0
  207. package/dist/src/extensions/manifest.js +196 -0
  208. package/dist/src/extensions/project-local-activity.js +114 -0
  209. package/dist/src/extensions/project-local-discovery.js +261 -0
  210. package/dist/src/extensions/project-local-doctor.js +281 -0
  211. package/dist/src/extensions/project-local-gitignore.js +176 -0
  212. package/dist/src/extensions/project-local-paths.js +121 -0
  213. package/dist/src/extensions/project-local-promote.js +218 -0
  214. package/dist/src/extensions/project-local-remove.js +373 -0
  215. package/dist/src/extensions/project-local-scaffold.js +252 -0
  216. package/dist/src/extensions/project-quickref.js +291 -0
  217. package/dist/src/extensions/registry.js +368 -0
  218. package/dist/src/extensions/shadow-resolver.js +271 -0
  219. package/dist/src/extensions/types.js +86 -0
  220. package/dist/src/extensions/upstream-registry.js +195 -0
  221. package/dist/src/extensions/validation.js +631 -0
  222. package/dist/src/features/catalog.js +88 -0
  223. package/dist/src/features/cli.js +197 -0
  224. package/dist/src/features/installer.js +57 -0
  225. package/dist/src/features/paths.js +14 -0
  226. package/dist/src/features/runtime.js +21 -0
  227. package/dist/src/features/status.js +134 -0
  228. package/dist/src/hooks/laziness-detection.js +532 -0
  229. package/dist/src/intake/git-history-analyzer.js +486 -0
  230. package/dist/src/intake/index.js +9 -0
  231. package/dist/src/issues/cli.js +327 -0
  232. package/dist/src/issues/index.js +5 -0
  233. package/dist/src/issues/live.js +154 -0
  234. package/dist/src/issues/local.js +666 -0
  235. package/dist/src/issues/sync.js +143 -0
  236. package/dist/src/issues/types.js +2 -0
  237. package/dist/src/issues/workflows.js +164 -0
  238. package/dist/src/kb/cli.js +173 -0
  239. package/dist/src/lint/cli.js +171 -0
  240. package/dist/src/lint/loader.js +225 -0
  241. package/dist/src/lint/reporters.js +141 -0
  242. package/dist/src/lint/runner.js +364 -0
  243. package/dist/src/lint/types.js +10 -0
  244. package/dist/src/marketplace/cache.js +110 -0
  245. package/dist/src/marketplace/identifier.js +63 -0
  246. package/dist/src/marketplace/registry.js +36 -0
  247. package/dist/src/marketplace/sources/clawhub.js +53 -0
  248. package/dist/src/marketplace/sources/git.js +45 -0
  249. package/dist/src/marketplace/types.js +9 -0
  250. package/dist/src/mcp/adapters/codex-runtime.js +224 -0
  251. package/dist/src/mcp/cli.mjs +1197 -0
  252. package/dist/src/mcp/elicitation.mjs +149 -0
  253. package/dist/src/mcp/helpers.mjs +287 -0
  254. package/dist/src/mcp/index.mjs +11 -0
  255. package/dist/src/mcp/profiles.js +263 -0
  256. package/dist/src/mcp/profiles.mjs +360 -0
  257. package/dist/src/mcp/registry.js +376 -0
  258. package/dist/src/mcp/registry.mjs +387 -0
  259. package/dist/src/mcp/server.mjs +633 -0
  260. package/dist/src/mcp/test-simple.mjs +271 -0
  261. package/dist/src/mcp/tools/command-run.mjs +101 -0
  262. package/dist/src/mcp/tools/discovery.mjs +291 -0
  263. package/dist/src/mcp/tools/interaction.mjs +87 -0
  264. package/dist/src/mcp/tools/orchestration.mjs +320 -0
  265. package/dist/src/mcp/tools/subsystems.mjs +640 -0
  266. package/dist/src/memory/cli.js +166 -0
  267. package/dist/src/memory/project-registry.js +282 -0
  268. package/dist/src/metrics/artifact-metrics.js +184 -0
  269. package/dist/src/metrics/context-budget.js +174 -0
  270. package/dist/src/metrics/token-counter.js +133 -0
  271. package/dist/src/models/config-loader.js +228 -0
  272. package/dist/src/models/index.js +22 -0
  273. package/dist/src/models/model-capabilities.v1.json +120 -0
  274. package/dist/src/models/model-catalog.v1.json +96 -0
  275. package/dist/src/models/model-discovery.js +521 -0
  276. package/dist/src/models/provider-models.js +73 -0
  277. package/dist/src/models/provider-policy.js +273 -0
  278. package/dist/src/models/resolver.js +245 -0
  279. package/dist/src/models/router.js +70 -0
  280. package/dist/src/models/types.js +8 -0
  281. package/dist/src/models/wrapper-deployment.js +178 -0
  282. package/dist/src/models/wrapper-route.js +54 -0
  283. package/dist/src/ops/cli.js +268 -0
  284. package/dist/src/ops/registry.js +623 -0
  285. package/dist/src/packages/adapters/clawhub.js +113 -0
  286. package/dist/src/packages/adapters/git.js +153 -0
  287. package/dist/src/packages/adapters/gitea.js +93 -0
  288. package/dist/src/packages/adapters/github.js +69 -0
  289. package/dist/src/packages/adapters/local-cache.js +89 -0
  290. package/dist/src/packages/package-registry.js +143 -0
  291. package/dist/src/packages/registry.js +238 -0
  292. package/dist/src/packages/types.js +11 -0
  293. package/dist/src/plugin/cross-framework-ops.js +475 -0
  294. package/dist/src/plugin/framework-config-loader.js +235 -0
  295. package/dist/src/plugin/framework-detector.js +186 -0
  296. package/dist/src/plugin/framework-isolator.js +341 -0
  297. package/dist/src/plugin/framework-migration.js +371 -0
  298. package/dist/src/plugin/metadata-validator.js +906 -0
  299. package/dist/src/plugin/plugin-installer.js +436 -0
  300. package/dist/src/plugin/plugin-status.js +369 -0
  301. package/dist/src/plugin/plugin-uninstaller.js +473 -0
  302. package/dist/src/plugin/registry-validator.js +344 -0
  303. package/dist/src/plugin/skill-command-translator.js +415 -0
  304. package/dist/src/plugin/workspace-creator.js +189 -0
  305. package/dist/src/plugin/workspace-migrator.js +821 -0
  306. package/dist/src/policy/authorization.js +390 -0
  307. package/dist/src/policy/repo-access.js +215 -0
  308. package/dist/src/provenance/cli.js +16 -0
  309. package/dist/src/providers/capability-matrix.js +300 -0
  310. package/dist/src/providers/capability-matrix.yaml +514 -0
  311. package/dist/src/providers/provider-definitions.js +977 -0
  312. package/dist/src/providers/provider-definitions.mjs +159 -0
  313. package/dist/src/providers/provider-inventory.js +169 -0
  314. package/dist/src/quality/feedback-ab.js +174 -0
  315. package/dist/src/quality/feedback-collector.js +176 -0
  316. package/dist/src/quality/feedback-tracker.js +123 -0
  317. package/dist/src/quality/patterns/adr.json +24 -0
  318. package/dist/src/quality/patterns/sad.json +24 -0
  319. package/dist/src/quality/patterns/test-plan.json +24 -0
  320. package/dist/src/quality/patterns/use-case.json +24 -0
  321. package/dist/src/quality/scoring.js +157 -0
  322. package/dist/src/reflections/cli.js +15 -0
  323. package/dist/src/release/plan.js +139 -0
  324. package/dist/src/research/cache/manager.js +227 -0
  325. package/dist/src/research/clients/arxiv.js +156 -0
  326. package/dist/src/research/clients/base.js +197 -0
  327. package/dist/src/research/clients/crossref.js +112 -0
  328. package/dist/src/research/clients/semantic-scholar.js +126 -0
  329. package/dist/src/research/clients/unpaywall.js +87 -0
  330. package/dist/src/research/index.js +14 -0
  331. package/dist/src/research/query-cli.js +336 -0
  332. package/dist/src/research/services/acquisition.js +313 -0
  333. package/dist/src/research/services/archival.js +225 -0
  334. package/dist/src/research/services/citation.js +279 -0
  335. package/dist/src/research/services/discovery.js +243 -0
  336. package/dist/src/research/services/documentation.js +269 -0
  337. package/dist/src/research/services/index.js +14 -0
  338. package/dist/src/research/services/provenance.js +279 -0
  339. package/dist/src/research/services/quality.js +370 -0
  340. package/dist/src/research/services/types.js +7 -0
  341. package/dist/src/research/storage-cli.js +20 -0
  342. package/dist/src/research/types.js +47 -0
  343. package/dist/src/resources/index.d.ts +2 -0
  344. package/dist/src/resources/index.js +2 -0
  345. package/dist/src/resources/web-release.d.ts +89 -0
  346. package/dist/src/resources/web-release.js +882 -0
  347. package/dist/src/rlm/cache/cli.js +162 -0
  348. package/dist/src/rlm/cache/hash.js +35 -0
  349. package/dist/src/rlm/cache/store.js +170 -0
  350. package/dist/src/rlm/cache/types.js +8 -0
  351. package/dist/src/rlm/cli.js +650 -0
  352. package/dist/src/serve/a2a-terminal-observer.js +120 -0
  353. package/dist/src/serve/agent-router.js +205 -0
  354. package/dist/src/serve/dispatch-router.js +171 -0
  355. package/dist/src/serve/executor-registry.js +715 -0
  356. package/dist/src/serve/mission-conductor.js +177 -0
  357. package/dist/src/serve/orchestrator-adapter.js +113 -0
  358. package/dist/src/serve/orchestrator-override.js +94 -0
  359. package/dist/src/serve/orchestrator-pty.js +133 -0
  360. package/dist/src/serve/pty-bridge.js +799 -0
  361. package/dist/src/serve/sandbox-registry.js +832 -0
  362. package/dist/src/serve/screen-reader.js +228 -0
  363. package/dist/src/serve/stack-adapters.js +68 -0
  364. package/dist/src/serve/telemetry.js +143 -0
  365. package/dist/src/skills/adapters/clawhub.js +250 -0
  366. package/dist/src/skills/adapters/local.js +335 -0
  367. package/dist/src/skills/adapters/openclaw.js +316 -0
  368. package/dist/src/skills/cli.js +334 -0
  369. package/dist/src/skills/registry.js +117 -0
  370. package/dist/src/skills/run.js +259 -0
  371. package/dist/src/skills/runtime.js +94 -0
  372. package/dist/src/skills/types.js +10 -0
  373. package/dist/src/smiths/agentsmith/demo.js +116 -0
  374. package/dist/src/smiths/agentsmith/examples.js +300 -0
  375. package/dist/src/smiths/agentsmith/generator.js +503 -0
  376. package/dist/src/smiths/agentsmith/index.js +11 -0
  377. package/dist/src/smiths/agentsmith/types.js +8 -0
  378. package/dist/src/smiths/commandsmith/example.js +220 -0
  379. package/dist/src/smiths/commandsmith/generator.js +288 -0
  380. package/dist/src/smiths/commandsmith/index.js +41 -0
  381. package/dist/src/smiths/commandsmith/templates.js +296 -0
  382. package/dist/src/smiths/commandsmith/types.js +7 -0
  383. package/dist/src/smiths/context-pipeline/aiwg-md.js +104 -0
  384. package/dist/src/smiths/context-pipeline/allowlist.js +195 -0
  385. package/dist/src/smiths/context-pipeline/claude-hook.js +127 -0
  386. package/dist/src/smiths/context-pipeline/discovery.js +187 -0
  387. package/dist/src/smiths/context-pipeline/external-links-section.js +49 -0
  388. package/dist/src/smiths/context-pipeline/finalization.js +147 -0
  389. package/dist/src/smiths/context-pipeline/generator.js +477 -0
  390. package/dist/src/smiths/context-pipeline/index.js +30 -0
  391. package/dist/src/smiths/context-pipeline/legacy-inject.js +103 -0
  392. package/dist/src/smiths/context-pipeline/managed-hook.js +95 -0
  393. package/dist/src/smiths/context-pipeline/overflow.js +212 -0
  394. package/dist/src/smiths/context-pipeline/parallelism-section.js +94 -0
  395. package/dist/src/smiths/context-pipeline/provider-policy.js +64 -0
  396. package/dist/src/smiths/context-pipeline/sanitizer.js +93 -0
  397. package/dist/src/smiths/context-pipeline/types.js +23 -0
  398. package/dist/src/smiths/context-pipeline/workspace-context.js +988 -0
  399. package/dist/src/smiths/hook-bridge/codex-translator.js +129 -0
  400. package/dist/src/smiths/hook-bridge/copilot-translator.js +64 -0
  401. package/dist/src/smiths/hook-bridge/factory-translator.js +43 -0
  402. package/dist/src/smiths/hook-bridge/hermes-translator.js +88 -0
  403. package/dist/src/smiths/hook-bridge/index.js +79 -0
  404. package/dist/src/smiths/hook-bridge/loader.js +116 -0
  405. package/dist/src/smiths/hook-bridge/shim.js +89 -0
  406. package/dist/src/smiths/hook-bridge/types.js +31 -0
  407. package/dist/src/smiths/index.js +39 -0
  408. package/dist/src/smiths/mcpsmith/analyzers/cli-analyzer.js +390 -0
  409. package/dist/src/smiths/mcpsmith/example.js +119 -0
  410. package/dist/src/smiths/mcpsmith/generator.js +236 -0
  411. package/dist/src/smiths/mcpsmith/index.js +17 -0
  412. package/dist/src/smiths/mcpsmith/types.js +10 -0
  413. package/dist/src/smiths/platform-paths.js +101 -0
  414. package/dist/src/smiths/skillsmith/codex-sidecar.js +85 -0
  415. package/dist/src/smiths/skillsmith/collision-detector.js +249 -0
  416. package/dist/src/smiths/skillsmith/demo.js +83 -0
  417. package/dist/src/smiths/skillsmith/examples.js +161 -0
  418. package/dist/src/smiths/skillsmith/generator.js +316 -0
  419. package/dist/src/smiths/skillsmith/index.js +12 -0
  420. package/dist/src/smiths/skillsmith/namespace-adapter.js +180 -0
  421. package/dist/src/smiths/skillsmith/platform-resolver.js +157 -0
  422. package/dist/src/smiths/skillsmith/types.js +9 -0
  423. package/dist/src/smiths/toolsmith/index.js +10 -0
  424. package/dist/src/smiths/toolsmith/runtime-discovery.mjs +710 -0
  425. package/dist/src/smiths/toolsmith/types.js +8 -0
  426. package/dist/src/storage/backends/fortemi.js +227 -0
  427. package/dist/src/storage/backends/fs.js +134 -0
  428. package/dist/src/storage/backends/logseq.js +309 -0
  429. package/dist/src/storage/backends/obsidian.js +233 -0
  430. package/dist/src/storage/cli.js +468 -0
  431. package/dist/src/storage/config.js +272 -0
  432. package/dist/src/storage/index.js +108 -0
  433. package/dist/src/storage/subsystem-cli.js +201 -0
  434. package/dist/src/storage/types.js +33 -0
  435. package/dist/src/testing/corpus/corpus-builder.js +383 -0
  436. package/dist/src/testing/corpus/ground-truth-manager.js +460 -0
  437. package/dist/src/testing/corpus/index.js +10 -0
  438. package/dist/src/testing/fixtures/index.js +9 -0
  439. package/dist/src/testing/fixtures/test-data-factory.js +472 -0
  440. package/dist/src/testing/generators/index.js +11 -0
  441. package/dist/src/testing/generators/test-case-generator.js +393 -0
  442. package/dist/src/testing/generators/test-code-generator.js +384 -0
  443. package/dist/src/testing/generators/use-case-parser.js +325 -0
  444. package/dist/src/testing/index.js +11 -0
  445. package/dist/src/tracker/capability-protocol.js +118 -0
  446. package/dist/src/update/checker.mjs +396 -0
  447. package/dist/src/update/notifier.mjs +235 -0
  448. package/dist/src/writing/content-diversifier.js +804 -0
  449. package/dist/src/writing/example-generator.js +959 -0
  450. package/dist/src/writing/example-templates.js +99 -0
  451. package/dist/src/writing/pattern-library.js +486 -0
  452. package/dist/src/writing/patterns/banned-phrases.json +4413 -0
  453. package/dist/src/writing/patterns/formulaic-structures.json +1300 -0
  454. package/dist/src/writing/patterns/generic-adjectives.json +1510 -0
  455. package/dist/src/writing/patterns/hedging-language.json +2096 -0
  456. package/dist/src/writing/patterns/transition-words.json +1285 -0
  457. package/dist/src/writing/patterns/weak-verbs.json +1112 -0
  458. package/dist/src/writing/prompt-optimizer.js +660 -0
  459. package/dist/src/writing/prompt-templates.js +583 -0
  460. package/dist/src/writing/scoring-config-loader.js +168 -0
  461. package/dist/src/writing/validation-engine.js +595 -0
  462. package/dist/src/writing/validation-rules.js +380 -0
  463. package/dist/src/writing/voice-analyzer.js +439 -0
  464. package/dist/src/writing/voice-calibration.js +661 -0
  465. package/dist/src/writing/voice-profiles.json +588 -0
  466. package/package.json +64 -9
@@ -0,0 +1,1010 @@
1
+ /**
2
+ * Mission Control Command Handler
3
+ *
4
+ * Multi-loop background orchestration dashboard. Lets an orchestrator
5
+ * spawn multiple long-running agent loops, monitor all simultaneously,
6
+ * and react to completions or failures without blocking the primary session.
7
+ *
8
+ * Subcommands: start, dispatch, status, watch, abort, pause, resume, stop, list
9
+ *
10
+ * @implements @agentic/code/frameworks/sdlc-complete/rules/self-maintenance.md
11
+ * @source @src/cli/router.ts
12
+ * @issue #483
13
+ */
14
+ import * as ui from '../ui.js';
15
+ import { promises as fs } from 'node:fs';
16
+ import { join } from 'node:path';
17
+ import { randomBytes } from 'node:crypto';
18
+ // ── Constants ────────────────────────────────────────────────
19
+ const MC_ROOT = '.aiwg/ralph-external/mc';
20
+ const SESSIONS_DIR = join(MC_ROOT, 'sessions');
21
+ // ── Helpers ──────────────────────────────────────────────────
22
+ function genId(prefix) {
23
+ return `${prefix}-${Date.now().toString(36)}-${randomBytes(3).toString('hex')}`;
24
+ }
25
+ async function ensureDir(dir) {
26
+ await fs.mkdir(dir, { recursive: true });
27
+ }
28
+ async function readSession(sessionId) {
29
+ const path = join(SESSIONS_DIR, sessionId, 'session.json');
30
+ try {
31
+ const raw = await fs.readFile(path, 'utf-8');
32
+ return JSON.parse(raw);
33
+ }
34
+ catch {
35
+ return null;
36
+ }
37
+ }
38
+ async function writeSession(session) {
39
+ const dir = join(SESSIONS_DIR, session.id);
40
+ await ensureDir(dir);
41
+ session.updatedAt = new Date().toISOString();
42
+ await fs.writeFile(join(dir, 'session.json'), JSON.stringify(session, null, 2));
43
+ }
44
+ async function appendLog(sessionId, event) {
45
+ const logPath = join(SESSIONS_DIR, sessionId, 'log.jsonl');
46
+ const entry = JSON.stringify({ ...event, ts: new Date().toISOString() });
47
+ await fs.appendFile(logPath, entry + '\n');
48
+ }
49
+ async function listSessions() {
50
+ try {
51
+ const entries = await fs.readdir(SESSIONS_DIR, { withFileTypes: true });
52
+ const sessions = [];
53
+ for (const entry of entries) {
54
+ if (entry.isDirectory()) {
55
+ const s = await readSession(entry.name);
56
+ if (s)
57
+ sessions.push(s);
58
+ }
59
+ }
60
+ return sessions;
61
+ }
62
+ catch {
63
+ return [];
64
+ }
65
+ }
66
+ async function findActiveSession(sessionIdArg) {
67
+ if (sessionIdArg)
68
+ return readSession(sessionIdArg);
69
+ // Find latest active session
70
+ const sessions = await listSessions();
71
+ const active = sessions
72
+ .filter(s => s.state === 'active' || s.state === 'paused')
73
+ .sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
74
+ return active[0] || null;
75
+ }
76
+ function parseFlag(args, flag) {
77
+ for (let i = 0; i < args.length; i++) {
78
+ if (args[i] === flag) {
79
+ return i + 1 < args.length ? args[i + 1] : undefined;
80
+ }
81
+ // Support --flag=value — previously silently ignored, which for budget
82
+ // flags meant an unbounded loop the operator believed was capped (#1770)
83
+ if (args[i].startsWith(`${flag}=`)) {
84
+ return args[i].slice(flag.length + 1);
85
+ }
86
+ }
87
+ return undefined;
88
+ }
89
+ function hasFlag(args, flag) {
90
+ return args.includes(flag);
91
+ }
92
+ /**
93
+ * Parse a numeric flag. A flag that is PRESENT but not a positive number is a
94
+ * usage error collected into `invalidSink` — silently dropping it used to
95
+ * dispatch missions with no ceiling while the operator believed one applied
96
+ * (#1770).
97
+ */
98
+ function parseNumberFlag(args, flag, invalidSink) {
99
+ const raw = parseFlag(args, flag);
100
+ if (raw === undefined)
101
+ return undefined;
102
+ const value = Number(raw);
103
+ if (!Number.isFinite(value) || value <= 0) {
104
+ invalidSink?.push(`${flag} (got '${raw}')`);
105
+ return undefined;
106
+ }
107
+ return value;
108
+ }
109
+ function wantsHelp(args) {
110
+ return args.includes('--help') || args.includes('-h');
111
+ }
112
+ /**
113
+ * Per-subcommand usage strings (#1440).
114
+ *
115
+ * `aiwg mc <subcommand> --help` MUST print this text and exit before any
116
+ * side-effecting logic. The previous behavior — running `aiwg mc start --help`
117
+ * actually created a session — violated the universal `--help` convention.
118
+ */
119
+ const subcommandUsage = {
120
+ start: `Usage: aiwg mc start [--name "<label>"] [--max-missions N]
121
+
122
+ Create a new Mission Control session for tracking background missions.
123
+
124
+ --name <label> Human-readable session name (default: "Mission YYYY-MM-DD")
125
+ --max-missions N Capacity limit for queued missions (default: 10)
126
+
127
+ Note: 'start' only creates the session. Use 'dispatch' to queue missions and
128
+ 'run' to drain the queue.`,
129
+ dispatch: `Usage: aiwg mc dispatch <session-id> "<objective>" [options]
130
+
131
+ Queue a mission onto a session. Does NOT execute — use 'aiwg mc run' to launch.
132
+
133
+ --completion "<criteria>" Verifiable completion criteria (required for 'mc run')
134
+ --priority <level> Priority hint (default: normal)
135
+ --max-iterations N Ralph iteration cap when launched (default: 10)
136
+ --max-total-tokens N Hard cumulative token ceiling when observable
137
+ --max-output-tokens N Hard cumulative output-token ceiling
138
+ --max-tool-calls N Hard cumulative tool-call ceiling
139
+ --max-total-cost USD Hard cumulative provider-reported spend ceiling
140
+ --max-wall-clock-minutes N Hard cumulative runtime ceiling
141
+ --exploration-quota K Require structural variant after K flat cycles
142
+ (off unless declared; no default K)
143
+ --budget-stop-policy P completion-wins (default) | budget-wins
144
+ --mode pty-orchestrator PTY-orchestrator mode (requires --target-agent)
145
+ --target-agent <id> Required for --mode pty-orchestrator`,
146
+ run: `Usage: aiwg mc run [<session-id>] [--accept-cost]
147
+
148
+ Drain queued missions in a session by launching each as a ralph loop. Missions
149
+ without --completion criteria are skipped with a warning.
150
+
151
+ --accept-cost Skip the cost-warning gate (required for non-TTY contexts
152
+ when estimated cumulative cost exceeds $5). See #1450.`,
153
+ status: `Usage: aiwg mc status [<session-id>] [--json]
154
+
155
+ Show mission status for a session. Auto-syncs from ralph loop state files.
156
+
157
+ --json Emit JSON instead of human-readable table`,
158
+ watch: `Usage: aiwg mc watch [<session-id>]
159
+
160
+ Live-monitor mission progress (non-interactive context prints status once).`,
161
+ abort: `Usage: aiwg mc abort <session-id> <mission-id>
162
+
163
+ Mark a specific mission as aborted.`,
164
+ pause: `Usage: aiwg mc pause [<session-id>]
165
+
166
+ Pause an active session; running missions transition to 'paused' status.`,
167
+ resume: `Usage: aiwg mc resume [<session-id>]
168
+
169
+ Resume a paused session; paused missions transition back to 'running'.`,
170
+ stop: `Usage: aiwg mc stop [<session-id>] [--drain]
171
+
172
+ Shut down a session.
173
+
174
+ --drain Cancel queued, let running finish (default: abort all non-completed)`,
175
+ list: `Usage: aiwg mc list [--json]
176
+
177
+ List all Mission Control sessions.`,
178
+ agents: `Usage: aiwg mc agents [filters] [--json]
179
+
180
+ Query routable agents from a local 'aiwg serve' instance.
181
+
182
+ --framework <name> Require an AIWG framework (repeatable)
183
+ --sandbox <id> Restrict to a sandbox
184
+ --agent <id> Restrict to a specific agent ID
185
+ --name <n> Match by logical name
186
+ --max-cpu <pct> Reject agents above this CPU %
187
+ --min-memory <gb> Reject agents below this memory threshold
188
+ --json Output raw JSON`,
189
+ };
190
+ function printSubcommandHelp(name) {
191
+ const text = subcommandUsage[name];
192
+ ui.blank();
193
+ if (text) {
194
+ console.log(` ${text}`);
195
+ }
196
+ else {
197
+ console.log(` No detailed help for 'aiwg mc ${name}'. Run 'aiwg mc --help' for the full reference.`);
198
+ }
199
+ ui.blank();
200
+ }
201
+ function getPositionalArgs(args) {
202
+ const positional = [];
203
+ for (let i = 0; i < args.length; i++) {
204
+ if (args[i].startsWith('--')) {
205
+ // Skip flag and its value if it has one
206
+ if (i + 1 < args.length && !args[i + 1].startsWith('--'))
207
+ i++;
208
+ continue;
209
+ }
210
+ positional.push(args[i]);
211
+ }
212
+ return positional;
213
+ }
214
+ // ── Subcommand handlers ──────────────────────────────────────
215
+ async function mcStart(ctx) {
216
+ const name = parseFlag(ctx.args, '--name') || `Mission ${new Date().toISOString().slice(0, 10)}`;
217
+ const maxMissions = parseInt(parseFlag(ctx.args, '--max-missions') || '10', 10);
218
+ const session = {
219
+ id: genId('mc'),
220
+ name,
221
+ state: 'active',
222
+ maxMissions,
223
+ createdAt: new Date().toISOString(),
224
+ updatedAt: new Date().toISOString(),
225
+ missions: [],
226
+ };
227
+ await writeSession(session);
228
+ await appendLog(session.id, { event: 'session_started', name, maxMissions });
229
+ ui.blank();
230
+ console.log(` ${ui.brandMark()} ${ui.bold('Mission Control')} — ${ui.accent(name)}`);
231
+ ui.rule();
232
+ ui.success(`Session started: ${session.id}`);
233
+ ui.info(`Max missions: ${maxMissions}`);
234
+ // #1439: the lifecycle is start → dispatch → run → status. State this
235
+ // up-front so users don't get stuck wondering why missions never execute.
236
+ ui.info(`Next: \`aiwg mc dispatch ${session.id} "<objective>" --completion "<criteria>"\``);
237
+ ui.info(`Then: \`aiwg mc run ${session.id}\` to drain the queue (missions stay 'queued' until you do).`);
238
+ ui.blank();
239
+ return { exitCode: 0, message: session.id };
240
+ }
241
+ async function mcDispatch(ctx) {
242
+ const positional = getPositionalArgs(ctx.args);
243
+ const sessionId = positional[0];
244
+ const objective = positional.slice(1).join(' ') || parseFlag(ctx.args, '--objective');
245
+ const completion = parseFlag(ctx.args, '--completion');
246
+ const priority = parseFlag(ctx.args, '--priority') || 'normal';
247
+ const invalidFlags = [];
248
+ const maxIterationsRaw = parseFlag(ctx.args, '--max-iterations');
249
+ let maxIterations = 10;
250
+ if (maxIterationsRaw !== undefined) {
251
+ const parsedIterations = parseInt(maxIterationsRaw, 10);
252
+ if (!Number.isFinite(parsedIterations) || parsedIterations <= 0) {
253
+ invalidFlags.push(`--max-iterations (got '${maxIterationsRaw}')`);
254
+ }
255
+ else {
256
+ maxIterations = parsedIterations;
257
+ }
258
+ }
259
+ const maxTotalTokens = parseNumberFlag(ctx.args, '--max-total-tokens', invalidFlags);
260
+ const maxOutputTokens = parseNumberFlag(ctx.args, '--max-output-tokens', invalidFlags);
261
+ const maxToolCalls = parseNumberFlag(ctx.args, '--max-tool-calls', invalidFlags);
262
+ const maxTotalCost = parseNumberFlag(ctx.args, '--max-total-cost', invalidFlags);
263
+ const maxWallClockMinutes = parseNumberFlag(ctx.args, '--max-wall-clock-minutes', invalidFlags);
264
+ const explorationQuota = parseNumberFlag(ctx.args, '--exploration-quota', invalidFlags);
265
+ const budgetStopPolicyRaw = parseFlag(ctx.args, '--budget-stop-policy');
266
+ let budgetStopPolicy;
267
+ if (budgetStopPolicyRaw !== undefined) {
268
+ if (budgetStopPolicyRaw === 'completion-wins' || budgetStopPolicyRaw === 'budget-wins') {
269
+ budgetStopPolicy = budgetStopPolicyRaw;
270
+ }
271
+ else {
272
+ invalidFlags.push(`--budget-stop-policy (got '${budgetStopPolicyRaw}', expected completion-wins|budget-wins)`);
273
+ }
274
+ }
275
+ const modeRaw = parseFlag(ctx.args, '--mode') || 'direct';
276
+ const mode = modeRaw === 'pty-orchestrator' ? 'pty-orchestrator' : 'direct';
277
+ const targetAgent = parseFlag(ctx.args, '--target-agent');
278
+ if (!objective) {
279
+ // #1438: keep this in sync with subcommandUsage.dispatch above so 'mc
280
+ // dispatch' with bad args and 'mc dispatch --help' agree on flags.
281
+ ui.error('Usage: aiwg mc dispatch <session-id> "<objective>" [--completion "<criteria>"] [--max-iterations N] [--priority <level>] [--mode pty-orchestrator] [--target-agent <agent-id>]');
282
+ return { exitCode: 1 };
283
+ }
284
+ if (invalidFlags.length > 0) {
285
+ ui.error(`Invalid numeric flag value(s): ${invalidFlags.join(', ')}. Budget/quota flags require positive numbers. Mission not dispatched.`);
286
+ return { exitCode: 1 };
287
+ }
288
+ if (mode === 'pty-orchestrator' && !targetAgent) {
289
+ ui.error('--mode pty-orchestrator requires --target-agent <agent-id>');
290
+ return { exitCode: 1 };
291
+ }
292
+ const session = await findActiveSession(sessionId);
293
+ if (!session) {
294
+ ui.error(sessionId ? `Session not found: ${sessionId}` : 'No active session. Run `aiwg mc start` first.');
295
+ return { exitCode: 1 };
296
+ }
297
+ if (session.missions.length >= session.maxMissions) {
298
+ ui.error(`Session at capacity (${session.maxMissions} missions). Increase with --max-missions or stop completed missions.`);
299
+ return { exitCode: 1 };
300
+ }
301
+ // #1361: Check project-level parallelism cap. Active missions = running or
302
+ // queued; if at cap, warn but still queue (FIFO behavior — the mission goes
303
+ // into the session as 'queued' and will run when a slot frees up).
304
+ let capWarning;
305
+ try {
306
+ const { readAiwgConfig, resolveParallelism } = await import('../../config/aiwg-config.js');
307
+ const cfg = await readAiwgConfig(ctx.cwd || process.cwd());
308
+ if (cfg) {
309
+ const resolved = resolveParallelism(cfg.parallelism, cfg.providers[0]);
310
+ const activeCount = session.missions.filter(m => m.status === 'running' || m.status === 'queued' || m.status === 'paused').length;
311
+ if (activeCount >= resolved.max_parallel_mc_missions) {
312
+ capWarning = `Active missions (${activeCount}) at or above project parallelism cap (${resolved.max_parallel_mc_missions}). Mission will queue; bump via 'aiwg config set --project parallelism.max_parallel_mc_missions N'.`;
313
+ }
314
+ }
315
+ }
316
+ catch {
317
+ // Non-fatal — config read failure doesn't block dispatch
318
+ }
319
+ const mission = {
320
+ id: genId('m'),
321
+ objective,
322
+ completion,
323
+ status: 'queued',
324
+ loop: 0,
325
+ maxIterations,
326
+ maxTotalTokens,
327
+ maxOutputTokens,
328
+ maxToolCalls,
329
+ maxTotalCost,
330
+ maxWallClockMinutes,
331
+ explorationQuota,
332
+ budgetStopPolicy,
333
+ priority,
334
+ mode,
335
+ targetAgent: targetAgent || undefined,
336
+ };
337
+ session.missions.push(mission);
338
+ await writeSession(session);
339
+ await appendLog(session.id, {
340
+ event: 'mission_dispatched',
341
+ missionId: mission.id,
342
+ objective,
343
+ priority,
344
+ mode,
345
+ targetAgent,
346
+ lfdBudgets: {
347
+ maxTotalTokens,
348
+ maxOutputTokens,
349
+ maxToolCalls,
350
+ maxTotalCost,
351
+ maxWallClockMinutes,
352
+ explorationQuota,
353
+ },
354
+ });
355
+ if (capWarning)
356
+ ui.warn(capWarning);
357
+ ui.success(`Dispatched mission ${mission.id}: ${objective}`);
358
+ const modeLabel = mode === 'pty-orchestrator' ? ` | Mode: PTY orchestrator → ${targetAgent}` : '';
359
+ ui.info(`Priority: ${priority} | Max iterations: ${maxIterations}${modeLabel}`);
360
+ const lfdLimits = [
361
+ maxTotalTokens ? `total tokens ${maxTotalTokens}` : null,
362
+ maxOutputTokens ? `output tokens ${maxOutputTokens}` : null,
363
+ maxToolCalls ? `tool calls ${maxToolCalls}` : null,
364
+ maxTotalCost ? `total cost $${maxTotalCost}` : null,
365
+ maxWallClockMinutes ? `wall clock ${maxWallClockMinutes}m` : null,
366
+ explorationQuota ? `exploration quota ${explorationQuota}` : null,
367
+ ].filter(Boolean);
368
+ if (lfdLimits.length > 0) {
369
+ ui.info(`LFD limits: ${lfdLimits.join(' | ')}`);
370
+ }
371
+ // #1439: dispatch alone does NOT execute the mission. Surface the next step
372
+ // so the user knows the queue won't drain on its own.
373
+ ui.info(`Next: run \`aiwg mc run ${session.id}\` to launch queued missions as ralph loops.`);
374
+ return { exitCode: 0, message: mission.id };
375
+ }
376
+ /**
377
+ * Drain queued missions in a session by launching each as a ralph loop (#1439).
378
+ *
379
+ * Before this command existed, missions sat in `queued` status forever because
380
+ * no supervisor process drained the queue. `mc run` is the explicit supervisor
381
+ * step: for every queued direct-mode mission, spawn a detached ralph process
382
+ * via launchExternalRalph(), record the resulting loopId + pid on the mission,
383
+ * and flip the mission to `running`.
384
+ *
385
+ * Status sync back from ralph to mc happens lazily via syncMissionsFromRalph()
386
+ * called from `mc status` and `mc watch` — no separate daemon needed.
387
+ *
388
+ * Limitations of cycle 1:
389
+ * - pty-orchestrator missions are skipped with a warning (separate codepath)
390
+ * - missions launch sequentially (each call to launchExternalRalph spawns a
391
+ * detached process, so they DO run in parallel after launch — the loop here
392
+ * is just for orderly dispatch, not for parallel scheduling)
393
+ */
394
+ async function mcRun(ctx) {
395
+ const positional = getPositionalArgs(ctx.args);
396
+ const sessionId = positional[0];
397
+ const acceptCost = hasFlag(ctx.args, '--accept-cost');
398
+ const session = await findActiveSession(sessionId);
399
+ if (!session) {
400
+ ui.error(sessionId ? `Session not found: ${sessionId}` : 'No active session. Run `aiwg mc start` first.');
401
+ return { exitCode: 1 };
402
+ }
403
+ const queued = session.missions.filter((m) => m.status === 'queued');
404
+ if (queued.length === 0) {
405
+ ui.info(`No queued missions in session ${session.id}. Run \`aiwg mc dispatch ${session.id} "<objective>"\` to add one.`);
406
+ return { exitCode: 0 };
407
+ }
408
+ // #1450 P0: cost warning gate.
409
+ //
410
+ // Each headless claude session pays a ~$1.60 cache-creation cost on iteration
411
+ // 1 before any user-meaningful work (sonnet baseline; opus is ~$3.90). Across
412
+ // N missions × M iterations the floor compounds quickly. Warn before launch
413
+ // and refuse in non-TTY contexts unless --accept-cost is set.
414
+ //
415
+ // Estimate is intentionally conservative: cumulative iteration floor =
416
+ // missions × max_iterations × sonnet_cache_cost. Real spend may be lower if
417
+ // missions complete in fewer iterations.
418
+ const eligible = queued.filter(m => m.mode !== 'pty-orchestrator' && !!m.completion);
419
+ const SONNET_CACHE_USD = 1.60;
420
+ const iterFloor = eligible.reduce((sum, m) => sum + m.maxIterations, 0);
421
+ const estimateUsd = iterFloor * SONNET_CACHE_USD;
422
+ const COST_WARNING_THRESHOLD_USD = 5.0;
423
+ if (estimateUsd >= COST_WARNING_THRESHOLD_USD && !acceptCost) {
424
+ ui.blank();
425
+ ui.warn(`Cost estimate: ~$${estimateUsd.toFixed(2)} (${eligible.length} missions × iteration floors × ~$${SONNET_CACHE_USD.toFixed(2)} cache cost per claude headless iter).`);
426
+ ui.warn('Actual spend may be lower if missions complete early, higher if model is opus or context grows.');
427
+ if (!process.stdout.isTTY) {
428
+ ui.error('Refusing to launch in non-interactive context. Re-run with `--accept-cost` to proceed.');
429
+ return { exitCode: 1 };
430
+ }
431
+ // TTY path: surface the warning and continue. A future revision should
432
+ // prompt y/N here; for now the warning is informational and the operator
433
+ // can Ctrl+C before launches actually begin.
434
+ ui.info('Continuing (TTY). Use Ctrl+C to abort within the next 2 seconds.');
435
+ await new Promise((resolve) => setTimeout(resolve, 2000));
436
+ }
437
+ ui.blank();
438
+ console.log(` ${ui.brandMark()} ${ui.bold('Mission Control')} — running ${queued.length} queued mission(s) in ${ui.accent(session.id)}`);
439
+ ui.rule();
440
+ // Lazy import — ralph-launcher pulls in node:child_process and other heavy
441
+ // deps we don't want to pay for on every mc subcommand.
442
+ const { launchExternalRalph } = await import('./ralph-launcher.js');
443
+ let launched = 0;
444
+ let skipped = 0;
445
+ let failed = 0;
446
+ const projectRoot = ctx.cwd || process.cwd();
447
+ const frameworkRoot = ctx.frameworkRoot;
448
+ for (const mission of queued) {
449
+ if (mission.mode === 'pty-orchestrator') {
450
+ ui.warn(`Mission ${mission.id} mode=pty-orchestrator is not yet wired to mc run; skipping. Use 'aiwg ralph' directly for PTY-orchestrator workflows.`);
451
+ skipped += 1;
452
+ continue;
453
+ }
454
+ if (!mission.completion) {
455
+ ui.warn(`Mission ${mission.id} has no --completion criteria; ralph requires one. Skipping. Re-dispatch with --completion "<criteria>" to include this mission.`);
456
+ skipped += 1;
457
+ continue;
458
+ }
459
+ try {
460
+ const result = await launchExternalRalph(frameworkRoot, projectRoot, {
461
+ objective: mission.objective,
462
+ completionCriteria: mission.completion,
463
+ maxIterations: mission.maxIterations,
464
+ maxTotalTokens: mission.maxTotalTokens,
465
+ maxOutputTokens: mission.maxOutputTokens,
466
+ maxToolCalls: mission.maxToolCalls,
467
+ maxTotalCost: mission.maxTotalCost,
468
+ maxWallClockMinutes: mission.maxWallClockMinutes,
469
+ explorationQuota: mission.explorationQuota,
470
+ budgetStopPolicy: mission.budgetStopPolicy,
471
+ // Defaults for cycle 1; advanced options can be added per-mission later.
472
+ verbose: false,
473
+ });
474
+ mission.status = 'running';
475
+ mission.startedAt = new Date().toISOString();
476
+ mission.ralphLoopId = result.loopId;
477
+ mission.ralphPid = result.pid;
478
+ await writeSession(session);
479
+ await appendLog(session.id, {
480
+ event: 'mission_started',
481
+ missionId: mission.id,
482
+ loopId: result.loopId,
483
+ pid: result.pid,
484
+ });
485
+ ui.success(`Started ${mission.id} → ralph loop ${result.loopId} (PID ${result.pid})`);
486
+ launched += 1;
487
+ }
488
+ catch (err) {
489
+ const msg = err instanceof Error ? err.message : String(err);
490
+ mission.status = 'failed';
491
+ mission.error = msg;
492
+ mission.completedAt = new Date().toISOString();
493
+ await writeSession(session);
494
+ await appendLog(session.id, {
495
+ event: 'mission_launch_failed',
496
+ missionId: mission.id,
497
+ error: msg,
498
+ });
499
+ ui.error(`Failed to start ${mission.id}: ${msg}`);
500
+ failed += 1;
501
+ }
502
+ }
503
+ ui.blank();
504
+ ui.info(`Launched: ${launched} | Skipped: ${skipped} | Failed: ${failed}`);
505
+ if (launched > 0) {
506
+ ui.info(`Monitor: \`aiwg mc status ${session.id}\` or \`aiwg mc watch ${session.id}\``);
507
+ }
508
+ return { exitCode: failed > 0 && launched === 0 ? 1 : 0 };
509
+ }
510
+ /**
511
+ * Sync mission statuses from their backing ralph loop state files (#1439).
512
+ *
513
+ * Each mission launched by `mc run` carries a `ralphLoopId`. The ralph loop
514
+ * writes its own session-state.json with a `status` field
515
+ * (running|completed|failed|paused|aborted). This helper polls those files
516
+ * and reflects the truth back into the mc session.json so `mc status` and
517
+ * `mc watch` show actual progress without manual intervention.
518
+ *
519
+ * Sync is best-effort — a missing or unreadable ralph state file leaves the
520
+ * mission status unchanged (typically 'running' until the ralph process
521
+ * actually writes state).
522
+ */
523
+ async function syncMissionsFromRalph(session, projectRoot) {
524
+ let mutated = false;
525
+ for (const mission of session.missions) {
526
+ if (mission.status !== 'running')
527
+ continue;
528
+ if (!mission.ralphLoopId)
529
+ continue;
530
+ const ralphStatePath = join(projectRoot, '.aiwg', 'ralph-external', 'loops', mission.ralphLoopId, 'session-state.json');
531
+ try {
532
+ const raw = await fs.readFile(ralphStatePath, 'utf-8');
533
+ const state = JSON.parse(raw);
534
+ const ralphStatus = String(state.status || '').toLowerCase();
535
+ // Map ralph status → mc mission status
536
+ let nextStatus = null;
537
+ if (ralphStatus === 'completed')
538
+ nextStatus = 'done';
539
+ else if (ralphStatus === 'failed' || ralphStatus === 'crashed')
540
+ nextStatus = 'failed';
541
+ else if (ralphStatus === 'aborted')
542
+ nextStatus = 'aborted';
543
+ else if (ralphStatus === 'paused')
544
+ nextStatus = 'paused';
545
+ // 'running' stays running
546
+ // Reflect iteration count if available
547
+ const iter = typeof state.iteration === 'number' ? state.iteration : (typeof state.currentIteration === 'number' ? state.currentIteration : null);
548
+ if (iter !== null && iter !== mission.loop) {
549
+ mission.loop = iter;
550
+ mutated = true;
551
+ }
552
+ // mission.status is narrowed to 'running' by the early-continue above,
553
+ // and nextStatus is one of {done, failed, aborted, paused}. So the
554
+ // transition is always real when nextStatus is set — no equality
555
+ // check needed.
556
+ if (nextStatus) {
557
+ mission.status = nextStatus;
558
+ mission.completedAt = new Date().toISOString();
559
+ if (nextStatus === 'failed' && typeof state.error === 'string') {
560
+ mission.error = state.error;
561
+ }
562
+ mutated = true;
563
+ }
564
+ }
565
+ catch {
566
+ // State file missing/unreadable — leave mission status unchanged.
567
+ }
568
+ }
569
+ if (mutated) {
570
+ session.updatedAt = new Date().toISOString();
571
+ await writeSession(session);
572
+ }
573
+ return mutated;
574
+ }
575
+ async function mcStatus(ctx) {
576
+ const positional = getPositionalArgs(ctx.args);
577
+ const sessionId = positional[0];
578
+ const json = hasFlag(ctx.args, '--json');
579
+ const session = await findActiveSession(sessionId);
580
+ if (!session) {
581
+ if (json) {
582
+ console.log(JSON.stringify({ error: 'no_active_session' }));
583
+ }
584
+ else {
585
+ ui.error(sessionId ? `Session not found: ${sessionId}` : 'No active session.');
586
+ }
587
+ return { exitCode: 1 };
588
+ }
589
+ // #1439: Sync mission statuses from ralph loop state files BEFORE display
590
+ // so 'mc status' reflects the true state of any in-flight ralph processes
591
+ // (otherwise missions launched by 'mc run' stay 'running' in mc.session.json
592
+ // even after the ralph loop has completed).
593
+ await syncMissionsFromRalph(session, ctx.cwd || process.cwd());
594
+ if (json) {
595
+ console.log(JSON.stringify(session, null, 2));
596
+ return { exitCode: 0 };
597
+ }
598
+ const statusIcons = {
599
+ done: '✓',
600
+ running: '⏳',
601
+ queued: '⏺',
602
+ failed: '✗',
603
+ aborted: '⊘',
604
+ paused: '⏸',
605
+ };
606
+ ui.blank();
607
+ console.log(` ${ui.brandMark()} ${ui.bold('MISSION CONTROL')} — ${ui.accent(session.name)} [${session.id}]`);
608
+ ui.rule(60);
609
+ // Header
610
+ // #1441: ui.dim() returns void (it prints) — wrapping in console.log emitted
611
+ // a literal "undefined" between the header and the separator. Use dimText()
612
+ // (returns a string) for inline styling.
613
+ const header = ` ${'#'.padEnd(4)} ${'Mission'.padEnd(32)} ${'Mode'.padEnd(6)} ${'Status'.padEnd(12)} ${'Loop'.padEnd(8)} ${'Started'.padEnd(8)}`;
614
+ console.log(ui.dimText(header));
615
+ ui.rule(68);
616
+ for (let i = 0; i < session.missions.length; i++) {
617
+ const m = session.missions[i];
618
+ const icon = statusIcons[m.status] || '?';
619
+ const num = String(i + 1).padEnd(4);
620
+ const obj = m.objective.length > 30 ? m.objective.slice(0, 27) + '...' : m.objective.padEnd(32);
621
+ const modeTag = m.mode === 'pty-orchestrator' ? 'PTY'.padEnd(6) : '—'.padEnd(6);
622
+ const status = `${icon} ${m.status.toUpperCase()}`.padEnd(12);
623
+ const loop = m.status === 'queued' ? '—'.padEnd(8) : `${m.loop}/${m.maxIterations}`.padEnd(8);
624
+ const started = m.startedAt ? new Date(m.startedAt).toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: false }) : '—';
625
+ console.log(` ${num} ${obj} ${modeTag} ${status} ${loop} ${started}`);
626
+ // Show last action for PTY-orchestrated missions
627
+ if (m.mode === 'pty-orchestrator' && m.lastAction && m.status === 'running') {
628
+ console.log(ui.dimText(` └─ Last: ${m.lastAction}`));
629
+ }
630
+ }
631
+ ui.rule(68);
632
+ const counts = {
633
+ done: session.missions.filter(m => m.status === 'done').length,
634
+ running: session.missions.filter(m => m.status === 'running').length,
635
+ queued: session.missions.filter(m => m.status === 'queued').length,
636
+ failed: session.missions.filter(m => m.status === 'failed').length,
637
+ };
638
+ console.log(` ${session.missions.length} missions | ${counts.done} done | ${counts.running} running | ${counts.queued} queued | ${counts.failed} failed`);
639
+ // #1361: Show project parallelism cap when one is configured.
640
+ try {
641
+ const { readAiwgConfig, resolveParallelism } = await import('../../config/aiwg-config.js');
642
+ const cfg = await readAiwgConfig(ctx.cwd || process.cwd());
643
+ if (cfg) {
644
+ const resolved = resolveParallelism(cfg.parallelism, cfg.providers[0]);
645
+ const active = counts.running + counts.queued;
646
+ const cap = resolved.max_parallel_mc_missions;
647
+ const overCap = active > cap ? ` ${ui.dim(`(${active - cap} over cap — queued)`)}` : '';
648
+ console.log(` Parallelism cap: ${active}/${cap} active${overCap}`);
649
+ }
650
+ }
651
+ catch {
652
+ // Non-fatal
653
+ }
654
+ ui.blank();
655
+ return { exitCode: 0 };
656
+ }
657
+ async function mcAbort(ctx) {
658
+ const positional = getPositionalArgs(ctx.args);
659
+ const sessionId = positional[0];
660
+ const missionId = positional[1];
661
+ if (!sessionId || !missionId) {
662
+ ui.error('Usage: aiwg mc abort <session-id> <mission-id>');
663
+ return { exitCode: 1 };
664
+ }
665
+ const session = await readSession(sessionId);
666
+ if (!session) {
667
+ ui.error(`Session not found: ${sessionId}`);
668
+ return { exitCode: 1 };
669
+ }
670
+ const mission = session.missions.find(m => m.id === missionId);
671
+ if (!mission) {
672
+ ui.error(`Mission not found: ${missionId}`);
673
+ return { exitCode: 1 };
674
+ }
675
+ mission.status = 'aborted';
676
+ mission.completedAt = new Date().toISOString();
677
+ await writeSession(session);
678
+ await appendLog(session.id, { event: 'mission_aborted', missionId });
679
+ ui.success(`Aborted mission: ${missionId}`);
680
+ return { exitCode: 0 };
681
+ }
682
+ async function mcPause(ctx) {
683
+ const positional = getPositionalArgs(ctx.args);
684
+ const sessionId = positional[0];
685
+ const session = await findActiveSession(sessionId);
686
+ if (!session) {
687
+ ui.error('No active session to pause.');
688
+ return { exitCode: 1 };
689
+ }
690
+ session.state = 'paused';
691
+ for (const m of session.missions) {
692
+ if (m.status === 'running')
693
+ m.status = 'paused';
694
+ }
695
+ await writeSession(session);
696
+ await appendLog(session.id, { event: 'session_paused' });
697
+ ui.success(`Paused session: ${session.id}`);
698
+ return { exitCode: 0 };
699
+ }
700
+ async function mcResume(ctx) {
701
+ const positional = getPositionalArgs(ctx.args);
702
+ const sessionId = positional[0];
703
+ const session = await findActiveSession(sessionId);
704
+ if (!session || session.state !== 'paused') {
705
+ ui.error('No paused session to resume.');
706
+ return { exitCode: 1 };
707
+ }
708
+ session.state = 'active';
709
+ for (const m of session.missions) {
710
+ if (m.status === 'paused')
711
+ m.status = 'running';
712
+ }
713
+ await writeSession(session);
714
+ await appendLog(session.id, { event: 'session_resumed' });
715
+ ui.success(`Resumed session: ${session.id}`);
716
+ return { exitCode: 0 };
717
+ }
718
+ async function mcStop(ctx) {
719
+ const positional = getPositionalArgs(ctx.args);
720
+ const sessionId = positional[0];
721
+ const drain = hasFlag(ctx.args, '--drain');
722
+ const session = await findActiveSession(sessionId);
723
+ if (!session) {
724
+ ui.error('No active session to stop.');
725
+ return { exitCode: 1 };
726
+ }
727
+ if (drain) {
728
+ // Mark queued missions as aborted, let running finish
729
+ for (const m of session.missions) {
730
+ if (m.status === 'queued') {
731
+ m.status = 'aborted';
732
+ m.completedAt = new Date().toISOString();
733
+ }
734
+ }
735
+ ui.info('Draining: queued missions cancelled, running missions will complete.');
736
+ }
737
+ else {
738
+ // Abort all non-completed missions
739
+ for (const m of session.missions) {
740
+ if (m.status === 'running' || m.status === 'queued' || m.status === 'paused') {
741
+ m.status = 'aborted';
742
+ m.completedAt = new Date().toISOString();
743
+ }
744
+ }
745
+ }
746
+ session.state = 'stopped';
747
+ await writeSession(session);
748
+ await appendLog(session.id, { event: 'session_stopped', drain });
749
+ ui.success(`Stopped session: ${session.id}`);
750
+ return { exitCode: 0 };
751
+ }
752
+ async function mcList(ctx) {
753
+ const json = hasFlag(ctx.args, '--json');
754
+ const sessions = await listSessions();
755
+ if (json) {
756
+ console.log(JSON.stringify(sessions.map(s => ({
757
+ id: s.id,
758
+ name: s.name,
759
+ state: s.state,
760
+ missions: s.missions.length,
761
+ created: s.createdAt,
762
+ updated: s.updatedAt,
763
+ })), null, 2));
764
+ return { exitCode: 0 };
765
+ }
766
+ if (sessions.length === 0) {
767
+ ui.info('No Mission Control sessions. Run `aiwg mc start` to create one.');
768
+ return { exitCode: 0 };
769
+ }
770
+ ui.blank();
771
+ console.log(` ${ui.brandMark()} ${ui.bold('Mission Control Sessions')}`);
772
+ ui.rule();
773
+ for (const s of sessions) {
774
+ const stateIcon = s.state === 'active' ? '●' : s.state === 'paused' ? '⏸' : '○';
775
+ const missionCount = s.missions.length;
776
+ const done = s.missions.filter(m => m.status === 'done').length;
777
+ console.log(` ${stateIcon} ${s.id} ${ui.accent(s.name)} (${done}/${missionCount} done) [${s.state}]`);
778
+ }
779
+ ui.blank();
780
+ return { exitCode: 0 };
781
+ }
782
+ async function mcWatch(ctx) {
783
+ const positional = getPositionalArgs(ctx.args);
784
+ const sessionId = positional[0];
785
+ const session = await findActiveSession(sessionId);
786
+ if (!session) {
787
+ ui.error('No active session to watch.');
788
+ return { exitCode: 1 };
789
+ }
790
+ // For non-interactive contexts, show status once with a note
791
+ // Real streaming would use fs.watch on the session file
792
+ ui.info(`Watch mode: polling session ${session.id}`);
793
+ ui.info('Press Ctrl+C to stop watching.');
794
+ ui.blank();
795
+ // Show current status
796
+ ctx.args = [session.id];
797
+ return mcStatus(ctx);
798
+ }
799
+ // ── Agent routing query ──────────────────────────────────────
800
+ /**
801
+ * aiwg mc agents [--filter key=value...] [--json]
802
+ *
803
+ * Queries GET /api/agents/candidates on the local aiwg serve instance and
804
+ * prints a table of agents that match the given routing filter. This is a thin
805
+ * CLI wrapper over the #916 routing endpoint so operators can check routing
806
+ * from a terminal without opening the dashboard.
807
+ *
808
+ * Filter flags:
809
+ * --framework <name> Require a specific AIWG framework (repeatable)
810
+ * --sandbox <id> Restrict to a specific sandbox
811
+ * --agent <id> Restrict to a specific agent ID
812
+ * --name <n> Match by logical name
813
+ * --max-cpu <pct> Reject agents above this CPU %
814
+ * --min-memory <gb> Reject agents below this memory threshold
815
+ * --json Output raw JSON
816
+ */
817
+ async function mcAgents(ctx) {
818
+ const args = ctx.args;
819
+ const json = hasFlag(args, '--json');
820
+ const port = process.env['AIWG_SERVE_PORT'] ?? '7337';
821
+ const base = `http://127.0.0.1:${port}`;
822
+ const params = new URLSearchParams();
823
+ for (let i = 0; i < args.length; i++) {
824
+ const a = args[i];
825
+ if (a === '--framework' && args[i + 1]) {
826
+ params.append('frameworks', args[++i]);
827
+ }
828
+ else if (a === '--sandbox' && args[i + 1]) {
829
+ params.set('sandbox_id', args[++i]);
830
+ }
831
+ else if (a === '--agent' && args[i + 1]) {
832
+ params.set('agent_id', args[++i]);
833
+ }
834
+ else if (a === '--name' && args[i + 1]) {
835
+ params.set('agent_name', args[++i]);
836
+ }
837
+ else if (a === '--max-cpu' && args[i + 1]) {
838
+ params.set('max_cpu_percent', args[++i]);
839
+ }
840
+ else if (a === '--min-memory' && args[i + 1]) {
841
+ params.set('min_memory_gb', args[++i]);
842
+ }
843
+ }
844
+ // 5s timeout so a wedged serve cannot hang the CLI. Override with
845
+ // AIWG_FETCH_TIMEOUT_MS for slow local environments or integration tests.
846
+ const fetchTimeoutMs = (() => {
847
+ const raw = process.env['AIWG_FETCH_TIMEOUT_MS'];
848
+ const n = raw ? parseInt(raw, 10) : NaN;
849
+ return Number.isFinite(n) && n > 0 ? n : 5_000;
850
+ })();
851
+ let result;
852
+ try {
853
+ // Combine user-cancel (Ctrl-C) with the per-call timeout so the fetch
854
+ // aborts on either. `AbortSignal.any` requires Node 20+.
855
+ const signal = ctx.signal
856
+ ? AbortSignal.any([ctx.signal, AbortSignal.timeout(fetchTimeoutMs)])
857
+ : AbortSignal.timeout(fetchTimeoutMs);
858
+ const resp = await fetch(`${base}/api/agents/candidates?${params.toString()}`, {
859
+ signal,
860
+ });
861
+ if (!resp.ok) {
862
+ ui.error(`aiwg serve returned ${resp.status} — is it running on port ${port}?`);
863
+ return { exitCode: 1 };
864
+ }
865
+ result = await resp.json();
866
+ }
867
+ catch (err) {
868
+ if (err instanceof Error && err.name === 'TimeoutError') {
869
+ ui.error(`aiwg serve on port ${port} timed out after ${fetchTimeoutMs}ms. Is it wedged?`);
870
+ }
871
+ else {
872
+ ui.error(`Cannot reach aiwg serve on port ${port}. Start it with: aiwg serve`);
873
+ }
874
+ return { exitCode: 1 };
875
+ }
876
+ if (json) {
877
+ console.log(JSON.stringify(result, null, 2));
878
+ return { exitCode: 0 };
879
+ }
880
+ ui.blank();
881
+ console.log(` ${ui.brandMark()} ${ui.bold('Agent Routing Candidates')}`);
882
+ ui.rule();
883
+ if (result.candidates.length === 0) {
884
+ ui.info('No matching agents found for the given filter.');
885
+ return { exitCode: 0 };
886
+ }
887
+ for (const c of result.candidates) {
888
+ const agent = c['agent'];
889
+ const agentId = agent?.['agentId'] ?? '?';
890
+ const logicalName = agent?.['logicalName'];
891
+ const sandboxName = c['sandboxName'] ?? '';
892
+ const cpu = agent?.['latestMetrics']?.['cpu_percent'];
893
+ const status = agent?.['status'] ?? '';
894
+ const isSelected = result.selected?.['sandboxName'] === sandboxName &&
895
+ result.selected?.['agent']?.['agentId'] === agentId;
896
+ const label = logicalName ? `${logicalName} (${agentId})` : agentId;
897
+ const cpuStr = cpu !== undefined ? ` cpu:${cpu.toFixed(0)}%` : '';
898
+ const selectedMark = isSelected ? ' ← selected' : '';
899
+ console.log(` • ${ui.bold(label)} sandbox:${sandboxName} status:${status}${cpuStr}${selectedMark}`);
900
+ const reason = c['matchReason'];
901
+ if (reason)
902
+ console.log(` reason: ${reason}`);
903
+ }
904
+ ui.blank();
905
+ return { exitCode: 0 };
906
+ }
907
+ // ── Subcommand router ────────────────────────────────────────
908
+ const subcommands = {
909
+ start: mcStart,
910
+ dispatch: mcDispatch,
911
+ run: mcRun, // #1439
912
+ status: mcStatus,
913
+ watch: mcWatch,
914
+ abort: mcAbort,
915
+ pause: mcPause,
916
+ resume: mcResume,
917
+ stop: mcStop,
918
+ list: mcList,
919
+ agents: mcAgents,
920
+ };
921
+ function showMcHelp() {
922
+ ui.blank();
923
+ console.log(` ${ui.brandMark()} ${ui.bold('Mission Control')} — multi-loop background orchestration`);
924
+ ui.rule();
925
+ console.log(`
926
+ ${ui.bold('Usage:')} aiwg mc <subcommand> [options]
927
+
928
+ ${ui.bold('Lifecycle:')}
929
+ 1. start — create a session (state-tracking only)
930
+ 2. dispatch — queue one or more missions onto a session
931
+ 3. run — drain the queue by launching each mission as a ralph loop
932
+ 4. status — view progress (auto-syncs from ralph loop state)
933
+ 5. watch — live tail of progress
934
+ 6. stop — shut down the session
935
+
936
+ ${ui.bold('Subcommands:')}
937
+ start Start a new Mission Control session
938
+ dispatch <id> "<objective>" Queue a mission on the session (does NOT execute)
939
+ [--completion "<criteria>"] [--max-iterations N]
940
+ [--max-total-tokens N] [--max-output-tokens N]
941
+ [--max-tool-calls N] [--max-total-cost USD]
942
+ [--max-wall-clock-minutes N] [--exploration-quota N]
943
+ [--mode pty-orchestrator] [--target-agent <id>]
944
+ run <id> [--accept-cost] Launch queued missions as ralph loops (#1439)
945
+ Cost gate warns/refuses above ~$5 estimate
946
+ status [<id>] [--json] View mission status dashboard
947
+ watch [<id>] Live monitor (streaming)
948
+ abort <session> <mission> Abort a specific mission
949
+ pause [<id>] Pause active session
950
+ resume [<id>] Resume paused session
951
+ stop [<id>] [--drain] Shut down session
952
+ list [--json] List all sessions
953
+ agents [--filter] [--json] Query routable agents from aiwg serve (#916)
954
+ ${ui.bold('Examples:')}
955
+ aiwg mc start --name "Sprint 4"
956
+ aiwg mc dispatch mc-abc123 "Fix auth" --completion "tests pass"
957
+ aiwg mc dispatch mc-abc123 "Fix auth" --completion "tests pass" --max-total-tokens 5000 --exploration-quota 2
958
+ aiwg mc dispatch mc-abc123 "Refactor users" --completion "npm test passes"
959
+ aiwg mc run mc-abc123 # launches queued missions
960
+ aiwg mc status mc-abc123 # syncs progress from ralph loops
961
+ aiwg mc dispatch mc-abc123 "Supervise agent-01" --mode pty-orchestrator --target-agent agent-01 --completion "migration complete"
962
+ aiwg mc stop mc-abc123 --drain
963
+ aiwg mc agents --framework sdlc-complete --max-cpu 80
964
+
965
+ ${ui.bold('A2A route for new sandbox work:')}
966
+ GET /api/v2/admin/instances
967
+ GET /agents/{instance_id}/.well-known/agent-card.json
968
+ GET /agents/{instance_id}/v1/extendedAgentCard
969
+ POST /agents/{instance_id}/v1/messages:send
970
+ `);
971
+ }
972
+ // ── Exported handler ─────────────────────────────────────────
973
+ export const mcHandler = {
974
+ id: 'mc',
975
+ name: 'Mission Control',
976
+ description: 'Multi-loop background orchestration (start, dispatch, status, watch, stop)',
977
+ category: 'orchestration',
978
+ aliases: ['mission-control'],
979
+ async execute(ctx) {
980
+ const subcmd = ctx.args[0];
981
+ if (!subcmd || subcmd === '--help' || subcmd === '-h') {
982
+ showMcHelp();
983
+ return { exitCode: 0 };
984
+ }
985
+ const handler = subcommands[subcmd];
986
+ if (!handler) {
987
+ ui.error(`Unknown subcommand: ${subcmd}. Run 'aiwg mc --help' for usage.`);
988
+ return { exitCode: 1 };
989
+ }
990
+ // #1440: '--help' / '-h' on any subcommand prints usage and exits BEFORE
991
+ // any side-effecting logic. The previous behavior — e.g. 'mc start --help'
992
+ // actually creating a session — violated the universal --help convention.
993
+ const subArgs = ctx.args.slice(1);
994
+ if (wantsHelp(subArgs)) {
995
+ printSubcommandHelp(subcmd);
996
+ return { exitCode: 0 };
997
+ }
998
+ // Pass remaining args to subcommand
999
+ const subCtx = {
1000
+ ...ctx,
1001
+ args: subArgs,
1002
+ };
1003
+ return handler(subCtx);
1004
+ },
1005
+ };
1006
+ /**
1007
+ * All MC-related handlers for bulk registration
1008
+ */
1009
+ export const mcHandlers = [mcHandler];
1010
+ //# sourceMappingURL=mc.js.map