@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,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* check-no-build-artifacts.mjs — packaging hygiene ratchet (Phase 0 item 0.5).
|
|
3
|
+
*
|
|
4
|
+
* The npm tarball ships everything under the package.json `files[]` allowlist,
|
|
5
|
+
* which includes `scripts/`. Two artifact classes must never be tracked there:
|
|
6
|
+
*
|
|
7
|
+
* 1. Python bytecode (`*.pyc`, `*.pyo`, anything under `__pycache__/`). Beyond
|
|
8
|
+
* being build output, the original instance shipped cross-tenant bytecode
|
|
9
|
+
* (e.g. `send-email-as-principal.cpython-313.pyc`) inside the tarball — a
|
|
10
|
+
* confidentiality leak that source-level scrubbing alone would miss.
|
|
11
|
+
* 2. Symlinks. A tracked symlink resolves only on the author's machine
|
|
12
|
+
* (the broken `validate_outbound.py -> /Users/agent/...` symlink silently
|
|
13
|
+
* disabled Layer-0 outbound validation on every other machine) and cannot
|
|
14
|
+
* survive `npm pack`/install intact. Ship real files, never links.
|
|
15
|
+
*
|
|
16
|
+
* Source of truth is `git ls-files -s` (the index), so this catches artifacts
|
|
17
|
+
* the moment they are staged, before they can be published.
|
|
18
|
+
*
|
|
19
|
+
* Pure, dependency-light: Node builtins only. ESM.
|
|
20
|
+
*
|
|
21
|
+
* Usage: `node scripts/ci/check-no-build-artifacts.mjs` (exit 0 ok / 1 found)
|
|
22
|
+
* @module scripts/ci/check-no-build-artifacts
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
"use strict";
|
|
26
|
+
|
|
27
|
+
import { execFileSync } from "node:child_process";
|
|
28
|
+
import path from "node:path";
|
|
29
|
+
import { fileURLToPath } from "node:url";
|
|
30
|
+
|
|
31
|
+
const REPO_ROOT = path.resolve(fileURLToPath(new URL(".", import.meta.url)), "..", "..");
|
|
32
|
+
|
|
33
|
+
/** Tracked-path patterns that must never appear in the index. @type {Array<{label:string, re:RegExp}>} */
|
|
34
|
+
export const ARTIFACT_PATTERNS = [
|
|
35
|
+
{ label: "python bytecode (*.pyc)", re: /\.pyc$/ },
|
|
36
|
+
{ label: "python bytecode (*.pyo)", re: /\.pyo$/ },
|
|
37
|
+
{ label: "bytecode cache dir (__pycache__)", re: /(^|\/)__pycache__(\/|$)/ },
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
/** git's mode for a symbolic link entry in `git ls-files -s`. */
|
|
41
|
+
const GIT_SYMLINK_MODE = "120000";
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Parse `git ls-files -s` output into {mode, path} rows. Each line is:
|
|
45
|
+
* <mode> <object> <stage>\t<path>
|
|
46
|
+
* @param {string} stdout
|
|
47
|
+
* @returns {Array<{mode:string, path:string}>}
|
|
48
|
+
*/
|
|
49
|
+
export function parseLsFiles(stdout) {
|
|
50
|
+
const rows = [];
|
|
51
|
+
for (const line of stdout.split("\n")) {
|
|
52
|
+
if (!line) continue;
|
|
53
|
+
const tab = line.indexOf("\t");
|
|
54
|
+
if (tab === -1) continue;
|
|
55
|
+
const meta = line.slice(0, tab).split(/\s+/);
|
|
56
|
+
const file = line.slice(tab + 1);
|
|
57
|
+
rows.push({ mode: meta[0] || "", path: file });
|
|
58
|
+
}
|
|
59
|
+
return rows;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @param {object} [opts] @param {string} [opts.cwd=REPO_ROOT]
|
|
64
|
+
* @returns {Array<{ file: string, reason: string }>}
|
|
65
|
+
*/
|
|
66
|
+
export function findArtifacts(opts = {}) {
|
|
67
|
+
const cwd = opts.cwd || REPO_ROOT;
|
|
68
|
+
let stdout;
|
|
69
|
+
try {
|
|
70
|
+
stdout = execFileSync("git", ["ls-files", "-s"], { cwd, encoding: "utf8", maxBuffer: 64 * 1024 * 1024 });
|
|
71
|
+
} catch (err) {
|
|
72
|
+
// No git / not a work tree: cannot assess, fail closed.
|
|
73
|
+
throw new Error(`git ls-files failed: ${err && err.message ? err.message : err}`);
|
|
74
|
+
}
|
|
75
|
+
const offenders = [];
|
|
76
|
+
for (const { mode, path: file } of parseLsFiles(stdout)) {
|
|
77
|
+
if (mode === GIT_SYMLINK_MODE) {
|
|
78
|
+
offenders.push({ file, reason: "tracked symlink (does not survive npm pack; may point cross-tenant)" });
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
for (const p of ARTIFACT_PATTERNS) {
|
|
82
|
+
if (p.re.test(file)) {
|
|
83
|
+
offenders.push({ file, reason: p.label });
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return offenders;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** @param {string} [cwd=REPO_ROOT] @returns {Promise<number>} */
|
|
92
|
+
export async function run(cwd = REPO_ROOT) {
|
|
93
|
+
let offenders;
|
|
94
|
+
try {
|
|
95
|
+
offenders = findArtifacts({ cwd });
|
|
96
|
+
} catch (err) {
|
|
97
|
+
console.error(`check-no-build-artifacts: ERROR — ${err.message}`);
|
|
98
|
+
return 2;
|
|
99
|
+
}
|
|
100
|
+
if (offenders.length === 0) {
|
|
101
|
+
console.log("check-no-build-artifacts: OK (no tracked bytecode or symlinks)");
|
|
102
|
+
return 0;
|
|
103
|
+
}
|
|
104
|
+
console.error("check-no-build-artifacts: FAIL — packaging artifacts tracked in the index:");
|
|
105
|
+
for (const o of offenders) console.error(` ${o.file} :: ${o.reason}`);
|
|
106
|
+
return 1;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
110
|
+
run().then((c) => process.exit(c)).catch((e) => { console.error("check-no-build-artifacts: ERROR", e && e.message ? e.message : e); process.exit(2); });
|
|
111
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for check-no-build-artifacts.mjs — packaging hygiene guard.
|
|
3
|
+
* Pure-function level (parseLsFiles + pattern matching); no git invocation.
|
|
4
|
+
* @module scripts/ci/check-no-build-artifacts.test
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { describe, it } from "node:test";
|
|
8
|
+
import assert from "node:assert/strict";
|
|
9
|
+
import { parseLsFiles, ARTIFACT_PATTERNS } from "./check-no-build-artifacts.mjs";
|
|
10
|
+
|
|
11
|
+
/** Re-implement the offender classification over parsed rows (mirrors findArtifacts' inner loop). */
|
|
12
|
+
function classify(rows) {
|
|
13
|
+
const out = [];
|
|
14
|
+
for (const { mode, path: file } of rows) {
|
|
15
|
+
if (mode === "120000") { out.push({ file, reason: "symlink" }); continue; }
|
|
16
|
+
for (const p of ARTIFACT_PATTERNS) if (p.re.test(file)) { out.push({ file, reason: p.label }); break; }
|
|
17
|
+
}
|
|
18
|
+
return out;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
describe("check-no-build-artifacts: parseLsFiles", () => {
|
|
22
|
+
it("parses mode + path from `git ls-files -s` rows", () => {
|
|
23
|
+
const stdout = [
|
|
24
|
+
"100644 abc123 0\tlib/index.js",
|
|
25
|
+
"120000 def456 0\tscripts/validate_outbound.py",
|
|
26
|
+
"100755 aaa111 0\tscripts/send-email.sh",
|
|
27
|
+
].join("\n");
|
|
28
|
+
const rows = parseLsFiles(stdout);
|
|
29
|
+
assert.equal(rows.length, 3);
|
|
30
|
+
assert.deepEqual(rows[0], { mode: "100644", path: "lib/index.js" });
|
|
31
|
+
assert.equal(rows[1].mode, "120000");
|
|
32
|
+
assert.equal(rows[1].path, "scripts/validate_outbound.py");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("ignores blank lines and malformed rows (no tab)", () => {
|
|
36
|
+
const rows = parseLsFiles("\n100644 abc 0\tok.js\ngarbage-no-tab\n");
|
|
37
|
+
assert.equal(rows.length, 1);
|
|
38
|
+
assert.equal(rows[0].path, "ok.js");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("preserves paths containing spaces (split only on the first tab)", () => {
|
|
42
|
+
const rows = parseLsFiles("100644 abc 0\tdocs/a file with spaces.md");
|
|
43
|
+
assert.equal(rows[0].path, "docs/a file with spaces.md");
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe("check-no-build-artifacts: classification", () => {
|
|
48
|
+
it("flags tracked symlinks", () => {
|
|
49
|
+
const off = classify(parseLsFiles("120000 x 0\tscripts/validate_outbound.py"));
|
|
50
|
+
assert.equal(off.length, 1);
|
|
51
|
+
assert.equal(off[0].reason, "symlink");
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("flags .pyc, .pyo, and __pycache__ paths", () => {
|
|
55
|
+
const off = classify(parseLsFiles([
|
|
56
|
+
"100644 a 0\tscripts/__pycache__/x.cpython-313.pyc",
|
|
57
|
+
"100644 b 0\tscripts/foo.pyo",
|
|
58
|
+
"100644 c 0\tscripts/__pycache__/bar.py",
|
|
59
|
+
].join("\n")));
|
|
60
|
+
assert.equal(off.length, 3);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("passes clean trees (real source files, executable shell)", () => {
|
|
64
|
+
const off = classify(parseLsFiles([
|
|
65
|
+
"100644 a 0\tlib/index.js",
|
|
66
|
+
"100755 b 0\tscripts/send-email.sh",
|
|
67
|
+
"100644 c 0\tscripts/validate_outbound.py",
|
|
68
|
+
].join("\n")));
|
|
69
|
+
assert.equal(off.length, 0);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* check-no-confidential.mjs — CI guard that fails if confidential instance data
|
|
3
|
+
* would ship in the published npm tarball.
|
|
4
|
+
*
|
|
5
|
+
* Why this exists: `@cohortapp/agent-sdk` is a *framework* published (publicly) to
|
|
6
|
+
* npm and copied into every agent repo. It must never carry one instance's
|
|
7
|
+
* confidential business data. A path-based prune (`files[]` + `.npmignore`) is
|
|
8
|
+
* necessary but NOT sufficient — confidential markers leak into otherwise-
|
|
9
|
+
* legitimate framework files (e.g. architecture docs that use real M&A targets
|
|
10
|
+
* as examples). This guard inspects the *contents* of every file that would
|
|
11
|
+
* actually ship (`npm pack --dry-run --json`) for a denylist of markers and
|
|
12
|
+
* fails closed.
|
|
13
|
+
*
|
|
14
|
+
* IMPORTANT — the denylist is NOT hardcoded here. A content-denylist that
|
|
15
|
+
* embeds the confidential terms would itself become the leak it prevents (a
|
|
16
|
+
* shipped file that literally lists the protected names). The sensitive
|
|
17
|
+
* markers live in `<repo>/.confidential-patterns.json`, which is deliberately
|
|
18
|
+
* NOT shipped (absent from files[]; listed in .npmignore). This source file
|
|
19
|
+
* contains only safe, generic structural logic. If that denylist file is
|
|
20
|
+
* absent (e.g. in an installed package or a fork), the content scan is skipped
|
|
21
|
+
* with a warning and only the generic path check runs.
|
|
22
|
+
*
|
|
23
|
+
* Scope note: the denylist is the genuinely-confidential set (named live
|
|
24
|
+
* acquisition targets, their principals, entity registration numbers) — NOT
|
|
25
|
+
* pervasive instance identity (the original Sophie/Mehran/Adaptic deployment). De-
|
|
26
|
+
* identifying those is the check-no-residual-identity guard's job.
|
|
27
|
+
*
|
|
28
|
+
* Pure, dependency-light: Node builtins only. ESM ("type":"module").
|
|
29
|
+
*
|
|
30
|
+
* Usage: `node scripts/ci/check-no-confidential.mjs`
|
|
31
|
+
* exit 0 → clean; exit 1 → confidential content would ship; exit 2 → could
|
|
32
|
+
* not run `npm pack` (fail closed for a security guard).
|
|
33
|
+
*
|
|
34
|
+
* @module scripts/ci/check-no-confidential
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
"use strict";
|
|
38
|
+
|
|
39
|
+
import { execFileSync } from "node:child_process";
|
|
40
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
41
|
+
import path from "node:path";
|
|
42
|
+
import { fileURLToPath } from "node:url";
|
|
43
|
+
|
|
44
|
+
/** Repo root: two levels up from scripts/ci/. @type {string} */
|
|
45
|
+
const REPO_ROOT = path.resolve(fileURLToPath(new URL(".", import.meta.url)), "..", "..");
|
|
46
|
+
|
|
47
|
+
/** Non-shipped denylist file (see header). @type {string} */
|
|
48
|
+
export const DENYLIST_RELATIVE = ".confidential-patterns.json";
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Generic, safe-to-ship path markers (directory/category names, not the
|
|
52
|
+
* confidential association itself). Defense-in-depth against re-adding a
|
|
53
|
+
* `docs/` blanket to files[].
|
|
54
|
+
* @type {RegExp}
|
|
55
|
+
*/
|
|
56
|
+
export const DENY_PATH_RE = /(?:^|\/)business-synthesis\/|ownership-structure|final-ownership/i;
|
|
57
|
+
|
|
58
|
+
/** Extensions whose contents are scanned (text formats only). @type {Set<string>} */
|
|
59
|
+
export const TEXT_EXT = new Set([
|
|
60
|
+
".md", ".markdown", ".json", ".yaml", ".yml", ".js", ".mjs", ".cjs",
|
|
61
|
+
".ts", ".txt", ".html", ".htm", ".latex", ".tex", ".sh", ".py", ".env", ".example",
|
|
62
|
+
]);
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Load and compile content patterns from the non-shipped denylist file.
|
|
66
|
+
*
|
|
67
|
+
* @param {string} [cwd=REPO_ROOT]
|
|
68
|
+
* @returns {{ patterns: Array<{ label: string, re: RegExp }>, present: boolean }}
|
|
69
|
+
*/
|
|
70
|
+
export function loadContentPatterns(cwd = REPO_ROOT) {
|
|
71
|
+
const file = path.join(cwd, DENYLIST_RELATIVE);
|
|
72
|
+
if (!existsSync(file)) return { patterns: [], present: false };
|
|
73
|
+
const raw = JSON.parse(readFileSync(file, "utf8"));
|
|
74
|
+
const patterns = (raw.contentPatterns || []).map((p) => ({
|
|
75
|
+
label: p.label || "confidential marker",
|
|
76
|
+
re: new RegExp(p.pattern, p.flags || ""),
|
|
77
|
+
}));
|
|
78
|
+
return { patterns, present: true };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Scan a single text blob for confidential markers. Pure (no I/O) — unit-testable.
|
|
83
|
+
*
|
|
84
|
+
* @param {string} text
|
|
85
|
+
* @param {Array<{ label: string, re: RegExp }>} patterns
|
|
86
|
+
* @returns {Array<{ label: string, match: string }>}
|
|
87
|
+
*/
|
|
88
|
+
export function scanText(text, patterns) {
|
|
89
|
+
if (typeof text !== "string" || text.length === 0 || !Array.isArray(patterns)) return [];
|
|
90
|
+
const hits = [];
|
|
91
|
+
for (const p of patterns) {
|
|
92
|
+
const m = text.match(p.re);
|
|
93
|
+
if (m) hits.push({ label: p.label, match: m[0] });
|
|
94
|
+
}
|
|
95
|
+
return hits;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Ask npm which files the tarball would contain.
|
|
100
|
+
*
|
|
101
|
+
* @param {string} [cwd=REPO_ROOT]
|
|
102
|
+
* @returns {string[]} relative paths
|
|
103
|
+
*/
|
|
104
|
+
export function listPackedFiles(cwd = REPO_ROOT) {
|
|
105
|
+
const out = execFileSync("npm", ["pack", "--dry-run", "--json"], {
|
|
106
|
+
cwd,
|
|
107
|
+
encoding: "utf8",
|
|
108
|
+
maxBuffer: 32 * 1024 * 1024,
|
|
109
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
110
|
+
});
|
|
111
|
+
const parsed = JSON.parse(out);
|
|
112
|
+
const entry = Array.isArray(parsed) ? parsed[0] : parsed;
|
|
113
|
+
const files = (entry && entry.files) || [];
|
|
114
|
+
return files.map((f) => f.path).filter((p) => typeof p === "string");
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Inspect every would-ship file for confidential paths/content.
|
|
119
|
+
*
|
|
120
|
+
* @param {object} [opts]
|
|
121
|
+
* @param {string} [opts.cwd=REPO_ROOT]
|
|
122
|
+
* @param {string[]} [opts.files] - Pre-computed packed file list (skips npm pack).
|
|
123
|
+
* @param {Array<{ label: string, re: RegExp }>} [opts.patterns] - Pre-loaded content patterns.
|
|
124
|
+
* @returns {{ ok: boolean, scanned: number, denylistPresent: boolean, pathHits: string[], contentHits: Array<{ file: string, label: string, match: string }> }}
|
|
125
|
+
*/
|
|
126
|
+
export function scanPackedForConfidential(opts = {}) {
|
|
127
|
+
const cwd = opts.cwd || REPO_ROOT;
|
|
128
|
+
const files = opts.files || listPackedFiles(cwd);
|
|
129
|
+
let patterns = opts.patterns;
|
|
130
|
+
let denylistPresent = true;
|
|
131
|
+
if (!patterns) {
|
|
132
|
+
const loaded = loadContentPatterns(cwd);
|
|
133
|
+
patterns = loaded.patterns;
|
|
134
|
+
denylistPresent = loaded.present;
|
|
135
|
+
}
|
|
136
|
+
const pathHits = files.filter((f) => DENY_PATH_RE.test(f));
|
|
137
|
+
const contentHits = [];
|
|
138
|
+
let scanned = 0;
|
|
139
|
+
if (patterns.length > 0) {
|
|
140
|
+
for (const rel of files) {
|
|
141
|
+
const ext = path.extname(rel).toLowerCase();
|
|
142
|
+
if (!TEXT_EXT.has(ext)) continue;
|
|
143
|
+
const abs = path.resolve(cwd, rel);
|
|
144
|
+
if (!existsSync(abs)) continue;
|
|
145
|
+
let body;
|
|
146
|
+
try {
|
|
147
|
+
body = readFileSync(abs, "utf8");
|
|
148
|
+
} catch {
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
scanned++;
|
|
152
|
+
for (const hit of scanText(body, patterns)) {
|
|
153
|
+
contentHits.push({ file: rel, label: hit.label, match: hit.match });
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return { ok: pathHits.length === 0 && contentHits.length === 0, scanned, denylistPresent, pathHits, contentHits };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Run the guard and print a human report.
|
|
162
|
+
*
|
|
163
|
+
* @param {string} [cwd=REPO_ROOT]
|
|
164
|
+
* @returns {Promise<number>} exit code (0 ok, 1 leak found, 2 could not run).
|
|
165
|
+
*/
|
|
166
|
+
export async function run(cwd = REPO_ROOT) {
|
|
167
|
+
let result;
|
|
168
|
+
try {
|
|
169
|
+
result = scanPackedForConfidential({ cwd });
|
|
170
|
+
} catch (err) {
|
|
171
|
+
console.error("check-no-confidential: ERROR — could not run `npm pack --dry-run --json` (failing closed):", err && err.message ? err.message : err);
|
|
172
|
+
return 2;
|
|
173
|
+
}
|
|
174
|
+
const { ok, scanned, denylistPresent, pathHits, contentHits } = result;
|
|
175
|
+
if (!denylistPresent) {
|
|
176
|
+
console.warn(`check-no-confidential: WARN — ${DENYLIST_RELATIVE} not present; ran path-only scan (content scan skipped).`);
|
|
177
|
+
}
|
|
178
|
+
if (ok) {
|
|
179
|
+
const mode = denylistPresent ? `${scanned} packed text files scanned` : "path-only";
|
|
180
|
+
console.log(`check-no-confidential: OK (${mode}; no confidential markers)`);
|
|
181
|
+
return 0;
|
|
182
|
+
}
|
|
183
|
+
console.error("check-no-confidential: FAIL — confidential content would ship in the npm tarball:");
|
|
184
|
+
for (const p of pathHits) console.error(` [path] ${p}`);
|
|
185
|
+
for (const c of contentHits) console.error(` [content] ${c.file} :: ${c.label} ("${c.match}")`);
|
|
186
|
+
console.error(`check-no-confidential: ${pathHits.length} path hit(s) + ${contentHits.length} content hit(s)`);
|
|
187
|
+
return 1;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Run when invoked directly (not when imported).
|
|
191
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
192
|
+
run()
|
|
193
|
+
.then((code) => process.exit(code))
|
|
194
|
+
.catch((err) => {
|
|
195
|
+
console.error("check-no-confidential: ERROR", err && err.message ? err.message : err);
|
|
196
|
+
process.exit(2);
|
|
197
|
+
});
|
|
198
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* check-no-conflict-markers.mjs — CI guard that fails if any tracked file
|
|
3
|
+
* contains a git merge-conflict marker.
|
|
4
|
+
*
|
|
5
|
+
* Ported/adapted from OpenClaw's scripts/check-no-conflict-markers.mjs idea.
|
|
6
|
+
* Pure, dependency-light: Node builtins + git (via execFile) only. ESM module
|
|
7
|
+
* (maestro is "type":"module").
|
|
8
|
+
*
|
|
9
|
+
* A successful `git merge` that hit a conflict leaves lines like
|
|
10
|
+
* `<<<<<<< HEAD`, `=======`, `>>>>>>> branch`, and (with merge.conflictStyle
|
|
11
|
+
* diff3) `||||||| base`. If one of those slips past review into a commit, the
|
|
12
|
+
* code may still parse but is semantically broken. This guard scans every
|
|
13
|
+
* tracked, text file for those exact markers and exits non-zero if found.
|
|
14
|
+
*
|
|
15
|
+
* Usage (standalone): `node scripts/ci/check-no-conflict-markers.mjs`
|
|
16
|
+
* exit 0 → clean; exit 1 → markers found (details printed to stderr).
|
|
17
|
+
*
|
|
18
|
+
* Programmatic: `import { findConflictMarkers, lineHasConflictMarker } from "./check-no-conflict-markers.mjs"`
|
|
19
|
+
*
|
|
20
|
+
* @module scripts/ci/check-no-conflict-markers
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
"use strict";
|
|
24
|
+
|
|
25
|
+
import { execFile } from "node:child_process";
|
|
26
|
+
import { promisify } from "node:util";
|
|
27
|
+
import { readFile } from "node:fs/promises";
|
|
28
|
+
import path from "node:path";
|
|
29
|
+
import { fileURLToPath } from "node:url";
|
|
30
|
+
|
|
31
|
+
const execFileAsync = promisify(execFile);
|
|
32
|
+
|
|
33
|
+
/** Repo root: two levels up from scripts/ci/. @type {string} */
|
|
34
|
+
const REPO_ROOT = path.resolve(fileURLToPath(new URL(".", import.meta.url)), "..", "..");
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Conflict-marker prefixes. A line is a marker if, after stripping a trailing
|
|
38
|
+
* carriage return, it equals one of these or starts with one followed by a
|
|
39
|
+
* space. Markers are exactly seven repeated characters — we match the prefix
|
|
40
|
+
* and then require either end-of-line or a single space so that e.g. a markdown
|
|
41
|
+
* rule of eight or more `=` does not false-positive.
|
|
42
|
+
* @type {string[]}
|
|
43
|
+
*/
|
|
44
|
+
const MARKER_PREFIXES = ["<<<<<<<", "=======", ">>>>>>>", "|||||||"];
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* True if a single line of text is a git conflict marker.
|
|
48
|
+
*
|
|
49
|
+
* @param {string} line - One line of file content (without the newline).
|
|
50
|
+
* @returns {boolean}
|
|
51
|
+
*/
|
|
52
|
+
export function lineHasConflictMarker(line) {
|
|
53
|
+
if (typeof line !== "string") return false;
|
|
54
|
+
// Tolerate CRLF: drop a trailing carriage return.
|
|
55
|
+
const trimmed = line.endsWith("\r") ? line.slice(0, -1) : line;
|
|
56
|
+
for (const prefix of MARKER_PREFIXES) {
|
|
57
|
+
if (trimmed === prefix) return true;
|
|
58
|
+
if (trimmed.startsWith(prefix + " ")) return true;
|
|
59
|
+
}
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Scan a blob of file content for conflict markers.
|
|
65
|
+
*
|
|
66
|
+
* @param {string} content - Full file text.
|
|
67
|
+
* @returns {Array<{ line: number, text: string }>} 1-based line hits.
|
|
68
|
+
*/
|
|
69
|
+
export function scanContentForMarkers(content) {
|
|
70
|
+
if (typeof content !== "string" || content.length === 0) return [];
|
|
71
|
+
const hits = [];
|
|
72
|
+
const lines = content.split("\n");
|
|
73
|
+
for (let i = 0; i < lines.length; i++) {
|
|
74
|
+
if (lineHasConflictMarker(lines[i])) {
|
|
75
|
+
hits.push({ line: i + 1, text: lines[i].replace(/\r$/, "") });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return hits;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Heuristic: skip obviously-binary content so we don't read megabytes of
|
|
83
|
+
* images/audio looking for ASCII markers. A NUL byte in the first 8 KiB is the
|
|
84
|
+
* classic "this is binary" signal git itself uses.
|
|
85
|
+
*
|
|
86
|
+
* @param {Buffer} buf
|
|
87
|
+
* @returns {boolean}
|
|
88
|
+
*/
|
|
89
|
+
function looksBinary(buf) {
|
|
90
|
+
const n = Math.min(buf.length, 8192);
|
|
91
|
+
for (let i = 0; i < n; i++) {
|
|
92
|
+
if (buf[i] === 0) return true;
|
|
93
|
+
}
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* List every tracked file path (relative to repo root) via git.
|
|
99
|
+
*
|
|
100
|
+
* @param {string} [cwd=REPO_ROOT]
|
|
101
|
+
* @returns {Promise<string[]>}
|
|
102
|
+
*/
|
|
103
|
+
export async function listTrackedFiles(cwd = REPO_ROOT) {
|
|
104
|
+
// -z gives NUL-separated paths so filenames with spaces/newlines are safe.
|
|
105
|
+
const { stdout } = await execFileAsync("git", ["ls-files", "-z"], {
|
|
106
|
+
cwd,
|
|
107
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
108
|
+
});
|
|
109
|
+
return stdout.split("\0").filter((p) => p.length > 0);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Scan all tracked files for conflict markers.
|
|
114
|
+
*
|
|
115
|
+
* @param {string} [cwd=REPO_ROOT]
|
|
116
|
+
* @returns {Promise<Array<{ file: string, hits: Array<{ line: number, text: string }> }>>}
|
|
117
|
+
* One entry per offending file. Empty array means the tree is clean.
|
|
118
|
+
*/
|
|
119
|
+
export async function findConflictMarkers(cwd = REPO_ROOT) {
|
|
120
|
+
const files = await listTrackedFiles(cwd);
|
|
121
|
+
const offenders = [];
|
|
122
|
+
for (const rel of files) {
|
|
123
|
+
const abs = path.resolve(cwd, rel);
|
|
124
|
+
let buf;
|
|
125
|
+
try {
|
|
126
|
+
buf = await readFile(abs);
|
|
127
|
+
} catch {
|
|
128
|
+
// File tracked but missing on disk (sparse checkout, deleted-not-staged):
|
|
129
|
+
// nothing to scan, skip quietly.
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (looksBinary(buf)) continue;
|
|
133
|
+
const hits = scanContentForMarkers(buf.toString("utf8"));
|
|
134
|
+
if (hits.length > 0) offenders.push({ file: rel, hits });
|
|
135
|
+
}
|
|
136
|
+
return offenders;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Run the check and print a human report.
|
|
141
|
+
*
|
|
142
|
+
* @param {string} [cwd=REPO_ROOT]
|
|
143
|
+
* @returns {Promise<number>} Process exit code (0 = clean, 1 = markers found).
|
|
144
|
+
*/
|
|
145
|
+
export async function run(cwd = REPO_ROOT) {
|
|
146
|
+
const offenders = await findConflictMarkers(cwd);
|
|
147
|
+
if (offenders.length === 0) {
|
|
148
|
+
console.log("check-no-conflict-markers: OK (no conflict markers in tracked files)");
|
|
149
|
+
return 0;
|
|
150
|
+
}
|
|
151
|
+
console.error("check-no-conflict-markers: FAIL — conflict markers found:");
|
|
152
|
+
for (const { file, hits } of offenders) {
|
|
153
|
+
for (const h of hits) {
|
|
154
|
+
console.error(` ${file}:${h.line}: ${h.text}`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
console.error(`check-no-conflict-markers: ${offenders.length} file(s) contain conflict markers`);
|
|
158
|
+
return 1;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Run when invoked directly (not when imported).
|
|
162
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
163
|
+
run()
|
|
164
|
+
.then((code) => process.exit(code))
|
|
165
|
+
.catch((err) => {
|
|
166
|
+
console.error("check-no-conflict-markers: ERROR", err && err.message ? err.message : err);
|
|
167
|
+
process.exit(2);
|
|
168
|
+
});
|
|
169
|
+
}
|