@thebackstoryis/engineering-with-ai 0.2.9

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 (334) hide show
  1. package/Docs/README.md +50 -0
  2. package/Docs/adoption/consultancy-and-multi-project-rollout.md +135 -0
  3. package/Docs/adoption/non-technical-team-guide.md +126 -0
  4. package/Docs/archaeology-technology-and-hosting-discovery.md +212 -0
  5. package/Docs/blast-radius-and-impact-routing-guide.md +325 -0
  6. package/Docs/blueprints/internal-blueprint-catalogue.md +146 -0
  7. package/Docs/blueprints/maintaining-organisation-blueprints.md +154 -0
  8. package/Docs/blueprints/validation-and-troubleshooting.md +168 -0
  9. package/Docs/cli-reference.md +113 -0
  10. package/Docs/completed-phase-evidence-amendments.md +74 -0
  11. package/Docs/consultancy-network-rollout-control-plane-guide.md +202 -0
  12. package/Docs/context-aware-delivery-companion-guide.md +198 -0
  13. package/Docs/context-aware-delivery-companion-user-guide.md +184 -0
  14. package/Docs/context-management-and-token-efficiency.md +113 -0
  15. package/Docs/design-systems/design-system-implementation-guide.md +85 -0
  16. package/Docs/design-systems/design-system-pack-authoring-guide.md +95 -0
  17. package/Docs/design-systems/design-system-review-guide.md +51 -0
  18. package/Docs/design-systems/design-system-user-guide.md +96 -0
  19. package/Docs/design-systems/product-owner-guide.md +49 -0
  20. package/Docs/designing-organisation-blueprint-packs.md +384 -0
  21. package/Docs/developer-delivery-guide.md +224 -0
  22. package/Docs/error-reporting-guide.md +110 -0
  23. package/Docs/error-reporting-provider-guide.md +49 -0
  24. package/Docs/examples/error-report-adapter.md +70 -0
  25. package/Docs/examples/meeting-review.md +76 -0
  26. package/Docs/examples/minimal-design-system.md +67 -0
  27. package/Docs/examples/prototype-review-inputs.md +175 -0
  28. package/Docs/examples/reproducible-archaeology-depth-example.md +144 -0
  29. package/Docs/examples/test-scenario-input.md +68 -0
  30. package/Docs/examples/worked-examples.md +147 -0
  31. package/Docs/existing-project-onboarding-guide.md +214 -0
  32. package/Docs/explanation/core-concepts.md +26 -0
  33. package/Docs/explanation/delivery-workflow.md +48 -0
  34. package/Docs/governance/governance-team-guide.md +139 -0
  35. package/Docs/governed-starter-project-materialisation-guide.md +284 -0
  36. package/Docs/guide-catalogue.md +117 -0
  37. package/Docs/guided-discovery-facilitator-guide.md +172 -0
  38. package/Docs/guided-intent-workspace-guide.md +109 -0
  39. package/Docs/guided-phase-evidence-drafting-guide.md +119 -0
  40. package/Docs/human-approval-and-assurance-guide.md +146 -0
  41. package/Docs/knowledge-proposals-implementer-guide.md +106 -0
  42. package/Docs/knowledge-proposals-user-guide.md +247 -0
  43. package/Docs/maintainers/context-benchmarks.md +29 -0
  44. package/Docs/maintainers/contributing.md +58 -0
  45. package/Docs/maintainers/evidence-depth-acceptance.md +72 -0
  46. package/Docs/maintainers/verification-walkthroughs.md +104 -0
  47. package/Docs/meeting-evidence-implementer-guide.md +132 -0
  48. package/Docs/meeting-evidence-user-guide.md +200 -0
  49. package/Docs/operations/dashboard-and-delivery-state.md +153 -0
  50. package/Docs/operations/dashboard-configuration.md +87 -0
  51. package/Docs/operations/installation-updating-and-entitlements.md +135 -0
  52. package/Docs/operations/premium-personas-setup.md +76 -0
  53. package/Docs/operations/troubleshooting-and-recovery.md +205 -0
  54. package/Docs/organisation-rollout-guide.md +142 -0
  55. package/Docs/persona-entitlement-provider-guide.md +199 -0
  56. package/Docs/persona-guided-prototype-iteration.md +129 -0
  57. package/Docs/personas/organisation-specific-personas.md +103 -0
  58. package/Docs/personas/persona-authoring-cookbook.md +176 -0
  59. package/Docs/personas/persona-engagement-ui.md +133 -0
  60. package/Docs/personas/persona-governance.md +118 -0
  61. package/Docs/platform-export-analysis-guide.md +336 -0
  62. package/Docs/policies/governance-owner-guide.md +36 -0
  63. package/Docs/policies/implementation-guide.md +42 -0
  64. package/Docs/policies/organisation-policy-design-gates.md +58 -0
  65. package/Docs/policies/policy-pack-authoring-guide.md +108 -0
  66. package/Docs/policies/product-owner-guide.md +43 -0
  67. package/Docs/policies/technical-owner-guide.md +37 -0
  68. package/Docs/product-owner-guide.md +327 -0
  69. package/Docs/project-portfolio-orchestration-guide.md +199 -0
  70. package/Docs/quality/manual-qa-and-acceptance.md +162 -0
  71. package/Docs/quality/persona-driven-test-scenarios.md +172 -0
  72. package/Docs/quality/reproducible-archaeology-depth-review-checklist.md +89 -0
  73. package/Docs/reference/capabilities-and-project-layout.md +678 -0
  74. package/Docs/reference/cli-and-configuration.md +398 -0
  75. package/Docs/reference/contributions-api.md +23 -0
  76. package/Docs/reference/security-adapter-authoring.md +81 -0
  77. package/Docs/reference/starter-adapter-authoring.md +74 -0
  78. package/Docs/repository-source-map-guide.md +381 -0
  79. package/Docs/reproducible-archaeology-and-discovery-depth.md +292 -0
  80. package/Docs/screen-prototype-creation-guide.md +324 -0
  81. package/Docs/security-validation-guide.md +353 -0
  82. package/Docs/solution-readiness-review-guide.md +123 -0
  83. package/Docs/standards/project-standards-authoring.md +157 -0
  84. package/Docs/team-hub-guide.md +162 -0
  85. package/Docs/team-hub-resource-registry-guide.md +167 -0
  86. package/Docs/tutorials/first-delivery.md +83 -0
  87. package/Docs/tutorials/first-session.md +62 -0
  88. package/Docs/using-lifecycle-hooks.md +381 -0
  89. package/Docs/working-with-personas.md +274 -0
  90. package/LICENSE +165 -0
  91. package/README.md +96 -0
  92. package/agents-src/claude/ewai-security-reviewer.md +15 -0
  93. package/bin/ewai +5 -0
  94. package/config/archaeology-record-families.yaml +59 -0
  95. package/config/delivery-artifacts.yaml +121 -0
  96. package/config/delivery-stages.yaml +77 -0
  97. package/config/design-system.schema.json +46 -0
  98. package/config/error-reporting.schema.json +79 -0
  99. package/config/evidence-depth.schema.json +53 -0
  100. package/config/intent.schema.json +90 -0
  101. package/config/knowledge-proposals-proposal.schema.json +34 -0
  102. package/config/lifecycle-event.schema.json +68 -0
  103. package/config/lifecycle-handler.schema.json +45 -0
  104. package/config/lifecycle-hook-ack.schema.json +19 -0
  105. package/config/meeting-evidence-candidate.schema.json +102 -0
  106. package/config/organisation-policy.schema.json +137 -0
  107. package/config/pack.schema.json +250 -0
  108. package/config/persona-pack.schema.json +21 -0
  109. package/config/persona.schema.json +17 -0
  110. package/config/policy-evaluation.schema.json +77 -0
  111. package/config/policy-facts.schema.json +140 -0
  112. package/config/portfolio.schema.json +68 -0
  113. package/config/project.schema.json +313 -0
  114. package/config/prototype-iteration.schema.json +128 -0
  115. package/config/rollout.schema.json +87 -0
  116. package/config/security-adapter.schema.json +31 -0
  117. package/config/security-scan-request.schema.json +64 -0
  118. package/config/security-scan-response.schema.json +52 -0
  119. package/config/security-validation-policy.schema.json +74 -0
  120. package/config/starter-source-acknowledgement.schema.json +13 -0
  121. package/config/starter-source-adapter.schema.json +38 -0
  122. package/config/starter-source-request.schema.json +61 -0
  123. package/package.json +77 -0
  124. package/packs/core/pack.yaml +7 -0
  125. package/packs/design-systems/default/experience-promise.md +9 -0
  126. package/packs/design-systems/default/intentional-review.md +10 -0
  127. package/packs/design-systems/default/interaction-and-entry.md +9 -0
  128. package/packs/design-systems/default/meaningful-content-and-states.md +9 -0
  129. package/packs/design-systems/default/pack.yaml +44 -0
  130. package/packs/design-systems/default/principles.md +10 -0
  131. package/packs/personas/core/pack.yaml +7 -0
  132. package/packs/personas/core/personas/archaeologist.md +37 -0
  133. package/packs/personas/core/personas/end-user.md +17 -0
  134. package/packs/personas/core/personas/maintainer.md +17 -0
  135. package/packs/personas/core/personas/operator.md +17 -0
  136. package/packs/personas/core/personas/specs-knowledge-curator.md +35 -0
  137. package/packs/technologies/laravel/pack.yaml +30 -0
  138. package/packs/technologies/laravel-nuxt/pack.yaml +30 -0
  139. package/packs/technologies/nuxt/pack.yaml +30 -0
  140. package/packs/technologies/power-platform/pack.yaml +31 -0
  141. package/packs/technologies/salesforce/pack.yaml +25 -0
  142. package/public/app.js +4896 -0
  143. package/public/apple-touch-icon.png +0 -0
  144. package/public/assets/backstory-icon.png +0 -0
  145. package/public/dashboard-navigation.js +98 -0
  146. package/public/favicon-16.png +0 -0
  147. package/public/favicon-32.png +0 -0
  148. package/public/favicon.ico +0 -0
  149. package/public/index.html +789 -0
  150. package/public/styles.css +2693 -0
  151. package/public/team-hub/app.js +202 -0
  152. package/public/team-hub/index.html +79 -0
  153. package/public/team-hub/styles.css +90 -0
  154. package/scripts/publication-check.mjs +140 -0
  155. package/scripts/setup.mjs +21 -0
  156. package/skills-src/ewai-archaeology/SKILL.md +334 -0
  157. package/skills-src/ewai-archaeology/agents/openai.yaml +4 -0
  158. package/skills-src/ewai-archaeology/references/archaeology-contract.md +201 -0
  159. package/skills-src/ewai-archaeology/references/lifecycle-reconstruction.md +177 -0
  160. package/skills-src/ewai-archaeology/references/maximum-detail-reconstruction.md +97 -0
  161. package/skills-src/ewai-archaeology/references/model-routing.md +26 -0
  162. package/skills-src/ewai-architecture/SKILL.md +108 -0
  163. package/skills-src/ewai-architecture/agents/openai.yaml +4 -0
  164. package/skills-src/ewai-architecture/references/architecture-contract.md +176 -0
  165. package/skills-src/ewai-context/SKILL.md +68 -0
  166. package/skills-src/ewai-context/agents/openai.yaml +4 -0
  167. package/skills-src/ewai-context-import/SKILL.md +118 -0
  168. package/skills-src/ewai-context-import/agents/openai.yaml +4 -0
  169. package/skills-src/ewai-context-import/references/context-import-contract.md +106 -0
  170. package/skills-src/ewai-dashboard-configuration/SKILL.md +20 -0
  171. package/skills-src/ewai-deliver/SKILL.md +122 -0
  172. package/skills-src/ewai-deliver/references/delivery-evidence.md +92 -0
  173. package/skills-src/ewai-deliver/references/phase-routing.md +31 -0
  174. package/skills-src/ewai-design-system-apply/SKILL.md +27 -0
  175. package/skills-src/ewai-design-system-apply/agents/openai.yaml +4 -0
  176. package/skills-src/ewai-design-system-apply/references/application-contract.md +36 -0
  177. package/skills-src/ewai-design-system-author/SKILL.md +28 -0
  178. package/skills-src/ewai-design-system-author/agents/openai.yaml +4 -0
  179. package/skills-src/ewai-design-system-author/references/authoring-contract.md +38 -0
  180. package/skills-src/ewai-design-system-review/SKILL.md +26 -0
  181. package/skills-src/ewai-design-system-review/agents/openai.yaml +4 -0
  182. package/skills-src/ewai-design-system-review/references/review-contract.md +40 -0
  183. package/skills-src/ewai-error-reporting/SKILL.md +46 -0
  184. package/skills-src/ewai-error-reporting/agents/openai.yaml +4 -0
  185. package/skills-src/ewai-error-reporting/references/provider-contract.md +74 -0
  186. package/skills-src/ewai-evidence-depth/SKILL.md +72 -0
  187. package/skills-src/ewai-evidence-depth/agents/openai.yaml +4 -0
  188. package/skills-src/ewai-evidence-depth/references/evidence-depth-contract.md +127 -0
  189. package/skills-src/ewai-intent/SKILL.md +68 -0
  190. package/skills-src/ewai-intent/agents/openai.yaml +4 -0
  191. package/skills-src/ewai-intent/references/intent-contract.md +42 -0
  192. package/skills-src/ewai-knowledge-proposals/SKILL.md +105 -0
  193. package/skills-src/ewai-knowledge-proposals/agents/openai.yaml +4 -0
  194. package/skills-src/ewai-knowledge-proposals/references/proposal-contract.md +59 -0
  195. package/skills-src/ewai-meeting-evidence/SKILL.md +106 -0
  196. package/skills-src/ewai-meeting-evidence/agents/openai.yaml +4 -0
  197. package/skills-src/ewai-meeting-evidence/references/candidate-contract.md +64 -0
  198. package/skills-src/ewai-organisation-policy/SKILL.md +62 -0
  199. package/skills-src/ewai-organisation-policy/agents/openai.yaml +4 -0
  200. package/skills-src/ewai-organisation-policy/references/policy-contract.md +94 -0
  201. package/skills-src/ewai-palace-housekeeping/SKILL.md +55 -0
  202. package/skills-src/ewai-palace-housekeeping/agents/openai.yaml +4 -0
  203. package/skills-src/ewai-persona-entitlement/SKILL.md +60 -0
  204. package/skills-src/ewai-persona-entitlement/agents/openai.yaml +4 -0
  205. package/skills-src/ewai-phase-evidence/SKILL.md +79 -0
  206. package/skills-src/ewai-phase-evidence/agents/openai.yaml +4 -0
  207. package/skills-src/ewai-pipeline/SKILL.md +130 -0
  208. package/skills-src/ewai-pipeline/agents/openai.yaml +4 -0
  209. package/skills-src/ewai-pipeline/references/cli.md +86 -0
  210. package/skills-src/ewai-pipeline/references/specs-contract.md +16 -0
  211. package/skills-src/ewai-portfolio/SKILL.md +70 -0
  212. package/skills-src/ewai-portfolio/agents/openai.yaml +4 -0
  213. package/skills-src/ewai-portfolio/references/portfolio-contract.md +67 -0
  214. package/skills-src/ewai-project-discovery/SKILL.md +95 -0
  215. package/skills-src/ewai-project-discovery/agents/openai.yaml +4 -0
  216. package/skills-src/ewai-project-discovery/references/discovery-contract.md +34 -0
  217. package/skills-src/ewai-prototype-iteration/SKILL.md +30 -0
  218. package/skills-src/ewai-prototype-iteration/agents/openai.yaml +4 -0
  219. package/skills-src/ewai-prototype-iteration/references/review-contract.md +49 -0
  220. package/skills-src/ewai-retro/SKILL.md +48 -0
  221. package/skills-src/ewai-retro/agents/openai.yaml +4 -0
  222. package/skills-src/ewai-retro/references/asset-routing.md +14 -0
  223. package/skills-src/ewai-rollout/SKILL.md +74 -0
  224. package/skills-src/ewai-rollout/agents/openai.yaml +4 -0
  225. package/skills-src/ewai-rollout/references/rollout-contract.md +74 -0
  226. package/skills-src/ewai-shape-intents/SKILL.md +84 -0
  227. package/skills-src/ewai-shape-intents/agents/openai.yaml +4 -0
  228. package/skills-src/ewai-shape-intents/references/intent-mapping-contract.md +109 -0
  229. package/skills-src/ewai-solution-readiness/SKILL.md +55 -0
  230. package/skills-src/ewai-solution-readiness/agents/openai.yaml +4 -0
  231. package/skills-src/ewai-standards-check/SKILL.md +93 -0
  232. package/skills-src/ewai-standards-check/agents/openai.yaml +4 -0
  233. package/skills-src/ewai-standards-check/references/report-contract.md +116 -0
  234. package/skills-src/ewai-test-scenarios/SKILL.md +94 -0
  235. package/skills-src/ewai-test-scenarios/agents/openai.yaml +4 -0
  236. package/skills-src/ewai-test-scenarios/references/scenario-contract.md +88 -0
  237. package/src/afk-worker.mjs +16 -0
  238. package/src/archaeology.mjs +1333 -0
  239. package/src/checkin.mjs +261 -0
  240. package/src/cli.mjs +2427 -0
  241. package/src/companion-guidance.mjs +257 -0
  242. package/src/companion-opening.mjs +62 -0
  243. package/src/companion.mjs +256 -0
  244. package/src/context.mjs +210 -0
  245. package/src/dashboard-preferences.mjs +80 -0
  246. package/src/delivery-artifacts.mjs +204 -0
  247. package/src/delivery-documents.mjs +248 -0
  248. package/src/delivery-gates.mjs +317 -0
  249. package/src/delivery.mjs +1433 -0
  250. package/src/design-system-application.mjs +291 -0
  251. package/src/design-system-authoring.mjs +101 -0
  252. package/src/design-systems.mjs +466 -0
  253. package/src/discovery.mjs +1314 -0
  254. package/src/error-reporting.mjs +323 -0
  255. package/src/evidence-depth.mjs +543 -0
  256. package/src/execution-state.mjs +243 -0
  257. package/src/install.mjs +166 -0
  258. package/src/intent-dependencies.mjs +117 -0
  259. package/src/intent-maps.mjs +402 -0
  260. package/src/intents.mjs +747 -0
  261. package/src/knowledge-proposals.mjs +717 -0
  262. package/src/launcher.mjs +51 -0
  263. package/src/meeting-evidence.mjs +703 -0
  264. package/src/network-rollout.mjs +386 -0
  265. package/src/organisation-blueprints.mjs +438 -0
  266. package/src/organisation-policies.mjs +448 -0
  267. package/src/packs.mjs +44 -0
  268. package/src/paths.mjs +62 -0
  269. package/src/persona-entitlements.mjs +438 -0
  270. package/src/persona-licence-config.mjs +98 -0
  271. package/src/persona-website-provider.mjs +134 -0
  272. package/src/persona-zip.mjs +87 -0
  273. package/src/personas.mjs +159 -0
  274. package/src/platform-metadata-analysis.mjs +314 -0
  275. package/src/policy-design-gates.mjs +623 -0
  276. package/src/policy-gate-integration.mjs +318 -0
  277. package/src/portfolio.mjs +509 -0
  278. package/src/power-platform-source-map.mjs +190 -0
  279. package/src/project.mjs +449 -0
  280. package/src/prototype-iterations.mjs +730 -0
  281. package/src/repository-source-map.mjs +603 -0
  282. package/src/runtime/afk-conductor.mjs +973 -0
  283. package/src/runtime/context-assembly.mjs +457 -0
  284. package/src/runtime/context-benchmarks.mjs +115 -0
  285. package/src/runtime/dashboard-actions.mjs +109 -0
  286. package/src/runtime/dashboard-handoffs.mjs +149 -0
  287. package/src/runtime/dashboard-server.mjs +1272 -0
  288. package/src/runtime/dashboard.mjs +197 -0
  289. package/src/runtime/database.mjs +789 -0
  290. package/src/runtime/error-reporting.mjs +581 -0
  291. package/src/runtime/evidence-depth-workspace.mjs +412 -0
  292. package/src/runtime/execution-leases.mjs +299 -0
  293. package/src/runtime/guided-discovery.mjs +350 -0
  294. package/src/runtime/guided-intents.mjs +517 -0
  295. package/src/runtime/impact-analysis.mjs +535 -0
  296. package/src/runtime/intents.mjs +222 -0
  297. package/src/runtime/knowledge.mjs +86 -0
  298. package/src/runtime/lifecycle-hooks.mjs +1239 -0
  299. package/src/runtime/mcp-config.mjs +110 -0
  300. package/src/runtime/mcp-server.mjs +1885 -0
  301. package/src/runtime/palace.mjs +362 -0
  302. package/src/runtime/paths.mjs +58 -0
  303. package/src/runtime/persona-engagement.mjs +255 -0
  304. package/src/runtime/phase-contributions.mjs +594 -0
  305. package/src/runtime/policy-workspace.mjs +170 -0
  306. package/src/runtime/prototype-iterations.mjs +235 -0
  307. package/src/runtime/provider-adapters.mjs +163 -0
  308. package/src/runtime/repository-index.mjs +838 -0
  309. package/src/runtime/runs.mjs +185 -0
  310. package/src/runtime/security-validation.mjs +1230 -0
  311. package/src/runtime/starter-materialisation.mjs +1155 -0
  312. package/src/runtime/team-hub-client.mjs +479 -0
  313. package/src/runtime/team-hub-database.mjs +288 -0
  314. package/src/runtime/team-hub-server.mjs +191 -0
  315. package/src/runtime/team-hub.mjs +110 -0
  316. package/src/runtime/tree-sitter-index.mjs +390 -0
  317. package/src/runtime/version.mjs +1 -0
  318. package/src/runtime/work.mjs +633 -0
  319. package/src/salesforce-source-map.mjs +212 -0
  320. package/src/security-validation-config.mjs +224 -0
  321. package/src/solution-readiness.mjs +620 -0
  322. package/src/starter-materialisation-contract.mjs +407 -0
  323. package/src/task-graph.mjs +544 -0
  324. package/src/team-hub-resources.mjs +239 -0
  325. package/src/team-hub.mjs +242 -0
  326. package/src/test-scenarios.mjs +622 -0
  327. package/src/validation-config.mjs +289 -0
  328. package/templates/SPECS/1.Scope/personas/registry.yaml +12 -0
  329. package/templates/SPECS/5.Strategy/patterns/context-packet.md +119 -0
  330. package/templates/SPECS/6.Build/_tracker-template.md +16 -0
  331. package/templates/SPECS/pipeline.yaml +62 -0
  332. package/templates/discovery-answers.yaml +86 -0
  333. package/templates/intent-body.md +21 -0
  334. package/tests/fixtures/context-benchmarks.json +9 -0
