@cohortapp/agent-sdk 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (731) hide show
  1. package/.claude/commands/init-agent.md +104 -0
  2. package/.claude/commands/init-maestro.md +1187 -0
  3. package/.claude/settings.json +161 -0
  4. package/.env.example +216 -0
  5. package/README.md +632 -0
  6. package/agents/browser-operator/agent.md +52 -0
  7. package/agents/calendar-ops/agent.md +50 -0
  8. package/agents/communications/agent.md +96 -0
  9. package/agents/decision-log/agent.md +65 -0
  10. package/agents/desktop-operator/agent.md +59 -0
  11. package/agents/gmail-operator/agent.md +62 -0
  12. package/agents/inbound-dispatcher/agent.md +66 -0
  13. package/agents/inbox-processor/agent.md +39 -0
  14. package/agents/pmo-execution/agent.md +60 -0
  15. package/agents/session-spawner/agent.md +64 -0
  16. package/agents/slack-operator/agent.md +60 -0
  17. package/agents/whatsapp-operator/agent.md +60 -0
  18. package/agents/workflow-automation/agent.md +61 -0
  19. package/archetypes/altitudes/c-suite.yaml +58 -0
  20. package/archetypes/altitudes/founder.yaml +68 -0
  21. package/archetypes/altitudes/senior-manager.yaml +63 -0
  22. package/archetypes/altitudes/svp.yaml +60 -0
  23. package/archetypes/altitudes/vp.yaml +50 -0
  24. package/archetypes/archetype.schema.json +77 -0
  25. package/archetypes/base.yaml +47 -0
  26. package/archetypes/capabilities/commercial-leader.yaml +159 -0
  27. package/archetypes/capabilities/compliance-officer.yaml +159 -0
  28. package/archetypes/capabilities/executive-operator.yaml +169 -0
  29. package/archetypes/capabilities/finance-leader.yaml +162 -0
  30. package/archetypes/capabilities/operations-leader.yaml +154 -0
  31. package/archetypes/capabilities/product-leader.yaml +148 -0
  32. package/archetypes/capabilities/technical-leader.yaml +146 -0
  33. package/archetypes/functions/commercial-leader.yaml +62 -0
  34. package/archetypes/functions/compliance-officer.yaml +64 -0
  35. package/archetypes/functions/executive-operator.yaml +70 -0
  36. package/archetypes/functions/finance-leader.yaml +70 -0
  37. package/archetypes/functions/operations-leader.yaml +62 -0
  38. package/archetypes/functions/product-leader.yaml +61 -0
  39. package/archetypes/functions/technical-leader.yaml +57 -0
  40. package/bin/cohort-mcp.mjs +81 -0
  41. package/bin/maestro.mjs +3516 -0
  42. package/bin/maestro.test.mjs +1015 -0
  43. package/desktop-control/README.md +56 -0
  44. package/desktop-control/app-profiles/gmail.yaml +120 -0
  45. package/desktop-control/app-profiles/slack.yaml +315 -0
  46. package/desktop-control/app-profiles/whatsapp.yaml +107 -0
  47. package/docs/architecture/agent-topology.md +2239 -0
  48. package/docs/architecture/archetype-agent-factory.md +110 -0
  49. package/docs/architecture/collective-memory-and-org-mesh.md +115 -0
  50. package/docs/architecture/continuous-monitoring.md +221 -0
  51. package/docs/architecture/mcp-capability-map.md +585 -0
  52. package/docs/architecture/system-architecture.md +1272 -0
  53. package/docs/company-context/README.md +40 -0
  54. package/docs/guides/agent-persona-setup.md +600 -0
  55. package/docs/guides/agents-observe-setup.md +64 -0
  56. package/docs/guides/billing-console-keys.md +88 -0
  57. package/docs/guides/ccxray-diagnostics.md +65 -0
  58. package/docs/guides/channel-bus.md +127 -0
  59. package/docs/guides/claude-mem-setup.md +79 -0
  60. package/docs/guides/claude-pace-setup.md +56 -0
  61. package/docs/guides/claudraband-sessions.md +98 -0
  62. package/docs/guides/clawteam-swarm.md +116 -0
  63. package/docs/guides/code-review-graph-setup.md +86 -0
  64. package/docs/guides/email-setup.md +431 -0
  65. package/docs/guides/mac-mini.md +119 -0
  66. package/docs/guides/media-generation-setup.md +349 -0
  67. package/docs/guides/model-routing.md +162 -0
  68. package/docs/guides/observability-otel.md +265 -0
  69. package/docs/guides/org-onboarding.md +132 -0
  70. package/docs/guides/outbound-governance-setup.md +437 -0
  71. package/docs/guides/pdf-generation-setup.md +315 -0
  72. package/docs/guides/poller-daemon-setup.md +563 -0
  73. package/docs/guides/rag-context-setup.md +459 -0
  74. package/docs/guides/self-optimization-pattern.md +82 -0
  75. package/docs/guides/setup-wizard.md +178 -0
  76. package/docs/guides/slack-setup.md +350 -0
  77. package/docs/guides/telegram-setup.md +227 -0
  78. package/docs/guides/twilio-subaccounts-setup.md +223 -0
  79. package/docs/guides/verification.md +128 -0
  80. package/docs/guides/voice-mode.md +188 -0
  81. package/docs/guides/voice-sms-setup.md +698 -0
  82. package/docs/guides/webhook-relay-setup.md +349 -0
  83. package/docs/guides/whatsapp-setup.md +288 -0
  84. package/docs/prompts/board-pack-cover-template.md +36 -0
  85. package/docs/prompts/decision-recommendation-template.md +88 -0
  86. package/docs/prompts/followup-message-template.md +141 -0
  87. package/docs/prompts/investor-letter-template.md +52 -0
  88. package/docs/prompts/morning-brief-template.md +82 -0
  89. package/docs/prompts/presentation-template.md +58 -0
  90. package/docs/prompts/weekly-strategic-memo-template.md +104 -0
  91. package/docs/research/hallucinated-tool-output-investigation.md +151 -0
  92. package/docs/runbooks/backup-restore.md +205 -0
  93. package/docs/runbooks/cohort-cutover.md +129 -0
  94. package/docs/runbooks/fleet-operations.md +200 -0
  95. package/docs/runbooks/incident-response.md +226 -0
  96. package/docs/runbooks/mac-mini-bootstrap.md +431 -0
  97. package/docs/runbooks/perpetual-operations.md +509 -0
  98. package/docs/runbooks/recovery-and-failover.md +260 -0
  99. package/framework-features.json +267 -0
  100. package/ingest/README.md +87 -0
  101. package/lib/action-executor.js +689 -0
  102. package/lib/action-executor.test.mjs +871 -0
  103. package/lib/agent-root.mjs +37 -0
  104. package/lib/archetype.mjs +236 -0
  105. package/lib/archetype.test.mjs +132 -0
  106. package/lib/autonomy.mjs +114 -0
  107. package/lib/autonomy.test.mjs +66 -0
  108. package/lib/backlog.mjs +358 -0
  109. package/lib/backlog.test.mjs +266 -0
  110. package/lib/budget-guard.mjs +279 -0
  111. package/lib/budget-guard.test.mjs +291 -0
  112. package/lib/cadence-bus-schedule.test.mjs +194 -0
  113. package/lib/cadence-bus.mjs +1120 -0
  114. package/lib/cadence-bus.test.mjs +720 -0
  115. package/lib/cadences.mjs +205 -0
  116. package/lib/cadences.test.mjs +125 -0
  117. package/lib/capability.mjs +154 -0
  118. package/lib/capability.test.mjs +78 -0
  119. package/lib/channels/base-adapter.mjs +719 -0
  120. package/lib/channels/base-adapter.test.mjs +590 -0
  121. package/lib/channels/channel.mjs +128 -0
  122. package/lib/channels/channels.test.mjs +371 -0
  123. package/lib/channels/contract.mjs +215 -0
  124. package/lib/channels/contract.test.mjs +137 -0
  125. package/lib/channels/conversation-resolver.mjs +95 -0
  126. package/lib/channels/gmail/adapter.mjs +87 -0
  127. package/lib/channels/inbox-item.mjs +255 -0
  128. package/lib/channels/inbox-item.test.mjs +335 -0
  129. package/lib/channels/index.mjs +94 -0
  130. package/lib/channels/orgmail/adapter.mjs +353 -0
  131. package/lib/channels/orgmail/adapter.test.mjs +311 -0
  132. package/lib/channels/pairing.mjs +363 -0
  133. package/lib/channels/pairing.test.mjs +270 -0
  134. package/lib/channels/registry.mjs +164 -0
  135. package/lib/channels/slack/adapter.mjs +317 -0
  136. package/lib/channels/slack-adapter.test.mjs +212 -0
  137. package/lib/channels/sms/adapter.mjs +43 -0
  138. package/lib/channels/telegram/adapter.mjs +432 -0
  139. package/lib/channels/telegram-adapter.test.mjs +306 -0
  140. package/lib/channels/voice/adapter.mjs +301 -0
  141. package/lib/channels/voice/adapter.test.mjs +278 -0
  142. package/lib/channels/whatsapp/adapter-baileys.mjs +587 -0
  143. package/lib/channels/whatsapp/adapter-baileys.test.mjs +359 -0
  144. package/lib/channels/whatsapp/adapter-twilio.mjs +65 -0
  145. package/lib/channels/whatsapp/baileys-typing.test.mjs +154 -0
  146. package/lib/charter.mjs +256 -0
  147. package/lib/charter.test.mjs +89 -0
  148. package/lib/claude-bin.mjs +134 -0
  149. package/lib/claude-bin.test.mjs +75 -0
  150. package/lib/collective/capture.mjs +185 -0
  151. package/lib/collective/capture.test.mjs +121 -0
  152. package/lib/collective/cards.mjs +201 -0
  153. package/lib/collective/cards.test.mjs +114 -0
  154. package/lib/collective/config.mjs +186 -0
  155. package/lib/collective/config.test.mjs +123 -0
  156. package/lib/collective/global-config.mjs +113 -0
  157. package/lib/collective/global-config.test.mjs +75 -0
  158. package/lib/collective/presence.mjs +201 -0
  159. package/lib/collective/presence.test.mjs +95 -0
  160. package/lib/collective/recall.mjs +215 -0
  161. package/lib/collective/recall.test.mjs +116 -0
  162. package/lib/comms/send-gate.mjs +554 -0
  163. package/lib/comms/send-gate.test.mjs +577 -0
  164. package/lib/comms.mjs +67 -0
  165. package/lib/comms.test.mjs +41 -0
  166. package/lib/diagnostics/alerts.mjs +424 -0
  167. package/lib/diagnostics/alerts.test.mjs +318 -0
  168. package/lib/diagnostics/backup-freshness.mjs +188 -0
  169. package/lib/diagnostics/backup-freshness.test.mjs +185 -0
  170. package/lib/diagnostics/counters.mjs +269 -0
  171. package/lib/diagnostics/counters.test.mjs +206 -0
  172. package/lib/diagnostics/events.mjs +188 -0
  173. package/lib/diagnostics/events.test.mjs +290 -0
  174. package/lib/diagnostics/otel.mjs +237 -0
  175. package/lib/diagnostics/otel.test.mjs +196 -0
  176. package/lib/diagnostics/trace.mjs +216 -0
  177. package/lib/diagnostics/trace.test.mjs +251 -0
  178. package/lib/env-compat.mjs +74 -0
  179. package/lib/env-compat.test.mjs +104 -0
  180. package/lib/feature-init.mjs +331 -0
  181. package/lib/fs-atomic.mjs +112 -0
  182. package/lib/fs-atomic.test.mjs +72 -0
  183. package/lib/fs-ownership.mjs +111 -0
  184. package/lib/fs-ownership.test.mjs +158 -0
  185. package/lib/hooks/bus.mjs +347 -0
  186. package/lib/hooks/bus.test.mjs +387 -0
  187. package/lib/index.js +16 -0
  188. package/lib/learning/config.mjs +106 -0
  189. package/lib/learning/config.test.mjs +75 -0
  190. package/lib/learning/counters.mjs +156 -0
  191. package/lib/learning/counters.test.mjs +69 -0
  192. package/lib/learning/curator-consolidate.test.mjs +238 -0
  193. package/lib/learning/curator.mjs +453 -0
  194. package/lib/learning/curator.test.mjs +106 -0
  195. package/lib/learning/log.mjs +40 -0
  196. package/lib/learning/reflect.mjs +534 -0
  197. package/lib/learning/reflect.test.mjs +0 -0
  198. package/lib/learning/session-index.mjs +352 -0
  199. package/lib/learning/session-index.test.mjs +125 -0
  200. package/lib/learning/skill-writer.mjs +474 -0
  201. package/lib/learning/skill-writer.test.mjs +210 -0
  202. package/lib/mcp/server.mjs +328 -0
  203. package/lib/mcp/server.test.mjs +400 -0
  204. package/lib/model-router/auth-profiles.mjs +758 -0
  205. package/lib/model-router/auth-profiles.test.mjs +580 -0
  206. package/lib/model-router/catalog/anthropic.yaml +153 -0
  207. package/lib/model-router/catalog/deepseek.yaml +86 -0
  208. package/lib/model-router/catalog/moonshot.yaml +81 -0
  209. package/lib/model-router/catalog/qwen.yaml +114 -0
  210. package/lib/model-router/catalog.mjs +925 -0
  211. package/lib/model-router/catalog.test.mjs +385 -0
  212. package/lib/model-router/economics.mjs +564 -0
  213. package/lib/model-router/economics.test.mjs +344 -0
  214. package/lib/model-router/failover.mjs +298 -0
  215. package/lib/model-router/failover.test.mjs +439 -0
  216. package/lib/model-router/health.mjs +453 -0
  217. package/lib/model-router/health.test.mjs +338 -0
  218. package/lib/model-router/integration-coverage.test.mjs +829 -0
  219. package/lib/model-router/integration.test.mjs +564 -0
  220. package/lib/model-router/ledger.mjs +402 -0
  221. package/lib/model-router/ledger.test.mjs +382 -0
  222. package/lib/model-router/llm-task.mjs +515 -0
  223. package/lib/model-router/llm-task.test.mjs +392 -0
  224. package/lib/model-router/org-credentials.mjs +260 -0
  225. package/lib/model-router/org-credentials.test.mjs +265 -0
  226. package/lib/model-router/pricing-refresh.mjs +463 -0
  227. package/lib/model-router/pricing-refresh.test.mjs +286 -0
  228. package/lib/model-router/reconcile.mjs +429 -0
  229. package/lib/model-router/reconcile.test.mjs +316 -0
  230. package/lib/model-router/repair.mjs +471 -0
  231. package/lib/model-router/repair.test.mjs +180 -0
  232. package/lib/model-router/resolve.mjs +1206 -0
  233. package/lib/model-router/spawn.mjs +497 -0
  234. package/lib/model-router/spawn.test.mjs +425 -0
  235. package/lib/model-router/taxonomy.mjs +893 -0
  236. package/lib/model-router/taxonomy.test.mjs +410 -0
  237. package/lib/model-router.mjs +677 -0
  238. package/lib/model-router.test.mjs +907 -0
  239. package/lib/org/activity.mjs +211 -0
  240. package/lib/org/activity.test.mjs +134 -0
  241. package/lib/org/approvals.mjs +448 -0
  242. package/lib/org/approvals.test.mjs +216 -0
  243. package/lib/org/awareness.mjs +222 -0
  244. package/lib/org/awareness.test.mjs +159 -0
  245. package/lib/org/board.mjs +229 -0
  246. package/lib/org/board.test.mjs +177 -0
  247. package/lib/org/bootstrap-context.mjs +169 -0
  248. package/lib/org/bootstrap-context.test.mjs +153 -0
  249. package/lib/org/client.mjs +1628 -0
  250. package/lib/org/client.test.mjs +1107 -0
  251. package/lib/org/cohort-client.mjs +67 -0
  252. package/lib/org/cohort-client.test.mjs +126 -0
  253. package/lib/org/cost-sync.mjs +227 -0
  254. package/lib/org/cost-sync.test.mjs +153 -0
  255. package/lib/org/doctor.mjs +212 -0
  256. package/lib/org/doctor.test.mjs +212 -0
  257. package/lib/org/handoff.mjs +293 -0
  258. package/lib/org/handoff.test.mjs +269 -0
  259. package/lib/org/integration-tools.mjs +182 -0
  260. package/lib/org/integration-tools.test.mjs +160 -0
  261. package/lib/org/keys.mjs +131 -0
  262. package/lib/org/keys.test.mjs +92 -0
  263. package/lib/org/knowledge.mjs +463 -0
  264. package/lib/org/knowledge.test.mjs +319 -0
  265. package/lib/org/leases.mjs +335 -0
  266. package/lib/org/leases.test.mjs +235 -0
  267. package/lib/org/mesh-integration.test.mjs +127 -0
  268. package/lib/org/mesh.mjs +459 -0
  269. package/lib/org/mesh.test.mjs +345 -0
  270. package/lib/org/messaging.mjs +503 -0
  271. package/lib/org/messaging.test.mjs +238 -0
  272. package/lib/org/policy.mjs +345 -0
  273. package/lib/org/policy.test.mjs +237 -0
  274. package/lib/org/protocol.checksum +1 -0
  275. package/lib/org/protocol.checksum.test.mjs +90 -0
  276. package/lib/org/protocol.mjs +967 -0
  277. package/lib/org/protocol.test.mjs +264 -0
  278. package/lib/org/registry.mjs +194 -0
  279. package/lib/org/registry.test.mjs +100 -0
  280. package/lib/org/tool-surface-integration.test.mjs +120 -0
  281. package/lib/org/tool-surface.mjs +2535 -0
  282. package/lib/org/tool-surface.test.mjs +589 -0
  283. package/lib/org/ui-parity.mjs +3236 -0
  284. package/lib/org/ui-parity.test.mjs +348 -0
  285. package/lib/org/verify.mjs +176 -0
  286. package/lib/org/verify.test.mjs +194 -0
  287. package/lib/rag/embed.mjs +188 -0
  288. package/lib/rag/indexer.mjs +425 -0
  289. package/lib/rag/rag.test.mjs +505 -0
  290. package/lib/rag/search.mjs +475 -0
  291. package/lib/rate-guard.mjs +246 -0
  292. package/lib/rate-guard.test.mjs +201 -0
  293. package/lib/render.mjs +112 -0
  294. package/lib/render.test.mjs +68 -0
  295. package/lib/resource-governor.mjs +297 -0
  296. package/lib/resource-governor.test.mjs +262 -0
  297. package/lib/scheduling/dynamic-jobs.mjs +675 -0
  298. package/lib/scheduling/dynamic-jobs.test.mjs +344 -0
  299. package/lib/scheduling/jitter.mjs +0 -0
  300. package/lib/scheduling/jitter.test.mjs +140 -0
  301. package/lib/secrets/broker.mjs +315 -0
  302. package/lib/secrets/broker.test.mjs +280 -0
  303. package/lib/secrets/providers.mjs +461 -0
  304. package/lib/secrets/providers.test.mjs +274 -0
  305. package/lib/security/audit-engine.mjs +684 -0
  306. package/lib/security/audit-engine.test.mjs +389 -0
  307. package/lib/security/coerce-args.mjs +552 -0
  308. package/lib/security/coerce-args.test.mjs +281 -0
  309. package/lib/security/dangerous-tools.mjs +97 -0
  310. package/lib/security/dangerous-tools.test.mjs +68 -0
  311. package/lib/security/external-content.mjs +145 -0
  312. package/lib/security/external-content.test.mjs +67 -0
  313. package/lib/security/redact.mjs +592 -0
  314. package/lib/security/redact.test.mjs +441 -0
  315. package/lib/security/secret-equal.mjs +73 -0
  316. package/lib/security/secret-equal.test.mjs +55 -0
  317. package/lib/session-permissions.mjs +101 -0
  318. package/lib/session-permissions.test.mjs +100 -0
  319. package/lib/setup/claude-probe.mjs +74 -0
  320. package/lib/setup/completeness.mjs +175 -0
  321. package/lib/setup/completeness.test.mjs +110 -0
  322. package/lib/setup/context-pack.mjs +173 -0
  323. package/lib/setup/context-pack.test.mjs +89 -0
  324. package/lib/setup/enrich.mjs +277 -0
  325. package/lib/setup/enrich.test.mjs +115 -0
  326. package/lib/setup/enroll-from-cohort.mjs +441 -0
  327. package/lib/setup/enroll-from-cohort.test.mjs +233 -0
  328. package/lib/setup/integration.test.mjs +162 -0
  329. package/lib/setup/io.mjs +360 -0
  330. package/lib/setup/io.test.mjs +77 -0
  331. package/lib/setup/run-generator.mjs +81 -0
  332. package/lib/setup/runner.mjs +244 -0
  333. package/lib/setup/runner.test.mjs +132 -0
  334. package/lib/setup/sections/comms.mjs +173 -0
  335. package/lib/setup/sections/company.mjs +120 -0
  336. package/lib/setup/sections/enrich.mjs +138 -0
  337. package/lib/setup/sections/identity.mjs +182 -0
  338. package/lib/setup/sections/identity.test.mjs +140 -0
  339. package/lib/setup/sections/learning.mjs +153 -0
  340. package/lib/setup/sections/learning.test.mjs +81 -0
  341. package/lib/setup/sections/messaging.mjs +219 -0
  342. package/lib/setup/sections/messaging.test.mjs +127 -0
  343. package/lib/setup/sections/model.mjs +102 -0
  344. package/lib/setup/sections/operating-model.mjs +78 -0
  345. package/lib/setup/sections/org.mjs +475 -0
  346. package/lib/setup/sections/org.test.mjs +313 -0
  347. package/lib/setup/sections/orgmail.mjs +173 -0
  348. package/lib/setup/sections/orgmail.test.mjs +118 -0
  349. package/lib/setup/sections/recovery.mjs +159 -0
  350. package/lib/setup/sections/recovery.test.mjs +98 -0
  351. package/lib/setup/sections/tools.mjs +132 -0
  352. package/lib/setup/sections/verify.mjs +97 -0
  353. package/lib/setup/sot.mjs +205 -0
  354. package/lib/setup/sot.test.mjs +81 -0
  355. package/lib/setup/state.mjs +151 -0
  356. package/lib/setup/state.test.mjs +92 -0
  357. package/lib/singleton.js +229 -0
  358. package/lib/singleton.test.mjs +135 -0
  359. package/lib/telemetry/alerts.mjs +216 -0
  360. package/lib/telemetry/alerts.test.mjs +109 -0
  361. package/lib/telemetry/collect.mjs +512 -0
  362. package/lib/telemetry/collect.test.mjs +202 -0
  363. package/lib/tool-definitions-integration.test.mjs +83 -0
  364. package/lib/tool-definitions.js +738 -0
  365. package/lib/tool-definitions.test.mjs +437 -0
  366. package/lib/util/fetch-timeout.mjs +136 -0
  367. package/lib/util/fetch-timeout.test.mjs +202 -0
  368. package/lib/util/reconnect.mjs +343 -0
  369. package/lib/util/reconnect.test.mjs +369 -0
  370. package/lib/util/unhandled.mjs +205 -0
  371. package/lib/util/unhandled.test.mjs +216 -0
  372. package/lib/voice/context-loader.mjs +466 -0
  373. package/lib/voice/index.mjs +100 -0
  374. package/lib/voice/openai-realtime.mjs +510 -0
  375. package/lib/voice/outbound.mjs +542 -0
  376. package/lib/voice/outbound.test.mjs +69 -0
  377. package/lib/voice/post-call-brief.mjs +428 -0
  378. package/lib/voice/provider.mjs +52 -0
  379. package/lib/voice/session-rotation.mjs +257 -0
  380. package/lib/voice/stt.mjs +161 -0
  381. package/lib/voice/stt.test.mjs +226 -0
  382. package/lib/voice/tool-bridge.mjs +370 -0
  383. package/lib/voice/tts.mjs +104 -0
  384. package/lib/voice/twilio-sip-bridge.mjs +288 -0
  385. package/lib/voice/voice.test.mjs +990 -0
  386. package/mcp/README.md +80 -0
  387. package/package.json +151 -0
  388. package/plugins/maestro-skills/plugin.json +139 -0
  389. package/plugins/maestro-skills/skills/agents-observe.md +110 -0
  390. package/plugins/maestro-skills/skills/board-deck.md +68 -0
  391. package/plugins/maestro-skills/skills/books-close.md +77 -0
  392. package/plugins/maestro-skills/skills/brand-steward.md +121 -0
  393. package/plugins/maestro-skills/skills/calendar-plan.md +57 -0
  394. package/plugins/maestro-skills/skills/call-working-sessions.md +124 -0
  395. package/plugins/maestro-skills/skills/ccxray-diagnostics.md +91 -0
  396. package/plugins/maestro-skills/skills/claude-pace.md +61 -0
  397. package/plugins/maestro-skills/skills/code-review-graph.md +99 -0
  398. package/plugins/maestro-skills/skills/crm-pipeline.md +65 -0
  399. package/plugins/maestro-skills/skills/decision-brief.md +89 -0
  400. package/plugins/maestro-skills/skills/directory-hygiene.md +125 -0
  401. package/plugins/maestro-skills/skills/draft-comms.md +84 -0
  402. package/plugins/maestro-skills/skills/evening-wrap.md +53 -0
  403. package/plugins/maestro-skills/skills/files-find.md +65 -0
  404. package/plugins/maestro-skills/skills/generative-ui.md +228 -0
  405. package/plugins/maestro-skills/skills/hiring-triage.md +74 -0
  406. package/plugins/maestro-skills/skills/inbox-triage.md +61 -0
  407. package/plugins/maestro-skills/skills/mail-triage.md +86 -0
  408. package/plugins/maestro-skills/skills/morning-brief.md +54 -0
  409. package/plugins/maestro-skills/skills/native-artifacts.md +157 -0
  410. package/plugins/maestro-skills/skills/org-board.md +133 -0
  411. package/plugins/maestro-skills/skills/org-credential.md +68 -0
  412. package/plugins/maestro-skills/skills/org-recall.md +81 -0
  413. package/plugins/maestro-skills/skills/pipeline-review.md +76 -0
  414. package/plugins/maestro-skills/skills/regulatory-status.md +81 -0
  415. package/plugins/maestro-skills/skills/router-why.md +78 -0
  416. package/plugins/maestro-skills/skills/schedule-meeting.md +91 -0
  417. package/plugins/maestro-skills/skills/session-search.md +71 -0
  418. package/plugins/maestro-skills/skills/set-reminder.md +93 -0
  419. package/plugins/maestro-skills/skills/slack-followup.md +64 -0
  420. package/plugins/maestro-skills/skills/team-activity.md +86 -0
  421. package/plugins/maestro-skills/skills/weekly-memo.md +70 -0
  422. package/policies/action-classification.yaml +114 -0
  423. package/policies/ai-disclosure.yaml +294 -0
  424. package/policies/communication-style.md +139 -0
  425. package/policies/information-barriers.yaml +118 -0
  426. package/policies/prompt-injection-defence.yaml +138 -0
  427. package/public/assets/icon-dark.png +0 -0
  428. package/public/assets/icon-dark.svg +9 -0
  429. package/public/assets/icon-light.svg +9 -0
  430. package/public/assets/logo-dark.svg +15 -0
  431. package/public/assets/logo-light.svg +15 -0
  432. package/scaffold/.mcp.json +7 -0
  433. package/scaffold/CLAUDE.md +368 -0
  434. package/scaffold/config/agent.json +55 -0
  435. package/scaffold/config/agent.ts +76 -0
  436. package/scaffold/config/agent.ts.example +89 -0
  437. package/scaffold/config/alerts.yaml +23 -0
  438. package/scaffold/config/allowlist.yaml.example +25 -0
  439. package/scaffold/config/caller-id-map.yaml +46 -0
  440. package/scaffold/config/collective.yaml +49 -0
  441. package/scaffold/config/company.json +20 -0
  442. package/scaffold/config/known-agents.json +6 -0
  443. package/scaffold/config/learning.yaml +55 -0
  444. package/scaffold/config/model-routing.yaml.example +104 -0
  445. package/scaffold/config/org.yaml +25 -0
  446. package/scaffold/config/orgmail.yaml.example +19 -0
  447. package/scaffold/config/recovery.yaml +72 -0
  448. package/scaffold/config/secrets.yaml +27 -0
  449. package/scaffold/config/slack.yaml.example +35 -0
  450. package/scaffold/config/telegram.yaml.example +38 -0
  451. package/scaffold/config/voice.yaml.example +89 -0
  452. package/scaffold/config/whatsapp.yaml.example +39 -0
  453. package/schedules/README.md +49 -0
  454. package/schedules/triggers/backlog-executor.md +102 -0
  455. package/schedules/triggers/brand-steward.md +72 -0
  456. package/schedules/triggers/daily-evening-wrap.md +159 -0
  457. package/schedules/triggers/daily-midday-sweep.md +58 -0
  458. package/schedules/triggers/daily-morning-brief.md +55 -0
  459. package/schedules/triggers/directory-hygiene.md +81 -0
  460. package/schedules/triggers/dynamic-jobs.md +40 -0
  461. package/schedules/triggers/inbox-processor.md +115 -0
  462. package/schedules/triggers/meeting-action-capture.md +60 -0
  463. package/schedules/triggers/meeting-prep.md +69 -0
  464. package/schedules/triggers/messaging-inbound.md +50 -0
  465. package/schedules/triggers/org-pulse.md +24 -0
  466. package/schedules/triggers/quarterly-self-assessment.md +54 -0
  467. package/schedules/triggers/weekly-engineering-health.md +37 -0
  468. package/schedules/triggers/weekly-execution.md +65 -0
  469. package/schedules/triggers/weekly-hiring.md +53 -0
  470. package/schedules/triggers/weekly-priorities.md +38 -0
  471. package/schedules/triggers/weekly-strategic-memo.md +124 -0
  472. package/scripts/archive-email.sh +55 -0
  473. package/scripts/cadence/cadence-status.mjs +36 -0
  474. package/scripts/cadence/enqueue-cadence-tick.mjs +174 -0
  475. package/scripts/cadence/enqueue-cadence-tick.test.mjs +187 -0
  476. package/scripts/cadence/launchd-cadence-wrapper.sh +85 -0
  477. package/scripts/cadence/launchd-cloud-relay-wrapper.sh +95 -0
  478. package/scripts/cadence/launchd-socket-mode-wrapper.sh +95 -0
  479. package/scripts/ci/check-docs-accuracy.mjs +493 -0
  480. package/scripts/ci/check-docs-accuracy.test.mjs +409 -0
  481. package/scripts/ci/check-exports-exist.mjs +140 -0
  482. package/scripts/ci/check-files-exist.mjs +107 -0
  483. package/scripts/ci/check-no-build-artifacts.mjs +111 -0
  484. package/scripts/ci/check-no-build-artifacts.test.mjs +71 -0
  485. package/scripts/ci/check-no-confidential.mjs +198 -0
  486. package/scripts/ci/check-no-conflict-markers.mjs +169 -0
  487. package/scripts/ci/check-no-residual-identity.mjs +163 -0
  488. package/scripts/ci/check-no-residual-identity.test.mjs +89 -0
  489. package/scripts/ci/check-tarball-fidelity.mjs +205 -0
  490. package/scripts/ci/check-unresolved-tokens.mjs +83 -0
  491. package/scripts/ci/check.mjs +109 -0
  492. package/scripts/ci/check.test.mjs +194 -0
  493. package/scripts/ci/run-coverage.mjs +82 -0
  494. package/scripts/ci/run-tests.mjs +71 -0
  495. package/scripts/cloud-relay/README.md +59 -0
  496. package/scripts/cloud-relay/index.mjs +233 -0
  497. package/scripts/cloud-relay/package.json +15 -0
  498. package/scripts/cloud-relay/railway.json +13 -0
  499. package/scripts/cloud-relay/voice/README.md +94 -0
  500. package/scripts/cloud-relay/voice/package-lock.json +39 -0
  501. package/scripts/cloud-relay/voice/package.json +16 -0
  502. package/scripts/cloud-relay/voice/railway.json +13 -0
  503. package/scripts/cloud-relay/voice/server.mjs +532 -0
  504. package/scripts/collective/hook-runner.mjs +211 -0
  505. package/scripts/collective/hook-runner.test.mjs +90 -0
  506. package/scripts/collective/org-pulse.mjs +72 -0
  507. package/scripts/collective/org-sync.mjs +61 -0
  508. package/scripts/collective/recall.mjs +45 -0
  509. package/scripts/collective/who.mjs +30 -0
  510. package/scripts/comms-monitor.sh +288 -0
  511. package/scripts/configure-whatsapp-sandbox.sh +201 -0
  512. package/scripts/continuous-monitor.sh +91 -0
  513. package/scripts/cost/fleet-digest.mjs +407 -0
  514. package/scripts/cost/fleet-digest.test.mjs +207 -0
  515. package/scripts/cost/track-claude-usage.mjs +169 -0
  516. package/scripts/daemon/agent-daemon.mjs +989 -0
  517. package/scripts/daemon/agent-daemon.test.mjs +525 -0
  518. package/scripts/daemon/cadence-consumer-governance.test.mjs +220 -0
  519. package/scripts/daemon/cadence-consumer.mjs +1080 -0
  520. package/scripts/daemon/cadence-consumer.test.mjs +770 -0
  521. package/scripts/daemon/cadence-handlers.mjs +1121 -0
  522. package/scripts/daemon/cadence-handlers.test.mjs +617 -0
  523. package/scripts/daemon/classifier.mjs +704 -0
  524. package/scripts/daemon/classifier.test.mjs +238 -0
  525. package/scripts/daemon/classify-kind.mjs +54 -0
  526. package/scripts/daemon/classify-kind.test.mjs +40 -0
  527. package/scripts/daemon/context-compiler.mjs +605 -0
  528. package/scripts/daemon/context-compiler.test.mjs +300 -0
  529. package/scripts/daemon/dispatcher-cooldown.test.mjs +122 -0
  530. package/scripts/daemon/dispatcher-governance.test.mjs +886 -0
  531. package/scripts/daemon/dispatcher.mjs +1516 -0
  532. package/scripts/daemon/health.mjs +72 -0
  533. package/scripts/daemon/inbox-deferral.mjs +210 -0
  534. package/scripts/daemon/inbox-deferral.test.mjs +242 -0
  535. package/scripts/daemon/integration.test.mjs +149 -0
  536. package/scripts/daemon/launchd-wrapper-generic.sh +96 -0
  537. package/scripts/daemon/launchd-wrapper-slack-events.sh +37 -0
  538. package/scripts/daemon/launchd-wrapper.sh +91 -0
  539. package/scripts/daemon/lib/session-router.mjs +274 -0
  540. package/scripts/daemon/lib/session-router.test.mjs +295 -0
  541. package/scripts/daemon/maestro-daemon.mjs +275 -0
  542. package/scripts/daemon/prompt-builder.mjs +685 -0
  543. package/scripts/daemon/prompt-builder.test.mjs +213 -0
  544. package/scripts/daemon/responder.mjs +854 -0
  545. package/scripts/daemon/session-lock.mjs +721 -0
  546. package/scripts/daemon/session-lock.test.mjs +252 -0
  547. package/scripts/daemon/session-outcomes.mjs +640 -0
  548. package/scripts/daemon/session-outcomes.test.mjs +533 -0
  549. package/scripts/daemon/typing-registry.mjs +90 -0
  550. package/scripts/daemon/typing-registry.test.mjs +77 -0
  551. package/scripts/daemon/voice-webhook-server.mjs +804 -0
  552. package/scripts/decisions/capture-decision.mjs +116 -0
  553. package/scripts/disclosure_assessment.py +873 -0
  554. package/scripts/disclosure_boundaries.py +562 -0
  555. package/scripts/email-signature-principal.html +52 -0
  556. package/scripts/email-signature.html +60 -0
  557. package/scripts/email_quote_thread.py +167 -0
  558. package/scripts/email_thread_dedup.py +362 -0
  559. package/scripts/emergency-stop.sh +81 -0
  560. package/scripts/healthcheck.sh +116 -0
  561. package/scripts/hooks/block-mcp-cohort-send.sh +15 -0
  562. package/scripts/hooks/block-mcp-slack-send.sh +7 -0
  563. package/scripts/hooks/post-action-log.sh +126 -0
  564. package/scripts/hooks/pre-send-audit.sh +174 -0
  565. package/scripts/hooks/pre-send-audit.test.mjs +215 -0
  566. package/scripts/hooks/session-end-log.sh +27 -0
  567. package/scripts/hooks/session-start-banner.sh +115 -0
  568. package/scripts/huddle/audio-bridge.mjs +664 -0
  569. package/scripts/huddle/boot-slack-cdp.sh +102 -0
  570. package/scripts/huddle/huddle-controller.mjs +942 -0
  571. package/scripts/huddle/huddle-server.mjs +1229 -0
  572. package/scripts/huddle/launch-slack.sh +232 -0
  573. package/scripts/huddle/openai-realtime-bridge.mjs +462 -0
  574. package/scripts/huddle/package-lock.json +62 -0
  575. package/scripts/huddle/package.json +22 -0
  576. package/scripts/huddle/setup-audio.sh +239 -0
  577. package/scripts/huddle/start-call.mjs +318 -0
  578. package/scripts/huddle/test-pipeline.mjs +263 -0
  579. package/scripts/learning/consolidate-skills.mjs +72 -0
  580. package/scripts/learning/session-search.mjs +125 -0
  581. package/scripts/llm_email_dedup.py +442 -0
  582. package/scripts/local-triggers/generate-plists.sh +432 -0
  583. package/scripts/local-triggers/generate-plists.test.mjs +413 -0
  584. package/scripts/local-triggers/install-all.sh +49 -0
  585. package/scripts/local-triggers/plists/.gitkeep +0 -0
  586. package/scripts/local-triggers/run-trigger.sh +63 -0
  587. package/scripts/local-triggers/templates/rag-reindex.plist.template +47 -0
  588. package/scripts/local-triggers/templates/voice-relay-poller.plist.template +54 -0
  589. package/scripts/local-triggers/templates/voice-tunnel.plist.template +55 -0
  590. package/scripts/local-triggers/templates/voice-webhook.plist.template +51 -0
  591. package/scripts/maintenance/backup-to-cloud.sh +124 -0
  592. package/scripts/maintenance/health-check.sh +377 -0
  593. package/scripts/media-generation/README.md +105 -0
  594. package/scripts/media-generation/gemini-image-client.mjs +173 -0
  595. package/scripts/media-generation/generate-assets.mjs +289 -0
  596. package/scripts/media-generation/veo-video-client.mjs +219 -0
  597. package/scripts/org/send-orgmail.mjs +227 -0
  598. package/scripts/outbound-dedup-cleanup.sh +43 -0
  599. package/scripts/outbound-dedup.sh +477 -0
  600. package/scripts/outbound_dedup.py +115 -0
  601. package/scripts/parse-voice-transcript.mjs +481 -0
  602. package/scripts/pdf-generation/README.md +63 -0
  603. package/scripts/pdf-generation/build-document.mjs +247 -0
  604. package/scripts/pdf-generation/templates/board-pack.latex +136 -0
  605. package/scripts/pdf-generation/templates/corporate-letter.latex +126 -0
  606. package/scripts/pdf-generation/templates/memo.latex +114 -0
  607. package/scripts/poll-slack-events.sh +35 -0
  608. package/scripts/poller/calendar-poller.mjs +12 -0
  609. package/scripts/poller/gmail-poller.mjs +192 -0
  610. package/scripts/poller/imap-client.mjs +289 -0
  611. package/scripts/poller/inbox-scan-poller.mjs +156 -0
  612. package/scripts/poller/inbox-scan-poller.test.mjs +231 -0
  613. package/scripts/poller/index.mjs +73 -0
  614. package/scripts/poller/intra-session-check.mjs +285 -0
  615. package/scripts/poller/lib/cloud-relay-dedup.mjs +88 -0
  616. package/scripts/poller/lib/cloud-relay-dedup.test.mjs +133 -0
  617. package/scripts/poller/lib/slash-command-handlers.mjs +177 -0
  618. package/scripts/poller/secondary-gmail-poller.mjs +132 -0
  619. package/scripts/poller/slack-cloud-relay-client.mjs +368 -0
  620. package/scripts/poller/slack-poller.mjs +854 -0
  621. package/scripts/poller/slack-socket-mode.mjs +917 -0
  622. package/scripts/poller/slack-socket-mode.test.mjs +753 -0
  623. package/scripts/poller/trigger.mjs +75 -0
  624. package/scripts/poller/utils.mjs +371 -0
  625. package/scripts/poller/voice-cloud-relay-client.mjs +179 -0
  626. package/scripts/poller/voice-poller.mjs +236 -0
  627. package/scripts/poller-launchd/install.sh +66 -0
  628. package/scripts/poller-launchd/poller.plist.template +40 -0
  629. package/scripts/poller-launchd/whatsapp-handler.plist.template +39 -0
  630. package/scripts/post-interaction-indexer.py +1598 -0
  631. package/scripts/pre-draft-context.py +994 -0
  632. package/scripts/pre_draft_lookup.py +258 -0
  633. package/scripts/rag/build-index.mjs +47 -0
  634. package/scripts/rag/ingest.mjs +111 -0
  635. package/scripts/rag/search.mjs +119 -0
  636. package/scripts/rag-indexer.py +629 -0
  637. package/scripts/restore-from-backup.sh +248 -0
  638. package/scripts/restore-from-backup.test.mjs +178 -0
  639. package/scripts/resume-operations.sh +80 -0
  640. package/scripts/search-secondary-inbox.py +181 -0
  641. package/scripts/secondary-inbox-poller.py +437 -0
  642. package/scripts/self-optimization/compute-metrics.py +398 -0
  643. package/scripts/send-email-as-principal.py +369 -0
  644. package/scripts/send-email-threaded.py +392 -0
  645. package/scripts/send-email-with-attachment.py +377 -0
  646. package/scripts/send-email.sh +131 -0
  647. package/scripts/send-sms.sh +175 -0
  648. package/scripts/send-whatsapp.sh +292 -0
  649. package/scripts/session-start.sh +106 -0
  650. package/scripts/setup/boot-claude-session.sh +94 -0
  651. package/scripts/setup/configure-macos.sh +674 -0
  652. package/scripts/setup/configure-twilio-sip-trunk.mjs +207 -0
  653. package/scripts/setup/configure-voice-tunnel.mjs +182 -0
  654. package/scripts/setup/generate-agent-env.mjs +92 -0
  655. package/scripts/setup/generate-agent-package-json.mjs +222 -0
  656. package/scripts/setup/generate-agent-package-json.test.mjs +143 -0
  657. package/scripts/setup/generate-autonomy.mjs +60 -0
  658. package/scripts/setup/generate-backlog.mjs +101 -0
  659. package/scripts/setup/generate-cadences.mjs +92 -0
  660. package/scripts/setup/generate-capability.mjs +162 -0
  661. package/scripts/setup/generate-charter.mjs +76 -0
  662. package/scripts/setup/generate-comms.mjs +59 -0
  663. package/scripts/setup/generate-company.mjs +92 -0
  664. package/scripts/setup/init-agent.sh +547 -0
  665. package/scripts/setup/init-agent.test.mjs +151 -0
  666. package/scripts/setup/init-archetype.mjs +74 -0
  667. package/scripts/setup/init-backup.mjs +54 -0
  668. package/scripts/setup/init-cadence-bus.mjs +60 -0
  669. package/scripts/setup/init-channel-bus.mjs +46 -0
  670. package/scripts/setup/init-cost-tracking.mjs +45 -0
  671. package/scripts/setup/init-decision-capture.mjs +66 -0
  672. package/scripts/setup/init-known-agents.mjs +57 -0
  673. package/scripts/setup/init-learning.mjs +70 -0
  674. package/scripts/setup/init-memory-executive.mjs +45 -0
  675. package/scripts/setup/init-model-router.mjs +124 -0
  676. package/scripts/setup/init-rag.mjs +174 -0
  677. package/scripts/setup/init-session-router.mjs +38 -0
  678. package/scripts/setup/init-slack-socket-mode.mjs +260 -0
  679. package/scripts/setup/init-telegram.mjs +165 -0
  680. package/scripts/setup/init-voice-realtime.mjs +204 -0
  681. package/scripts/setup/init-whatsapp-baileys.mjs +77 -0
  682. package/scripts/setup/install-dev-tools.sh +150 -0
  683. package/scripts/setup/lib/install-plist.mjs +95 -0
  684. package/scripts/setup/migrate-agent-to-sot.mjs +192 -0
  685. package/scripts/setup/render-environment-yaml.mjs +133 -0
  686. package/scripts/slack-events-ctl.sh +177 -0
  687. package/scripts/slack-events-server.mjs +1045 -0
  688. package/scripts/slack-react.mjs +89 -0
  689. package/scripts/slack-responded.sh +232 -0
  690. package/scripts/slack-send.sh +287 -0
  691. package/scripts/slack-typing.mjs +196 -0
  692. package/scripts/slack-upload-v2.py +95 -0
  693. package/scripts/sms-handler.mjs +450 -0
  694. package/scripts/spawn-session.sh +120 -0
  695. package/scripts/sync-protocol.mjs +217 -0
  696. package/scripts/system-verify.sh +184 -0
  697. package/scripts/test-email-thread-dedup.py +239 -0
  698. package/scripts/test-information-barriers.py +484 -0
  699. package/scripts/test-llm-email-dedup.py +251 -0
  700. package/scripts/test-pre-draft-integration.py +203 -0
  701. package/scripts/test-rag-phase2.sh +442 -0
  702. package/scripts/test-rag-search.sh +251 -0
  703. package/scripts/test-voice-parser.mjs +316 -0
  704. package/scripts/user-context-search.py +659 -0
  705. package/scripts/validate_outbound.py +1504 -0
  706. package/scripts/watchdog/ai.maestro.memory-watchdog.plist +41 -0
  707. package/scripts/watchdog/force-reboot.sh +157 -0
  708. package/scripts/watchdog/memory-watchdog.sh +473 -0
  709. package/scripts/whatsapp-handler.mjs +538 -0
  710. package/teams/desktop-operations.yaml +34 -0
  711. package/teams/executive-office.yaml +27 -0
  712. package/teams/legal-and-regulatory.yaml +24 -0
  713. package/teams/platform-and-engineering.yaml +23 -0
  714. package/teams/strategy-and-growth.yaml +29 -0
  715. package/workflows/continuous/backlog-executor.yaml +141 -0
  716. package/workflows/continuous/inbound-monitor.yaml +168 -0
  717. package/workflows/daily/applicant-triage.yaml +197 -0
  718. package/workflows/daily/comms-triage.yaml +80 -0
  719. package/workflows/daily/evening-wrap.yaml +105 -0
  720. package/workflows/daily/morning-brief.yaml +164 -0
  721. package/workflows/daily/slack-followup-sweep.yaml +87 -0
  722. package/workflows/event-driven/README.md +50 -0
  723. package/workflows/event-driven/agent-failure-investigation.yaml +137 -0
  724. package/workflows/event-driven/pr-review.yaml +107 -0
  725. package/workflows/monthly/board-readiness.yaml +76 -0
  726. package/workflows/quarterly/strategic-scenario-analysis.yaml +85 -0
  727. package/workflows/session-protocol.md +171 -0
  728. package/workflows/weekly/engineering-health.yaml +154 -0
  729. package/workflows/weekly/hiring-review.yaml +169 -0
  730. package/workflows/weekly/rollup-pipeline-review.yaml +76 -0
  731. package/workflows/weekly/strategic-memo.yaml +79 -0
