@stravica-ai/digital-operator-pa 0.1.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/LICENSE +21 -0
- package/README.md +170 -0
- package/bin/desk +9 -0
- package/bin/distill +2 -0
- package/bin/estate-sync +2 -0
- package/bin/launch +2 -0
- package/bin/memory-sync +2 -0
- package/bin/migrate-inspect +2 -0
- package/bin/pa +2 -0
- package/bin/pa-coordinator-hook +2 -0
- package/bin/pa-init +2 -0
- package/bin/research +2 -0
- package/bin/scheduler +2 -0
- package/bin/secrets +2 -0
- package/bin/slice +2 -0
- package/bin/smoke +9 -0
- package/bin/sync +2 -0
- package/dist/engineering-pa-cli/README.md +205 -0
- package/dist/engineering-pa-cli/bin/estate-sync +2 -0
- package/dist/engineering-pa-cli/bin/launch +2 -0
- package/dist/engineering-pa-cli/bin/pa +2 -0
- package/dist/engineering-pa-cli/bin/pa-coordinator-hook +12 -0
- package/dist/engineering-pa-cli/bin/research +2 -0
- package/dist/engineering-pa-cli/bin/scheduler +2 -0
- package/dist/engineering-pa-cli/bin/secrets +2 -0
- package/dist/engineering-pa-cli/bin/smoke +9 -0
- package/dist/engineering-pa-cli/bin/sync +2 -0
- package/dist/engineering-pa-cli/config/coordinator-allowlist.json +118 -0
- package/dist/engineering-pa-cli/docs/coordinator-hook.md +211 -0
- package/dist/engineering-pa-cli/docs/estate-sync.md +412 -0
- package/dist/engineering-pa-cli/docs/gsc.md +175 -0
- package/dist/engineering-pa-cli/docs/lint-linkedin.md +139 -0
- package/dist/engineering-pa-cli/docs/secrets.md +221 -0
- package/dist/engineering-pa-cli/docs/smoke.md +113 -0
- package/dist/engineering-pa-cli/docs/staging-alias.md +114 -0
- package/dist/engineering-pa-cli/index.mjs +17 -0
- package/dist/engineering-pa-cli/package.json +37 -0
- package/dist/engineering-pa-cli/src/autonomy/cli.mjs +329 -0
- package/dist/engineering-pa-cli/src/autonomy/envelope.mjs +301 -0
- package/dist/engineering-pa-cli/src/coordinator-hook/cli.mjs +140 -0
- package/dist/engineering-pa-cli/src/coordinator-hook/config.mjs +134 -0
- package/dist/engineering-pa-cli/src/coordinator-hook/message.mjs +47 -0
- package/dist/engineering-pa-cli/src/coordinator-hook/policy.mjs +211 -0
- package/dist/engineering-pa-cli/src/coordinator-hook/repo-root.mjs +44 -0
- package/dist/engineering-pa-cli/src/coordinator-hook/self.mjs +30 -0
- package/dist/engineering-pa-cli/src/environment/cli.mjs +287 -0
- package/dist/engineering-pa-cli/src/environment/probes.mjs +302 -0
- package/dist/engineering-pa-cli/src/gsc/auth.mjs +148 -0
- package/dist/engineering-pa-cli/src/gsc/cli.mjs +93 -0
- package/dist/engineering-pa-cli/src/gsc/cloudflare.mjs +318 -0
- package/dist/engineering-pa-cli/src/gsc/commands/add-property.mjs +70 -0
- package/dist/engineering-pa-cli/src/gsc/commands/list-properties.mjs +33 -0
- package/dist/engineering-pa-cli/src/gsc/commands/pull-analytics.mjs +130 -0
- package/dist/engineering-pa-cli/src/gsc/commands/submit-sitemap.mjs +41 -0
- package/dist/engineering-pa-cli/src/gsc/commands/verify-via-cf-dns.mjs +189 -0
- package/dist/engineering-pa-cli/src/knowledge/call-log.mjs +59 -0
- package/dist/engineering-pa-cli/src/knowledge/cli.mjs +242 -0
- package/dist/engineering-pa-cli/src/knowledge/eval.mjs +446 -0
- package/dist/engineering-pa-cli/src/knowledge/formatters.mjs +331 -0
- package/dist/engineering-pa-cli/src/knowledge/graph.mjs +710 -0
- package/dist/engineering-pa-cli/src/launch/README.md +214 -0
- package/dist/engineering-pa-cli/src/launch/cli.mjs +51 -0
- package/dist/engineering-pa-cli/src/launch/commands/launch.mjs +106 -0
- package/dist/engineering-pa-cli/src/launch/identity.mjs +43 -0
- package/dist/engineering-pa-cli/src/launch/planner.mjs +44 -0
- package/dist/engineering-pa-cli/src/launch/repo-root.mjs +53 -0
- package/dist/engineering-pa-cli/src/launch/runner.mjs +147 -0
- package/dist/engineering-pa-cli/src/launch/task-loader.mjs +85 -0
- package/dist/engineering-pa-cli/src/lint/cli.mjs +61 -0
- package/dist/engineering-pa-cli/src/lint/commands/linkedin.mjs +133 -0
- package/dist/engineering-pa-cli/src/lint/detectors.mjs +90 -0
- package/dist/engineering-pa-cli/src/lint/linter.mjs +69 -0
- package/dist/engineering-pa-cli/src/lint/reporter.mjs +32 -0
- package/dist/engineering-pa-cli/src/lint/rules.mjs +132 -0
- package/dist/engineering-pa-cli/src/pa/cli.mjs +369 -0
- package/dist/engineering-pa-cli/src/pa/doc-groups.mjs +164 -0
- package/dist/engineering-pa-cli/src/pa/io.mjs +80 -0
- package/dist/engineering-pa-cli/src/pa/project-cli.mjs +385 -0
- package/dist/engineering-pa-cli/src/pa/projects.mjs +352 -0
- package/dist/engineering-pa-cli/src/pa/repo-root.mjs +64 -0
- package/dist/engineering-pa-cli/src/pa/session.mjs +254 -0
- package/dist/engineering-pa-cli/src/pa/work-cli.mjs +164 -0
- package/dist/engineering-pa-cli/src/pa/work.mjs +196 -0
- package/dist/engineering-pa-cli/src/pa/workstreams.mjs +201 -0
- package/dist/engineering-pa-cli/src/research/README.md +360 -0
- package/dist/engineering-pa-cli/src/research/briefs/extractor.md +103 -0
- package/dist/engineering-pa-cli/src/research/briefs/searcher.md +113 -0
- package/dist/engineering-pa-cli/src/research/briefs/synthesizer.md +124 -0
- package/dist/engineering-pa-cli/src/research/briefs/verifier.md +86 -0
- package/dist/engineering-pa-cli/src/research/cli.mjs +141 -0
- package/dist/engineering-pa-cli/src/research/commands/gates.mjs +176 -0
- package/dist/engineering-pa-cli/src/research/commands/list.mjs +56 -0
- package/dist/engineering-pa-cli/src/research/commands/mark.mjs +97 -0
- package/dist/engineering-pa-cli/src/research/commands/paths.mjs +69 -0
- package/dist/engineering-pa-cli/src/research/commands/prune.mjs +86 -0
- package/dist/engineering-pa-cli/src/research/commands/run.mjs +94 -0
- package/dist/engineering-pa-cli/src/research/commands/show.mjs +43 -0
- package/dist/engineering-pa-cli/src/research/docs/operator-present-flow.md +120 -0
- package/dist/engineering-pa-cli/src/research/format.mjs +59 -0
- package/dist/engineering-pa-cli/src/research/promote.mjs +260 -0
- package/dist/engineering-pa-cli/src/research/schema/finding.schema.json +55 -0
- package/dist/engineering-pa-cli/src/research/schema/gates.schema.json +34 -0
- package/dist/engineering-pa-cli/src/research/schema/manifest.schema.json +59 -0
- package/dist/engineering-pa-cli/src/research/schema/plan.schema.json +41 -0
- package/dist/engineering-pa-cli/src/research/schema/sources.schema.json +31 -0
- package/dist/engineering-pa-cli/src/research/store/findings.mjs +61 -0
- package/dist/engineering-pa-cli/src/research/store/gates.mjs +73 -0
- package/dist/engineering-pa-cli/src/research/store/index.mjs +312 -0
- package/dist/engineering-pa-cli/src/research/store/manifest.mjs +56 -0
- package/dist/engineering-pa-cli/src/research/store/plan.mjs +59 -0
- package/dist/engineering-pa-cli/src/research/store/sources.mjs +44 -0
- package/dist/engineering-pa-cli/src/research/store/validate.mjs +92 -0
- package/dist/engineering-pa-cli/src/research/workflows/research-normal.js +573 -0
- package/dist/engineering-pa-cli/src/scheduler/cli.mjs +197 -0
- package/dist/engineering-pa-cli/src/secrets/cli.mjs +254 -0
- package/dist/engineering-pa-cli/src/secrets/commands/backup-template.mjs +67 -0
- package/dist/engineering-pa-cli/src/secrets/commands/backup.mjs +85 -0
- package/dist/engineering-pa-cli/src/secrets/commands/delete.mjs +58 -0
- package/dist/engineering-pa-cli/src/secrets/commands/find.mjs +36 -0
- package/dist/engineering-pa-cli/src/secrets/commands/get.mjs +49 -0
- package/dist/engineering-pa-cli/src/secrets/commands/init.mjs +69 -0
- package/dist/engineering-pa-cli/src/secrets/commands/list.mjs +37 -0
- package/dist/engineering-pa-cli/src/secrets/commands/rotate.mjs +46 -0
- package/dist/engineering-pa-cli/src/secrets/commands/set.mjs +123 -0
- package/dist/engineering-pa-cli/src/secrets/commands/status.mjs +146 -0
- package/dist/engineering-pa-cli/src/secrets/commands/vaults.mjs +57 -0
- package/dist/engineering-pa-cli/src/secrets/commands/verify.mjs +143 -0
- package/dist/engineering-pa-cli/src/secrets/vault.mjs +766 -0
- package/dist/engineering-pa-cli/src/smoke/checks.mjs +199 -0
- package/dist/engineering-pa-cli/src/smoke/cli.mjs +108 -0
- package/dist/engineering-pa-cli/src/smoke/commands/run.mjs +237 -0
- package/dist/engineering-pa-cli/src/smoke/runner.mjs +191 -0
- package/dist/engineering-pa-cli/src/staging-alias/cli.mjs +91 -0
- package/dist/engineering-pa-cli/src/staging-alias/cloudflare.mjs +380 -0
- package/dist/engineering-pa-cli/src/staging-alias/commands/list.mjs +36 -0
- package/dist/engineering-pa-cli/src/staging-alias/commands/set.mjs +188 -0
- package/dist/engineering-pa-cli/src/staging-alias/commands/status.mjs +113 -0
- package/dist/engineering-pa-cli/src/staging-alias/commands/unset.mjs +95 -0
- package/dist/engineering-pa-cli/src/staging-alias/sites.mjs +105 -0
- package/dist/engineering-pa-cli/src/sync/cli.mjs +151 -0
- package/dist/engineering-pa-cli/src/sync/git.mjs +149 -0
- package/dist/engineering-pa-cli/src/sync/lock.mjs +30 -0
- package/dist/engineering-pa-cli/src/sync/regenerated-artifacts.mjs +114 -0
- package/dist/engineering-pa-cli/src/sync/registry-scope.mjs +260 -0
- package/dist/engineering-pa-cli/src/sync/run.mjs +531 -0
- package/dist/engineering-pa-cli/src/sync/runner.mjs +62 -0
- package/dist/engineering-pa-cli/src/sync/runs-digest.mjs +138 -0
- package/dist/engineering-pa-core/README.md +60 -0
- package/dist/engineering-pa-core/bin/desk +9 -0
- package/dist/engineering-pa-core/bin/distill +2 -0
- package/dist/engineering-pa-core/bin/memory-sync +2 -0
- package/dist/engineering-pa-core/bin/slice +2 -0
- package/dist/engineering-pa-core/docs/autonomy-envelope-tracker.md +111 -0
- package/dist/engineering-pa-core/docs/blueprints/README.md +45 -0
- package/dist/engineering-pa-core/docs/blueprints/docker-services/blueprint.md +168 -0
- package/dist/engineering-pa-core/docs/blueprints/git-identity/blueprint.md +116 -0
- package/dist/engineering-pa-core/docs/blueprints/ideas/custom-workspace-views.md +9 -0
- package/dist/engineering-pa-core/docs/blueprints/ideas/knowledge-domain-patterns.md +15 -0
- package/dist/engineering-pa-core/docs/blueprints/ideas/office-integration.md +11 -0
- package/dist/engineering-pa-core/docs/blueprints/ideas/team-scaling.md +9 -0
- package/dist/engineering-pa-core/docs/blueprints/ideas/work-personal-separation.md +9 -0
- package/dist/engineering-pa-core/docs/blueprints/scheduled-distillation/blueprint.md +129 -0
- package/dist/engineering-pa-core/docs/blueprints/tool-integration/blueprint.md +110 -0
- package/dist/engineering-pa-core/docs/commands/init.md +159 -0
- package/dist/engineering-pa-core/docs/commands/schedule.md +98 -0
- package/dist/engineering-pa-core/docs/commands/upgrade.md +67 -0
- package/dist/engineering-pa-core/docs/entity-location-map.md +93 -0
- package/dist/engineering-pa-core/docs/knowledge-scaffold.md +121 -0
- package/dist/engineering-pa-core/docs/lock.md +155 -0
- package/dist/engineering-pa-core/docs/memory-sync.md +28 -0
- package/dist/engineering-pa-core/docs/pa-operating-model.md +457 -0
- package/dist/engineering-pa-core/docs/scheduling.md +35 -0
- package/dist/engineering-pa-core/docs/standards/README.md +24 -0
- package/dist/engineering-pa-core/docs/standards/briefing-contract.md +161 -0
- package/dist/engineering-pa-core/docs/standards/tooling.md +240 -0
- package/dist/engineering-pa-core/docs/tools/distill.md +230 -0
- package/dist/engineering-pa-core/docs/tools/memory-sync.md +98 -0
- package/dist/engineering-pa-core/docs/tools/scheduler.md +243 -0
- package/dist/engineering-pa-core/docs/tools/slice.md +150 -0
- package/dist/engineering-pa-core/docs/upstream-issue-filing.md +88 -0
- package/dist/engineering-pa-core/docs/working-directory.md +29 -0
- package/dist/engineering-pa-core/docs/workspace-operations.md +46 -0
- package/dist/engineering-pa-core/index.mjs +21 -0
- package/dist/engineering-pa-core/knowledge/README.md +53 -0
- package/dist/engineering-pa-core/knowledge/canonical/.gitkeep +0 -0
- package/dist/engineering-pa-core/knowledge/content-voice/README.md +64 -0
- package/dist/engineering-pa-core/knowledge/content-voice/banned-ai-tells-baseline.md +133 -0
- package/dist/engineering-pa-core/knowledge/content-voice/banned-ai-tells-shape.md +58 -0
- package/dist/engineering-pa-core/knowledge/content-voice/banned-ai-tells-taxonomy.md +33 -0
- package/dist/engineering-pa-core/knowledge/content-voice/content-pipeline-routing.md +58 -0
- package/dist/engineering-pa-core/knowledge/content-voice/new-tell-capture-procedure.md +40 -0
- package/dist/engineering-pa-core/knowledge/content-voice/voice-guide-shape.md +43 -0
- package/dist/engineering-pa-core/knowledge/delegation-dispatch/README.md +34 -0
- package/dist/engineering-pa-core/knowledge/delegation-dispatch/briefing-contract.md +77 -0
- package/dist/engineering-pa-core/knowledge/delegation-dispatch/delegation-modes.md +84 -0
- package/dist/engineering-pa-core/knowledge/delegation-dispatch/dispatch-envelope.md +238 -0
- package/dist/engineering-pa-core/knowledge/delegation-dispatch/task-groups-and-orchestrators.md +62 -0
- package/dist/engineering-pa-core/knowledge/delegation-dispatch/team-improvement-loop.md +48 -0
- package/dist/engineering-pa-core/knowledge/delegation-dispatch/tool-development-policy.md +48 -0
- package/dist/engineering-pa-core/knowledge/engineering-discipline/README.md +33 -0
- package/dist/engineering-pa-core/knowledge/engineering-discipline/blast-radius-and-safety.md +50 -0
- package/dist/engineering-pa-core/knowledge/engineering-discipline/investigate-root-causes.md +48 -0
- package/dist/engineering-pa-core/knowledge/engineering-discipline/role-ladder.md +57 -0
- package/dist/engineering-pa-core/knowledge/engineering-discipline/verify-runtime-behaviour.md +53 -0
- package/dist/engineering-pa-core/knowledge/engineering-discipline/worktree-isolation.md +50 -0
- package/dist/engineering-pa-core/knowledge/knowledge-memory/README.md +33 -0
- package/dist/engineering-pa-core/knowledge/knowledge-memory/claude-md-hygiene.md +47 -0
- package/dist/engineering-pa-core/knowledge/knowledge-memory/journal-and-self-improvement.md +53 -0
- package/dist/engineering-pa-core/knowledge/knowledge-memory/knowledge-retrieval-discipline.md +62 -0
- package/dist/engineering-pa-core/knowledge/knowledge-memory/librarian-synthesis-mandate.md +56 -0
- package/dist/engineering-pa-core/knowledge/knowledge-memory/staging-and-observations.md +58 -0
- package/dist/engineering-pa-core/knowledge/operator-interface/README.md +32 -0
- package/dist/engineering-pa-core/knowledge/operator-interface/communication-style.md +33 -0
- package/dist/engineering-pa-core/knowledge/operator-interface/escalation-and-correction.md +45 -0
- package/dist/engineering-pa-core/knowledge/operator-interface/push-back-discipline.md +42 -0
- package/dist/engineering-pa-core/knowledge/operator-interface/session-boot-and-end.md +56 -0
- package/dist/engineering-pa-core/knowledge/platform-tooling/README.md +37 -0
- package/dist/engineering-pa-core/knowledge/platform-tooling/desk-cli-reference.md +52 -0
- package/dist/engineering-pa-core/knowledge/platform-tooling/environment-awareness.md +91 -0
- package/dist/engineering-pa-core/knowledge/platform-tooling/mcp-servers-catalogue.md +52 -0
- package/dist/engineering-pa-core/knowledge/platform-tooling/pa-cli-reference.md +55 -0
- package/dist/engineering-pa-core/knowledge/platform-tooling/scheduling-and-launchd.md +56 -0
- package/dist/engineering-pa-core/knowledge/platform-tooling/tool-discovery-discipline.md +49 -0
- package/dist/engineering-pa-core/knowledge/platform-tooling/working-directory-vs-project-root.md +43 -0
- package/dist/engineering-pa-core/knowledge/platform-tooling/workspace-server.md +51 -0
- package/dist/engineering-pa-core/knowledge/raw/.gitkeep +0 -0
- package/dist/engineering-pa-core/knowledge/scaffold/domains.json +5 -0
- package/dist/engineering-pa-core/knowledge/schema/.gitkeep +0 -0
- package/dist/engineering-pa-core/knowledge/work-management/README.md +33 -0
- package/dist/engineering-pa-core/knowledge/work-management/desk-cli.md +84 -0
- package/dist/engineering-pa-core/knowledge/work-management/progress-md.md +48 -0
- package/dist/engineering-pa-core/knowledge/work-management/projects.md +64 -0
- package/dist/engineering-pa-core/knowledge/work-management/spawn-log.md +48 -0
- package/dist/engineering-pa-core/knowledge/work-management/work-items.md +67 -0
- package/dist/engineering-pa-core/package.json +26 -0
- package/dist/engineering-pa-core/roles/agent-ops/definition.md +79 -0
- package/dist/engineering-pa-core/roles/dispatch-briefer/definition.md +195 -0
- package/dist/engineering-pa-core/roles/librarian/definition.md +176 -0
- package/dist/engineering-pa-core/roles/pa/definition.md +93 -0
- package/dist/engineering-pa-core/roles/system-admin/definition.md +106 -0
- package/dist/engineering-pa-core/roles/tool-developer/definition.md +116 -0
- package/dist/engineering-pa-core/roles/writer/definition.md +130 -0
- package/dist/engineering-pa-core/schema/autonomy-envelope.schema.json +61 -0
- package/dist/engineering-pa-core/schema/project.json +114 -0
- package/dist/engineering-pa-core/schema/schedule.json +63 -0
- package/dist/engineering-pa-core/schema/work-item.json +92 -0
- package/dist/engineering-pa-core/src/config/pa-package-name.mjs +30 -0
- package/dist/engineering-pa-core/src/desk/cli.mjs +95 -0
- package/dist/engineering-pa-core/src/desk/commands.mjs +398 -0
- package/dist/engineering-pa-core/src/desk/format.mjs +84 -0
- package/dist/engineering-pa-core/src/desk/repo.mjs +172 -0
- package/dist/engineering-pa-core/src/desk/team-scope.mjs +142 -0
- package/dist/engineering-pa-core/src/distill/cli.mjs +112 -0
- package/dist/engineering-pa-core/src/distill/prereqs.mjs +171 -0
- package/dist/engineering-pa-core/src/distill/prompt.mjs +74 -0
- package/dist/engineering-pa-core/src/distill/run.mjs +230 -0
- package/dist/engineering-pa-core/src/distill/runner.mjs +86 -0
- package/dist/engineering-pa-core/src/distill/staging.mjs +59 -0
- package/dist/engineering-pa-core/src/knowledge/retrieval/backlink-indexer.mjs +154 -0
- package/dist/engineering-pa-core/src/knowledge/retrieval/graph-builder.mjs +186 -0
- package/dist/engineering-pa-core/src/knowledge/retrieval/index-builder.mjs +225 -0
- package/dist/engineering-pa-core/src/knowledge/retrieval/query.mjs +263 -0
- package/dist/engineering-pa-core/src/lock/lock.mjs +202 -0
- package/dist/engineering-pa-core/src/memory-sync/cli-support.mjs +12 -0
- package/dist/engineering-pa-core/src/memory-sync/cli.mjs +56 -0
- package/dist/engineering-pa-core/src/memory-sync/commands/pull.mjs +44 -0
- package/dist/engineering-pa-core/src/memory-sync/commands/push.mjs +46 -0
- package/dist/engineering-pa-core/src/memory-sync/mirror.mjs +114 -0
- package/dist/engineering-pa-core/src/memory-sync/repo.mjs +65 -0
- package/dist/engineering-pa-core/src/scheduler/detect.mjs +111 -0
- package/dist/engineering-pa-core/src/scheduler/host.mjs +38 -0
- package/dist/engineering-pa-core/src/scheduler/index.mjs +13 -0
- package/dist/engineering-pa-core/src/scheduler/install.mjs +854 -0
- package/dist/engineering-pa-core/src/scheduler/list.mjs +168 -0
- package/dist/engineering-pa-core/src/scheduler/remove.mjs +176 -0
- package/dist/engineering-pa-core/src/scheduler/verify.mjs +435 -0
- package/dist/engineering-pa-core/src/session-boot/autonomy-injection.mjs +84 -0
- package/dist/engineering-pa-core/src/slice/cli.mjs +139 -0
- package/dist/engineering-pa-core/src/slice/inputs.mjs +174 -0
- package/dist/engineering-pa-core/src/slice/prompt.mjs +94 -0
- package/dist/engineering-pa-core/src/slice/prompts/dave-boot.md +91 -0
- package/dist/engineering-pa-core/src/slice/run.mjs +334 -0
- package/dist/engineering-pa-core-extensions-atlassian/README.md +31 -0
- package/dist/engineering-pa-core-extensions-atlassian/index.mjs +9 -0
- package/dist/engineering-pa-core-extensions-atlassian/package.json +19 -0
- package/dist/engineering-pa-core-extensions-n8n/README.md +29 -0
- package/dist/engineering-pa-core-extensions-n8n/index.mjs +8 -0
- package/dist/engineering-pa-core-extensions-n8n/package.json +19 -0
- package/dist/engineering-pa-librarian/README.md +38 -0
- package/dist/engineering-pa-librarian/index.mjs +12 -0
- package/dist/engineering-pa-librarian/package.json +19 -0
- package/dist/engineering-pa-migrate-inspect/README.md +131 -0
- package/dist/engineering-pa-migrate-inspect/bin/migrate-inspect +2 -0
- package/dist/engineering-pa-migrate-inspect/package.json +21 -0
- package/dist/engineering-pa-migrate-inspect/src/age-key-migrate.mjs +83 -0
- package/dist/engineering-pa-migrate-inspect/src/cli.mjs +97 -0
- package/dist/engineering-pa-migrate-inspect/src/inspector.mjs +237 -0
- package/dist/engineering-pa-migrate-inspect/src/platforms/linux.mjs +87 -0
- package/dist/engineering-pa-migrate-inspect/src/platforms/macos.mjs +105 -0
- package/dist/engineering-pa-migrate-inspect/src/runner.mjs +87 -0
- package/dist/engineering-pa-runbook/README.md +103 -0
- package/dist/engineering-pa-runbook/package.json +18 -0
- package/dist/engineering-pa-runbook/reference/dex-template.runbook.json +66 -0
- package/dist/engineering-pa-runbook/reference/hq-dave.runbook.json +69 -0
- package/dist/engineering-pa-runbook/reference/smarthome01.runbook.json +92 -0
- package/dist/engineering-pa-runbook/schema/inspection.schema.json +113 -0
- package/dist/engineering-pa-runbook/schema/runbook.schema.json +67 -0
- package/dist/engineering-pa-runbook/src/validator.mjs +167 -0
- package/dist/engineering-pa-scaffold/README.md +164 -0
- package/dist/engineering-pa-scaffold/bin/pa-init +2 -0
- package/dist/engineering-pa-scaffold/index.mjs +42 -0
- package/dist/engineering-pa-scaffold/package.json +22 -0
- package/dist/engineering-pa-scaffold/src/cli.mjs +115 -0
- package/dist/engineering-pa-scaffold/src/commands/init.mjs +169 -0
- package/dist/engineering-pa-scaffold/src/generator.mjs +354 -0
- package/dist/engineering-pa-scaffold/src/prompts.mjs +125 -0
- package/dist/engineering-pa-scaffold/src/runner.mjs +142 -0
- package/dist/engineering-pa-scaffold/templates/CLAUDE.md.template +196 -0
- package/dist/engineering-pa-scaffold/templates/operator-README.md.template +44 -0
- package/dist/engineering-pa-types-engineering/README.md +35 -0
- package/dist/engineering-pa-types-engineering/docs/commands/product-build.md +120 -0
- package/dist/engineering-pa-types-engineering/index.mjs +9 -0
- package/dist/engineering-pa-types-engineering/package.json +19 -0
- package/dist/engineering-pa-types-engineering/roles/product-engineer/definition.md +650 -0
- package/dist/engineering-pa-types-engineering/roles/product-engineer/templates/build-spec.md +82 -0
- package/dist/engineering-pa-types-engineering/roles/product-engineer/templates/preflight-checklist.md +158 -0
- package/dist/engineering-pa-types-engineering/roles/product-engineer/templates/worker-brief.md +130 -0
- package/dist/engineering-pa-workspace/README.md +197 -0
- package/dist/engineering-pa-workspace/app/assets/md.js +187 -0
- package/dist/engineering-pa-workspace/app/assets/modal.js +120 -0
- package/dist/engineering-pa-workspace/app/assets/theme.css +132 -0
- package/dist/engineering-pa-workspace/app/assets/theme.js +159 -0
- package/dist/engineering-pa-workspace/app/banner.js +320 -0
- package/dist/engineering-pa-workspace/app/delegations.html +345 -0
- package/dist/engineering-pa-workspace/app/home.html +1060 -0
- package/dist/engineering-pa-workspace/app/index.html +361 -0
- package/dist/engineering-pa-workspace/app/knowledge-map.html +163 -0
- package/dist/engineering-pa-workspace/app/knowledge.html +304 -0
- package/dist/engineering-pa-workspace/app/nav.json +75 -0
- package/dist/engineering-pa-workspace/app/notes.html +199 -0
- package/dist/engineering-pa-workspace/app/onboarding.html +592 -0
- package/dist/engineering-pa-workspace/app/profile.html +243 -0
- package/dist/engineering-pa-workspace/app/project-detail.html +432 -0
- package/dist/engineering-pa-workspace/app/projects.html +297 -0
- package/dist/engineering-pa-workspace/app/repos.html +598 -0
- package/dist/engineering-pa-workspace/app/schedules.html +486 -0
- package/dist/engineering-pa-workspace/app/secrets.html +164 -0
- package/dist/engineering-pa-workspace/app/staging.html +319 -0
- package/dist/engineering-pa-workspace/app/tasks.html +465 -0
- package/dist/engineering-pa-workspace/app/vendor/chart.umd.min.js +21 -0
- package/dist/engineering-pa-workspace/app/viewer.html +116 -0
- package/dist/engineering-pa-workspace/docs/DAEMON.md +104 -0
- package/dist/engineering-pa-workspace/docs/page-blueprint.md +286 -0
- package/dist/engineering-pa-workspace/docs/status-mon.md +168 -0
- package/dist/engineering-pa-workspace/index.mjs +12 -0
- package/dist/engineering-pa-workspace/nav.json.example +8 -0
- package/dist/engineering-pa-workspace/package.json +27 -0
- package/dist/engineering-pa-workspace/src/api.mjs +2086 -0
- package/dist/engineering-pa-workspace/src/install-daemon.mjs +573 -0
- package/dist/engineering-pa-workspace/src/nav.mjs +190 -0
- package/dist/engineering-pa-workspace/src/secrets-api.mjs +175 -0
- package/dist/engineering-pa-workspace/src/server.mjs +516 -0
- package/dist/engineering-pa-workspace/src/status-mon/poll.mjs +220 -0
- package/dist/engineering-pa-workspace/src/status-mon/poller.mjs +113 -0
- package/dist/engineering-pa-workspace/src/status-mon/run.mjs +133 -0
- package/dist/engineering-pa-workspace/src/supervisor.mjs +264 -0
- package/dist/engineering-pa-workspace/src/ws.mjs +633 -0
- package/index.mjs +60 -0
- package/package.json +58 -0
- package/templates/banned-ai-tells.md +102 -0
- package/templates/purpose.md +105 -0
- package/templates/voice-guide.md +98 -0
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
// envelope.mjs — pure functions for autonomy-envelope frontmatter parsing +
|
|
2
|
+
// rendering. Zero IO. All filesystem side effects live in cli.mjs.
|
|
3
|
+
//
|
|
4
|
+
// The envelope is a markdown file with a YAML frontmatter block carrying the
|
|
5
|
+
// machine-readable state. We ship our own minimal YAML reader/writer here
|
|
6
|
+
// (limited to the shape the schema defines) to avoid pulling in a YAML dep
|
|
7
|
+
// for such a narrow surface. Any file whose frontmatter falls outside the
|
|
8
|
+
// documented shape is rejected as invalid.
|
|
9
|
+
|
|
10
|
+
export const VALID_STATES = ['fullyApproved', 'checkBeforeCommitting', 'needsFullSupervision'];
|
|
11
|
+
|
|
12
|
+
const STATE_LABELS = {
|
|
13
|
+
fullyApproved: 'fully approved',
|
|
14
|
+
checkBeforeCommitting: 'check before committing',
|
|
15
|
+
needsFullSupervision: 'needs full supervision',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Parse an autonomy-envelope markdown file. Returns
|
|
20
|
+
* { frontmatter, body }
|
|
21
|
+
* where `frontmatter` matches the schema shape and `body` is the markdown
|
|
22
|
+
* after the closing `---`. Throws on malformed input; the error carries an
|
|
23
|
+
* `exitCode` for the CLI to propagate.
|
|
24
|
+
*/
|
|
25
|
+
export function parseEnvelope(source) {
|
|
26
|
+
if (typeof source !== 'string' || source.length === 0) {
|
|
27
|
+
throwUsage('envelope file is empty');
|
|
28
|
+
}
|
|
29
|
+
const lines = source.split(/\r?\n/);
|
|
30
|
+
if (lines[0].trim() !== '---') {
|
|
31
|
+
throwParse('envelope must start with YAML frontmatter delimited by ---');
|
|
32
|
+
}
|
|
33
|
+
let closeIdx = -1;
|
|
34
|
+
for (let i = 1; i < lines.length; i++) {
|
|
35
|
+
if (lines[i].trim() === '---') {
|
|
36
|
+
closeIdx = i;
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (closeIdx < 0) {
|
|
41
|
+
throwParse('unterminated YAML frontmatter (missing closing ---)');
|
|
42
|
+
}
|
|
43
|
+
const fmLines = lines.slice(1, closeIdx);
|
|
44
|
+
const bodyLines = lines.slice(closeIdx + 1);
|
|
45
|
+
const frontmatter = parseFrontmatter(fmLines);
|
|
46
|
+
return { frontmatter, body: bodyLines.join('\n') };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Parse a limited-shape YAML block. Supports:
|
|
51
|
+
* key: value (string)
|
|
52
|
+
* key: (empty list start)
|
|
53
|
+
* - key: value (list item — object)
|
|
54
|
+
* key: value
|
|
55
|
+
* Numbers, booleans, and nested maps beyond one level are NOT supported —
|
|
56
|
+
* the schema doesn't need them. Values are always treated as strings.
|
|
57
|
+
*/
|
|
58
|
+
function parseFrontmatter(lines) {
|
|
59
|
+
const out = {};
|
|
60
|
+
let i = 0;
|
|
61
|
+
while (i < lines.length) {
|
|
62
|
+
const raw = lines[i];
|
|
63
|
+
if (raw.trim() === '' || raw.trim().startsWith('#')) { i++; continue; }
|
|
64
|
+
const match = raw.match(/^([a-zA-Z][a-zA-Z0-9]*):(?:\s*(.*))?$/);
|
|
65
|
+
if (!match) {
|
|
66
|
+
throwParse(`could not parse frontmatter line ${i + 1}: ${raw}`);
|
|
67
|
+
}
|
|
68
|
+
const key = match[1];
|
|
69
|
+
const value = (match[2] ?? '').trim();
|
|
70
|
+
if (value === '') {
|
|
71
|
+
// Expect either a list (starts with "- " at next non-empty indented
|
|
72
|
+
// line) or an empty value.
|
|
73
|
+
const listItems = [];
|
|
74
|
+
let j = i + 1;
|
|
75
|
+
while (j < lines.length) {
|
|
76
|
+
const peek = lines[j];
|
|
77
|
+
if (peek.trim() === '' || peek.trim().startsWith('#')) { j++; continue; }
|
|
78
|
+
if (!/^\s+-\s/.test(peek)) break;
|
|
79
|
+
const item = readListItem(lines, j);
|
|
80
|
+
listItems.push(item.value);
|
|
81
|
+
j = item.next;
|
|
82
|
+
}
|
|
83
|
+
out[key] = listItems;
|
|
84
|
+
i = j;
|
|
85
|
+
} else {
|
|
86
|
+
out[key] = stripQuotes(value);
|
|
87
|
+
i++;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return out;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function readListItem(lines, start) {
|
|
94
|
+
const first = lines[start];
|
|
95
|
+
const indentMatch = first.match(/^(\s+)-\s/);
|
|
96
|
+
if (!indentMatch) {
|
|
97
|
+
throwParse(`expected list item at line ${start + 1}: ${first}`);
|
|
98
|
+
}
|
|
99
|
+
const itemIndent = indentMatch[1] + ' '; // continuation is deeper than "- "
|
|
100
|
+
const item = {};
|
|
101
|
+
// The "- key: value" first line is the first key on the item.
|
|
102
|
+
const firstBody = first.slice(indentMatch[0].length);
|
|
103
|
+
const firstMatch = firstBody.match(/^([a-zA-Z][a-zA-Z0-9]*):\s*(.*)$/);
|
|
104
|
+
if (!firstMatch) {
|
|
105
|
+
throwParse(`could not parse list item at line ${start + 1}: ${first}`);
|
|
106
|
+
}
|
|
107
|
+
item[firstMatch[1]] = stripQuotes(firstMatch[2].trim());
|
|
108
|
+
let j = start + 1;
|
|
109
|
+
while (j < lines.length) {
|
|
110
|
+
const peek = lines[j];
|
|
111
|
+
if (peek.trim() === '' || peek.trim().startsWith('#')) { j++; continue; }
|
|
112
|
+
// Terminate when indent doesn't extend the item (either new list item
|
|
113
|
+
// at the same "- " indent, or dedent back to a top-level key).
|
|
114
|
+
if (peek.startsWith(itemIndent)) {
|
|
115
|
+
const bodyLine = peek.slice(itemIndent.length);
|
|
116
|
+
const m = bodyLine.match(/^([a-zA-Z][a-zA-Z0-9]*):\s*(.*)$/);
|
|
117
|
+
if (!m) {
|
|
118
|
+
throwParse(`could not parse list item continuation at line ${j + 1}: ${peek}`);
|
|
119
|
+
}
|
|
120
|
+
item[m[1]] = stripQuotes(m[2].trim());
|
|
121
|
+
j++;
|
|
122
|
+
} else {
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return { value: item, next: j };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function stripQuotes(value) {
|
|
130
|
+
if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) {
|
|
131
|
+
return value.slice(1, -1);
|
|
132
|
+
}
|
|
133
|
+
if (value.length >= 2 && value.startsWith("'") && value.endsWith("'")) {
|
|
134
|
+
return value.slice(1, -1);
|
|
135
|
+
}
|
|
136
|
+
return value;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Render an envelope object (frontmatter shape) back to markdown. Body is
|
|
141
|
+
* regenerated: a current-state table + a history log preserved from prior
|
|
142
|
+
* body sections. This keeps the file readable + deterministic.
|
|
143
|
+
*/
|
|
144
|
+
export function renderEnvelope(frontmatter, history = []) {
|
|
145
|
+
const fm = renderFrontmatter(frontmatter);
|
|
146
|
+
const body = renderBody(frontmatter, history);
|
|
147
|
+
return `---\n${fm}---\n\n${body}\n`;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function renderFrontmatter(fm) {
|
|
151
|
+
const lines = [];
|
|
152
|
+
lines.push(`agent: ${quoteIfNeeded(fm.agent)}`);
|
|
153
|
+
lines.push(`owner: ${quoteIfNeeded(fm.owner)}`);
|
|
154
|
+
lines.push(`updated: ${quoteIfNeeded(fm.updated)}`);
|
|
155
|
+
lines.push('taskTypes:');
|
|
156
|
+
const items = fm.taskTypes ?? [];
|
|
157
|
+
for (const item of items) {
|
|
158
|
+
lines.push(` - id: ${quoteIfNeeded(item.id)}`);
|
|
159
|
+
lines.push(` label: ${quoteIfNeeded(item.label)}`);
|
|
160
|
+
lines.push(` state: ${quoteIfNeeded(item.state)}`);
|
|
161
|
+
if (item.stateSince) lines.push(` stateSince: ${quoteIfNeeded(item.stateSince)}`);
|
|
162
|
+
if (item.operatorNote) lines.push(` operatorNote: ${quoteIfNeeded(item.operatorNote)}`);
|
|
163
|
+
}
|
|
164
|
+
return lines.join('\n') + '\n';
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function quoteIfNeeded(value) {
|
|
168
|
+
if (value == null) return '""';
|
|
169
|
+
const str = String(value);
|
|
170
|
+
// Quote values that could be misparsed as YAML control sequences.
|
|
171
|
+
if (/^[\s"'#\-\[\{,&*!|>%@`]/.test(str)) return JSON.stringify(str);
|
|
172
|
+
if (/[:#]\s/.test(str)) return JSON.stringify(str);
|
|
173
|
+
if (str.trim() !== str) return JSON.stringify(str);
|
|
174
|
+
return str;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function renderBody(fm, history) {
|
|
178
|
+
const lines = [];
|
|
179
|
+
lines.push(`# Autonomy envelope — ${fm.agent}`);
|
|
180
|
+
lines.push('');
|
|
181
|
+
lines.push('## Current state');
|
|
182
|
+
lines.push('');
|
|
183
|
+
if (!fm.taskTypes || fm.taskTypes.length === 0) {
|
|
184
|
+
lines.push('_No task types configured. Use `pa autonomy set <agent> <task-type> <state>` to add one._');
|
|
185
|
+
} else {
|
|
186
|
+
lines.push('| Task type | State | Since | Note |');
|
|
187
|
+
lines.push('|---|---|---|---|');
|
|
188
|
+
for (const item of fm.taskTypes) {
|
|
189
|
+
const since = (item.stateSince ?? '').slice(0, 10);
|
|
190
|
+
const note = (item.operatorNote ?? '—').replace(/\|/g, '\\|');
|
|
191
|
+
lines.push(`| ${escapeCell(item.label)} | ${STATE_LABELS[item.state] ?? item.state} | ${since || '—'} | ${note} |`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
lines.push('');
|
|
195
|
+
lines.push('## History');
|
|
196
|
+
lines.push('');
|
|
197
|
+
const entries = [...history].sort((a, b) => (b.at ?? '').localeCompare(a.at ?? ''));
|
|
198
|
+
if (entries.length === 0) {
|
|
199
|
+
lines.push('_No history yet._');
|
|
200
|
+
} else {
|
|
201
|
+
for (const entry of entries) {
|
|
202
|
+
lines.push(`### ${entry.at} — ${entry.taskTypeId} → ${entry.state}`);
|
|
203
|
+
if (entry.note) {
|
|
204
|
+
lines.push('');
|
|
205
|
+
lines.push(`Operator note: ${entry.note}`);
|
|
206
|
+
}
|
|
207
|
+
lines.push('');
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return lines.join('\n');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function escapeCell(str) {
|
|
214
|
+
return String(str).replace(/\|/g, '\\|');
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Extract history entries from a rendered body. Returns an array of
|
|
219
|
+
* { at, taskTypeId, state, note }
|
|
220
|
+
* entries. Best-effort — malformed sections are skipped, not raised.
|
|
221
|
+
*/
|
|
222
|
+
export function extractHistory(body) {
|
|
223
|
+
const entries = [];
|
|
224
|
+
if (typeof body !== 'string') return entries;
|
|
225
|
+
const lines = body.split(/\r?\n/);
|
|
226
|
+
let i = 0;
|
|
227
|
+
while (i < lines.length) {
|
|
228
|
+
const line = lines[i];
|
|
229
|
+
const match = line.match(/^###\s+(\S+)\s+—\s+([a-z0-9][a-z0-9-]*)\s+→\s+([A-Za-z]+)\s*$/);
|
|
230
|
+
if (!match) { i++; continue; }
|
|
231
|
+
const at = match[1];
|
|
232
|
+
const taskTypeId = match[2];
|
|
233
|
+
const state = match[3];
|
|
234
|
+
let note = null;
|
|
235
|
+
let j = i + 1;
|
|
236
|
+
while (j < lines.length && lines[j].trim() === '') j++;
|
|
237
|
+
if (j < lines.length && lines[j].startsWith('Operator note:')) {
|
|
238
|
+
note = lines[j].slice('Operator note:'.length).trim();
|
|
239
|
+
}
|
|
240
|
+
entries.push({ at, taskTypeId, state, note });
|
|
241
|
+
i = j + 1;
|
|
242
|
+
}
|
|
243
|
+
return entries;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Validate a frontmatter object against the schema. Throws on invalid;
|
|
248
|
+
* returns nothing on valid.
|
|
249
|
+
*/
|
|
250
|
+
export function validateFrontmatter(fm) {
|
|
251
|
+
if (!fm || typeof fm !== 'object') throwParse('frontmatter must be an object');
|
|
252
|
+
requireString(fm, 'agent');
|
|
253
|
+
requireString(fm, 'owner');
|
|
254
|
+
requireString(fm, 'updated');
|
|
255
|
+
if (!Array.isArray(fm.taskTypes)) throwParse('taskTypes must be an array');
|
|
256
|
+
for (const [i, item] of fm.taskTypes.entries()) {
|
|
257
|
+
if (!item || typeof item !== 'object') throwParse(`taskTypes[${i}] must be an object`);
|
|
258
|
+
requireString(item, `taskTypes[${i}].id`, item.id);
|
|
259
|
+
if (!/^[a-z0-9][a-z0-9-]*$/.test(item.id)) throwParse(`taskTypes[${i}].id '${item.id}' must match ^[a-z0-9][a-z0-9-]*$`);
|
|
260
|
+
requireString(item, `taskTypes[${i}].label`, item.label);
|
|
261
|
+
requireString(item, `taskTypes[${i}].state`, item.state);
|
|
262
|
+
if (!VALID_STATES.includes(item.state)) {
|
|
263
|
+
throwParse(`taskTypes[${i}].state '${item.state}' must be one of ${VALID_STATES.join(', ')}`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function requireString(obj, field, value) {
|
|
269
|
+
const v = value ?? obj?.[field];
|
|
270
|
+
if (typeof v !== 'string' || v.length === 0) {
|
|
271
|
+
throwParse(`${field} must be a non-empty string`);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function throwParse(msg) {
|
|
276
|
+
const err = new Error(msg);
|
|
277
|
+
err.exitCode = 2;
|
|
278
|
+
throw err;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function throwUsage(msg) {
|
|
282
|
+
const err = new Error(msg);
|
|
283
|
+
err.exitCode = 1;
|
|
284
|
+
throw err;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export function nowIso() {
|
|
288
|
+
return new Date().toISOString();
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export function emptyEnvelope(agent, owner) {
|
|
292
|
+
return {
|
|
293
|
+
frontmatter: {
|
|
294
|
+
agent,
|
|
295
|
+
owner,
|
|
296
|
+
updated: nowIso(),
|
|
297
|
+
taskTypes: [],
|
|
298
|
+
},
|
|
299
|
+
history: [],
|
|
300
|
+
};
|
|
301
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// cli.mjs — pa-coordinator-hook entry point.
|
|
2
|
+
//
|
|
3
|
+
// This is a Claude Code PreToolUse hook, NOT a normal repo CLI — its exit
|
|
4
|
+
// code contract is dictated by Claude Code's hook protocol
|
|
5
|
+
// (https://code.claude.com/docs/en/hooks), not by this monorepo's usual
|
|
6
|
+
// "0 ok / 1 usage / 2 logical failure / 3 transport failure" convention
|
|
7
|
+
// (CONTRIBUTING.md). Deliberately:
|
|
8
|
+
//
|
|
9
|
+
// exit 0 — allow. Either coordinator mode is off, the tool is
|
|
10
|
+
// unconditionally allowed, or the tier2 arguments matched an
|
|
11
|
+
// allow pattern. No stdout JSON is emitted — Claude Code falls
|
|
12
|
+
// through to its normal permission flow, which is exactly what
|
|
13
|
+
// we want (the hook only ever tightens, never auto-grants
|
|
14
|
+
// something the user's own settings wouldn't already allow).
|
|
15
|
+
// exit 2 — block. stderr carries the message Claude Code shows the
|
|
16
|
+
// model. tool_input is never mutated. Also used for a hook-
|
|
17
|
+
// internal failure (config missing/corrupted, or any other
|
|
18
|
+
// unexpected exception while deciding) — a security-relevant
|
|
19
|
+
// gate fails CLOSED on its own bugs, never open.
|
|
20
|
+
// exit 1 — malformed input (unparseable stdin JSON, missing tool_name).
|
|
21
|
+
// Per the hooks doc this is a NON-BLOCKING error — the tool
|
|
22
|
+
// call proceeds normally. We'd rather fail open on a malformed
|
|
23
|
+
// payload than break every tool call estate-wide because of a
|
|
24
|
+
// hook bug; the alternative (fail closed) turns one hook bug
|
|
25
|
+
// into a total PA outage. Logged to stderr either way, so it's
|
|
26
|
+
// visible in the transcript.
|
|
27
|
+
//
|
|
28
|
+
// Input: PreToolUse JSON on stdin — { tool_name, tool_input, cwd, ... }
|
|
29
|
+
// (see packages/cli/docs/coordinator-hook.md for the full contract this
|
|
30
|
+
// was verified against).
|
|
31
|
+
//
|
|
32
|
+
// `pa-coordinator-hook --help` prints usage and exits 0 without touching
|
|
33
|
+
// stdin — matches the "every bin runs --help and exits 0" install-health
|
|
34
|
+
// convention documented in packages/engineering-pa/README.md, and gives
|
|
35
|
+
// operators a way to sanity-check the install without constructing a fake
|
|
36
|
+
// hook payload.
|
|
37
|
+
|
|
38
|
+
import { loadEffectiveConfig, overlayPaths } from './config.mjs';
|
|
39
|
+
import { decide } from './policy.mjs';
|
|
40
|
+
import { formatBlockMessage } from './message.mjs';
|
|
41
|
+
import { resolveRepoRoot } from './repo-root.mjs';
|
|
42
|
+
import { resolveSelfAgent } from './self.mjs';
|
|
43
|
+
|
|
44
|
+
const USAGE = `pa-coordinator-hook — Claude Code PreToolUse hook enforcing the three-tier
|
|
45
|
+
coordinator-mode tool policy for interactive PA main threads.
|
|
46
|
+
|
|
47
|
+
Not meant to be invoked by hand — wired up via settings.json's PreToolUse
|
|
48
|
+
hooks (see packages/cli/docs/coordinator-hook.md for the snippet). Reads a
|
|
49
|
+
PreToolUse JSON payload from stdin, exits 0 (allow) or 2 (block, message on
|
|
50
|
+
stderr).
|
|
51
|
+
|
|
52
|
+
Toggle: set PA_COORDINATOR_MODE=1 to enforce. Unset (or any other value)
|
|
53
|
+
is a transparent no-op — exits 0 for everything, including malformed input.
|
|
54
|
+
|
|
55
|
+
Usage:
|
|
56
|
+
pa-coordinator-hook Read a PreToolUse payload from stdin
|
|
57
|
+
pa-coordinator-hook --help Show this help and exit 0
|
|
58
|
+
`;
|
|
59
|
+
|
|
60
|
+
export async function main(argv = [], { stdin = process.stdin, stderr = process.stderr, env = process.env } = {}) {
|
|
61
|
+
if (argv.includes('-h') || argv.includes('--help')) {
|
|
62
|
+
stderr.write(USAGE);
|
|
63
|
+
return 0;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const mode = env.PA_COORDINATOR_MODE;
|
|
67
|
+
if (mode !== '1') {
|
|
68
|
+
// Transparent no-op — don't even read stdin, nothing to decide.
|
|
69
|
+
return 0;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (stdin.isTTY) {
|
|
73
|
+
// Invoked bare in an interactive terminal — there is no PreToolUse
|
|
74
|
+
// payload coming. Don't hang waiting for stdin to close; fail open
|
|
75
|
+
// and tell the human what happened.
|
|
76
|
+
stderr.write('pa-coordinator-hook: no stdin payload (interactive terminal) — nothing to decide, allowing. Use --help for usage.\n');
|
|
77
|
+
return 1;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let payload;
|
|
81
|
+
try {
|
|
82
|
+
const raw = await readAll(stdin);
|
|
83
|
+
payload = JSON.parse(raw);
|
|
84
|
+
} catch (err) {
|
|
85
|
+
stderr.write(`pa-coordinator-hook: malformed stdin payload (${err.message}) — failing open (allow)\n`);
|
|
86
|
+
return 1; // non-blocking per the hooks contract — tool call proceeds
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const toolName = payload?.tool_name;
|
|
90
|
+
const toolInput = payload?.tool_input ?? {};
|
|
91
|
+
const cwd = payload?.cwd || process.cwd();
|
|
92
|
+
|
|
93
|
+
if (!toolName || typeof toolName !== 'string') {
|
|
94
|
+
stderr.write('pa-coordinator-hook: payload missing tool_name — failing open (allow)\n');
|
|
95
|
+
return 1;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Everything from here on is "the gate itself" — config load, repo-root /
|
|
99
|
+
// self-agent resolution, and the actual policy decision. This is
|
|
100
|
+
// deliberately distinct from the malformed-stdin-JSON case above (which
|
|
101
|
+
// is a legitimate exit-1/fail-open case per the hooks contract): an
|
|
102
|
+
// exception in here means the gate itself is broken (missing/corrupted
|
|
103
|
+
// config, a bug in the decision logic, whatever) and a security-relevant
|
|
104
|
+
// gate must fail CLOSED on its own failures, not silently fall through to
|
|
105
|
+
// allow. Previously an uncaught exception here (e.g. loadEffectiveConfig
|
|
106
|
+
// throwing on a corrupted coordinator-allowlist.json) propagated out of
|
|
107
|
+
// main() entirely and crashed the process — which the hooks contract
|
|
108
|
+
// reads as a non-blocking/allow outcome, silently disabling the whole
|
|
109
|
+
// gate on a bad install.
|
|
110
|
+
try {
|
|
111
|
+
// PA_COORDINATOR_CORE_CONFIG_PATH is an internal test seam (also usable
|
|
112
|
+
// for a deliberate ops override) — unset in normal operation, so
|
|
113
|
+
// loadEffectiveConfig falls back to its own CORE_CONFIG_PATH default.
|
|
114
|
+
const configPath = env.PA_COORDINATOR_CORE_CONFIG_PATH || undefined;
|
|
115
|
+
const { config, warnings } = await loadEffectiveConfig({ cwd, env, configPath });
|
|
116
|
+
for (const w of warnings) stderr.write(`pa-coordinator-hook: ${w}\n`);
|
|
117
|
+
|
|
118
|
+
const repoRoot = await resolveRepoRoot(cwd, env);
|
|
119
|
+
const selfAgent = await resolveSelfAgent(repoRoot, env);
|
|
120
|
+
|
|
121
|
+
const result = decide(toolName, toolInput, config, { cwd, repoRoot, selfAgent });
|
|
122
|
+
|
|
123
|
+
if (result.decision === 'allow') return 0;
|
|
124
|
+
|
|
125
|
+
const { workspace } = overlayPaths({ repoRoot, cwd, env });
|
|
126
|
+
const message = formatBlockMessage(toolName, result, config, workspace);
|
|
127
|
+
stderr.write(message + '\n');
|
|
128
|
+
return 2;
|
|
129
|
+
} catch (err) {
|
|
130
|
+
stderr.write(`pa-coordinator-hook: internal error (${err && err.message}) — failing closed (block)\n`);
|
|
131
|
+
return 2;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function readAll(stream) {
|
|
136
|
+
if (stream.readableEnded || stream.destroyed) return '';
|
|
137
|
+
const chunks = [];
|
|
138
|
+
for await (const chunk of stream) chunks.push(chunk);
|
|
139
|
+
return Buffer.concat(chunks.map((c) => (Buffer.isBuffer(c) ? c : Buffer.from(c)))).toString('utf8');
|
|
140
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// config.mjs — load + merge the coordinator-hook allowlist config.
|
|
2
|
+
//
|
|
3
|
+
// Three layers, in priority order (lowest to highest — later layers only
|
|
4
|
+
// ADD allow-pattern entries, they never remove or reclassify anything):
|
|
5
|
+
// 1. Core config — packages/cli/config/coordinator-allowlist.json,
|
|
6
|
+
// resolved relative to this module via import.meta.url so it works
|
|
7
|
+
// identically from source (packages/cli/src/coordinator-hook/…) and
|
|
8
|
+
// from the bundled dist tree (dist/engineering-pa-cli/src/coordinator-hook/…) —
|
|
9
|
+
// build.mjs preserves the package's internal relative layout.
|
|
10
|
+
// 2. Home overlay — ~/.claude/coordinator-overlay.json
|
|
11
|
+
// 3. Workspace overlay — <repoRoot or cwd>/.claude/coordinator-overlay.json
|
|
12
|
+
//
|
|
13
|
+
// Overlay contract (spec-locked, do not relax): an overlay may ONLY extend
|
|
14
|
+
// `tier2.<Bash|Read|Write>.allowPatterns` / `allowPathPatterns`. It cannot
|
|
15
|
+
// move a tool between tiers, cannot touch tier1/tier3, and cannot touch
|
|
16
|
+
// hardDeny. Anything else in an overlay file is ignored with a stderr
|
|
17
|
+
// warning — silently accepting unknown shape would be how a stray overlay
|
|
18
|
+
// key quietly widens the policy.
|
|
19
|
+
|
|
20
|
+
import { readFile } from 'node:fs/promises';
|
|
21
|
+
import { dirname, join, resolve } from 'node:path';
|
|
22
|
+
import { fileURLToPath } from 'node:url';
|
|
23
|
+
import { homedir } from 'node:os';
|
|
24
|
+
|
|
25
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
26
|
+
const CORE_CONFIG_PATH = resolve(__dirname, '..', '..', 'config', 'coordinator-allowlist.json');
|
|
27
|
+
|
|
28
|
+
const OVERLAYABLE_TOOLS = ['Bash', 'Read', 'Write'];
|
|
29
|
+
const OVERLAYABLE_KEYS = ['allowPatterns', 'allowPathPatterns'];
|
|
30
|
+
|
|
31
|
+
export async function loadCoreConfig(configPath = CORE_CONFIG_PATH) {
|
|
32
|
+
const raw = await readFile(configPath, 'utf8');
|
|
33
|
+
return JSON.parse(raw);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Discriminated result so the caller never has to guess whether a `null`
|
|
37
|
+
// came from "file absent" vs. "file contained the JSON literal null".
|
|
38
|
+
async function tryReadJson(path) {
|
|
39
|
+
let raw;
|
|
40
|
+
try {
|
|
41
|
+
raw = await readFile(path, 'utf8');
|
|
42
|
+
} catch (err) {
|
|
43
|
+
if (err && err.code === 'ENOENT') return { ok: false, missing: true };
|
|
44
|
+
return { ok: false, error: err };
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
return { ok: true, data: JSON.parse(raw) };
|
|
48
|
+
} catch (err) {
|
|
49
|
+
return { ok: false, error: err };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Merge one overlay object's allowlisted keys onto `config` (returns a new
|
|
55
|
+
* object — never mutates the input). Unknown/disallowed keys are dropped
|
|
56
|
+
* and reported via `warnings`.
|
|
57
|
+
*/
|
|
58
|
+
export function mergeOverlay(config, overlay, warnings = []) {
|
|
59
|
+
if (!overlay || typeof overlay !== 'object') return config;
|
|
60
|
+
|
|
61
|
+
const next = {
|
|
62
|
+
...config,
|
|
63
|
+
tier2: { ...config.tier2 },
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
for (const [key, value] of Object.entries(overlay)) {
|
|
67
|
+
if (key !== 'tier2') {
|
|
68
|
+
warnings.push(`coordinator-overlay: ignoring top-level key "${key}" (overlays may only extend tier2 allow patterns)`);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (!value || typeof value !== 'object') continue;
|
|
72
|
+
for (const [tool, toolPatch] of Object.entries(value)) {
|
|
73
|
+
if (!OVERLAYABLE_TOOLS.includes(tool)) {
|
|
74
|
+
warnings.push(`coordinator-overlay: ignoring tier2.${tool} — overlays may only extend Bash/Read/Write`);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (!toolPatch || typeof toolPatch !== 'object') continue;
|
|
78
|
+
const mergedTool = { ...(next.tier2[tool] ?? {}) };
|
|
79
|
+
for (const [patchKey, patternList] of Object.entries(toolPatch)) {
|
|
80
|
+
if (!OVERLAYABLE_KEYS.includes(patchKey)) {
|
|
81
|
+
warnings.push(`coordinator-overlay: ignoring tier2.${tool}.${patchKey} — only allowPatterns/allowPathPatterns may be extended`);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (!Array.isArray(patternList)) continue;
|
|
85
|
+
const existing = Array.isArray(mergedTool[patchKey]) ? mergedTool[patchKey] : [];
|
|
86
|
+
mergedTool[patchKey] = [...new Set([...existing, ...patternList.filter((p) => typeof p === 'string')])];
|
|
87
|
+
}
|
|
88
|
+
next.tier2[tool] = mergedTool;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return next;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Resolve the two overlay file candidates (home, workspace). Workspace
|
|
97
|
+
* overlay is anchored at `repoRoot` when known, else `cwd` — either way it
|
|
98
|
+
* lives at `.claude/coordinator-overlay.json` under that directory.
|
|
99
|
+
*/
|
|
100
|
+
export function overlayPaths({ repoRoot, cwd, env = process.env }) {
|
|
101
|
+
const home = env.HOME || homedir();
|
|
102
|
+
const workspaceBase = repoRoot || cwd;
|
|
103
|
+
return {
|
|
104
|
+
home: home ? join(home, '.claude', 'coordinator-overlay.json') : null,
|
|
105
|
+
workspace: workspaceBase ? join(workspaceBase, '.claude', 'coordinator-overlay.json') : null,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Load the fully-merged effective config: core + home overlay + workspace
|
|
111
|
+
* overlay (workspace applied last, so it can extend past whatever the home
|
|
112
|
+
* overlay already added). Returns `{ config, warnings }` — warnings is a
|
|
113
|
+
* flat array of human-readable strings for anything ignored along the way.
|
|
114
|
+
*/
|
|
115
|
+
export async function loadEffectiveConfig({ repoRoot, cwd, env = process.env, configPath } = {}) {
|
|
116
|
+
const warnings = [];
|
|
117
|
+
let config = await loadCoreConfig(configPath);
|
|
118
|
+
|
|
119
|
+
const { home, workspace } = overlayPaths({ repoRoot, cwd, env });
|
|
120
|
+
for (const overlayPath of [home, workspace]) {
|
|
121
|
+
if (!overlayPath) continue;
|
|
122
|
+
const result = await tryReadJson(overlayPath);
|
|
123
|
+
if (!result.ok) {
|
|
124
|
+
if (result.missing) continue; // no overlay at this path — normal case
|
|
125
|
+
warnings.push(`coordinator-overlay: failed to parse ${overlayPath} (${result.error.message}) — ignoring`);
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
config = mergeOverlay(config, result.data, warnings);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return { config, warnings };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export { CORE_CONFIG_PATH };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// message.mjs — formats the exit-2 stderr message a blocked tool call sees.
|
|
2
|
+
//
|
|
3
|
+
// Format is spec-locked (agentic-harness product-vision.md §1 item 6):
|
|
4
|
+
//
|
|
5
|
+
// coordinator-mode: <ToolName> blocked (<tier> - <reason>).
|
|
6
|
+
// Try: <specific alternative>
|
|
7
|
+
// - Dispatch a worker: `Agent(...)` with the appropriate role.
|
|
8
|
+
// - Retrieve knowledge: `Bash(pa knowledge search "...")`.
|
|
9
|
+
// - Named exception needed? Add to allowlist: <config path>.
|
|
10
|
+
//
|
|
11
|
+
// The "Try:" line is tool-specific (config.messages[toolName], falling back
|
|
12
|
+
// to a generic line) — concrete alternatives matter more than a generic
|
|
13
|
+
// "dispatch instead" for the model reading the block.
|
|
14
|
+
|
|
15
|
+
export function formatBlockMessage(toolName, decisionResult, config, overlayPath) {
|
|
16
|
+
const { tier, reason, matched } = decisionResult;
|
|
17
|
+
|
|
18
|
+
const tryLine = pickTryLine(toolName, decisionResult, config);
|
|
19
|
+
const configPath = overlayPath || '~/.claude/coordinator-overlay.json (or <workspace>/.claude/coordinator-overlay.json)';
|
|
20
|
+
|
|
21
|
+
const lines = [
|
|
22
|
+
`coordinator-mode: ${toolName} blocked (${tier} - ${reason}).`,
|
|
23
|
+
`Try: ${tryLine}`,
|
|
24
|
+
'- Dispatch a worker: `Agent(...)` with the appropriate role.',
|
|
25
|
+
'- Retrieve knowledge: `Bash(pa knowledge search "...")`.',
|
|
26
|
+
`- Named exception needed? Add to allowlist: ${configPath} (tier2 allowPatterns/allowPathPatterns only — cannot move a tool between tiers).`,
|
|
27
|
+
];
|
|
28
|
+
return lines.join('\n');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function pickTryLine(toolName, decisionResult, config) {
|
|
32
|
+
const messages = config.messages ?? {};
|
|
33
|
+
|
|
34
|
+
// Read blocked specifically by the operator/knowledge blockPathPatterns
|
|
35
|
+
// entry gets its own, more specific message than a generic Read-denied one.
|
|
36
|
+
if (toolName === 'Read' && decisionResult.tier === 'tier2-explicit-block' && messages['operator/knowledge/**']) {
|
|
37
|
+
return messages['operator/knowledge/**'];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (decisionResult.tier === 'tier2-hard-deny' && messages.hardDeny) {
|
|
41
|
+
return messages.hardDeny;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (messages[toolName]) return messages[toolName];
|
|
45
|
+
|
|
46
|
+
return `dispatch a worker instead of calling ${toolName} inline — main thread is coordinator-mode.`;
|
|
47
|
+
}
|