@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,352 @@
|
|
|
1
|
+
// projects.mjs — write-side commands for `pa project <verb>`.
|
|
2
|
+
//
|
|
3
|
+
// Source of truth is projects/<id>/project.json. We never cache; every
|
|
4
|
+
// command re-reads. This matches the desk CLI's read-side discipline.
|
|
5
|
+
//
|
|
6
|
+
// What lives here:
|
|
7
|
+
// - create: scaffold a new project (dir + project.json + README.md)
|
|
8
|
+
// - status: change project status (stamps `shipped` on done)
|
|
9
|
+
// - docAdd: append a doc entry (migrates flat → grouped on first write)
|
|
10
|
+
// - docRm: remove a doc entry by path
|
|
11
|
+
// - list: list projects, optionally filtered
|
|
12
|
+
//
|
|
13
|
+
// Status enum and category list match packages/core/schema/project.json.
|
|
14
|
+
|
|
15
|
+
import { existsSync } from 'node:fs';
|
|
16
|
+
import { writeFile } from 'node:fs/promises';
|
|
17
|
+
import { join } from 'node:path';
|
|
18
|
+
|
|
19
|
+
import { getRepoRoot } from './repo-root.mjs';
|
|
20
|
+
import { readJson, writeJson, ensureDir, listSubdirs, today } from './io.mjs';
|
|
21
|
+
import { addDoc, removeDocByPath, setDocStatus, DEFAULT_GROUP, VALID_DOC_STATUSES } from './doc-groups.mjs';
|
|
22
|
+
import { filterProjectsByTeam, readTeamScopeConfig } from '../../../engineering-pa-core/src/desk/team-scope.mjs';
|
|
23
|
+
|
|
24
|
+
const STATUSES = new Set(['draft', 'planned', 'in-progress', 'blocked', 'done', 'archived']);
|
|
25
|
+
|
|
26
|
+
const KNOWN_CATEGORIES = new Set([
|
|
27
|
+
'feature', 'bug', 'chore', 'product', 'growth', 'discussion', 'research',
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
function validateStatus(status) {
|
|
31
|
+
if (!STATUSES.has(status)) {
|
|
32
|
+
const err = new Error(
|
|
33
|
+
`invalid status: ${status} (expected one of: ${[...STATUSES].join(', ')})`,
|
|
34
|
+
);
|
|
35
|
+
err.exitCode = 1;
|
|
36
|
+
throw err;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function validateCategory(category) {
|
|
41
|
+
if (!category) return;
|
|
42
|
+
if (!KNOWN_CATEGORIES.has(category)) {
|
|
43
|
+
const err = new Error(
|
|
44
|
+
`invalid category: ${category} (expected one of: ${[...KNOWN_CATEGORIES].join(', ')})`,
|
|
45
|
+
);
|
|
46
|
+
err.exitCode = 1;
|
|
47
|
+
throw err;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function validatePriority(priority) {
|
|
52
|
+
if (priority == null) return;
|
|
53
|
+
if (!Number.isInteger(priority) || priority < 0 || priority > 3) {
|
|
54
|
+
const err = new Error(`invalid priority: ${priority} (expected integer 0–3)`);
|
|
55
|
+
err.exitCode = 1;
|
|
56
|
+
throw err;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function validateId(id) {
|
|
61
|
+
if (!id || typeof id !== 'string') {
|
|
62
|
+
const err = new Error('project id is required');
|
|
63
|
+
err.exitCode = 1;
|
|
64
|
+
throw err;
|
|
65
|
+
}
|
|
66
|
+
if (!/^[a-z0-9][a-z0-9.-]*$/i.test(id)) {
|
|
67
|
+
const err = new Error(
|
|
68
|
+
`invalid project id: ${id} (lowercase letters, digits, '-' and '.' only; must start with a letter or digit)`,
|
|
69
|
+
);
|
|
70
|
+
err.exitCode = 1;
|
|
71
|
+
throw err;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Scan every projects/*\/project.json for the highest `number`. Returns
|
|
77
|
+
* 0 if no projects exist; the next free number is +1.
|
|
78
|
+
*/
|
|
79
|
+
async function nextProjectNumber(repoRoot) {
|
|
80
|
+
const dir = join(repoRoot, 'projects');
|
|
81
|
+
const subdirs = await listSubdirs(dir);
|
|
82
|
+
let max = 0;
|
|
83
|
+
for (const name of subdirs) {
|
|
84
|
+
const manifest = join(dir, name, 'project.json');
|
|
85
|
+
if (!existsSync(manifest)) continue;
|
|
86
|
+
try {
|
|
87
|
+
const p = await readJson(manifest);
|
|
88
|
+
if (typeof p.number === 'number' && p.number > max) max = p.number;
|
|
89
|
+
} catch {
|
|
90
|
+
// Skip unparseable manifests — they are not our concern here.
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return max + 1;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Create a new project. Writes:
|
|
98
|
+
* projects/<id>/project.json
|
|
99
|
+
* projects/<id>/README.md (only if it doesn't already exist)
|
|
100
|
+
*
|
|
101
|
+
* Returns { number, projectJsonPath, readmePath, created: { json, readme } }.
|
|
102
|
+
*/
|
|
103
|
+
export async function createProject(opts) {
|
|
104
|
+
validateId(opts.id);
|
|
105
|
+
if (!opts.name) {
|
|
106
|
+
const err = new Error('--name is required');
|
|
107
|
+
err.exitCode = 1;
|
|
108
|
+
throw err;
|
|
109
|
+
}
|
|
110
|
+
validateCategory(opts.category);
|
|
111
|
+
validatePriority(opts.priority);
|
|
112
|
+
|
|
113
|
+
const repoRoot = await getRepoRoot();
|
|
114
|
+
const projDir = join(repoRoot, 'projects', opts.id);
|
|
115
|
+
const projectJsonPath = join(projDir, 'project.json');
|
|
116
|
+
if (existsSync(projectJsonPath)) {
|
|
117
|
+
const err = new Error(`project already exists: ${opts.id} (${projectJsonPath})`);
|
|
118
|
+
err.exitCode = 1;
|
|
119
|
+
throw err;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
await ensureDir(projDir);
|
|
123
|
+
const number = await nextProjectNumber(repoRoot);
|
|
124
|
+
const todayStr = today();
|
|
125
|
+
|
|
126
|
+
const manifest = {
|
|
127
|
+
id: opts.id,
|
|
128
|
+
number,
|
|
129
|
+
name: opts.name,
|
|
130
|
+
category: opts.category || 'chore',
|
|
131
|
+
status: 'draft',
|
|
132
|
+
priority: opts.priority == null ? 2 : opts.priority,
|
|
133
|
+
owner: opts.owner || 'system-architect',
|
|
134
|
+
assignee: opts.assignee || opts.owner || 'system-architect',
|
|
135
|
+
created: todayStr,
|
|
136
|
+
shipped: null,
|
|
137
|
+
updated: todayStr,
|
|
138
|
+
path: `projects/${opts.id}`,
|
|
139
|
+
summary: opts.summary || '',
|
|
140
|
+
docs: {
|
|
141
|
+
[DEFAULT_GROUP]: [
|
|
142
|
+
{ label: 'README', path: `projects/${opts.id}/README.md` },
|
|
143
|
+
],
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
await writeJson(projectJsonPath, manifest);
|
|
147
|
+
|
|
148
|
+
const readmePath = join(projDir, 'README.md');
|
|
149
|
+
let readmeCreated = false;
|
|
150
|
+
if (!existsSync(readmePath)) {
|
|
151
|
+
const readme = `# ${opts.name}\n\n${opts.summary || '_TODO: project summary._'}\n`;
|
|
152
|
+
await writeFile(readmePath, readme, 'utf8');
|
|
153
|
+
readmeCreated = true;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
id: opts.id,
|
|
158
|
+
number,
|
|
159
|
+
projectJsonPath,
|
|
160
|
+
readmePath,
|
|
161
|
+
created: { json: true, readme: readmeCreated },
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Set project status. Stamps `shipped` to today when status=done (only
|
|
167
|
+
* if not already set). Always bumps `updated`.
|
|
168
|
+
*/
|
|
169
|
+
export async function setProjectStatus(id, status) {
|
|
170
|
+
validateId(id);
|
|
171
|
+
validateStatus(status);
|
|
172
|
+
|
|
173
|
+
const repoRoot = await getRepoRoot();
|
|
174
|
+
const path = join(repoRoot, 'projects', id, 'project.json');
|
|
175
|
+
if (!existsSync(path)) {
|
|
176
|
+
const err = new Error(`project not found: ${id}`);
|
|
177
|
+
err.exitCode = 1;
|
|
178
|
+
throw err;
|
|
179
|
+
}
|
|
180
|
+
const manifest = await readJson(path);
|
|
181
|
+
manifest.status = status;
|
|
182
|
+
manifest.updated = today();
|
|
183
|
+
if (status === 'done' && !manifest.shipped) {
|
|
184
|
+
manifest.shipped = today();
|
|
185
|
+
}
|
|
186
|
+
await writeJson(path, manifest);
|
|
187
|
+
return { id, status, shipped: manifest.shipped, path };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Append a doc entry to a group. Migrates a legacy flat-array shape to
|
|
192
|
+
* grouped in the same write. Idempotent on {label,path} match.
|
|
193
|
+
*/
|
|
194
|
+
export async function addProjectDoc(id, { label, path: docPath, group, status }) {
|
|
195
|
+
validateId(id);
|
|
196
|
+
if (!label) {
|
|
197
|
+
const err = new Error('--label is required');
|
|
198
|
+
err.exitCode = 1;
|
|
199
|
+
throw err;
|
|
200
|
+
}
|
|
201
|
+
if (!docPath) {
|
|
202
|
+
const err = new Error('--path is required');
|
|
203
|
+
err.exitCode = 1;
|
|
204
|
+
throw err;
|
|
205
|
+
}
|
|
206
|
+
if (status && !VALID_DOC_STATUSES.has(status)) {
|
|
207
|
+
const err = new Error(
|
|
208
|
+
`invalid doc status: ${status} (expected one of: ${[...VALID_DOC_STATUSES].join(', ')})`,
|
|
209
|
+
);
|
|
210
|
+
err.exitCode = 1;
|
|
211
|
+
throw err;
|
|
212
|
+
}
|
|
213
|
+
const repoRoot = await getRepoRoot();
|
|
214
|
+
const manifestPath = join(repoRoot, 'projects', id, 'project.json');
|
|
215
|
+
if (!existsSync(manifestPath)) {
|
|
216
|
+
const err = new Error(`project not found: ${id}`);
|
|
217
|
+
err.exitCode = 1;
|
|
218
|
+
throw err;
|
|
219
|
+
}
|
|
220
|
+
const manifest = await readJson(manifestPath);
|
|
221
|
+
const before = JSON.stringify(manifest.docs);
|
|
222
|
+
const entry = { label, path: docPath };
|
|
223
|
+
if (status) entry.status = status;
|
|
224
|
+
manifest.docs = addDoc(manifest.docs, group, entry);
|
|
225
|
+
const after = JSON.stringify(manifest.docs);
|
|
226
|
+
if (before !== after) {
|
|
227
|
+
manifest.updated = today();
|
|
228
|
+
}
|
|
229
|
+
await writeJson(manifestPath, manifest);
|
|
230
|
+
return {
|
|
231
|
+
id,
|
|
232
|
+
group: group || DEFAULT_GROUP,
|
|
233
|
+
label,
|
|
234
|
+
path: docPath,
|
|
235
|
+
status: status || null,
|
|
236
|
+
changed: before !== after,
|
|
237
|
+
manifestPath,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Remove a doc entry by path (matches across all groups). Reports the
|
|
243
|
+
* number of entries removed; returns changed=false when nothing matched.
|
|
244
|
+
*/
|
|
245
|
+
export async function removeProjectDoc(id, { path: docPath }) {
|
|
246
|
+
validateId(id);
|
|
247
|
+
if (!docPath) {
|
|
248
|
+
const err = new Error('--path is required');
|
|
249
|
+
err.exitCode = 1;
|
|
250
|
+
throw err;
|
|
251
|
+
}
|
|
252
|
+
const repoRoot = await getRepoRoot();
|
|
253
|
+
const manifestPath = join(repoRoot, 'projects', id, 'project.json');
|
|
254
|
+
if (!existsSync(manifestPath)) {
|
|
255
|
+
const err = new Error(`project not found: ${id}`);
|
|
256
|
+
err.exitCode = 1;
|
|
257
|
+
throw err;
|
|
258
|
+
}
|
|
259
|
+
const manifest = await readJson(manifestPath);
|
|
260
|
+
const { docs: nextDocs, removed } = removeDocByPath(manifest.docs, docPath);
|
|
261
|
+
if (removed > 0) {
|
|
262
|
+
manifest.docs = nextDocs;
|
|
263
|
+
manifest.updated = today();
|
|
264
|
+
await writeJson(manifestPath, manifest);
|
|
265
|
+
}
|
|
266
|
+
return { id, path: docPath, removed, manifestPath };
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Set or remove the status field on a doc entry identified by path.
|
|
271
|
+
* Searches across all groups.
|
|
272
|
+
*/
|
|
273
|
+
export async function setProjectDocStatus(id, { path: docPath, status }) {
|
|
274
|
+
validateId(id);
|
|
275
|
+
if (!docPath) {
|
|
276
|
+
const err = new Error('--path is required');
|
|
277
|
+
err.exitCode = 1;
|
|
278
|
+
throw err;
|
|
279
|
+
}
|
|
280
|
+
if (status && !VALID_DOC_STATUSES.has(status)) {
|
|
281
|
+
const err = new Error(
|
|
282
|
+
`invalid doc status: ${status} (expected one of: ${[...VALID_DOC_STATUSES].join(', ')})`,
|
|
283
|
+
);
|
|
284
|
+
err.exitCode = 1;
|
|
285
|
+
throw err;
|
|
286
|
+
}
|
|
287
|
+
const repoRoot = await getRepoRoot();
|
|
288
|
+
const manifestPath = join(repoRoot, 'projects', id, 'project.json');
|
|
289
|
+
if (!existsSync(manifestPath)) {
|
|
290
|
+
const err = new Error(`project not found: ${id}`);
|
|
291
|
+
err.exitCode = 1;
|
|
292
|
+
throw err;
|
|
293
|
+
}
|
|
294
|
+
const manifest = await readJson(manifestPath);
|
|
295
|
+
const { docs: nextDocs, matched } = setDocStatus(manifest.docs, docPath, status || null);
|
|
296
|
+
if (matched > 0) {
|
|
297
|
+
manifest.docs = nextDocs;
|
|
298
|
+
manifest.updated = today();
|
|
299
|
+
await writeJson(manifestPath, manifest);
|
|
300
|
+
}
|
|
301
|
+
return { id, path: docPath, status: status || null, matched, manifestPath };
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* List projects. Filters: status, category, owner, assignee, all,
|
|
306
|
+
* allTeams, team. By default excludes done + archived (mirrors desk-list
|
|
307
|
+
* semantics). Team scoping mirrors desk exactly — see
|
|
308
|
+
* `@stravica-ai/engineering-pa-core/src/desk/team-scope.mjs` for the rules
|
|
309
|
+
* (executor/assignee scoping key, fail-open on unclassifiable projects,
|
|
310
|
+
* disabled unless operator/config/preferences.json opts in).
|
|
311
|
+
*/
|
|
312
|
+
export async function listProjects(filters = {}) {
|
|
313
|
+
const repoRoot = await getRepoRoot();
|
|
314
|
+
const dir = join(repoRoot, 'projects');
|
|
315
|
+
const subdirs = await listSubdirs(dir);
|
|
316
|
+
const projects = [];
|
|
317
|
+
for (const name of subdirs) {
|
|
318
|
+
const manifest = join(dir, name, 'project.json');
|
|
319
|
+
if (!existsSync(manifest)) continue;
|
|
320
|
+
try {
|
|
321
|
+
projects.push(await readJson(manifest));
|
|
322
|
+
} catch {
|
|
323
|
+
// Skip unparseable — surfacing here would mask the user's intent.
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
const teamScopeConfig = await readTeamScopeConfig(repoRoot);
|
|
327
|
+
let filtered = filterProjectsByTeam(projects, {
|
|
328
|
+
selfIdentities: teamScopeConfig.teamIdentities,
|
|
329
|
+
scopeEnabled: teamScopeConfig.scopeEnabled,
|
|
330
|
+
allTeams: filters.allTeams === true,
|
|
331
|
+
team: filters.team || null,
|
|
332
|
+
});
|
|
333
|
+
if (!filters.all) {
|
|
334
|
+
filtered = filtered.filter((p) => !['done', 'archived'].includes(p.status));
|
|
335
|
+
}
|
|
336
|
+
if (filters.status) filtered = filtered.filter((p) => p.status === filters.status);
|
|
337
|
+
if (filters.category) filtered = filtered.filter((p) => p.category === filters.category);
|
|
338
|
+
if (filters.owner) filtered = filtered.filter((p) => p.owner === filters.owner);
|
|
339
|
+
if (filters.assignee) filtered = filtered.filter((p) => p.assignee === filters.assignee);
|
|
340
|
+
// Stable display order: status priority, then numeric priority, then number.
|
|
341
|
+
const STATE_ORDER = ['in-progress', 'blocked', 'planned', 'draft', 'done', 'archived'];
|
|
342
|
+
filtered.sort((a, b) => {
|
|
343
|
+
const sa = STATE_ORDER.indexOf(a.status || 'draft');
|
|
344
|
+
const sb = STATE_ORDER.indexOf(b.status || 'draft');
|
|
345
|
+
if (sa !== sb) return sa - sb;
|
|
346
|
+
const pa = a.priority == null ? 99 : a.priority;
|
|
347
|
+
const pb = b.priority == null ? 99 : b.priority;
|
|
348
|
+
if (pa !== pb) return pa - pb;
|
|
349
|
+
return (a.number || 999) - (b.number || 999);
|
|
350
|
+
});
|
|
351
|
+
return filtered;
|
|
352
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// repo-root.mjs — operator repo root discovery for the `pa project` /
|
|
2
|
+
// `pa work` write-side CLIs.
|
|
3
|
+
//
|
|
4
|
+
// Mirrors packages/cli/src/launch/repo-root.mjs. Own copy avoids
|
|
5
|
+
// cross-module coupling for a pure utility; the launch CLI's cache is
|
|
6
|
+
// independent.
|
|
7
|
+
//
|
|
8
|
+
// Resolution order:
|
|
9
|
+
// 1. DAVE_REPO_ROOT / PA_REPO_ROOT env var (explicit override)
|
|
10
|
+
// 2. Walk upward from process.cwd() looking for a `.git` entry
|
|
11
|
+
//
|
|
12
|
+
// Either a `.git` directory (primary checkout) or `.git` file (worktree)
|
|
13
|
+
// counts as the marker.
|
|
14
|
+
|
|
15
|
+
import { stat } from 'node:fs/promises';
|
|
16
|
+
import { join, resolve, dirname } from 'node:path';
|
|
17
|
+
|
|
18
|
+
async function pathKind(p) {
|
|
19
|
+
try {
|
|
20
|
+
const s = await stat(p);
|
|
21
|
+
if (s.isDirectory()) return 'dir';
|
|
22
|
+
if (s.isFile()) return 'file';
|
|
23
|
+
return 'other';
|
|
24
|
+
} catch {
|
|
25
|
+
return 'missing';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function findRepoRoot(start) {
|
|
30
|
+
let d = start;
|
|
31
|
+
while (d && d !== dirname(d)) {
|
|
32
|
+
const k = await pathKind(join(d, '.git'));
|
|
33
|
+
if (k === 'dir' || k === 'file') return d;
|
|
34
|
+
d = dirname(d);
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let _cache = null;
|
|
40
|
+
|
|
41
|
+
export async function getRepoRoot() {
|
|
42
|
+
if (_cache) return _cache;
|
|
43
|
+
const envRoot = process.env.DAVE_REPO_ROOT || process.env.PA_REPO_ROOT;
|
|
44
|
+
if (envRoot) {
|
|
45
|
+
_cache = resolve(envRoot);
|
|
46
|
+
return _cache;
|
|
47
|
+
}
|
|
48
|
+
const found = await findRepoRoot(process.cwd());
|
|
49
|
+
if (!found) {
|
|
50
|
+
const err = new Error(
|
|
51
|
+
'could not find operator repo root from ' + process.cwd() +
|
|
52
|
+
' (no .git ancestor; set DAVE_REPO_ROOT or PA_REPO_ROOT to override)',
|
|
53
|
+
);
|
|
54
|
+
err.exitCode = 2;
|
|
55
|
+
throw err;
|
|
56
|
+
}
|
|
57
|
+
_cache = found;
|
|
58
|
+
return _cache;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Test seam: reset the cached root between test cases.
|
|
62
|
+
export function _resetRepoRoot() {
|
|
63
|
+
_cache = null;
|
|
64
|
+
}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
// session.mjs — `pa session` verb.
|
|
2
|
+
//
|
|
3
|
+
// Resolve the operator repo root, read `operator/config/preferences.json`,
|
|
4
|
+
// chdir to `agents/<paName>/`, and exec `claude --dangerously-skip-permissions`
|
|
5
|
+
// so the operator boots straight into a PA session.
|
|
6
|
+
//
|
|
7
|
+
// Replaces v1's `dave.sh` wrapper, which was dropped during the v2 cutover.
|
|
8
|
+
// Fresh operators (Dex's upcoming cutover; future operators) saw
|
|
9
|
+
// `pa-init: done.` with no guidance on how to actually launch their PA —
|
|
10
|
+
// `pa session` closes that gap.
|
|
11
|
+
//
|
|
12
|
+
// Errors are surfaced with a clear stderr message; exit code 1 for
|
|
13
|
+
// user/config errors, exit code 2 for internal/unexpected failures, per
|
|
14
|
+
// the tooling standard §6.
|
|
15
|
+
//
|
|
16
|
+
// All filesystem effects + process replacement happen behind a `seam`
|
|
17
|
+
// argument so tests can drive the logic without touching disk or actually
|
|
18
|
+
// spawning `claude`.
|
|
19
|
+
|
|
20
|
+
import { readFile, stat } from 'node:fs/promises';
|
|
21
|
+
import { spawn } from 'node:child_process';
|
|
22
|
+
import { dirname, join, resolve } from 'node:path';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Resolve the operator repo root with the priority documented in the brief:
|
|
26
|
+
* 1. process.env.DAVE_REPO_ROOT (if set and the directory exists)
|
|
27
|
+
* 2. process.env.PA_REPO_ROOT (if set and the directory exists)
|
|
28
|
+
* 3. Walk upward from `startDir` looking for a `.git/` directory
|
|
29
|
+
* 4. Fallback: `startDir` itself, with a warning to stderr
|
|
30
|
+
*
|
|
31
|
+
* Returns `{ root, source }` where `source` is one of:
|
|
32
|
+
* 'DAVE_REPO_ROOT' | 'PA_REPO_ROOT' | '.git-walk' | 'cwd-fallback'.
|
|
33
|
+
*
|
|
34
|
+
* The seam exists so tests can inject a fake env + fake startDir without
|
|
35
|
+
* mutating process state.
|
|
36
|
+
*/
|
|
37
|
+
export async function resolveRepoRoot(seam = {}) {
|
|
38
|
+
const env = seam.env ?? process.env;
|
|
39
|
+
const startDir = seam.startDir ?? process.cwd();
|
|
40
|
+
const pathExists = seam.pathExists ?? defaultPathExists;
|
|
41
|
+
const stderr = seam.stderr ?? process.stderr;
|
|
42
|
+
|
|
43
|
+
for (const varName of ['DAVE_REPO_ROOT', 'PA_REPO_ROOT']) {
|
|
44
|
+
const v = env[varName];
|
|
45
|
+
if (v) {
|
|
46
|
+
const abs = resolve(v);
|
|
47
|
+
if (await pathExists(abs, 'dir')) {
|
|
48
|
+
return { root: abs, source: varName };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let d = resolve(startDir);
|
|
54
|
+
while (d && d !== dirname(d)) {
|
|
55
|
+
if (await pathExists(join(d, '.git'), 'dir-or-file')) {
|
|
56
|
+
return { root: d, source: '.git-walk' };
|
|
57
|
+
}
|
|
58
|
+
d = dirname(d);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const fallback = resolve(startDir);
|
|
62
|
+
stderr.write(
|
|
63
|
+
`[pa session] warning: no .git ancestor found from ${startDir}; falling back to current directory\n`,
|
|
64
|
+
);
|
|
65
|
+
return { root: fallback, source: 'cwd-fallback' };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function defaultPathExists(p, mode) {
|
|
69
|
+
try {
|
|
70
|
+
const s = await stat(p);
|
|
71
|
+
if (mode === 'dir') return s.isDirectory();
|
|
72
|
+
if (mode === 'dir-or-file') return s.isDirectory() || s.isFile();
|
|
73
|
+
return true;
|
|
74
|
+
} catch {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The thin error class. `exitCode` carries the suggested process exit
|
|
81
|
+
* code. `userMessage` is the operator-facing string written to stderr.
|
|
82
|
+
*/
|
|
83
|
+
export class SessionError extends Error {
|
|
84
|
+
constructor(userMessage, exitCode) {
|
|
85
|
+
super(userMessage);
|
|
86
|
+
this.userMessage = userMessage;
|
|
87
|
+
this.exitCode = exitCode;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Read the preferences.json at the given repo root, validate `paName`,
|
|
93
|
+
* and confirm `agents/<paName>/` exists. Returns a plan object describing
|
|
94
|
+
* what the verb is about to do — `runSession` then either execs `claude`
|
|
95
|
+
* or hands the plan to a test seam.
|
|
96
|
+
*/
|
|
97
|
+
export async function buildSessionPlan(repoRoot, seam = {}) {
|
|
98
|
+
const readFileImpl = seam.readFile ?? readFile;
|
|
99
|
+
const pathExists = seam.pathExists ?? defaultPathExists;
|
|
100
|
+
|
|
101
|
+
const preferencesPath = join(repoRoot, 'operator', 'config', 'preferences.json');
|
|
102
|
+
|
|
103
|
+
let raw;
|
|
104
|
+
try {
|
|
105
|
+
raw = await readFileImpl(preferencesPath, 'utf8');
|
|
106
|
+
} catch (err) {
|
|
107
|
+
if (err && err.code === 'ENOENT') {
|
|
108
|
+
throw new SessionError(
|
|
109
|
+
`preferences.json not found at ${preferencesPath}. Run \`pa-init\` to scaffold an operator config.`,
|
|
110
|
+
1,
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
throw new SessionError(
|
|
114
|
+
`failed to read preferences.json at ${preferencesPath}: ${err.message ?? err}`,
|
|
115
|
+
2,
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
let prefs;
|
|
120
|
+
try {
|
|
121
|
+
prefs = JSON.parse(raw);
|
|
122
|
+
} catch (err) {
|
|
123
|
+
throw new SessionError(
|
|
124
|
+
`preferences.json at ${preferencesPath} is not valid JSON: ${err.message ?? err}`,
|
|
125
|
+
1,
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (!prefs || typeof prefs !== 'object' || Array.isArray(prefs)) {
|
|
130
|
+
throw new SessionError(
|
|
131
|
+
`preferences.json at ${preferencesPath} must be a JSON object.`,
|
|
132
|
+
1,
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const paName = prefs.paName;
|
|
137
|
+
if (typeof paName !== 'string' || paName.trim().length === 0) {
|
|
138
|
+
throw new SessionError(
|
|
139
|
+
`preferences.json at ${preferencesPath} is missing required field \`paName\`.`,
|
|
140
|
+
1,
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const agentDir = join(repoRoot, 'agents', paName);
|
|
145
|
+
if (!(await pathExists(agentDir, 'dir'))) {
|
|
146
|
+
throw new SessionError(
|
|
147
|
+
`agent directory ${agentDir} does not exist. Did you complete \`pa-init\`?`,
|
|
148
|
+
1,
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
repoRoot,
|
|
154
|
+
preferencesPath,
|
|
155
|
+
paName,
|
|
156
|
+
agentDir,
|
|
157
|
+
command: 'claude',
|
|
158
|
+
args: ['--dangerously-skip-permissions'],
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Execute the plan: chdir to the agent directory and spawn `claude`,
|
|
164
|
+
* inheriting stdio. We use `spawn` rather than a process-replacing exec
|
|
165
|
+
* because Node has no portable cross-platform `execvp`; spawning with
|
|
166
|
+
* `stdio: 'inherit'` and exiting with the child's code is the standard
|
|
167
|
+
* Node equivalent.
|
|
168
|
+
*
|
|
169
|
+
* The seam is the chokepoint: tests inject a fake spawner and a fake
|
|
170
|
+
* chdir to verify the plan without launching real processes.
|
|
171
|
+
*/
|
|
172
|
+
export async function runSession(plan, seam = {}) {
|
|
173
|
+
const chdirImpl = seam.chdir ?? ((d) => process.chdir(d));
|
|
174
|
+
const spawner = seam.spawner ?? defaultSpawner;
|
|
175
|
+
const stderr = seam.stderr ?? process.stderr;
|
|
176
|
+
|
|
177
|
+
try {
|
|
178
|
+
chdirImpl(plan.agentDir);
|
|
179
|
+
} catch (err) {
|
|
180
|
+
throw new SessionError(
|
|
181
|
+
`failed to chdir to ${plan.agentDir}: ${err.message ?? err}`,
|
|
182
|
+
2,
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
try {
|
|
187
|
+
const code = await spawner(plan);
|
|
188
|
+
return typeof code === 'number' ? code : 0;
|
|
189
|
+
} catch (err) {
|
|
190
|
+
if (err && err.code === 'ENOENT') {
|
|
191
|
+
stderr.write(
|
|
192
|
+
'[pa session] `claude` not found on PATH. Install Claude Code or ensure `claude` is on `PATH` before running `pa session`.\n',
|
|
193
|
+
);
|
|
194
|
+
return 1;
|
|
195
|
+
}
|
|
196
|
+
throw new SessionError(
|
|
197
|
+
`failed to spawn \`claude\`: ${err.message ?? err}`,
|
|
198
|
+
2,
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function defaultSpawner(plan) {
|
|
204
|
+
return new Promise((resolveP, rejectP) => {
|
|
205
|
+
const child = spawn(plan.command, plan.args, { stdio: 'inherit' });
|
|
206
|
+
child.on('close', (code) => resolveP(code ?? 0));
|
|
207
|
+
child.on('error', (err) => rejectP(err));
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Top-level verb entry. Wires resolve → plan → run, translates SessionError
|
|
213
|
+
* to a stderr write + the carried exit code, and lets unexpected throws
|
|
214
|
+
* surface as exit code 2.
|
|
215
|
+
*/
|
|
216
|
+
export async function executeSession(seam = {}) {
|
|
217
|
+
const stderr = seam.stderr ?? process.stderr;
|
|
218
|
+
const stdout = seam.stdout ?? process.stdout;
|
|
219
|
+
|
|
220
|
+
let repoRoot;
|
|
221
|
+
try {
|
|
222
|
+
const resolved = await resolveRepoRoot(seam);
|
|
223
|
+
repoRoot = resolved.root;
|
|
224
|
+
if (seam.verbose) {
|
|
225
|
+
stdout.write(`[pa session] repo root: ${repoRoot} (via ${resolved.source})\n`);
|
|
226
|
+
}
|
|
227
|
+
} catch (err) {
|
|
228
|
+
stderr.write(`[pa session] ${err.message ?? err}\n`);
|
|
229
|
+
return 2;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
let plan;
|
|
233
|
+
try {
|
|
234
|
+
plan = await buildSessionPlan(repoRoot, seam);
|
|
235
|
+
} catch (err) {
|
|
236
|
+
if (err instanceof SessionError) {
|
|
237
|
+
stderr.write(`[pa session] ${err.userMessage}\n`);
|
|
238
|
+
return err.exitCode;
|
|
239
|
+
}
|
|
240
|
+
stderr.write(`[pa session] ${err.message ?? err}\n`);
|
|
241
|
+
return 2;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
try {
|
|
245
|
+
return await runSession(plan, seam);
|
|
246
|
+
} catch (err) {
|
|
247
|
+
if (err instanceof SessionError) {
|
|
248
|
+
stderr.write(`[pa session] ${err.userMessage}\n`);
|
|
249
|
+
return err.exitCode;
|
|
250
|
+
}
|
|
251
|
+
stderr.write(`[pa session] ${err.message ?? err}\n`);
|
|
252
|
+
return 2;
|
|
253
|
+
}
|
|
254
|
+
}
|