@event4u/agent-config 14.10.0 → 14.12.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 +161 -208
- 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/evidence-discipline.md +26 -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/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 +45 -1
- package/dist/agent-src/skills/code-review/SKILL.md +75 -5
- package/dist/agent-src/skills/command-routing/SKILL.md +6 -0
- package/dist/agent-src/skills/command-writing/SKILL.md +35 -0
- package/dist/agent-src/skills/condense-memory/SKILL.md +3 -0
- package/dist/agent-src/skills/context-authoring/SKILL.md +1 -1
- package/dist/agent-src/skills/conventional-commits-writing/SKILL.md +3 -0
- 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 +79 -1
- 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/estimate-ticket/SKILL.md +3 -0
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +28 -4
- package/dist/agent-src/skills/fe-design/SKILL.md +74 -41
- 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/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/scripts/check_package_surface.ts +270 -0
- package/dist/agent-src/skills/laravel-migration/SKILL.md +3 -0
- package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +3 -0
- 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/monorepo-workspace/SKILL.md +149 -0
- package/dist/agent-src/skills/motion-choreographer/SKILL.md +6 -0
- package/dist/agent-src/skills/multi-tenancy/SKILL.md +4 -0
- package/dist/agent-src/skills/operational-readiness/SKILL.md +184 -0
- package/dist/agent-src/skills/override-management/SKILL.md +3 -0
- 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/project-analysis-core/SKILL.md +23 -0
- package/dist/agent-src/skills/prompt-engineering-image/SKILL.md +3 -0
- package/dist/agent-src/skills/quality-tools/SKILL.md +5 -0
- package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +81 -33
- 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 +2 -2
- 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/scene-blueprint.schema.yaml +26 -0
- package/dist/agent-src/skills/secrets-management/SKILL.md +4 -0
- package/dist/agent-src/skills/server-hardening/SKILL.md +159 -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/skill-writing/references/procedure.md +24 -2
- package/dist/agent-src/skills/spreadsheet-authoring/SKILL.md +3 -0
- package/dist/agent-src/skills/storybook-workshop/SKILL.md +203 -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 +17 -1
- package/dist/agent-src/skills/upstream-contribute/SKILL.md +3 -0
- 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/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/uiRender.js +206 -0
- package/dist/cli/main.js +22 -1
- package/dist/cli/mcp/content.js +64 -1
- package/dist/cli/mcp/dispatch.js +175 -10
- package/dist/cli/registry.js +3 -0
- 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-YM4GZDJG.js → chunk-JCW7ZLHK.js} +10 -0
- package/dist/cli-delegate/{chunk-H4VIVD4C.js → chunk-JJA2QEWA.js} +413 -391
- package/dist/cli-delegate/{chunk-PLSG6UL5.js → chunk-K4FFODTS.js} +341 -204
- 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 +362 -127
- 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 +2575 -1472
- package/dist/install/install.mjs +800 -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/scoped_projection.js +84 -0
- package/dist/scripts/_lib/ui_surface.js +89 -0
- package/dist/scripts/condense.js +6 -10
- package/dist/scripts/project_thin_rules.js +40 -2
- package/dist/server/schemas/settings.js +6 -0
- package/dist/shared/skillRanking.js +159 -0
- package/docs/contracts/provider-lifecycle.md +47 -18
- package/docs/contracts/settings-classes.md +35 -5
- package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +49 -0
- package/docs/guidelines/agent-infra/downstream-changes-mechanics.md +91 -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/code-clarity.md +21 -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/docs/guidelines/redundancy-taxonomy.md +180 -0
- package/llms.txt +50 -39
- package/package.json +6 -2
- 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 +44 -0
- package/src/config/assurance-capability-registry.json +223 -0
- package/src/config/assurance-threshold-budget.json +65 -0
- package/src/config/budgets.yml +19 -0
- package/src/config/ci-local-parity.yml +29 -0
- package/src/config/discovery/prompt-patterns.yml +56 -0
- package/src/config/discovery/unassigned-artefacts.yml +2 -0
- package/src/config/dispatch-economy-metrics.json +4 -2
- package/src/config/estate-count-budget.json +8 -3
- package/src/config/evaluator-budgets.json +14 -14
- package/src/config/gate-coverage.yml +392 -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 +39 -10
- package/src/config/preamble-payload-budget.json +97 -71
- package/src/config/quorum-attendance-budget.json +12 -4
- package/src/config/routing-coverage-seed.json +29 -0
- 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/config/suggestion-capture.json +28 -0
- package/src/config/surface-matrix.yml +21 -2
- package/src/config/web-launch-readiness.json +188 -0
- 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/gate_baseline.ts +150 -1
- 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/skill_estate.ts +115 -0
- package/src/scripts/_lib/transcript_reads.ts +207 -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/argument_exhaustion.ts +130 -0
- 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/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_bundle_path_leakage.ts +162 -7
- package/src/scripts/check_command_examples.ts +557 -0
- 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_estate_count.ts +122 -3
- 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_preamble_payload_budget.ts +30 -3
- 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_routing_coverage.ts +352 -0
- package/src/scripts/check_score_contract.ts +505 -0
- package/src/scripts/check_skill_admissions.ts +514 -0
- 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/check_web_launch_readiness.ts +705 -0
- 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/description_route_check.ts +494 -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/grade_target_readiness.ts +478 -0
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +124 -16
- package/src/scripts/hooks/concern_registry.ts +6 -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/hooks/suggestion_capture_hook.ts +368 -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 +56 -5
- 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_skill_trigger_corpus.ts +349 -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/pack_install_smoke.ts +363 -0
- package/src/scripts/pr_conflict_census.ts +187 -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/report_pack_delta.ts +204 -0
- package/src/scripts/roadmap_context.ts +778 -0
- package/src/scripts/schemas/fidelity-measurement-sheet.schema.json +169 -0
- package/src/scripts/schemas/rule.schema.json +24 -2
- package/src/scripts/schemas/skill.schema.json +39 -0
- package/src/scripts/schemas/ui-authority.schema.json +114 -0
- package/src/scripts/select_analysis_mode.ts +112 -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/sync_pr_branch.ts +40 -1
- 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 +10 -0
- package/src/shared/skillRanking.ts +183 -0
- package/dist/agent-src/skills/accessibility-auditor/evals/evals.json +0 -15
- package/dist/agent-src/skills/agent-security-review/evals/triggers.json +0 -52
- package/dist/agent-src/skills/ai-code-blindspots/evals/triggers.json +0 -52
- package/dist/agent-src/skills/analysis-skill-router/evals/evals.json +0 -13
- package/dist/agent-src/skills/brand/evals/triggers.json +0 -17
- package/dist/agent-src/skills/brand-asset-generation/evals/triggers.json +0 -17
- package/dist/agent-src/skills/brand-audit/evals/triggers.json +0 -17
- package/dist/agent-src/skills/brand-identity/evals/triggers.json +0 -17
- package/dist/agent-src/skills/brand-strategy/evals/triggers.json +0 -17
- package/dist/agent-src/skills/brand-to-tokens/evals/triggers.json +0 -17
- package/dist/agent-src/skills/bug-analyzer/evals/evals.json +0 -13
- package/dist/agent-src/skills/canvas-design/evals/triggers.json +0 -17
- package/dist/agent-src/skills/character-consistency/evals/evals.json +0 -16
- package/dist/agent-src/skills/code-intelligence/evals/triggers.json +0 -52
- package/dist/agent-src/skills/code-review/evals/evals.json +0 -100
- package/dist/agent-src/skills/command-routing/evals/evals.json +0 -14
- package/dist/agent-src/skills/complexity-first-planning/evals/triggers.json +0 -17
- package/dist/agent-src/skills/contract-review/evals/triggers.json +0 -17
- package/dist/agent-src/skills/conventional-commits-writing/evals/evals.json +0 -15
- package/dist/agent-src/skills/corpus-grounding/evals/triggers.json +0 -17
- package/dist/agent-src/skills/dashboard-design/evals/evals.json +0 -16
- package/dist/agent-src/skills/dcf-modeling/evals/domain-truth.json +0 -29
- package/dist/agent-src/skills/dcf-modeling/evals/evals.json +0 -20
- package/dist/agent-src/skills/decision-record/evals/evals.json +0 -20
- package/dist/agent-src/skills/decision-record/evals/triggers.json +0 -87
- package/dist/agent-src/skills/design-intelligence/evals/evals.json +0 -14
- package/dist/agent-src/skills/design-intelligence/evals/triggers.json +0 -17
- package/dist/agent-src/skills/design-system-capture/evals/evals.json +0 -15
- package/dist/agent-src/skills/design-tokens/evals/triggers.json +0 -17
- package/dist/agent-src/skills/design-variations/evals/triggers.json +0 -17
- package/dist/agent-src/skills/developer-like-execution/evals/evals.json +0 -16
- package/dist/agent-src/skills/doc-coauthoring/evals/evals.json +0 -16
- package/dist/agent-src/skills/doc-coauthoring/evals/triggers.json +0 -17
- package/dist/agent-src/skills/docx-authoring/evals/triggers.json +0 -52
- package/dist/agent-src/skills/dpa-review/evals/triggers.json +0 -17
- package/dist/agent-src/skills/editorial-calendar/evals/domain-truth.json +0 -16
- package/dist/agent-src/skills/editorial-calendar/evals/evals.json +0 -16
- package/dist/agent-src/skills/eloquent/evals/triggers.json +0 -17
- package/dist/agent-src/skills/estimate-ticket/evals/evals.json +0 -16
- package/dist/agent-src/skills/estimate-ticket/evals/output-schema.yml +0 -20
- package/dist/agent-src/skills/estimate-ticket/evals/triggers.json +0 -19
- package/dist/agent-src/skills/evaluate-llm-feature/evals/triggers.json +0 -52
- package/dist/agent-src/skills/existing-ui-audit/evals/triggers.json +0 -47
- package/dist/agent-src/skills/experiment-loop/evals/output-schema.yml +0 -27
- package/dist/agent-src/skills/experiment-loop/evals/triggers.json +0 -17
- package/dist/agent-src/skills/fe-design/evals/triggers.json +0 -55
- package/dist/agent-src/skills/forecasting/evals/domain-truth.json +0 -29
- package/dist/agent-src/skills/forecasting/evals/evals.json +0 -20
- package/dist/agent-src/skills/forensics-report/evals/triggers.json +0 -17
- package/dist/agent-src/skills/frontend-render-security/evals/triggers.json +0 -52
- package/dist/agent-src/skills/fundraising-narrative/evals/domain-truth.json +0 -16
- package/dist/agent-src/skills/fundraising-narrative/evals/evals.json +0 -16
- package/dist/agent-src/skills/gated-reach/evals/triggers.json +0 -72
- package/dist/agent-src/skills/github-ci/evals/evals.json +0 -25
- package/dist/agent-src/skills/history-design/evals/triggers.json +0 -17
- package/dist/agent-src/skills/html-deck/evals/triggers.json +0 -17
- package/dist/agent-src/skills/humanizer/evals/strip_fixtures.json +0 -57
- package/dist/agent-src/skills/humanizer/evals/triggers.json +0 -17
- package/dist/agent-src/skills/iconography/evals/triggers.json +0 -17
- package/dist/agent-src/skills/image-analyser/evals/triggers.json +0 -17
- package/dist/agent-src/skills/image-creator/evals/triggers.json +0 -17
- package/dist/agent-src/skills/image-editing/evals/triggers.json +0 -17
- package/dist/agent-src/skills/image-generation/evals/triggers.json +0 -17
- package/dist/agent-src/skills/image-provider-routing/evals/triggers.json +0 -17
- package/dist/agent-src/skills/incident-commander/evals/domain-truth.json +0 -16
- package/dist/agent-src/skills/incident-commander/evals/evals.json +0 -20
- package/dist/agent-src/skills/judge-artifact-completeness/evals/triggers.json +0 -17
- package/dist/agent-src/skills/judge-injection-defense/evals/triggers.json +0 -17
- package/dist/agent-src/skills/judge-synthesis/evals/triggers.json +0 -17
- package/dist/agent-src/skills/launch-readiness/evals/evals.json +0 -21
- package/dist/agent-src/skills/learning-tutor/evals/triggers.json +0 -52
- package/dist/agent-src/skills/legal-intake-triage/evals/triggers.json +0 -17
- package/dist/agent-src/skills/legal-practice-profile/evals/triggers.json +0 -17
- package/dist/agent-src/skills/license-compliance-audit/evals/triggers.json +0 -52
- package/dist/agent-src/skills/license-compliance-borrow-check/evals/triggers.json +0 -52
- package/dist/agent-src/skills/license-compliance-credits/evals/triggers.json +0 -52
- package/dist/agent-src/skills/llm-provider-knowledge/evals/triggers.json +0 -20
- package/dist/agent-src/skills/logging-monitoring/evals/evals.json +0 -16
- package/dist/agent-src/skills/logo-generation/evals/triggers.json +0 -17
- package/dist/agent-src/skills/messaging-architecture/evals/domain-truth.json +0 -16
- package/dist/agent-src/skills/messaging-architecture/evals/evals.json +0 -16
- package/dist/agent-src/skills/nda-triage/evals/triggers.json +0 -17
- package/dist/agent-src/skills/pdf-tools/evals/triggers.json +0 -52
- package/dist/agent-src/skills/perf-feedback-craft/evals/evals.json +0 -16
- package/dist/agent-src/skills/persona-improvement/evals/triggers.json +0 -52
- package/dist/agent-src/skills/php-coder/evals/triggers.json +0 -17
- package/dist/agent-src/skills/prediction-pool-optimizer/evals/triggers.json +0 -21
- package/dist/agent-src/skills/prompt-engineering-image/evals/triggers.json +0 -17
- package/dist/agent-src/skills/prompt-engineering-patterns/evals/evals.json +0 -16
- package/dist/agent-src/skills/prompt-validator/evals/triggers.json +0 -17
- package/dist/agent-src/skills/quality-tools/evals/evals.json +0 -15
- package/dist/agent-src/skills/reasoning-orchestrator/evals/triggers.json +0 -17
- package/dist/agent-src/skills/refine-prompt/evals/evals.json +0 -16
- package/dist/agent-src/skills/refine-ticket/evals/evals.json +0 -16
- package/dist/agent-src/skills/refine-ticket/evals/output-schema.yml +0 -16
- package/dist/agent-src/skills/refine-ticket/evals/triggers.json +0 -17
- package/dist/agent-src/skills/release-comms/evals/evals.json +0 -16
- package/dist/agent-src/skills/rice-prioritization/evals/evals.json +0 -20
- package/dist/agent-src/skills/runway-cognition/evals/domain-truth.json +0 -29
- package/dist/agent-src/skills/runway-cognition/evals/evals.json +0 -20
- package/dist/agent-src/skills/scenario-modeling/evals/domain-truth.json +0 -29
- package/dist/agent-src/skills/scenario-modeling/evals/evals.json +0 -20
- package/dist/agent-src/skills/schema-review/evals/triggers.json +0 -17
- package/dist/agent-src/skills/screenshot-hygiene/evals/triggers.json +0 -52
- package/dist/agent-src/skills/security-maturity-assessment/evals/triggers.json +0 -52
- package/dist/agent-src/skills/skill-writing/evals/triggers.json +0 -17
- package/dist/agent-src/skills/spreadsheet-authoring/evals/triggers.json +0 -47
- package/dist/agent-src/skills/subagent-orchestration/evals/evals.json +0 -60
- package/dist/agent-src/skills/supply-chain-intake/evals/triggers.json +0 -52
- package/dist/agent-src/skills/systematic-debugging/evals/evals.json +0 -20
- package/dist/agent-src/skills/test-case-discovery/evals/triggers.json +0 -52
- package/dist/agent-src/skills/test-driven-development/evals/evals.json +0 -16
- package/dist/agent-src/skills/threat-modeling/evals/evals.json +0 -21
- package/dist/agent-src/skills/typography-system/evals/evals.json +0 -14
- package/dist/agent-src/skills/typography-system/evals/triggers.json +0 -17
- package/dist/agent-src/skills/unit-economics-modeling/evals/domain-truth.json +0 -40
- package/dist/agent-src/skills/unit-economics-modeling/evals/evals.json +0 -24
- package/dist/agent-src/skills/verify-completion-evidence/evals/evals.json +0 -16
- package/dist/agent-src/skills/verify-repair-loop/evals/output-schema.yml +0 -20
- package/dist/agent-src/skills/verify-repair-loop/evals/triggers.json +0 -17
- package/dist/agent-src/skills/vision-articulation/evals/evals.json +0 -16
- package/dist/agent-src/skills/voice-and-tone-design/evals/evals.json +0 -16
- package/dist/agent-src/skills/wireframe/evals/triggers.json +0 -17
- package/dist/agent-src/skills/worktree-lifecycle/evals/evals.json +0 -38
- package/dist/agent-src/skills/worktree-lifecycle/evals/triggers.json +0 -52
- package/dist/cli/agent-config.js.map +0 -1
- package/dist/cli/bash/runBash.js.map +0 -1
- package/dist/cli/commands/commands.js.map +0 -1
- package/dist/cli/commands/config.js.map +0 -1
- package/dist/cli/commands/doctorShell.js.map +0 -1
- package/dist/cli/commands/mcpServer.js.map +0 -1
- package/dist/cli/commands/packs.js.map +0 -1
- package/dist/cli/commands/profiles.js.map +0 -1
- package/dist/cli/commands/recordTriggerEval.js.map +0 -1
- package/dist/cli/commands/rtkDetect.js.map +0 -1
- package/dist/cli/commands/settings.js.map +0 -1
- package/dist/cli/commands/uiServe.js.map +0 -1
- package/dist/cli/commands/versions.js.map +0 -1
- package/dist/cli/commands/workspaces.js.map +0 -1
- package/dist/cli/configRoot.js.map +0 -1
- package/dist/cli/discovery/loadManifest.js.map +0 -1
- package/dist/cli/firstRunNotice.js.map +0 -1
- package/dist/cli/help.js.map +0 -1
- package/dist/cli/initRouting.js.map +0 -1
- package/dist/cli/log/logger.js.map +0 -1
- package/dist/cli/main.js.map +0 -1
- package/dist/cli/mcp/content.js.map +0 -1
- package/dist/cli/mcp/dispatch.js.map +0 -1
- package/dist/cli/mcp/stdio.js.map +0 -1
- package/dist/cli/paths.js.map +0 -1
- package/dist/cli/python/knowledge_ingest.js.map +0 -1
- package/dist/cli/python/workspace_analytics.js.map +0 -1
- package/dist/cli/python/workspace_crypto.js.map +0 -1
- package/dist/cli/python/workspace_documents.js.map +0 -1
- package/dist/cli/python/workspace_drive.js.map +0 -1
- package/dist/cli/python/workspace_drive_health.js.map +0 -1
- package/dist/cli/python/workspace_explain.js.map +0 -1
- package/dist/cli/python/workspace_hosts.js.map +0 -1
- package/dist/cli/python/workspace_inbox.js.map +0 -1
- package/dist/cli/python/workspace_render.js.map +0 -1
- package/dist/cli/python/workspace_roles.js.map +0 -1
- package/dist/cli/python/workspace_secrets.js.map +0 -1
- package/dist/cli/python/workspace_sessions.js.map +0 -1
- package/dist/cli/python/workspace_skills.js.map +0 -1
- package/dist/cli/registry.js.map +0 -1
- package/dist/install/agentSwitchDetection.js.map +0 -1
- package/dist/install/agentSwitchProfile.js.map +0 -1
- package/dist/install/atomic.js.map +0 -1
- package/dist/install/claudePathsPlan.js.map +0 -1
- package/dist/install/conflict.js.map +0 -1
- package/dist/install/detect.js.map +0 -1
- package/dist/install/emit_host_rules_cli.js.map +0 -1
- package/dist/install/globalRuleLayers.js.map +0 -1
- package/dist/install/hostLayerFingerprint.js.map +0 -1
- package/dist/install/partitionEligibility.js.map +0 -1
- package/dist/install/paths.js.map +0 -1
- package/dist/install/plan.js.map +0 -1
- package/dist/install/preflight.js.map +0 -1
- package/dist/install/rtkDetection.js.map +0 -1
- package/dist/install/ruleInScope.js.map +0 -1
- package/dist/install/ruleLayerPartition.js.map +0 -1
- package/dist/install/rule_scope.js.map +0 -1
- package/dist/install/rule_scope_cli.js.map +0 -1
- package/dist/install/selectedTools.js.map +0 -1
- package/dist/install/toolDetection.js.map +0 -1
- package/dist/install/txlog.js.map +0 -1
- package/dist/install/types.js.map +0 -1
- package/dist/install/wizard-plan.js.map +0 -1
- package/dist/install/wizardDismissals.js.map +0 -1
- package/dist/scripts/_lib/agent_settings.js.map +0 -1
- package/dist/scripts/_lib/agent_src.js.map +0 -1
- package/dist/scripts/_lib/claude_builtin_names.js.map +0 -1
- package/dist/scripts/_lib/claude_plugin.js.map +0 -1
- package/dist/scripts/_lib/claude_settings_hooks.js.map +0 -1
- package/dist/scripts/_lib/compile_time_toggles.js.map +0 -1
- package/dist/scripts/_lib/environment_detector.js.map +0 -1
- package/dist/scripts/_lib/host_launch.js.map +0 -1
- package/dist/scripts/_lib/install_layout.js.map +0 -1
- package/dist/scripts/_lib/installed_lock.js.map +0 -1
- package/dist/scripts/_lib/model_tier.js.map +0 -1
- package/dist/scripts/_lib/prune_empty_dirs.js.map +0 -1
- package/dist/scripts/_lib/scoped_projection.js.map +0 -1
- package/dist/scripts/_lib/script_output.js.map +0 -1
- package/dist/scripts/_lib/spawn_env.js.map +0 -1
- package/dist/scripts/_lib/surface_tiers.js.map +0 -1
- package/dist/scripts/_lib/token_count.js.map +0 -1
- package/dist/scripts/_lib/trigger_eval_floors.js.map +0 -1
- package/dist/scripts/_lib/user_global_paths.js.map +0 -1
- package/dist/scripts/ai_council/modes.js.map +0 -1
- package/dist/scripts/ai_council/transport_resolver.js.map +0 -1
- package/dist/scripts/condense.js.map +0 -1
- package/dist/scripts/project_thin_rules.js.map +0 -1
- package/dist/server/app.js.map +0 -1
- package/dist/server/io/atomicMultiWrite.js.map +0 -1
- package/dist/server/io/atomicWrite.js.map +0 -1
- package/dist/server/io/sharedWriteCheck.js.map +0 -1
- package/dist/server/io/substituteTemplate.js.map +0 -1
- package/dist/server/io/yamlIO.js.map +0 -1
- package/dist/server/nicknameResolver.js.map +0 -1
- package/dist/server/port.js.map +0 -1
- package/dist/server/routes/discovery.js.map +0 -1
- package/dist/server/routes/install.js.map +0 -1
- package/dist/server/routes/ping.js.map +0 -1
- package/dist/server/routes/schema.js.map +0 -1
- package/dist/server/routes/settings.js.map +0 -1
- package/dist/server/routes/settingsChanges.js.map +0 -1
- package/dist/server/routes/userMd.js.map +0 -1
- package/dist/server/routes/wizard.js.map +0 -1
- package/dist/server/routes/workspace.js.map +0 -1
- package/dist/server/schemas/settings.js.map +0 -1
- package/dist/server/serverInfo.js.map +0 -1
- package/dist/server/token.js.map +0 -1
- package/dist/server/writeRoot.js.map +0 -1
- package/dist/shared/capabilities.js.map +0 -1
- package/dist/shared/interactiveContext.js.map +0 -1
- package/dist/shared/nicknamePrefill.js.map +0 -1
- package/dist/shared/settingsAsks.js.map +0 -1
- package/dist/shared/settingsCarveOut.js.map +0 -1
- package/dist/shared/settingsClasses.js.map +0 -1
- package/dist/shared/settingsConsent.js.map +0 -1
- package/dist/shared/settingsSurface.js.map +0 -1
- package/dist/shared/userMd/formAdapter.js.map +0 -1
- package/dist/shared/userMd/schema.js.map +0 -1
- package/dist/shared/userMd/utils.js.map +0 -1
- package/dist/ui/assets/index-Cxgy63kz.js.map +0 -1
- package/src/scripts/_lib/adr_frontmatter.test.ts +0 -365
- package/src/scripts/_lib/adversarial_council_gate.test.ts +0 -112
- package/src/scripts/_lib/adversarial_reconcile.test.ts +0 -197
- package/src/scripts/_lib/auto_dispatch.corpus.test.ts +0 -150
- package/src/scripts/_lib/secret_detector.test.ts +0 -108
- package/src/scripts/_lib/secret_detector_adversarial.test.ts +0 -116
- package/src/scripts/_lib/untrusted_content.test.ts +0 -175
- package/src/scripts/check_secret_leak.test.ts +0 -90
- package/src/scripts/design_slop_rules.test.ts +0 -239
- package/src/scripts/self_review_gate.test.ts +0 -296
- package/src/shared/capabilities.test.ts +0 -43
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* Rule-inject — the delivery twin of `skill-route`, with the opposite payload
|
|
4
|
+
* policy (`road-to-trigger-delivered-rule-bodies` Phase 1).
|
|
5
|
+
*
|
|
6
|
+
* BODIES, NEVER POINTERS — and the difference from its twin is deliberate
|
|
7
|
+
* rather than incidental. `skill_route_hook.ts:14` ships "POINTERS, NEVER
|
|
8
|
+
* BODIES" on this same slot, and it is right to: a skill is a thing the agent
|
|
9
|
+
* *invokes*, so naming it is enough. A rule is a thing the agent must already
|
|
10
|
+
* be *under*, and the only datum anyone has on pointer-shaped rule delivery is
|
|
11
|
+
* the 36.2 %-against-48 % run at `docs/CLAIMS.md:188-189`. Whatever that
|
|
12
|
+
* instrument's flaws — and ADR-202 closed it — it points one way.
|
|
13
|
+
*
|
|
14
|
+
* DEFAULT OFF, AND OFF MEANS ZERO BYTES. The concern registers on two slots but
|
|
15
|
+
* returns before reading the router unless `lean_projection.mode: delivery` is
|
|
16
|
+
* set. Under every shipped default it emits nothing, costs no injection budget,
|
|
17
|
+
* and leaves the standing corpus exactly as it is today. That is why its
|
|
18
|
+
* `hook-token-budget.json` row is registered against the per-prompt cap rather
|
|
19
|
+
* than a measured emission: there is no measured emission to register yet.
|
|
20
|
+
*
|
|
21
|
+
* ONE MATCHER, SHARED WITH THE OFFLINE MODEL. Everything about selection,
|
|
22
|
+
* ordering, capping and body loading comes from `_lib/rule_injection.ts`, which
|
|
23
|
+
* `model_rule_injection.ts` also imports. Step 0.5 states the reason in as many
|
|
24
|
+
* words: an experiment whose offline pricing and runtime delivery use different
|
|
25
|
+
* matchers measures nothing.
|
|
26
|
+
*
|
|
27
|
+
* ONCE PER SESSION PER RULE, RE-ARMED ON COMPACTION. A rule's body is injected
|
|
28
|
+
* the first time one of its triggers fires and not again, because the model
|
|
29
|
+
* already has it. Compaction is exactly the event that makes that false, so
|
|
30
|
+
* `pre_compact` clears the seen-set — the same pin-lost shape `language-mirror`
|
|
31
|
+
* uses. State lives under `agents/runtime/state/`, the class
|
|
32
|
+
* `context-hygiene.json` already occupies; no new state convention is created.
|
|
33
|
+
*
|
|
34
|
+
* NEVER BLOCKS. Every failure path returns 0: unreadable stdin, malformed JSON,
|
|
35
|
+
* missing router, unreadable body, unwritable state. The one non-zero exit is
|
|
36
|
+
* the host's advisory context channel (exit 2 + `decision: "warn"`), the same
|
|
37
|
+
* channel `ui-route-nudge` and `code-graph-nudge` already use on `pre_tool_use`
|
|
38
|
+
* with `severity: advisory` — a warn there is an injection, not a deny.
|
|
39
|
+
*
|
|
40
|
+
* HOST BOUNDARY, STATED RATHER THAN IMPLIED. `user_prompt_submit` and
|
|
41
|
+
* `pre_tool_use` are bound on a subset of hosts, and only `claude` honours a
|
|
42
|
+
* deny at all. On a host where neither slot is bound this concern cannot run,
|
|
43
|
+
* which is the whole reason the flip step 2.4 gates on is Claude-only. Run
|
|
44
|
+
* `agent-config hooks:status` for the host you are actually on.
|
|
45
|
+
*
|
|
46
|
+
* SUBAGENTS ARE OUT OF REACH, MEASURED. Neither slot fires inside a spawned
|
|
47
|
+
* child session and the `subagent_start` payload carries no prompt field — see
|
|
48
|
+
* `agents/evidence/investigations/subagent-start-payload-probe.md` (2026-08-23,
|
|
49
|
+
* claude 2.1.241, three verdicts). Delivery is therefore orchestrator-only, and
|
|
50
|
+
* no third binding is added.
|
|
51
|
+
*/
|
|
52
|
+
import * as fs from 'node:fs';
|
|
53
|
+
import * as path from 'node:path';
|
|
54
|
+
import process from 'node:process';
|
|
55
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
56
|
+
|
|
57
|
+
import { hookSectionEnabled, leanProjectionModeRaw } from '../_lib/hook_settings.js';
|
|
58
|
+
import { deliversBodies, normalizeLeanProjectionMode } from '../_lib/lean_projection_mode.js';
|
|
59
|
+
import {
|
|
60
|
+
loadRuleBody,
|
|
61
|
+
loadRouter,
|
|
62
|
+
matchTierRules,
|
|
63
|
+
selectForInjection,
|
|
64
|
+
} from '../_lib/rule_injection.js';
|
|
65
|
+
import { readHookStdin } from './hook_stdin.js';
|
|
66
|
+
|
|
67
|
+
const EXIT_ALLOW = 0;
|
|
68
|
+
const EXIT_WARN = 2;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Per-prompt injection ceiling, in UTF-8 BYTES.
|
|
72
|
+
*
|
|
73
|
+
* DERIVED, NOT PICKED: `model_rule_injection --corpus tests/eval/routing-matrix`
|
|
74
|
+
* measured the matched-body-token distribution over the frozen labelled corpus
|
|
75
|
+
* at p50 1,728 / p90 4,804 / p99 8,248 / max 12,957 exact-BPE tokens, and step
|
|
76
|
+
* 1.1 specifies "the p90 from 0.4, rounded to 500" — 4,804 rounds up to 5,000.
|
|
77
|
+
*
|
|
78
|
+
* BYTES rather than tokens, and the unit change is the point rather than a
|
|
79
|
+
* detail. `_lib/token_count.ts` resolves `js-tiktoken` at module load, so a
|
|
80
|
+
* token-capped concern drags a tokenizer into EVERY hook dispatch on every
|
|
81
|
+
* slot, for a concern that is default-OFF and emits nothing. Measured: 202 ms
|
|
82
|
+
* -> 196 ms on the `pre_tool_use` p95 when the concern is unbound entirely, and
|
|
83
|
+
* the CI latency gate went red on the branch that introduced it while passing
|
|
84
|
+
* on main. So the runtime cap is the same bound in the unit
|
|
85
|
+
* `hook-token-budget.json` already enforces — 5,000 tok at the ~4 bytes/token
|
|
86
|
+
* this corpus measures is 20,480 B, which is exactly this concern's registered
|
|
87
|
+
* row. Cap and budget row are now one number instead of two units.
|
|
88
|
+
*
|
|
89
|
+
* Re-run that command if the corpus or the bodies move; a cap copied from a
|
|
90
|
+
* stale measurement is worse than no cap, because it looks derived.
|
|
91
|
+
*/
|
|
92
|
+
export const CAP_BYTES = 20480;
|
|
93
|
+
|
|
94
|
+
/** Tools whose input names a file this concern can match path triggers against. */
|
|
95
|
+
export const FILE_TOOLS = new Set(['Write', 'Edit', 'NotebookEdit', 'Read', 'MultiEdit']);
|
|
96
|
+
|
|
97
|
+
type JsonObject = Record<string, unknown>;
|
|
98
|
+
|
|
99
|
+
function isObject(v: unknown): v is JsonObject {
|
|
100
|
+
return typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function str(o: JsonObject, ...keys: string[]): string | null {
|
|
104
|
+
for (const k of keys) {
|
|
105
|
+
const v = o[k];
|
|
106
|
+
if (typeof v === 'string' && v !== '') return v;
|
|
107
|
+
}
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Workspace root the envelope points at, falling back to the process cwd. */
|
|
112
|
+
export function workspaceRoot(env: JsonObject): string {
|
|
113
|
+
return str(env, 'workspace', 'cwd', 'project_dir') ?? process.cwd();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ── seen-set state ───────────────────────────────────────────────────────
|
|
117
|
+
|
|
118
|
+
export interface SeenState {
|
|
119
|
+
rules: string[];
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function statePath(root: string, session: string): string {
|
|
123
|
+
const safe = session.replace(/[^A-Za-z0-9_-]/g, '_').slice(0, 80) || 'unknown';
|
|
124
|
+
return path.join(root, 'agents', 'runtime', 'state', 'rule-inject', `${safe}.json`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function readSeen(root: string, session: string): Set<string> {
|
|
128
|
+
try {
|
|
129
|
+
const raw = fs.readFileSync(statePath(root, session), 'utf-8');
|
|
130
|
+
const parsed = JSON.parse(raw) as SeenState;
|
|
131
|
+
return new Set(Array.isArray(parsed.rules) ? parsed.rules.map(String) : []);
|
|
132
|
+
} catch {
|
|
133
|
+
return new Set(); // fresh session, or a file nothing can parse
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function writeSeen(root: string, session: string, seen: Set<string>): void {
|
|
138
|
+
const p = statePath(root, session);
|
|
139
|
+
try {
|
|
140
|
+
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
141
|
+
const payload: SeenState = { rules: [...seen].sort() };
|
|
142
|
+
fs.writeFileSync(`${p}.tmp`, `${JSON.stringify(payload)}\n`, 'utf-8');
|
|
143
|
+
fs.renameSync(`${p}.tmp`, p);
|
|
144
|
+
} catch {
|
|
145
|
+
/* unwritable state must never fail a turn — worst case a re-injection */
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function clearSeen(root: string, session: string): void {
|
|
150
|
+
try {
|
|
151
|
+
fs.rmSync(statePath(root, session), { force: true });
|
|
152
|
+
} catch {
|
|
153
|
+
/* ignore */
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ── payload extraction ───────────────────────────────────────────────────
|
|
158
|
+
|
|
159
|
+
/** The user's prompt, across the shapes the hosts use. */
|
|
160
|
+
export function extractPrompt(payload: JsonObject): string {
|
|
161
|
+
return str(payload, 'prompt', 'user_prompt', 'userPrompt', 'message', 'text') ?? '';
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** The file path a file-touching tool call names, or `null`. */
|
|
165
|
+
export function extractFilePath(payload: JsonObject): string | null {
|
|
166
|
+
const ti = payload['tool_input'] ?? payload['toolInput'] ?? payload['input'];
|
|
167
|
+
if (!isObject(ti)) return null;
|
|
168
|
+
return str(ti, 'file_path', 'path', 'filePath', 'notebook_path');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// ── decision ─────────────────────────────────────────────────────────────
|
|
172
|
+
|
|
173
|
+
export interface Injection {
|
|
174
|
+
rules: string[];
|
|
175
|
+
bytes: number;
|
|
176
|
+
body: string;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Build the injection for one event, or `null` for silence.
|
|
181
|
+
*
|
|
182
|
+
* `prompt` drives keyword / phrase / command triggers; `openFiles` drives
|
|
183
|
+
* `path_prefix` / `file_pattern`. On the tool slot the prompt is deliberately
|
|
184
|
+
* empty, so a file event can only ever fire a path trigger — a tool call is not
|
|
185
|
+
* a restatement of the user's request and must not re-fire keyword rules.
|
|
186
|
+
*/
|
|
187
|
+
export function buildInjection(
|
|
188
|
+
root: string,
|
|
189
|
+
prompt: string,
|
|
190
|
+
openFiles: string[] | null,
|
|
191
|
+
command: string | null,
|
|
192
|
+
seen: Set<string>,
|
|
193
|
+
): Injection | null {
|
|
194
|
+
let router;
|
|
195
|
+
try {
|
|
196
|
+
router = loadRouter(root);
|
|
197
|
+
} catch {
|
|
198
|
+
return null; // no router — nothing to deliver, and never a failure
|
|
199
|
+
}
|
|
200
|
+
const matches = matchTierRules(router, prompt, openFiles, command).filter(
|
|
201
|
+
(m) => !seen.has(m.id),
|
|
202
|
+
);
|
|
203
|
+
if (matches.length === 0) return null;
|
|
204
|
+
const sel = selectForInjection(root, matches, CAP_BYTES);
|
|
205
|
+
const parts: string[] = [];
|
|
206
|
+
const ids: string[] = [];
|
|
207
|
+
for (const m of sel.selected) {
|
|
208
|
+
const body = loadRuleBody(root, m.id);
|
|
209
|
+
if (body === null) continue;
|
|
210
|
+
ids.push(m.id);
|
|
211
|
+
parts.push(`<rule id="${m.id}" tier="${m.tier}">\n${body.trim()}\n</rule>`);
|
|
212
|
+
}
|
|
213
|
+
if (ids.length === 0) return null;
|
|
214
|
+
return { rules: ids, bytes: sel.bytes, body: parts.join('\n\n') };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// ── main ─────────────────────────────────────────────────────────────────
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Whether the settings gate applies.
|
|
221
|
+
*
|
|
222
|
+
* Through the dispatcher the gate is absolute: no `lean_projection.mode:
|
|
223
|
+
* delivery`, no bytes. A DIRECT CLI invocation is a probe by definition — an
|
|
224
|
+
* operator piping an envelope into this file is asking to see what it would
|
|
225
|
+
* deliver — so there the mode defaults to on. `AGENT_CONFIG_REPLAY` re-imposes
|
|
226
|
+
* the gate, which is what keeps `bench_hook_injection` measuring the shipped
|
|
227
|
+
* default (zero bytes) rather than the probe.
|
|
228
|
+
*/
|
|
229
|
+
export function gateOpen(root: string, cliEntry: boolean): boolean {
|
|
230
|
+
if (deliversBodies(normalizeLeanProjectionMode(leanProjectionModeRaw(root)))) return true;
|
|
231
|
+
if (hookSectionEnabled(root, 'rule_inject')) return true;
|
|
232
|
+
return cliEntry && process.env['AGENT_CONFIG_REPLAY'] !== '1';
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export function main(argv: readonly string[] = process.argv.slice(2)): number {
|
|
236
|
+
let eventOverride: string | null = null;
|
|
237
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
238
|
+
if (argv[i] === '--event') {
|
|
239
|
+
i += 1;
|
|
240
|
+
eventOverride = argv[i] ?? null;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
let env: JsonObject = {};
|
|
245
|
+
try {
|
|
246
|
+
const raw = readHookStdin();
|
|
247
|
+
const parsed = raw.trim() ? (JSON.parse(raw) as unknown) : {};
|
|
248
|
+
env = isObject(parsed) ? parsed : {};
|
|
249
|
+
} catch {
|
|
250
|
+
return EXIT_ALLOW; // malformed envelope — never block
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const root = workspaceRoot(env);
|
|
254
|
+
const payload = isObject(env['payload']) ? (env['payload'] as JsonObject) : env;
|
|
255
|
+
const slot = eventOverride ?? str(env, 'event') ?? 'user_prompt_submit';
|
|
256
|
+
const session = str(env, 'session_id', 'sessionId') ?? str(payload, 'session_id', 'sessionId') ?? 'unknown';
|
|
257
|
+
|
|
258
|
+
if (slot === 'pre_compact') {
|
|
259
|
+
clearSeen(root, session);
|
|
260
|
+
return EXIT_ALLOW; // re-arm is silent; the next turn re-injects
|
|
261
|
+
}
|
|
262
|
+
if (slot !== 'user_prompt_submit' && slot !== 'pre_tool_use') return EXIT_ALLOW;
|
|
263
|
+
if (!gateOpen(root, _isCliEntry())) return EXIT_ALLOW;
|
|
264
|
+
|
|
265
|
+
let prompt = '';
|
|
266
|
+
let openFiles: string[] | null = null;
|
|
267
|
+
let command: string | null = null;
|
|
268
|
+
if (slot === 'user_prompt_submit') {
|
|
269
|
+
prompt = extractPrompt(payload);
|
|
270
|
+
if (prompt === '') return EXIT_ALLOW;
|
|
271
|
+
const m = /^\s*(\/[A-Za-z0-9:_-]+)/.exec(prompt);
|
|
272
|
+
command = m ? (m[1] as string) : null;
|
|
273
|
+
} else {
|
|
274
|
+
const tool = str(payload, 'tool_name', 'toolName', 'tool');
|
|
275
|
+
if (tool === null || !FILE_TOOLS.has(tool)) return EXIT_ALLOW;
|
|
276
|
+
const fp = extractFilePath(payload);
|
|
277
|
+
if (fp === null) return EXIT_ALLOW;
|
|
278
|
+
openFiles = [fp];
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const seen = readSeen(root, session);
|
|
282
|
+
const injection = buildInjection(root, prompt, openFiles, command, seen);
|
|
283
|
+
if (injection === null) return EXIT_ALLOW; // silence is the default
|
|
284
|
+
|
|
285
|
+
for (const id of injection.rules) seen.add(id);
|
|
286
|
+
writeSeen(root, session, seen);
|
|
287
|
+
|
|
288
|
+
process.stdout.write(
|
|
289
|
+
`${JSON.stringify({
|
|
290
|
+
decision: 'warn',
|
|
291
|
+
reason: `rule-inject: ${injection.rules.length} rule body/bodies on ${slot} (${injection.bytes} B)`,
|
|
292
|
+
additional_context: injection.body,
|
|
293
|
+
})}\n`,
|
|
294
|
+
);
|
|
295
|
+
return EXIT_WARN;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Bundle-safety: never auto-run when inlined into an esbuild bundle, where
|
|
299
|
+
// every module shares the bundle's `import.meta.url`.
|
|
300
|
+
declare const __AGENT_CONFIG_BUNDLE__: boolean | undefined;
|
|
301
|
+
function _isCliEntry(): boolean {
|
|
302
|
+
if (typeof __AGENT_CONFIG_BUNDLE__ !== 'undefined' && __AGENT_CONFIG_BUNDLE__) return false;
|
|
303
|
+
if (process.argv[1] === undefined) return false;
|
|
304
|
+
const argvUrl = pathToFileURL(path.resolve(process.argv[1])).href;
|
|
305
|
+
if (import.meta.url === argvUrl) return true;
|
|
306
|
+
try {
|
|
307
|
+
return (
|
|
308
|
+
fs.realpathSync(fileURLToPath(import.meta.url)) ===
|
|
309
|
+
fs.realpathSync(path.resolve(process.argv[1]))
|
|
310
|
+
);
|
|
311
|
+
} catch {
|
|
312
|
+
return false;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (_isCliEntry()) {
|
|
317
|
+
process.exit(main());
|
|
318
|
+
}
|
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* Suggestion-block capture — a two-slot latch, counts only.
|
|
4
|
+
*
|
|
5
|
+
* ## What it measures and why it exists
|
|
6
|
+
*
|
|
7
|
+
* `command_suggester/render.ts` emits a numbered-options block with exactly one
|
|
8
|
+
* `Recommendation: N — …` line and the as-is option always last. Nothing records
|
|
9
|
+
* whether those blocks are ever *answered*, so no roadmap can cite a selection
|
|
10
|
+
* rate — and the model-carried alternative is measured and dead:
|
|
11
|
+
* `orchestration_record` captured 1 of 369 dispatches. This instrument exists so
|
|
12
|
+
* that figure is not repeated by construction.
|
|
13
|
+
*
|
|
14
|
+
* ## Two slots, not one — and the probe is why
|
|
15
|
+
*
|
|
16
|
+
* `road-to-suggestion-block-capture` planned a single `user_prompt_submit` hook
|
|
17
|
+
* reading the transcript tail. A live probe
|
|
18
|
+
* (`agents/evidence/analysis/suggestion-capture-probe.md`) found something
|
|
19
|
+
* cheaper: **`stop` carries `last_assistant_message` in the payload**, so the
|
|
20
|
+
* "was a block emitted" half needs no file read at all, and the turn-1 blind
|
|
21
|
+
* spot (`transcript_path` present but the file not yet written) does not arise.
|
|
22
|
+
*
|
|
23
|
+
* stop → detect the signature in `last_assistant_message`,
|
|
24
|
+
* write a latch keyed by `prompt_id`
|
|
25
|
+
* user_prompt_submit → CONSUME the latch, classify `prompt`, append one line
|
|
26
|
+
*
|
|
27
|
+
* ## The misclassification guard, which is the whole correctness argument
|
|
28
|
+
*
|
|
29
|
+
* A bare `1` three turns later must never read as a pick. So the latch is
|
|
30
|
+
* **consumed exactly once**: `user_prompt_submit` deletes it after reading, and
|
|
31
|
+
* a prompt arriving with no latch is `other`, never `option_n`. A latch older
|
|
32
|
+
* than `LATCH_TTL_MS` is `stale_block`. An unparseable latch is `stale_block`
|
|
33
|
+
* too — never a guess.
|
|
34
|
+
*
|
|
35
|
+
* ## Privacy is a property of the schema, not of a scrubber
|
|
36
|
+
*
|
|
37
|
+
* `src/config/suggestion-capture.json` registers five fields and **none can hold
|
|
38
|
+
* free text**: a timestamp, a boolean, an options COUNT, and two closed enums.
|
|
39
|
+
* `option_n` records THAT a numbered option was chosen and out of how many —
|
|
40
|
+
* deliberately not WHICH, because the block's option order is content and the
|
|
41
|
+
* capture-rate question does not need it. A type with no field able to carry a
|
|
42
|
+
* prompt cannot leak one; a scrubber can fail.
|
|
43
|
+
*
|
|
44
|
+
* Default-OFF (`hooks.suggestion_capture.enabled: true`). `fail_closed: false`,
|
|
45
|
+
* always exit 0: an instrument must never break a turn it is only observing.
|
|
46
|
+
*/
|
|
47
|
+
import fs from 'node:fs';
|
|
48
|
+
import path from 'node:path';
|
|
49
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
50
|
+
|
|
51
|
+
import { unwrap } from './envelope.js';
|
|
52
|
+
import { readHookStdin } from './hook_stdin.js';
|
|
53
|
+
|
|
54
|
+
const EXIT_ALLOW = 0;
|
|
55
|
+
const SETTINGS_FILE = '.agent-settings.yml';
|
|
56
|
+
const SINK_REL = path.join('agents', 'runtime', 'state', 'audit', 'suggestion-capture.jsonl');
|
|
57
|
+
const LATCH_REL = path.join('agents', 'runtime', 'state', 'suggestion-latch.json');
|
|
58
|
+
/** A block older than this cannot be what the current prompt is answering. */
|
|
59
|
+
export const LATCH_TTL_MS = 15 * 60 * 1000;
|
|
60
|
+
|
|
61
|
+
export type Classification = 'option_n' | 'as_is' | 'other' | 'stale_block';
|
|
62
|
+
export type EvidenceClass = 'render-signature' | 'latch-consumed' | 'latch-stale' | 'no-latch';
|
|
63
|
+
|
|
64
|
+
type JsonValue = string | number | boolean | null | JsonValue[] | { [k: string]: JsonValue };
|
|
65
|
+
type JsonObject = { [k: string]: JsonValue };
|
|
66
|
+
|
|
67
|
+
const isObject = (v: unknown): v is JsonObject =>
|
|
68
|
+
typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
69
|
+
|
|
70
|
+
const str = (v: JsonValue | undefined): string => (typeof v === 'string' ? v : '');
|
|
71
|
+
|
|
72
|
+
/* ── the render signature ──────────────────────────────────────────────────── */
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Exactly one `Recommendation:`/`Empfehlung:` line, plus at least two numbered
|
|
76
|
+
* options. Both halves are required: a numbered list alone is ordinary prose,
|
|
77
|
+
* and a `Recommendation:` line alone is a sentence.
|
|
78
|
+
*
|
|
79
|
+
* The label is matched in both languages because `user-interaction`'s Iron Law 1
|
|
80
|
+
* makes a wrong-language label a rule violation, not a variant — so a German
|
|
81
|
+
* session's block carries `Empfehlung:` and a single-language matcher would
|
|
82
|
+
* under-count exactly the sessions the mirror rule produces.
|
|
83
|
+
*/
|
|
84
|
+
export function detectBlock(text: string): { emitted: boolean; optionsCount: number } {
|
|
85
|
+
if (text === '') return { emitted: false, optionsCount: 0 };
|
|
86
|
+
const lines = text.split('\n');
|
|
87
|
+
const recommendation = lines.filter((l) => /^\s*(?:\*\*)?(?:Recommendation|Empfehlung)\s*:/i.test(l)).length;
|
|
88
|
+
const numbered = new Set<string>();
|
|
89
|
+
for (const l of lines) {
|
|
90
|
+
const m = /^\s*(\d{1,2})[.)]\s+\S/.exec(l);
|
|
91
|
+
if (m?.[1] !== undefined) numbered.add(m[1]);
|
|
92
|
+
}
|
|
93
|
+
const emitted = recommendation === 1 && numbered.size >= 2;
|
|
94
|
+
return { emitted, optionsCount: emitted ? numbered.size : 0 };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* ── classification ────────────────────────────────────────────────────────── */
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* The as-is escape hatch, matched by intent rather than by an exact string:
|
|
101
|
+
* `command-suggestion-policy` requires it present and last but fixes no wording,
|
|
102
|
+
* so a literal matcher would silently classify every as-is pick as `other`.
|
|
103
|
+
*/
|
|
104
|
+
const AS_IS_RE = /\b(as[- ]is|as is|unver(?:ä|ae)ndert|so lassen|wie es ist|keep it|leave it|no change)\b/i;
|
|
105
|
+
|
|
106
|
+
export function classify(prompt: string, optionsCount: number): Classification {
|
|
107
|
+
const t = prompt.trim();
|
|
108
|
+
if (t === '') return 'other';
|
|
109
|
+
if (AS_IS_RE.test(t)) return 'as_is';
|
|
110
|
+
// A bare number, or a number with trailing punctuation — the shape a pick
|
|
111
|
+
// actually takes. `1. do the thing` is NOT a pick: it is an instruction that
|
|
112
|
+
// happens to start with a digit, and counting it would inflate the rate.
|
|
113
|
+
const bare = /^(\d{1,2})\s*[.)]?\s*$/.exec(t);
|
|
114
|
+
if (bare?.[1] !== undefined) {
|
|
115
|
+
const n = Number(bare[1]);
|
|
116
|
+
// Out-of-range is `other`: a "7" against a 3-option block is answering
|
|
117
|
+
// something else, and recording it as a pick would corrupt the metric
|
|
118
|
+
// the whole instrument exists to produce.
|
|
119
|
+
if (n >= 1 && (optionsCount === 0 || n <= optionsCount)) return 'option_n';
|
|
120
|
+
}
|
|
121
|
+
return 'other';
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/* ── settings, latch, sink ─────────────────────────────────────────────────── */
|
|
125
|
+
|
|
126
|
+
/** `hooks.suggestion_capture.enabled: true` — the same mini-parse siblings use. */
|
|
127
|
+
export function enabled(root: string): boolean {
|
|
128
|
+
let raw: string;
|
|
129
|
+
try {
|
|
130
|
+
raw = fs.readFileSync(path.join(root, SETTINGS_FILE), 'utf8');
|
|
131
|
+
} catch {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
let inHooks = false;
|
|
135
|
+
let inSection = false;
|
|
136
|
+
for (const line of raw.split('\n')) {
|
|
137
|
+
if (/^hooks:\s*$/.test(line)) {
|
|
138
|
+
inHooks = true;
|
|
139
|
+
inSection = false;
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (/^\S/.test(line)) {
|
|
143
|
+
inHooks = false;
|
|
144
|
+
inSection = false;
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
if (!inHooks) continue;
|
|
148
|
+
if (/^\s{2}suggestion_capture:\s*$/.test(line)) {
|
|
149
|
+
inSection = true;
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
if (/^\s{2}\S/.test(line)) {
|
|
153
|
+
inSection = false;
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (inSection && /^\s{4}enabled:\s*true\s*$/.test(line)) return true;
|
|
157
|
+
}
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export interface Latch {
|
|
162
|
+
readonly prompt_id: string;
|
|
163
|
+
readonly options_count: number;
|
|
164
|
+
readonly at: number;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function writeLatch(root: string, latch: Latch): void {
|
|
168
|
+
const p = path.join(root, LATCH_REL);
|
|
169
|
+
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
170
|
+
fs.writeFileSync(p, `${JSON.stringify(latch)}\n`, 'utf8');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** Read AND delete. Consume-once is the misclassification guard, not a detail. */
|
|
174
|
+
export function consumeLatch(root: string): Latch | null {
|
|
175
|
+
const p = path.join(root, LATCH_REL);
|
|
176
|
+
let raw: string;
|
|
177
|
+
try {
|
|
178
|
+
raw = fs.readFileSync(p, 'utf8');
|
|
179
|
+
} catch {
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
try {
|
|
183
|
+
fs.rmSync(p, { force: true });
|
|
184
|
+
} catch {
|
|
185
|
+
/* a latch we cannot delete is still consumed for this turn */
|
|
186
|
+
}
|
|
187
|
+
try {
|
|
188
|
+
const v = JSON.parse(raw) as unknown;
|
|
189
|
+
if (!isObject(v)) return null;
|
|
190
|
+
const at = v['at'];
|
|
191
|
+
const oc = v['options_count'];
|
|
192
|
+
const pid = v['prompt_id'];
|
|
193
|
+
if (typeof at !== 'number' || typeof oc !== 'number' || typeof pid !== 'string') return null;
|
|
194
|
+
return { prompt_id: pid, options_count: oc, at };
|
|
195
|
+
} catch {
|
|
196
|
+
return null; // unparseable → stale_block, never a guess
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export interface Record_ {
|
|
201
|
+
readonly ts: string;
|
|
202
|
+
readonly block_emitted: boolean;
|
|
203
|
+
readonly options_count: number;
|
|
204
|
+
readonly evidence_class: EvidenceClass;
|
|
205
|
+
readonly turn_classification: Classification;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** The exact key set `src/config/suggestion-capture.json` registers. */
|
|
209
|
+
export const RECORD_KEYS: readonly string[] = [
|
|
210
|
+
'ts',
|
|
211
|
+
'block_emitted',
|
|
212
|
+
'options_count',
|
|
213
|
+
'evidence_class',
|
|
214
|
+
'turn_classification',
|
|
215
|
+
];
|
|
216
|
+
|
|
217
|
+
export function appendRecord(root: string, rec: Record_, now: Date): void {
|
|
218
|
+
const p = path.join(root, SINK_REL);
|
|
219
|
+
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
220
|
+
const line = {
|
|
221
|
+
ts: now.toISOString().replace(/\.\d{3}Z$/, 'Z'),
|
|
222
|
+
block_emitted: rec.block_emitted,
|
|
223
|
+
options_count: rec.options_count,
|
|
224
|
+
evidence_class: rec.evidence_class,
|
|
225
|
+
turn_classification: rec.turn_classification,
|
|
226
|
+
};
|
|
227
|
+
fs.appendFileSync(p, `${JSON.stringify(line)}\n`, 'utf8');
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/* ── the two slots ─────────────────────────────────────────────────────────── */
|
|
231
|
+
|
|
232
|
+
/** Decide what a prompt turn should record. Pure, so the tests drive it directly. */
|
|
233
|
+
export function classifyTurn(
|
|
234
|
+
prompt: string,
|
|
235
|
+
latch: Latch | null,
|
|
236
|
+
nowMs: number,
|
|
237
|
+
): { record: boolean; evidence_class: EvidenceClass; turn_classification: Classification; options_count: number } {
|
|
238
|
+
if (latch === null) {
|
|
239
|
+
// No block preceded this turn. Recording every ordinary turn would make
|
|
240
|
+
// the sink a prompt log with the volume of one, so nothing is written.
|
|
241
|
+
return { record: false, evidence_class: 'no-latch', turn_classification: 'other', options_count: 0 };
|
|
242
|
+
}
|
|
243
|
+
if (nowMs - latch.at > LATCH_TTL_MS) {
|
|
244
|
+
return {
|
|
245
|
+
record: true,
|
|
246
|
+
evidence_class: 'latch-stale',
|
|
247
|
+
turn_classification: 'stale_block',
|
|
248
|
+
options_count: latch.options_count,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
return {
|
|
252
|
+
record: true,
|
|
253
|
+
evidence_class: 'latch-consumed',
|
|
254
|
+
turn_classification: classify(prompt, latch.options_count),
|
|
255
|
+
options_count: latch.options_count,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Canonical event name, from either invocation shape.
|
|
261
|
+
*
|
|
262
|
+
* The dispatcher supplies `envelope.event` (`stop`, `user_prompt_submit`); a
|
|
263
|
+
* direct/legacy invocation carries only the host's own `hook_event_name`
|
|
264
|
+
* (`Stop`, `UserPromptSubmit`). Reading just the second one is how the first
|
|
265
|
+
* version of this concern became a silent no-op under the dispatcher: it ran,
|
|
266
|
+
* matched nothing, and exited 0 — indistinguishable from a disabled hook.
|
|
267
|
+
*/
|
|
268
|
+
export function eventOf(envelope: JsonObject, payload: JsonObject): string {
|
|
269
|
+
const canonical = str(envelope['event'] as JsonValue | undefined);
|
|
270
|
+
const native = str(
|
|
271
|
+
(envelope['native_event'] ?? payload['hook_event_name']) as JsonValue | undefined,
|
|
272
|
+
);
|
|
273
|
+
const raw = (canonical !== '' ? canonical : native).toLowerCase();
|
|
274
|
+
if (raw === 'stop') return 'stop';
|
|
275
|
+
if (raw === 'userpromptsubmit' || raw === 'user_prompt_submit') return 'user_prompt_submit';
|
|
276
|
+
return raw;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* The dispatcher calls every in-process concern as `main(argv)` — see
|
|
281
|
+
* `_run_concern_inproc` in `hooks/dispatch_hook.ts`, and the `ConcernMain`
|
|
282
|
+
* signature in `hooks/concern_registry.ts`.
|
|
283
|
+
*
|
|
284
|
+
* This used to be `main(now: Date = new Date())`, and the mismatch made the
|
|
285
|
+
* concern a SILENT NO-OP on every live dispatch: argv arrived where a Date was
|
|
286
|
+
* expected, `now.getTime()` threw `TypeError: now.getTime is not a function`,
|
|
287
|
+
* and the outer catch — "an instrument never breaks the turn it observes" —
|
|
288
|
+
* swallowed it and returned EXIT_ALLOW. The dispatcher recorded exit 0 and no
|
|
289
|
+
* output, which is indistinguishable from a disabled hook. Three earlier
|
|
290
|
+
* isolation attempts read that silence as a `--project-dir` problem; it was
|
|
291
|
+
* this.
|
|
292
|
+
*
|
|
293
|
+
* The clock stays injectable because the TTL and the timestamp need to be
|
|
294
|
+
* driven from a test, but it is now the SECOND parameter, where the dispatcher
|
|
295
|
+
* cannot land on it.
|
|
296
|
+
*/
|
|
297
|
+
export function main(_argv: readonly string[] = [], now: Date = new Date()): number {
|
|
298
|
+
let envelope: JsonObject;
|
|
299
|
+
let payload: JsonObject;
|
|
300
|
+
try {
|
|
301
|
+
[envelope, payload] = unwrap(readHookStdin(), 'claude');
|
|
302
|
+
} catch {
|
|
303
|
+
return EXIT_ALLOW;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// `workspace_root` is the dispatcher's; `cwd` is the host payload's. Both
|
|
307
|
+
// are read because the concern must work under either invocation.
|
|
308
|
+
const root =
|
|
309
|
+
str(envelope['workspace_root'] as JsonValue | undefined) ||
|
|
310
|
+
str(payload['cwd'] as JsonValue | undefined) ||
|
|
311
|
+
str(envelope['project_root'] as JsonValue | undefined) ||
|
|
312
|
+
'.';
|
|
313
|
+
if (!enabled(root)) return EXIT_ALLOW;
|
|
314
|
+
|
|
315
|
+
const event = eventOf(envelope, payload);
|
|
316
|
+
try {
|
|
317
|
+
if (event === 'stop') {
|
|
318
|
+
const { emitted, optionsCount } = detectBlock(
|
|
319
|
+
str(payload['last_assistant_message'] as JsonValue | undefined),
|
|
320
|
+
);
|
|
321
|
+
if (emitted) {
|
|
322
|
+
writeLatch(root, {
|
|
323
|
+
prompt_id: str(payload['prompt_id'] as JsonValue | undefined),
|
|
324
|
+
options_count: optionsCount,
|
|
325
|
+
at: now.getTime(),
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
return EXIT_ALLOW;
|
|
329
|
+
}
|
|
330
|
+
if (event === 'user_prompt_submit') {
|
|
331
|
+
const latch = consumeLatch(root);
|
|
332
|
+
const v = classifyTurn(str(payload['prompt'] as JsonValue | undefined), latch, now.getTime());
|
|
333
|
+
if (v.record) {
|
|
334
|
+
appendRecord(
|
|
335
|
+
root,
|
|
336
|
+
{
|
|
337
|
+
ts: '',
|
|
338
|
+
block_emitted: true,
|
|
339
|
+
options_count: v.options_count,
|
|
340
|
+
evidence_class: v.evidence_class,
|
|
341
|
+
turn_classification: v.turn_classification,
|
|
342
|
+
},
|
|
343
|
+
now,
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
return EXIT_ALLOW;
|
|
347
|
+
}
|
|
348
|
+
} catch {
|
|
349
|
+
return EXIT_ALLOW; // an instrument never breaks the turn it observes
|
|
350
|
+
}
|
|
351
|
+
return EXIT_ALLOW;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
declare const __AGENT_CONFIG_BUNDLE__: boolean | undefined;
|
|
355
|
+
function isCliEntry(): boolean {
|
|
356
|
+
if (typeof __AGENT_CONFIG_BUNDLE__ !== 'undefined' && __AGENT_CONFIG_BUNDLE__) return false;
|
|
357
|
+
if (process.argv[1] === undefined) return false;
|
|
358
|
+
const argvUrl = pathToFileURL(path.resolve(process.argv[1])).href;
|
|
359
|
+
if (import.meta.url === argvUrl) return true;
|
|
360
|
+
try {
|
|
361
|
+
return fs.realpathSync(fileURLToPath(import.meta.url)) === fs.realpathSync(path.resolve(process.argv[1]));
|
|
362
|
+
} catch {
|
|
363
|
+
return false;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
if (isCliEntry()) process.exit(main());
|
|
367
|
+
|
|
368
|
+
export { SINK_REL, LATCH_REL };
|