@@ -0,0 +1,162 @@
1
+ /**
2
+ * integration.test.mjs — end-to-end `maestro setup` acceptance (WS1 done-bar).
3
+ *
4
+ * Creates a temp agent repo seeded from scaffold/config, runs the runner in
5
+ * --headless with a fixture answers file (maestroRoot = the real repo so the
6
+ * deterministic generators run for real), and asserts:
7
+ * 1. a GREEN completeness gate after the run,
8
+ * 2. config/agent.ts is a thin SoT wrapper,
9
+ * 3. re-running is a no-op (operating-model + identity skipped).
10
+ *
11
+ * The `claude --print` enrichment spawn is INJECTED (fake) so the test is
12
+ * hermetic — no network. Generators that only template from agent.json run real.
13
+ *
14
+ * Run: `node --test lib/setup/integration.test.mjs`
15
+ * @module lib/setup/integration.test
16
+ */
17
+
18
+ "use strict";
19
+
20
+ import { test } from "node:test";
21
+ import assert from "node:assert/strict";
22
+ import { mkdtempSync, mkdirSync, writeFileSync, copyFileSync, readFileSync, existsSync } from "node:fs";
23
+ import { tmpdir } from "node:os";
24
+ import { join, dirname, resolve } from "node:path";
25
+ import { fileURLToPath } from "node:url";
26
+ import { runSetup, loadSections } from "./runner.mjs";
27
+ import { assertComplete } from "./completeness.mjs";
28
+ import { isWrapper } from "./sot.mjs";
29
+ import { loadProgress } from "./state.mjs";
30
+
31
+ const HERE = dirname(fileURLToPath(import.meta.url));
32
+ const MAESTRO_ROOT = resolve(HERE, "..", "..");
33
+
34
+ /** A fixture answers file: everything the headless sections need. */
35
+ const ANSWERS = {
36
+ firstName: "Jacob", lastName: "Chen", title: "VP, Engineering", email: "jacob@acme.com",
37
+ function: "technical-leader", altitude: "vp",
38
+ principalName: "Dana Reyes", principalTitle: "CEO", principalEmail: "dana@acme.com",
39
+ companyName: "Acme", companyWebsite: "https://acme.com", companyDomain: "acme.com",
40
+ companyIndustry: "Developer tooling", companyStage: "Series B", companyTagline: "Ship faster",
41
+ provider: "subscription",
42
+ channels: ["slack", "gmail"],
43
+ };
44
+
45
+ /** Fake enrichment spawn — returns valid JSON for whatever keys are requested. */
46
+ function fakeEnrichSpawn(prompt) {
47
+ // The system prompt lists the keys as JSON; pick a sensible filler per key.
48
+ const m = prompt.system.match(/ONLY these keys:\s*(\[[^\]]*\])/);
49
+ let keys = [];
50
+ try { keys = m ? JSON.parse(m[1]) : []; } catch { keys = []; }
51
+ const out = {};
52
+ for (const k of keys) {
53
+ if (k === "responsibilities" || k === "operatingPrinciples") {
54
+ out[k] = ["Own platform reliability and delivery confidence", "Keep the engineering org unblocked and accountable"];
55
+ } else if (k === "persona") {
56
+ out[k] = "A pragmatic engineering leader who keeps delivery honest and the team focused on outcomes.";
57
+ } else if (k === "description") {
58
+ out[k] = "Acme builds developer tooling that helps fintechs ship faster with confidence.";
59
+ } else {
60
+ out[k] = "Concrete, company-specific copy that reads like a colleague wrote it.";
61
+ }
62
+ }
63
+ return Promise.resolve(JSON.stringify(out));
64
+ }
65
+
66
+ function seedAgentRepo() {
67
+ const root = mkdtempSync(join(tmpdir(), "maestro-int-"));
68
+ mkdirSync(join(root, "config"), { recursive: true });
69
+ // Seed agent.json from the scaffold (placeholder identity) + agent.ts standalone literal.
70
+ copyFileSync(join(MAESTRO_ROOT, "scaffold", "config", "agent.json"), join(root, "config", "agent.json"));
71
+ copyFileSync(join(MAESTRO_ROOT, "scaffold", "config", "agent.ts"), join(root, "config", "agent.ts"));
72
+ copyFileSync(join(MAESTRO_ROOT, "scaffold", "config", "company.json"), join(root, "config", "company.json"));
73
+ writeFileSync(join(root, "README.md"), "# Acme agent\nAutonomous engineering leader for Acme.\n");
74
+ writeFileSync(join(root, ".env"), "");
75
+ return root;
76
+ }
77
+
78
+ test("maestro setup --headless produces a green operating model + wrapper, re-run is a no-op", async () => {
79
+ const root = seedAgentRepo();
80
+ const sections = await loadSections();
81
+
82
+ // First run.
83
+ const res = await runSetup({
84
+ agentRoot: root,
85
+ maestroRoot: MAESTRO_ROOT,
86
+ sections,
87
+ flags: { resume: false, from: null, only: null, status: false, headless: true, answersFile: null, noEnrich: false, dryRun: false, verbose: false, help: false },
88
+ answers: ANSWERS,
89
+ enrichSpawn: fakeEnrichSpawn,
90
+ });
91
+
92
+ // identity + company + operating-model all ran.
93
+ assert.ok(res.ran.includes("identity"), "identity ran");
94
+ assert.ok(res.ran.includes("operating-model"), "operating-model ran");
95
+
96
+ // 1. Green completeness gate.
97
+ const gate = await assertComplete(root);
98
+ assert.equal(gate.ok, true, "completeness gate green; missing: " + JSON.stringify(gate.missing));
99
+
100
+ // 2. agent.ts is the thin wrapper (born unified).
101
+ assert.ok(isWrapper(readFileSync(join(root, "config", "agent.ts"), "utf-8")), "agent.ts is a SoT wrapper");
102
+
103
+ // identity was actually written.
104
+ const id = JSON.parse(readFileSync(join(root, "config", "agent.json"), "utf-8"));
105
+ assert.equal(id.firstName, "Jacob");
106
+ assert.equal(id.function, "technical-leader");
107
+ assert.equal(id.altitude, "vp");
108
+ // enriched prose landed.
109
+ assert.ok(Array.isArray(id.responsibilities) && id.responsibilities.length >= 1, "responsibilities enriched");
110
+
111
+ // verify section produced a result and it is ok (no hard failures).
112
+ assert.ok(res.verify, "verify ran");
113
+
114
+ // 3. Re-run is a no-op for the heavy sections.
115
+ const res2 = await runSetup({
116
+ agentRoot: root,
117
+ maestroRoot: MAESTRO_ROOT,
118
+ sections,
119
+ flags: { resume: true, from: null, only: null, status: false, headless: true, answersFile: null, noEnrich: false, dryRun: false, verbose: false, help: false },
120
+ answers: ANSWERS,
121
+ enrichSpawn: fakeEnrichSpawn,
122
+ });
123
+ assert.ok(res2.skipped.includes("identity"), "identity skipped on re-run");
124
+ assert.ok(res2.skipped.includes("operating-model"), "operating-model skipped on re-run");
125
+ assert.ok(res2.skipped.includes("company"), "company skipped on re-run");
126
+
127
+ // checkpoint recorded the completed sections.
128
+ const prog = loadProgress(root);
129
+ assert.equal(prog.sections["operating-model"].status, "complete");
130
+ });
131
+
132
+ test("maestro setup --status runs detect on every section without writing", async () => {
133
+ const root = seedAgentRepo();
134
+ const sections = await loadSections();
135
+ const res = await runSetup({
136
+ agentRoot: root,
137
+ maestroRoot: MAESTRO_ROOT,
138
+ sections,
139
+ flags: { resume: false, from: null, only: null, status: true, headless: true, answersFile: null, noEnrich: false, dryRun: false, verbose: false, help: false },
140
+ answers: {},
141
+ });
142
+ assert.equal(res.status, true);
143
+ // status must not have generated the operating model.
144
+ assert.equal(existsSync(join(root, "config", "operating-charter.md")), false);
145
+ });
146
+
147
+ test("maestro setup --no-enrich leaves prose skeletons but still completes the gate", async () => {
148
+ const root = seedAgentRepo();
149
+ const sections = await loadSections();
150
+ await runSetup({
151
+ agentRoot: root,
152
+ maestroRoot: MAESTRO_ROOT,
153
+ sections,
154
+ flags: { resume: false, from: null, only: null, status: false, headless: true, answersFile: null, noEnrich: true, dryRun: false, verbose: false, help: false },
155
+ answers: ANSWERS,
156
+ });
157
+ // The deterministic operating-model artifacts still exist...
158
+ assert.ok(existsSync(join(root, "config", "agent-roster.yaml")));
159
+ // ...but the charter still carries TBD slots (not enriched), so the gate is NOT green.
160
+ const gate = await assertComplete(root);
161
+ assert.equal(gate.ok, false, "without enrichment, charter TBD slots keep the gate red");
162
+ });
@@ -0,0 +1,360 @@
1
+ /**
2
+ * lib/setup/io.mjs — terminal prompt helpers for the `maestro setup` wizard.
3
+ *
4
+ * House style: Node built-ins only (`node:readline/promises` + raw-mode stdin),
5
+ * no new npm dependency. Every helper accepts a `{ headless, answers }` context;
6
+ * in headless/non-TTY mode each returns the pre-supplied answer
7
+ * (`answers[key]` → env → default) and NEVER blocks on input.
8
+ *
9
+ * Helpers:
10
+ * text(ctx, opts) → string
11
+ * select(ctx, opts) → value of the chosen choice
12
+ * multiselect(ctx, opts) → array of chosen values
13
+ * confirm(ctx, opts) → boolean
14
+ * secret(ctx, opts) → string (masked echo, never persisted by io)
15
+ * note(...) → void (stderr-free informational print)
16
+ *
17
+ * Pure helpers: no side effects beyond stdio. The wizard sections own all file
18
+ * writes; io only reads input and prints prompts.
19
+ *
20
+ * @module lib/setup/io
21
+ */
22
+
23
+ "use strict";
24
+
25
+ import { createInterface } from "node:readline/promises";
26
+ import { stdin as defaultIn, stdout as defaultOut } from "node:process";
27
+
28
+ /** ANSI escape helpers (kept tiny + dependency-free). */
29
+ const ESC = {
30
+ dim: (s) => `\x1b[2m${s}\x1b[0m`,
31
+ bold: (s) => `\x1b[1m${s}\x1b[0m`,
32
+ cyan: (s) => `\x1b[36m${s}\x1b[0m`,
33
+ green: (s) => `\x1b[32m${s}\x1b[0m`,
34
+ reset: "\x1b[0m",
35
+ };
36
+
37
+ /**
38
+ * Is this context running without an interactive TTY (so prompts must not
39
+ * block)? True when `headless` is set, when stdin lacks `isTTY`, or when raw
40
+ * mode is unavailable.
41
+ * @param {object} [ctx]
42
+ * @param {NodeJS.ReadStream} [input]
43
+ * @returns {boolean}
44
+ */
45
+ export function isHeadless(ctx = {}, input = defaultIn) {
46
+ if (ctx.headless) return true;
47
+ if (!input || !input.isTTY) return true;
48
+ return typeof input.setRawMode !== "function";
49
+ }
50
+
51
+ /**
52
+ * Resolve a pre-supplied answer for `key` from the context answers bag, then
53
+ * the environment, then the provided default. Returns `undefined` when nothing
54
+ * is available (caller decides whether that is fatal).
55
+ * @param {object} ctx
56
+ * @param {string} [key]
57
+ * @param {{env?:string, default?:*}} [opts]
58
+ */
59
+ export function resolveAnswer(ctx, key, opts = {}) {
60
+ const answers = (ctx && ctx.answers) || {};
61
+ if (key && Object.prototype.hasOwnProperty.call(answers, key) && answers[key] !== undefined) {
62
+ return answers[key];
63
+ }
64
+ if (opts.env && process.env[opts.env] !== undefined && process.env[opts.env] !== "") {
65
+ return process.env[opts.env];
66
+ }
67
+ if (Object.prototype.hasOwnProperty.call(opts, "default")) return opts.default;
68
+ return undefined;
69
+ }
70
+
71
+ /** Strip bracketed-paste markers a terminal may wrap pasted input with. */
72
+ export function stripBracketedPaste(s) {
73
+ if (typeof s !== "string") return s;
74
+ // ESC [ 200 ~ … ESC [ 201 ~ (and any bare occurrence of the markers).
75
+ return s.replace(/\x1b\[20[01]~/g, "");
76
+ }
77
+
78
+ /**
79
+ * Free-text prompt.
80
+ * @param {object} ctx wizard context ({ headless, answers })
81
+ * @param {{message:string, key?:string, env?:string, default?:string, trim?:boolean}} opts
82
+ * @returns {Promise<string>}
83
+ */
84
+ export async function text(ctx, opts = {}) {
85
+ const trim = opts.trim !== false;
86
+ if (isHeadless(ctx)) {
87
+ const v = resolveAnswer(ctx, opts.key, opts);
88
+ const s = v === undefined || v === null ? "" : String(v);
89
+ return trim ? s.trim() : s;
90
+ }
91
+ const rl = createInterface({ input: defaultIn, output: defaultOut });
92
+ try {
93
+ const def = resolveAnswer(ctx, opts.key, opts);
94
+ const suffix = def ? ` ${ESC.dim(`(${def})`)}` : "";
95
+ const answer = await rl.question(`${opts.message}${suffix}: `);
96
+ const clean = stripBracketedPaste(answer);
97
+ const out = trim ? clean.trim() : clean;
98
+ if (!out && def !== undefined && def !== null) return trim ? String(def).trim() : String(def);
99
+ return out;
100
+ } finally {
101
+ rl.close();
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Yes/no confirmation.
107
+ * @param {object} ctx
108
+ * @param {{message:string, key?:string, env?:string, default?:boolean}} opts
109
+ * @returns {Promise<boolean>}
110
+ */
111
+ export async function confirm(ctx, opts = {}) {
112
+ const dflt = opts.default !== undefined ? !!opts.default : false;
113
+ if (isHeadless(ctx)) {
114
+ const v = resolveAnswer(ctx, opts.key, { ...opts, default: dflt });
115
+ return coerceBool(v, dflt);
116
+ }
117
+ const rl = createInterface({ input: defaultIn, output: defaultOut });
118
+ try {
119
+ const hint = dflt ? "Y/n" : "y/N";
120
+ const answer = (await rl.question(`${opts.message} ${ESC.dim(`(${hint})`)} `)).trim().toLowerCase();
121
+ if (!answer) return dflt;
122
+ return /^y(es)?$/.test(answer);
123
+ } finally {
124
+ rl.close();
125
+ }
126
+ }
127
+
128
+ function coerceBool(v, dflt) {
129
+ if (typeof v === "boolean") return v;
130
+ if (v === undefined || v === null || v === "") return dflt;
131
+ return /^(1|y|yes|true|on)$/i.test(String(v).trim());
132
+ }
133
+
134
+ /**
135
+ * Single-choice select. Choices: `[{ label, value }]` or bare strings.
136
+ * Interactive mode uses raw ↑/↓ + enter; headless/non-TTY returns the
137
+ * pre-supplied answer (matched against value or label) or the default.
138
+ * @param {object} ctx
139
+ * @param {{message:string, choices:Array, key?:string, env?:string, default?:*}} opts
140
+ * @returns {Promise<*>}
141
+ */
142
+ export async function select(ctx, opts = {}) {
143
+ const choices = normalizeChoices(opts.choices);
144
+ if (choices.length === 0) throw new Error("select: choices must be non-empty");
145
+
146
+ if (isHeadless(ctx)) {
147
+ const v = resolveAnswer(ctx, opts.key, opts);
148
+ const match = matchChoice(choices, v);
149
+ if (match) return match.value;
150
+ return (matchChoice(choices, opts.default) || choices[0]).value;
151
+ }
152
+ const idx = await rawSelect(opts.message, choices, defaultIndex(choices, resolveAnswer(ctx, opts.key, opts)));
153
+ return choices[idx].value;
154
+ }
155
+
156
+ /**
157
+ * Multi-choice select (space toggles, enter confirms). Headless returns the
158
+ * pre-supplied answer array (each matched against value/label) or default.
159
+ * @param {object} ctx
160
+ * @param {{message:string, choices:Array, key?:string, env?:string, default?:Array}} opts
161
+ * @returns {Promise<Array>}
162
+ */
163
+ export async function multiselect(ctx, opts = {}) {
164
+ const choices = normalizeChoices(opts.choices);
165
+ if (choices.length === 0) return [];
166
+
167
+ if (isHeadless(ctx)) {
168
+ const raw = resolveAnswer(ctx, opts.key, opts);
169
+ const arr = Array.isArray(raw) ? raw : raw == null || raw === "" ? (opts.default || []) : [raw];
170
+ const out = [];
171
+ for (const item of arr) {
172
+ const m = matchChoice(choices, item);
173
+ if (m) out.push(m.value);
174
+ }
175
+ return out;
176
+ }
177
+ const preselected = new Set();
178
+ const def = resolveAnswer(ctx, opts.key, opts);
179
+ if (Array.isArray(def)) for (const d of def) { const m = matchChoice(choices, d); if (m) preselected.add(choices.indexOf(m)); }
180
+ const chosen = await rawMultiSelect(opts.message, choices, preselected);
181
+ return chosen.map((i) => choices[i].value);
182
+ }
183
+
184
+ /**
185
+ * Masked secret prompt. Echoes `*` per character; strips bracketed-paste
186
+ * markers. Falls back to a non-masked readline if raw mode is unavailable but
187
+ * stdin is still a TTY (rare). Headless returns the pre-supplied answer.
188
+ * `io` never persists secrets — the caller writes them.
189
+ * @param {object} ctx
190
+ * @param {{message:string, key?:string, env?:string, default?:string}} opts
191
+ * @returns {Promise<string>}
192
+ */
193
+ export async function secret(ctx, opts = {}) {
194
+ if (isHeadless(ctx)) {
195
+ const v = resolveAnswer(ctx, opts.key, opts);
196
+ return v === undefined || v === null ? "" : String(v);
197
+ }
198
+ const input = defaultIn;
199
+ const output = defaultOut;
200
+ if (typeof input.setRawMode !== "function") {
201
+ // Non-raw TTY fallback (no masking possible) — still functional.
202
+ const rl = createInterface({ input, output });
203
+ try { return stripBracketedPaste((await rl.question(`${opts.message}: `)).trim()); }
204
+ finally { rl.close(); }
205
+ }
206
+
207
+ return new Promise((resolve, reject) => {
208
+ output.write(`${opts.message}: `);
209
+ let buf = "";
210
+ const prevRaw = input.isRaw;
211
+ input.setRawMode(true);
212
+ input.resume();
213
+ input.setEncoding("utf8");
214
+
215
+ const cleanup = () => {
216
+ input.setRawMode(prevRaw || false);
217
+ input.pause();
218
+ input.removeListener("data", onData);
219
+ output.write("\n");
220
+ };
221
+
222
+ const onData = (chunk) => {
223
+ const data = stripBracketedPaste(chunk);
224
+ for (const ch of data) {
225
+ if (ch === "\r" || ch === "\n") { cleanup(); resolve(buf); return; }
226
+ if (ch === "") { cleanup(); reject(new Error("cancelled")); return; } // Ctrl-C
227
+ if (ch === "" || ch === "\b") { // backspace
228
+ if (buf.length > 0) { buf = buf.slice(0, -1); output.write("\b \b"); }
229
+ continue;
230
+ }
231
+ if (ch < " ") continue; // ignore other control chars
232
+ buf += ch;
233
+ output.write("*");
234
+ }
235
+ };
236
+ input.on("data", onData);
237
+ });
238
+ }
239
+
240
+ /** Informational line — printed to stdout, never to stderr (not an error). */
241
+ export function note(message, { dim = false } = {}) {
242
+ defaultOut.write(`${dim ? ESC.dim(message) : message}\n`);
243
+ }
244
+
245
+ // ---------------------------------------------------------------------------
246
+ // internals
247
+ // ---------------------------------------------------------------------------
248
+
249
+ function normalizeChoices(choices) {
250
+ if (!Array.isArray(choices)) return [];
251
+ return choices.map((c) =>
252
+ c && typeof c === "object" && "value" in c
253
+ ? { label: c.label != null ? String(c.label) : String(c.value), value: c.value, hint: c.hint }
254
+ : { label: String(c), value: c }
255
+ );
256
+ }
257
+
258
+ function matchChoice(choices, needle) {
259
+ if (needle === undefined || needle === null) return null;
260
+ const n = String(needle).trim().toLowerCase();
261
+ return (
262
+ choices.find((c) => String(c.value).toLowerCase() === n) ||
263
+ choices.find((c) => c.label.toLowerCase() === n) ||
264
+ null
265
+ );
266
+ }
267
+
268
+ function defaultIndex(choices, needle) {
269
+ const m = matchChoice(choices, needle);
270
+ const i = m ? choices.indexOf(m) : 0;
271
+ return i < 0 ? 0 : i;
272
+ }
273
+
274
+ /** Raw-mode single select returning the chosen index. */
275
+ function rawSelect(message, choices, start = 0) {
276
+ return new Promise((resolve, reject) => {
277
+ const input = defaultIn;
278
+ const output = defaultOut;
279
+ let cur = start;
280
+ const prevRaw = input.isRaw;
281
+ input.setRawMode(true);
282
+ input.resume();
283
+ input.setEncoding("utf8");
284
+
285
+ const render = (first) => {
286
+ if (!first) output.write(`\x1b[${choices.length + 1}A`); // move cursor up to redraw
287
+ output.write(`${ESC.bold(message)} ${ESC.dim("(↑/↓, enter)")}\n`);
288
+ choices.forEach((c, i) => {
289
+ const marker = i === cur ? ESC.cyan("❯") : " ";
290
+ const label = i === cur ? ESC.cyan(c.label) : c.label;
291
+ const hint = c.hint ? ` ${ESC.dim(c.hint)}` : "";
292
+ output.write(`\x1b[2K${marker} ${label}${hint}\n`);
293
+ });
294
+ };
295
+
296
+ const cleanup = () => {
297
+ input.setRawMode(prevRaw || false);
298
+ input.pause();
299
+ input.removeListener("data", onData);
300
+ };
301
+
302
+ const onData = (chunk) => {
303
+ const data = stripBracketedPaste(chunk);
304
+ if (data === "") { cleanup(); reject(new Error("cancelled")); return; }
305
+ if (data === "\r" || data === "\n") { cleanup(); resolve(cur); return; }
306
+ if (data === "" || data === "k") cur = (cur - 1 + choices.length) % choices.length;
307
+ else if (data === "" || data === "j") cur = (cur + 1) % choices.length;
308
+ else { const n = parseInt(data, 10); if (!Number.isNaN(n) && n >= 1 && n <= choices.length) cur = n - 1; }
309
+ render(false);
310
+ };
311
+ render(true);
312
+ input.on("data", onData);
313
+ });
314
+ }
315
+
316
+ /** Raw-mode multi select returning the chosen indices (sorted). */
317
+ function rawMultiSelect(message, choices, preselected = new Set()) {
318
+ return new Promise((resolve, reject) => {
319
+ const input = defaultIn;
320
+ const output = defaultOut;
321
+ let cur = 0;
322
+ const chosen = new Set(preselected);
323
+ const prevRaw = input.isRaw;
324
+ input.setRawMode(true);
325
+ input.resume();
326
+ input.setEncoding("utf8");
327
+
328
+ const render = (first) => {
329
+ if (!first) output.write(`\x1b[${choices.length + 1}A`);
330
+ output.write(`${ESC.bold(message)} ${ESC.dim("(↑/↓, space, enter)")}\n`);
331
+ choices.forEach((c, i) => {
332
+ const cursor = i === cur ? ESC.cyan("❯") : " ";
333
+ const box = chosen.has(i) ? ESC.green("◉") : "◯";
334
+ const label = i === cur ? ESC.cyan(c.label) : c.label;
335
+ const hint = c.hint ? ` ${ESC.dim(c.hint)}` : "";
336
+ output.write(`\x1b[2K${cursor} ${box} ${label}${hint}\n`);
337
+ });
338
+ };
339
+
340
+ const cleanup = () => {
341
+ input.setRawMode(prevRaw || false);
342
+ input.pause();
343
+ input.removeListener("data", onData);
344
+ };
345
+
346
+ const onData = (chunk) => {
347
+ const data = stripBracketedPaste(chunk);
348
+ if (data === "") { cleanup(); reject(new Error("cancelled")); return; }
349
+ if (data === "\r" || data === "\n") { cleanup(); resolve([...chosen].sort((a, b) => a - b)); return; }
350
+ if (data === " ") { if (chosen.has(cur)) chosen.delete(cur); else chosen.add(cur); }
351
+ else if (data === "" || data === "k") cur = (cur - 1 + choices.length) % choices.length;
352
+ else if (data === "" || data === "j") cur = (cur + 1) % choices.length;
353
+ render(false);
354
+ };
355
+ render(true);
356
+ input.on("data", onData);
357
+ });
358
+ }
359
+
360
+ export default { text, select, multiselect, confirm, secret, note, isHeadless, resolveAnswer, stripBracketedPaste };
@@ -0,0 +1,77 @@
1
+ /**
2
+ * io.test.mjs — headless-mode + helper tests for lib/setup/io.mjs.
3
+ * Interactive raw-mode paths are not exercised here (no TTY in CI); we test the
4
+ * pure helpers and the headless contract (no blocking, pre-supplied answers).
5
+ * Run: `node --test lib/setup/io.test.mjs`
6
+ * @module lib/setup/io.test
7
+ */
8
+
9
+ "use strict";
10
+
11
+ import { test } from "node:test";
12
+ import assert from "node:assert/strict";
13
+ import {
14
+ text, select, multiselect, confirm, secret,
15
+ isHeadless, resolveAnswer, stripBracketedPaste,
16
+ } from "./io.mjs";
17
+
18
+ const HL = { headless: true, answers: {} };
19
+
20
+ test("isHeadless is true when ctx.headless set", () => {
21
+ assert.equal(isHeadless({ headless: true }), true);
22
+ });
23
+
24
+ test("resolveAnswer prefers answers, then env, then default", () => {
25
+ assert.equal(resolveAnswer({ answers: { k: "a" } }, "k", { default: "d" }), "a");
26
+ process.env.__IO_TEST_ENV = "fromenv";
27
+ assert.equal(resolveAnswer({ answers: {} }, "k", { env: "__IO_TEST_ENV", default: "d" }), "fromenv");
28
+ delete process.env.__IO_TEST_ENV;
29
+ assert.equal(resolveAnswer({ answers: {} }, "k", { default: "d" }), "d");
30
+ assert.equal(resolveAnswer({ answers: {} }, "k", {}), undefined);
31
+ });
32
+
33
+ test("stripBracketedPaste removes paste markers", () => {
34
+ assert.equal(stripBracketedPaste("\x1b[200~hello\x1b[201~"), "hello");
35
+ assert.equal(stripBracketedPaste("plain"), "plain");
36
+ });
37
+
38
+ test("text returns pre-supplied answer in headless mode (never blocks)", async () => {
39
+ const v = await text({ ...HL, answers: { name: "Jacob" } }, { message: "Name?", key: "name" });
40
+ assert.equal(v, "Jacob");
41
+ });
42
+
43
+ test("text falls back to default when no answer", async () => {
44
+ const v = await text(HL, { message: "Name?", key: "missing", default: "Anon" });
45
+ assert.equal(v, "Anon");
46
+ });
47
+
48
+ test("confirm coerces headless answer strings", async () => {
49
+ assert.equal(await confirm({ ...HL, answers: { ok: "yes" } }, { message: "?", key: "ok" }), true);
50
+ assert.equal(await confirm({ ...HL, answers: { ok: false } }, { message: "?", key: "ok" }), false);
51
+ assert.equal(await confirm(HL, { message: "?", key: "x", default: true }), true);
52
+ });
53
+
54
+ test("select matches by value or label, falls back to first/default", async () => {
55
+ const choices = [{ label: "Sonnet", value: "sonnet" }, { label: "Haiku", value: "haiku" }];
56
+ assert.equal(await select({ ...HL, answers: { m: "haiku" } }, { message: "?", key: "m", choices }), "haiku");
57
+ assert.equal(await select({ ...HL, answers: { m: "Sonnet" } }, { message: "?", key: "m", choices }), "sonnet");
58
+ assert.equal(await select(HL, { message: "?", key: "m", choices, default: "haiku" }), "haiku");
59
+ assert.equal(await select(HL, { message: "?", key: "m", choices }), "sonnet"); // first
60
+ });
61
+
62
+ test("multiselect returns matched values, ignoring unknowns", async () => {
63
+ const choices = [{ label: "Slack", value: "slack" }, { label: "Email", value: "email" }, { label: "SMS", value: "sms" }];
64
+ const v = await multiselect({ ...HL, answers: { ch: ["slack", "sms", "nope"] } }, { message: "?", key: "ch", choices });
65
+ assert.deepEqual(v, ["slack", "sms"]);
66
+ });
67
+
68
+ test("multiselect default applies when answer absent", async () => {
69
+ const choices = ["a", "b", "c"];
70
+ const v = await multiselect(HL, { message: "?", key: "ch", choices, default: ["b"] });
71
+ assert.deepEqual(v, ["b"]);
72
+ });
73
+
74
+ test("secret returns pre-supplied answer headless, never echoing", async () => {
75
+ const v = await secret({ ...HL, answers: { token: "xapp-123" } }, { message: "Token", key: "token" });
76
+ assert.equal(v, "xapp-123");
77
+ });
@@ -0,0 +1,81 @@
1
+ /**
2
+ * lib/setup/run-generator.mjs — invoke a scripts/setup/*.mjs generator with the
3
+ * shared execution contract (cwd = agent repo; env MAESTRO_ROOT / AGENT_ROOT).
4
+ *
5
+ * Generators that only template from config/agent.json run for real. The runner
6
+ * surfaces failures to the caller, which decides whether they are fatal (the
7
+ * operating-model section treats a generator failure as fatal; verify reports it).
8
+ *
9
+ * @module lib/setup/run-generator
10
+ */
11
+
12
+ "use strict";
13
+
14
+ import { join, dirname } from "node:path";
15
+ import { fileURLToPath } from "node:url";
16
+ import { existsSync } from "node:fs";
17
+ import { execFileSync } from "node:child_process";
18
+
19
+ const HERE = dirname(fileURLToPath(import.meta.url));
20
+ const MAESTRO_ROOT = join(HERE, "..", "..");
21
+
22
+ /**
23
+ * Run a generator script. Returns { ok, stdout, stderr, code }. Never throws on
24
+ * a non-zero exit (captured), but a spawn failure (ENOENT) is captured too.
25
+ *
26
+ * @param {string} script filename under scripts/setup/ (e.g. "generate-charter.mjs")
27
+ * @param {string} agentRoot
28
+ * @param {object} [opts]
29
+ * @param {string} [opts.maestroRoot]
30
+ * @param {boolean} [opts.bash] run via /bin/bash (for .sh generators)
31
+ * @param {string[]} [opts.args]
32
+ * @returns {{ok:boolean, stdout:string, stderr:string, code:number}}
33
+ */
34
+ export function runGenerator(script, agentRoot, opts = {}) {
35
+ const maestroRoot = opts.maestroRoot || MAESTRO_ROOT;
36
+ const scriptPath = join(maestroRoot, "scripts", "setup", script);
37
+ if (!existsSync(scriptPath)) {
38
+ return { ok: false, stdout: "", stderr: `generator not found: ${scriptPath}`, code: 127 };
39
+ }
40
+ const cmd = opts.bash ? "/bin/bash" : process.execPath;
41
+ const argv = opts.bash ? [scriptPath, ...(opts.args || [])] : [scriptPath, ...(opts.args || [])];
42
+ try {
43
+ const stdout = execFileSync(cmd, argv, {
44
+ cwd: agentRoot,
45
+ env: { ...process.env, MAESTRO_ROOT: maestroRoot, AGENT_ROOT: agentRoot, AGENT_DIR: agentRoot },
46
+ encoding: "utf-8",
47
+ });
48
+ return { ok: true, stdout: stdout || "", stderr: "", code: 0 };
49
+ } catch (e) {
50
+ return {
51
+ ok: false,
52
+ stdout: e && e.stdout ? String(e.stdout) : "",
53
+ stderr: e && e.stderr ? String(e.stderr) : (e && e.message ? e.message : String(e)),
54
+ code: e && typeof e.status === "number" ? e.status : 1,
55
+ };
56
+ }
57
+ }
58
+
59
+ /** Run a script that lives somewhere other than scripts/setup (absolute path). */
60
+ export function runScriptAbs(scriptPath, agentRoot, opts = {}) {
61
+ const maestroRoot = opts.maestroRoot || MAESTRO_ROOT;
62
+ if (!existsSync(scriptPath)) return { ok: false, stdout: "", stderr: `script not found: ${scriptPath}`, code: 127 };
63
+ const cmd = opts.bash ? "/bin/bash" : process.execPath;
64
+ try {
65
+ const stdout = execFileSync(cmd, [scriptPath, ...(opts.args || [])], {
66
+ cwd: agentRoot,
67
+ env: { ...process.env, MAESTRO_ROOT: maestroRoot, AGENT_ROOT: agentRoot, AGENT_DIR: agentRoot },
68
+ encoding: "utf-8",
69
+ });
70
+ return { ok: true, stdout: stdout || "", stderr: "", code: 0 };
71
+ } catch (e) {
72
+ return {
73
+ ok: false,
74
+ stdout: e && e.stdout ? String(e.stdout) : "",
75
+ stderr: e && e.stderr ? String(e.stderr) : (e && e.message ? e.message : String(e)),
76
+ code: e && typeof e.status === "number" ? e.status : 1,
77
+ };
78
+ }
79
+ }
80
+
81
+ export { MAESTRO_ROOT as DEFAULT_MAESTRO_ROOT };