@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,138 @@
1
+ /**
2
+ * sections/enrich.mjs — LLM enrichment of company-specific prose slots. (order 70)
3
+ *
4
+ * Iterates the enumerated enrichable units that still need filling and calls
5
+ * lib/setup/enrich.enrichField (one `claude --print` per unit). Honors
6
+ * `--no-enrich` (skips entirely, leaving deterministic skeletons) and is
7
+ * fail-open: any unit that fails is marked `pending` and the section continues.
8
+ *
9
+ * The spawn is injectable via `ctx.enrichSpawn` so the wizard's integration
10
+ * tests stay hermetic.
11
+ *
12
+ * @module lib/setup/sections/enrich
13
+ */
14
+
15
+ "use strict";
16
+
17
+ import { join } from "node:path";
18
+ import { existsSync, readFileSync, writeFileSync, renameSync } from "node:fs";
19
+ import { pathToFileURL } from "node:url";
20
+ import { enrichField, ENRICHABLE } from "../enrich.mjs";
21
+ import { readAgentJson } from "../sot.mjs";
22
+ import { loadProgress } from "../state.mjs";
23
+ import * as io from "../io.mjs";
24
+
25
+ /**
26
+ * Substitute enriched company-slot values into the deterministic charter body,
27
+ * clearing the placeholder markers (mandate stub, KPI "TBD" targets, stakeholder
28
+ * "_TBD_") so the completeness gate's findCharterPlaceholders sees none.
29
+ * @param {string} agentRoot
30
+ * @param {{mandate?:string, kpiTarget?:string, stakeholderContact?:string}} value
31
+ */
32
+ function applyCharterEnrichment(agentRoot, value) {
33
+ const p = join(agentRoot, "config", "operating-charter.md");
34
+ if (!existsSync(p)) return;
35
+ let body = readFileSync(p, "utf-8");
36
+ const mandate = (value.mandate || "").trim();
37
+ const kpi = (value.kpiTarget || "set against the company baseline this quarter").trim();
38
+ const contact = (value.stakeholderContact || "named at onboarding").trim();
39
+
40
+ if (mandate) {
41
+ body = body.replace(
42
+ /_Mandate \(enrich at init\):[^_]*_/,
43
+ mandate.replace(/\$/g, "$$$$")
44
+ );
45
+ }
46
+ // KPI targets: "_target: TBD at init_" → concrete phrasing.
47
+ body = body.replace(/_target: TBD at init_/g, `target: ${kpi}`);
48
+ // Stakeholder named contacts: "| _TBD_ |" → concrete phrasing.
49
+ body = body.replace(/_TBD_/g, contact);
50
+ // Any remaining bare "TBD" (defensive) — replace with the KPI phrasing.
51
+ body = body.replace(/\bTBD\b/g, kpi);
52
+
53
+ const tmp = `${p}.${process.pid}.${Date.now()}.tmp`;
54
+ writeFileSync(tmp, body, "utf-8");
55
+ renameSync(tmp, p);
56
+ }
57
+
58
+ function emptyArr(v) { return !Array.isArray(v) || v.length === 0; }
59
+ function emptyStr(v) { return v === undefined || v === null || String(v).trim() === ""; }
60
+
61
+ function companyDescriptionEmpty(agentRoot) {
62
+ const p = join(agentRoot, "config", "company.json");
63
+ try { const c = existsSync(p) ? JSON.parse(readFileSync(p, "utf-8")) : {}; return emptyStr(c.description); }
64
+ catch { return true; }
65
+ }
66
+
67
+ async function charterHasPlaceholders(agentRoot, maestroRoot) {
68
+ const p = join(agentRoot, "config", "operating-charter.md");
69
+ if (!existsSync(p)) return false;
70
+ try {
71
+ const { findCharterPlaceholders } = await import(pathToFileURL(join(maestroRoot, "lib", "charter.mjs")).href);
72
+ return findCharterPlaceholders(readFileSync(p, "utf-8")).length > 0;
73
+ } catch { return false; }
74
+ }
75
+
76
+ /** Which enrichable units still need work, given current on-disk state. */
77
+ export async function pendingUnits(agentRoot, maestroRoot) {
78
+ const id = readAgentJson(agentRoot) || {};
79
+ const units = [];
80
+ if (emptyStr(id.persona)) units.push("agent.persona");
81
+ if (emptyArr(id.responsibilities)) units.push("agent.responsibilities");
82
+ if (emptyArr(id.operatingPrinciples)) units.push("agent.operatingPrinciples");
83
+ if (companyDescriptionEmpty(agentRoot)) units.push("company.description");
84
+ if (await charterHasPlaceholders(agentRoot, maestroRoot)) units.push("charter.companySlots");
85
+ return units.filter((u) => u in ENRICHABLE);
86
+ }
87
+
88
+ export default {
89
+ id: "enrich",
90
+ label: "LLM enrichment (company-specific prose)",
91
+ order: 70,
92
+
93
+ async detect(ctx) {
94
+ if (ctx.flags && ctx.flags.noEnrich) return { status: "complete", summary: "skipped (--no-enrich)" };
95
+ const pending = await pendingUnits(ctx.agentRoot, ctx.maestroRoot);
96
+ if (pending.length === 0) return { status: "complete", summary: "all prose slots filled" };
97
+ return { status: "partial", missing: pending };
98
+ },
99
+
100
+ async prompt() { /* non-interactive */ },
101
+
102
+ async apply(ctx) {
103
+ if (ctx.flags && ctx.flags.noEnrich) {
104
+ io.note("Enrichment skipped (--no-enrich) — deterministic skeletons left in place.", { dim: true });
105
+ return;
106
+ }
107
+ const units = await pendingUnits(ctx.agentRoot, ctx.maestroRoot);
108
+ if (units.length === 0) return;
109
+ const spawn = ctx.enrichSpawn; // undefined → real claude spawn inside enrichField
110
+ let enriched = 0, pending = 0;
111
+ for (const unit of units) {
112
+ const opts = spawn ? { spawn } : {};
113
+ const res = await enrichField(ctx.agentRoot, unit, opts);
114
+ if (res.status === "enriched") {
115
+ enriched++;
116
+ // charter company-slots: substitute the enriched values into the
117
+ // deterministic charter body (clears the TBD / mandate placeholders).
118
+ if (unit === "charter.companySlots" && res.value) {
119
+ try { applyCharterEnrichment(ctx.agentRoot, res.value); } catch { /* fail-open */ }
120
+ }
121
+ } else {
122
+ pending++;
123
+ }
124
+ }
125
+ if (ctx.flags && ctx.flags.verbose) io.note(`enrichment: ${enriched} filled, ${pending} left as skeleton`, { dim: true });
126
+ },
127
+
128
+ async verify(ctx) {
129
+ // Enrichment is best-effort — a pending unit is NOT a hard failure (the
130
+ // deterministic skeleton is still a real artifact). Report as informational.
131
+ const ledger = loadProgress(ctx.agentRoot).enrich || {};
132
+ const pendingCount = Object.values(ledger).filter((e) => e && e.status === "pending").length;
133
+ return {
134
+ ok: true,
135
+ checks: [{ name: `enrichment (${pendingCount} unit(s) left as skeleton)`, ok: true, remedy: pendingCount ? "re-run `maestro setup --only enrich` to retry" : undefined }],
136
+ };
137
+ },
138
+ };
@@ -0,0 +1,182 @@
1
+ /**
2
+ * sections/identity.mjs — collect the agent's identity, write the SoT, ensure
3
+ * the agent.ts wrapper, and generate config/agent.env. (order 10)
4
+ *
5
+ * SetupSection contract (design §3.3):
6
+ * { id, label, order, detect, prompt, apply, verify }
7
+ *
8
+ * @module lib/setup/sections/identity
9
+ */
10
+
11
+ "use strict";
12
+
13
+ import { join } from "node:path";
14
+ import { existsSync, readFileSync } from "node:fs";
15
+ import { pathToFileURL } from "node:url";
16
+ import * as io from "../io.mjs";
17
+ import { readAgentJson, mergeAgentJson, ensureSotWrapper } from "../sot.mjs";
18
+ import { isPlaceholder } from "../completeness.mjs";
19
+ import { runGenerator } from "../run-generator.mjs";
20
+ import { cohortPullEnv, pullAndPlan } from "../enroll-from-cohort.mjs";
21
+ import { fetchSelfProfile } from "../../org/client.mjs";
22
+
23
+ const FUNCTIONS = [
24
+ { value: "executive-operator", label: "Executive operator (chief of staff / COO-style)" },
25
+ { value: "technical-leader", label: "Technical leader (eng / platform / CTO-style)" },
26
+ { value: "commercial-leader", label: "Commercial leader (sales / GTM / revenue)" },
27
+ { value: "people-leader", label: "People leader (HR / talent / org)" },
28
+ { value: "finance-leader", label: "Finance leader (finance / corp dev / capital)" },
29
+ { value: "compliance-leader", label: "Compliance leader (legal / risk / regulatory)" },
30
+ ];
31
+ const ALTITUDES = [
32
+ { value: "c-suite", label: "C-suite" },
33
+ { value: "svp", label: "SVP" },
34
+ { value: "vp", label: "VP" },
35
+ { value: "senior-manager", label: "Senior manager" },
36
+ ];
37
+
38
+ /**
39
+ * When the Cohort pull env (COHORT_API_KEY + COHORT_ORG_ID, optional
40
+ * COHORT_AGENT_ID) is present, PULL this agent's record and merge it into
41
+ * config/agent.json BEFORE we prompt — so the prompts default to (and thus
42
+ * CONFIRM) the org-authoritative values rather than starting from scratch. A
43
+ * locally-customised value is never clobbered (mergeProfilePatch drops it unless
44
+ * --force). Fail-open + non-blocking: a missing var / unreachable server is a
45
+ * no-op and the manual prompts stand. Returns the pull plan (or null).
46
+ * @param {string} agentRoot
47
+ * @returns {Promise<object|null>}
48
+ */
49
+ async function maybePullProfile(agentRoot) {
50
+ if (!cohortPullEnv().enabled) return null;
51
+ try {
52
+ const plan = await pullAndPlan({ agentRoot, fetchSelfProfileImpl: fetchSelfProfile });
53
+ if (plan && plan.pulled && plan.patch && Object.keys(plan.patch).length) {
54
+ mergeAgentJson(agentRoot, plan.patch);
55
+ }
56
+ return plan;
57
+ } catch {
58
+ return null;
59
+ }
60
+ }
61
+
62
+ async function archetypeResolves(agentRoot, maestroRoot) {
63
+ try {
64
+ const arch = await import(pathToFileURL(join(maestroRoot, "lib", "archetype.mjs")).href);
65
+ const id = readAgentJson(agentRoot) || {};
66
+ const sel = id.function && id.altitude
67
+ ? { function: id.function, altitude: id.altitude }
68
+ : arch.migrateLegacyArchetype(id.archetype);
69
+ if (!sel) return { ok: false, label: null };
70
+ const p = await arch.resolveArchetype(sel);
71
+ return { ok: true, label: p.label };
72
+ } catch (e) {
73
+ return { ok: false, label: null, error: e && e.message ? e.message : String(e) };
74
+ }
75
+ }
76
+
77
+ export default {
78
+ id: "identity",
79
+ label: "Agent identity",
80
+ order: 10,
81
+
82
+ async detect(ctx) {
83
+ const id = readAgentJson(ctx.agentRoot);
84
+ if (!id) return { status: "unconfigured", missing: ["config/agent.json"] };
85
+ const fields = ["firstName", "lastName", "title", "email"];
86
+ const missing = fields.filter((f) => isPlaceholder(id[f]));
87
+ const arch = await archetypeResolves(ctx.agentRoot, ctx.maestroRoot);
88
+ if (!arch.ok) missing.push("archetype");
89
+ if (missing.length === 0) {
90
+ return { status: "complete", summary: `${id.fullName || id.firstName} — ${id.title} (${arch.label})` };
91
+ }
92
+ return { status: missing.length === fields.length + 1 ? "unconfigured" : "partial", missing };
93
+ },
94
+
95
+ async prompt(ctx) {
96
+ // Cohort-driven setup: pull the org record first so the prompts CONFIRM the
97
+ // pulled values (they become the defaults below) instead of starting blank.
98
+ const plan = await maybePullProfile(ctx.agentRoot);
99
+ if (plan && plan.pulled) {
100
+ const n = plan.changes ? plan.changes.length : 0;
101
+ io.note(`Pulled this agent's profile from Cohort (${n} field${n === 1 ? "" : "s"}). Confirm or edit below.`, { dim: true });
102
+ }
103
+ const cur = readAgentJson(ctx.agentRoot) || {};
104
+ const firstName = await io.text(ctx, { message: "Agent first name", key: "firstName", default: placeholderless(cur.firstName) });
105
+ const lastName = await io.text(ctx, { message: "Agent last name", key: "lastName", default: placeholderless(cur.lastName) });
106
+ const title = await io.text(ctx, { message: "Agent title / role", key: "title", default: placeholderless(cur.title) });
107
+ const email = await io.text(ctx, { message: "Agent work email", key: "email", default: placeholderless(cur.email) });
108
+ const fn = await io.select(ctx, { message: "Function (what the agent owns)", key: "function", choices: FUNCTIONS, default: cur.function || "executive-operator" });
109
+ const altitude = await io.select(ctx, { message: "Altitude (seniority)", key: "altitude", choices: ALTITUDES, default: cur.altitude || "c-suite" });
110
+ const principalName = await io.text(ctx, { message: "Principal full name (who the agent reports to)", key: "principalName", default: (cur.principal && cur.principal.fullName) || "" });
111
+ const principalTitle = await io.text(ctx, { message: "Principal title", key: "principalTitle", default: (cur.principal && cur.principal.title) || "" });
112
+ const principalEmail = await io.text(ctx, { message: "Principal email", key: "principalEmail", default: (cur.principal && cur.principal.email) || "" });
113
+
114
+ ctx.answers.identity = { firstName, lastName, title, email, function: fn, altitude, principalName, principalTitle, principalEmail };
115
+ },
116
+
117
+ async apply(ctx) {
118
+ // Idempotent safety net: ensure the Cohort pull has landed even on an
119
+ // apply-only path (e.g. headless re-run where prompt() was a no-op). Pulled
120
+ // values are merged into config/agent.json; they never clobber local edits.
121
+ await maybePullProfile(ctx.agentRoot);
122
+ const a = (ctx.answers && ctx.answers.identity) || pullFromAnswers(ctx);
123
+ if (!a) {
124
+ // No prompt answers (headless with no --answers) but a pull may have just
125
+ // populated config/agent.json — still run the downstream generators so the
126
+ // archetype + wrapper + env reflect the pulled identity.
127
+ if (cohortPullEnv().enabled) {
128
+ runGenerator("init-archetype.mjs", ctx.agentRoot, { maestroRoot: ctx.maestroRoot });
129
+ ensureSotWrapper(ctx.agentRoot);
130
+ runGenerator("generate-agent-env.mjs", ctx.agentRoot, { maestroRoot: ctx.maestroRoot });
131
+ }
132
+ return;
133
+ }
134
+ const fullName = [a.firstName, a.lastName].filter(Boolean).join(" ").trim();
135
+ const [pf, ...pr] = String(a.principalName || "").trim().split(/\s+/);
136
+ const principalLast = pr.join(" ");
137
+ const patch = {
138
+ firstName: a.firstName, lastName: a.lastName, fullName, title: a.title, email: a.email,
139
+ function: a.function, altitude: a.altitude,
140
+ principal: {
141
+ firstName: pf || "", lastName: principalLast || "",
142
+ fullName: String(a.principalName || "").trim(),
143
+ title: a.principalTitle || "", email: a.principalEmail || "",
144
+ },
145
+ };
146
+ mergeAgentJson(ctx.agentRoot, patch);
147
+ // Normalise archetype (function/altitude → archetype) via init-archetype.
148
+ runGenerator("init-archetype.mjs", ctx.agentRoot, { maestroRoot: ctx.maestroRoot });
149
+ // Born-unified: ensure config/agent.ts is the thin wrapper.
150
+ ensureSotWrapper(ctx.agentRoot);
151
+ // Regenerate the shell env file.
152
+ runGenerator("generate-agent-env.mjs", ctx.agentRoot, { maestroRoot: ctx.maestroRoot });
153
+ },
154
+
155
+ async verify(ctx) {
156
+ const checks = [];
157
+ const id = readAgentJson(ctx.agentRoot);
158
+ const idOk = !!id && ["firstName", "lastName", "title", "email"].every((f) => !isPlaceholder(id[f]));
159
+ checks.push({ name: "identity populated", ok: idOk, remedy: idOk ? undefined : "run `maestro setup --only identity`" });
160
+ const arch = await archetypeResolves(ctx.agentRoot, ctx.maestroRoot);
161
+ checks.push({ name: "archetype resolves", ok: arch.ok, remedy: arch.ok ? undefined : "set a valid function × altitude" });
162
+ const tsPath = join(ctx.agentRoot, "config", "agent.ts");
163
+ const wrapperOk = existsSync(tsPath) && /agent\.json/.test(readFileSync(tsPath, "utf-8"));
164
+ checks.push({ name: "agent.ts is SoT wrapper", ok: wrapperOk, remedy: wrapperOk ? undefined : "ensureSotWrapper failed" });
165
+ return { ok: checks.every((c) => c.ok), checks };
166
+ },
167
+ };
168
+
169
+ function placeholderless(v) { return isPlaceholder(v) ? "" : v; }
170
+
171
+ /** Allow `--answers` files to supply flat keys (no nested identity object). */
172
+ function pullFromAnswers(ctx) {
173
+ const ans = ctx.answers || {};
174
+ if (ans.identity) return ans.identity;
175
+ const flat = ["firstName", "lastName", "title", "email", "function", "altitude", "principalName", "principalTitle", "principalEmail"];
176
+ if (flat.some((k) => k in ans)) {
177
+ const out = {};
178
+ for (const k of flat) out[k] = ans[k];
179
+ return out;
180
+ }
181
+ return null;
182
+ }
@@ -0,0 +1,140 @@
1
+ /**
2
+ * identity.test.mjs — Cohort-driven setup pull in the identity section.
3
+ *
4
+ * Focuses on the pull seam added for Cohort-driven setup: when COHORT_API_KEY +
5
+ * COHORT_ORG_ID are present, prompt()/apply() PULL the agent's record and merge
6
+ * it into config/agent.json so the prompts CONFIRM (rather than start blank); a
7
+ * locally-customised value is protected unless --force; absent vars leave the
8
+ * manual path unchanged. The real client.fetchSelfProfile is exercised via an
9
+ * injected global fetch (no network).
10
+ *
11
+ * Run: node --test lib/setup/sections/identity.test.mjs
12
+ */
13
+ "use strict";
14
+
15
+ import { test } from "node:test";
16
+ import assert from "node:assert/strict";
17
+ import { mkdtempSync, mkdirSync, writeFileSync } from "node:fs";
18
+ import { tmpdir } from "node:os";
19
+ import { join, dirname } from "node:path";
20
+ import { fileURLToPath } from "node:url";
21
+
22
+ import identity from "./identity.mjs";
23
+ import { readAgentJson } from "../sot.mjs";
24
+ import * as io from "../io.mjs";
25
+
26
+ const MAESTRO_ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "..");
27
+
28
+ function tmpRoot() {
29
+ const root = mkdtempSync(join(tmpdir(), "maestro-identity-"));
30
+ mkdirSync(join(root, "config"), { recursive: true });
31
+ return root;
32
+ }
33
+ function ctxFor(root, answers = {}) {
34
+ return { agentRoot: root, maestroRoot: MAESTRO_ROOT, flags: { headless: true }, answers, io };
35
+ }
36
+ function writeAgent(root, obj) {
37
+ writeFileSync(join(root, "config", "agent.json"), JSON.stringify(obj, null, 2) + "\n");
38
+ }
39
+
40
+ const PROFILE = {
41
+ slug: "robin", kind: "agent", displayName: "Robin Vega", firstName: "Robin", lastName: "Vega",
42
+ title: "VP Eng", team: "Platform", role: "engineering-lead", agentEmail: "robin@example.com",
43
+ supervisor: { displayName: "Layla Al-Masri", title: "CTO", email: "layla@example.com" },
44
+ };
45
+
46
+ function withCohortEnv(vars, fn) {
47
+ const keys = ["COHORT_API_KEY", "COHORT_ORG_ID", "COHORT_AGENT_ID", "COHORT_BASE"];
48
+ const saved = Object.fromEntries(keys.map((k) => [k, process.env[k]]));
49
+ for (const k of keys) delete process.env[k];
50
+ Object.assign(process.env, vars);
51
+ return Promise.resolve().then(fn).finally(() => {
52
+ for (const k of keys) { if (saved[k] === undefined) delete process.env[k]; else process.env[k] = saved[k]; }
53
+ });
54
+ }
55
+
56
+ function withFetch(impl, fn) {
57
+ const saved = globalThis.fetch;
58
+ globalThis.fetch = impl;
59
+ return Promise.resolve().then(fn).finally(() => { globalThis.fetch = saved; });
60
+ }
61
+
62
+ const okFetch = (sink) => async (url) => {
63
+ if (sink) sink.push(String(url));
64
+ return { ok: true, status: 200, headers: { get: () => undefined }, json: async () => PROFILE };
65
+ };
66
+
67
+ test("section shape: id/order=10/hooks", () => {
68
+ assert.equal(identity.id, "identity");
69
+ assert.equal(identity.order, 10);
70
+ for (const h of ["detect", "prompt", "apply", "verify"]) assert.equal(typeof identity[h], "function");
71
+ });
72
+
73
+ test("prompt: with COHORT_* env, pulls the profile into config/agent.json", async () => {
74
+ const root = tmpRoot();
75
+ writeAgent(root, { firstName: "UNCONFIGURED" });
76
+ const urls = [];
77
+ await withCohortEnv(
78
+ { COHORT_API_KEY: "k", COHORT_ORG_ID: "acme", COHORT_AGENT_ID: "robin", COHORT_BASE: "https://cohort.example" },
79
+ () => withFetch(okFetch(urls), async () => {
80
+ const ctx = ctxFor(root);
81
+ await identity.prompt(ctx);
82
+ // Headless prompt resolves the answers from the (now pulled) config defaults.
83
+ assert.equal(ctx.answers.identity.firstName, "Robin");
84
+ assert.equal(ctx.answers.identity.email, "robin@example.com");
85
+ })
86
+ );
87
+ assert.ok(urls.some((u) => u.includes("/api/v1/org/members/robin")), "members endpoint hit");
88
+ const id = readAgentJson(root);
89
+ assert.equal(id.firstName, "Robin", "placeholder overwritten by pull");
90
+ assert.equal(id.function, "technical-leader");
91
+ });
92
+
93
+ test("COHORT_AGENT_ID selects /org/members/<id>; absent selects whoami", async () => {
94
+ const root = tmpRoot();
95
+ const urls = [];
96
+ await withCohortEnv(
97
+ { COHORT_API_KEY: "k", COHORT_ORG_ID: "acme", COHORT_BASE: "https://cohort.example" },
98
+ () => withFetch(okFetch(urls), async () => {
99
+ await identity.prompt(ctxFor(root));
100
+ })
101
+ );
102
+ assert.ok(urls.some((u) => u.includes("/api/v1/org/whoami?orgId=acme")), "whoami endpoint hit when no agent id (org sent as the tenant pin, not a member slug)");
103
+ });
104
+
105
+ test("a local override is NOT clobbered by the pull (without --force)", async () => {
106
+ const root = tmpRoot();
107
+ writeAgent(root, { title: "My Custom Title", firstName: "" });
108
+ await withCohortEnv(
109
+ { COHORT_API_KEY: "k", COHORT_ORG_ID: "acme", COHORT_AGENT_ID: "robin", COHORT_BASE: "https://cohort.example" },
110
+ () => withFetch(okFetch(), async () => {
111
+ await identity.prompt(ctxFor(root));
112
+ })
113
+ );
114
+ const id = readAgentJson(root);
115
+ assert.equal(id.title, "My Custom Title", "operator title preserved");
116
+ assert.equal(id.firstName, "Robin", "empty field still filled from org");
117
+ });
118
+
119
+ test("absent COHORT_* env → no pull (manual path unchanged)", async () => {
120
+ const root = tmpRoot();
121
+ writeAgent(root, { firstName: "UNCONFIGURED" });
122
+ let fetched = false;
123
+ await withCohortEnv({}, () => withFetch(async () => { fetched = true; return { ok: true, status: 200, headers: { get: () => undefined }, json: async () => PROFILE }; }, async () => {
124
+ await identity.prompt(ctxFor(root));
125
+ }));
126
+ assert.equal(fetched, false, "no network call without the pull env");
127
+ assert.equal(readAgentJson(root).firstName, "UNCONFIGURED", "config unchanged");
128
+ });
129
+
130
+ test("pull is fail-open: a fetch error leaves config untouched", async () => {
131
+ const root = tmpRoot();
132
+ writeAgent(root, { firstName: "UNCONFIGURED" });
133
+ await withCohortEnv(
134
+ { COHORT_API_KEY: "k", COHORT_ORG_ID: "acme", COHORT_AGENT_ID: "robin", COHORT_BASE: "https://cohort.example" },
135
+ () => withFetch(async () => { throw new Error("network down"); }, async () => {
136
+ await identity.prompt(ctxFor(root)); // must not throw
137
+ })
138
+ );
139
+ assert.equal(readAgentJson(root).firstName, "UNCONFIGURED");
140
+ });
@@ -0,0 +1,153 @@
1
+ /**
2
+ * sections/learning.mjs — the self-learning loop (WS3, order 80).
3
+ *
4
+ * Cross-workstream seam (design §3.2): registered by dropping this file into
5
+ * sections/ (no core edit). It turns the autonomous skill-authoring loop on,
6
+ * seeds its runtime state (counters + the transcript search DB), and verifies
7
+ * the global Stop-hook the loop piggybacks on is wired.
8
+ *
9
+ * Order 80 (after enrich/tools, before verify) per design §3.2 / §4.
10
+ *
11
+ * Every hook is non-blocking under --headless: detect/apply/verify never read
12
+ * the TTY; prompt honours `io` headless resolution. Idempotent.
13
+ *
14
+ * @module lib/setup/sections/learning
15
+ */
16
+
17
+ "use strict";
18
+
19
+ import { join } from "node:path";
20
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, copyFileSync } from "node:fs";
21
+ import { homedir } from "node:os";
22
+ import * as io from "../io.mjs";
23
+ import { countersPath, emptyCounters } from "../../learning/counters.mjs";
24
+
25
+ const CONFIG_REL = join("config", "learning.yaml");
26
+
27
+ /** Is the loop enabled per the on-disk config? (absent file → default-enabled) */
28
+ function configEnabled(agentRoot) {
29
+ const p = join(agentRoot, CONFIG_REL);
30
+ if (!existsSync(p)) return null; // unconfigured
31
+ try {
32
+ const body = readFileSync(p, "utf-8");
33
+ // Cheap scan to avoid a yaml dep in the wizard: an explicit `enabled: false`.
34
+ if (/^\s*enabled\s*:\s*false\b/m.test(body)) return false;
35
+ return true;
36
+ } catch {
37
+ return null;
38
+ }
39
+ }
40
+
41
+ /** Is the global maestro hook-runner wired into ~/.claude/settings.json? */
42
+ export function hookWired() {
43
+ try {
44
+ const p = join(homedir(), ".claude", "settings.json");
45
+ if (!existsSync(p)) return false;
46
+ const body = readFileSync(p, "utf-8");
47
+ // The collective + learning loop share the one hook entry point.
48
+ return /collective\/hook-runner\.mjs/.test(body);
49
+ } catch {
50
+ return false;
51
+ }
52
+ }
53
+
54
+ /** Write config/learning.yaml from the scaffold (or a minimal enabled stub). */
55
+ function ensureConfig(ctx, enabled) {
56
+ const dst = join(ctx.agentRoot, CONFIG_REL);
57
+ if (existsSync(dst)) {
58
+ // Respect an explicit operator choice to disable, but don't rewrite an
59
+ // existing file the operator may have customised.
60
+ if (enabled === false) flipEnabled(dst, false);
61
+ return;
62
+ }
63
+ mkdirSync(join(ctx.agentRoot, "config"), { recursive: true });
64
+ const scaffold = join(ctx.maestroRoot, "scaffold", "config", "learning.yaml");
65
+ if (existsSync(scaffold)) {
66
+ try { copyFileSync(scaffold, dst); } catch { writeStub(dst, enabled); }
67
+ } else {
68
+ writeStub(dst, enabled);
69
+ }
70
+ if (enabled === false) flipEnabled(dst, false);
71
+ }
72
+
73
+ function writeStub(dst, enabled) {
74
+ writeFileSync(dst, `# config/learning.yaml — Self-Learning Loop (WS3)\nenabled: ${enabled === false ? "false" : "true"}\nminTurns: 4\nreviewModel: sonnet\n`);
75
+ }
76
+
77
+ function flipEnabled(dst, value) {
78
+ try {
79
+ let body = readFileSync(dst, "utf-8");
80
+ if (/^\s*enabled\s*:/m.test(body)) body = body.replace(/^\s*enabled\s*:.*$/m, `enabled: ${value}`);
81
+ else body = `enabled: ${value}\n` + body;
82
+ writeFileSync(dst, body);
83
+ } catch { /* fail-open */ }
84
+ }
85
+
86
+ /** Seed state/learning/counters.json if absent. */
87
+ function seedCounters(agentRoot) {
88
+ const p = countersPath(agentRoot);
89
+ if (existsSync(p)) return;
90
+ try {
91
+ mkdirSync(join(agentRoot, "state", "learning"), { recursive: true });
92
+ writeFileSync(p, JSON.stringify(emptyCounters(), null, 2) + "\n");
93
+ } catch { /* fail-open */ }
94
+ }
95
+
96
+ export default {
97
+ id: "learning",
98
+ label: "Self-learning loop",
99
+ order: 80,
100
+
101
+ async detect(ctx) {
102
+ const enabled = configEnabled(ctx.agentRoot);
103
+ if (enabled === null) return { status: "unconfigured", missing: ["config/learning.yaml"] };
104
+ if (enabled === false) return { status: "complete", summary: "self-learning disabled (config)" };
105
+ const missing = [];
106
+ if (!existsSync(countersPath(ctx.agentRoot))) missing.push("state/learning/counters.json");
107
+ if (!hookWired()) missing.push("global hook (run `maestro global-setup`)");
108
+ if (missing.length === 0) return { status: "complete", summary: "self-learning enabled + wired" };
109
+ return { status: "partial", summary: "self-learning enabled", missing };
110
+ },
111
+
112
+ async prompt(ctx) {
113
+ const cur = configEnabled(ctx.agentRoot);
114
+ const on = await io.confirm(ctx, {
115
+ message: "Enable the self-learning loop (the agent autonomously authors & prunes its own skills)?",
116
+ key: "learningEnabled",
117
+ default: cur !== false,
118
+ });
119
+ ctx.answers.learningEnabled = on;
120
+ },
121
+
122
+ async apply(ctx) {
123
+ const enabled = ctx.answers && typeof ctx.answers.learningEnabled === "boolean"
124
+ ? ctx.answers.learningEnabled
125
+ : (configEnabled(ctx.agentRoot) !== false);
126
+ ensureConfig(ctx, enabled);
127
+ if (enabled === false) return;
128
+ seedCounters(ctx.agentRoot);
129
+ // Seed the transcript search DB from any existing session logs (best-effort;
130
+ // the Stop-hook worker + daily catch-up keep it warm thereafter).
131
+ try {
132
+ const sidx = await import("../../learning/session-index.mjs");
133
+ await sidx.ingestAll({ agentRoot: ctx.agentRoot });
134
+ } catch { /* DB is on-demand; a failed seed is non-fatal */ }
135
+ },
136
+
137
+ async verify(ctx) {
138
+ const checks = [];
139
+ const enabled = configEnabled(ctx.agentRoot);
140
+ checks.push({ name: "config/learning.yaml present", ok: enabled !== null, remedy: enabled !== null ? undefined : "run `maestro setup --only learning`" });
141
+ if (enabled === false) {
142
+ checks.push({ name: "self-learning disabled (by config)", ok: true });
143
+ return { ok: true, checks };
144
+ }
145
+ const countersOk = existsSync(countersPath(ctx.agentRoot));
146
+ checks.push({ name: "counters seeded", ok: countersOk, remedy: countersOk ? undefined : "seed state/learning/counters.json" });
147
+ // The hook is a SOFT check: on a fresh repo / CI / non-agent machine it
148
+ // isn't wired yet, which is expected — surface a remedy but don't hard-fail.
149
+ const wired = hookWired();
150
+ checks.push({ name: "global Stop-hook wired", ok: true, remedy: wired ? undefined : "run `maestro global-setup` to enable on this machine" });
151
+ return { ok: checks.every((c) => c.ok), checks };
152
+ },
153
+ };