package/src/cli.mjs ADDED
@@ -0,0 +1,2427 @@
1
+ import { dirname, isAbsolute, relative, resolve } from 'node:path';
2
+ import { fileURLToPath } from 'node:url';
3
+ import { readFileSync, realpathSync } from 'node:fs';
4
+ import YAML from 'yaml';
5
+ import {DASHBOARD_VIEWS,readDashboardPreferences,saveDashboardPreferences} from './dashboard-preferences.mjs';
6
+ import { createInterface } from 'node:readline/promises';
7
+ import { Writable } from 'node:stream';
8
+ import { configureAndInstallPremiumPersonas } from './checkin.mjs';
9
+ import {
10
+ configureValidationCheckpoint,
11
+ configureExternalValidation,
12
+ doctorProject,
13
+ externalValidationStatus,
14
+ initProject,
15
+ loadProjectConfig,
16
+ validationStatus,
17
+ } from './project.mjs';
18
+ import { install } from './install.mjs';
19
+ import { auditIntentStateCopies, createIntent } from './intents.mjs';
20
+ import { createIntentMap, loadIntentMapRequest } from './intent-maps.mjs';
21
+ import { discoverProject, loadDiscoveryAnswers, runDiscoveryInterview } from './discovery.mjs';
22
+ import { listPacks } from './packs.mjs';
23
+ import {
24
+ designSystemStatus,
25
+ listDesignSystems,
26
+ projectDesignSystem,
27
+ projectResolvedDesignSystem,
28
+ resolveDesignSystem,
29
+ selectDesignSystem,
30
+ } from './design-systems.mjs';
31
+ import { installDesignSystemCandidate, validateDesignSystemCandidate } from './design-system-authoring.mjs';
32
+ import { applyDesignSystem } from './design-system-application.mjs';
33
+ import {
34
+ createPersona,
35
+ indexPersonas,
36
+ listPersonas,
37
+ personaLibraryRoot,
38
+ personalPersonaRoot,
39
+ projectPersonaRoot
40
+ } from './personas.mjs';
41
+ import { resolveProjectRoot } from './paths.mjs';
42
+ import {
43
+ checkinProject,
44
+ premiumPersonaEntitlementStatus,
45
+ premiumPersonaRoot,
46
+ syncPremiumPersonas,
47
+ } from './checkin.mjs';
48
+ import {
49
+ preparePersonaTestScenarioBrief,
50
+ readPersonaTestScenarioWorkspace,
51
+ recordPersonaTestScenarios
52
+ } from './test-scenarios.mjs';
53
+ import { dashboardStatus, ensureDashboard, stopDashboard } from './runtime/dashboard.mjs';
54
+ import { ASSURANCE_NOTICE } from './security-validation-config.mjs';
55
+ import { registerContextSource } from './context.mjs';
56
+ import { prepareProjectContext } from './runtime/context-assembly.mjs';
57
+ import { runContextBenchmark } from './runtime/context-benchmarks.mjs';
58
+ import {
59
+ MEETING_EVIDENCE_DISCLAIMER,
60
+ prepareMeetingExtraction,
61
+ promoteMeetingEvidence,
62
+ readMeetingEvidenceWorkspace,
63
+ recordMeetingReview,
64
+ registerMeetingSource,
65
+ } from './meeting-evidence.mjs';
66
+ import {
67
+ KNOWLEDGE_PROPOSALS_DISCLAIMER,
68
+ listKnowledgeSources,
69
+ materialiseKnowledgeProposals,
70
+ prepareKnowledgeProposals,
71
+ readKnowledgeProposalWorkspace,
72
+ recordKnowledgeProposalBundle,
73
+ recordKnowledgeProposalReview,
74
+ recoverKnowledgeMaterialisation,
75
+ } from './knowledge-proposals.mjs';
76
+ import {
77
+ curateArchaeologyBundle,
78
+ prepareArchaeologyPersonaGate,
79
+ prepareArchaeologyReview,
80
+ prepareArchaeologyTechnologyHosting,
81
+ readArchaeologyTechnologyHosting,
82
+ recordArchaeologyTechnologyHosting,
83
+ validateArchaeologyBundle,
84
+ validateArchaeologyCompletion,
85
+ validateArchaeologyPersonaGate
86
+ } from './archaeology.mjs';
87
+ import {
88
+ compareEvidenceDepthWorkspaceRuns,
89
+ prepareEvidenceDepthWorkspace,
90
+ readEvidenceDepthWorkspace,
91
+ recordEvidenceDepthWorkspaceRun,
92
+ } from './runtime/evidence-depth-workspace.mjs';
93
+ import {
94
+ comparePrototypeWorkspaceReviews,
95
+ preparePrototypeCycleWorkspace,
96
+ preparePrototypePlanWorkspace,
97
+ readPrototypeIterationWorkspace,
98
+ recordPrototypeCycleWorkspaceReview,
99
+ recordPrototypePlanWorkspaceReview,
100
+ } from './runtime/prototype-iterations.mjs';
101
+ import {
102
+ beginDelivery,
103
+ completeDeliveryPhase,
104
+ continueDelivery,
105
+ phaseGateTemplate,
106
+ previewCompletedEvidenceAmendment,
107
+ ratifyDeliveryAmendments,
108
+ ratifyCompletedEvidenceAmendment,
109
+ readDeliveryState,
110
+ recordBuildApproval,
111
+ recordExternalValidationCycle,
112
+ recordManualQaApproval,
113
+ recordPhaseGate,
114
+ resumeShelvedDelivery,
115
+ startDeliveryPhase
116
+ } from './delivery.mjs';
117
+ import {
118
+ refreshRepositoryIndex,
119
+ repositoryGraph,
120
+ repositoryIndexFreshness,
121
+ repositoryIndexStatus,
122
+ repositorySourceMapCoverage,
123
+ repositorySourceMapFiles,
124
+ repositorySourceMapProfiles,
125
+ repositoryStandards,
126
+ repositoryStandardsCoverage,
127
+ repositoryTruth,
128
+ searchRepositoryIndex,
129
+ similarRepositoryCapabilities
130
+ } from './runtime/repository-index.mjs';
131
+ import { listCommandRuns, markStaleCommandRuns } from './runtime/runs.mjs';
132
+ import {
133
+ palaceHousekeeping,
134
+ palaceIndexStatus,
135
+ palaceTidiness,
136
+ refreshPalaceIndex,
137
+ searchPalace
138
+ } from './runtime/palace.mjs';
139
+ import {
140
+ afkRunStatus,
141
+ cancelAfkRun,
142
+ pauseAfkRun,
143
+ preflightAfkRun,
144
+ resumeAfkRun,
145
+ startAfkRun,
146
+ } from './runtime/afk-conductor.mjs';
147
+ import {
148
+ LIFECYCLE_EVENT_CATALOGUE,
149
+ disableLifecycleSubscription,
150
+ readLifecycleHookWorkspace,
151
+ reconcileCanonicalLifecycleEvents,
152
+ registerLifecycleHandler,
153
+ retryLifecycleDelivery,
154
+ subscribeLifecycleHandler,
155
+ validateLifecycleHandlerPackage,
156
+ } from './runtime/lifecycle-hooks.mjs';
157
+ import {
158
+ cancelSecurityRun,
159
+ discoverSecurityProviders,
160
+ executeSecurityRun,
161
+ importSecurityArtifacts,
162
+ listSecurityAdapters,
163
+ listSecurityDispositions,
164
+ listSecurityFindings,
165
+ listSecurityRuns,
166
+ prepareSecurityArtifactImport,
167
+ prepareSecurityRun,
168
+ readSecurityWorkspace,
169
+ recordSecurityDisposition,
170
+ registerSecurityAdapter,
171
+ resolveSecurityRevision,
172
+ validateSecurityAdapter,
173
+ } from './runtime/security-validation.mjs';
174
+ import {
175
+ canonicalStarterTree,
176
+ starterPackDigest,
177
+ starterTreeDigest,
178
+ validateStarterAdapterPackage,
179
+ } from './starter-materialisation-contract.mjs';
180
+ import {
181
+ applyStarterMaterialisation,
182
+ listStarterAdapters,
183
+ prepareStarterMaterialisation,
184
+ readStarterMaterialisationWorkspace,
185
+ recoverStarterMaterialisation,
186
+ registerStarterAdapter,
187
+ selectStarterPersonas,
188
+ } from './runtime/starter-materialisation.mjs';
189
+ import { readPortfolioWorkspace } from './portfolio.mjs';
190
+ import {
191
+ ROLLOUT_ADVISORY_NOTICE,
192
+ ROLLOUT_ASSURANCE_NOTICE,
193
+ readRolloutWorkspace,
194
+ } from './network-rollout.mjs';
195
+ import { readCompanionGuidance } from './companion-guidance.mjs';
196
+ import {
197
+ READINESS_AUTHORITY_NOTICE,
198
+ listSolutionReadinessProfiles,
199
+ prepareSolutionReadinessReview,
200
+ readSolutionReadinessStatus,
201
+ recordSolutionReadinessReview,
202
+ } from './solution-readiness.mjs';
203
+ import {
204
+ POLICY_DESIGN_AUTHORITY_NOTICE,
205
+ confirmPolicyFactsAction,
206
+ evaluatePolicyDesignAction,
207
+ readPolicyWorkspace,
208
+ recordPolicyExceptionAction,
209
+ recordPolicyReviewAction,
210
+ } from './runtime/policy-workspace.mjs';
211
+ import {
212
+ archiveErrorReport,
213
+ captureErrorReportFailure,
214
+ createLocalErrorReport,
215
+ deleteErrorReport,
216
+ finaliseLocalErrorReport,
217
+ listErrorReportProviders,
218
+ listErrorReportReceipts,
219
+ listLocalErrorReports,
220
+ prepareErrorReportEmail,
221
+ readLocalErrorReport,
222
+ registerErrorReportProvider,
223
+ submitErrorReportProvider,
224
+ updateErrorReportSettings,
225
+ updateLocalErrorReport,
226
+ validateErrorReportProvider,
227
+ } from './runtime/error-reporting.mjs';
228
+ import { inspectTeamHubDisclosure } from './team-hub.mjs';
229
+ import {
230
+ connectTeamHub,
231
+ disconnectTeamHub,
232
+ inspectConnectedTeamHubResource,
233
+ installConnectedTeamHubResource,
234
+ listConnectedTeamHubResources,
235
+ listTeamHubResourceReceipts,
236
+ readTeamHubWorkspace,
237
+ syncTeamHub,
238
+ } from './runtime/team-hub-client.mjs';
239
+ import { ensureTeamHub, stopTeamHub, teamHubStatus } from './runtime/team-hub.mjs';
240
+ import { buildTeamHubResourcePackage } from './team-hub-resources.mjs';
241
+
242
+ const moduleDir = dirname(fileURLToPath(import.meta.url));
243
+ const installRoot = resolve(moduleDir, '..');
244
+
245
+ function option(args, name, fallback = '') {
246
+ const index = args.indexOf(name);
247
+ return index === -1 ? fallback : args[index + 1] ?? fallback;
248
+ }
249
+
250
+ function has(args, name) {
251
+ return args.includes(name);
252
+ }
253
+
254
+ function options(args, name) {
255
+ const values = [];
256
+ for (let index = 0; index < args.length; index += 1) {
257
+ if (args[index] === name && args[index + 1] !== undefined) values.push(args[index + 1]);
258
+ }
259
+ return values;
260
+ }
261
+
262
+ function automaticErrorCommand(args) {
263
+ const safe = ['ewai'];
264
+ for (const value of args.slice(0, 2)) {
265
+ if (String(value).startsWith('--') || !/^[a-z0-9][a-z0-9:-]*$/.test(String(value))) break;
266
+ safe.push(String(value));
267
+ }
268
+ return safe.join(' ');
269
+ }
270
+
271
+ function print(value, json = false) {
272
+ if (json) {
273
+ console.log(JSON.stringify(value, null, 2));
274
+ return;
275
+ }
276
+ if (Array.isArray(value)) {
277
+ for (const item of value) console.log(typeof item === 'string' ? item : JSON.stringify(item));
278
+ return;
279
+ }
280
+ console.log(typeof value === 'string' ? value : JSON.stringify(value, null, 2));
281
+ }
282
+
283
+ function printSecurity(value, json = false) {
284
+ if (json) return print(value, true);
285
+ const safe = value && typeof value === 'object' ? { ...value } : { result: value };
286
+ delete safe.assurance_notice;
287
+ print(`${ASSURANCE_NOTICE}\n\n${JSON.stringify(safe, null, 2)}`);
288
+ }
289
+
290
+ function printReadiness(value, json = false) {
291
+ if (json) return print(value, true);
292
+ const safe = value && typeof value === 'object' ? { ...value } : { result: value };
293
+ delete safe.notices;
294
+ print(`${ASSURANCE_NOTICE}\n${READINESS_AUTHORITY_NOTICE}\n\n${JSON.stringify(safe, null, 2)}`);
295
+ }
296
+
297
+ function printPolicy(value, json = false) {
298
+ if (json) return print(value, true);
299
+ print(`${POLICY_DESIGN_AUTHORITY_NOTICE}\n\n${JSON.stringify(value, null, 2)}`);
300
+ }
301
+
302
+ function readProjectJsonInput(projectRoot, inputPath, label) {
303
+ if (!inputPath) throw new Error(`${label} requires --input FILE`);
304
+ const candidate = resolve(projectRoot, inputPath);
305
+ let safePath;
306
+ try { safePath = realpathSync(candidate); }
307
+ catch { throw new Error(`Cannot read ${label} input file`); }
308
+ const projectRelative = relative(projectRoot, safePath);
309
+ if (!projectRelative || projectRelative.startsWith('..') || isAbsolute(projectRelative)) {
310
+ throw new Error(`${label} --input must be a project-relative file`);
311
+ }
312
+ try { return JSON.parse(readFileSync(safePath, 'utf8')); }
313
+ catch { throw new Error(`Cannot parse ${label} input JSON`); }
314
+ }
315
+
316
+ function formatInitResult(result) {
317
+ const lines = [
318
+ `EWAI initialized for ${result.projectRoot}`,
319
+ `SPECS: ${result.specsRelative}${result.specsRelative === 'SPECS' ? ' (project-local)' : ' (dedicated location)'}`,
320
+ `Created ${result.created.length} file(s); preserved ${result.skipped.length} existing file(s).`
321
+ ];
322
+
323
+ if (result.specsRepositoryInitialized) {
324
+ lines.push(`Knowledge repository initialized: ${result.specsRepositoryInitialized}`);
325
+ }
326
+
327
+ if (result.onboarding.existingCodebase) {
328
+ lines.push('', 'Existing codebase detected:');
329
+ for (const path of result.onboarding.evidence.slice(0, 8)) lines.push(` • ${path}`);
330
+ if (result.onboarding.evidence.length > 8) {
331
+ lines.push(` • …and ${result.onboarding.evidence.length - 8} more signal(s)`);
332
+ }
333
+ lines.push(
334
+ '',
335
+ 'Recommended next step: establish the human project briefing before Archaeology.',
336
+ 'Ask your agent:',
337
+ ` ${result.onboarding.prompt}`,
338
+ '',
339
+ 'After the briefing is confirmed:',
340
+ ` ${result.onboarding.afterBrief}`
341
+ );
342
+ } else {
343
+ lines.push('', 'No existing codebase was detected.', `Next: ${result.onboarding.command}`);
344
+ }
345
+
346
+ return lines.join('\n');
347
+ }
348
+
349
+ export function formatCheckin(result, options = {}) {
350
+ if (options.verbose) return formatCheckinDetails(result);
351
+ const { premium, framework } = result;
352
+ const dashboard = result.runtime?.dashboard;
353
+ const selected = [...new Set(Object.values(result.validation?.checkpoints ?? {})
354
+ .flatMap(checkpoint => checkpoint.validators ?? []))];
355
+ const consistent = result.intentState?.status === 'consistent'
356
+ && result.operationalState?.status === 'consistent';
357
+ const version = framework.current ? ` ${framework.current}` : '';
358
+ const frameworkText = framework.status === 'current' ? 'up to date'
359
+ : framework.status === 'update-available' ? `update ${framework.latest} available`
360
+ : framework.status === 'not-checked' ? 'update check not run'
361
+ : `update check unavailable${framework.reason ? `: ${framework.reason}` : ''}`;
362
+ const personaText = premium.accessReason === 'licence-not-configured' ? 'Core personas available · Premium licence not configured'
363
+ : premium.installed && premium.verified ? `Premium personas installed${premium.version ? ` · ${premium.version}` : ''}${premium.message ? ` · ${premium.message}` : ''}`
364
+ : premium.access === 'available' ? 'Core personas available · Premium access verified; pack not ready'
365
+ : `Core personas available · Premium access ${premium.access === 'unavailable' ? 'unavailable' : 'unverified'}${premium.message ? `: ${premium.message}` : ''}`;
366
+ const personaActions = {
367
+ 'offer-update': `Persona update ${premium.latestVersion ?? 'release'} available; ask before downloading`,
368
+ 'blocked-local-changes': 'Local changes in the managed pack; resolve them before replacement',
369
+ 'offer-replace': 'Pack belongs to another licence or provider; explicit replacement consent required',
370
+ 'offer-repair': 'Pack needs repair; explicit download consent required',
371
+ 'offer-install': 'Installation available; explicit download consent required'
372
+ };
373
+ const personaAction = Object.hasOwn(personaActions, premium.action?.kind) ? personaActions[premium.action.kind] : null;
374
+ const lines = [
375
+ `EWAI · ${result.project.name}${dashboard?.status === 'running' ? ` · Dashboard: ${dashboard.url}` : ' · Dashboard not started'}`,
376
+ `EWAI${version} · ${frameworkText}`,
377
+ `${personaText}${personaAction ? ` · ${personaAction}` : ''}`,
378
+ `${consistent ? 'State consistent' : 'State needs reconciliation; pause delivery'} · Standards required · Independent reviewers: ${selected.join(', ') || 'none configured'}`
379
+ ];
380
+ if (result.intentDependencies?.errors?.length) lines.push('Dependency blockers found; resolve them before delivery.');
381
+ for (const host of result.validation?.hosts ?? []) {
382
+ const configured = result.validation.providers?.[host.host];
383
+ if (configured?.state === 'available' && configured.enabled && !host.available)
384
+ lines.push(`Validation warning: ${host.label} is enabled but unavailable in this terminal.`);
385
+ }
386
+ if (result.palace?.status !== 'tidy' && result.palace?.findings?.length)
387
+ lines.push(`Mind Palace: ${result.palace.findings.length} item(s) could use housekeeping.`);
388
+ if (result.dashboardHandoffError) lines.push('Dashboard handoff could not be read; check its status before continuing.');
389
+ return lines.join('\n');
390
+ }
391
+
392
+ function formatCheckinDetails(result) {
393
+ const premium = result.premium;
394
+ const lines = [`EWAI check-in · ${result.project.name}`];
395
+ if (premium.access === 'available') {
396
+ lines.push(`Premium personas: access available · ${premium.status}`);
397
+ if (premium.action) lines.push(premium.action.prompt);
398
+ } else if (premium.access === 'unavailable') {
399
+ lines.push('Premium personas: access not available', `Get premium access: ${premium.upgradeUrl}`);
400
+ } else {
401
+ lines.push('Premium personas: access could not be verified', premium.message || 'Check your connection and try again.');
402
+ }
403
+ const version = result.framework.current ? ` ${result.framework.current}` : '';
404
+ const latest = result.framework.latest ? ` ${result.framework.latest}` : '';
405
+ const frameworkSource = result.framework.source ? ` via ${result.framework.source}` : '';
406
+ if (result.framework.status === 'update-available') {
407
+ lines.push(`EWAI Pipeline:${version} · update${latest} available${frameworkSource}`);
408
+ if (result.framework.action?.prompt) lines.push(result.framework.action.prompt);
409
+ } else if (result.framework.status === 'current') lines.push(`EWAI Pipeline:${version} · current${frameworkSource}`);
410
+ else if (result.framework.status !== 'not-checked') lines.push(`EWAI Pipeline:${version} · update status unknown${result.framework.reason ? ` · ${result.framework.reason}` : ''}`);
411
+ if (result.runtime?.dashboard?.status === 'running') {
412
+ const action = result.runtime.dashboard.started ? 'started' : 'ready';
413
+ lines.push(`Pipeline dashboard: ${action} · ${result.runtime.dashboard.url}`);
414
+ }
415
+ if (result.intentState?.status === 'drift-detected') {
416
+ lines.push(`Intent state: drift detected in ${result.intentState.drift.length} intent(s); delivery must pause for reconciliation.`);
417
+ } else if (result.intentState && result.operationalState?.status === 'consistent') {
418
+ lines.push(`Intent state: ${result.intentState.checked} checked · Markdown, intent JSON, delivery JSON, and SQLite consistent`);
419
+ } else if (result.operationalState) {
420
+ lines.push(`Intent state: SQLite projection ${result.operationalState.status}`);
421
+ }
422
+ if (result.validation) {
423
+ const selected = [...new Set(
424
+ Object.values(result.validation.checkpoints ?? {})
425
+ .flatMap((checkpoint) => checkpoint.validators ?? []),
426
+ )];
427
+ lines.push(
428
+ `Validation: standards mandatory · orchestrator ${result.validation.orchestrator} · independent reviewers ${
429
+ selected.join(', ') || 'none configured'
430
+ }`,
431
+ );
432
+ const mismatches = (result.validation.hosts ?? []).filter((host) => {
433
+ const configured = result.validation.providers?.[host.host];
434
+ return configured?.state === 'available' && configured.enabled && !host.available;
435
+ });
436
+ for (const mismatch of mismatches) {
437
+ lines.push(`Validation warning: ${mismatch.label} is enabled in project config but unavailable in this terminal.`);
438
+ }
439
+ }
440
+ if (result.palace?.status === 'tidy') {
441
+ lines.push(`Mind Palace: tidy · ${result.palace.documents} artefacts indexed`);
442
+ } else if (result.palace) {
443
+ lines.push(`Mind Palace: ${result.palace.findings.length} item(s) could use housekeeping`);
444
+ }
445
+ return lines.join('\n');
446
+ }
447
+
448
+ function formatPalaceSearch(result) {
449
+ if (!result.results.length) return `EWAI Mind Palace · no results for “${result.query}”`;
450
+ return [
451
+ `EWAI Mind Palace · ${result.results.length} result${result.results.length === 1 ? '' : 's'} for “${result.query}”`,
452
+ '',
453
+ ...result.results.flatMap((item) => [
454
+ `${item.path}:${item.line} · ${item.headingPath || item.heading}`,
455
+ ` ${String(item.snippet).replaceAll('⟦', '').replaceAll('⟧', '')}`
456
+ ])
457
+ ].join('\n');
458
+ }
459
+
460
+ function formatPalaceTidiness(result) {
461
+ const lines = [
462
+ 'EWAI Mind Palace · Tidiness',
463
+ '',
464
+ `${result.documents} artefact${result.documents === 1 ? '' : 's'} · ${result.sections} sections · ${result.links} links`
465
+ ];
466
+ if (result.status === 'tidy') {
467
+ lines.push('✓ Everything is in good order. No housekeeping is needed.');
468
+ return lines.join('\n');
469
+ }
470
+ const grouped = new Map();
471
+ for (const item of result.findings) grouped.set(item.code, (grouped.get(item.code) ?? 0) + 1);
472
+ lines.push('');
473
+ for (const [code, count] of grouped) lines.push(`⚠ ${count} ${code.replaceAll('-', ' ')}`);
474
+ lines.push('', 'A little housekeeping is recommended.', 'Next: ask your agent to use $ewai-palace-housekeeping.');
475
+ return lines.join('\n');
476
+ }
477
+
478
+ function formatPalaceHousekeeping(result) {
479
+ if (result.status === 'tidy') return `EWAI Mind Palace · Housekeeping\n\n${result.guidance}`;
480
+ return [
481
+ 'EWAI Mind Palace · Housekeeping',
482
+ '',
483
+ ...result.groups.map((group) => `• ${group.count} ${group.code.replaceAll('-', ' ')}`),
484
+ '',
485
+ 'No SPECS files were changed.',
486
+ result.guidance
487
+ ].join('\n');
488
+ }
489
+
490
+ function usage() {
491
+ return `EWAI Pipeline
492
+
493
+ Commands:
494
+ ewai install [--project PATH] [--host auto|codex|claude|antigravity] [--mode copy|link]
495
+ ewai init [--project PATH] [--name NAME] [--specs PATH] [--init-specs-repo] [--claude] [--codex] [--antigravity] [--force]
496
+ ewai checkin [--project PATH]
497
+ ewai dashboard [--project PATH]
498
+ ewai dashboard preferences [--project PATH] [--json]
499
+ ewai dashboard configure --enable VIEW | --disable VIEW [--collapse | --expand] --expected-digest DIGEST --yes [--project PATH] [--json]
500
+ ewai server start|status|stop [--project PATH]
501
+ ewai mcp [--project PATH]
502
+ ewai companion status [--focus TEXT] [--project PATH]
503
+ ewai portfolio validate [--project PATH]
504
+ ewai portfolio status [--focus TEXT] [--project PATH]
505
+ ewai team status|disclosure|connect|sync|disconnect|resources [ENDPOINT] [--project PATH]
506
+ ewai team resource inspect ID VERSION [--project PATH]
507
+ ewai team resource install ID VERSION --expected-digest DIGEST --approved-by NAME --yes [--project PATH]
508
+ ewai team resource receipts [--project PATH]
509
+ ewai team-hub start|status|stop --data PATH [--token-env NAME] [--publisher-token-env NAME] [--host HOST] [--port N] [--allow-network]
510
+ ewai team-hub resource publish FOLDER --data PATH --publisher-token-env NAME [--publisher-name NAME] --yes
511
+ ewai rollout validate [--project PATH]
512
+ ewai rollout status [--focus TEXT] [--project PATH]
513
+ ewai rollout assurance PROJECT_ID [--project PATH]
514
+ ewai discover [--project PATH] [--answers FILE] [--stack PACK] [--force]
515
+ ewai context register FOLDER [--project PATH] --yes [--label NAME] [--classification public|internal|confidential|restricted] [--cloud-processing allowed|denied|unknown]
516
+ ewai context prepare PROFILE --slug SLUG [--task TASK] [--phase PHASE] [--focus TEXT] [--budget TOKENS] [--previous DIGEST] [--project PATH]
517
+ ewai context benchmark [--samples COUNT]
518
+ ewai design-system status|list|inspect|resolve|select [ID] [--project PATH]
519
+ ewai design-system select ID --expected-digest DIGEST --approved-by NAME --yes [--project PATH]
520
+ ewai design-system validate FOLDER [--project PATH]
521
+ ewai design-system install FOLDER --scope project|personal --expected-digest DIGEST --yes [--project PATH]
522
+ ewai design-system apply DELIVERY_SLUG [--focus TEXT] [--budget TOKENS] [--project PATH]
523
+ ewai meeting register FILE [--project PATH] --yes [--label NAME] [--classification public|internal|confidential|restricted] [--cloud-processing allowed|denied|unknown]
524
+ ewai meeting prepare SOURCE_ID [--focus TEXT] [--project PATH]
525
+ ewai meeting review SOURCE_ID --input FILE --reviewed-by NAME [--project PATH]
526
+ ewai meeting promote SOURCE_ID --yes --approved-by NAME [--project PATH]
527
+ ewai meeting status [SOURCE_ID] [--focus TEXT] [--project PATH]
528
+ ewai knowledge sources [--project PATH]
529
+ ewai knowledge prepare SOURCE_REF [--focus TEXT] [--project PATH]
530
+ ewai knowledge record SOURCE_REF --input FILE [--project PATH]
531
+ ewai knowledge review BUNDLE_ID --input FILE --reviewed-by NAME [--project PATH]
532
+ ewai knowledge materialise BUNDLE_ID --yes --approved-by NAME [--project PATH]
533
+ ewai knowledge recover BUNDLE_ID --yes [--project PATH]
534
+ ewai knowledge status [BUNDLE_ID] [--project PATH]
535
+ ewai hook catalogue [--project PATH]
536
+ ewai hook validate FOLDER [--project PATH]
537
+ ewai hook register FOLDER --yes [--project PATH]
538
+ ewai hook subscribe HANDLER_ID --events EVENT[,EVENT] --yes [--project PATH]
539
+ ewai hook list [--project PATH]
540
+ ewai hook deliveries [--status STATUS] [--event EVENT] [--handler HANDLER_ID] [--project PATH]
541
+ ewai hook retry DELIVERY_ID --yes [--project PATH]
542
+ ewai hook disable SUBSCRIPTION_ID --yes [--project PATH]
543
+ ewai archaeology prepare-personas BUNDLE [--project PATH] [--force]
544
+ ewai archaeology validate-personas BUNDLE [--project PATH]
545
+ ewai archaeology prepare-technology-hosting BUNDLE [--project PATH] [--force]
546
+ ewai archaeology record-technology-hosting BUNDLE --input FILE --reviewed-by NAME [--project PATH]
547
+ ewai archaeology technology-hosting-status BUNDLE [--project PATH]
548
+ ewai archaeology depth-status [--project PATH]
549
+ ewai archaeology depth-prepare [--focus TEXT] [--input FILE] [--project PATH]
550
+ ewai archaeology depth-record --input FILE [--project PATH]
551
+ ewai archaeology depth-compare LEFT_RUN_ID RIGHT_RUN_ID [--project PATH]
552
+ ewai prototype-review status DELIVERY_SLUG [--project PATH]
553
+ ewai prototype-review plan-prepare DELIVERY_SLUG --input FILE [--project PATH]
554
+ ewai prototype-review plan-record DELIVERY_SLUG --input FILE [--project PATH]
555
+ ewai prototype-review cycle-prepare DELIVERY_SLUG --input FILE [--project PATH]
556
+ ewai prototype-review cycle-record DELIVERY_SLUG --input FILE [--project PATH]
557
+ ewai prototype-review compare DELIVERY_SLUG LEFT_DIGEST RIGHT_DIGEST [--project PATH]
558
+ ewai archaeology validate BUNDLE [--project PATH]
559
+ ewai archaeology prepare-review BUNDLE [--project PATH] [--force]
560
+ ewai archaeology curate BUNDLE [--project PATH] --yes [--approved-by NAME]
561
+ ewai archaeology validate-completion BUNDLE [--project PATH]
562
+ ewai validation list [--project PATH] [--orchestrator manual|claude|codex|antigravity]
563
+ ewai validation set claude|codex|antigravity available|unavailable [--enabled|--disabled] [--project PATH]
564
+ ewai validation checkpoint implementation-plan|test-plan|code [--cycles N] [--validators auto|PROVIDER,...] [--breadth targeted|change-set|capability|system] [--depth issues-only|issues-and-fixes|analysis-and-recommendations] [--output small|medium|large] [--enabled|--disabled] [--project PATH]
565
+ ewai security status|providers|adapters|runs|findings|dispositions [--project PATH]
566
+ ewai security adapter-validate FOLDER [--project PATH]
567
+ ewai security adapter-register FOLDER --yes [--project PATH]
568
+ ewai security run PROFILE --yes [--mode command|skill] [--confirm-runtime-target] [--project PATH]
569
+ ewai security import-prepare PROFILE --provider PROVIDER [--project PATH]
570
+ ewai security import TOKEN --yes [--project PATH]
571
+ ewai security cancel RUN --yes [--project PATH]
572
+ ewai security disposition FINDING --yes --decision DECISION --reviewer NAME --reason TEXT [--evidence PATH] [--risk-owner NAME] [--expires-at ISO] [--next-role ROLE] [--project PATH]
573
+ ewai error-report status|create|show|update|finalise|prepare-email|providers|receipts|send|archive|delete [REPORT] [--project PATH]
574
+ ewai error-report prepare-email REPORT --expected-digest DIGEST [--recipient ADDRESS] [--launch] [--project PATH]
575
+ ewai error-report settings [--automatic-local-drafts true|false] [--support-email ADDRESS] [--project PATH]
576
+ ewai error-report adapter-validate FOLDER [--project PATH]
577
+ ewai error-report adapter-register FOLDER --yes [--project PATH]
578
+ ewai policy status [INTENT] [--mode business|technical] [--project PATH]
579
+ ewai policy confirm-facts|evaluate|review|exception --input FILE [--project PATH]
580
+ ewai readiness profiles [--project PATH]
581
+ ewai readiness prepare SLUG --profile internal-only|internal-sensitive|client-facing|public-service|critical-regulated [--project PATH]
582
+ ewai readiness review ASSESSMENT --input FILE --reviewed-by NAME [--project PATH]
583
+ ewai readiness status ASSESSMENT [--project PATH]
584
+ ewai starter status [--project PATH]
585
+ ewai starter adapters [--project PATH]
586
+ ewai starter digest ROLE=FOLDER [ROLE=FOLDER...] [--project PATH]
587
+ ewai starter adapter-validate FOLDER [--project PATH]
588
+ ewai starter adapter-register FOLDER --yes [--project PATH]
589
+ ewai starter preview RECEIPT --adapter ADAPTER --yes [--timeout-ms N] [--project PATH]
590
+ ewai starter apply PREVIEW --yes --approved-by NAME [--project PATH]
591
+ ewai starter recover ATTEMPT --yes [--project PATH]
592
+ ewai doctor [--project PATH]
593
+ ewai intent create SLUG [--domain DOMAIN] [--title TITLE] [--persona REF:ROLE:DEPTH] [--delivery-shape FILE]
594
+ ewai intent map-create FILE --yes [--approved-by NAME] [--project PATH]
595
+ ewai intent audit-state [--project PATH]
596
+ ewai delivery begin SLUG [--project PATH] [--tool HOST] [--ideate] [--existing-code] [--ui] [--mode normal|shelf|resume]
597
+ ewai delivery status SLUG [--project PATH]
598
+ ewai delivery continue SLUG [--project PATH]
599
+ ewai delivery resume SLUG [--project PATH] [--tool HOST]
600
+ ewai delivery gate SLUG PHASE --input FILE [--project PATH]
601
+ ewai delivery gate-template SLUG PHASE [--project PATH]
602
+ ewai delivery phase-start SLUG PHASE [--project PATH]
603
+ ewai delivery phase-complete SLUG PHASE [--artefact PATH] [--project PATH]
604
+ ewai delivery ratify-amendments SLUG --yes --approved-by NAME --scope TEXT [--project PATH]
605
+ ewai delivery evidence-amendment SLUG [--yes --approved-by NAME --reason TEXT --expected-state-digest DIGEST] [--project PATH]
606
+ ewai delivery validation-cycle SLUG PHASE --provider claude|codex|antigravity --outcome pass|issues [--response PATH] [--fix PATH] [--notes TEXT] [--project PATH]
607
+ ewai delivery approve-build SLUG --yes --approved-by NAME [--scope TEXT] [--project PATH]
608
+ ewai delivery approve-manual-qa SLUG --yes --approved-by NAME --evidence PATH [--notes TEXT] [--project PATH]
609
+ ewai delivery runs [SLUG] [--project PATH]
610
+ ewai delivery runs --mark-stale [--hours N] [--project PATH]
611
+ ewai afk preflight SLUG [--provider auto|claude|codex|antigravity] [--parallel N] [--project PATH]
612
+ ewai afk start SLUG [--provider auto|claude|codex|antigravity] [--parallel N] [--timeout-minutes N] [--project PATH]
613
+ ewai afk status [RUN_ID] [--project PATH]
614
+ ewai afk pause RUN_ID [--project PATH]
615
+ ewai afk resume RUN_ID [--project PATH]
616
+ ewai afk cancel RUN_ID [--project PATH]
617
+ ewai palace refresh [--project PATH]
618
+ ewai palace status [--project PATH]
619
+ ewai palace search QUERY [--limit N] [--project PATH]
620
+ ewai palace tidiness [--project PATH]
621
+ ewai palace housekeeping [--project PATH]
622
+ ewai index refresh [--project PATH]
623
+ ewai index status [--project PATH]
624
+ ewai index freshness [--project PATH]
625
+ ewai index coverage [--project PATH]
626
+ ewai index profiles [--source core|technology|stack|organisation|project] [--analyser NAME] [--limit N] [--project PATH]
627
+ ewai index files [--outcome OUTCOME] [--classification CLASS] [--profile ID] [--repository NAME] [--query TEXT] [--limit N] [--project PATH]
628
+ ewai index search QUERY [--limit N] [--project PATH]
629
+ ewai index graph TARGET [--limit N] [--project PATH]
630
+ ewai index truth SLUG [--limit N] [--project PATH]
631
+ ewai index similar SLUG [--limit N] [--project PATH]
632
+ ewai index standards TARGET [--project PATH]
633
+ ewai index standards-coverage SLUG [--project PATH]
634
+ ewai pack list
635
+ ewai persona index [--project PATH] [--query TEXT]
636
+ ewai persona list [--project PATH] [--query TEXT]
637
+ ewai persona create SLUG [--scope personal|project] [--project PATH] [--name NAME] [--category CATEGORY] [--force]
638
+ ewai persona path [--scope personal|project] [--project PATH]
639
+ ewai persona premium status [--project PATH]
640
+ ewai persona premium configure [--project PATH] [--machine-name NAME]
641
+ ewai persona premium sync [--project PATH] --yes
642
+ ewai test-scenarios prepare SLUG [--focus TEXT] [--project PATH]
643
+ ewai test-scenarios record SLUG --input FILE --reviewed-by NAME [--project PATH]
644
+ ewai test-scenarios status SLUG [--project PATH]
645
+
646
+ Use --json for machine-readable output.`;
647
+ }
648
+
649
+ function selectedProject(args, allowUninitialised = false) {
650
+ const explicit = option(args, '--project');
651
+ if (explicit) return allowUninitialised ? resolve(explicit) : resolveProjectRoot(explicit);
652
+ if (allowUninitialised) return process.cwd();
653
+ return resolveProjectRoot(process.cwd());
654
+ }
655
+
656
+ function availablePersonaRoots(project = '') {
657
+ const roots = [
658
+ resolve(installRoot, 'packs/personas/core/personas'),
659
+ personalPersonaRoot(),
660
+ premiumPersonaRoot()
661
+ ];
662
+ if (project) {
663
+ const { paths } = loadProjectConfig(resolve(project));
664
+ roots.push(projectPersonaRoot(paths.projectRoot));
665
+ }
666
+ return roots;
667
+ }
668
+
669
+ function assertCompanionArguments(args) {
670
+ const valued = new Set(['--project', '--focus']);
671
+ const switches = new Set(['--json']);
672
+ for (let index = 2; index < args.length; index += 1) {
673
+ const argument = args[index];
674
+ if (switches.has(argument)) continue;
675
+ if (valued.has(argument)) {
676
+ if (args[index + 1] === undefined || String(args[index + 1]).startsWith('--')) throw new Error(`${argument} requires a value`);
677
+ index += 1;
678
+ continue;
679
+ }
680
+ throw new Error(`Unsupported companion argument: ${argument}`);
681
+ }
682
+ }
683
+
684
+ function assertDesignSystemArguments(args, subcommand) {
685
+ const valued = new Set(['--project']);
686
+ if (subcommand === 'select') {
687
+ valued.add('--expected-digest');
688
+ valued.add('--approved-by');
689
+ }
690
+ if (subcommand === 'install') {
691
+ valued.add('--scope');
692
+ valued.add('--expected-digest');
693
+ }
694
+ if (subcommand === 'apply') {
695
+ valued.add('--focus');
696
+ valued.add('--budget');
697
+ }
698
+ const switches = new Set(['--json', ...(['select', 'install'].includes(subcommand) ? ['--yes'] : [])]);
699
+ const positional = ['inspect', 'resolve', 'select', 'validate', 'install', 'apply'].includes(subcommand) ? 3 : 2;
700
+ for (let index = positional; index < args.length; index += 1) {
701
+ const argument = args[index];
702
+ if (switches.has(argument)) continue;
703
+ if (valued.has(argument)) {
704
+ if (args[index + 1] === undefined || String(args[index + 1]).startsWith('--')) throw new Error(`${argument} requires a value`);
705
+ index += 1;
706
+ continue;
707
+ }
708
+ throw new Error(`Unsupported design-system argument: ${argument}`);
709
+ }
710
+ }
711
+
712
+ function assertPortfolioArguments(args, subcommand) {
713
+ const valued = new Set(['--project', ...(subcommand === 'status' ? ['--focus'] : [])]);
714
+ const switches = new Set(['--json']);
715
+ for (let index = 2; index < args.length; index += 1) {
716
+ const argument = args[index];
717
+ if (switches.has(argument)) continue;
718
+ if (valued.has(argument)) {
719
+ if (args[index + 1] === undefined || String(args[index + 1]).startsWith('--')) {
720
+ throw new Error(`${argument} requires a value`);
721
+ }
722
+ index += 1;
723
+ continue;
724
+ }
725
+ throw new Error(`Unsupported portfolio argument: ${argument}`);
726
+ }
727
+ }
728
+
729
+ function assertRolloutArguments(args, subcommand) {
730
+ const valued = new Set(['--project', ...(subcommand === 'status' ? ['--focus'] : [])]);
731
+ const switches = new Set(['--json']);
732
+ const start = subcommand === 'assurance' ? 3 : 2;
733
+ for (let index = start; index < args.length; index += 1) {
734
+ const argument = args[index];
735
+ if (switches.has(argument)) continue;
736
+ if (valued.has(argument)) {
737
+ if (args[index + 1] === undefined || String(args[index + 1]).startsWith('--')) throw new Error(`${argument} requires a value`);
738
+ index += 1;
739
+ continue;
740
+ }
741
+ throw new Error(`Unsupported rollout argument: ${argument}`);
742
+ }
743
+ }
744
+
745
+ function assertTeamArguments(args, subcommand) {
746
+ const valued = new Set(['--project']);
747
+ const switches = new Set(['--json']);
748
+ let start = 2;
749
+ if (subcommand === 'connect') {
750
+ start = 3;
751
+ valued.add('--project-id');
752
+ valued.add('--token-env');
753
+ switches.add('--acknowledge-disclosure');
754
+ switches.add('--yes');
755
+ }
756
+ if (subcommand === 'sync') {
757
+ valued.add('--timeout-ms');
758
+ switches.add('--yes');
759
+ }
760
+ if (subcommand === 'disconnect') switches.add('--yes');
761
+ if (subcommand === 'resource') {
762
+ const action = args[2];
763
+ start = action === 'receipts' ? 3 : action === 'inspect' ? 5 : 5;
764
+ if (action === 'install') {
765
+ valued.add('--expected-digest');
766
+ valued.add('--approved-by');
767
+ switches.add('--yes');
768
+ }
769
+ if (!['inspect', 'install', 'receipts'].includes(action)) throw new Error(`Unknown team resource command: ${action ?? ''}`);
770
+ }
771
+ for (let index = start; index < args.length; index += 1) {
772
+ const argument = args[index];
773
+ if (switches.has(argument)) continue;
774
+ if (valued.has(argument)) {
775
+ if (args[index + 1] === undefined || String(args[index + 1]).startsWith('--')) throw new Error(`${argument} requires a value`);
776
+ index += 1;
777
+ continue;
778
+ }
779
+ throw new Error(`Unsupported team argument: ${argument}`);
780
+ }
781
+ }
782
+
783
+ function assertTeamHubArguments(args, subcommand) {
784
+ const valued = new Set(['--data', '--project']);
785
+ const switches = new Set(['--json']);
786
+ if (subcommand === 'start') {
787
+ valued.add('--token-env');
788
+ valued.add('--publisher-token-env');
789
+ valued.add('--host');
790
+ valued.add('--port');
791
+ switches.add('--allow-network');
792
+ }
793
+ let start = 2;
794
+ if (subcommand === 'resource') {
795
+ if (args[2] !== 'publish') throw new Error(`Unknown team-hub resource command: ${args[2] ?? ''}`);
796
+ start = 4;
797
+ valued.add('--publisher-token-env');
798
+ valued.add('--publisher-name');
799
+ switches.add('--yes');
800
+ }
801
+ for (let index = start; index < args.length; index += 1) {
802
+ const argument = args[index];
803
+ if (switches.has(argument)) continue;
804
+ if (valued.has(argument)) {
805
+ if (args[index + 1] === undefined || String(args[index + 1]).startsWith('--')) throw new Error(`${argument} requires a value`);
806
+ index += 1;
807
+ continue;
808
+ }
809
+ throw new Error(`Unsupported team-hub argument: ${argument}`);
810
+ }
811
+ }
812
+
813
+ export async function run(args) {
814
+ const json = has(args, '--json');
815
+ const command = args[0];
816
+ const subcommand = args[1];
817
+
818
+ try {
819
+ if (!command || command === 'help' || has(args, '--help')) {
820
+ print(usage());
821
+ return;
822
+ }
823
+
824
+ if (command === 'init') {
825
+ const result = initProject(selectedProject(args, true), {
826
+ name: option(args, '--name'),
827
+ specsRoot: option(args, '--specs') || undefined,
828
+ initSpecsRepository: has(args, '--init-specs-repo'),
829
+ validators: ['claude', 'codex', 'antigravity'].filter((validator) => has(args, `--${validator}`)),
830
+ force: has(args, '--force')
831
+ });
832
+ print(json ? result : formatInitResult(result), json);
833
+ return;
834
+ }
835
+
836
+ if (command === 'checkin') {
837
+ const result = await checkinProject(selectedProject(args), { installRoot });
838
+ print(json ? result : formatCheckin(result, { verbose: has(args, '--verbose') }), json);
839
+ return;
840
+ }
841
+
842
+ if (command === 'dashboard' && ['preferences','configure'].includes(subcommand)) {
843
+ const valueFlags=['--project','--enable','--disable','--expected-digest'];
844
+ const booleanFlags=['--json','--yes','--collapse','--expand'];
845
+ for(let index=2;index<args.length;index+=1){
846
+ if(valueFlags.includes(args[index])){if(!args[index+1]||args[index+1].startsWith('--'))throw new Error('A dashboard preference option is missing its value.');index+=1;}
847
+ else if(!booleanFlags.includes(args[index]))throw new Error('Unsupported dashboard preference option.');
848
+ }
849
+ const projectRoot=selectedProject(args),current=readDashboardPreferences(projectRoot);
850
+ if(subcommand==='preferences'){
851
+ print(json?current:current.views.map(v=>`${current.preferences.optional_views[v.id]?'On':'Off'} · ${v.title}: ${v.description}`).concat(`Sidebar: ${current.preferences.sidebar_collapsed?'collapsed':'expanded'}`).join('\n'),json);return;
852
+ }
853
+ const enable=options(args,'--enable'),disable=options(args,'--disable');
854
+ if(enable.some(id=>disable.includes(id))||has(args,'--collapse')&&has(args,'--expand'))throw new Error('Choose one setting for each dashboard option.');
855
+ if(!enable.length&&!disable.length&&!has(args,'--collapse')&&!has(args,'--expand'))throw new Error('Choose which dashboard preferences to change.');
856
+ if([...enable,...disable].some(id=>!DASHBOARD_VIEWS.some(v=>v.id===id)))throw new Error('Unsupported dashboard view. Read dashboard preferences for the available options.');
857
+ const preferences=current.preferences;
858
+ for(const id of enable)preferences.optional_views[id]=true;
859
+ for(const id of disable)preferences.optional_views[id]=false;
860
+ if(has(args,'--collapse'))preferences.sidebar_collapsed=true;
861
+ if(has(args,'--expand'))preferences.sidebar_collapsed=false;
862
+ const saved=saveDashboardPreferences(projectRoot,{confirmed:has(args,'--yes'),expectedDigest:option(args,'--expected-digest'),preferences});
863
+ print(json?saved:'Dashboard preferences saved. Required checks and approvals are unchanged.',json);return;
864
+ }
865
+
866
+ if (command === 'dashboard' || (command === 'server' && subcommand === 'start')) {
867
+ const result = await ensureDashboard(selectedProject(args));
868
+ print(json ? result : `EWAI pipeline dashboard: ${result.url}`, json);
869
+ return;
870
+ }
871
+
872
+ if (command === 'server' && subcommand === 'status') {
873
+ print(await dashboardStatus(selectedProject(args)), json);
874
+ return;
875
+ }
876
+
877
+ if (command === 'server' && subcommand === 'stop') {
878
+ print(await stopDashboard(selectedProject(args)), json);
879
+ return;
880
+ }
881
+
882
+ if (command === 'mcp') {
883
+ await import('./runtime/mcp-server.mjs');
884
+ return;
885
+ }
886
+
887
+ if (command === 'team') {
888
+ assertTeamArguments(args, subcommand);
889
+ const projectRoot = selectedProject(args);
890
+ if (subcommand === 'status') {
891
+ print(readTeamHubWorkspace(projectRoot), json);
892
+ return;
893
+ }
894
+ if (subcommand === 'disclosure') {
895
+ print(inspectTeamHubDisclosure(), json);
896
+ return;
897
+ }
898
+ if (subcommand === 'connect') {
899
+ if (!has(args, '--yes')) throw new Error('Team Hub connection requires explicit --yes confirmation.');
900
+ if (!has(args, '--acknowledge-disclosure')) throw new Error('Team Hub connection requires --acknowledge-disclosure.');
901
+ const endpoint = args[2] ?? '';
902
+ if (!endpoint || endpoint.startsWith('--')) throw new Error('Team Hub connection requires ENDPOINT.');
903
+ print(connectTeamHub(projectRoot, {
904
+ endpoint,
905
+ projectId: option(args, '--project-id') || undefined,
906
+ tokenEnv: option(args, '--token-env'),
907
+ confirmed: true,
908
+ disclosureAcknowledged: true,
909
+ }), json);
910
+ return;
911
+ }
912
+ if (subcommand === 'sync') {
913
+ if (!has(args, '--yes')) throw new Error('Team Hub sync requires explicit --yes confirmation.');
914
+ const timeout = option(args, '--timeout-ms');
915
+ const result = await syncTeamHub(projectRoot, {
916
+ confirmed: true,
917
+ ...(timeout ? { timeoutMs: Number(timeout) } : {}),
918
+ });
919
+ print(result, json);
920
+ if (result.attempt.status !== 'accepted') process.exitCode = 1;
921
+ return;
922
+ }
923
+ if (subcommand === 'disconnect') {
924
+ if (!has(args, '--yes')) throw new Error('Team Hub disconnect requires explicit --yes confirmation.');
925
+ print(disconnectTeamHub(projectRoot, { confirmed: true }), json);
926
+ return;
927
+ }
928
+ if (subcommand === 'resources') {
929
+ print(await listConnectedTeamHubResources(projectRoot), json);
930
+ return;
931
+ }
932
+ if (subcommand === 'resource') {
933
+ const action = args[2];
934
+ if (action === 'receipts') {
935
+ print({ schema: 'ewai.team-hub-resource-receipts/v1', receipts: listTeamHubResourceReceipts(projectRoot) }, json);
936
+ return;
937
+ }
938
+ const id = args[3] ?? '';
939
+ const version = args[4] ?? '';
940
+ if (!id || !version) throw new Error(`team resource ${action} requires ID and VERSION.`);
941
+ if (action === 'inspect') {
942
+ print(await inspectConnectedTeamHubResource(projectRoot, id, version), json);
943
+ return;
944
+ }
945
+ if (action === 'install') {
946
+ if (!has(args, '--yes')) throw new Error('Team Hub resource installation requires --yes confirmation.');
947
+ print(await installConnectedTeamHubResource(projectRoot, {
948
+ id, version, expectedDigest: option(args, '--expected-digest'), approvedBy: option(args, '--approved-by'), confirmed: true,
949
+ }), json);
950
+ return;
951
+ }
952
+ }
953
+ throw new Error(`Unknown team command: ${subcommand ?? ''}`);
954
+ }
955
+
956
+ if (command === 'team-hub') {
957
+ assertTeamHubArguments(args, subcommand);
958
+ const dataRoot = option(args, '--data');
959
+ if (!dataRoot) throw new Error('Team Hub service commands require --data PATH.');
960
+ if (subcommand === 'start') {
961
+ const port = option(args, '--port');
962
+ print(await ensureTeamHub({
963
+ dataRoot,
964
+ tokenEnv: option(args, '--token-env'),
965
+ publisherTokenEnv: option(args, '--publisher-token-env') || undefined,
966
+ host: option(args, '--host', '127.0.0.1'),
967
+ ...(port ? { port: Number(port) } : {}),
968
+ allowNetwork: has(args, '--allow-network'),
969
+ }), json);
970
+ return;
971
+ }
972
+ if (subcommand === 'resource') {
973
+ if (!has(args, '--yes')) throw new Error('Team Hub resource publication requires --yes confirmation.');
974
+ const folder = args[3] ?? '';
975
+ if (!folder || folder.startsWith('--')) throw new Error('Team Hub resource publication requires FOLDER.');
976
+ const publisherTokenEnv = option(args, '--publisher-token-env');
977
+ const publisherToken = String(process.env[publisherTokenEnv] ?? '');
978
+ if (!publisherTokenEnv || !publisherToken) throw new Error(`Team Hub publisher token environment variable is unavailable: ${publisherTokenEnv || '<missing>'}`);
979
+ const status = await teamHubStatus({ dataRoot });
980
+ if (status.status !== 'running') throw new Error('Team Hub must be running before publishing a resource.');
981
+ const resourcePackage = buildTeamHubResourcePackage(folder, { publisherName: option(args, '--publisher-name') });
982
+ const response = await fetch(`${status.state.url}/api/v1/resources`, {
983
+ method: 'POST',
984
+ headers: { authorization: `Bearer ${publisherToken}`, 'content-type': 'application/json', 'idempotency-key': `${resourcePackage.resource.id}@${resourcePackage.resource.version}:${resourcePackage.digest}` },
985
+ body: JSON.stringify(resourcePackage),
986
+ signal: AbortSignal.timeout(8_000),
987
+ });
988
+ const body = await response.json().catch(() => null);
989
+ if (!response.ok) throw new Error(body?.error?.message ?? `Team Hub rejected resource publication with HTTP ${response.status}.`);
990
+ print(body, json);
991
+ return;
992
+ }
993
+ if (subcommand === 'status') {
994
+ print(await teamHubStatus({ dataRoot }), json);
995
+ return;
996
+ }
997
+ if (subcommand === 'stop') {
998
+ print(await stopTeamHub({ dataRoot }), json);
999
+ return;
1000
+ }
1001
+ throw new Error(`Unknown team-hub command: ${subcommand ?? ''}`);
1002
+ }
1003
+
1004
+ if (command === 'companion' && subcommand === 'status') {
1005
+ assertCompanionArguments(args);
1006
+ const project = selectedProject(args);
1007
+ print(readCompanionGuidance(project, {
1008
+ focus: option(args, '--focus'),
1009
+ personas: listPersonas(availablePersonaRoots(project)),
1010
+ }), json);
1011
+ return;
1012
+ }
1013
+
1014
+ if (command === 'design-system' && ['status', 'list', 'inspect', 'resolve', 'select', 'validate', 'install', 'apply'].includes(subcommand)) {
1015
+ assertDesignSystemArguments(args, subcommand);
1016
+ const project = selectedProject(args);
1017
+ if (subcommand === 'status') {
1018
+ print(designSystemStatus(project), json);
1019
+ return;
1020
+ }
1021
+ const catalogue = listDesignSystems({ projectRoot: project });
1022
+ if (subcommand === 'list') {
1023
+ print({ schema: 'ewai.design-system-list/v1', systems: catalogue.map(projectDesignSystem) }, json);
1024
+ return;
1025
+ }
1026
+ const id = args[2] ?? '';
1027
+ if (!id || id.startsWith('--')) throw new Error(`design-system ${subcommand} requires ${['validate', 'install'].includes(subcommand) ? 'FOLDER' : subcommand === 'apply' ? 'DELIVERY_SLUG' : 'ID'}`);
1028
+ if (subcommand === 'validate') {
1029
+ print(validateDesignSystemCandidate(id), json);
1030
+ return;
1031
+ }
1032
+ if (subcommand === 'install') {
1033
+ if (!has(args, '--yes')) throw new Error('Design-system installation requires --yes confirmation');
1034
+ print(installDesignSystemCandidate(project, id, {
1035
+ scope: option(args, '--scope'), expectedDigest: option(args, '--expected-digest'), confirmed: true,
1036
+ }), json);
1037
+ return;
1038
+ }
1039
+ if (subcommand === 'apply') {
1040
+ const budget = option(args, '--budget');
1041
+ const result = applyDesignSystem(project, id, {
1042
+ focus: option(args, '--focus'),
1043
+ ...(budget ? { budgetTokens: Number(budget) } : {}),
1044
+ personaCatalogue: listPersonas(availablePersonaRoots(project)),
1045
+ });
1046
+ print(result, json);
1047
+ if (result.status !== 'applied') process.exitCode = 1;
1048
+ return;
1049
+ }
1050
+ if (subcommand === 'inspect') {
1051
+ const pack = catalogue.find((candidate) => candidate.id === id);
1052
+ if (!pack) throw new Error(`Unknown design system: ${id}`);
1053
+ print(projectDesignSystem(pack), json);
1054
+ return;
1055
+ }
1056
+ if (subcommand === 'resolve') {
1057
+ print(projectResolvedDesignSystem(resolveDesignSystem(id, catalogue)), json);
1058
+ return;
1059
+ }
1060
+ if (!has(args, '--yes')) throw new Error('Design-system selection requires --yes confirmation');
1061
+ print(selectDesignSystem(project, id, {
1062
+ expectedDigest: option(args, '--expected-digest'),
1063
+ approvedBy: option(args, '--approved-by'),
1064
+ }), json);
1065
+ return;
1066
+ }
1067
+
1068
+ if (command === 'design-system') throw new Error(`Unknown design-system command: ${subcommand ?? ''}`);
1069
+
1070
+ if (command === 'portfolio' && ['validate', 'status'].includes(subcommand)) {
1071
+ assertPortfolioArguments(args, subcommand);
1072
+ const project = selectedProject(args);
1073
+ const focus = option(args, '--focus');
1074
+ if (focus.length > 500) throw new Error('Portfolio focus exceeds 500 characters');
1075
+ const workspace = readPortfolioWorkspace(project, {
1076
+ focus,
1077
+ personas: listPersonas(availablePersonaRoots(project)),
1078
+ });
1079
+ if (subcommand === 'validate') {
1080
+ const valid = ['ready', 'attention'].includes(workspace.status);
1081
+ print({
1082
+ schema: 'ewai.portfolio-validation/v1',
1083
+ status: workspace.status,
1084
+ valid,
1085
+ diagnostics: workspace.diagnostics,
1086
+ memberCount: workspace.members.length,
1087
+ dependencyCount: workspace.dependencies.length,
1088
+ guide: workspace.guide,
1089
+ }, json);
1090
+ if (!valid) process.exitCode = 1;
1091
+ return;
1092
+ }
1093
+ print(workspace, json);
1094
+ if (workspace.status === 'invalid') process.exitCode = 1;
1095
+ return;
1096
+ }
1097
+
1098
+ if (command === 'rollout' && ['validate', 'status', 'assurance'].includes(subcommand)) {
1099
+ assertRolloutArguments(args, subcommand);
1100
+ const project = selectedProject(args);
1101
+ const focus = option(args, '--focus');
1102
+ if (focus.length > 500 || focus.includes('\0')) throw new Error('Rollout focus exceeds 500 characters or contains unsafe text');
1103
+ if (subcommand === 'assurance' && !args[2]) throw new Error('rollout assurance requires PROJECT_ID');
1104
+ const workspace = readRolloutWorkspace(project, {
1105
+ focus,
1106
+ ...(subcommand === 'assurance' ? { projectId: args[2] } : {}),
1107
+ personas: listPersonas(availablePersonaRoots(project)),
1108
+ });
1109
+ if (subcommand === 'validate') {
1110
+ const valid = ['ready', 'attention'].includes(workspace.status);
1111
+ print({
1112
+ schema: 'ewai.rollout-validation/v1',
1113
+ status: workspace.status,
1114
+ valid,
1115
+ diagnostics: workspace.diagnostics,
1116
+ cohortCount: workspace.cohorts.length,
1117
+ projectCount: workspace.cohorts.reduce((count, cohort) => count + cohort.projects.length, 0),
1118
+ notices: workspace.notices,
1119
+ guide: workspace.guide,
1120
+ }, json);
1121
+ if (!valid) process.exitCode = 1;
1122
+ return;
1123
+ }
1124
+ print(workspace, json);
1125
+ if (workspace.status === 'invalid') process.exitCode = 1;
1126
+ return;
1127
+ }
1128
+
1129
+ if (command === 'install') {
1130
+ const project = option(args, '--project');
1131
+ const result = install({
1132
+ scope: project ? 'project' : 'global',
1133
+ projectRoot: project ? resolve(project) : '',
1134
+ host: option(args, '--host', 'auto'),
1135
+ mode: option(args, '--mode', 'copy'),
1136
+ installBin: false,
1137
+ force: has(args, '--force')
1138
+ });
1139
+ print(result, json);
1140
+ return;
1141
+ }
1142
+
1143
+ if (command === 'discover') {
1144
+ const projectRoot = selectedProject(args);
1145
+ const answersPath = option(args, '--answers');
1146
+ if (!answersPath && json) {
1147
+ throw new Error('Interactive discovery cannot use --json; provide --answers FILE');
1148
+ }
1149
+ if (answersPath) {
1150
+ const answers = loadDiscoveryAnswers(resolve(answersPath));
1151
+ const selectedStacks = options(args, '--stack');
1152
+ if (selectedStacks.length) {
1153
+ answers.delivery = { ...(answers.delivery ?? {}), technologyPacks: selectedStacks };
1154
+ }
1155
+ print(discoverProject(projectRoot, answers, { force: has(args, '--force') }), json);
1156
+ } else {
1157
+ print(await runDiscoveryInterview(projectRoot, { force: has(args, '--force') }), false);
1158
+ }
1159
+ return;
1160
+ }
1161
+
1162
+ if (command === 'doctor') {
1163
+ const checks = doctorProject(selectedProject(args));
1164
+ print(checks, json);
1165
+ if (checks.some((check) => check.status === 'fail')) process.exitCode = 1;
1166
+ return;
1167
+ }
1168
+
1169
+ if (command === 'context' && subcommand === 'register') {
1170
+ const sourcePath = args[2] ?? '';
1171
+ if (!sourcePath) throw new Error('Context registration requires a source folder');
1172
+ const result = registerContextSource(selectedProject(args), sourcePath, {
1173
+ confirmed: has(args, '--yes'),
1174
+ label: option(args, '--label'),
1175
+ classification: option(args, '--classification', 'confidential'),
1176
+ cloudProcessing: option(args, '--cloud-processing', 'unknown'),
1177
+ exclusions: options(args, '--exclude'),
1178
+ force: has(args, '--force')
1179
+ });
1180
+ print(result, json);
1181
+ return;
1182
+ }
1183
+
1184
+ if (command === 'context' && subcommand === 'prepare') {
1185
+ const allowedValues = new Set(['--project', '--slug', '--task', '--phase', '--focus', '--budget', '--previous']);
1186
+ const allowedSwitches = new Set(['--json']);
1187
+ for (let index = 3; index < args.length; index += 1) {
1188
+ const argument = args[index];
1189
+ if (!argument.startsWith('--')) continue;
1190
+ if (!allowedValues.has(argument) && !allowedSwitches.has(argument)) throw new Error(`Unsupported context preparation option: ${argument}`);
1191
+ if (allowedValues.has(argument)) index += 1;
1192
+ }
1193
+ const profile = args[2] ?? '';
1194
+ const budgetValue = option(args, '--budget');
1195
+ const projectRoot = selectedProject(args);
1196
+ print(prepareProjectContext(projectRoot, {
1197
+ profile,
1198
+ slug: option(args, '--slug'),
1199
+ taskId: option(args, '--task'),
1200
+ phase: option(args, '--phase'),
1201
+ focus: option(args, '--focus'),
1202
+ budgetTokens: budgetValue ? Number(budgetValue) : undefined,
1203
+ previousDigest: option(args, '--previous'),
1204
+ personaCatalogue: listPersonas(availablePersonaRoots(projectRoot)),
1205
+ }), json);
1206
+ return;
1207
+ }
1208
+
1209
+ if (command === 'context' && subcommand === 'benchmark') {
1210
+ const allowedValues = new Set(['--samples']);
1211
+ const allowedSwitches = new Set(['--json']);
1212
+ for (let index = 2; index < args.length; index += 1) {
1213
+ const argument = args[index];
1214
+ if (!argument.startsWith('--')) continue;
1215
+ if (!allowedValues.has(argument) && !allowedSwitches.has(argument)) throw new Error(`Unsupported context benchmark option: ${argument}`);
1216
+ if (allowedValues.has(argument)) index += 1;
1217
+ }
1218
+ const sampleValue = option(args, '--samples');
1219
+ const samples = sampleValue ? Number(sampleValue) : undefined;
1220
+ if (sampleValue && (!Number.isInteger(samples) || samples < 1 || samples > 100)) {
1221
+ throw new Error('Context benchmark samples must be an integer from 1 to 100');
1222
+ }
1223
+ const fixtures = JSON.parse(readFileSync(new URL('../tests/fixtures/context-benchmarks.json', import.meta.url), 'utf8'));
1224
+ const report = runContextBenchmark(fixtures, { samples });
1225
+ print(report, json);
1226
+ if (report.status !== 'pass') process.exitCode = 1;
1227
+ return;
1228
+ }
1229
+
1230
+ if (command === 'context') throw new Error(`Unknown context command: ${subcommand ?? ''}`);
1231
+
1232
+ if (command === 'meeting' && subcommand === 'register') {
1233
+ if (!has(args, '--yes')) throw new Error('Meeting source registration requires --yes confirmation');
1234
+ const file = args[2] ?? '';
1235
+ if (!file) throw new Error('Meeting source registration requires FILE');
1236
+ print(registerMeetingSource(selectedProject(args), file, {
1237
+ confirmed: true,
1238
+ label: option(args, '--label'),
1239
+ classification: option(args, '--classification', 'confidential'),
1240
+ cloudProcessing: option(args, '--cloud-processing', 'unknown'),
1241
+ }), json);
1242
+ return;
1243
+ }
1244
+
1245
+ if (command === 'meeting' && subcommand === 'prepare') {
1246
+ const sourceId = args[2] ?? '';
1247
+ if (!sourceId) throw new Error('Meeting extraction preparation requires SOURCE_ID');
1248
+ print(prepareMeetingExtraction(selectedProject(args), sourceId, {
1249
+ focus: option(args, '--focus'),
1250
+ }), json);
1251
+ return;
1252
+ }
1253
+
1254
+ if (command === 'meeting' && subcommand === 'review') {
1255
+ const project = selectedProject(args);
1256
+ const sourceId = args[2] ?? '';
1257
+ if (!sourceId) throw new Error('Meeting review requires SOURCE_ID');
1258
+ const inputPath = option(args, '--input');
1259
+ if (!inputPath) throw new Error('Meeting review requires --input FILE');
1260
+ const reviewedBy = option(args, '--reviewed-by');
1261
+ if (!reviewedBy) throw new Error('Meeting review requires --reviewed-by NAME');
1262
+ const safeInputPath = realpathSync(resolve(inputPath));
1263
+ const inputRelative = relative(project, safeInputPath);
1264
+ if (!inputRelative || inputRelative.startsWith('..') || isAbsolute(inputRelative)) {
1265
+ throw new Error('Meeting review --input must be a project-relative file');
1266
+ }
1267
+ let input;
1268
+ try {
1269
+ input = JSON.parse(readFileSync(safeInputPath, 'utf8'));
1270
+ } catch (error) {
1271
+ throw new Error(`Cannot read meeting review input: ${error.message}`);
1272
+ }
1273
+ print(recordMeetingReview(project, sourceId, {
1274
+ bundle: input.bundle,
1275
+ dispositions: input.dispositions,
1276
+ reviewedBy,
1277
+ }), json);
1278
+ return;
1279
+ }
1280
+
1281
+ if (command === 'meeting' && subcommand === 'promote') {
1282
+ if (!has(args, '--yes')) throw new Error('Meeting evidence promotion requires --yes confirmation');
1283
+ const sourceId = args[2] ?? '';
1284
+ if (!sourceId) throw new Error('Meeting evidence promotion requires SOURCE_ID');
1285
+ const approvedBy = option(args, '--approved-by');
1286
+ if (!approvedBy) throw new Error('Meeting evidence promotion requires --approved-by NAME');
1287
+ print(promoteMeetingEvidence(selectedProject(args), sourceId, {
1288
+ confirmed: true,
1289
+ approvedBy,
1290
+ }), json);
1291
+ return;
1292
+ }
1293
+
1294
+ if (command === 'meeting' && subcommand === 'status') {
1295
+ const sourceId = args[2]?.startsWith('--') ? '' : args[2] ?? '';
1296
+ print(readMeetingEvidenceWorkspace(selectedProject(args), {
1297
+ ...(sourceId ? { sourceId } : {}),
1298
+ focus: option(args, '--focus'),
1299
+ }), json);
1300
+ return;
1301
+ }
1302
+
1303
+ if (command === 'meeting') throw new Error(`Unknown meeting command: ${subcommand ?? ''}`);
1304
+
1305
+ if (command === 'knowledge' && subcommand === 'sources') {
1306
+ print({
1307
+ schema: 'ewai.knowledge-source-list/v1',
1308
+ sources: listKnowledgeSources(selectedProject(args)),
1309
+ notices: [KNOWLEDGE_PROPOSALS_DISCLAIMER],
1310
+ }, json);
1311
+ return;
1312
+ }
1313
+
1314
+ if (command === 'knowledge' && subcommand === 'prepare') {
1315
+ const sourceRef = args[2] ?? '';
1316
+ if (!sourceRef || sourceRef.startsWith('--')) throw new Error('Knowledge proposal preparation requires SOURCE_REF');
1317
+ print(prepareKnowledgeProposals(selectedProject(args), sourceRef, { focus: option(args, '--focus') }), json);
1318
+ return;
1319
+ }
1320
+
1321
+ if (command === 'knowledge' && ['record', 'review'].includes(subcommand)) {
1322
+ const project = selectedProject(args);
1323
+ const reference = args[2] ?? '';
1324
+ if (!reference || reference.startsWith('--')) throw new Error(`Knowledge proposal ${subcommand} requires ${subcommand === 'record' ? 'SOURCE_REF' : 'BUNDLE_ID'}`);
1325
+ const inputPath = option(args, '--input');
1326
+ if (!inputPath) throw new Error(`Knowledge proposal ${subcommand} requires --input FILE`);
1327
+ let safeInputPath;
1328
+ try {
1329
+ safeInputPath = realpathSync(resolve(inputPath));
1330
+ } catch {
1331
+ throw new Error(`Knowledge proposal ${subcommand} --input must be a readable project-relative file`);
1332
+ }
1333
+ const inputRelative = relative(project, safeInputPath);
1334
+ if (!inputRelative || inputRelative.startsWith('..') || isAbsolute(inputRelative)) {
1335
+ throw new Error(`Knowledge proposal ${subcommand} --input must be a project-relative file`);
1336
+ }
1337
+ let input;
1338
+ try {
1339
+ input = JSON.parse(readFileSync(safeInputPath, 'utf8'));
1340
+ } catch {
1341
+ throw new Error(`Cannot read knowledge proposal ${subcommand} input as JSON`);
1342
+ }
1343
+ if (subcommand === 'record') {
1344
+ print({
1345
+ ...recordKnowledgeProposalBundle(project, reference, { bundle: input.bundle, activePersonas: input.activePersonas }),
1346
+ notices: [KNOWLEDGE_PROPOSALS_DISCLAIMER],
1347
+ }, json);
1348
+ return;
1349
+ }
1350
+ const reviewedBy = option(args, '--reviewed-by');
1351
+ if (!reviewedBy) throw new Error('Knowledge proposal review requires --reviewed-by NAME');
1352
+ print({
1353
+ ...recordKnowledgeProposalReview(project, reference, { reviewedBy, dispositions: input.dispositions }),
1354
+ notices: [KNOWLEDGE_PROPOSALS_DISCLAIMER],
1355
+ }, json);
1356
+ return;
1357
+ }
1358
+
1359
+ if (command === 'knowledge' && subcommand === 'materialise') {
1360
+ if (!has(args, '--yes')) throw new Error('Knowledge proposal materialisation requires --yes confirmation');
1361
+ const bundleId = args[2] ?? '';
1362
+ if (!bundleId || bundleId.startsWith('--')) throw new Error('Knowledge proposal materialisation requires BUNDLE_ID');
1363
+ const approvedBy = option(args, '--approved-by');
1364
+ if (!approvedBy) throw new Error('Knowledge proposal materialisation requires --approved-by NAME');
1365
+ print({
1366
+ ...materialiseKnowledgeProposals(selectedProject(args), bundleId, { confirmed: true, approvedBy }),
1367
+ notices: [KNOWLEDGE_PROPOSALS_DISCLAIMER],
1368
+ }, json);
1369
+ return;
1370
+ }
1371
+
1372
+ if (command === 'knowledge' && subcommand === 'recover') {
1373
+ if (!has(args, '--yes')) throw new Error('Knowledge proposal recovery requires --yes confirmation');
1374
+ const bundleId = args[2] ?? '';
1375
+ if (!bundleId || bundleId.startsWith('--')) throw new Error('Knowledge proposal recovery requires BUNDLE_ID');
1376
+ print({
1377
+ ...recoverKnowledgeMaterialisation(selectedProject(args), bundleId, { confirmed: true }),
1378
+ notices: [KNOWLEDGE_PROPOSALS_DISCLAIMER],
1379
+ }, json);
1380
+ return;
1381
+ }
1382
+
1383
+ if (command === 'knowledge' && subcommand === 'status') {
1384
+ const bundleId = args[2]?.startsWith('--') ? '' : args[2] ?? '';
1385
+ print(readKnowledgeProposalWorkspace(selectedProject(args), { ...(bundleId ? { bundleId } : {}) }), json);
1386
+ return;
1387
+ }
1388
+
1389
+ if (command === 'knowledge') throw new Error(`Unknown knowledge command: ${subcommand ?? ''}`);
1390
+
1391
+ if (command === 'hook' && subcommand === 'catalogue') {
1392
+ print({ schema: 'ewai.lifecycle-event-catalogue/v1', events: LIFECYCLE_EVENT_CATALOGUE }, json);
1393
+ return;
1394
+ }
1395
+
1396
+ if (command === 'hook' && subcommand === 'validate') {
1397
+ const folder = args[2] ?? '';
1398
+ if (!folder) throw new Error('Lifecycle hook validation requires a handler folder');
1399
+ print(validateLifecycleHandlerPackage(selectedProject(args), resolve(folder)), json);
1400
+ return;
1401
+ }
1402
+
1403
+ if (command === 'hook' && subcommand === 'register') {
1404
+ const folder = args[2] ?? '';
1405
+ if (!folder) throw new Error('Lifecycle hook registration requires a handler folder');
1406
+ print(registerLifecycleHandler(selectedProject(args), resolve(folder), { confirmed: has(args, '--yes') }), json);
1407
+ return;
1408
+ }
1409
+
1410
+ if (command === 'hook' && subcommand === 'subscribe') {
1411
+ const handlerId = args[2] ?? '';
1412
+ const patterns = option(args, '--events').split(',').map((value) => value.trim()).filter(Boolean);
1413
+ if (!handlerId) throw new Error('Lifecycle hook subscription requires a handler ID');
1414
+ if (!patterns.length) throw new Error('Lifecycle hook subscription requires --events EVENT[,EVENT]');
1415
+ print(subscribeLifecycleHandler(selectedProject(args), handlerId, patterns, { confirmed: has(args, '--yes') }), json);
1416
+ return;
1417
+ }
1418
+
1419
+ if (command === 'hook' && ['list', 'deliveries'].includes(subcommand)) {
1420
+ const projectRoot = selectedProject(args);
1421
+ reconcileCanonicalLifecycleEvents(projectRoot);
1422
+ const workspace = readLifecycleHookWorkspace(projectRoot, {
1423
+ status: option(args, '--status'),
1424
+ event: option(args, '--event'),
1425
+ handler: option(args, '--handler'),
1426
+ });
1427
+ print(subcommand === 'deliveries'
1428
+ ? { schema: workspace.schema, summary: workspace.summary, deliveries: workspace.deliveries, attempts: workspace.attempts }
1429
+ : workspace, json);
1430
+ return;
1431
+ }
1432
+
1433
+ if (command === 'hook' && subcommand === 'retry') {
1434
+ const deliveryId = args[2] ?? '';
1435
+ if (!deliveryId) throw new Error('Lifecycle hook retry requires a delivery ID');
1436
+ print(retryLifecycleDelivery(selectedProject(args), deliveryId, { confirmed: has(args, '--yes') }), json);
1437
+ return;
1438
+ }
1439
+
1440
+ if (command === 'hook' && subcommand === 'disable') {
1441
+ const subscriptionId = args[2] ?? '';
1442
+ if (!subscriptionId) throw new Error('Lifecycle hook disable requires a subscription ID');
1443
+ print(disableLifecycleSubscription(selectedProject(args), subscriptionId, { confirmed: has(args, '--yes') }), json);
1444
+ return;
1445
+ }
1446
+
1447
+ if (command === 'archaeology' && subcommand === 'validate') {
1448
+ const bundlePath = args[2] ?? '';
1449
+ if (!bundlePath) throw new Error('Archaeology validation requires a bundle path');
1450
+ const result = validateArchaeologyBundle(selectedProject(args), bundlePath);
1451
+ print(result, json);
1452
+ if (!result.valid) process.exitCode = 1;
1453
+ return;
1454
+ }
1455
+
1456
+ if (command === 'archaeology' && subcommand === 'prepare-personas') {
1457
+ const bundlePath = args[2] ?? '';
1458
+ if (!bundlePath) throw new Error('Archaeology persona preparation requires a bundle path');
1459
+ const projectRoot = selectedProject(args);
1460
+ const personas = indexPersonas(availablePersonaRoots(projectRoot));
1461
+ print(prepareArchaeologyPersonaGate(projectRoot, bundlePath, personas, { force: has(args, '--force') }), json);
1462
+ return;
1463
+ }
1464
+
1465
+ if (command === 'archaeology' && subcommand === 'validate-personas') {
1466
+ const bundlePath = args[2] ?? '';
1467
+ if (!bundlePath) throw new Error('Archaeology persona validation requires a bundle path');
1468
+ const result = validateArchaeologyPersonaGate(selectedProject(args), bundlePath);
1469
+ print(result, json);
1470
+ if (!result.valid) process.exitCode = 1;
1471
+ return;
1472
+ }
1473
+
1474
+ if (command === 'archaeology' && subcommand === 'prepare-technology-hosting') {
1475
+ const bundlePath = args[2] ?? '';
1476
+ if (!bundlePath) throw new Error('Archaeology technology and hosting preparation requires a bundle path');
1477
+ print(prepareArchaeologyTechnologyHosting(selectedProject(args), bundlePath, { force: has(args, '--force') }), json);
1478
+ return;
1479
+ }
1480
+
1481
+ if (command === 'archaeology' && subcommand === 'record-technology-hosting') {
1482
+ const project = selectedProject(args);
1483
+ const bundlePath = args[2] ?? '';
1484
+ if (!bundlePath) throw new Error('Archaeology technology and hosting recording requires a bundle path');
1485
+ const inputPath = option(args, '--input');
1486
+ if (!inputPath) throw new Error('Archaeology technology and hosting recording requires --input FILE');
1487
+ const reviewedBy = option(args, '--reviewed-by');
1488
+ if (!reviewedBy) throw new Error('Archaeology technology and hosting recording requires --reviewed-by NAME');
1489
+ const safeInputPath = realpathSync(resolve(inputPath));
1490
+ const inputRelative = relative(project, safeInputPath);
1491
+ if (!inputRelative || inputRelative.startsWith('..') || isAbsolute(inputRelative)) {
1492
+ throw new Error('Archaeology technology and hosting --input must be a project-relative file');
1493
+ }
1494
+ let input;
1495
+ try {
1496
+ input = JSON.parse(readFileSync(safeInputPath, 'utf8'));
1497
+ } catch (error) {
1498
+ throw new Error(`Cannot read technology and hosting input: ${error.message}`);
1499
+ }
1500
+ print(recordArchaeologyTechnologyHosting(project, bundlePath, input, { reviewedBy }), json);
1501
+ return;
1502
+ }
1503
+
1504
+ if (command === 'archaeology' && subcommand === 'technology-hosting-status') {
1505
+ const bundlePath = args[2] ?? '';
1506
+ if (!bundlePath) throw new Error('Archaeology technology and hosting status requires a bundle path');
1507
+ print(readArchaeologyTechnologyHosting(selectedProject(args), bundlePath), json);
1508
+ return;
1509
+ }
1510
+
1511
+ if (command === 'archaeology' && subcommand === 'depth-status') {
1512
+ const project = selectedProject(args);
1513
+ const personas = indexPersonas(availablePersonaRoots(project));
1514
+ print(readEvidenceDepthWorkspace(project, { personas }), json);
1515
+ return;
1516
+ }
1517
+
1518
+ if (command === 'archaeology' && subcommand === 'depth-prepare') {
1519
+ const project = selectedProject(args);
1520
+ const personas = indexPersonas(availablePersonaRoots(project));
1521
+ const inputPath = option(args, '--input');
1522
+ const supplied = inputPath ? readProjectJsonInput(project, inputPath, 'Archaeology evidence-depth preparation') : {};
1523
+ if (!supplied || typeof supplied !== 'object' || Array.isArray(supplied)) throw new Error('Archaeology evidence-depth preparation input must be an object');
1524
+ const unknown = Object.keys(supplied).filter((key) => !['focus', 'ownerEvidence'].includes(key));
1525
+ if (unknown.length) throw new Error(`Archaeology evidence-depth preparation input has unknown field ${unknown[0]}`);
1526
+ print(prepareEvidenceDepthWorkspace(
1527
+ project,
1528
+ { focus: option(args, '--focus', supplied.focus ?? '') },
1529
+ { personas, ownerEvidence: supplied.ownerEvidence ?? [] },
1530
+ ), json);
1531
+ return;
1532
+ }
1533
+
1534
+ if (command === 'archaeology' && subcommand === 'depth-record') {
1535
+ const project = selectedProject(args);
1536
+ const input = readProjectJsonInput(project, option(args, '--input'), 'Archaeology evidence-depth recording');
1537
+ print(recordEvidenceDepthWorkspaceRun(project, input), json);
1538
+ return;
1539
+ }
1540
+
1541
+ if (command === 'archaeology' && subcommand === 'depth-compare') {
1542
+ const leftRunId = args[2] ?? '';
1543
+ const rightRunId = args[3] ?? '';
1544
+ if (!leftRunId || !rightRunId) throw new Error('Archaeology evidence-depth comparison requires two run IDs');
1545
+ print(compareEvidenceDepthWorkspaceRuns(selectedProject(args), { leftRunId, rightRunId }), json);
1546
+ return;
1547
+ }
1548
+
1549
+ if (command === 'archaeology' && subcommand === 'prepare-review') {
1550
+ const bundlePath = args[2] ?? '';
1551
+ if (!bundlePath) throw new Error('Archaeology review preparation requires a bundle path');
1552
+ print(prepareArchaeologyReview(selectedProject(args), bundlePath, { force: has(args, '--force') }), json);
1553
+ return;
1554
+ }
1555
+
1556
+ if (command === 'prototype-review') {
1557
+ const project = selectedProject(args);
1558
+ const slug = args[2] ?? '';
1559
+ if (!slug) throw new Error(`Prototype review ${subcommand || 'command'} requires a delivery slug`);
1560
+ const personas = indexPersonas(availablePersonaRoots(project));
1561
+ if (subcommand === 'status') {
1562
+ print(readPrototypeIterationWorkspace(project, slug, { personas }), json);
1563
+ return;
1564
+ }
1565
+ if (['plan-prepare', 'plan-record', 'cycle-prepare', 'cycle-record'].includes(subcommand)) {
1566
+ const input = readProjectJsonInput(project, option(args, '--input'), `Prototype review ${subcommand}`);
1567
+ const operation = {
1568
+ 'plan-prepare': () => preparePrototypePlanWorkspace(project, slug, input, { personas }),
1569
+ 'plan-record': () => recordPrototypePlanWorkspaceReview(project, slug, input),
1570
+ 'cycle-prepare': () => preparePrototypeCycleWorkspace(project, slug, input, { personas }),
1571
+ 'cycle-record': () => recordPrototypeCycleWorkspaceReview(project, slug, input),
1572
+ }[subcommand];
1573
+ print(operation(), json);
1574
+ return;
1575
+ }
1576
+ if (subcommand === 'compare') {
1577
+ const leftDigest = args[3] ?? '';
1578
+ const rightDigest = args[4] ?? '';
1579
+ if (!leftDigest || !rightDigest) throw new Error('Prototype review comparison requires left and right digests');
1580
+ print(comparePrototypeWorkspaceReviews(project, slug, { leftDigest, rightDigest }), json);
1581
+ return;
1582
+ }
1583
+ throw new Error(`Unsupported prototype-review command: ${subcommand || '(missing)'}`);
1584
+ }
1585
+
1586
+ if (command === 'archaeology' && subcommand === 'curate') {
1587
+ const bundlePath = args[2] ?? '';
1588
+ if (!bundlePath) throw new Error('Archaeology curation requires a bundle path');
1589
+ print(curateArchaeologyBundle(selectedProject(args), bundlePath, {
1590
+ confirmed: has(args, '--yes'),
1591
+ approvedBy: option(args, '--approved-by')
1592
+ }), json);
1593
+ return;
1594
+ }
1595
+
1596
+ if (command === 'archaeology' && subcommand === 'validate-completion') {
1597
+ const bundlePath = args[2] ?? '';
1598
+ if (!bundlePath) throw new Error('Archaeology completion validation requires a bundle path');
1599
+ const result = validateArchaeologyCompletion(selectedProject(args), bundlePath);
1600
+ print(result, json);
1601
+ if (!result.valid) process.exitCode = 1;
1602
+ return;
1603
+ }
1604
+
1605
+ if (command === 'validation' && subcommand === 'list') {
1606
+ const { config } = loadProjectConfig(selectedProject(args));
1607
+ const orchestrator = option(args, '--orchestrator', 'manual');
1608
+ const status = validationStatus(config, orchestrator);
1609
+ if (json) {
1610
+ print(status, true);
1611
+ return;
1612
+ }
1613
+ const providers = externalValidationStatus(config, orchestrator);
1614
+ const lines = [
1615
+ 'Standards compliance\trequired; no waiver',
1616
+ ...providers.map((provider) => (
1617
+ `${provider.validator}\t${provider.state}\t${provider.enabled ? 'enabled' : 'disabled'}${
1618
+ provider.excluded_as_orchestrator ? '\texcluded (orchestrator)' : ''
1619
+ }`
1620
+ )),
1621
+ ...Object.entries(status.checkpoints).map(([checkpoint, setting]) => (
1622
+ `${checkpoint}\t${setting.max_cycles} cycle(s)\t${
1623
+ setting.validators.join(',') || 'no independent validator'
1624
+ }\t${setting.review.breadth}/${setting.review.depth}/${setting.review.output}`
1625
+ )),
1626
+ ];
1627
+ print(lines);
1628
+ return;
1629
+ }
1630
+
1631
+ if (command === 'validation' && subcommand === 'set') {
1632
+ const validator = args[2] ?? '';
1633
+ const state = args[3] ?? '';
1634
+ const enabled = has(args, '--enabled') ? true : has(args, '--disabled') ? false : undefined;
1635
+ print(configureExternalValidation(selectedProject(args), validator, state, enabled), json);
1636
+ return;
1637
+ }
1638
+
1639
+ if (command === 'validation' && subcommand === 'checkpoint') {
1640
+ const checkpoint = args[2] ?? '';
1641
+ const update = {};
1642
+ const cycles = option(args, '--cycles');
1643
+ if (cycles) update.max_cycles = Number.parseInt(cycles, 10);
1644
+ const validators = option(args, '--validators');
1645
+ if (validators) {
1646
+ update.validators = validators === 'auto'
1647
+ ? 'auto'
1648
+ : validators.split(',').map((value) => value.trim()).filter(Boolean);
1649
+ }
1650
+ if (has(args, '--enabled')) update.enabled = true;
1651
+ if (has(args, '--disabled')) update.enabled = false;
1652
+ const review = {};
1653
+ for (const field of ['breadth', 'depth', 'output']) {
1654
+ const value = option(args, `--${field}`);
1655
+ if (value) review[field] = value;
1656
+ }
1657
+ if (Object.keys(review).length) update.review = review;
1658
+ print(configureValidationCheckpoint(selectedProject(args), checkpoint, update), json);
1659
+ return;
1660
+ }
1661
+
1662
+ if (command === 'security') {
1663
+ const projectRoot = selectedProject(args);
1664
+ const personas = listPersonas(availablePersonaRoots(projectRoot));
1665
+ if (subcommand === 'status') return printSecurity(readSecurityWorkspace(projectRoot, { personas }), json);
1666
+ if (subcommand === 'providers') return printSecurity(discoverSecurityProviders(projectRoot), json);
1667
+ if (subcommand === 'adapters') return printSecurity(listSecurityAdapters(projectRoot), json);
1668
+ if (subcommand === 'runs') return printSecurity(listSecurityRuns(projectRoot), json);
1669
+ if (subcommand === 'findings') return printSecurity(listSecurityFindings(projectRoot), json);
1670
+ if (subcommand === 'dispositions') return printSecurity(listSecurityDispositions(projectRoot), json);
1671
+ if (subcommand === 'adapter-validate') {
1672
+ const folder = args[2] ?? '';
1673
+ if (!folder) throw new Error('Security adapter validation requires a folder.');
1674
+ return printSecurity(validateSecurityAdapter(projectRoot, resolve(folder)), json);
1675
+ }
1676
+ if (subcommand === 'adapter-register') {
1677
+ const folder = args[2] ?? '';
1678
+ if (!folder) throw new Error('Security adapter registration requires a folder.');
1679
+ if (!has(args, '--yes')) throw new Error('Security adapter registration requires explicit --yes confirmation.');
1680
+ return printSecurity(registerSecurityAdapter(projectRoot, resolve(folder), { confirmed: true }), json);
1681
+ }
1682
+ if (subcommand === 'run') {
1683
+ if (!has(args, '--yes')) throw new Error('Security run requires explicit --yes confirmation.');
1684
+ const profileId = args[2] ?? '';
1685
+ if (!profileId) throw new Error('Security run requires a profile ID.');
1686
+ const prepared = prepareSecurityRun(projectRoot, profileId, {
1687
+ confirmed: true,
1688
+ mode: option(args, '--mode') || undefined,
1689
+ confirmedRuntimeTarget: has(args, '--confirm-runtime-target'),
1690
+ trustedRevision: resolveSecurityRevision(projectRoot),
1691
+ });
1692
+ return printSecurity(prepared.run ? await executeSecurityRun(projectRoot, prepared.run.id) : prepared, json);
1693
+ }
1694
+ if (subcommand === 'import-prepare') {
1695
+ const profileId = args[2] ?? '';
1696
+ const providerId = option(args, '--provider');
1697
+ if (!profileId || !providerId) throw new Error('Security import preparation requires PROFILE and --provider PROVIDER.');
1698
+ return printSecurity(prepareSecurityArtifactImport(projectRoot, profileId, providerId, {
1699
+ trustedRevision: resolveSecurityRevision(projectRoot),
1700
+ }), json);
1701
+ }
1702
+ if (subcommand === 'import') {
1703
+ if (!has(args, '--yes')) throw new Error('Security artefact import requires explicit --yes confirmation.');
1704
+ return printSecurity(importSecurityArtifacts(projectRoot, args[2] ?? '', { confirmed: true }), json);
1705
+ }
1706
+ if (subcommand === 'cancel') {
1707
+ if (!has(args, '--yes')) throw new Error('Security run cancellation requires explicit --yes confirmation.');
1708
+ return printSecurity(cancelSecurityRun(projectRoot, args[2] ?? '', { confirmed: true }), json);
1709
+ }
1710
+ if (subcommand === 'disposition') {
1711
+ if (!has(args, '--yes')) throw new Error('Security disposition requires explicit --yes confirmation.');
1712
+ return printSecurity(recordSecurityDisposition(projectRoot, args[2] ?? '', {
1713
+ decision: option(args, '--decision'),
1714
+ reviewer: option(args, '--reviewer'),
1715
+ reason: option(args, '--reason'),
1716
+ evidence: options(args, '--evidence'),
1717
+ riskOwner: option(args, '--risk-owner'),
1718
+ expiresAt: option(args, '--expires-at'),
1719
+ nextRole: option(args, '--next-role'),
1720
+ }), json);
1721
+ }
1722
+ throw new Error(`Unknown security command: ${args.slice(1).join(' ')}`);
1723
+ }
1724
+
1725
+ if (command === 'error-report') {
1726
+ const projectRoot = selectedProject(args);
1727
+ const reportId = args[2] ?? '';
1728
+ if (subcommand === 'status') return print(listLocalErrorReports(projectRoot, { status: option(args, '--status') }), json);
1729
+ if (subcommand === 'show') {
1730
+ if (!reportId) throw new Error('error-report show requires REPORT.');
1731
+ const report = readLocalErrorReport(projectRoot, reportId);
1732
+ if (!report) throw new Error('Error report not found.');
1733
+ return print({ report, ...listErrorReportReceipts(projectRoot, reportId) }, json);
1734
+ }
1735
+ if (subcommand === 'create') {
1736
+ const packageVersion = JSON.parse(readFileSync(resolve(installRoot, 'package.json'), 'utf8')).version;
1737
+ return print(createLocalErrorReport(projectRoot, {
1738
+ capability: option(args, '--capability'),
1739
+ errorCode: option(args, '--error-code'),
1740
+ command: option(args, '--command'),
1741
+ ewaiVersion: packageVersion,
1742
+ nodeVersion: process.versions.node,
1743
+ osClass: ['darwin', 'linux', 'win32'].includes(process.platform) ? process.platform : 'other',
1744
+ installationSource: option(args, '--installation-source', 'npm'),
1745
+ title: option(args, '--title'),
1746
+ expected: option(args, '--expected'),
1747
+ actual: option(args, '--actual'),
1748
+ reproductionSteps: options(args, '--step'),
1749
+ }), json);
1750
+ }
1751
+ if (subcommand === 'update') {
1752
+ if (!reportId) throw new Error('error-report update requires REPORT.');
1753
+ const changes = {};
1754
+ for (const [flag, field] of [['--title', 'title'], ['--expected', 'expected'], ['--actual', 'actual']]) {
1755
+ if (has(args, flag)) changes[field] = option(args, flag);
1756
+ }
1757
+ if (has(args, '--step')) changes.reproductionSteps = options(args, '--step');
1758
+ return print(updateLocalErrorReport(projectRoot, reportId, changes), json);
1759
+ }
1760
+ if (subcommand === 'finalise') {
1761
+ if (!reportId) throw new Error('error-report finalise requires REPORT.');
1762
+ return print(finaliseLocalErrorReport(projectRoot, reportId), json);
1763
+ }
1764
+ if (subcommand === 'prepare-email') {
1765
+ if (!reportId) throw new Error('error-report prepare-email requires REPORT.');
1766
+ return print(prepareErrorReportEmail(projectRoot, reportId, {
1767
+ expectedDigest: option(args, '--expected-digest'), recipient: option(args, '--recipient') || undefined,
1768
+ launch: has(args, '--launch'),
1769
+ }), json);
1770
+ }
1771
+ if (subcommand === 'providers') return print(listErrorReportProviders(projectRoot), json);
1772
+ if (subcommand === 'receipts') {
1773
+ if (!reportId) throw new Error('error-report receipts requires REPORT.');
1774
+ return print(listErrorReportReceipts(projectRoot, reportId), json);
1775
+ }
1776
+ if (subcommand === 'send') {
1777
+ if (!has(args, '--yes')) throw new Error('error-report send requires explicit --yes confirmation.');
1778
+ if (!reportId || !option(args, '--provider') || !option(args, '--expected-digest')) throw new Error('error-report send requires REPORT, --provider and --expected-digest.');
1779
+ return print(await submitErrorReportProvider(projectRoot, reportId, option(args, '--provider'), {
1780
+ confirmed: true, expectedDigest: option(args, '--expected-digest'),
1781
+ ...(option(args, '--timeout-ms') ? { timeoutMs: Number(option(args, '--timeout-ms')) } : {}),
1782
+ }), json);
1783
+ }
1784
+ if (subcommand === 'archive') {
1785
+ if (!has(args, '--yes')) throw new Error('error-report archive requires explicit --yes confirmation.');
1786
+ return print(archiveErrorReport(projectRoot, reportId, { confirmed: true }), json);
1787
+ }
1788
+ if (subcommand === 'delete') {
1789
+ if (!has(args, '--yes')) throw new Error('error-report delete requires explicit --yes confirmation.');
1790
+ return print(deleteErrorReport(projectRoot, reportId, { confirmed: true }), json);
1791
+ }
1792
+ if (subcommand === 'settings') {
1793
+ const update = {};
1794
+ if (has(args, '--automatic-local-drafts')) {
1795
+ const value = option(args, '--automatic-local-drafts');
1796
+ if (!['true', 'false'].includes(value)) throw new Error('--automatic-local-drafts must be true or false.');
1797
+ update.automaticLocalDrafts = value === 'true';
1798
+ }
1799
+ if (has(args, '--support-email')) update.supportEmail = option(args, '--support-email');
1800
+ return print(updateErrorReportSettings(projectRoot, update), json);
1801
+ }
1802
+ if (subcommand === 'adapter-validate') {
1803
+ if (!reportId) throw new Error('error-report adapter-validate requires FOLDER.');
1804
+ return print(validateErrorReportProvider(resolve(reportId)), json);
1805
+ }
1806
+ if (subcommand === 'adapter-register') {
1807
+ if (!has(args, '--yes')) throw new Error('error-report adapter-register requires explicit --yes confirmation.');
1808
+ if (!reportId) throw new Error('error-report adapter-register requires FOLDER.');
1809
+ return print(registerErrorReportProvider(projectRoot, resolve(reportId), { confirmed: true }), json);
1810
+ }
1811
+ throw new Error(`Unknown error-report command: ${args.slice(1).join(' ')}`);
1812
+ }
1813
+
1814
+ if (command === 'policy') {
1815
+ const projectRoot = selectedProject(args);
1816
+ const personas = listPersonas(availablePersonaRoots(projectRoot));
1817
+ if (subcommand === 'status') {
1818
+ return printPolicy(readPolicyWorkspace(projectRoot, {
1819
+ intentReference: args[2] ?? option(args, '--intent'),
1820
+ mode: option(args, '--mode', 'business'),
1821
+ personas,
1822
+ }), json);
1823
+ }
1824
+ if (!['confirm-facts', 'evaluate', 'review', 'exception'].includes(subcommand)) {
1825
+ throw new Error(`Unknown policy command: ${subcommand ?? ''}`);
1826
+ }
1827
+ const input = readProjectJsonInput(projectRoot, option(args, '--input'), `policy ${subcommand ?? 'action'}`);
1828
+ if (subcommand === 'confirm-facts') return printPolicy(confirmPolicyFactsAction(projectRoot, input, { personas }), json);
1829
+ if (subcommand === 'evaluate') return printPolicy(evaluatePolicyDesignAction(projectRoot, input, { personas }), json);
1830
+ if (subcommand === 'review') return printPolicy(recordPolicyReviewAction(projectRoot, input, { personas }), json);
1831
+ if (subcommand === 'exception') return printPolicy(recordPolicyExceptionAction(projectRoot, input, { personas }), json);
1832
+ }
1833
+
1834
+ if (command === 'readiness') {
1835
+ if (subcommand === 'profiles') {
1836
+ return printReadiness({
1837
+ schema: 'ewai.solution-readiness-profiles/v1',
1838
+ profiles: listSolutionReadinessProfiles(),
1839
+ notices: [ASSURANCE_NOTICE, READINESS_AUTHORITY_NOTICE],
1840
+ }, json);
1841
+ }
1842
+ const projectRoot = selectedProject(args);
1843
+ if (subcommand === 'prepare') {
1844
+ const slug = args[2];
1845
+ const profile = option(args, '--profile');
1846
+ if (!slug) throw new Error('readiness prepare requires SLUG');
1847
+ if (!profile) throw new Error('readiness prepare requires --profile PROFILE');
1848
+ return printReadiness(prepareSolutionReadinessReview(projectRoot, slug, profile, {
1849
+ personaCatalogue: listPersonas(availablePersonaRoots(projectRoot)),
1850
+ }), json);
1851
+ }
1852
+ if (subcommand === 'review') {
1853
+ const assessmentId = args[2];
1854
+ const inputPath = option(args, '--input');
1855
+ const reviewedBy = option(args, '--reviewed-by');
1856
+ if (!assessmentId) throw new Error('readiness review requires ASSESSMENT');
1857
+ if (!inputPath) throw new Error('readiness review requires --input FILE');
1858
+ if (!reviewedBy) throw new Error('readiness review requires --reviewed-by NAME');
1859
+ const candidateInputPath = resolve(inputPath);
1860
+ let safeInputPath;
1861
+ try { safeInputPath = realpathSync(candidateInputPath); }
1862
+ catch { throw new Error('Cannot read readiness review input file'); }
1863
+ const inputRelative = relative(projectRoot, safeInputPath);
1864
+ if (!inputRelative || inputRelative.startsWith('..') || isAbsolute(inputRelative)) throw new Error('readiness --input must resolve to a project-relative file');
1865
+ let input;
1866
+ try { input = JSON.parse(readFileSync(safeInputPath, 'utf8')); }
1867
+ catch { throw new Error('Cannot parse readiness review input JSON'); }
1868
+ return printReadiness(recordSolutionReadinessReview(projectRoot, assessmentId, input, { reviewedBy }), json);
1869
+ }
1870
+ if (subcommand === 'status') {
1871
+ const assessmentId = args[2];
1872
+ if (!assessmentId) throw new Error('readiness status requires ASSESSMENT');
1873
+ return printReadiness(readSolutionReadinessStatus(projectRoot, assessmentId), json);
1874
+ }
1875
+ throw new Error(`Unknown readiness command: ${subcommand ?? ''}`);
1876
+ }
1877
+
1878
+ if (command === 'starter') {
1879
+ const projectRoot = selectedProject(args);
1880
+ const personas = listPersonas(availablePersonaRoots(projectRoot));
1881
+ if (subcommand === 'status') {
1882
+ return print(readStarterMaterialisationWorkspace(projectRoot, {
1883
+ personas: selectStarterPersonas(personas, 'review'),
1884
+ }), json);
1885
+ }
1886
+ if (subcommand === 'adapters') return print(listStarterAdapters(projectRoot), json);
1887
+ if (subcommand === 'digest') {
1888
+ const assignments = [];
1889
+ for (const value of args.slice(2)) {
1890
+ if (value.startsWith('--')) break;
1891
+ assignments.push(value);
1892
+ }
1893
+ if (!assignments.length) throw new Error('starter digest requires one or more ROLE=FOLDER targets');
1894
+ const roles = new Set();
1895
+ const targets = assignments.map((assignment) => {
1896
+ const separator = assignment.indexOf('=');
1897
+ const role = separator > 0 ? assignment.slice(0, separator) : '';
1898
+ const folder = separator > 0 ? assignment.slice(separator + 1) : '';
1899
+ if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(role) || roles.has(role) || !folder) {
1900
+ throw new Error(`starter digest target must be a unique ROLE=FOLDER value: ${assignment}`);
1901
+ }
1902
+ roles.add(role);
1903
+ const tree = canonicalStarterTree(realpathSync(resolve(folder)));
1904
+ return {
1905
+ role,
1906
+ digest: starterTreeDigest(tree),
1907
+ files: tree.length,
1908
+ bytes: tree.reduce((total, file) => total + file.bytes, 0),
1909
+ };
1910
+ });
1911
+ return print({
1912
+ schema: 'ewai.governed-starter-pack-digest/v1',
1913
+ targets,
1914
+ digest: starterPackDigest(targets),
1915
+ }, json);
1916
+ }
1917
+ if (subcommand === 'adapter-validate') {
1918
+ const folder = args[2];
1919
+ if (!folder) throw new Error('starter adapter-validate requires FOLDER');
1920
+ return print(validateStarterAdapterPackage(folder), json);
1921
+ }
1922
+ if (subcommand === 'adapter-register') {
1923
+ if (!has(args, '--yes')) throw new Error('starter adapter-register requires --yes because the package is trusted local code');
1924
+ const folder = args[2];
1925
+ if (!folder) throw new Error('starter adapter-register requires FOLDER');
1926
+ return print(registerStarterAdapter(projectRoot, folder, { confirmed: true }), json);
1927
+ }
1928
+ if (subcommand === 'preview') {
1929
+ if (!has(args, '--yes')) throw new Error('starter preview requires --yes');
1930
+ const receiptId = args[2];
1931
+ const adapterId = option(args, '--adapter');
1932
+ if (!receiptId || !adapterId) throw new Error('starter preview requires RECEIPT and --adapter ADAPTER');
1933
+ const timeout = option(args, '--timeout-ms');
1934
+ return print(await prepareStarterMaterialisation(projectRoot, receiptId, adapterId, {
1935
+ confirmed: true,
1936
+ ...(timeout ? { timeoutMs: Number(timeout) } : {}),
1937
+ }), json);
1938
+ }
1939
+ if (subcommand === 'apply') {
1940
+ if (!has(args, '--yes')) throw new Error('starter apply requires --yes');
1941
+ const approvedBy = option(args, '--approved-by');
1942
+ if (!approvedBy) throw new Error('starter apply requires --approved-by NAME');
1943
+ const previewId = args[2];
1944
+ if (!previewId) throw new Error('starter apply requires PREVIEW');
1945
+ return print(applyStarterMaterialisation(projectRoot, previewId, {
1946
+ confirmed: true,
1947
+ approvedBy,
1948
+ personas: selectStarterPersonas(personas, 'apply'),
1949
+ }), json);
1950
+ }
1951
+ if (subcommand === 'recover') {
1952
+ if (!has(args, '--yes')) throw new Error('starter recover requires --yes');
1953
+ const attemptId = args[2];
1954
+ if (!attemptId) throw new Error('starter recover requires ATTEMPT');
1955
+ return print(recoverStarterMaterialisation(projectRoot, attemptId, { confirmed: true }), json);
1956
+ }
1957
+ throw new Error(`Unknown starter command: ${subcommand ?? ''}`);
1958
+ }
1959
+
1960
+ if (command === 'intent' && subcommand === 'create') {
1961
+ const slug = args[2] ?? '';
1962
+ const deliveryShapePath = option(args, '--delivery-shape');
1963
+ const result = createIntent(selectedProject(args), {
1964
+ slug,
1965
+ domain: option(args, '--domain', 'general'),
1966
+ title: option(args, '--title'),
1967
+ personas: options(args, '--persona'),
1968
+ deliveryShape: deliveryShapePath
1969
+ ? YAML.parse(readFileSync(resolve(deliveryShapePath), 'utf8'))
1970
+ : null,
1971
+ force: has(args, '--force')
1972
+ });
1973
+ print(result, json);
1974
+ return;
1975
+ }
1976
+
1977
+ if (command === 'intent' && subcommand === 'map-create') {
1978
+ const requestPath = args[2] ?? '';
1979
+ if (!requestPath) throw new Error('Intent map creation requires a JSON or YAML request file');
1980
+ const request = loadIntentMapRequest(resolve(requestPath));
1981
+ print(createIntentMap(selectedProject(args), request, {
1982
+ confirmed: has(args, '--yes'),
1983
+ approvedBy: option(args, '--approved-by'),
1984
+ }), json);
1985
+ return;
1986
+ }
1987
+
1988
+ if (command === 'intent' && subcommand === 'audit-state') {
1989
+ print(auditIntentStateCopies(selectedProject(args), { repairMissing: true }), json);
1990
+ return;
1991
+ }
1992
+
1993
+ if (command === 'delivery' && subcommand === 'begin') {
1994
+ const slug = args[2] ?? '';
1995
+ print(beginDelivery(selectedProject(args), slug, {
1996
+ tool: option(args, '--tool', 'manual'),
1997
+ ideate: has(args, '--ideate'),
1998
+ existingCode: has(args, '--existing-code'),
1999
+ ui: has(args, '--ui'),
2000
+ mode: option(args, '--mode', 'normal'),
2001
+ intensity: option(args, '--intensity') || undefined
2002
+ }), json);
2003
+ return;
2004
+ }
2005
+
2006
+ if (command === 'delivery' && subcommand === 'status') {
2007
+ print(readDeliveryState(selectedProject(args), args[2] ?? ''), json);
2008
+ return;
2009
+ }
2010
+
2011
+ if (command === 'delivery' && subcommand === 'validation-cycle') {
2012
+ const slug = args[2] ?? '';
2013
+ const phaseId = args[3] ?? '';
2014
+ print(recordExternalValidationCycle(selectedProject(args), slug, phaseId, {
2015
+ provider: option(args, '--provider'),
2016
+ outcome: option(args, '--outcome'),
2017
+ responsePath: option(args, '--response') || undefined,
2018
+ fixPath: option(args, '--fix') || undefined,
2019
+ notes: option(args, '--notes'),
2020
+ }), json);
2021
+ return;
2022
+ }
2023
+
2024
+ if (command === 'delivery' && subcommand === 'continue') {
2025
+ print(continueDelivery(selectedProject(args), args[2] ?? ''), json);
2026
+ return;
2027
+ }
2028
+
2029
+ if (command === 'delivery' && subcommand === 'resume') {
2030
+ print(resumeShelvedDelivery(selectedProject(args), args[2] ?? '', {
2031
+ tool: option(args, '--tool', 'manual')
2032
+ }), json);
2033
+ return;
2034
+ }
2035
+
2036
+ if (command === 'delivery' && subcommand === 'gate') {
2037
+ const inputPath = option(args, '--input');
2038
+ if (!inputPath) throw new Error('A phase gate requires --input FILE');
2039
+ const input = JSON.parse(readFileSync(resolve(inputPath), 'utf8'));
2040
+ print(recordPhaseGate(selectedProject(args), args[2] ?? '', args[3] ?? '', input), json);
2041
+ return;
2042
+ }
2043
+
2044
+ if (command === 'delivery' && subcommand === 'gate-template') {
2045
+ print(phaseGateTemplate(selectedProject(args), args[2] ?? '', args[3] ?? ''), true);
2046
+ return;
2047
+ }
2048
+
2049
+ if (command === 'delivery' && subcommand === 'phase-start') {
2050
+ print(startDeliveryPhase(selectedProject(args), args[2] ?? '', args[3] ?? ''), json);
2051
+ return;
2052
+ }
2053
+
2054
+ if (command === 'delivery' && subcommand === 'phase-complete') {
2055
+ print(completeDeliveryPhase(selectedProject(args), args[2] ?? '', args[3] ?? '', {
2056
+ artefactPath: option(args, '--artefact') || undefined
2057
+ }), json);
2058
+ return;
2059
+ }
2060
+
2061
+ if (command === 'delivery' && subcommand === 'ratify-amendments') {
2062
+ if (!has(args, '--yes')) throw new Error('Amendment ratification requires explicit --yes confirmation');
2063
+ print(ratifyDeliveryAmendments(selectedProject(args), args[2] ?? '', {
2064
+ decision: 'approved',
2065
+ approvedBy: option(args, '--approved-by'),
2066
+ scope: option(args, '--scope'),
2067
+ }), json);
2068
+ return;
2069
+ }
2070
+
2071
+ if (command === 'delivery' && subcommand === 'evidence-amendment') {
2072
+ const projectRoot = selectedProject(args);
2073
+ const slug = args[2] ?? '';
2074
+ if (!has(args, '--yes')) {
2075
+ print(previewCompletedEvidenceAmendment(projectRoot, slug), json);
2076
+ return;
2077
+ }
2078
+ print(ratifyCompletedEvidenceAmendment(projectRoot, slug, {
2079
+ confirmed: true,
2080
+ approvedBy: option(args, '--approved-by'),
2081
+ reason: option(args, '--reason'),
2082
+ expectedStateDigest: option(args, '--expected-state-digest'),
2083
+ }), json);
2084
+ return;
2085
+ }
2086
+
2087
+ if (command === 'delivery' && subcommand === 'approve-build') {
2088
+ if (!has(args, '--yes')) throw new Error('Build approval requires explicit --yes confirmation');
2089
+ print(recordBuildApproval(selectedProject(args), args[2] ?? '', {
2090
+ decision: 'approved',
2091
+ approvedBy: option(args, '--approved-by'),
2092
+ scope: option(args, '--scope')
2093
+ }), json);
2094
+ return;
2095
+ }
2096
+
2097
+ if (command === 'delivery' && subcommand === 'approve-manual-qa') {
2098
+ if (!has(args, '--yes')) throw new Error('Manual QA approval requires explicit --yes confirmation');
2099
+ print(recordManualQaApproval(selectedProject(args), args[2] ?? '', {
2100
+ decision: 'approved',
2101
+ approvedBy: option(args, '--approved-by'),
2102
+ evidencePath: option(args, '--evidence'),
2103
+ notes: option(args, '--notes')
2104
+ }), json);
2105
+ return;
2106
+ }
2107
+
2108
+ if (command === 'delivery' && subcommand === 'runs') {
2109
+ print(has(args, '--mark-stale')
2110
+ ? markStaleCommandRuns(selectedProject(args), {
2111
+ hours: Number(option(args, '--hours', '6')),
2112
+ summary: option(args, '--summary')
2113
+ })
2114
+ : listCommandRuns(selectedProject(args), args[2]?.startsWith('--') ? '' : args[2] ?? ''), json);
2115
+ return;
2116
+ }
2117
+
2118
+ if (command === 'afk' && subcommand === 'preflight') {
2119
+ print(preflightAfkRun(selectedProject(args), args[2] ?? '', {
2120
+ provider: option(args, '--provider', 'auto'),
2121
+ maxParallel: Number(option(args, '--parallel', '1')),
2122
+ }), json);
2123
+ return;
2124
+ }
2125
+
2126
+ if (command === 'afk' && subcommand === 'start') {
2127
+ const timeoutMinutes = Number(option(args, '--timeout-minutes', '45'));
2128
+ if (!Number.isFinite(timeoutMinutes) || timeoutMinutes < 1) throw new Error('--timeout-minutes must be at least 1');
2129
+ print(startAfkRun(selectedProject(args), args[2] ?? '', {
2130
+ provider: option(args, '--provider', 'auto'),
2131
+ maxParallel: Number(option(args, '--parallel', '1')),
2132
+ timeoutMs: timeoutMinutes * 60 * 1000,
2133
+ }), json);
2134
+ return;
2135
+ }
2136
+
2137
+ if (command === 'afk' && subcommand === 'status') {
2138
+ print(afkRunStatus(selectedProject(args), args[2]?.startsWith('--') ? '' : args[2] ?? ''), json);
2139
+ return;
2140
+ }
2141
+
2142
+ if (command === 'afk' && subcommand === 'pause') {
2143
+ print(pauseAfkRun(selectedProject(args), args[2] ?? ''), json);
2144
+ return;
2145
+ }
2146
+
2147
+ if (command === 'afk' && subcommand === 'resume') {
2148
+ print(resumeAfkRun(selectedProject(args), args[2] ?? ''), json);
2149
+ return;
2150
+ }
2151
+
2152
+ if (command === 'afk' && subcommand === 'cancel') {
2153
+ print(cancelAfkRun(selectedProject(args), args[2] ?? ''), json);
2154
+ return;
2155
+ }
2156
+
2157
+ if (command === 'palace' && subcommand === 'refresh') {
2158
+ const result = refreshPalaceIndex(selectedProject(args), { force: true });
2159
+ print(json ? result : `EWAI Mind Palace indexed · ${result.documents} artefacts · ${result.sections} sections · ${result.links} links`, json);
2160
+ return;
2161
+ }
2162
+
2163
+ if (command === 'palace' && subcommand === 'status') {
2164
+ print(palaceIndexStatus(selectedProject(args)), json);
2165
+ return;
2166
+ }
2167
+
2168
+ if (command === 'palace' && subcommand === 'search') {
2169
+ const result = searchPalace(selectedProject(args), args[2] ?? '', {
2170
+ limit: Number(option(args, '--limit', '50'))
2171
+ });
2172
+ print(json ? result : formatPalaceSearch(result), json);
2173
+ return;
2174
+ }
2175
+
2176
+ if (command === 'palace' && (subcommand === 'tidiness' || subcommand === 'tidyness')) {
2177
+ const result = palaceTidiness(selectedProject(args));
2178
+ print(json ? result : formatPalaceTidiness(result), json);
2179
+ return;
2180
+ }
2181
+
2182
+ if (command === 'palace' && subcommand === 'housekeeping') {
2183
+ const result = palaceHousekeeping(selectedProject(args));
2184
+ print(json ? result : formatPalaceHousekeeping(result), json);
2185
+ return;
2186
+ }
2187
+
2188
+ if (command === 'index' && subcommand === 'refresh') {
2189
+ print(refreshRepositoryIndex(selectedProject(args)), json);
2190
+ return;
2191
+ }
2192
+
2193
+ if (command === 'index' && subcommand === 'status') {
2194
+ print(repositoryIndexStatus(selectedProject(args)), json);
2195
+ return;
2196
+ }
2197
+
2198
+ if (command === 'index' && subcommand === 'freshness') {
2199
+ print(repositoryIndexFreshness(selectedProject(args)), json);
2200
+ return;
2201
+ }
2202
+
2203
+ if (command === 'index' && subcommand === 'coverage') {
2204
+ print(repositorySourceMapCoverage(selectedProject(args)), json);
2205
+ return;
2206
+ }
2207
+
2208
+ if (command === 'index' && subcommand === 'profiles') {
2209
+ print(repositorySourceMapProfiles(selectedProject(args), {
2210
+ sourceKind: option(args, '--source'),
2211
+ analyser: option(args, '--analyser'),
2212
+ limit: Number(option(args, '--limit', '100'))
2213
+ }), json);
2214
+ return;
2215
+ }
2216
+
2217
+ if (command === 'index' && subcommand === 'files') {
2218
+ print(repositorySourceMapFiles(selectedProject(args), {
2219
+ outcome: option(args, '--outcome'),
2220
+ classification: option(args, '--classification'),
2221
+ profileId: option(args, '--profile'),
2222
+ repository: option(args, '--repository'),
2223
+ query: option(args, '--query'),
2224
+ limit: Number(option(args, '--limit', '50'))
2225
+ }), json);
2226
+ return;
2227
+ }
2228
+
2229
+ if (command === 'index' && subcommand === 'search') {
2230
+ print(searchRepositoryIndex(selectedProject(args), args[2] ?? '', {
2231
+ limit: Number(option(args, '--limit', '50'))
2232
+ }), json);
2233
+ return;
2234
+ }
2235
+
2236
+ if (command === 'index' && subcommand === 'graph') {
2237
+ print(repositoryGraph(selectedProject(args), args[2] ?? '', {
2238
+ limit: Number(option(args, '--limit', '80'))
2239
+ }), json);
2240
+ return;
2241
+ }
2242
+
2243
+ if (command === 'index' && subcommand === 'truth') {
2244
+ print(repositoryTruth(selectedProject(args), args[2] ?? '', {
2245
+ limit: Number(option(args, '--limit', '80'))
2246
+ }), json);
2247
+ return;
2248
+ }
2249
+
2250
+ if (command === 'index' && subcommand === 'similar') {
2251
+ print(similarRepositoryCapabilities(selectedProject(args), args[2] ?? '', {
2252
+ limit: Number(option(args, '--limit', '12'))
2253
+ }), json);
2254
+ return;
2255
+ }
2256
+
2257
+ if (command === 'index' && subcommand === 'standards') {
2258
+ print(repositoryStandards(selectedProject(args), args[2] ?? ''), json);
2259
+ return;
2260
+ }
2261
+
2262
+ if (command === 'index' && subcommand === 'standards-coverage') {
2263
+ print(repositoryStandardsCoverage(selectedProject(args), args[2] ?? ''), json);
2264
+ return;
2265
+ }
2266
+
2267
+ if (command === 'pack' && subcommand === 'list') {
2268
+ print(listPacks().map(({ path, ...pack }) => json ? { ...pack, path } : `${pack.id} ${pack.version} [${pack.type}]`), json);
2269
+ return;
2270
+ }
2271
+
2272
+ if (command === 'persona' && subcommand === 'list') {
2273
+ const project = option(args, '--project');
2274
+ const personas = listPersonas(availablePersonaRoots(project), option(args, '--query'));
2275
+ print(personas.map((persona) => json ? persona : `${persona.id}\t${persona.name}`), json);
2276
+ return;
2277
+ }
2278
+
2279
+ if (command === 'persona' && subcommand === 'index') {
2280
+ const project = option(args, '--project');
2281
+ const index = indexPersonas(availablePersonaRoots(project), option(args, '--query'));
2282
+ print(index.map((persona) => json ? persona : `${persona.id}\t${persona.name}\t${persona.description}`), json);
2283
+ return;
2284
+ }
2285
+
2286
+ if (command === 'persona' && subcommand === 'create') {
2287
+ const explicitProject = option(args, '--project');
2288
+ const scope = option(args, '--scope', explicitProject ? 'project' : 'personal');
2289
+ let projectRoot = '';
2290
+ if (scope === 'project') {
2291
+ const loaded = loadProjectConfig(selectedProject(args));
2292
+ projectRoot = loaded.paths.projectRoot;
2293
+ }
2294
+ const result = createPersona({
2295
+ scope,
2296
+ slug: args[2] ?? '',
2297
+ name: option(args, '--name'),
2298
+ category: option(args, '--category'),
2299
+ projectRoot,
2300
+ force: has(args, '--force')
2301
+ });
2302
+ print(result, json);
2303
+ return;
2304
+ }
2305
+
2306
+ if (command === 'persona' && subcommand === 'path') {
2307
+ const explicitProject = option(args, '--project');
2308
+ const scope = option(args, '--scope', explicitProject ? 'project' : 'personal');
2309
+ let projectRoot = '';
2310
+ if (scope === 'project') {
2311
+ const loaded = loadProjectConfig(selectedProject(args));
2312
+ projectRoot = loaded.paths.projectRoot;
2313
+ }
2314
+ print(personaLibraryRoot(scope, { projectRoot }), json);
2315
+ return;
2316
+ }
2317
+
2318
+ if (command === 'persona' && subcommand === 'premium' && args[2] === 'status') {
2319
+ print(await premiumPersonaEntitlementStatus(selectedProject(args)), json);
2320
+ return;
2321
+ }
2322
+
2323
+ if (command === 'persona' && subcommand === 'premium' && args[2] === 'configure') {
2324
+ if (has(args, '--licence-key') || has(args, '--license-key')) throw new Error('Do not put licence keys in command arguments. Use the private terminal prompt.');
2325
+ if (!process.stdin.isTTY || !process.stdout.isTTY) throw new Error('Licence setup requires an interactive terminal so the key is not echoed or captured in command history.');
2326
+ loadProjectConfig(selectedProject(args));
2327
+ const muted = new Writable({ write(_chunk, _encoding, callback) { callback(); } });
2328
+ muted.isTTY = true; muted.columns = process.stdout.columns;
2329
+ const prompt = createInterface({ input: process.stdin, output: muted, terminal: true, historySize: 0 });
2330
+ let licenceKey;
2331
+ let result;
2332
+ try {
2333
+ process.stdout.write('Submitting your key verifies the licence and downloads premium personas now. Core personas remain available if you cancel.\n');
2334
+ process.stdout.write('Persona licence key (hidden): ');
2335
+ licenceKey = await prompt.question('');
2336
+ process.stdout.write('\n');
2337
+ result = await configureAndInstallPremiumPersonas(selectedProject(args), { licenceKey, server: option(args, '--server') || undefined, machineName: option(args, '--machine-name') || undefined, confirmed: true });
2338
+ } finally { licenceKey = undefined; prompt.close(); }
2339
+ print(result, json);
2340
+ if (result.status !== 'ready') process.exitCode = 1;
2341
+ return;
2342
+ }
2343
+
2344
+ if (command === 'persona' && subcommand === 'premium' && args[2] === 'sync') {
2345
+ const result = await syncPremiumPersonas(selectedProject(args), {
2346
+ confirmed: has(args, '--yes'), replaceConfirmed: has(args, '--replace')
2347
+ });
2348
+ print(result, json);
2349
+ return;
2350
+ }
2351
+
2352
+ if (command === 'test-scenarios' && subcommand === 'prepare') {
2353
+ const project = selectedProject(args);
2354
+ const result = preparePersonaTestScenarioBrief(project, args[2] ?? '', {
2355
+ focus: option(args, '--focus'),
2356
+ personas: listPersonas(availablePersonaRoots(project))
2357
+ });
2358
+ print(result, json);
2359
+ return;
2360
+ }
2361
+
2362
+ if (command === 'test-scenarios' && subcommand === 'record') {
2363
+ const project = selectedProject(args);
2364
+ const inputPath = option(args, '--input');
2365
+ if (!inputPath) throw new Error('test-scenarios record requires --input FILE');
2366
+ const reviewedBy = option(args, '--reviewed-by');
2367
+ if (!reviewedBy) throw new Error('test-scenarios record requires --reviewed-by NAME');
2368
+ const safeInputPath = realpathSync(resolve(inputPath));
2369
+ const inputRelative = relative(project, safeInputPath);
2370
+ if (!inputRelative || inputRelative.startsWith('..') || isAbsolute(inputRelative)) {
2371
+ throw new Error('test-scenarios --input must be a project-relative file');
2372
+ }
2373
+ let input;
2374
+ try {
2375
+ input = JSON.parse(readFileSync(safeInputPath, 'utf8'));
2376
+ } catch (error) {
2377
+ throw new Error(`Cannot read test-scenario input: ${error.message}`);
2378
+ }
2379
+ const result = recordPersonaTestScenarios(project, args[2] ?? '', input, {
2380
+ reviewedBy,
2381
+ personas: listPersonas(availablePersonaRoots(project))
2382
+ });
2383
+ print(result, json);
2384
+ return;
2385
+ }
2386
+
2387
+ if (command === 'test-scenarios' && subcommand === 'status') {
2388
+ const project = selectedProject(args);
2389
+ const result = readPersonaTestScenarioWorkspace(project, args[2] ?? '', {
2390
+ personas: listPersonas(availablePersonaRoots(project))
2391
+ });
2392
+ print(result, json);
2393
+ return;
2394
+ }
2395
+
2396
+ throw new Error(`Unknown command: ${args.join(' ')}`);
2397
+ } catch (error) {
2398
+ if (command !== 'error-report') {
2399
+ try {
2400
+ const projectRoot = selectedProject(args);
2401
+ const commandIdentity = automaticErrorCommand(args);
2402
+ captureErrorReportFailure(projectRoot, {
2403
+ capability: 'cli',
2404
+ errorCode: 'EWAI-CLI-FAILED',
2405
+ command: commandIdentity,
2406
+ ewaiVersion: JSON.parse(readFileSync(resolve(installRoot, 'package.json'), 'utf8')).version,
2407
+ nodeVersion: process.versions.node,
2408
+ osClass: ['darwin', 'linux', 'win32'].includes(process.platform) ? process.platform : 'other',
2409
+ installationSource: 'npm',
2410
+ title: 'An EWAI command did not complete',
2411
+ expected: 'The requested EWAI command completes.',
2412
+ actual: 'The command returned an error. The original error was shown separately and is not copied into this report.',
2413
+ reproductionSteps: [`Run ${commandIdentity}`],
2414
+ });
2415
+ } catch {
2416
+ // Reporting must never replace or mask the original command failure.
2417
+ }
2418
+ }
2419
+ const suppliedRolloutProject = option(args, '--project');
2420
+ const rolloutError = command === 'rollout'
2421
+ ? { error: suppliedRolloutProject ? String(error.message).replaceAll(suppliedRolloutProject, '<project>') : String(error.message), notices: { advisory: ROLLOUT_ADVISORY_NOTICE, security: ROLLOUT_ASSURANCE_NOTICE } }
2422
+ : null;
2423
+ if (json) console.error(JSON.stringify(command === 'security' ? { error: error.message, assurance_notice: ASSURANCE_NOTICE } : command === 'readiness' ? { error: error.message, notices: { security: ASSURANCE_NOTICE, authority: READINESS_AUTHORITY_NOTICE } } : command === 'policy' ? { error: error.message, authority_notice: POLICY_DESIGN_AUTHORITY_NOTICE } : command === 'meeting' ? { error: error.message, assurance_notice: MEETING_EVIDENCE_DISCLAIMER } : command === 'knowledge' ? { error: error.message, assurance_notice: KNOWLEDGE_PROPOSALS_DISCLAIMER } : rolloutError ?? { error: error.message }));
2424
+ else console.error(command === 'security' ? `${ASSURANCE_NOTICE}\n\nError: ${error.message}` : command === 'readiness' ? `${ASSURANCE_NOTICE}\n${READINESS_AUTHORITY_NOTICE}\n\nError: ${error.message}` : command === 'policy' ? `${POLICY_DESIGN_AUTHORITY_NOTICE}\n\nError: ${error.message}` : command === 'meeting' ? `${MEETING_EVIDENCE_DISCLAIMER}\n\nError: ${error.message}` : command === 'knowledge' ? `${KNOWLEDGE_PROPOSALS_DISCLAIMER}\n\nError: ${error.message}` : command === 'rollout' ? `${ROLLOUT_ADVISORY_NOTICE}\n${ROLLOUT_ASSURANCE_NOTICE}\n\nError: ${rolloutError.error}` : `Error: ${error.message}`);
2425
+ process.exitCode = 1;
2426
+ }
2427
+ }