@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.
- package/.claude/commands/init-agent.md +104 -0
- package/.claude/commands/init-maestro.md +1187 -0
- package/.claude/settings.json +161 -0
- package/.env.example +216 -0
- package/README.md +632 -0
- package/agents/browser-operator/agent.md +52 -0
- package/agents/calendar-ops/agent.md +50 -0
- package/agents/communications/agent.md +96 -0
- package/agents/decision-log/agent.md +65 -0
- package/agents/desktop-operator/agent.md +59 -0
- package/agents/gmail-operator/agent.md +62 -0
- package/agents/inbound-dispatcher/agent.md +66 -0
- package/agents/inbox-processor/agent.md +39 -0
- package/agents/pmo-execution/agent.md +60 -0
- package/agents/session-spawner/agent.md +64 -0
- package/agents/slack-operator/agent.md +60 -0
- package/agents/whatsapp-operator/agent.md +60 -0
- package/agents/workflow-automation/agent.md +61 -0
- package/archetypes/altitudes/c-suite.yaml +58 -0
- package/archetypes/altitudes/founder.yaml +68 -0
- package/archetypes/altitudes/senior-manager.yaml +63 -0
- package/archetypes/altitudes/svp.yaml +60 -0
- package/archetypes/altitudes/vp.yaml +50 -0
- package/archetypes/archetype.schema.json +77 -0
- package/archetypes/base.yaml +47 -0
- package/archetypes/capabilities/commercial-leader.yaml +159 -0
- package/archetypes/capabilities/compliance-officer.yaml +159 -0
- package/archetypes/capabilities/executive-operator.yaml +169 -0
- package/archetypes/capabilities/finance-leader.yaml +162 -0
- package/archetypes/capabilities/operations-leader.yaml +154 -0
- package/archetypes/capabilities/product-leader.yaml +148 -0
- package/archetypes/capabilities/technical-leader.yaml +146 -0
- package/archetypes/functions/commercial-leader.yaml +62 -0
- package/archetypes/functions/compliance-officer.yaml +64 -0
- package/archetypes/functions/executive-operator.yaml +70 -0
- package/archetypes/functions/finance-leader.yaml +70 -0
- package/archetypes/functions/operations-leader.yaml +62 -0
- package/archetypes/functions/product-leader.yaml +61 -0
- package/archetypes/functions/technical-leader.yaml +57 -0
- package/bin/cohort-mcp.mjs +81 -0
- package/bin/maestro.mjs +3516 -0
- package/bin/maestro.test.mjs +1015 -0
- package/desktop-control/README.md +56 -0
- package/desktop-control/app-profiles/gmail.yaml +120 -0
- package/desktop-control/app-profiles/slack.yaml +315 -0
- package/desktop-control/app-profiles/whatsapp.yaml +107 -0
- package/docs/architecture/agent-topology.md +2239 -0
- package/docs/architecture/archetype-agent-factory.md +110 -0
- package/docs/architecture/collective-memory-and-org-mesh.md +115 -0
- package/docs/architecture/continuous-monitoring.md +221 -0
- package/docs/architecture/mcp-capability-map.md +585 -0
- package/docs/architecture/system-architecture.md +1272 -0
- package/docs/company-context/README.md +40 -0
- package/docs/guides/agent-persona-setup.md +600 -0
- package/docs/guides/agents-observe-setup.md +64 -0
- package/docs/guides/billing-console-keys.md +88 -0
- package/docs/guides/ccxray-diagnostics.md +65 -0
- package/docs/guides/channel-bus.md +127 -0
- package/docs/guides/claude-mem-setup.md +79 -0
- package/docs/guides/claude-pace-setup.md +56 -0
- package/docs/guides/claudraband-sessions.md +98 -0
- package/docs/guides/clawteam-swarm.md +116 -0
- package/docs/guides/code-review-graph-setup.md +86 -0
- package/docs/guides/email-setup.md +431 -0
- package/docs/guides/mac-mini.md +119 -0
- package/docs/guides/media-generation-setup.md +349 -0
- package/docs/guides/model-routing.md +162 -0
- package/docs/guides/observability-otel.md +265 -0
- package/docs/guides/org-onboarding.md +132 -0
- package/docs/guides/outbound-governance-setup.md +437 -0
- package/docs/guides/pdf-generation-setup.md +315 -0
- package/docs/guides/poller-daemon-setup.md +563 -0
- package/docs/guides/rag-context-setup.md +459 -0
- package/docs/guides/self-optimization-pattern.md +82 -0
- package/docs/guides/setup-wizard.md +178 -0
- package/docs/guides/slack-setup.md +350 -0
- package/docs/guides/telegram-setup.md +227 -0
- package/docs/guides/twilio-subaccounts-setup.md +223 -0
- package/docs/guides/verification.md +128 -0
- package/docs/guides/voice-mode.md +188 -0
- package/docs/guides/voice-sms-setup.md +698 -0
- package/docs/guides/webhook-relay-setup.md +349 -0
- package/docs/guides/whatsapp-setup.md +288 -0
- package/docs/prompts/board-pack-cover-template.md +36 -0
- package/docs/prompts/decision-recommendation-template.md +88 -0
- package/docs/prompts/followup-message-template.md +141 -0
- package/docs/prompts/investor-letter-template.md +52 -0
- package/docs/prompts/morning-brief-template.md +82 -0
- package/docs/prompts/presentation-template.md +58 -0
- package/docs/prompts/weekly-strategic-memo-template.md +104 -0
- package/docs/research/hallucinated-tool-output-investigation.md +151 -0
- package/docs/runbooks/backup-restore.md +205 -0
- package/docs/runbooks/cohort-cutover.md +129 -0
- package/docs/runbooks/fleet-operations.md +200 -0
- package/docs/runbooks/incident-response.md +226 -0
- package/docs/runbooks/mac-mini-bootstrap.md +431 -0
- package/docs/runbooks/perpetual-operations.md +509 -0
- package/docs/runbooks/recovery-and-failover.md +260 -0
- package/framework-features.json +267 -0
- package/ingest/README.md +87 -0
- package/lib/action-executor.js +689 -0
- package/lib/action-executor.test.mjs +871 -0
- package/lib/agent-root.mjs +37 -0
- package/lib/archetype.mjs +236 -0
- package/lib/archetype.test.mjs +132 -0
- package/lib/autonomy.mjs +114 -0
- package/lib/autonomy.test.mjs +66 -0
- package/lib/backlog.mjs +358 -0
- package/lib/backlog.test.mjs +266 -0
- package/lib/budget-guard.mjs +279 -0
- package/lib/budget-guard.test.mjs +291 -0
- package/lib/cadence-bus-schedule.test.mjs +194 -0
- package/lib/cadence-bus.mjs +1120 -0
- package/lib/cadence-bus.test.mjs +720 -0
- package/lib/cadences.mjs +205 -0
- package/lib/cadences.test.mjs +125 -0
- package/lib/capability.mjs +154 -0
- package/lib/capability.test.mjs +78 -0
- package/lib/channels/base-adapter.mjs +719 -0
- package/lib/channels/base-adapter.test.mjs +590 -0
- package/lib/channels/channel.mjs +128 -0
- package/lib/channels/channels.test.mjs +371 -0
- package/lib/channels/contract.mjs +215 -0
- package/lib/channels/contract.test.mjs +137 -0
- package/lib/channels/conversation-resolver.mjs +95 -0
- package/lib/channels/gmail/adapter.mjs +87 -0
- package/lib/channels/inbox-item.mjs +255 -0
- package/lib/channels/inbox-item.test.mjs +335 -0
- package/lib/channels/index.mjs +94 -0
- package/lib/channels/orgmail/adapter.mjs +353 -0
- package/lib/channels/orgmail/adapter.test.mjs +311 -0
- package/lib/channels/pairing.mjs +363 -0
- package/lib/channels/pairing.test.mjs +270 -0
- package/lib/channels/registry.mjs +164 -0
- package/lib/channels/slack/adapter.mjs +317 -0
- package/lib/channels/slack-adapter.test.mjs +212 -0
- package/lib/channels/sms/adapter.mjs +43 -0
- package/lib/channels/telegram/adapter.mjs +432 -0
- package/lib/channels/telegram-adapter.test.mjs +306 -0
- package/lib/channels/voice/adapter.mjs +301 -0
- package/lib/channels/voice/adapter.test.mjs +278 -0
- package/lib/channels/whatsapp/adapter-baileys.mjs +587 -0
- package/lib/channels/whatsapp/adapter-baileys.test.mjs +359 -0
- package/lib/channels/whatsapp/adapter-twilio.mjs +65 -0
- package/lib/channels/whatsapp/baileys-typing.test.mjs +154 -0
- package/lib/charter.mjs +256 -0
- package/lib/charter.test.mjs +89 -0
- package/lib/claude-bin.mjs +134 -0
- package/lib/claude-bin.test.mjs +75 -0
- package/lib/collective/capture.mjs +185 -0
- package/lib/collective/capture.test.mjs +121 -0
- package/lib/collective/cards.mjs +201 -0
- package/lib/collective/cards.test.mjs +114 -0
- package/lib/collective/config.mjs +186 -0
- package/lib/collective/config.test.mjs +123 -0
- package/lib/collective/global-config.mjs +113 -0
- package/lib/collective/global-config.test.mjs +75 -0
- package/lib/collective/presence.mjs +201 -0
- package/lib/collective/presence.test.mjs +95 -0
- package/lib/collective/recall.mjs +215 -0
- package/lib/collective/recall.test.mjs +116 -0
- package/lib/comms/send-gate.mjs +554 -0
- package/lib/comms/send-gate.test.mjs +577 -0
- package/lib/comms.mjs +67 -0
- package/lib/comms.test.mjs +41 -0
- package/lib/diagnostics/alerts.mjs +424 -0
- package/lib/diagnostics/alerts.test.mjs +318 -0
- package/lib/diagnostics/backup-freshness.mjs +188 -0
- package/lib/diagnostics/backup-freshness.test.mjs +185 -0
- package/lib/diagnostics/counters.mjs +269 -0
- package/lib/diagnostics/counters.test.mjs +206 -0
- package/lib/diagnostics/events.mjs +188 -0
- package/lib/diagnostics/events.test.mjs +290 -0
- package/lib/diagnostics/otel.mjs +237 -0
- package/lib/diagnostics/otel.test.mjs +196 -0
- package/lib/diagnostics/trace.mjs +216 -0
- package/lib/diagnostics/trace.test.mjs +251 -0
- package/lib/env-compat.mjs +74 -0
- package/lib/env-compat.test.mjs +104 -0
- package/lib/feature-init.mjs +331 -0
- package/lib/fs-atomic.mjs +112 -0
- package/lib/fs-atomic.test.mjs +72 -0
- package/lib/fs-ownership.mjs +111 -0
- package/lib/fs-ownership.test.mjs +158 -0
- package/lib/hooks/bus.mjs +347 -0
- package/lib/hooks/bus.test.mjs +387 -0
- package/lib/index.js +16 -0
- package/lib/learning/config.mjs +106 -0
- package/lib/learning/config.test.mjs +75 -0
- package/lib/learning/counters.mjs +156 -0
- package/lib/learning/counters.test.mjs +69 -0
- package/lib/learning/curator-consolidate.test.mjs +238 -0
- package/lib/learning/curator.mjs +453 -0
- package/lib/learning/curator.test.mjs +106 -0
- package/lib/learning/log.mjs +40 -0
- package/lib/learning/reflect.mjs +534 -0
- package/lib/learning/reflect.test.mjs +0 -0
- package/lib/learning/session-index.mjs +352 -0
- package/lib/learning/session-index.test.mjs +125 -0
- package/lib/learning/skill-writer.mjs +474 -0
- package/lib/learning/skill-writer.test.mjs +210 -0
- package/lib/mcp/server.mjs +328 -0
- package/lib/mcp/server.test.mjs +400 -0
- package/lib/model-router/auth-profiles.mjs +758 -0
- package/lib/model-router/auth-profiles.test.mjs +580 -0
- package/lib/model-router/catalog/anthropic.yaml +153 -0
- package/lib/model-router/catalog/deepseek.yaml +86 -0
- package/lib/model-router/catalog/moonshot.yaml +81 -0
- package/lib/model-router/catalog/qwen.yaml +114 -0
- package/lib/model-router/catalog.mjs +925 -0
- package/lib/model-router/catalog.test.mjs +385 -0
- package/lib/model-router/economics.mjs +564 -0
- package/lib/model-router/economics.test.mjs +344 -0
- package/lib/model-router/failover.mjs +298 -0
- package/lib/model-router/failover.test.mjs +439 -0
- package/lib/model-router/health.mjs +453 -0
- package/lib/model-router/health.test.mjs +338 -0
- package/lib/model-router/integration-coverage.test.mjs +829 -0
- package/lib/model-router/integration.test.mjs +564 -0
- package/lib/model-router/ledger.mjs +402 -0
- package/lib/model-router/ledger.test.mjs +382 -0
- package/lib/model-router/llm-task.mjs +515 -0
- package/lib/model-router/llm-task.test.mjs +392 -0
- package/lib/model-router/org-credentials.mjs +260 -0
- package/lib/model-router/org-credentials.test.mjs +265 -0
- package/lib/model-router/pricing-refresh.mjs +463 -0
- package/lib/model-router/pricing-refresh.test.mjs +286 -0
- package/lib/model-router/reconcile.mjs +429 -0
- package/lib/model-router/reconcile.test.mjs +316 -0
- package/lib/model-router/repair.mjs +471 -0
- package/lib/model-router/repair.test.mjs +180 -0
- package/lib/model-router/resolve.mjs +1206 -0
- package/lib/model-router/spawn.mjs +497 -0
- package/lib/model-router/spawn.test.mjs +425 -0
- package/lib/model-router/taxonomy.mjs +893 -0
- package/lib/model-router/taxonomy.test.mjs +410 -0
- package/lib/model-router.mjs +677 -0
- package/lib/model-router.test.mjs +907 -0
- package/lib/org/activity.mjs +211 -0
- package/lib/org/activity.test.mjs +134 -0
- package/lib/org/approvals.mjs +448 -0
- package/lib/org/approvals.test.mjs +216 -0
- package/lib/org/awareness.mjs +222 -0
- package/lib/org/awareness.test.mjs +159 -0
- package/lib/org/board.mjs +229 -0
- package/lib/org/board.test.mjs +177 -0
- package/lib/org/bootstrap-context.mjs +169 -0
- package/lib/org/bootstrap-context.test.mjs +153 -0
- package/lib/org/client.mjs +1628 -0
- package/lib/org/client.test.mjs +1107 -0
- package/lib/org/cohort-client.mjs +67 -0
- package/lib/org/cohort-client.test.mjs +126 -0
- package/lib/org/cost-sync.mjs +227 -0
- package/lib/org/cost-sync.test.mjs +153 -0
- package/lib/org/doctor.mjs +212 -0
- package/lib/org/doctor.test.mjs +212 -0
- package/lib/org/handoff.mjs +293 -0
- package/lib/org/handoff.test.mjs +269 -0
- package/lib/org/integration-tools.mjs +182 -0
- package/lib/org/integration-tools.test.mjs +160 -0
- package/lib/org/keys.mjs +131 -0
- package/lib/org/keys.test.mjs +92 -0
- package/lib/org/knowledge.mjs +463 -0
- package/lib/org/knowledge.test.mjs +319 -0
- package/lib/org/leases.mjs +335 -0
- package/lib/org/leases.test.mjs +235 -0
- package/lib/org/mesh-integration.test.mjs +127 -0
- package/lib/org/mesh.mjs +459 -0
- package/lib/org/mesh.test.mjs +345 -0
- package/lib/org/messaging.mjs +503 -0
- package/lib/org/messaging.test.mjs +238 -0
- package/lib/org/policy.mjs +345 -0
- package/lib/org/policy.test.mjs +237 -0
- package/lib/org/protocol.checksum +1 -0
- package/lib/org/protocol.checksum.test.mjs +90 -0
- package/lib/org/protocol.mjs +967 -0
- package/lib/org/protocol.test.mjs +264 -0
- package/lib/org/registry.mjs +194 -0
- package/lib/org/registry.test.mjs +100 -0
- package/lib/org/tool-surface-integration.test.mjs +120 -0
- package/lib/org/tool-surface.mjs +2535 -0
- package/lib/org/tool-surface.test.mjs +589 -0
- package/lib/org/ui-parity.mjs +3236 -0
- package/lib/org/ui-parity.test.mjs +348 -0
- package/lib/org/verify.mjs +176 -0
- package/lib/org/verify.test.mjs +194 -0
- package/lib/rag/embed.mjs +188 -0
- package/lib/rag/indexer.mjs +425 -0
- package/lib/rag/rag.test.mjs +505 -0
- package/lib/rag/search.mjs +475 -0
- package/lib/rate-guard.mjs +246 -0
- package/lib/rate-guard.test.mjs +201 -0
- package/lib/render.mjs +112 -0
- package/lib/render.test.mjs +68 -0
- package/lib/resource-governor.mjs +297 -0
- package/lib/resource-governor.test.mjs +262 -0
- package/lib/scheduling/dynamic-jobs.mjs +675 -0
- package/lib/scheduling/dynamic-jobs.test.mjs +344 -0
- package/lib/scheduling/jitter.mjs +0 -0
- package/lib/scheduling/jitter.test.mjs +140 -0
- package/lib/secrets/broker.mjs +315 -0
- package/lib/secrets/broker.test.mjs +280 -0
- package/lib/secrets/providers.mjs +461 -0
- package/lib/secrets/providers.test.mjs +274 -0
- package/lib/security/audit-engine.mjs +684 -0
- package/lib/security/audit-engine.test.mjs +389 -0
- package/lib/security/coerce-args.mjs +552 -0
- package/lib/security/coerce-args.test.mjs +281 -0
- package/lib/security/dangerous-tools.mjs +97 -0
- package/lib/security/dangerous-tools.test.mjs +68 -0
- package/lib/security/external-content.mjs +145 -0
- package/lib/security/external-content.test.mjs +67 -0
- package/lib/security/redact.mjs +592 -0
- package/lib/security/redact.test.mjs +441 -0
- package/lib/security/secret-equal.mjs +73 -0
- package/lib/security/secret-equal.test.mjs +55 -0
- package/lib/session-permissions.mjs +101 -0
- package/lib/session-permissions.test.mjs +100 -0
- package/lib/setup/claude-probe.mjs +74 -0
- package/lib/setup/completeness.mjs +175 -0
- package/lib/setup/completeness.test.mjs +110 -0
- package/lib/setup/context-pack.mjs +173 -0
- package/lib/setup/context-pack.test.mjs +89 -0
- package/lib/setup/enrich.mjs +277 -0
- package/lib/setup/enrich.test.mjs +115 -0
- package/lib/setup/enroll-from-cohort.mjs +441 -0
- package/lib/setup/enroll-from-cohort.test.mjs +233 -0
- package/lib/setup/integration.test.mjs +162 -0
- package/lib/setup/io.mjs +360 -0
- package/lib/setup/io.test.mjs +77 -0
- package/lib/setup/run-generator.mjs +81 -0
- package/lib/setup/runner.mjs +244 -0
- package/lib/setup/runner.test.mjs +132 -0
- package/lib/setup/sections/comms.mjs +173 -0
- package/lib/setup/sections/company.mjs +120 -0
- package/lib/setup/sections/enrich.mjs +138 -0
- package/lib/setup/sections/identity.mjs +182 -0
- package/lib/setup/sections/identity.test.mjs +140 -0
- package/lib/setup/sections/learning.mjs +153 -0
- package/lib/setup/sections/learning.test.mjs +81 -0
- package/lib/setup/sections/messaging.mjs +219 -0
- package/lib/setup/sections/messaging.test.mjs +127 -0
- package/lib/setup/sections/model.mjs +102 -0
- package/lib/setup/sections/operating-model.mjs +78 -0
- package/lib/setup/sections/org.mjs +475 -0
- package/lib/setup/sections/org.test.mjs +313 -0
- package/lib/setup/sections/orgmail.mjs +173 -0
- package/lib/setup/sections/orgmail.test.mjs +118 -0
- package/lib/setup/sections/recovery.mjs +159 -0
- package/lib/setup/sections/recovery.test.mjs +98 -0
- package/lib/setup/sections/tools.mjs +132 -0
- package/lib/setup/sections/verify.mjs +97 -0
- package/lib/setup/sot.mjs +205 -0
- package/lib/setup/sot.test.mjs +81 -0
- package/lib/setup/state.mjs +151 -0
- package/lib/setup/state.test.mjs +92 -0
- package/lib/singleton.js +229 -0
- package/lib/singleton.test.mjs +135 -0
- package/lib/telemetry/alerts.mjs +216 -0
- package/lib/telemetry/alerts.test.mjs +109 -0
- package/lib/telemetry/collect.mjs +512 -0
- package/lib/telemetry/collect.test.mjs +202 -0
- package/lib/tool-definitions-integration.test.mjs +83 -0
- package/lib/tool-definitions.js +738 -0
- package/lib/tool-definitions.test.mjs +437 -0
- package/lib/util/fetch-timeout.mjs +136 -0
- package/lib/util/fetch-timeout.test.mjs +202 -0
- package/lib/util/reconnect.mjs +343 -0
- package/lib/util/reconnect.test.mjs +369 -0
- package/lib/util/unhandled.mjs +205 -0
- package/lib/util/unhandled.test.mjs +216 -0
- package/lib/voice/context-loader.mjs +466 -0
- package/lib/voice/index.mjs +100 -0
- package/lib/voice/openai-realtime.mjs +510 -0
- package/lib/voice/outbound.mjs +542 -0
- package/lib/voice/outbound.test.mjs +69 -0
- package/lib/voice/post-call-brief.mjs +428 -0
- package/lib/voice/provider.mjs +52 -0
- package/lib/voice/session-rotation.mjs +257 -0
- package/lib/voice/stt.mjs +161 -0
- package/lib/voice/stt.test.mjs +226 -0
- package/lib/voice/tool-bridge.mjs +370 -0
- package/lib/voice/tts.mjs +104 -0
- package/lib/voice/twilio-sip-bridge.mjs +288 -0
- package/lib/voice/voice.test.mjs +990 -0
- package/mcp/README.md +80 -0
- package/package.json +151 -0
- package/plugins/maestro-skills/plugin.json +139 -0
- package/plugins/maestro-skills/skills/agents-observe.md +110 -0
- package/plugins/maestro-skills/skills/board-deck.md +68 -0
- package/plugins/maestro-skills/skills/books-close.md +77 -0
- package/plugins/maestro-skills/skills/brand-steward.md +121 -0
- package/plugins/maestro-skills/skills/calendar-plan.md +57 -0
- package/plugins/maestro-skills/skills/call-working-sessions.md +124 -0
- package/plugins/maestro-skills/skills/ccxray-diagnostics.md +91 -0
- package/plugins/maestro-skills/skills/claude-pace.md +61 -0
- package/plugins/maestro-skills/skills/code-review-graph.md +99 -0
- package/plugins/maestro-skills/skills/crm-pipeline.md +65 -0
- package/plugins/maestro-skills/skills/decision-brief.md +89 -0
- package/plugins/maestro-skills/skills/directory-hygiene.md +125 -0
- package/plugins/maestro-skills/skills/draft-comms.md +84 -0
- package/plugins/maestro-skills/skills/evening-wrap.md +53 -0
- package/plugins/maestro-skills/skills/files-find.md +65 -0
- package/plugins/maestro-skills/skills/generative-ui.md +228 -0
- package/plugins/maestro-skills/skills/hiring-triage.md +74 -0
- package/plugins/maestro-skills/skills/inbox-triage.md +61 -0
- package/plugins/maestro-skills/skills/mail-triage.md +86 -0
- package/plugins/maestro-skills/skills/morning-brief.md +54 -0
- package/plugins/maestro-skills/skills/native-artifacts.md +157 -0
- package/plugins/maestro-skills/skills/org-board.md +133 -0
- package/plugins/maestro-skills/skills/org-credential.md +68 -0
- package/plugins/maestro-skills/skills/org-recall.md +81 -0
- package/plugins/maestro-skills/skills/pipeline-review.md +76 -0
- package/plugins/maestro-skills/skills/regulatory-status.md +81 -0
- package/plugins/maestro-skills/skills/router-why.md +78 -0
- package/plugins/maestro-skills/skills/schedule-meeting.md +91 -0
- package/plugins/maestro-skills/skills/session-search.md +71 -0
- package/plugins/maestro-skills/skills/set-reminder.md +93 -0
- package/plugins/maestro-skills/skills/slack-followup.md +64 -0
- package/plugins/maestro-skills/skills/team-activity.md +86 -0
- package/plugins/maestro-skills/skills/weekly-memo.md +70 -0
- package/policies/action-classification.yaml +114 -0
- package/policies/ai-disclosure.yaml +294 -0
- package/policies/communication-style.md +139 -0
- package/policies/information-barriers.yaml +118 -0
- package/policies/prompt-injection-defence.yaml +138 -0
- package/public/assets/icon-dark.png +0 -0
- package/public/assets/icon-dark.svg +9 -0
- package/public/assets/icon-light.svg +9 -0
- package/public/assets/logo-dark.svg +15 -0
- package/public/assets/logo-light.svg +15 -0
- package/scaffold/.mcp.json +7 -0
- package/scaffold/CLAUDE.md +368 -0
- package/scaffold/config/agent.json +55 -0
- package/scaffold/config/agent.ts +76 -0
- package/scaffold/config/agent.ts.example +89 -0
- package/scaffold/config/alerts.yaml +23 -0
- package/scaffold/config/allowlist.yaml.example +25 -0
- package/scaffold/config/caller-id-map.yaml +46 -0
- package/scaffold/config/collective.yaml +49 -0
- package/scaffold/config/company.json +20 -0
- package/scaffold/config/known-agents.json +6 -0
- package/scaffold/config/learning.yaml +55 -0
- package/scaffold/config/model-routing.yaml.example +104 -0
- package/scaffold/config/org.yaml +25 -0
- package/scaffold/config/orgmail.yaml.example +19 -0
- package/scaffold/config/recovery.yaml +72 -0
- package/scaffold/config/secrets.yaml +27 -0
- package/scaffold/config/slack.yaml.example +35 -0
- package/scaffold/config/telegram.yaml.example +38 -0
- package/scaffold/config/voice.yaml.example +89 -0
- package/scaffold/config/whatsapp.yaml.example +39 -0
- package/schedules/README.md +49 -0
- package/schedules/triggers/backlog-executor.md +102 -0
- package/schedules/triggers/brand-steward.md +72 -0
- package/schedules/triggers/daily-evening-wrap.md +159 -0
- package/schedules/triggers/daily-midday-sweep.md +58 -0
- package/schedules/triggers/daily-morning-brief.md +55 -0
- package/schedules/triggers/directory-hygiene.md +81 -0
- package/schedules/triggers/dynamic-jobs.md +40 -0
- package/schedules/triggers/inbox-processor.md +115 -0
- package/schedules/triggers/meeting-action-capture.md +60 -0
- package/schedules/triggers/meeting-prep.md +69 -0
- package/schedules/triggers/messaging-inbound.md +50 -0
- package/schedules/triggers/org-pulse.md +24 -0
- package/schedules/triggers/quarterly-self-assessment.md +54 -0
- package/schedules/triggers/weekly-engineering-health.md +37 -0
- package/schedules/triggers/weekly-execution.md +65 -0
- package/schedules/triggers/weekly-hiring.md +53 -0
- package/schedules/triggers/weekly-priorities.md +38 -0
- package/schedules/triggers/weekly-strategic-memo.md +124 -0
- package/scripts/archive-email.sh +55 -0
- package/scripts/cadence/cadence-status.mjs +36 -0
- package/scripts/cadence/enqueue-cadence-tick.mjs +174 -0
- package/scripts/cadence/enqueue-cadence-tick.test.mjs +187 -0
- package/scripts/cadence/launchd-cadence-wrapper.sh +85 -0
- package/scripts/cadence/launchd-cloud-relay-wrapper.sh +95 -0
- package/scripts/cadence/launchd-socket-mode-wrapper.sh +95 -0
- package/scripts/ci/check-docs-accuracy.mjs +493 -0
- package/scripts/ci/check-docs-accuracy.test.mjs +409 -0
- package/scripts/ci/check-exports-exist.mjs +140 -0
- package/scripts/ci/check-files-exist.mjs +107 -0
- package/scripts/ci/check-no-build-artifacts.mjs +111 -0
- package/scripts/ci/check-no-build-artifacts.test.mjs +71 -0
- package/scripts/ci/check-no-confidential.mjs +198 -0
- package/scripts/ci/check-no-conflict-markers.mjs +169 -0
- package/scripts/ci/check-no-residual-identity.mjs +163 -0
- package/scripts/ci/check-no-residual-identity.test.mjs +89 -0
- package/scripts/ci/check-tarball-fidelity.mjs +205 -0
- package/scripts/ci/check-unresolved-tokens.mjs +83 -0
- package/scripts/ci/check.mjs +109 -0
- package/scripts/ci/check.test.mjs +194 -0
- package/scripts/ci/run-coverage.mjs +82 -0
- package/scripts/ci/run-tests.mjs +71 -0
- package/scripts/cloud-relay/README.md +59 -0
- package/scripts/cloud-relay/index.mjs +233 -0
- package/scripts/cloud-relay/package.json +15 -0
- package/scripts/cloud-relay/railway.json +13 -0
- package/scripts/cloud-relay/voice/README.md +94 -0
- package/scripts/cloud-relay/voice/package-lock.json +39 -0
- package/scripts/cloud-relay/voice/package.json +16 -0
- package/scripts/cloud-relay/voice/railway.json +13 -0
- package/scripts/cloud-relay/voice/server.mjs +532 -0
- package/scripts/collective/hook-runner.mjs +211 -0
- package/scripts/collective/hook-runner.test.mjs +90 -0
- package/scripts/collective/org-pulse.mjs +72 -0
- package/scripts/collective/org-sync.mjs +61 -0
- package/scripts/collective/recall.mjs +45 -0
- package/scripts/collective/who.mjs +30 -0
- package/scripts/comms-monitor.sh +288 -0
- package/scripts/configure-whatsapp-sandbox.sh +201 -0
- package/scripts/continuous-monitor.sh +91 -0
- package/scripts/cost/fleet-digest.mjs +407 -0
- package/scripts/cost/fleet-digest.test.mjs +207 -0
- package/scripts/cost/track-claude-usage.mjs +169 -0
- package/scripts/daemon/agent-daemon.mjs +989 -0
- package/scripts/daemon/agent-daemon.test.mjs +525 -0
- package/scripts/daemon/cadence-consumer-governance.test.mjs +220 -0
- package/scripts/daemon/cadence-consumer.mjs +1080 -0
- package/scripts/daemon/cadence-consumer.test.mjs +770 -0
- package/scripts/daemon/cadence-handlers.mjs +1121 -0
- package/scripts/daemon/cadence-handlers.test.mjs +617 -0
- package/scripts/daemon/classifier.mjs +704 -0
- package/scripts/daemon/classifier.test.mjs +238 -0
- package/scripts/daemon/classify-kind.mjs +54 -0
- package/scripts/daemon/classify-kind.test.mjs +40 -0
- package/scripts/daemon/context-compiler.mjs +605 -0
- package/scripts/daemon/context-compiler.test.mjs +300 -0
- package/scripts/daemon/dispatcher-cooldown.test.mjs +122 -0
- package/scripts/daemon/dispatcher-governance.test.mjs +886 -0
- package/scripts/daemon/dispatcher.mjs +1516 -0
- package/scripts/daemon/health.mjs +72 -0
- package/scripts/daemon/inbox-deferral.mjs +210 -0
- package/scripts/daemon/inbox-deferral.test.mjs +242 -0
- package/scripts/daemon/integration.test.mjs +149 -0
- package/scripts/daemon/launchd-wrapper-generic.sh +96 -0
- package/scripts/daemon/launchd-wrapper-slack-events.sh +37 -0
- package/scripts/daemon/launchd-wrapper.sh +91 -0
- package/scripts/daemon/lib/session-router.mjs +274 -0
- package/scripts/daemon/lib/session-router.test.mjs +295 -0
- package/scripts/daemon/maestro-daemon.mjs +275 -0
- package/scripts/daemon/prompt-builder.mjs +685 -0
- package/scripts/daemon/prompt-builder.test.mjs +213 -0
- package/scripts/daemon/responder.mjs +854 -0
- package/scripts/daemon/session-lock.mjs +721 -0
- package/scripts/daemon/session-lock.test.mjs +252 -0
- package/scripts/daemon/session-outcomes.mjs +640 -0
- package/scripts/daemon/session-outcomes.test.mjs +533 -0
- package/scripts/daemon/typing-registry.mjs +90 -0
- package/scripts/daemon/typing-registry.test.mjs +77 -0
- package/scripts/daemon/voice-webhook-server.mjs +804 -0
- package/scripts/decisions/capture-decision.mjs +116 -0
- package/scripts/disclosure_assessment.py +873 -0
- package/scripts/disclosure_boundaries.py +562 -0
- package/scripts/email-signature-principal.html +52 -0
- package/scripts/email-signature.html +60 -0
- package/scripts/email_quote_thread.py +167 -0
- package/scripts/email_thread_dedup.py +362 -0
- package/scripts/emergency-stop.sh +81 -0
- package/scripts/healthcheck.sh +116 -0
- package/scripts/hooks/block-mcp-cohort-send.sh +15 -0
- package/scripts/hooks/block-mcp-slack-send.sh +7 -0
- package/scripts/hooks/post-action-log.sh +126 -0
- package/scripts/hooks/pre-send-audit.sh +174 -0
- package/scripts/hooks/pre-send-audit.test.mjs +215 -0
- package/scripts/hooks/session-end-log.sh +27 -0
- package/scripts/hooks/session-start-banner.sh +115 -0
- package/scripts/huddle/audio-bridge.mjs +664 -0
- package/scripts/huddle/boot-slack-cdp.sh +102 -0
- package/scripts/huddle/huddle-controller.mjs +942 -0
- package/scripts/huddle/huddle-server.mjs +1229 -0
- package/scripts/huddle/launch-slack.sh +232 -0
- package/scripts/huddle/openai-realtime-bridge.mjs +462 -0
- package/scripts/huddle/package-lock.json +62 -0
- package/scripts/huddle/package.json +22 -0
- package/scripts/huddle/setup-audio.sh +239 -0
- package/scripts/huddle/start-call.mjs +318 -0
- package/scripts/huddle/test-pipeline.mjs +263 -0
- package/scripts/learning/consolidate-skills.mjs +72 -0
- package/scripts/learning/session-search.mjs +125 -0
- package/scripts/llm_email_dedup.py +442 -0
- package/scripts/local-triggers/generate-plists.sh +432 -0
- package/scripts/local-triggers/generate-plists.test.mjs +413 -0
- package/scripts/local-triggers/install-all.sh +49 -0
- package/scripts/local-triggers/plists/.gitkeep +0 -0
- package/scripts/local-triggers/run-trigger.sh +63 -0
- package/scripts/local-triggers/templates/rag-reindex.plist.template +47 -0
- package/scripts/local-triggers/templates/voice-relay-poller.plist.template +54 -0
- package/scripts/local-triggers/templates/voice-tunnel.plist.template +55 -0
- package/scripts/local-triggers/templates/voice-webhook.plist.template +51 -0
- package/scripts/maintenance/backup-to-cloud.sh +124 -0
- package/scripts/maintenance/health-check.sh +377 -0
- package/scripts/media-generation/README.md +105 -0
- package/scripts/media-generation/gemini-image-client.mjs +173 -0
- package/scripts/media-generation/generate-assets.mjs +289 -0
- package/scripts/media-generation/veo-video-client.mjs +219 -0
- package/scripts/org/send-orgmail.mjs +227 -0
- package/scripts/outbound-dedup-cleanup.sh +43 -0
- package/scripts/outbound-dedup.sh +477 -0
- package/scripts/outbound_dedup.py +115 -0
- package/scripts/parse-voice-transcript.mjs +481 -0
- package/scripts/pdf-generation/README.md +63 -0
- package/scripts/pdf-generation/build-document.mjs +247 -0
- package/scripts/pdf-generation/templates/board-pack.latex +136 -0
- package/scripts/pdf-generation/templates/corporate-letter.latex +126 -0
- package/scripts/pdf-generation/templates/memo.latex +114 -0
- package/scripts/poll-slack-events.sh +35 -0
- package/scripts/poller/calendar-poller.mjs +12 -0
- package/scripts/poller/gmail-poller.mjs +192 -0
- package/scripts/poller/imap-client.mjs +289 -0
- package/scripts/poller/inbox-scan-poller.mjs +156 -0
- package/scripts/poller/inbox-scan-poller.test.mjs +231 -0
- package/scripts/poller/index.mjs +73 -0
- package/scripts/poller/intra-session-check.mjs +285 -0
- package/scripts/poller/lib/cloud-relay-dedup.mjs +88 -0
- package/scripts/poller/lib/cloud-relay-dedup.test.mjs +133 -0
- package/scripts/poller/lib/slash-command-handlers.mjs +177 -0
- package/scripts/poller/secondary-gmail-poller.mjs +132 -0
- package/scripts/poller/slack-cloud-relay-client.mjs +368 -0
- package/scripts/poller/slack-poller.mjs +854 -0
- package/scripts/poller/slack-socket-mode.mjs +917 -0
- package/scripts/poller/slack-socket-mode.test.mjs +753 -0
- package/scripts/poller/trigger.mjs +75 -0
- package/scripts/poller/utils.mjs +371 -0
- package/scripts/poller/voice-cloud-relay-client.mjs +179 -0
- package/scripts/poller/voice-poller.mjs +236 -0
- package/scripts/poller-launchd/install.sh +66 -0
- package/scripts/poller-launchd/poller.plist.template +40 -0
- package/scripts/poller-launchd/whatsapp-handler.plist.template +39 -0
- package/scripts/post-interaction-indexer.py +1598 -0
- package/scripts/pre-draft-context.py +994 -0
- package/scripts/pre_draft_lookup.py +258 -0
- package/scripts/rag/build-index.mjs +47 -0
- package/scripts/rag/ingest.mjs +111 -0
- package/scripts/rag/search.mjs +119 -0
- package/scripts/rag-indexer.py +629 -0
- package/scripts/restore-from-backup.sh +248 -0
- package/scripts/restore-from-backup.test.mjs +178 -0
- package/scripts/resume-operations.sh +80 -0
- package/scripts/search-secondary-inbox.py +181 -0
- package/scripts/secondary-inbox-poller.py +437 -0
- package/scripts/self-optimization/compute-metrics.py +398 -0
- package/scripts/send-email-as-principal.py +369 -0
- package/scripts/send-email-threaded.py +392 -0
- package/scripts/send-email-with-attachment.py +377 -0
- package/scripts/send-email.sh +131 -0
- package/scripts/send-sms.sh +175 -0
- package/scripts/send-whatsapp.sh +292 -0
- package/scripts/session-start.sh +106 -0
- package/scripts/setup/boot-claude-session.sh +94 -0
- package/scripts/setup/configure-macos.sh +674 -0
- package/scripts/setup/configure-twilio-sip-trunk.mjs +207 -0
- package/scripts/setup/configure-voice-tunnel.mjs +182 -0
- package/scripts/setup/generate-agent-env.mjs +92 -0
- package/scripts/setup/generate-agent-package-json.mjs +222 -0
- package/scripts/setup/generate-agent-package-json.test.mjs +143 -0
- package/scripts/setup/generate-autonomy.mjs +60 -0
- package/scripts/setup/generate-backlog.mjs +101 -0
- package/scripts/setup/generate-cadences.mjs +92 -0
- package/scripts/setup/generate-capability.mjs +162 -0
- package/scripts/setup/generate-charter.mjs +76 -0
- package/scripts/setup/generate-comms.mjs +59 -0
- package/scripts/setup/generate-company.mjs +92 -0
- package/scripts/setup/init-agent.sh +547 -0
- package/scripts/setup/init-agent.test.mjs +151 -0
- package/scripts/setup/init-archetype.mjs +74 -0
- package/scripts/setup/init-backup.mjs +54 -0
- package/scripts/setup/init-cadence-bus.mjs +60 -0
- package/scripts/setup/init-channel-bus.mjs +46 -0
- package/scripts/setup/init-cost-tracking.mjs +45 -0
- package/scripts/setup/init-decision-capture.mjs +66 -0
- package/scripts/setup/init-known-agents.mjs +57 -0
- package/scripts/setup/init-learning.mjs +70 -0
- package/scripts/setup/init-memory-executive.mjs +45 -0
- package/scripts/setup/init-model-router.mjs +124 -0
- package/scripts/setup/init-rag.mjs +174 -0
- package/scripts/setup/init-session-router.mjs +38 -0
- package/scripts/setup/init-slack-socket-mode.mjs +260 -0
- package/scripts/setup/init-telegram.mjs +165 -0
- package/scripts/setup/init-voice-realtime.mjs +204 -0
- package/scripts/setup/init-whatsapp-baileys.mjs +77 -0
- package/scripts/setup/install-dev-tools.sh +150 -0
- package/scripts/setup/lib/install-plist.mjs +95 -0
- package/scripts/setup/migrate-agent-to-sot.mjs +192 -0
- package/scripts/setup/render-environment-yaml.mjs +133 -0
- package/scripts/slack-events-ctl.sh +177 -0
- package/scripts/slack-events-server.mjs +1045 -0
- package/scripts/slack-react.mjs +89 -0
- package/scripts/slack-responded.sh +232 -0
- package/scripts/slack-send.sh +287 -0
- package/scripts/slack-typing.mjs +196 -0
- package/scripts/slack-upload-v2.py +95 -0
- package/scripts/sms-handler.mjs +450 -0
- package/scripts/spawn-session.sh +120 -0
- package/scripts/sync-protocol.mjs +217 -0
- package/scripts/system-verify.sh +184 -0
- package/scripts/test-email-thread-dedup.py +239 -0
- package/scripts/test-information-barriers.py +484 -0
- package/scripts/test-llm-email-dedup.py +251 -0
- package/scripts/test-pre-draft-integration.py +203 -0
- package/scripts/test-rag-phase2.sh +442 -0
- package/scripts/test-rag-search.sh +251 -0
- package/scripts/test-voice-parser.mjs +316 -0
- package/scripts/user-context-search.py +659 -0
- package/scripts/validate_outbound.py +1504 -0
- package/scripts/watchdog/ai.maestro.memory-watchdog.plist +41 -0
- package/scripts/watchdog/force-reboot.sh +157 -0
- package/scripts/watchdog/memory-watchdog.sh +473 -0
- package/scripts/whatsapp-handler.mjs +538 -0
- package/teams/desktop-operations.yaml +34 -0
- package/teams/executive-office.yaml +27 -0
- package/teams/legal-and-regulatory.yaml +24 -0
- package/teams/platform-and-engineering.yaml +23 -0
- package/teams/strategy-and-growth.yaml +29 -0
- package/workflows/continuous/backlog-executor.yaml +141 -0
- package/workflows/continuous/inbound-monitor.yaml +168 -0
- package/workflows/daily/applicant-triage.yaml +197 -0
- package/workflows/daily/comms-triage.yaml +80 -0
- package/workflows/daily/evening-wrap.yaml +105 -0
- package/workflows/daily/morning-brief.yaml +164 -0
- package/workflows/daily/slack-followup-sweep.yaml +87 -0
- package/workflows/event-driven/README.md +50 -0
- package/workflows/event-driven/agent-failure-investigation.yaml +137 -0
- package/workflows/event-driven/pr-review.yaml +107 -0
- package/workflows/monthly/board-readiness.yaml +76 -0
- package/workflows/quarterly/strategic-scenario-analysis.yaml +85 -0
- package/workflows/session-protocol.md +171 -0
- package/workflows/weekly/engineering-health.yaml +154 -0
- package/workflows/weekly/hiring-review.yaml +169 -0
- package/workflows/weekly/rollup-pipeline-review.yaml +76 -0
- package/workflows/weekly/strategic-memo.yaml +79 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/setup/runner.mjs — the `maestro setup` orchestrator.
|
|
3
|
+
*
|
|
4
|
+
* Globs `lib/setup/sections/*.mjs`, sorts by `order`, and runs the
|
|
5
|
+
* detect → (skip if complete & not forced) → prompt → apply → verify →
|
|
6
|
+
* checkpoint loop. The on-disk SoT is authoritative; the checkpoint
|
|
7
|
+
* (state/setup/progress.json) is a resume hint.
|
|
8
|
+
*
|
|
9
|
+
* SetupSection contract (design §3.3) — the seam WS2/3/4 register against by
|
|
10
|
+
* dropping a file into sections/ (no core edit):
|
|
11
|
+
*
|
|
12
|
+
* @typedef {Object} SetupSection
|
|
13
|
+
* @property {string} id
|
|
14
|
+
* @property {string} label
|
|
15
|
+
* @property {number} order // gaps of 10 so others insert
|
|
16
|
+
* @property {(ctx)=>boolean|'partial'} [enabledFor]
|
|
17
|
+
* @property {(ctx)=>Promise<{status:'unconfigured'|'partial'|'complete',summary?,missing?}>} detect
|
|
18
|
+
* @property {(ctx)=>Promise<void>} prompt // only this touches the TTY; honors headless
|
|
19
|
+
* @property {(ctx)=>Promise<void>} apply // writes SoT + runs generators; idempotent
|
|
20
|
+
* @property {(ctx)=>Promise<{ok,checks:Array<{name,ok,remedy?}>}>} verify
|
|
21
|
+
*
|
|
22
|
+
* The ctx passed to every hook:
|
|
23
|
+
* { agentRoot, maestroRoot, flags, answers, io, sections, enrichSpawn? }
|
|
24
|
+
*
|
|
25
|
+
* Flags (parsed with node:util parseArgs): --resume --from <id> --only <ids>
|
|
26
|
+
* --status --headless --answers <file> --no-enrich --dry-run --verbose.
|
|
27
|
+
*
|
|
28
|
+
* @module lib/setup/runner
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
"use strict";
|
|
32
|
+
|
|
33
|
+
import { readdirSync, existsSync, readFileSync } from "node:fs";
|
|
34
|
+
import { join, dirname } from "node:path";
|
|
35
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
36
|
+
import { parseArgs } from "node:util";
|
|
37
|
+
import * as io from "./io.mjs";
|
|
38
|
+
import { markSection, hashAnswers, mergeAnswers } from "./state.mjs";
|
|
39
|
+
|
|
40
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
41
|
+
const SECTIONS_DIR = join(HERE, "sections");
|
|
42
|
+
const DEFAULT_MAESTRO_ROOT = join(HERE, "..", "..");
|
|
43
|
+
|
|
44
|
+
/** parseArgs spec for `maestro setup`. */
|
|
45
|
+
export const FLAG_SPEC = {
|
|
46
|
+
resume: { type: "boolean", default: false },
|
|
47
|
+
from: { type: "string" },
|
|
48
|
+
only: { type: "string" },
|
|
49
|
+
status: { type: "boolean", default: false },
|
|
50
|
+
headless: { type: "boolean", default: false },
|
|
51
|
+
answers: { type: "string" },
|
|
52
|
+
"no-enrich": { type: "boolean", default: false },
|
|
53
|
+
"dry-run": { type: "boolean", default: false },
|
|
54
|
+
verbose: { type: "boolean", default: false },
|
|
55
|
+
help: { type: "boolean", default: false },
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Parse argv (without node/script) into a normalized flags object + a positional
|
|
60
|
+
* section id (the optional `[section]` argument).
|
|
61
|
+
* @param {string[]} argv
|
|
62
|
+
*/
|
|
63
|
+
export function parseFlags(argv = []) {
|
|
64
|
+
const { values, positionals } = parseArgs({ args: argv, options: FLAG_SPEC, allowPositionals: true, strict: false });
|
|
65
|
+
return {
|
|
66
|
+
section: positionals[0] || null,
|
|
67
|
+
flags: {
|
|
68
|
+
resume: !!values.resume,
|
|
69
|
+
from: values.from || null,
|
|
70
|
+
only: values.only ? String(values.only).split(",").map((s) => s.trim()).filter(Boolean) : null,
|
|
71
|
+
status: !!values.status,
|
|
72
|
+
headless: !!values.headless,
|
|
73
|
+
answersFile: values.answers || null,
|
|
74
|
+
noEnrich: !!values["no-enrich"],
|
|
75
|
+
dryRun: !!values["dry-run"],
|
|
76
|
+
verbose: !!values.verbose,
|
|
77
|
+
help: !!values.help,
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Dynamically import every section module under sections/, sorted by order. */
|
|
83
|
+
export async function loadSections(dir = SECTIONS_DIR) {
|
|
84
|
+
if (!existsSync(dir)) return [];
|
|
85
|
+
const files = readdirSync(dir).filter((f) => f.endsWith(".mjs") && !f.endsWith(".test.mjs")).sort();
|
|
86
|
+
const sections = [];
|
|
87
|
+
for (const f of files) {
|
|
88
|
+
try {
|
|
89
|
+
const mod = await import(pathToFileURL(join(dir, f)).href);
|
|
90
|
+
const sec = mod.default || mod.section;
|
|
91
|
+
if (sec && typeof sec === "object" && sec.id && typeof sec.order === "number") sections.push(sec);
|
|
92
|
+
} catch (e) {
|
|
93
|
+
io.note(`[setup] skipping section ${f}: ${e && e.message ? e.message : e}`, { dim: true });
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
sections.sort((a, b) => a.order - b.order || a.id.localeCompare(b.id));
|
|
97
|
+
return sections;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Load + merge the answers file (if any) into a flat answers bag. */
|
|
101
|
+
function loadAnswers(flags) {
|
|
102
|
+
if (!flags.answersFile) return {};
|
|
103
|
+
if (!existsSync(flags.answersFile)) throw new Error(`--answers file not found: ${flags.answersFile}`);
|
|
104
|
+
try { return JSON.parse(readFileSync(flags.answersFile, "utf-8")); }
|
|
105
|
+
catch (e) { throw new Error(`--answers file is not valid JSON: ${e && e.message ? e.message : e}`); }
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Filter the section list per --from / --only / positional section. */
|
|
109
|
+
function selectSections(sections, sectionArg, flags) {
|
|
110
|
+
let list = sections;
|
|
111
|
+
if (flags.only && flags.only.length) {
|
|
112
|
+
const set = new Set(flags.only);
|
|
113
|
+
list = sections.filter((s) => set.has(s.id));
|
|
114
|
+
} else if (sectionArg) {
|
|
115
|
+
list = sections.filter((s) => s.id === sectionArg);
|
|
116
|
+
} else if (flags.from) {
|
|
117
|
+
const fromIdx = sections.findIndex((s) => s.id === flags.from);
|
|
118
|
+
if (fromIdx >= 0) list = sections.slice(fromIdx);
|
|
119
|
+
}
|
|
120
|
+
return list;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Print the status table (--status): each section's detect() outcome.
|
|
125
|
+
* @param {object} ctx
|
|
126
|
+
* @param {SetupSection[]} sections
|
|
127
|
+
*/
|
|
128
|
+
export async function printStatus(ctx, sections) {
|
|
129
|
+
io.note("");
|
|
130
|
+
io.note(`maestro setup — status (${ctx.agentRoot})`);
|
|
131
|
+
for (const s of sections) {
|
|
132
|
+
let d;
|
|
133
|
+
try { d = await s.detect(ctx); } catch (e) { d = { status: "error", summary: e && e.message ? e.message : String(e) }; }
|
|
134
|
+
const badge = d.status === "complete" ? "✓ complete " : d.status === "partial" ? "~ partial " : d.status === "error" ? "! error " : "· todo ";
|
|
135
|
+
const detail = d.summary ? d.summary : d.missing ? `missing: ${(d.missing || []).join(", ")}` : "";
|
|
136
|
+
io.note(` [${badge}] ${String(s.order).padStart(2, " ")} ${s.id.padEnd(18)} ${detail}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Run the wizard.
|
|
142
|
+
* @param {object} [opts]
|
|
143
|
+
* @param {string} [opts.agentRoot=process.cwd()]
|
|
144
|
+
* @param {string} [opts.maestroRoot]
|
|
145
|
+
* @param {string[]} [opts.argv] CLI args (without node/script)
|
|
146
|
+
* @param {object} [opts.flags] pre-parsed flags (overrides argv)
|
|
147
|
+
* @param {string} [opts.section] positional section
|
|
148
|
+
* @param {object} [opts.answers] extra answers (merged over file)
|
|
149
|
+
* @param {Function} [opts.enrichSpawn] injectable enrich spawn (tests)
|
|
150
|
+
* @param {SetupSection[]} [opts.sections] inject sections (tests)
|
|
151
|
+
* @returns {Promise<{ok:boolean, ran:string[], skipped:string[], verify?:object}>}
|
|
152
|
+
*/
|
|
153
|
+
export async function runSetup(opts = {}) {
|
|
154
|
+
const agentRoot = opts.agentRoot || process.cwd();
|
|
155
|
+
const maestroRoot = opts.maestroRoot || DEFAULT_MAESTRO_ROOT;
|
|
156
|
+
const parsed = opts.flags ? { flags: opts.flags, section: opts.section || null } : parseFlags(opts.argv || []);
|
|
157
|
+
const flags = parsed.flags;
|
|
158
|
+
const sectionArg = opts.section || parsed.section;
|
|
159
|
+
|
|
160
|
+
const fileAnswers = loadAnswers(flags);
|
|
161
|
+
const answers = { ...fileAnswers, ...(opts.answers || {}) };
|
|
162
|
+
|
|
163
|
+
const allSections = (opts.sections ? [...opts.sections] : await loadSections())
|
|
164
|
+
.sort((a, b) => a.order - b.order || String(a.id).localeCompare(String(b.id)));
|
|
165
|
+
const ctx = { agentRoot, maestroRoot, flags, answers, io, sections: allSections, enrichSpawn: opts.enrichSpawn };
|
|
166
|
+
|
|
167
|
+
// --status: print and return.
|
|
168
|
+
if (flags.status) {
|
|
169
|
+
await printStatus(ctx, allSections);
|
|
170
|
+
return { ok: true, ran: [], skipped: allSections.map((s) => s.id), status: true };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const targeted = selectSections(allSections, sectionArg, flags);
|
|
174
|
+
|
|
175
|
+
const ran = [];
|
|
176
|
+
const skipped = [];
|
|
177
|
+
let verifyResult = null;
|
|
178
|
+
|
|
179
|
+
for (const section of targeted) {
|
|
180
|
+
// enabledFor gate (optional).
|
|
181
|
+
if (typeof section.enabledFor === "function") {
|
|
182
|
+
try { const en = section.enabledFor(ctx); if (en === false) { skipped.push(section.id); continue; } } catch { /* default-enabled */ }
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// detect — the on-disk SoT is authoritative.
|
|
186
|
+
let det;
|
|
187
|
+
try { det = await section.detect(ctx); } catch (e) { det = { status: "error", summary: e && e.message ? e.message : String(e) }; }
|
|
188
|
+
|
|
189
|
+
const forced = !!(flags.only || sectionArg || flags.from);
|
|
190
|
+
const answersHash = hashAnswers(answers);
|
|
191
|
+
|
|
192
|
+
// Skip when the SoT already satisfies this section and we are not forcing a
|
|
193
|
+
// re-run. detect() (not the checkpoint) is authoritative — the checkpoint is
|
|
194
|
+
// only a hint. `verify` is never skipped (it has no persisted "done" state).
|
|
195
|
+
if (det.status === "complete" && !forced && section.id !== "verify") {
|
|
196
|
+
skipped.push(section.id);
|
|
197
|
+
if (!flags.dryRun) markSection(agentRoot, section.id, { status: "complete", answersHash });
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (flags.dryRun) {
|
|
202
|
+
io.note(` [dry-run] ${section.id} (${section.order}) — status: ${det.status}${det.missing ? `, missing: ${(det.missing || []).join(", ")}` : ""}`, { dim: true });
|
|
203
|
+
ran.push(section.id);
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// prompt (TTY-only; headless returns pre-supplied answers, never blocks).
|
|
208
|
+
if (typeof section.prompt === "function") {
|
|
209
|
+
try { await section.prompt(ctx); } catch (e) {
|
|
210
|
+
io.note(`[setup] ${section.id} prompt error: ${e && e.message ? e.message : e}`, { dim: true });
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
// Persist the answers bag (durable across sections / resumes).
|
|
214
|
+
mergeAnswers(agentRoot, answers);
|
|
215
|
+
|
|
216
|
+
// apply — writes SoT + runs generators. A throw here is fatal (the operating
|
|
217
|
+
// model must be coherent); record the failure on the checkpoint first.
|
|
218
|
+
try {
|
|
219
|
+
if (typeof section.apply === "function") await section.apply(ctx);
|
|
220
|
+
} catch (e) {
|
|
221
|
+
markSection(agentRoot, section.id, { status: "failed", answersHash, verify: { ok: false, error: e && e.message ? e.message : String(e) } });
|
|
222
|
+
throw new Error(`setup section "${section.id}" failed in apply(): ${e && e.message ? e.message : e}`);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// verify.
|
|
226
|
+
let ver = { ok: true, checks: [] };
|
|
227
|
+
try { if (typeof section.verify === "function") ver = await section.verify(ctx); }
|
|
228
|
+
catch (e) { ver = { ok: false, checks: [{ name: `${section.id} verify`, ok: false, remedy: e && e.message ? e.message : String(e) }] }; }
|
|
229
|
+
|
|
230
|
+
if (section.id === "verify") {
|
|
231
|
+
verifyResult = ver;
|
|
232
|
+
// Always render the capability table — it's the operator's at-a-glance
|
|
233
|
+
// report and is plain text (harmless in scripted/headless runs too).
|
|
234
|
+
if (typeof section.renderTable === "function") section.renderTable(ver);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
markSection(agentRoot, section.id, { status: ver.ok ? "complete" : "partial", answersHash, verify: { ok: ver.ok } });
|
|
238
|
+
ran.push(section.id);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return { ok: verifyResult ? !!verifyResult.ok : true, ran, skipped, verify: verifyResult };
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export { SECTIONS_DIR, DEFAULT_MAESTRO_ROOT };
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* runner.test.mjs — section ordering, flag parsing, skip-complete, dry-run.
|
|
3
|
+
* Uses injected fake sections so it stays fast + deterministic.
|
|
4
|
+
* Run: `node --test lib/setup/runner.test.mjs`
|
|
5
|
+
* @module lib/setup/runner.test
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
"use strict";
|
|
9
|
+
|
|
10
|
+
import { test } from "node:test";
|
|
11
|
+
import assert from "node:assert/strict";
|
|
12
|
+
import { mkdtempSync, existsSync, readdirSync } from "node:fs";
|
|
13
|
+
import { tmpdir } from "node:os";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { parseFlags, loadSections, runSetup } from "./runner.mjs";
|
|
16
|
+
import { loadProgress, progressPath } from "./state.mjs";
|
|
17
|
+
|
|
18
|
+
function tmpRoot() { return mkdtempSync(join(tmpdir(), "maestro-runner-")); }
|
|
19
|
+
|
|
20
|
+
test("parseFlags parses --only, --from, --headless, --dry-run", () => {
|
|
21
|
+
const a = parseFlags(["--only", "identity,company", "--headless"]);
|
|
22
|
+
assert.deepEqual(a.flags.only, ["identity", "company"]);
|
|
23
|
+
assert.equal(a.flags.headless, true);
|
|
24
|
+
const b = parseFlags(["company", "--from", "model", "--dry-run", "--no-enrich"]);
|
|
25
|
+
assert.equal(b.section, "company");
|
|
26
|
+
assert.equal(b.flags.from, "model");
|
|
27
|
+
assert.equal(b.flags.dryRun, true);
|
|
28
|
+
assert.equal(b.flags.noEnrich, true);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test("loadSections discovers the real sections sorted by order", async () => {
|
|
32
|
+
const sections = await loadSections();
|
|
33
|
+
const ids = sections.map((s) => s.id);
|
|
34
|
+
// core WS1 sections present and ordered
|
|
35
|
+
assert.ok(ids.includes("identity"));
|
|
36
|
+
assert.ok(ids.includes("operating-model"));
|
|
37
|
+
assert.ok(ids.includes("verify"));
|
|
38
|
+
const orders = sections.map((s) => s.order);
|
|
39
|
+
assert.deepEqual(orders, [...orders].sort((x, y) => x - y), "sections sorted by order");
|
|
40
|
+
// identity precedes operating-model precedes verify
|
|
41
|
+
assert.ok(ids.indexOf("identity") < ids.indexOf("operating-model"));
|
|
42
|
+
assert.ok(ids.indexOf("operating-model") < ids.indexOf("verify"));
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
function fakeSection(id, order, opts = {}) {
|
|
46
|
+
return {
|
|
47
|
+
id, order,
|
|
48
|
+
label: id,
|
|
49
|
+
applied: 0,
|
|
50
|
+
async detect() { return { status: opts.complete ? "complete" : "unconfigured" }; },
|
|
51
|
+
async prompt(ctx) { ctx[`prompted_${id}`] = true; },
|
|
52
|
+
async apply(ctx) { ctx[`applied_${id}`] = (ctx[`applied_${id}`] || 0) + 1; },
|
|
53
|
+
async verify() { return { ok: opts.verifyOk !== false, checks: [{ name: id, ok: opts.verifyOk !== false }] }; },
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
test("runSetup runs sections in order and records checkpoint", async () => {
|
|
58
|
+
const root = tmpRoot();
|
|
59
|
+
const order = [];
|
|
60
|
+
const mk = (id, o) => ({
|
|
61
|
+
id, order: o, label: id,
|
|
62
|
+
async detect() { return { status: "unconfigured" }; },
|
|
63
|
+
async prompt() {},
|
|
64
|
+
async apply() { order.push(id); },
|
|
65
|
+
async verify() { return { ok: true, checks: [] }; },
|
|
66
|
+
});
|
|
67
|
+
const sections = [mk("b", 20), mk("a", 10), mk("c", 30)];
|
|
68
|
+
const res = await runSetup({ agentRoot: root, sections, flags: baseFlags() });
|
|
69
|
+
assert.deepEqual(order, ["a", "b", "c"], "executed in ascending order");
|
|
70
|
+
assert.deepEqual(res.ran, ["a", "b", "c"]);
|
|
71
|
+
const p = loadProgress(root);
|
|
72
|
+
assert.equal(p.sections.a.status, "complete");
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("runSetup skips already-complete sections (resume default)", async () => {
|
|
76
|
+
const root = tmpRoot();
|
|
77
|
+
const applied = [];
|
|
78
|
+
const mk = (id, o, complete) => ({
|
|
79
|
+
id, order: o, label: id,
|
|
80
|
+
async detect() { return { status: complete ? "complete" : "unconfigured" }; },
|
|
81
|
+
async prompt() {},
|
|
82
|
+
async apply() { applied.push(id); },
|
|
83
|
+
async verify() { return { ok: true, checks: [] }; },
|
|
84
|
+
});
|
|
85
|
+
const sections = [mk("a", 10, true), mk("b", 20, false)];
|
|
86
|
+
const res = await runSetup({ agentRoot: root, sections, flags: baseFlags() });
|
|
87
|
+
assert.deepEqual(applied, ["b"], "complete section a was skipped");
|
|
88
|
+
assert.ok(res.skipped.includes("a"));
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test("runSetup --dry-run writes nothing", async () => {
|
|
92
|
+
const root = tmpRoot();
|
|
93
|
+
const sections = [fakeSection("x", 10), fakeSection("y", 20)];
|
|
94
|
+
const res = await runSetup({ agentRoot: root, sections, flags: { ...baseFlags(), dryRun: true } });
|
|
95
|
+
assert.equal(existsSync(progressPath(root)), false, "no progress.json on dry-run");
|
|
96
|
+
// nothing under the temp root was created
|
|
97
|
+
assert.equal(readdirSync(root).length, 0, "dry-run created no files");
|
|
98
|
+
assert.deepEqual(res.ran, ["x", "y"]);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("runSetup --only restricts and forces the named sections", async () => {
|
|
102
|
+
const root = tmpRoot();
|
|
103
|
+
const applied = [];
|
|
104
|
+
const mk = (id, o) => ({
|
|
105
|
+
id, order: o, label: id,
|
|
106
|
+
async detect() { return { status: "complete" }; }, // would normally skip
|
|
107
|
+
async prompt() {},
|
|
108
|
+
async apply() { applied.push(id); },
|
|
109
|
+
async verify() { return { ok: true, checks: [] }; },
|
|
110
|
+
});
|
|
111
|
+
const sections = [mk("a", 10), mk("b", 20), mk("c", 30)];
|
|
112
|
+
await runSetup({ agentRoot: root, sections, flags: { ...baseFlags(), only: ["b"] } });
|
|
113
|
+
assert.deepEqual(applied, ["b"], "only b runs, and forced despite complete detect");
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("runSetup --status returns without applying", async () => {
|
|
117
|
+
const root = tmpRoot();
|
|
118
|
+
const applied = [];
|
|
119
|
+
const sections = [{
|
|
120
|
+
id: "a", order: 10, label: "a",
|
|
121
|
+
async detect() { return { status: "partial", summary: "x" }; },
|
|
122
|
+
async prompt() {}, async apply() { applied.push("a"); },
|
|
123
|
+
async verify() { return { ok: true, checks: [] }; },
|
|
124
|
+
}];
|
|
125
|
+
const res = await runSetup({ agentRoot: root, sections, flags: { ...baseFlags(), status: true } });
|
|
126
|
+
assert.equal(res.status, true);
|
|
127
|
+
assert.deepEqual(applied, [], "status mode applies nothing");
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
function baseFlags() {
|
|
131
|
+
return { resume: false, from: null, only: null, status: false, headless: true, answersFile: null, noEnrich: false, dryRun: false, verbose: false, help: false };
|
|
132
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sections/comms.mjs — messaging channels (WS2, order 45).
|
|
3
|
+
*
|
|
4
|
+
* Replaces the WS1 stub. This section is the cross-workstream seam for
|
|
5
|
+
* channels (design §3.2): it wires each selected channel by running its
|
|
6
|
+
* idempotent `init-*` script and then runs a verify-inbound probe — a
|
|
7
|
+
* generalised form of the Slack doctor "did the listener connect / did an
|
|
8
|
+
* inbox item appear" check.
|
|
9
|
+
*
|
|
10
|
+
* Order 45 (between core comms placeholder 40 and tools 50) so it runs after
|
|
11
|
+
* identity/company/model but before tools, per design §3.2.
|
|
12
|
+
*
|
|
13
|
+
* Registered via the file-drop section interface (runner.mjs globs sections/).
|
|
14
|
+
* Every hook is non-blocking under --headless: detect/apply/verify never read
|
|
15
|
+
* the TTY; prompt honours `io` headless resolution.
|
|
16
|
+
*
|
|
17
|
+
* @module lib/setup/sections/comms
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
"use strict";
|
|
21
|
+
|
|
22
|
+
import { join } from "node:path";
|
|
23
|
+
import { existsSync, readFileSync, statSync, readdirSync } from "node:fs";
|
|
24
|
+
import * as io from "../io.mjs";
|
|
25
|
+
import { runGenerator } from "../run-generator.mjs";
|
|
26
|
+
|
|
27
|
+
/** Channel → signals that indicate it is configured, its init script, and probe. */
|
|
28
|
+
const CHANNELS = {
|
|
29
|
+
slack: {
|
|
30
|
+
label: "Slack",
|
|
31
|
+
env: ["SLACK_USER_TOKEN", "SLACK_BOT_TOKEN", "SLACK_APP_LEVEL_TOKEN"],
|
|
32
|
+
init: "init-slack-socket-mode.mjs",
|
|
33
|
+
probe: { kind: "log", file: "logs/polling", suffix: "-slack-socket.jsonl", markers: ["hello — connected to Slack Socket Mode", "inbox-item-written", "starting Socket Mode listener"] },
|
|
34
|
+
},
|
|
35
|
+
gmail: { label: "Email (Gmail)", env: ["GMAIL_APP_PASSWORD"], probe: { kind: "inbox", service: "gmail" } },
|
|
36
|
+
sms: { label: "SMS", env: ["TWILIO_ACCOUNT_SID"], probe: { kind: "inbox", service: "sms" } },
|
|
37
|
+
whatsapp: { label: "WhatsApp", gate: ["config/whatsapp.yaml"], init: "init-whatsapp-baileys.mjs", probe: { kind: "inbox", service: "whatsapp" } },
|
|
38
|
+
telegram: { label: "Telegram", gate: ["config/telegram.yaml"], init: "init-telegram.mjs", probe: { kind: "inbox", service: "telegram" } },
|
|
39
|
+
voice: { label: "Voice / phone", gate: ["config/voice.yaml"], probe: { kind: "inbox", service: "voice" } },
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
function readEnv(agentRoot) {
|
|
43
|
+
const p = join(agentRoot, ".env");
|
|
44
|
+
return existsSync(p) ? readFileSync(p, "utf-8") : "";
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Which channels look configured right now (env key set OR gate file present). */
|
|
48
|
+
export function detectConfiguredChannels(agentRoot) {
|
|
49
|
+
const env = readEnv(agentRoot);
|
|
50
|
+
const configured = [];
|
|
51
|
+
for (const [name, sig] of Object.entries(CHANNELS)) {
|
|
52
|
+
const envHit = (sig.env || []).some((k) => new RegExp(`^${k}=.+`, "m").test(env));
|
|
53
|
+
const gateHit = (sig.gate || []).some((f) => existsSync(join(agentRoot, f)));
|
|
54
|
+
if (envHit || gateHit) configured.push(name);
|
|
55
|
+
}
|
|
56
|
+
return configured;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Verify-inbound probe for one channel. Generalises the Slack doctor probe:
|
|
61
|
+
* - "log" : a recent connect/inbox marker in a JSONL log file (last 60 min).
|
|
62
|
+
* - "inbox": at least one item file present under state/inbox/<service>/.
|
|
63
|
+
* Returns { ok, detail }. Probes never block — they read the filesystem only.
|
|
64
|
+
*
|
|
65
|
+
* @param {string} agentRoot
|
|
66
|
+
* @param {object} probe
|
|
67
|
+
* @param {number} [windowMs]
|
|
68
|
+
*/
|
|
69
|
+
export function probeInbound(agentRoot, probe, windowMs = 60 * 60_000) {
|
|
70
|
+
if (!probe) return { ok: true, detail: "no probe configured" };
|
|
71
|
+
if (probe.kind === "inbox") {
|
|
72
|
+
const dir = join(agentRoot, "state/inbox", probe.service);
|
|
73
|
+
if (!existsSync(dir)) return { ok: false, detail: `no items in state/inbox/${probe.service}/ yet` };
|
|
74
|
+
let count = 0;
|
|
75
|
+
try { count = readdirSync(dir).filter((f) => f.endsWith(".yaml") || f.endsWith(".json") || f.includes(".processed")).length; } catch { /* */ }
|
|
76
|
+
return count > 0
|
|
77
|
+
? { ok: true, detail: `${count} item(s) seen in state/inbox/${probe.service}/` }
|
|
78
|
+
: { ok: false, detail: `no items in state/inbox/${probe.service}/ yet` };
|
|
79
|
+
}
|
|
80
|
+
if (probe.kind === "log") {
|
|
81
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
82
|
+
const yesterday = new Date(Date.now() - 86400000).toISOString().slice(0, 10);
|
|
83
|
+
for (const day of [today, yesterday]) {
|
|
84
|
+
const logPath = join(agentRoot, probe.file, `${day}${probe.suffix}`);
|
|
85
|
+
if (!existsSync(logPath)) continue;
|
|
86
|
+
try {
|
|
87
|
+
const stat = statSync(logPath);
|
|
88
|
+
const buf = readFileSync(logPath, "utf-8");
|
|
89
|
+
void stat;
|
|
90
|
+
for (const line of buf.split("\n").reverse()) {
|
|
91
|
+
if (!line) continue;
|
|
92
|
+
try {
|
|
93
|
+
const entry = JSON.parse(line);
|
|
94
|
+
if (probe.markers.some((m) => entry.message === m)) {
|
|
95
|
+
const ageMs = Date.now() - new Date(entry.ts).getTime();
|
|
96
|
+
if (ageMs >= 0 && ageMs < windowMs) return { ok: true, detail: `listener active ${Math.round(ageMs / 60000)}m ago` };
|
|
97
|
+
return { ok: false, detail: `last connect ${Math.round(ageMs / 60000)}m ago — may be stalled` };
|
|
98
|
+
}
|
|
99
|
+
} catch { /* skip */ }
|
|
100
|
+
}
|
|
101
|
+
} catch { /* unreadable */ }
|
|
102
|
+
}
|
|
103
|
+
return { ok: false, detail: "listener has not produced a connect log yet" };
|
|
104
|
+
}
|
|
105
|
+
return { ok: true, detail: "" };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Resolve which channels to wire: explicit answers, else currently-configured. */
|
|
109
|
+
function selectedChannels(ctx) {
|
|
110
|
+
const ans = (ctx.answers && (ctx.answers.channels || ctx.answers.comms)) || null;
|
|
111
|
+
if (Array.isArray(ans) && ans.length) return ans.filter((c) => CHANNELS[c]);
|
|
112
|
+
// Fall back to whatever already looks configured (resume / non-interactive).
|
|
113
|
+
return detectConfiguredChannels(ctx.agentRoot);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export default {
|
|
117
|
+
id: "comms",
|
|
118
|
+
label: "Messaging channels",
|
|
119
|
+
order: 45,
|
|
120
|
+
|
|
121
|
+
async detect(ctx) {
|
|
122
|
+
const configured = detectConfiguredChannels(ctx.agentRoot);
|
|
123
|
+
if (configured.length === 0) return { status: "unconfigured", missing: ["no channels configured"] };
|
|
124
|
+
// "complete" iff every configured channel passes its inbound probe; else partial.
|
|
125
|
+
const failing = configured.filter((c) => !probeInbound(ctx.agentRoot, CHANNELS[c]?.probe).ok);
|
|
126
|
+
if (failing.length === 0) return { status: "complete", summary: `wired: ${configured.join(", ")}` };
|
|
127
|
+
return { status: "partial", summary: `configured: ${configured.join(", ")}`, missing: failing.map((c) => `${c} (no inbound yet)`) };
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
async prompt(ctx) {
|
|
131
|
+
const configured = detectConfiguredChannels(ctx.agentRoot);
|
|
132
|
+
io.note(`Messaging channels detected: ${configured.length ? configured.join(", ") : "none yet"}.`, { dim: true });
|
|
133
|
+
// Offer a multiselect of channels to wire (defaults to currently-configured).
|
|
134
|
+
const chosen = await io.multiselect(ctx, {
|
|
135
|
+
message: "Channels to wire now (runs each channel's init script)",
|
|
136
|
+
key: "channels",
|
|
137
|
+
choices: Object.entries(CHANNELS).map(([value, c]) => ({ value, label: c.label })),
|
|
138
|
+
default: configured,
|
|
139
|
+
});
|
|
140
|
+
ctx.answers.channels = Array.isArray(chosen) && chosen.length ? chosen : configured;
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
async apply(ctx) {
|
|
144
|
+
const channels = selectedChannels(ctx);
|
|
145
|
+
for (const name of channels) {
|
|
146
|
+
const spec = CHANNELS[name];
|
|
147
|
+
if (!spec || !spec.init) continue; // gmail/sms/voice configured elsewhere
|
|
148
|
+
const r = runGenerator(spec.init, ctx.agentRoot, { maestroRoot: ctx.maestroRoot });
|
|
149
|
+
if (!r.ok) {
|
|
150
|
+
// Non-fatal: a channel's interactive init may need a TTY/token. Surface
|
|
151
|
+
// it but don't abort the whole operating-model apply.
|
|
152
|
+
io.note(`[comms] ${name} init reported: ${(r.stderr || r.stdout || "").trim().slice(0, 200)}`, { dim: true });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
async verify(ctx) {
|
|
158
|
+
const configured = detectConfiguredChannels(ctx.agentRoot);
|
|
159
|
+
const checks = configured.map((name) => {
|
|
160
|
+
const res = probeInbound(ctx.agentRoot, CHANNELS[name]?.probe);
|
|
161
|
+
return {
|
|
162
|
+
name: `${CHANNELS[name]?.label || name} inbound`,
|
|
163
|
+
ok: res.ok,
|
|
164
|
+
remedy: res.ok ? undefined : `${res.detail}${CHANNELS[name]?.init ? ` — run node scripts/setup/${CHANNELS[name].init}` : ""}`,
|
|
165
|
+
};
|
|
166
|
+
});
|
|
167
|
+
if (checks.length === 0) checks.push({ name: "messaging channels (0 configured)", ok: true });
|
|
168
|
+
// Channels are optional → a not-yet-connected channel WARNs (remedy shown)
|
|
169
|
+
// but does not hard-fail setup. ok reflects whether configured channels are
|
|
170
|
+
// live; the runner records partial when not.
|
|
171
|
+
return { ok: checks.every((c) => c.ok), checks };
|
|
172
|
+
},
|
|
173
|
+
};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sections/company.mjs — company interview → config/company.json, gather the
|
|
3
|
+
* deterministic context pack, render docs/company/overview.md. (order 20)
|
|
4
|
+
*
|
|
5
|
+
* @module lib/setup/sections/company
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
"use strict";
|
|
9
|
+
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import { existsSync, readFileSync, writeFileSync, renameSync, mkdirSync } from "node:fs";
|
|
12
|
+
import * as io from "../io.mjs";
|
|
13
|
+
import { gatherContextPack } from "../context-pack.mjs";
|
|
14
|
+
import { runGenerator } from "../run-generator.mjs";
|
|
15
|
+
|
|
16
|
+
function readCompany(agentRoot) {
|
|
17
|
+
const p = join(agentRoot, "config", "company.json");
|
|
18
|
+
try { return existsSync(p) ? JSON.parse(readFileSync(p, "utf-8")) : {}; } catch { return {}; }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function writeCompany(agentRoot, obj) {
|
|
22
|
+
const p = join(agentRoot, "config", "company.json");
|
|
23
|
+
mkdirSync(join(agentRoot, "config"), { recursive: true });
|
|
24
|
+
const tmp = `${p}.${process.pid}.${Date.now()}.tmp`;
|
|
25
|
+
writeFileSync(tmp, JSON.stringify(obj, null, 2) + "\n", "utf-8");
|
|
26
|
+
renameSync(tmp, p);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function isUnset(v) { return v === undefined || v === null || v === "" || v === "UNCONFIGURED"; }
|
|
30
|
+
|
|
31
|
+
export default {
|
|
32
|
+
id: "company",
|
|
33
|
+
label: "Company context",
|
|
34
|
+
order: 20,
|
|
35
|
+
|
|
36
|
+
async detect(ctx) {
|
|
37
|
+
const c = readCompany(ctx.agentRoot);
|
|
38
|
+
if (isUnset(c.name)) return { status: "unconfigured", missing: ["company.name"] };
|
|
39
|
+
const missing = [];
|
|
40
|
+
if (isUnset(c.website) && isUnset(c.domain)) missing.push("company.website");
|
|
41
|
+
if (missing.length === 0) return { status: "complete", summary: `${c.name}${c.website ? ` (${c.website})` : ""}` };
|
|
42
|
+
return { status: "partial", missing };
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
async prompt(ctx) {
|
|
46
|
+
const cur = readCompany(ctx.agentRoot);
|
|
47
|
+
const name = await io.text(ctx, { message: "Company name", key: "companyName", default: isUnset(cur.name) ? "" : cur.name });
|
|
48
|
+
const website = await io.text(ctx, { message: "Company website", key: "companyWebsite", default: cur.website || "" });
|
|
49
|
+
const domain = await io.text(ctx, { message: "Email/domain (e.g. acme.com)", key: "companyDomain", default: cur.domain || deriveDomain(website) });
|
|
50
|
+
const industry = await io.text(ctx, { message: "Industry", key: "companyIndustry", default: cur.industry || "" });
|
|
51
|
+
const stage = await io.text(ctx, { message: "Stage (e.g. Series A, public)", key: "companyStage", default: cur.stage || "" });
|
|
52
|
+
const tagline = await io.text(ctx, { message: "One-line tagline (optional)", key: "companyTagline", default: cur.tagline || "" });
|
|
53
|
+
ctx.answers.company = { name, website, domain, industry, stage, tagline };
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
async apply(ctx) {
|
|
57
|
+
const a = (ctx.answers && ctx.answers.company) || pull(ctx);
|
|
58
|
+
const cur = readCompany(ctx.agentRoot);
|
|
59
|
+
const merged = { ...cur };
|
|
60
|
+
if (a) {
|
|
61
|
+
if (a.name) merged.name = a.name;
|
|
62
|
+
if (a.website) merged.website = a.website;
|
|
63
|
+
if (a.domain) merged.domain = a.domain;
|
|
64
|
+
if (a.industry) merged.industry = a.industry;
|
|
65
|
+
if (a.stage) merged.stage = a.stage;
|
|
66
|
+
if (a.tagline) merged.tagline = a.tagline;
|
|
67
|
+
}
|
|
68
|
+
// Keep arrays present (scaffold ships them) — never drop.
|
|
69
|
+
for (const k of ["locations", "jurisdictions", "products", "competitors", "strategicPriorities", "keyPeople", "contextDocs", "repos"]) {
|
|
70
|
+
if (!Array.isArray(merged[k])) merged[k] = [];
|
|
71
|
+
}
|
|
72
|
+
writeCompany(ctx.agentRoot, merged);
|
|
73
|
+
// Mirror the company name/domain into the agent SoT so {{company.*}} and the
|
|
74
|
+
// env file stay consistent.
|
|
75
|
+
try {
|
|
76
|
+
const aj = join(ctx.agentRoot, "config", "agent.json");
|
|
77
|
+
if (existsSync(aj)) {
|
|
78
|
+
const id = JSON.parse(readFileSync(aj, "utf-8"));
|
|
79
|
+
id.company = merged.name || id.company;
|
|
80
|
+
id.companyDomain = merged.domain || id.companyDomain;
|
|
81
|
+
const tmp = `${aj}.${process.pid}.${Date.now()}.tmp`;
|
|
82
|
+
writeFileSync(tmp, JSON.stringify(id, null, 2) + "\n", "utf-8");
|
|
83
|
+
renameSync(tmp, aj);
|
|
84
|
+
}
|
|
85
|
+
} catch { /* fail-open */ }
|
|
86
|
+
|
|
87
|
+
gatherContextPack(ctx.agentRoot);
|
|
88
|
+
runGenerator("generate-company.mjs", ctx.agentRoot, { maestroRoot: ctx.maestroRoot });
|
|
89
|
+
runGenerator("generate-agent-env.mjs", ctx.agentRoot, { maestroRoot: ctx.maestroRoot });
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
async verify(ctx) {
|
|
93
|
+
const c = readCompany(ctx.agentRoot);
|
|
94
|
+
const nameOk = !isUnset(c.name);
|
|
95
|
+
const overviewOk = existsSync(join(ctx.agentRoot, "docs", "company", "overview.md"));
|
|
96
|
+
return {
|
|
97
|
+
ok: nameOk && overviewOk,
|
|
98
|
+
checks: [
|
|
99
|
+
{ name: "company.json populated", ok: nameOk, remedy: nameOk ? undefined : "run `maestro setup --only company`" },
|
|
100
|
+
{ name: "company overview generated", ok: overviewOk, remedy: overviewOk ? undefined : "run generate-company.mjs" },
|
|
101
|
+
],
|
|
102
|
+
};
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
function deriveDomain(website) {
|
|
107
|
+
if (!website) return "";
|
|
108
|
+
try { return new URL(website.startsWith("http") ? website : `https://${website}`).hostname.replace(/^www\./, ""); }
|
|
109
|
+
catch { return ""; }
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function pull(ctx) {
|
|
113
|
+
const ans = ctx.answers || {};
|
|
114
|
+
if (ans.company) return ans.company;
|
|
115
|
+
const map = { name: "companyName", website: "companyWebsite", domain: "companyDomain", industry: "companyIndustry", stage: "companyStage", tagline: "companyTagline" };
|
|
116
|
+
const out = {};
|
|
117
|
+
let any = false;
|
|
118
|
+
for (const [k, src] of Object.entries(map)) { if (src in ans) { out[k] = ans[src]; any = true; } }
|
|
119
|
+
return any ? out : null;
|
|
120
|
+
}
|