@event4u/agent-config 14.9.0 → 14.11.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/.augment-plugin/marketplace.json +3 -3
- package/.augment-plugin/plugin.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +384 -0
- package/README.md +1 -1
- package/dist/agent-src/commands/design.md +88 -0
- package/dist/agent-src/commands/project/analyze.md +47 -0
- package/dist/agent-src/commands/review/changes.md +67 -6
- package/dist/agent-src/commands/roadmap/create.md +49 -0
- package/dist/agent-src/commands/roadmap/next.md +27 -11
- package/dist/agent-src/commands/roadmap/process-full.md +14 -0
- package/dist/agent-src/commands/video/from-script.md +60 -3
- package/dist/agent-src/commands/video/stitch.md +14 -0
- package/dist/agent-src/contexts/communication/rules-auto/guidelines-mechanics.md +1 -0
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +154 -2
- package/dist/agent-src/rules/active-remediation.md +3 -3
- package/dist/agent-src/rules/code-provenance.md +2 -2
- package/dist/agent-src/rules/context-hygiene.md +13 -33
- package/dist/agent-src/rules/council-availability.md +2 -2
- package/dist/agent-src/rules/decision-revisit-gate.md +3 -1
- package/dist/agent-src/rules/design-fidelity.md +18 -0
- package/dist/agent-src/rules/design-review-after-ui-write.md +2 -2
- package/dist/agent-src/rules/evaluator-independence.md +56 -44
- package/dist/agent-src/rules/fix-what-you-see.md +9 -1
- package/dist/agent-src/rules/framework-neutrality-in-generic-skills.md +1 -1
- package/dist/agent-src/rules/minimal-safe-diff.md +14 -20
- package/dist/agent-src/rules/missing-skill-recovery.md +13 -1
- package/dist/agent-src/rules/playbook-precedence.md +105 -0
- package/dist/agent-src/rules/recurring-criticism.md +3 -1
- package/dist/agent-src/rules/roadmap-progress-sync.md +14 -13
- package/dist/agent-src/rules/scale-discipline.md +24 -0
- package/dist/agent-src/rules/security-sensitive-stop.md +2 -2
- package/dist/agent-src/rules/settings-ask-protocol.md +2 -2
- package/dist/agent-src/rules/ui-audit-gate.md +27 -15
- package/dist/agent-src/rules/untrusted-input-defense.md +2 -2
- package/dist/agent-src/skills/accessibility-auditor/SKILL.md +19 -0
- package/dist/agent-src/skills/adr-create/SKILL.md +7 -0
- package/dist/agent-src/skills/agents-md-thin-root/SKILL.md +33 -0
- package/dist/agent-src/skills/ai-council/references/advanced-modes.md +17 -3
- package/dist/agent-src/skills/ai-council/references/output-and-synthesis.md +31 -0
- package/dist/agent-src/skills/ai-council/references/procedure.md +19 -0
- package/dist/agent-src/skills/alerting-doctrine/SKILL.md +225 -0
- package/dist/agent-src/skills/alerting-doctrine/evals/triggers.json +17 -0
- package/dist/agent-src/skills/async-python-patterns/SKILL.md +1 -1
- package/dist/agent-src/skills/authz-review/SKILL.md +0 -1
- package/dist/agent-src/skills/blast-radius-analyzer/SKILL.md +32 -0
- package/dist/agent-src/skills/brand/SKILL.md +3 -0
- package/dist/agent-src/skills/brand-asset-generation/SKILL.md +3 -0
- package/dist/agent-src/skills/brand-audit/SKILL.md +3 -0
- package/dist/agent-src/skills/brand-identity/SKILL.md +3 -0
- package/dist/agent-src/skills/brand-strategy/SKILL.md +3 -0
- package/dist/agent-src/skills/brand-to-tokens/SKILL.md +3 -0
- package/dist/agent-src/skills/check-refs/SKILL.md +3 -0
- package/dist/agent-src/skills/code-refactoring/SKILL.md +1 -1
- package/dist/agent-src/skills/code-review/SKILL.md +11 -0
- package/dist/agent-src/skills/command-routing/SKILL.md +6 -0
- package/dist/agent-src/skills/condense-memory/SKILL.md +4 -1
- package/dist/agent-src/skills/context-authoring/SKILL.md +1 -1
- package/dist/agent-src/skills/conventional-commits-writing/SKILL.md +4 -1
- package/dist/agent-src/skills/copilot-agents-optimization/SKILL.md +10 -0
- package/dist/agent-src/skills/corpus-grounding/SKILL.md +3 -0
- package/dist/agent-src/skills/design-intelligence/SKILL.md +3 -0
- package/dist/agent-src/skills/design-intelligence/data/landing.csv +36 -35
- package/dist/agent-src/skills/design-intelligence/data/stacks/threejs.csv +1 -0
- package/dist/agent-src/skills/design-review/SKILL.md +80 -2
- package/dist/agent-src/skills/design-review/references/verification-automation.md +41 -0
- package/dist/agent-src/skills/design-system-capture/SKILL.md +35 -40
- package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +50 -0
- package/dist/agent-src/skills/design-tokens/SKILL.md +3 -0
- package/dist/agent-src/skills/design-variations/SKILL.md +3 -0
- package/dist/agent-src/skills/developer-like-execution/SKILL.md +3 -0
- package/dist/agent-src/skills/discovery-interview/SKILL.md +1 -1
- package/dist/agent-src/skills/doc-coauthoring/SKILL.md +1 -1
- package/dist/agent-src/skills/docx-authoring/SKILL.md +1 -1
- package/dist/agent-src/skills/editorial-calendar/SKILL.md +1 -1
- package/dist/agent-src/skills/estimate-ticket/SKILL.md +3 -0
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +20 -3
- package/dist/agent-src/skills/fe-design/SKILL.md +74 -41
- package/dist/agent-src/skills/fe-design/evals/triggers.json +65 -53
- package/dist/agent-src/skills/fe-design/references/craft-floor.md +52 -0
- package/dist/agent-src/skills/fe-design/references/source-led-port.md +36 -0
- package/dist/agent-src/skills/file-editor/SKILL.md +5 -0
- package/dist/agent-src/skills/finishing-a-development-branch/SKILL.md +2 -2
- package/dist/agent-src/skills/git-workflow/SKILL.md +3 -0
- package/dist/agent-src/skills/github-ci/SKILL.md +1 -1
- package/dist/agent-src/skills/html-deck/SKILL.md +3 -0
- package/dist/agent-src/skills/iconography/SKILL.md +3 -0
- package/dist/agent-src/skills/image-editing/SKILL.md +3 -0
- package/dist/agent-src/skills/image-generation/SKILL.md +3 -0
- package/dist/agent-src/skills/image-provider-routing/SKILL.md +3 -0
- package/dist/agent-src/skills/incident-commander/SKILL.md +10 -0
- package/dist/agent-src/skills/js-library-packaging/SKILL.md +173 -0
- package/dist/agent-src/skills/js-library-packaging/evals/triggers.json +17 -0
- package/dist/agent-src/skills/js-library-packaging/scripts/check_package_surface.ts +270 -0
- package/dist/agent-src/skills/judge-artifact-completeness/SKILL.md +1 -1
- package/dist/agent-src/skills/judge-injection-defense/SKILL.md +1 -1
- package/dist/agent-src/skills/laravel-migration/SKILL.md +3 -0
- package/dist/agent-src/skills/laravel-validation/SKILL.md +1 -1
- package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +3 -0
- package/dist/agent-src/skills/legal-intake-triage/SKILL.md +1 -1
- package/dist/agent-src/skills/legal-practice-profile/SKILL.md +1 -1
- package/dist/agent-src/skills/lint-skills/SKILL.md +3 -0
- package/dist/agent-src/skills/llm-provider-knowledge/SKILL.md +3 -0
- package/dist/agent-src/skills/logging-monitoring/SKILL.md +234 -51
- package/dist/agent-src/skills/logo-generation/SKILL.md +3 -0
- package/dist/agent-src/skills/md-language-check/SKILL.md +3 -0
- package/dist/agent-src/skills/messaging-architecture/SKILL.md +1 -1
- package/dist/agent-src/skills/migration-architect/SKILL.md +1 -1
- package/dist/agent-src/skills/module-detect-on-the-fly/SKILL.md +1 -1
- package/dist/agent-src/skills/module-management/SKILL.md +1 -1
- package/dist/agent-src/skills/monorepo-workspace/SKILL.md +149 -0
- package/dist/agent-src/skills/monorepo-workspace/evals/triggers.json +17 -0
- package/dist/agent-src/skills/motion-choreographer/SKILL.md +7 -1
- package/dist/agent-src/skills/multi-tenancy/SKILL.md +4 -0
- package/dist/agent-src/skills/onboarding-program/SKILL.md +1 -1
- package/dist/agent-src/skills/operational-readiness/SKILL.md +184 -0
- package/dist/agent-src/skills/operational-readiness/evals/triggers.json +17 -0
- package/dist/agent-src/skills/override-management/SKILL.md +3 -0
- package/dist/agent-src/skills/perf-feedback-craft/SKILL.md +1 -1
- package/dist/agent-src/skills/persona-improvement/SKILL.md +3 -0
- package/dist/agent-src/skills/pest-testing/SKILL.md +3 -0
- package/dist/agent-src/skills/playbook-authoring/SKILL.md +162 -0
- package/dist/agent-src/skills/playwright-testing/SKILL.md +3 -0
- package/dist/agent-src/skills/privacy-review/SKILL.md +3 -0
- package/dist/agent-src/skills/prompt-engineering-image/SKILL.md +3 -0
- package/dist/agent-src/skills/prompt-optimizer/SKILL.md +1 -1
- package/dist/agent-src/skills/quality-tools/SKILL.md +5 -0
- package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +52 -21
- package/dist/agent-src/skills/readme-reviewer/SKILL.md +3 -0
- package/dist/agent-src/skills/readme-writing/SKILL.md +3 -0
- package/dist/agent-src/skills/readme-writing-package/SKILL.md +3 -0
- package/dist/agent-src/skills/receiving-code-review/SKILL.md +3 -3
- package/dist/agent-src/skills/refine-prompt/SKILL.md +3 -0
- package/dist/agent-src/skills/refine-ticket/SKILL.md +3 -0
- package/dist/agent-src/skills/requesting-code-review/SKILL.md +2 -2
- package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +23 -41
- package/dist/agent-src/skills/roadmap-writing/references/follow-up-roadmap-shape.md +47 -0
- package/dist/agent-src/skills/rtk-output-filtering/SKILL.md +22 -6
- package/dist/agent-src/skills/runway-cognition/SKILL.md +3 -0
- package/dist/agent-src/skills/scene-expander/SKILL.md +1 -1
- package/dist/agent-src/skills/scene-expander/scene-blueprint.schema.yaml +26 -0
- package/dist/agent-src/skills/secrets-management/SKILL.md +5 -1
- package/dist/agent-src/skills/server-hardening/SKILL.md +159 -0
- package/dist/agent-src/skills/server-hardening/evals/triggers.json +17 -0
- package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +3 -0
- package/dist/agent-src/skills/skill-management/SKILL.md +3 -0
- package/dist/agent-src/skills/skill-reviewer/SKILL.md +3 -0
- package/dist/agent-src/skills/skill-writing/SKILL.md +6 -0
- package/dist/agent-src/skills/spreadsheet-authoring/SKILL.md +3 -0
- package/dist/agent-src/skills/storybook-workshop/SKILL.md +194 -0
- package/dist/agent-src/skills/storybook-workshop/evals/triggers.json +17 -0
- package/dist/agent-src/skills/storybook-workshop/scripts/story_contrast_floor.ts +134 -0
- package/dist/agent-src/skills/subagent-orchestration/SKILL.md +1 -1
- package/dist/agent-src/skills/subagent-orchestration/prompts/README.md +25 -0
- package/dist/agent-src/skills/supply-chain-intake/SKILL.md +12 -0
- package/dist/agent-src/skills/systematic-debugging/SKILL.md +1 -1
- package/dist/agent-src/skills/terraform/SKILL.md +3 -0
- package/dist/agent-src/skills/test-driven-development/SKILL.md +1 -1
- package/dist/agent-src/skills/threat-modeling/SKILL.md +0 -3
- package/dist/agent-src/skills/token-optimizer/SKILL.md +3 -0
- package/dist/agent-src/skills/typography-system/SKILL.md +3 -0
- package/dist/agent-src/skills/ui-component-architect/SKILL.md +9 -1
- package/dist/agent-src/skills/upstream-contribute/SKILL.md +3 -0
- package/dist/agent-src/skills/using-git-worktrees/SKILL.md +1 -1
- package/dist/agent-src/skills/validate-feature-fit/SKILL.md +3 -0
- package/dist/agent-src/skills/verify-completion-evidence/SKILL.md +2 -2
- package/dist/agent-src/skills/wireframe/SKILL.md +3 -0
- package/dist/agent-src/skills/wireframe/references/story-beat-ledger.schema.json +115 -0
- package/dist/agent-src/skills/workspace-link/SKILL.md +131 -0
- package/dist/agent-src/skills/workspace-link/evals/triggers.json +17 -0
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/playbook.md +75 -0
- package/dist/agent-src/templates/roadmaps.md +37 -0
- package/dist/agent-src/templates/scripts/check_playbook_invokes.ts +231 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +24 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +2 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/review.ts +216 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +159 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/stack_bundles.ts +73 -4
- package/dist/agent-src/templates/scripts/work_engine/stack/detect.ts +325 -40
- package/dist/cli/commands/uiAudit.js +221 -0
- package/dist/cli/commands/uiAudit.js.map +1 -0
- package/dist/cli/commands/uiRender.js +206 -0
- package/dist/cli/commands/uiRender.js.map +1 -0
- package/dist/cli/main.js +22 -1
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/mcp/content.js +64 -1
- package/dist/cli/mcp/content.js.map +1 -1
- package/dist/cli/mcp/dispatch.js +175 -10
- package/dist/cli/mcp/dispatch.js.map +1 -1
- package/dist/cli/registry.js +3 -0
- package/dist/cli/registry.js.map +1 -1
- package/dist/cli-delegate/{chunk-H4VIVD4C.js → chunk-236PNQZK.js} +413 -391
- package/dist/cli-delegate/{chunk-G23AT6GD.js → chunk-DMSZHXZS.js} +57 -1
- package/dist/cli-delegate/{chunk-77GR3VD4.js → chunk-FF7WAFMR.js} +58 -3
- package/dist/cli-delegate/{chunk-PLSG6UL5.js → chunk-K4FFODTS.js} +341 -204
- package/dist/cli-delegate/{chunk-YM4GZDJG.js → chunk-L43NCYPB.js} +5 -0
- package/dist/cli-delegate/{chunk-B2ERU2VA.js → chunk-WOKWX5PL.js} +1 -1
- package/dist/cli-delegate/cmd_brand_status.js +8 -6
- package/dist/cli-delegate/cmd_conformance.js +3 -3
- package/dist/cli-delegate/cmd_doctor.js +3 -3
- package/dist/cli-delegate/cmd_export.js +4 -4
- package/dist/cli-delegate/cmd_handoff.js +1 -1
- package/dist/cli-delegate/cmd_preflight.js +2 -2
- package/dist/cli-delegate/cmd_prune.js +4 -4
- package/dist/cli-delegate/cmd_session_recycle.js +70 -9
- package/dist/cli-delegate/cmd_settings_migrate.js +4 -4
- package/dist/cli-delegate/cmd_settings_set.js +1 -1
- package/dist/cli-delegate/cmd_sync.js +4 -4
- package/dist/cli-delegate/cmd_uninstall.js +4 -4
- package/dist/cli-delegate/cmd_validate.js +4 -4
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +380 -145
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +4 -4
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +18 -8
- package/dist/discovery/trust-report.md +3 -3
- package/dist/discovery/workspaces.json +16 -6
- package/dist/hooks/dispatch.js +2337 -1436
- package/dist/install/install.mjs +795 -708
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +1685 -1250
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/lean_projection_mode.js +40 -0
- package/dist/scripts/_lib/lean_projection_mode.js.map +1 -0
- package/dist/scripts/_lib/scoped_projection.js +84 -0
- package/dist/scripts/_lib/scoped_projection.js.map +1 -1
- package/dist/scripts/_lib/ui_surface.js +89 -0
- package/dist/scripts/_lib/ui_surface.js.map +1 -0
- package/dist/scripts/condense.js +6 -10
- package/dist/scripts/condense.js.map +1 -1
- package/dist/scripts/project_thin_rules.js +40 -2
- package/dist/scripts/project_thin_rules.js.map +1 -1
- package/dist/server/schemas/settings.js +3 -0
- package/dist/server/schemas/settings.js.map +1 -1
- package/dist/shared/skillRanking.js +159 -0
- package/dist/shared/skillRanking.js.map +1 -0
- package/docs/contracts/provider-lifecycle.md +47 -18
- package/docs/contracts/settings-classes.md +14 -4
- package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +49 -0
- package/docs/guidelines/agent-infra/evaluator-independence-mechanics.md +92 -0
- package/docs/guidelines/agent-infra/minimal-safe-diff-mechanics.md +45 -1
- package/docs/guidelines/agent-infra/roadmap-deferred-resolution-provenance.md +54 -0
- package/docs/guidelines/design-antipatterns.md +4 -2
- package/docs/guidelines/design-asset-discipline.md +38 -0
- package/docs/guidelines/design-fidelity-mechanics.md +45 -29
- package/docs/guidelines/design-handover-extraction.md +35 -5
- package/docs/guidelines/design-modes.md +41 -1
- package/docs/guidelines/monorepo-antipatterns.md +141 -0
- package/llms.txt +50 -39
- package/package.json +1 -1
- package/src/agent-src/templates/scripts/check_playbook_invokes.ts +231 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +24 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +2 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui/review.ts +216 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +159 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui/stack_bundles.ts +73 -4
- package/src/agent-src/templates/scripts/work_engine/stack/detect.ts +325 -40
- package/src/config/agent-settings.template.yml +28 -0
- package/src/config/assurance-capability-registry.json +223 -0
- package/src/config/assurance-threshold-budget.json +65 -0
- package/src/config/ci-local-parity.yml +14 -0
- package/src/config/discovery/unassigned-artefacts.yml +2 -0
- package/src/config/dispatch-economy-metrics.json +4 -2
- package/src/config/evaluator-budgets.json +14 -14
- package/src/config/gate-coverage.yml +227 -8
- package/src/config/gate-violation-baselines.json +76 -72
- package/src/config/hook-latency-budget.json +1 -0
- package/src/config/hook-token-budget.json +137 -125
- package/src/config/pack-size-budget.json +4 -2
- package/src/config/preamble-payload-budget.json +71 -67
- package/src/config/quorum-attendance-budget.json +12 -4
- package/src/config/rule-activation-census.json +7 -3
- package/src/config/rule-enforcement-baseline.json +0 -1
- package/src/config/rule-stub-ceilings.json +112 -91
- package/src/scripts/_cli/cmd_doctor.ts +15 -16
- package/src/scripts/_cli/doctor_install_mode.ts +49 -0
- package/src/scripts/_cli/doctor_overrides.ts +154 -0
- package/src/scripts/_dispatch.bash +17 -0
- package/src/scripts/_lib/as_of.ts +412 -0
- package/src/scripts/_lib/exec_evidence.ts +7 -0
- package/src/scripts/_lib/hook_settings.ts +33 -0
- package/src/scripts/_lib/host_listing_model.ts +192 -0
- package/src/scripts/_lib/lean_projection_mode.ts +44 -0
- package/src/scripts/_lib/mcp_bridge.ts +199 -0
- package/src/scripts/_lib/review_independence.ts +62 -10
- package/src/scripts/_lib/rule_injection.ts +239 -0
- package/src/scripts/_lib/run_checkpoint.ts +54 -1
- package/src/scripts/_lib/scoped_projection.ts +96 -0
- package/src/scripts/_lib/session_register.ts +44 -2
- package/src/scripts/_lib/skill_catalogue.ts +38 -2
- package/src/scripts/_lib/transcript_reads.ts +207 -0
- package/src/scripts/_lib/ui_authority.test.ts +251 -0
- package/src/scripts/_lib/ui_authority.ts +343 -0
- package/src/scripts/ai-video/adapters/fal.sh +8 -3
- package/src/scripts/ai-video/adapters/gemini-veo.sh +10 -0
- package/src/scripts/ai-video/adapters/higgsfield.sh +10 -2
- package/src/scripts/ai-video/adapters/kling.sh +10 -0
- package/src/scripts/ai-video/adapters/sora.sh +10 -0
- package/src/scripts/ai-video/lib/calibrate-cost.sh +145 -0
- package/src/scripts/ai-video/lib/model-capabilities/README.md +53 -7
- package/src/scripts/ai-video/lib/model-capabilities/comfyui.json +15 -3
- package/src/scripts/ai-video/lib/model-capabilities/fal.json +36 -6
- package/src/scripts/ai-video/lib/model-capabilities/gemini-veo.json +23 -0
- package/src/scripts/ai-video/lib/model-capabilities/higgsfield.json +23 -0
- package/src/scripts/ai-video/lib/model-capabilities/kling.json +23 -0
- package/src/scripts/ai-video/lib/model-capabilities/musetalk.json +8 -2
- package/src/scripts/ai-video/lib/model-capabilities/replicate.json +36 -6
- package/src/scripts/ai-video/lib/model-capabilities/sora.json +45 -0
- package/src/scripts/ai-video/lib/model-capabilities/syncso.json +8 -2
- package/src/scripts/ai-video/lib/parse-blueprint.sh +52 -5
- package/src/scripts/ai-video/lib/resume-scan.sh +92 -3
- package/src/scripts/ai-video/lib/seam-plan.sh +212 -0
- package/src/scripts/ai-video/smoke-trace.sh +450 -2
- package/src/scripts/ai-video/stitch.sh +191 -10
- package/src/scripts/ai-video/test-pipeline.sh +84 -6
- package/src/scripts/ai_council/cli_agency_bounds.ts +139 -0
- package/src/scripts/ai_council/cli_least_agency_canary.ts +347 -0
- package/src/scripts/ai_council/clients.ts +3 -2
- package/src/scripts/ai_council/consensus.ts +63 -6
- package/src/scripts/ai_council/events_log.ts +46 -1
- package/src/scripts/ai_council/orchestrator.ts +103 -108
- package/src/scripts/ai_council/orchestrator_results.ts +166 -0
- package/src/scripts/ai_council/prompts.ts +12 -5
- package/src/scripts/ai_council/quorum.ts +82 -1
- package/src/scripts/ai_council/quorum_wiring.ts +82 -5
- package/src/scripts/ai_council/seating.ts +211 -0
- package/src/scripts/ai_council/stance_tally.ts +22 -0
- package/src/scripts/bench_hook_latency.ts +62 -0
- package/src/scripts/build_proof.ts +12 -1
- package/src/scripts/capture_skill_catalogue.ts +21 -0
- package/src/scripts/check_always_budget.ts +2 -1
- package/src/scripts/check_augmentignore.ts +2 -1
- package/src/scripts/check_beta_review_markers.ts +2 -1
- package/src/scripts/check_composite_arming.ts +364 -0
- package/src/scripts/check_corpus_staleness.ts +2 -1
- package/src/scripts/check_council_pin_staleness.ts +3 -1
- package/src/scripts/check_enforcement_coverage.ts +116 -3
- package/src/scripts/check_enforcement_denominator.ts +365 -0
- package/src/scripts/check_gate_coverage.ts +2 -1
- package/src/scripts/check_hook_bundle_freshness.ts +13 -1
- package/src/scripts/check_knowledge_cards.ts +2 -1
- package/src/scripts/check_knowledge_pages.ts +3 -2
- package/src/scripts/check_memory.ts +2 -1
- package/src/scripts/check_no_automerge_key.ts +287 -0
- package/src/scripts/check_pack_conformance_fixture.ts +565 -0
- package/src/scripts/check_pack_passport_reconciliation.ts +274 -0
- package/src/scripts/check_proposal.ts +2 -1
- package/src/scripts/check_reach_staleness.ts +2 -1
- package/src/scripts/check_release_adjacent_health.ts +2 -1
- package/src/scripts/check_release_pr_shape.ts +5 -0
- package/src/scripts/check_review_schema.ts +34 -1
- package/src/scripts/check_roadmap_trackable.ts +48 -3
- package/src/scripts/check_source_size_budget.ts +2 -1
- package/src/scripts/check_standing_payload_delta.ts +505 -0
- package/src/scripts/check_trigger_evals.ts +2 -1
- package/src/scripts/classify_change_risk.ts +281 -0
- package/src/scripts/compute_skill_tiers.ts +186 -0
- package/src/scripts/condense.ts +8 -13
- package/src/scripts/council_attendance_metrics.ts +183 -0
- package/src/scripts/council_cli.ts +10 -8
- package/src/scripts/council_parse_rate.ts +116 -0
- package/src/scripts/derive_playbooks.ts +406 -0
- package/src/scripts/design_slop_rules.test.ts +7 -0
- package/src/scripts/design_slop_rules.ts +56 -2
- package/src/scripts/dispatch_r2_reviewer.ts +16 -0
- package/src/scripts/frontend_corpus_hash.ts +116 -0
- package/src/scripts/generate_pack_manifests.ts +78 -0
- package/src/scripts/grade_target_readiness.ts +478 -0
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +97 -16
- package/src/scripts/hooks/concern_registry.ts +4 -0
- package/src/scripts/hooks/design_pass_hook.ts +356 -0
- package/src/scripts/hooks/rtk_wrap_hook.ts +30 -7
- package/src/scripts/hooks/rule_inject_hook.ts +318 -0
- package/src/scripts/hot_context_hook.ts +74 -7
- package/src/scripts/install.ts +40 -89
- package/src/scripts/lint_adapter_tier.ts +663 -0
- package/src/scripts/lint_archived_skills.ts +93 -0
- package/src/scripts/lint_budget_ownership.ts +2 -1
- package/src/scripts/lint_deterministic_time.ts +470 -0
- package/src/scripts/lint_framework_leakage.ts +5 -0
- package/src/scripts/lint_handoffs.ts +525 -1
- package/src/scripts/lint_judge_prompt_expectation.ts +155 -0
- package/src/scripts/lint_marketplace.ts +88 -0
- package/src/scripts/lint_media_policy_linkage.ts +140 -1
- package/src/scripts/lint_one_off_age.ts +6 -1
- package/src/scripts/lint_override_kernel_guard.ts +87 -5
- package/src/scripts/lint_roadmap_complexity.ts +56 -0
- package/src/scripts/lint_rule_enforcement_declaration.ts +24 -7
- package/src/scripts/lint_rule_norm_pin.ts +321 -0
- package/src/scripts/lint_spawn_payload.ts +42 -5
- package/src/scripts/lint_symptom_intake.ts +2 -1
- package/src/scripts/mcp_parity_smoke.ts +30 -4
- package/src/scripts/mcp_render.ts +66 -1
- package/src/scripts/mcp_tool_fingerprint.ts +241 -0
- package/src/scripts/measure_risk_class_drift.ts +140 -0
- package/src/scripts/measure_skill_ranker_baseline.ts +212 -0
- package/src/scripts/media/lib/adapter-common.sh +211 -1
- package/src/scripts/media/lib/adapter-contract.md +74 -6
- package/src/scripts/model_rule_injection.ts +907 -0
- package/src/scripts/project_thin_rules.ts +45 -2
- package/src/scripts/release.ts +66 -860
- package/src/scripts/release_drill.ts +59 -3
- package/src/scripts/release_env.ts +298 -0
- package/src/scripts/release_publication.ts +720 -0
- package/src/scripts/roadmap_context.ts +778 -0
- package/src/scripts/schemas/fidelity-measurement-sheet.schema.json +169 -0
- package/src/scripts/schemas/pack.schema.json +62 -4
- package/src/scripts/schemas/rule.schema.json +24 -2
- package/src/scripts/schemas/skill.schema.json +41 -2
- package/src/scripts/schemas/ui-authority.schema.json +114 -0
- package/src/scripts/session_register_hook.ts +29 -3
- package/src/scripts/sessions_cli.ts +67 -3
- package/src/scripts/skill_tools/score_skill_relevance.ts +36 -53
- package/src/scripts/tighten_skill_descriptions.ts +264 -0
- package/src/scripts/token_sink_report.ts +239 -0
- package/src/scripts/trigger_coverage.ts +122 -3
- package/src/scripts/update_counts.ts +7 -3
- package/src/scripts/validate_frontmatter.ts +66 -0
- package/src/server/schemas/settings.ts +5 -0
- package/src/shared/skillRanking.ts +183 -0
|
@@ -0,0 +1,778 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* `agent-config roadmap:context` — the one deterministic situational-awareness
|
|
4
|
+
* probe behind every `/roadmap:*` entry point.
|
|
5
|
+
*
|
|
6
|
+
* ## Why this exists
|
|
7
|
+
*
|
|
8
|
+
* The ingredients of situational awareness were already in the tree and none of
|
|
9
|
+
* them reached a run. The four-command live screen was hand-written inside ONE
|
|
10
|
+
* command file (`/roadmap:next` § 1) and existed for **selection** only; the
|
|
11
|
+
* loop's own screen is claim-triggered — it fires when a message would
|
|
12
|
+
* *describe* a roadmap as in-flight, not before the work starts; file-overlap
|
|
13
|
+
* derivation lived inside the multi-roadmap set contract, which a
|
|
14
|
+
* single-roadmap run never enters. This file is the extraction: one probe, one
|
|
15
|
+
* report, callable from all five entry points.
|
|
16
|
+
*
|
|
17
|
+
* Measured on the population that motivated it: 4 of 24 active roadmaps were
|
|
18
|
+
* already closed in an open PR at `33d7f74af`, and 2 of 22 six days later at
|
|
19
|
+
* `f6703b78a` — the sample halved itself in a week. That decay is the argument
|
|
20
|
+
* for a refresh cadence rather than a better one-shot screen, and it is why
|
|
21
|
+
* every read below is live and none is cached.
|
|
22
|
+
*
|
|
23
|
+
* ## Honesty boundary — the probe is deterministic, the invocation is not
|
|
24
|
+
*
|
|
25
|
+
* ```
|
|
26
|
+
* THE PROBE IS DETERMINISTIC ONCE INVOKED. THE INVOCATION IS MODEL-CARRIED.
|
|
27
|
+
* NOTHING FIRES THIS SCRIPT, AND NOTHING NOTICES WHEN IT IS SKIPPED.
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* Same shape as the boundary `/roadmap:next` already states over its own
|
|
31
|
+
* screen, and stated here rather than left as prose that reads like a
|
|
32
|
+
* guarantee. What IS deterministic: what the probe reports once it runs.
|
|
33
|
+
*
|
|
34
|
+
* ## Failure posture
|
|
35
|
+
*
|
|
36
|
+
* Every read degrades rather than throws. No network, no `gh`, no
|
|
37
|
+
* authentication → `network: "unavailable"`, an empty PR set, an explicit
|
|
38
|
+
* `scanned: 0 PRs (network unavailable)` line, and exit 0. A probe that exits
|
|
39
|
+
* non-zero offline would make the call site conditional, which is the one thing
|
|
40
|
+
* this change is trying to remove.
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
import { execFileSync } from 'node:child_process';
|
|
44
|
+
import * as fs from 'node:fs';
|
|
45
|
+
import * as path from 'node:path';
|
|
46
|
+
import process from 'node:process';
|
|
47
|
+
import { fileURLToPath } from 'node:url';
|
|
48
|
+
|
|
49
|
+
import { gitEnv } from './_lib/git_env.js';
|
|
50
|
+
import { read_live_records, register_dir } from './_lib/session_register.js';
|
|
51
|
+
import { other_worktree_branches_detailed } from './sessions_cli.js';
|
|
52
|
+
|
|
53
|
+
/** One open pull request, with the file set it changes. */
|
|
54
|
+
export interface PullRequestInfo {
|
|
55
|
+
number: number;
|
|
56
|
+
title: string;
|
|
57
|
+
headRefName: string;
|
|
58
|
+
files: string[];
|
|
59
|
+
/** Head commit of the PR branch. Feeds the refresh fingerprint. */
|
|
60
|
+
headRefOid?: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** A roadmap file found in one of the four roadmap directories. */
|
|
64
|
+
export interface RoadmapEntry {
|
|
65
|
+
slug: string;
|
|
66
|
+
/** Repo-relative path. */
|
|
67
|
+
path: string;
|
|
68
|
+
title: string;
|
|
69
|
+
dir: RoadmapDir;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export type RoadmapDir = 'active' | 'later' | 'stubs' | 'archive';
|
|
73
|
+
|
|
74
|
+
/** A sibling roadmap whose slug or title shares keywords with the subject. */
|
|
75
|
+
export interface KeywordHit {
|
|
76
|
+
slug: string;
|
|
77
|
+
path: string;
|
|
78
|
+
dir: RoadmapDir;
|
|
79
|
+
matched: string[];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** A remote branch whose name carries a roadmap slug. */
|
|
83
|
+
export interface RoadmapBranch {
|
|
84
|
+
branch: string;
|
|
85
|
+
slug: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** One (roadmap, open PR) pair that touches at least one path in common. */
|
|
89
|
+
export interface OverlapPair {
|
|
90
|
+
roadmap: string;
|
|
91
|
+
pr: number;
|
|
92
|
+
paths: string[];
|
|
93
|
+
/** Which owned-path set the roadmap side came from. */
|
|
94
|
+
source: OverlapSource;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Where the roadmap's owned-path set came from.
|
|
99
|
+
*
|
|
100
|
+
* `pre-scan` is the set `roadmap-process-loop § 3d` already derives and is
|
|
101
|
+
* authoritative. `cited-path` is the fallback heuristic and is labelled as one:
|
|
102
|
+
* backticked paths in roadmap prose include examples and files the roadmap does
|
|
103
|
+
* not own, so it over-reports. The conservative direction is the one the set
|
|
104
|
+
* contract already states — overlap resolves toward serial — so a false pair
|
|
105
|
+
* costs ordering, never a skip.
|
|
106
|
+
*/
|
|
107
|
+
export type OverlapSource = 'pre-scan' | 'cited-path';
|
|
108
|
+
|
|
109
|
+
export interface SessionsView {
|
|
110
|
+
records: unknown[];
|
|
111
|
+
other_worktree_branches: unknown[];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface RoadmapContext {
|
|
115
|
+
generated_at: string;
|
|
116
|
+
network: 'live' | 'unavailable';
|
|
117
|
+
roadmap: string | null;
|
|
118
|
+
/** `origin/main` at probe time, or `null` when the ref was unreadable. */
|
|
119
|
+
base_sha: string | null;
|
|
120
|
+
/**
|
|
121
|
+
* Stable digest of everything a refresh decision depends on. Compare, never
|
|
122
|
+
* parse: two probes with the same fingerprint saw the same world.
|
|
123
|
+
*/
|
|
124
|
+
fingerprint: string;
|
|
125
|
+
scanned: {
|
|
126
|
+
prs: number;
|
|
127
|
+
roadmaps: number;
|
|
128
|
+
remote_branches: number;
|
|
129
|
+
inbox_files: number;
|
|
130
|
+
sessions: number;
|
|
131
|
+
};
|
|
132
|
+
open_prs: PullRequestInfo[];
|
|
133
|
+
roadmap_branches: RoadmapBranch[];
|
|
134
|
+
sessions: SessionsView;
|
|
135
|
+
/** File NAMES only — never contents. `agents/tmp/` is a private scratch area. */
|
|
136
|
+
inbox_files: string[];
|
|
137
|
+
hits: KeywordHit[];
|
|
138
|
+
overlaps: OverlapPair[];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ---------------------------------------------------------------------------
|
|
142
|
+
// Pure helpers — every one of these is exercised without a network.
|
|
143
|
+
// ---------------------------------------------------------------------------
|
|
144
|
+
|
|
145
|
+
/** Roadmap directories the probe walks, in report order. */
|
|
146
|
+
export const ROADMAP_DIRS: ReadonlyArray<readonly [RoadmapDir, string]> = [
|
|
147
|
+
['active', 'agents/roadmaps'],
|
|
148
|
+
['later', 'agents/roadmaps/later'],
|
|
149
|
+
['stubs', 'agents/roadmaps/stubs'],
|
|
150
|
+
['archive', 'agents/roadmaps/archive'],
|
|
151
|
+
];
|
|
152
|
+
|
|
153
|
+
const NON_ROADMAP_NAMES: ReadonlySet<string> = new Set([
|
|
154
|
+
'template.md',
|
|
155
|
+
'README.md',
|
|
156
|
+
'progress.md',
|
|
157
|
+
'roadmaps-progress.md',
|
|
158
|
+
]);
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Parse `gh pr list --json number,title,headRefName,files`.
|
|
162
|
+
*
|
|
163
|
+
* Tolerant on purpose: a `gh` version that omits `files`, or a PR whose file
|
|
164
|
+
* list is truncated, yields an empty set for that PR rather than dropping the
|
|
165
|
+
* PR. Losing the overlap signal for one PR is recoverable; losing the PR from
|
|
166
|
+
* the screen is the failure this whole probe exists to prevent.
|
|
167
|
+
*/
|
|
168
|
+
export function parsePrList(raw: string): PullRequestInfo[] {
|
|
169
|
+
let parsed: unknown;
|
|
170
|
+
try {
|
|
171
|
+
parsed = JSON.parse(raw);
|
|
172
|
+
} catch {
|
|
173
|
+
return [];
|
|
174
|
+
}
|
|
175
|
+
if (!Array.isArray(parsed)) return [];
|
|
176
|
+
const out: PullRequestInfo[] = [];
|
|
177
|
+
for (const item of parsed) {
|
|
178
|
+
if (item === null || typeof item !== 'object') continue;
|
|
179
|
+
const rec = item as Record<string, unknown>;
|
|
180
|
+
const num = rec['number'];
|
|
181
|
+
if (typeof num !== 'number') continue;
|
|
182
|
+
const files: string[] = [];
|
|
183
|
+
const rawFiles = rec['files'];
|
|
184
|
+
if (Array.isArray(rawFiles)) {
|
|
185
|
+
for (const f of rawFiles) {
|
|
186
|
+
if (typeof f === 'string') files.push(f);
|
|
187
|
+
else if (f !== null && typeof f === 'object') {
|
|
188
|
+
const p = (f as Record<string, unknown>)['path'];
|
|
189
|
+
if (typeof p === 'string') files.push(p);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
const oid = rec['headRefOid'];
|
|
194
|
+
out.push({
|
|
195
|
+
number: num,
|
|
196
|
+
title: typeof rec['title'] === 'string' ? rec['title'] : '',
|
|
197
|
+
headRefName: typeof rec['headRefName'] === 'string' ? rec['headRefName'] : '',
|
|
198
|
+
files: files.sort(),
|
|
199
|
+
...(typeof oid === 'string' && oid !== '' ? { headRefOid: oid } : {}),
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
return out.sort((a, b) => a.number - b.number);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Remote branches whose name carries a roadmap slug — the "roadmap tail".
|
|
207
|
+
*
|
|
208
|
+
* The branch axis needs no claim to have been written: a checkout is on disk
|
|
209
|
+
* from the first minute, and a peer that never claimed is invisible to every
|
|
210
|
+
* other axis.
|
|
211
|
+
*/
|
|
212
|
+
export function roadmapTailBranches(
|
|
213
|
+
branches: readonly string[],
|
|
214
|
+
slugs: readonly string[],
|
|
215
|
+
): RoadmapBranch[] {
|
|
216
|
+
const out: RoadmapBranch[] = [];
|
|
217
|
+
for (const b of branches) {
|
|
218
|
+
const ref = b.trim();
|
|
219
|
+
if (ref === '' || ref.endsWith('/HEAD')) continue;
|
|
220
|
+
for (const slug of slugs) {
|
|
221
|
+
if (slug !== '' && ref.includes(slug)) {
|
|
222
|
+
out.push({ branch: ref, slug });
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return out;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const STOPWORDS: ReadonlySet<string> = new Set([
|
|
231
|
+
'road',
|
|
232
|
+
'to',
|
|
233
|
+
'the',
|
|
234
|
+
'and',
|
|
235
|
+
'for',
|
|
236
|
+
'of',
|
|
237
|
+
'a',
|
|
238
|
+
'an',
|
|
239
|
+
'in',
|
|
240
|
+
'on',
|
|
241
|
+
'with',
|
|
242
|
+
'over',
|
|
243
|
+
'from',
|
|
244
|
+
]);
|
|
245
|
+
|
|
246
|
+
/** Keyword set for a slug: kebab segments minus stopwords and 2-char noise. */
|
|
247
|
+
export function slugKeywords(slug: string): string[] {
|
|
248
|
+
const seen = new Set<string>();
|
|
249
|
+
for (const part of slug.toLowerCase().split(/[^a-z0-9]+/)) {
|
|
250
|
+
if (part.length < 3 || STOPWORDS.has(part)) continue;
|
|
251
|
+
seen.add(part);
|
|
252
|
+
}
|
|
253
|
+
return [...seen].sort();
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Sibling roadmaps sharing at least `minMatches` keywords with the subject.
|
|
258
|
+
*
|
|
259
|
+
* This is the axis `/roadmap:create`'s collision check does NOT cover: that
|
|
260
|
+
* check is a recursive `find -iname`, so it already sees `later/`, `stubs/` and
|
|
261
|
+
* `archive/` — lexically. Same topic under a different name passes it.
|
|
262
|
+
*/
|
|
263
|
+
export function keywordHits(
|
|
264
|
+
entries: readonly RoadmapEntry[],
|
|
265
|
+
keywords: readonly string[],
|
|
266
|
+
subjectSlug: string,
|
|
267
|
+
minMatches = 2,
|
|
268
|
+
): KeywordHit[] {
|
|
269
|
+
if (keywords.length === 0) return [];
|
|
270
|
+
const out: KeywordHit[] = [];
|
|
271
|
+
for (const e of entries) {
|
|
272
|
+
if (e.slug === subjectSlug) continue;
|
|
273
|
+
const hay = `${e.slug} ${e.title}`.toLowerCase();
|
|
274
|
+
const matched = keywords.filter((k) => hay.includes(k));
|
|
275
|
+
if (matched.length >= minMatches) {
|
|
276
|
+
out.push({ slug: e.slug, path: e.path, dir: e.dir, matched });
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return out.sort((a, b) => b.matched.length - a.matched.length || a.slug.localeCompare(b.slug));
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Stable digest of everything a refresh decision depends on.
|
|
284
|
+
*
|
|
285
|
+
* `road-to-roadmap-situational-awareness` § 5.1, as re-scoped: the roadmap asked
|
|
286
|
+
* for a `roadmap.context_refresh_cadence` enum and the tree's settings contract
|
|
287
|
+
* refused it — `derivable` is a deletion queue whose size may only fall, and a
|
|
288
|
+
* fixed beat is exactly the flag the contract says a mechanism should replace
|
|
289
|
+
* (`agents/evidence/analysis/situational-awareness-cadence-key-decision.md`).
|
|
290
|
+
* So the trigger is a comparison, not a cadence: re-probe when the world moved.
|
|
291
|
+
*
|
|
292
|
+
* **`origin/main` alone is not enough**, and this is the case a SHA-only trigger
|
|
293
|
+
* misses: a peer pushing to their OWN open PR branch mid-run can add a file that
|
|
294
|
+
* now overlaps this run's owned paths, and `origin/main` has not moved. So the
|
|
295
|
+
* PR head SHAs are in the digest too, at the cost of one extra `gh` field.
|
|
296
|
+
*
|
|
297
|
+
* Not a security hash — a cheap, order-independent digest. It is compared, never
|
|
298
|
+
* parsed, and a collision costs one skipped re-probe rather than a wrong answer.
|
|
299
|
+
*/
|
|
300
|
+
export function contextFingerprint(
|
|
301
|
+
base_sha: string | null,
|
|
302
|
+
prs: readonly PullRequestInfo[],
|
|
303
|
+
): string {
|
|
304
|
+
const parts = [
|
|
305
|
+
`base:${base_sha ?? 'none'}`,
|
|
306
|
+
...prs
|
|
307
|
+
.map((p) => `pr:${p.number}@${p.headRefOid ?? 'unknown'}`)
|
|
308
|
+
.sort((a, b) => a.localeCompare(b)),
|
|
309
|
+
];
|
|
310
|
+
let h1 = 0x811c9dc5;
|
|
311
|
+
let h2 = 0x01000193;
|
|
312
|
+
const joined = parts.join('|');
|
|
313
|
+
for (let i = 0; i < joined.length; i++) {
|
|
314
|
+
const c = joined.charCodeAt(i);
|
|
315
|
+
h1 = Math.imul(h1 ^ c, 0x01000193) >>> 0;
|
|
316
|
+
h2 = Math.imul(h2 + c, 0x85ebca6b) >>> 0;
|
|
317
|
+
}
|
|
318
|
+
return `${h1.toString(16).padStart(8, '0')}${h2.toString(16).padStart(8, '0')}`;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const CITED_PATH_RE = /`([A-Za-z0-9_][A-Za-z0-9_./-]*\/[A-Za-z0-9_./-]+\.[A-Za-z0-9]+)`/g;
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Paths a roadmap cites in backticks — the FALLBACK owned-path set.
|
|
325
|
+
*
|
|
326
|
+
* Deliberately labelled everywhere it is used: backticked paths include
|
|
327
|
+
* examples, evidence pointers and files the roadmap merely reads. It
|
|
328
|
+
* over-reports, and over-reporting resolves toward serial execution, which is
|
|
329
|
+
* the direction the set contract already declares safe.
|
|
330
|
+
*/
|
|
331
|
+
export function citedPaths(text: string): string[] {
|
|
332
|
+
const out = new Set<string>();
|
|
333
|
+
CITED_PATH_RE.lastIndex = 0;
|
|
334
|
+
let m: RegExpExecArray | null;
|
|
335
|
+
while ((m = CITED_PATH_RE.exec(text)) !== null) {
|
|
336
|
+
const p = m[1]!;
|
|
337
|
+
if (p.includes('..')) continue;
|
|
338
|
+
out.add(p);
|
|
339
|
+
}
|
|
340
|
+
return [...out].sort();
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* `(roadmap, open PR)` pairs with a non-empty file intersection.
|
|
345
|
+
*
|
|
346
|
+
* Pure over two maps so the assertion in the test suite pins a committed
|
|
347
|
+
* fixture and never a live PR number — the property D1b showed to be
|
|
348
|
+
* essential, since the live population halved inside six days.
|
|
349
|
+
*/
|
|
350
|
+
export function computeOverlaps(
|
|
351
|
+
owned: ReadonlyMap<string, { paths: readonly string[]; source: OverlapSource }>,
|
|
352
|
+
prs: readonly PullRequestInfo[],
|
|
353
|
+
): OverlapPair[] {
|
|
354
|
+
const out: OverlapPair[] = [];
|
|
355
|
+
for (const [roadmap, set] of [...owned.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
|
|
356
|
+
const mine = new Set(set.paths);
|
|
357
|
+
if (mine.size === 0) continue;
|
|
358
|
+
for (const pr of prs) {
|
|
359
|
+
const shared = pr.files.filter((f) => mine.has(f)).sort();
|
|
360
|
+
if (shared.length > 0) {
|
|
361
|
+
out.push({ roadmap, pr: pr.number, paths: shared, source: set.source });
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return out;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// ---------------------------------------------------------------------------
|
|
369
|
+
// I/O — injected so every test above runs offline.
|
|
370
|
+
// ---------------------------------------------------------------------------
|
|
371
|
+
|
|
372
|
+
export interface ExecResult {
|
|
373
|
+
code: number;
|
|
374
|
+
stdout: string;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
export type Exec = (cmd: string, args: readonly string[]) => ExecResult;
|
|
378
|
+
|
|
379
|
+
export const realExec: Exec = (cmd, args) => {
|
|
380
|
+
try {
|
|
381
|
+
const stdout = execFileSync(cmd, [...args], {
|
|
382
|
+
encoding: 'utf-8',
|
|
383
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
384
|
+
env: gitEnv(),
|
|
385
|
+
timeout: 30_000,
|
|
386
|
+
maxBuffer: 32 * 1024 * 1024,
|
|
387
|
+
});
|
|
388
|
+
return { code: 0, stdout };
|
|
389
|
+
} catch (exc) {
|
|
390
|
+
const e = exc as { status?: number; stdout?: string | Buffer };
|
|
391
|
+
return { code: typeof e.status === 'number' ? e.status : 1, stdout: String(e.stdout ?? '') };
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
function readTitle(text: string, slug: string): string {
|
|
396
|
+
const m = /^#[ \t]+(.+)$/m.exec(text);
|
|
397
|
+
return m ? m[1]!.trim() : slug;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/** Enumerate the four roadmap directories. Non-recursive per directory. */
|
|
401
|
+
export function enumerateRoadmaps(repoRoot: string): RoadmapEntry[] {
|
|
402
|
+
const out: RoadmapEntry[] = [];
|
|
403
|
+
for (const [dir, rel] of ROADMAP_DIRS) {
|
|
404
|
+
const abs = path.join(repoRoot, rel);
|
|
405
|
+
let names: string[];
|
|
406
|
+
try {
|
|
407
|
+
names = fs.readdirSync(abs);
|
|
408
|
+
} catch {
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
for (const name of names.sort()) {
|
|
412
|
+
if (!name.endsWith('.md') || NON_ROADMAP_NAMES.has(name)) continue;
|
|
413
|
+
const full = path.join(abs, name);
|
|
414
|
+
try {
|
|
415
|
+
if (!fs.statSync(full).isFile()) continue;
|
|
416
|
+
} catch {
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
const slug = name.slice(0, -3);
|
|
420
|
+
let text = '';
|
|
421
|
+
try {
|
|
422
|
+
text = fs.readFileSync(full, 'utf-8');
|
|
423
|
+
} catch {
|
|
424
|
+
/* unreadable file still counts as present */
|
|
425
|
+
}
|
|
426
|
+
out.push({ slug, path: path.posix.join(rel, name), title: readTitle(text, slug), dir });
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
return out;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/** `agents/tmp/` file NAMES. Never contents — the directory is private scratch. */
|
|
433
|
+
export function inboxNames(repoRoot: string): string[] {
|
|
434
|
+
try {
|
|
435
|
+
return fs
|
|
436
|
+
.readdirSync(path.join(repoRoot, 'agents/tmp'), { withFileTypes: true })
|
|
437
|
+
.filter((d) => d.isFile() && !d.name.startsWith('.'))
|
|
438
|
+
.map((d) => d.name)
|
|
439
|
+
.sort();
|
|
440
|
+
} catch {
|
|
441
|
+
return [];
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/** Verdict for a step whose cited artefacts were checked against the tree. */
|
|
446
|
+
export type ArtefactVerdict = 'present' | 'unverified';
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Which of a step's cited paths are missing from the tree.
|
|
450
|
+
*
|
|
451
|
+
* `road-to-roadmap-situational-awareness` § 5.5, harvested from a dropped draft:
|
|
452
|
+
* **absence of the file is absence of evidence, not evidence of completion.** The
|
|
453
|
+
* failure mode is specific and quiet — a step cites an artefact, the artefact is
|
|
454
|
+
* gone, and "nothing to do here" reads identically to "already done". One of
|
|
455
|
+
* those is a closed step and the other is a lost one.
|
|
456
|
+
*/
|
|
457
|
+
export function absentCitedPaths(repoRoot: string, paths: readonly string[]): string[] {
|
|
458
|
+
return paths.filter((rel) => !fs.existsSync(path.join(repoRoot, rel))).sort();
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* `unverified` when ANY cited artefact is missing, `present` only when all are.
|
|
463
|
+
*
|
|
464
|
+
* Deliberately not a ratio and not a majority: one missing artefact is enough,
|
|
465
|
+
* because the whole point is that the step's evidence cannot be checked. A step
|
|
466
|
+
* with no cited paths is `present` — there was nothing to check, and inventing a
|
|
467
|
+
* doubt would fire this on every prose step.
|
|
468
|
+
*/
|
|
469
|
+
export function staleArtefactVerdict(
|
|
470
|
+
repoRoot: string,
|
|
471
|
+
paths: readonly string[],
|
|
472
|
+
): { verdict: ArtefactVerdict; absent: string[] } {
|
|
473
|
+
const absent = absentCitedPaths(repoRoot, paths);
|
|
474
|
+
return { verdict: absent.length > 0 ? 'unverified' : 'present', absent };
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
export interface ProbeOptions {
|
|
478
|
+
repoRoot: string;
|
|
479
|
+
roadmap?: string | null;
|
|
480
|
+
exec?: Exec;
|
|
481
|
+
/** Pre-scan owned-path sets, slug → paths. Authoritative where present. */
|
|
482
|
+
ownedPaths?: ReadonlyMap<string, readonly string[]>;
|
|
483
|
+
now?: Date;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/** Run the probe. Never throws; degrades to `network: "unavailable"`. */
|
|
487
|
+
export function probe(opts: ProbeOptions): RoadmapContext {
|
|
488
|
+
const exec = opts.exec ?? realExec;
|
|
489
|
+
const root = opts.repoRoot;
|
|
490
|
+
const subject = opts.roadmap ?? null;
|
|
491
|
+
|
|
492
|
+
// A pruning fetch first: every read below is only as live as the refs.
|
|
493
|
+
const fetched = exec('git', ['-C', root, 'fetch', 'origin', '--prune']);
|
|
494
|
+
|
|
495
|
+
const prRun = exec('gh', [
|
|
496
|
+
'pr',
|
|
497
|
+
'list',
|
|
498
|
+
'--state',
|
|
499
|
+
'open',
|
|
500
|
+
'--limit',
|
|
501
|
+
'100',
|
|
502
|
+
'--json',
|
|
503
|
+
'number,title,headRefName,headRefOid,files',
|
|
504
|
+
]);
|
|
505
|
+
const online = prRun.code === 0 && fetched.code === 0;
|
|
506
|
+
const open_prs = prRun.code === 0 ? parsePrList(prRun.stdout) : [];
|
|
507
|
+
|
|
508
|
+
const baseRun = exec('git', ['-C', root, 'rev-parse', 'origin/main']);
|
|
509
|
+
const base_sha = baseRun.code === 0 ? baseRun.stdout.trim() || null : null;
|
|
510
|
+
|
|
511
|
+
const roadmaps = enumerateRoadmaps(root);
|
|
512
|
+
const activeSlugs = roadmaps.filter((r) => r.dir === 'active').map((r) => r.slug);
|
|
513
|
+
|
|
514
|
+
const branchRun = exec('git', [
|
|
515
|
+
'-C',
|
|
516
|
+
root,
|
|
517
|
+
'for-each-ref',
|
|
518
|
+
'--format=%(refname:short)',
|
|
519
|
+
'refs/remotes/origin',
|
|
520
|
+
]);
|
|
521
|
+
const branches = branchRun.stdout.split('\n').filter((l) => l.trim() !== '');
|
|
522
|
+
const roadmap_branches = roadmapTailBranches(branches, activeSlugs);
|
|
523
|
+
|
|
524
|
+
const sessions = readSessions(root);
|
|
525
|
+
|
|
526
|
+
const inbox_files = inboxNames(root);
|
|
527
|
+
|
|
528
|
+
const hits =
|
|
529
|
+
subject === null ? [] : keywordHits(roadmaps, slugKeywords(subject), subject);
|
|
530
|
+
|
|
531
|
+
// Pre-scan sets, unioned from two sources and NEVER re-derived. The loop's
|
|
532
|
+
// § 3d overlap derivation runs in the model's head; the register is where it
|
|
533
|
+
// becomes readable by anything else — a session that ran
|
|
534
|
+
// `sessions:claim --paths` has published exactly that set. An explicit
|
|
535
|
+
// `--owned-paths` file wins, because a caller that names a set means it.
|
|
536
|
+
const fromRegister = registerOwnedPaths(sessions.records);
|
|
537
|
+
const owned = new Map<string, { paths: readonly string[]; source: OverlapSource }>();
|
|
538
|
+
const subjects = subject === null ? roadmaps.filter((r) => r.dir === 'active') : roadmaps.filter((r) => r.slug === subject);
|
|
539
|
+
for (const r of subjects) {
|
|
540
|
+
const pre = opts.ownedPaths?.get(r.slug) ?? fromRegister.get(r.slug);
|
|
541
|
+
if (pre !== undefined && pre.length > 0) {
|
|
542
|
+
owned.set(r.slug, { paths: pre, source: 'pre-scan' });
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
let text = '';
|
|
546
|
+
try {
|
|
547
|
+
text = fs.readFileSync(path.join(root, r.path), 'utf-8');
|
|
548
|
+
} catch {
|
|
549
|
+
continue;
|
|
550
|
+
}
|
|
551
|
+
owned.set(r.slug, { paths: citedPaths(text), source: 'cited-path' });
|
|
552
|
+
}
|
|
553
|
+
const overlaps = computeOverlaps(owned, open_prs);
|
|
554
|
+
|
|
555
|
+
return {
|
|
556
|
+
generated_at: (opts.now ?? new Date()).toISOString(),
|
|
557
|
+
network: online ? 'live' : 'unavailable',
|
|
558
|
+
roadmap: subject,
|
|
559
|
+
base_sha,
|
|
560
|
+
fingerprint: contextFingerprint(base_sha, open_prs),
|
|
561
|
+
scanned: {
|
|
562
|
+
prs: open_prs.length,
|
|
563
|
+
roadmaps: roadmaps.length,
|
|
564
|
+
remote_branches: branches.length,
|
|
565
|
+
inbox_files: inbox_files.length,
|
|
566
|
+
sessions: sessions.records.length,
|
|
567
|
+
},
|
|
568
|
+
open_prs,
|
|
569
|
+
roadmap_branches,
|
|
570
|
+
sessions,
|
|
571
|
+
inbox_files,
|
|
572
|
+
hits,
|
|
573
|
+
overlaps,
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* The session register, read through the same library `sessions:list` uses.
|
|
579
|
+
*
|
|
580
|
+
* Both axes, because the register alone reports the half that can be silent: a
|
|
581
|
+
* peer that never claimed is visible only as a branch checked out in another
|
|
582
|
+
* worktree.
|
|
583
|
+
*/
|
|
584
|
+
function readSessions(root: string): SessionsView {
|
|
585
|
+
let records: unknown[] = [];
|
|
586
|
+
let other: unknown[] = [];
|
|
587
|
+
try {
|
|
588
|
+
const dir = register_dir(root);
|
|
589
|
+
records = dir === null ? [] : read_live_records(dir, { prune: false });
|
|
590
|
+
} catch {
|
|
591
|
+
/* no register is the normal pre-first-session state, never an error */
|
|
592
|
+
}
|
|
593
|
+
try {
|
|
594
|
+
other = other_worktree_branches_detailed(root).rows;
|
|
595
|
+
} catch {
|
|
596
|
+
/* a git failure costs the branch axis, not the whole report */
|
|
597
|
+
}
|
|
598
|
+
return { records, other_worktree_branches: other };
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* Owned-path sets published by live sessions, slug → paths.
|
|
603
|
+
*
|
|
604
|
+
* This is the "extend before create" half of § 3.3: no second derivation exists
|
|
605
|
+
* anywhere: the set is whatever a session declared through
|
|
606
|
+
* `sessions:claim --paths`, and a slug with no declaration simply is not in the
|
|
607
|
+
* map, so the caller falls through to the labelled cited-path heuristic.
|
|
608
|
+
*/
|
|
609
|
+
export function registerOwnedPaths(records: readonly unknown[]): Map<string, string[]> {
|
|
610
|
+
const out = new Map<string, string[]>();
|
|
611
|
+
for (const r of records) {
|
|
612
|
+
if (r === null || typeof r !== 'object') continue;
|
|
613
|
+
const rec = r as Record<string, unknown>;
|
|
614
|
+
const slug = rec['roadmap_slug'];
|
|
615
|
+
const paths = rec['owned_paths'];
|
|
616
|
+
if (typeof slug !== 'string' || slug === '' || !Array.isArray(paths)) continue;
|
|
617
|
+
const clean = paths.filter((x): x is string => typeof x === 'string' && x.trim() !== '');
|
|
618
|
+
if (clean.length === 0) continue;
|
|
619
|
+
const prior = out.get(slug) ?? [];
|
|
620
|
+
out.set(slug, [...new Set([...prior, ...clean])].sort());
|
|
621
|
+
}
|
|
622
|
+
return out;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/** The four relations rule 18 allows. Closed set — an unknown value reds the lint. */
|
|
626
|
+
export const RELATES_RELATIONS = ['extends', 'supersedes', 'depends', 'disjoint'] as const;
|
|
627
|
+
export type RelatesRelation = (typeof RELATES_RELATIONS)[number];
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
* The `relates: []` frontmatter line for a probe that found nothing.
|
|
631
|
+
*
|
|
632
|
+
* Fully determined, which is why it is emitted rather than asked about: zero
|
|
633
|
+
* hits leaves nothing for a numbered question to be about. The `scanned:` count
|
|
634
|
+
* rides along because that is what separates "somebody looked and found
|
|
635
|
+
* nothing" from "nobody looked" — the whole absent-versus-empty distinction
|
|
636
|
+
* template rule 18 rests on.
|
|
637
|
+
*/
|
|
638
|
+
export function emptyRelatesBlock(scanned: number): string {
|
|
639
|
+
return `relates: [] # scanned: ${scanned} roadmap file(s), 0 sibling hits`;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* The `relates:` frontmatter block for a probe that found hits.
|
|
644
|
+
*
|
|
645
|
+
* One row per hit. The RELATION is not inferred: `answers` carries the choice
|
|
646
|
+
* for each slug, because "extends" and "supersedes" are the same lexical
|
|
647
|
+
* evidence and opposite decisions — guessing would manufacture the reflex-empty
|
|
648
|
+
* failure in a louder form. A hit with no answer is emitted with its own note
|
|
649
|
+
* saying so rather than silently dropped, so an unanswered question cannot
|
|
650
|
+
* disappear into a clean-looking block.
|
|
651
|
+
*/
|
|
652
|
+
export function relatesRowsFromHits(
|
|
653
|
+
hits: readonly KeywordHit[],
|
|
654
|
+
answers: ReadonlyMap<string, RelatesRelation>,
|
|
655
|
+
): string {
|
|
656
|
+
if (hits.length === 0) return 'relates: []';
|
|
657
|
+
const lines = ['relates:'];
|
|
658
|
+
for (const h of hits) {
|
|
659
|
+
const rel = answers.get(h.slug);
|
|
660
|
+
lines.push(` - slug: ${h.slug}`);
|
|
661
|
+
lines.push(` relation: ${rel ?? 'disjoint'}`);
|
|
662
|
+
lines.push(
|
|
663
|
+
rel === undefined
|
|
664
|
+
? ` note: "UNANSWERED — probe hit in ${h.dir}/ on [${h.matched.join(' ')}]; confirm the relation"`
|
|
665
|
+
: ` note: "probe hit in ${h.dir}/ on [${h.matched.join(' ')}]"`,
|
|
666
|
+
);
|
|
667
|
+
}
|
|
668
|
+
return lines.join('\n');
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
/** The human report. One compact block — a refresh runs at every phase boundary. */
|
|
672
|
+
export function renderText(ctx: RoadmapContext): string {
|
|
673
|
+
const L: string[] = [];
|
|
674
|
+
L.push(`roadmap:context — ${ctx.generated_at}${ctx.roadmap ? ` · subject: ${ctx.roadmap}` : ''}`);
|
|
675
|
+
L.push('');
|
|
676
|
+
if (ctx.network === 'unavailable') {
|
|
677
|
+
L.push('scanned: 0 PRs (network unavailable)');
|
|
678
|
+
} else {
|
|
679
|
+
L.push(`scanned: ${ctx.scanned.prs} PRs`);
|
|
680
|
+
}
|
|
681
|
+
L.push(`scanned: ${ctx.scanned.roadmaps} roadmap file(s) across active/later/stubs/archive`);
|
|
682
|
+
L.push(`scanned: ${ctx.scanned.remote_branches} remote branch(es)`);
|
|
683
|
+
L.push(`scanned: ${ctx.scanned.sessions} live session record(s)`);
|
|
684
|
+
L.push(`scanned: ${ctx.scanned.inbox_files} inbox file name(s) in agents/tmp/`);
|
|
685
|
+
L.push('');
|
|
686
|
+
L.push('open PRs:');
|
|
687
|
+
if (ctx.open_prs.length === 0) L.push(' (none)');
|
|
688
|
+
for (const pr of ctx.open_prs) {
|
|
689
|
+
L.push(` #${pr.number} ${pr.title}`);
|
|
690
|
+
L.push(` head: ${pr.headRefName} · ${pr.files.length} changed file(s)`);
|
|
691
|
+
}
|
|
692
|
+
L.push('');
|
|
693
|
+
L.push('remote branches carrying a roadmap slug:');
|
|
694
|
+
if (ctx.roadmap_branches.length === 0) L.push(' (none)');
|
|
695
|
+
for (const b of ctx.roadmap_branches) L.push(` ${b.branch} → ${b.slug}`);
|
|
696
|
+
L.push('');
|
|
697
|
+
L.push('roadmap ↔ open-PR file overlap:');
|
|
698
|
+
if (ctx.overlaps.length === 0) L.push(' (none)');
|
|
699
|
+
for (const o of ctx.overlaps) {
|
|
700
|
+
L.push(` ${o.roadmap} × #${o.pr} [${o.source}] ${o.paths.length} path(s)`);
|
|
701
|
+
for (const p of o.paths.slice(0, 5)) L.push(` ${p}`);
|
|
702
|
+
}
|
|
703
|
+
L.push('');
|
|
704
|
+
L.push('sibling roadmaps on the same topic (slug/title keywords):');
|
|
705
|
+
if (ctx.hits.length === 0) L.push(' (none)');
|
|
706
|
+
for (const h of ctx.hits) L.push(` ${h.dir.padEnd(7)} ${h.slug} [${h.matched.join(' ')}]`);
|
|
707
|
+
L.push('');
|
|
708
|
+
L.push('inbox notes (names only, never contents):');
|
|
709
|
+
if (ctx.inbox_files.length === 0) L.push(' (none)');
|
|
710
|
+
for (const n of ctx.inbox_files) L.push(` agents/tmp/${n}`);
|
|
711
|
+
L.push(`context fingerprint: ${ctx.fingerprint} (base ${ctx.base_sha ?? 'unknown'})`);
|
|
712
|
+
L.push('A refresh re-probes when this value differs from the one the run holds.');
|
|
713
|
+
L.push('');
|
|
714
|
+
L.push('This probe is deterministic once invoked; the invocation is model-carried.');
|
|
715
|
+
return `${L.join('\n')}\n`;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
function readOwnedPathsFile(p: string): Map<string, string[]> {
|
|
719
|
+
const out = new Map<string, string[]>();
|
|
720
|
+
try {
|
|
721
|
+
const parsed = JSON.parse(fs.readFileSync(p, 'utf-8')) as Record<string, unknown>;
|
|
722
|
+
for (const [slug, v] of Object.entries(parsed)) {
|
|
723
|
+
if (Array.isArray(v)) out.set(slug, v.filter((x): x is string => typeof x === 'string'));
|
|
724
|
+
}
|
|
725
|
+
} catch {
|
|
726
|
+
/* an unreadable pre-scan file falls back to the cited-path heuristic */
|
|
727
|
+
}
|
|
728
|
+
return out;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
export function main(argv: string[] = process.argv.slice(2)): number {
|
|
732
|
+
if (argv.includes('--help') || argv.includes('-h')) {
|
|
733
|
+
process.stdout.write(
|
|
734
|
+
'usage: agent-config roadmap:context [--roadmap <slug>] [--owned-paths <file.json>] [--json] [--relates] [--fingerprint]\n',
|
|
735
|
+
);
|
|
736
|
+
return 0;
|
|
737
|
+
}
|
|
738
|
+
const at = (flag: string): string | null => {
|
|
739
|
+
const i = argv.indexOf(flag);
|
|
740
|
+
return i >= 0 && argv[i + 1] !== undefined ? argv[i + 1]! : null;
|
|
741
|
+
};
|
|
742
|
+
const root = at('--root') ?? process.cwd();
|
|
743
|
+
const ownedFile = at('--owned-paths');
|
|
744
|
+
const ctx = probe({
|
|
745
|
+
repoRoot: root,
|
|
746
|
+
roadmap: at('--roadmap'),
|
|
747
|
+
...(ownedFile !== null ? { ownedPaths: readOwnedPathsFile(ownedFile) } : {}),
|
|
748
|
+
});
|
|
749
|
+
if (argv.includes('--fingerprint')) {
|
|
750
|
+
// Just the digest, for the loop's phase-boundary comparison and for the
|
|
751
|
+
// resume checkpoint's `context_fingerprint` (§ 5.6 — same value).
|
|
752
|
+
process.stdout.write(`${ctx.fingerprint}\n`);
|
|
753
|
+
return 0;
|
|
754
|
+
}
|
|
755
|
+
if (argv.includes('--relates')) {
|
|
756
|
+
// The frontmatter block, ready to paste. Zero hits is fully determined
|
|
757
|
+
// and printed as-is; hits print with every relation UNANSWERED, because
|
|
758
|
+
// that is a question for the author and not something to infer.
|
|
759
|
+
process.stdout.write(
|
|
760
|
+
ctx.hits.length === 0
|
|
761
|
+
? `${emptyRelatesBlock(ctx.scanned.roadmaps)}\n`
|
|
762
|
+
: `${relatesRowsFromHits(ctx.hits, new Map())}\n`,
|
|
763
|
+
);
|
|
764
|
+
return 0;
|
|
765
|
+
}
|
|
766
|
+
process.stdout.write(
|
|
767
|
+
argv.includes('--json') ? `${JSON.stringify(ctx, null, 2)}\n` : renderText(ctx),
|
|
768
|
+
);
|
|
769
|
+
return 0;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
// Bundle-safety: never auto-run when inlined into an esbuild bundle, where
|
|
773
|
+
// every module shares the bundle's `import.meta.url` (see sessions_cli.ts).
|
|
774
|
+
declare const __AGENT_CONFIG_BUNDLE__: boolean | undefined;
|
|
775
|
+
const _bundled = typeof __AGENT_CONFIG_BUNDLE__ !== 'undefined' && __AGENT_CONFIG_BUNDLE__;
|
|
776
|
+
if (!_bundled && fileURLToPath(import.meta.url) === path.resolve(process.argv[1] ?? '')) {
|
|
777
|
+
process.exit(main());
|
|
778
|
+
}
|