@event4u/agent-config 16.2.0 → 16.4.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 +337 -234
- package/README.md +15 -13
- package/agents/templates/.ai-council.yml.example +26 -8
- package/dist/agent-src/commands/analyze/inbox.md +11 -8
- package/dist/agent-src/commands/analyze/roadmap-repos.md +1 -1
- package/dist/agent-src/commands/bug/fix.md +17 -2
- package/dist/agent-src/commands/bug/investigate.md +1 -1
- package/dist/agent-src/commands/commit/in-chunks.md +40 -31
- package/dist/agent-src/commands/commit.md +72 -33
- package/dist/agent-src/commands/condense.md +2 -8
- package/dist/agent-src/commands/council/debate.md +11 -7
- package/dist/agent-src/commands/council/default.md +10 -9
- package/dist/agent-src/commands/council.md +1 -1
- package/dist/agent-src/commands/estimate-ticket.md +1 -1
- package/dist/agent-src/commands/feature/explore.md +3 -3
- package/dist/agent-src/commands/feature/plan.md +2 -2
- package/dist/agent-src/commands/feature/roadmap.md +1 -1
- package/dist/agent-src/commands/fix/ci.md +18 -4
- package/dist/agent-src/commands/fix/commit-messages.md +48 -23
- package/dist/agent-src/commands/fix/pr-comments.md +3 -6
- package/dist/agent-src/commands/implement-ticket.md +4 -4
- package/dist/agent-src/commands/jira-ticket.md +1 -1
- package/dist/agent-src/commands/memory/add.md +4 -16
- package/dist/agent-src/commands/memory/load.md +5 -6
- package/dist/agent-src/commands/memory/promote.md +3 -16
- package/dist/agent-src/commands/mission/upgrade.md +3 -2
- package/dist/agent-src/commands/optimize/agents-dir.md +2 -2
- package/dist/agent-src/commands/optimize/deep.md +4 -2
- package/dist/agent-src/commands/optimize/rtk.md +4 -26
- package/dist/agent-src/commands/pr/create/description-only.md +18 -15
- package/dist/agent-src/commands/pr/create.md +42 -34
- package/dist/agent-src/commands/pr/merge.md +136 -9
- package/dist/agent-src/commands/prepare-for-review.md +13 -11
- package/dist/agent-src/commands/project/analyze.md +7 -35
- package/dist/agent-src/commands/refine-ticket.md +1 -1
- package/dist/agent-src/commands/review/changes.md +14 -19
- package/dist/agent-src/commands/roadmap/create.md +1 -1
- package/dist/agent-src/commands/roadmap/materialize.md +1 -1
- package/dist/agent-src/commands/roadmap/next.md +10 -1
- package/dist/agent-src/commands/roadmap/process-full.md +56 -42
- package/dist/agent-src/commands/roadmap/resolve-blockers.md +411 -0
- package/dist/agent-src/commands/roadmap/triage-parked.md +325 -0
- package/dist/agent-src/commands/roadmap.md +18 -4
- package/dist/agent-src/commands/tests/create.md +17 -3
- package/dist/agent-src/commands/tests/execute.md +22 -5
- package/dist/agent-src/commands/worktree/create.md +7 -3
- package/dist/agent-src/commands/worktree/verify.md +2 -1
- package/dist/agent-src/contexts/authority/decision-revisit-gate-enforcement.md +43 -0
- package/dist/agent-src/contexts/communication/rules-auto/design-fidelity-enforcement.md +24 -0
- package/dist/agent-src/contexts/communication/rules-auto/think-before-action-mechanics.md +5 -3
- package/dist/agent-src/contexts/communication/rules-auto/token-efficiency-mechanics.md +7 -9
- package/dist/agent-src/contexts/contracts/command-suggestion-flow.md +8 -6
- package/dist/agent-src/contexts/contracts/frugality-charter.md +8 -5
- package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +7 -7
- package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +0 -1
- package/dist/agent-src/contexts/execution/evidence-discipline.md +2 -2
- package/dist/agent-src/contexts/execution/non-interactive-contract.md +5 -8
- package/dist/agent-src/contexts/execution/rdp-gate.md +4 -4
- package/dist/agent-src/contexts/execution/roadmap-execution-contract.md +9 -8
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +99 -54
- package/dist/agent-src/contexts/execution/session-canary-enforcement-history.md +67 -0
- package/dist/agent-src/contexts/execution/subagent-routing.md +8 -8
- package/dist/agent-src/contexts/execution/subagent-steering.md +6 -4
- package/dist/agent-src/contexts/execution/terminal-states.md +2 -2
- package/dist/agent-src/contexts/execution/toolchain-resolver.md +95 -7
- package/dist/agent-src/contexts/subagent-configuration.md +1 -1
- package/dist/agent-src/guidelines/agent-infra/active-remediation-mechanics.md +39 -0
- package/dist/agent-src/guidelines/agent-infra/context-hygiene-mechanics.md +1 -1
- package/dist/agent-src/guidelines/agent-infra/cross-source-consistency-mechanics.md +8 -10
- package/dist/agent-src/guidelines/agent-infra/frontier-reasoning-operating-profile.md +3 -2
- package/dist/agent-src/guidelines/agent-infra/layered-settings.md +13 -1
- package/dist/agent-src/guidelines/agent-infra/roadmap-progress-mechanics.md +1 -1
- package/dist/agent-src/guidelines/agent-infra/self-improvement-pipeline.md +10 -3
- package/dist/agent-src/guidelines/agent-infra/size-and-scope.md +36 -0
- package/dist/agent-src/guidelines/php/git.md +15 -2
- package/dist/agent-src/rules/active-remediation.md +1 -2
- package/dist/agent-src/rules/analysis-skill-routing.md +0 -1
- package/dist/agent-src/rules/architecture.md +0 -1
- package/dist/agent-src/rules/artifact-drafting-protocol.md +0 -1
- package/dist/agent-src/rules/artifact-engagement-recording.md +0 -1
- package/dist/agent-src/rules/augment-edit-discipline.md +0 -1
- package/dist/agent-src/rules/autonomous-execution.md +1 -1
- package/dist/agent-src/rules/brand-consistency.md +0 -1
- package/dist/agent-src/rules/brand-source-of-truth.md +0 -1
- package/dist/agent-src/rules/broken-access-control.md +1 -1
- package/dist/agent-src/rules/cli-output-handling.md +0 -1
- package/dist/agent-src/rules/code-comment-discipline.md +0 -1
- package/dist/agent-src/rules/code-provenance.md +0 -1
- package/dist/agent-src/rules/command-suggestion-policy.md +1 -1
- package/dist/agent-src/rules/commit-conventions.md +1 -2
- package/dist/agent-src/rules/communication-through-line.md +1 -2
- package/dist/agent-src/rules/content-quoting-floor.md +1 -2
- package/dist/agent-src/rules/context-hygiene.md +6 -6
- package/dist/agent-src/rules/copilot-routing.md +1 -2
- package/dist/agent-src/rules/council-availability.md +4 -4
- package/dist/agent-src/rules/cross-source-consistency.md +4 -6
- package/dist/agent-src/rules/decision-revisit-gate.md +17 -35
- package/dist/agent-src/rules/delegation-policy.md +2 -3
- package/dist/agent-src/rules/design-fidelity.md +12 -16
- package/dist/agent-src/rules/design-review-after-ui-write.md +3 -3
- package/dist/agent-src/rules/devcontainer-routing.md +1 -2
- package/dist/agent-src/rules/doc-screenshot-hygiene.md +1 -1
- package/dist/agent-src/rules/docker-commands.md +0 -1
- package/dist/agent-src/rules/domain-adoption-policy.md +0 -1
- package/dist/agent-src/rules/domain-safety-disclaimer.md +1 -1
- package/dist/agent-src/rules/domain-safety-pii.md +1 -1
- package/dist/agent-src/rules/domain-safety-retention.md +1 -1
- package/dist/agent-src/rules/downstream-changes.md +0 -1
- package/dist/agent-src/rules/engineering-safety-floor.md +1 -1
- package/dist/agent-src/rules/evaluator-independence.md +3 -2
- package/dist/agent-src/rules/external-code-graph-interop.md +0 -1
- package/dist/agent-src/rules/external-reference-deep-dive.md +0 -1
- package/dist/agent-src/rules/fast-path-marker-visibility.md +2 -2
- package/dist/agent-src/rules/finance-safety-floor.md +1 -1
- package/dist/agent-src/rules/fix-what-you-see.md +6 -32
- package/dist/agent-src/rules/framework-neutrality-in-generic-skills.md +0 -1
- package/dist/agent-src/rules/git-history-discipline.md +2 -2
- package/dist/agent-src/rules/guidelines.md +0 -1
- package/dist/agent-src/rules/history-discipline.md +0 -1
- package/dist/agent-src/rules/icon-consistency.md +0 -1
- package/dist/agent-src/rules/image-likeness-and-rights.md +1 -1
- package/dist/agent-src/rules/improve-before-implement.md +0 -1
- package/dist/agent-src/rules/invite-challenge.md +0 -1
- package/dist/agent-src/rules/laravel-routing.md +1 -2
- package/dist/agent-src/rules/laravel-translations.md +0 -1
- package/dist/agent-src/rules/legal-safety-floor.md +3 -3
- package/dist/agent-src/rules/lethal-trifecta-guard.md +1 -1
- package/dist/agent-src/rules/linked-projects-onboarding-gate.md +0 -1
- package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +1 -1
- package/dist/agent-src/rules/markdown-safe-codeblocks.md +0 -1
- package/dist/agent-src/rules/media-governance-routing.md +13 -17
- package/dist/agent-src/rules/media-sync-ground-truth.md +0 -1
- package/dist/agent-src/rules/minimal-safe-diff.md +0 -1
- package/dist/agent-src/rules/missing-skill-recovery.md +0 -1
- package/dist/agent-src/rules/missing-tool-handling.md +0 -1
- package/dist/agent-src/rules/model-recommendation.md +0 -1
- package/dist/agent-src/rules/neighbour-precedence.md +64 -0
- package/dist/agent-src/rules/no-attribution-footers.md +0 -1
- package/dist/agent-src/rules/no-decorative-emojis-in-git-surfaces.md +0 -1
- package/dist/agent-src/rules/no-pr-progress-comments.md +0 -1
- package/dist/agent-src/rules/no-roadmap-references.md +0 -1
- package/dist/agent-src/rules/notes-first-reasoning.md +0 -1
- package/dist/agent-src/rules/onboarding-gate.md +0 -1
- package/dist/agent-src/rules/output-discipline.md +0 -1
- package/dist/agent-src/rules/package-ci-checks.md +0 -1
- package/dist/agent-src/rules/persona-governance.md +4 -5
- package/dist/agent-src/rules/php-coding.md +0 -1
- package/dist/agent-src/rules/prefer-enums-over-literals.md +0 -1
- package/dist/agent-src/rules/preservation-guard.md +0 -1
- package/dist/agent-src/rules/provider-lifecycle-discipline.md +3 -4
- package/dist/agent-src/rules/question-not-instruction.md +1 -1
- package/dist/agent-src/rules/reviewer-awareness.md +0 -1
- package/dist/agent-src/rules/roadmap-ci-steps-policy.md +0 -1
- package/dist/agent-src/rules/roadmap-progress-sync.md +4 -7
- package/dist/agent-src/rules/role-mode-adherence.md +0 -1
- package/dist/agent-src/rules/rule-type-governance.md +0 -1
- package/dist/agent-src/rules/runtime-safety.md +1 -1
- package/dist/agent-src/rules/scale-discipline.md +0 -1
- package/dist/agent-src/rules/secret-vcs-guard.md +1 -1
- package/dist/agent-src/rules/security-sensitive-stop.md +3 -3
- package/dist/agent-src/rules/self-repair-loop.md +0 -1
- package/dist/agent-src/rules/senior-engineering-discipline.md +0 -1
- package/dist/agent-src/rules/session-canary.md +7 -62
- package/dist/agent-src/rules/settings-ask-protocol.md +6 -6
- package/dist/agent-src/rules/size-enforcement.md +0 -1
- package/dist/agent-src/rules/skill-improvement-trigger.md +2 -3
- package/dist/agent-src/rules/skill-quality.md +0 -1
- package/dist/agent-src/rules/slash-command-routing-policy.md +0 -1
- package/dist/agent-src/rules/source-confidentiality.md +1 -1
- package/dist/agent-src/rules/source-discovery-gate.md +0 -1
- package/dist/agent-src/rules/source-of-truth.md +0 -1
- package/dist/agent-src/rules/spreadsheet-source-quality.md +1 -2
- package/dist/agent-src/rules/strategy-safety-floor.md +1 -1
- package/dist/agent-src/rules/symfony-routing.md +1 -2
- package/dist/agent-src/rules/think-before-action.md +0 -1
- package/dist/agent-src/rules/token-budget-discipline.md +12 -46
- package/dist/agent-src/rules/token-efficiency.md +0 -1
- package/dist/agent-src/rules/token-optimizer-maintenance.md +0 -1
- package/dist/agent-src/rules/tool-safety.md +1 -1
- package/dist/agent-src/rules/ui-audit-gate.md +1 -2
- package/dist/agent-src/rules/untrusted-input-defense.md +1 -1
- package/dist/agent-src/rules/upstream-proposal.md +0 -1
- package/dist/agent-src/rules/user-interaction.md +0 -1
- package/dist/agent-src/rules/user-interrupt-priority.md +1 -1
- package/dist/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
- package/dist/agent-src/scripts/deferral_carry.ts +339 -0
- package/dist/agent-src/scripts/gate_budget.ts +47 -23
- package/dist/agent-src/scripts/gate_execute.ts +12 -4
- package/dist/agent-src/scripts/update_roadmap_progress.ts +4 -1
- package/dist/agent-src/skills/ai-council/SKILL.md +7 -4
- package/dist/agent-src/skills/ai-council/references/cost-and-redaction.md +21 -11
- package/dist/agent-src/skills/ai-council/references/procedure.md +12 -6
- package/dist/agent-src/skills/analysis-autonomous-mode/SKILL.md +1 -1
- package/dist/agent-src/skills/api-design/data/manifest.json +2 -2
- package/dist/agent-src/skills/api-testing/SKILL.md +17 -0
- package/dist/agent-src/skills/bug-analyzer/SKILL.md +1 -1
- package/dist/agent-src/skills/code-intelligence/SKILL.md +14 -2
- package/dist/agent-src/skills/command-routing/SKILL.md +3 -4
- package/dist/agent-src/skills/conventional-commits-writing/SKILL.md +127 -139
- package/dist/agent-src/skills/corpus-grounding/ATTRIBUTION.md +39 -0
- package/dist/agent-src/skills/corpus-grounding/SKILL.md +8 -2
- package/dist/agent-src/skills/database/data/manifest.json +2 -2
- package/dist/agent-src/skills/database/data/query-tuning.csv +13 -13
- package/dist/agent-src/skills/design-system-capture/SKILL.md +0 -3
- package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +1 -1
- package/dist/agent-src/skills/estimate-ticket/SKILL.md +1 -1
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +2 -2
- package/dist/agent-src/skills/fe-design/SKILL.md +1 -1
- package/dist/agent-src/skills/feature-planning/SKILL.md +1 -1
- package/dist/agent-src/skills/git-workflow/SKILL.md +44 -44
- package/dist/agent-src/skills/git-workflow/references/branch-update.md +312 -0
- package/dist/agent-src/skills/git-workflow/references/commit-subject.md +205 -0
- package/dist/agent-src/skills/iconography/SKILL.md +1 -1
- package/dist/agent-src/skills/jira-integration/SKILL.md +3 -3
- package/dist/agent-src/skills/js-library-packaging/SKILL.md +2 -2
- package/dist/agent-src/skills/merge-conflicts/SKILL.md +10 -4
- package/dist/agent-src/skills/monorepo-workspace/SKILL.md +1 -1
- package/dist/agent-src/skills/po-discovery/SKILL.md +1 -1
- package/dist/agent-src/skills/quality-tools/SKILL.md +11 -1
- package/dist/agent-src/skills/quality-tools/references/go-tools.md +76 -0
- package/dist/agent-src/skills/quality-tools/references/python-tools.md +104 -0
- package/dist/agent-src/skills/refine-ticket/SKILL.md +4 -4
- package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +2 -2
- package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +5 -3
- package/dist/agent-src/skills/source-discovery/SKILL.md +1 -1
- package/dist/agent-src/skills/storybook-workshop/SKILL.md +3 -3
- package/dist/agent-src/skills/tailwind-engineer/SKILL.md +4 -2
- package/dist/agent-src/skills/test-case-discovery/SKILL.md +7 -0
- package/dist/agent-src/skills/test-driven-development/SKILL.md +17 -0
- package/dist/agent-src/skills/test-performance/SKILL.md +19 -1
- package/dist/agent-src/skills/testing-anti-patterns/SKILL.md +1 -1
- package/dist/agent-src/skills/ui-component-architect/SKILL.md +2 -2
- package/dist/agent-src/skills/using-git-worktrees/SKILL.md +1 -1
- package/dist/agent-src/skills/verify-repair-loop/SKILL.md +2 -2
- package/dist/agent-src/templates/AGENTS.md +1 -1
- package/dist/agent-src/templates/agent-settings.md +46 -129
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +3 -3
- package/dist/agent-src/templates/command.md +2 -2
- package/dist/agent-src/templates/roadmaps.md +6 -1
- package/dist/agent-src/templates/scripts/work_engine/_lib/agent_settings.ts +0 -6
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +11 -7
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +48 -6
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/decision_trace.ts +3 -2
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/memory_visibility.ts +2 -1
- package/dist/agent-src/templates/scripts/work_engine/hooks/settings.ts +0 -6
- package/dist/agent-src/templates/scripts/work_engine/scoring/decision_engine.ts +9 -12
- package/dist/agent-src/templates/scripts/work_engine/scoring/memory_visibility.ts +2 -1
- package/dist/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
- package/dist/agent-src/templates/skill.md +1 -1
- package/dist/cli/registry.js +2 -1
- package/dist/cli-delegate/archive_completed_roadmaps.js +92 -12
- package/dist/cli-delegate/check_update_banner.js +207 -120
- package/dist/cli-delegate/chunk-523HCFYO.js +90 -0
- package/dist/cli-delegate/{chunk-FP36SWJD.js → chunk-5GPULQBE.js} +2797 -338
- package/dist/cli-delegate/{chunk-IGD6ERX6.js → chunk-6SRJTYJS.js} +42 -176
- package/dist/cli-delegate/chunk-6V5FMA3P.js +186 -0
- package/dist/cli-delegate/{chunk-M2DPGFZ4.js → chunk-6V6TA25H.js} +22 -152
- package/dist/cli-delegate/chunk-6YQC2HLF.js +415 -0
- package/dist/cli-delegate/{chunk-E4XIZDIB.js → chunk-7HKRE54K.js} +1 -64
- package/dist/cli-delegate/chunk-7IQE3D5S.js +191 -0
- package/dist/cli-delegate/{chunk-YJ72UIAK.js → chunk-AED2VQGM.js} +1245 -249
- package/dist/cli-delegate/{chunk-7HQ52DHU.js → chunk-AL3RZ22D.js} +1 -1
- package/dist/cli-delegate/chunk-DEIV7XP7.js +108 -0
- package/dist/cli-delegate/{chunk-DBH4AHDK.js → chunk-ED7KCMLB.js} +9 -11
- package/dist/cli-delegate/chunk-F3QDG2GT.js +2569 -0
- package/dist/cli-delegate/{chunk-6KFEC3G7.js → chunk-G5VVIZRC.js} +3 -3
- package/dist/cli-delegate/{chunk-FC75PGJE.js → chunk-H76PHZBI.js} +149 -61
- package/dist/cli-delegate/chunk-IAYTIIQW.js +67 -0
- package/dist/cli-delegate/chunk-JQOM7TTM.js +1201 -0
- package/dist/cli-delegate/chunk-KB5FZ655.js +208 -0
- package/dist/cli-delegate/{chunk-7RQP4UDH.js → chunk-KR6EM23A.js} +235 -1455
- package/dist/cli-delegate/{chunk-LTYVIY76.js → chunk-KXXWJTDO.js} +6 -11
- package/dist/cli-delegate/{chunk-T7L2QK3K.js → chunk-LQOCRFWT.js} +1 -1
- package/dist/cli-delegate/{chunk-6KM6ORRY.js → chunk-MDPPKQCQ.js} +58 -9
- package/dist/cli-delegate/{chunk-4XFP2Y3E.js → chunk-MSUL5NYP.js} +5 -5
- package/dist/cli-delegate/chunk-OAFSJBHH.js +272 -0
- package/dist/cli-delegate/{chunk-4E2W63DA.js → chunk-OKJUIAPS.js} +62 -1
- package/dist/cli-delegate/{chunk-QAJS5YWY.js → chunk-PHD7OOLE.js} +2 -1
- package/dist/cli-delegate/{chunk-P6ZHP5W2.js → chunk-PWX7CKMD.js} +4 -4
- package/dist/cli-delegate/{chunk-NDJLNC6Q.js → chunk-T6BP34WI.js} +1 -0
- package/dist/cli-delegate/{chunk-7XGBX6ZH.js → chunk-T7XSJSUQ.js} +1144 -589
- package/dist/cli-delegate/chunk-ULQRUR23.js +208 -0
- package/dist/cli-delegate/chunk-WQWXGIWB.js +1779 -0
- package/dist/cli-delegate/{chunk-RGNIWTEA.js → chunk-XQTPXKFA.js} +1 -1
- package/dist/cli-delegate/{chunk-NUHLI2RX.js → chunk-YCU3TIXH.js} +2 -0
- package/dist/cli-delegate/chunk-ZBZV2G5R.js +210 -0
- package/dist/cli-delegate/{chunk-WGXTVKCQ.js → chunk-ZUZKZYCN.js} +5 -3
- package/dist/cli-delegate/cmd_analyze_session.js +223 -43
- package/dist/cli-delegate/cmd_brand_status.js +3 -2
- package/dist/cli-delegate/cmd_conformance.js +1252 -249
- package/dist/cli-delegate/cmd_converge.js +3 -3
- package/dist/cli-delegate/cmd_doctor.js +19 -9
- package/dist/cli-delegate/cmd_doctor_neighbours.js +30 -0
- package/dist/cli-delegate/cmd_explain.js +45 -21
- package/dist/cli-delegate/cmd_export.js +13 -10
- package/dist/cli-delegate/cmd_fleet.js +3 -3
- package/dist/cli-delegate/cmd_git_convention.js +1851 -0
- package/dist/cli-delegate/cmd_handoff.js +44 -22
- package/dist/cli-delegate/cmd_mcp_available.js +3 -2
- package/dist/cli-delegate/cmd_memory_get.js +3 -1
- package/dist/cli-delegate/cmd_migrate.js +3 -3
- package/dist/cli-delegate/cmd_packs_active.js +5 -4
- package/dist/cli-delegate/cmd_preflight.js +6 -6
- package/dist/cli-delegate/cmd_prune.js +14 -11
- package/dist/cli-delegate/cmd_refresh.js +2 -1
- package/dist/cli-delegate/cmd_route_audit.js +10 -8
- package/dist/cli-delegate/cmd_route_explain.js +4 -4
- package/dist/cli-delegate/cmd_session_recycle.js +10 -7
- package/dist/cli-delegate/cmd_settings_check.js +54 -7
- package/dist/cli-delegate/cmd_settings_get.js +18 -8
- package/dist/cli-delegate/cmd_settings_migrate.js +13 -10
- package/dist/cli-delegate/cmd_settings_set.js +4 -4
- package/dist/cli-delegate/cmd_sync.js +14 -11
- package/dist/cli-delegate/cmd_uninstall.js +15 -12
- package/dist/cli-delegate/cmd_update.js +7 -7
- package/dist/cli-delegate/cmd_upgrade.js +9 -8
- package/dist/cli-delegate/cmd_validate.js +15 -12
- package/dist/cli-delegate/cmd_versions.js +5 -5
- package/dist/cli-delegate/deferral_carry.js +16 -0
- package/dist/cli-delegate/gate_budget.js +1 -1
- package/dist/cli-delegate/gate_execute.js +3 -3
- package/dist/cli-delegate/roadmap_gates.js +6 -6
- package/dist/cli-delegate/update_roadmap_progress.js +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +232 -141
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +3 -3
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +26 -9
- package/dist/discovery/trust-report.md +3 -3
- package/dist/discovery/workspaces.json +7 -4
- package/dist/hooks/dispatch.js +5655 -6623
- package/dist/hooks/dispatch.sha256 +1 -1
- package/dist/install/claudePathsPlan.js +47 -6
- package/dist/install/claudeRuleRewrite.js +7 -0
- package/dist/install/emit_host_rules_cli.js +39 -36
- package/dist/install/globalRuleScope.js +87 -0
- package/dist/install/host_rule_ownership.js +160 -0
- package/dist/install/install.mjs +2336 -1249
- package/dist/install/installThinLayer.js +289 -0
- package/dist/install/installedRuleLinks.js +262 -0
- package/dist/install/wizard-plan.js +24 -0
- package/dist/mcp/awesome-mcp-servers.row.md +1 -1
- package/dist/mcp/mcp-cloudflare-catalogue.json +2 -2
- package/dist/mcp/registry-manifest.json +5 -5
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +36504 -30986
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/agent_settings.js +26 -120
- package/dist/scripts/_lib/claude_settings_hooks.js +10 -8
- package/dist/scripts/_lib/compile_time_toggles.js +12 -21
- package/dist/scripts/_lib/git_convention.js +232 -0
- package/dist/scripts/_lib/global_deploy_inventory.js +730 -0
- package/dist/scripts/_lib/installed_layer.js +704 -0
- package/dist/scripts/_lib/lean_projection_mode.js +218 -15
- package/dist/scripts/_lib/package_root.js +68 -0
- package/dist/scripts/_lib/rule_consequence_class.js +45 -0
- package/dist/scripts/_lib/rule_injection.js +67 -10
- package/dist/scripts/_lib/rule_law_section.js +122 -0
- package/dist/scripts/_lib/settings_removed_keys.js +168 -0
- package/dist/scripts/_lib/thin_rules.js +535 -0
- package/dist/scripts/_lib/windsurf_render.js +60 -0
- package/dist/scripts/condense.js +14 -26
- package/dist/scripts/project_thin_rules.js +14 -294
- package/dist/server/app.js +2 -1
- package/dist/server/gitKeysGate.js +105 -0
- package/dist/server/routes/settings.js +67 -8
- package/dist/server/routes/wizard.js +4 -0
- package/dist/server/schemas/settings.js +29 -81
- package/dist/shared/skillRanking.js +103 -16
- package/dist/ui/assets/{index-Cwq4d0hX.js → index-C3djEJah.js} +2 -2
- package/dist/ui/index.html +1 -1
- package/docs/MIGRATION.md +72 -1
- package/docs/contracts/settings-classes.md +39 -67
- package/docs/guidelines/agent-infra/active-remediation-mechanics.md +39 -0
- package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +1 -1
- package/docs/guidelines/agent-infra/cross-source-consistency-mechanics.md +8 -10
- package/docs/guidelines/agent-infra/frontier-reasoning-operating-profile.md +3 -2
- package/docs/guidelines/agent-infra/layered-settings.md +13 -1
- package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +1 -1
- package/docs/guidelines/agent-infra/self-improvement-pipeline.md +10 -3
- package/docs/guidelines/agent-infra/size-and-scope.md +36 -0
- package/docs/guidelines/php/git.md +15 -2
- package/package.json +1 -1
- package/src/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
- package/src/agent-src/scripts/deferral_carry.ts +339 -0
- package/src/agent-src/scripts/gate_budget.ts +47 -23
- package/src/agent-src/scripts/gate_execute.ts +12 -4
- package/src/agent-src/scripts/update_roadmap_progress.ts +4 -1
- package/src/agent-src/templates/AGENTS.md +1 -1
- package/src/agent-src/templates/scripts/work_engine/_lib/agent_settings.ts +0 -6
- package/src/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +11 -7
- package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +48 -6
- package/src/agent-src/templates/scripts/work_engine/hooks/builtin/decision_trace.ts +3 -2
- package/src/agent-src/templates/scripts/work_engine/hooks/builtin/memory_visibility.ts +2 -1
- package/src/agent-src/templates/scripts/work_engine/hooks/settings.ts +0 -6
- package/src/agent-src/templates/scripts/work_engine/scoring/decision_engine.ts +9 -12
- package/src/agent-src/templates/scripts/work_engine/scoring/memory_visibility.ts +2 -1
- package/src/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
- package/src/config/agent-settings.template.yml +141 -334
- package/src/config/agents-paths.yml +16 -0
- package/src/config/assurance-capability-registry.json +7 -3
- package/src/config/ci-local-parity.yml +3 -0
- package/src/config/continuity-surface.json +10 -3
- package/src/config/discovery/packs.yml +17 -1
- package/src/config/evaluator-budgets.json +23 -10
- package/src/config/gate-coverage.yml +138 -3
- package/src/config/gate-violation-baselines.json +64 -16
- package/src/config/gitignore-block.txt +19 -0
- package/src/config/hook-bundle-budget.json +25 -0
- package/src/config/hook-token-budget.json +6 -1
- package/src/config/host-instruction-limits.json +33 -0
- package/src/config/release-gate-locality.yml +2 -1
- package/src/config/routing-coverage-seed.json +67 -4
- package/src/config/rule-activation-census.json +7 -3
- package/src/config/rule-consequence-class.json +161 -0
- package/src/config/rule-law-ceilings.json +105 -0
- package/src/config/rule-obligations.json +833 -0
- package/src/install/claudePathsPlan.ts +46 -4
- package/src/install/claudeRuleRewrite.ts +10 -1
- package/src/install/emit_host_rules_cli.ts +43 -34
- package/src/install/globalRuleScope.ts +109 -0
- package/src/install/host_rule_ownership.ts +170 -0
- package/src/install/installThinLayer.ts +378 -0
- package/src/install/installedRuleLinks.ts +308 -0
- package/src/install/wizard-plan.ts +24 -0
- package/src/scripts/_cli/cmd_analyze_session.ts +9 -15
- package/src/scripts/_cli/cmd_conformance.ts +13 -2
- package/src/scripts/_cli/cmd_doctor.ts +33 -81
- package/src/scripts/_cli/cmd_doctor_neighbours.ts +426 -0
- package/src/scripts/_cli/cmd_explain.ts +72 -14
- package/src/scripts/_cli/cmd_git_convention.ts +637 -0
- package/src/scripts/_cli/cmd_settings_check.ts +66 -8
- package/src/scripts/_cli/cmd_settings_get.ts +18 -3
- package/src/scripts/_cli/doctor_execution.ts +72 -1
- package/src/scripts/_dispatch.bash +53 -4
- package/src/scripts/_lib/agent_settings.ts +26 -124
- package/src/scripts/_lib/blocked_by_marker.ts +1 -1
- package/src/scripts/_lib/claude_settings_hooks.ts +10 -8
- package/src/scripts/_lib/compile_time_toggles.ts +12 -21
- package/src/scripts/_lib/dispatch_import_closure.ts +163 -0
- package/src/scripts/_lib/estate_offsets.ts +241 -0
- package/src/scripts/_lib/exemption_shape.ts +143 -0
- package/src/scripts/_lib/fnv.ts +23 -0
- package/src/scripts/_lib/foreign_scan.ts +106 -0
- package/src/scripts/_lib/git_base_ref.ts +92 -0
- package/src/scripts/_lib/git_convention.ts +291 -0
- package/src/scripts/_lib/git_convention_carrier.ts +389 -0
- package/src/scripts/_lib/git_convention_grammar.ts +246 -0
- package/src/scripts/_lib/git_convention_measure.ts +344 -0
- package/src/scripts/_lib/gitignore_block.ts +314 -0
- package/src/scripts/_lib/global_deploy_inventory.ts +106 -0
- package/src/scripts/_lib/graph_feeder_record.ts +437 -0
- package/src/scripts/_lib/hook_settings.ts +73 -0
- package/src/scripts/_lib/host_capability.ts +40 -19
- package/src/scripts/_lib/host_hook_merge.ts +165 -0
- package/src/scripts/_lib/installed_layer.ts +963 -0
- package/src/scripts/_lib/json_merge.ts +82 -0
- package/src/scripts/_lib/json_pointers.ts +93 -11
- package/src/scripts/_lib/knowledge_global.ts +14 -1
- package/src/scripts/_lib/lean_projection_mode.ts +275 -15
- package/src/scripts/_lib/machine_wake.ts +16 -3
- package/src/scripts/_lib/module_reach.ts +456 -0
- package/src/scripts/_lib/neighbour_census.ts +760 -0
- package/src/scripts/_lib/neighbour_contradictions.ts +236 -0
- package/src/scripts/_lib/neighbour_scan.ts +122 -0
- package/src/scripts/_lib/neighbour_tool_use.ts +187 -0
- package/src/scripts/_lib/prompt_shape.ts +46 -0
- package/src/scripts/_lib/ratification_artifact.ts +220 -1
- package/src/scripts/_lib/ratification_subject.ts +92 -0
- package/src/scripts/_lib/release_findings_ingest.ts +15 -7
- package/src/scripts/_lib/release_highlights.ts +5 -0
- package/src/scripts/_lib/release_migration_gate.ts +19 -3
- package/src/scripts/_lib/reserved_name_sweep.ts +73 -0
- package/src/scripts/_lib/review_coverage.ts +134 -0
- package/src/scripts/_lib/rule_consequence_class.ts +71 -0
- package/src/scripts/_lib/rule_injection.ts +130 -10
- package/src/scripts/_lib/rule_law_section.ts +147 -0
- package/src/scripts/_lib/rule_layer_overlap.ts +58 -0
- package/src/scripts/_lib/rule_obligations.ts +205 -0
- package/src/scripts/_lib/self_repair.ts +1 -1
- package/src/scripts/_lib/settings_removed_keys.ts +173 -0
- package/src/scripts/_lib/skill_origin.ts +254 -0
- package/src/scripts/_lib/structured_ask.ts +64 -21
- package/src/scripts/_lib/test_red_state.ts +5 -0
- package/src/scripts/_lib/thin_rules.ts +620 -0
- package/src/scripts/_lib/touched_file_quality.ts +570 -0
- package/src/scripts/_lib/turn_end_refusals.ts +77 -2
- package/src/scripts/_lib/windsurf_render.ts +63 -0
- package/src/scripts/_lib/worker_budget.ts +1 -1
- package/src/scripts/adr/regenerate_index.ts +16 -1
- package/src/scripts/ai_council/cli_call_budget.ts +13 -5
- package/src/scripts/ai_council/clients.ts +26 -57
- package/src/scripts/ai_council/config.ts +8 -8
- package/src/scripts/ai_council/modification_review.ts +100 -0
- package/src/scripts/ai_council/modification_review_questions.ts +90 -0
- package/src/scripts/ai_council/orchestrator.ts +5 -5
- package/src/scripts/ai_council/spend_gate.ts +18 -7
- package/src/scripts/ai_council/vendor_defaults.ts +45 -0
- package/src/scripts/ai_team/config.ts +11 -16
- package/src/scripts/audit_skill_overlap.ts +261 -0
- package/src/scripts/audit_user_type_axis.ts +20 -8
- package/src/scripts/before_complete_hook.ts +243 -12
- package/src/scripts/bench_ab_clone.ts +1 -2
- package/src/scripts/bench_concern_spawn_path.ts +129 -0
- package/src/scripts/bench_graph_feeder_latency.ts +584 -0
- package/src/scripts/bench_touched_file_quality.ts +238 -0
- package/src/scripts/check_agent_artifact_location.ts +36 -0
- package/src/scripts/check_branch_freshness.ts +147 -24
- package/src/scripts/check_enforcement_coverage.ts +130 -0
- package/src/scripts/check_enforcement_matrix.ts +377 -20
- package/src/scripts/check_estate_count.ts +130 -153
- package/src/scripts/check_evaluator_budgets.ts +4 -0
- package/src/scripts/check_finding_dispositions.ts +196 -21
- package/src/scripts/check_gate_coverage.ts +4 -3
- package/src/scripts/check_gate_reachability.ts +48 -1
- package/src/scripts/check_generator_sync.ts +48 -0
- package/src/scripts/check_hook_bundle_composition.ts +477 -0
- package/src/scripts/check_kernel_edit_ratified.ts +132 -14
- package/src/scripts/check_memory.ts +7 -1
- package/src/scripts/check_pack_size.ts +1 -1
- package/src/scripts/check_release_highlights.ts +10 -0
- package/src/scripts/check_release_manifest_content.ts +189 -0
- package/src/scripts/check_release_pr_shape.ts +38 -15
- package/src/scripts/check_standing_rule_delivery.ts +129 -13
- package/src/scripts/check_suppression_hygiene.ts +2 -5
- package/src/scripts/check_verify_expectation_delta.ts +521 -0
- package/src/scripts/ci_settle.ts +42 -5
- package/src/scripts/classify_merge_risk.ts +320 -0
- package/src/scripts/closure_scan.ts +91 -1
- package/src/scripts/code_graph/detect.ts +127 -7
- package/src/scripts/code_graph/foreign.ts +194 -0
- package/src/scripts/code_graph/query.ts +217 -4
- package/src/scripts/code_graph/types.ts +26 -2
- package/src/scripts/code_graph/validate.ts +3 -1
- package/src/scripts/code_graph/verbs.ts +103 -0
- package/src/scripts/command_suggester/settings.ts +8 -108
- package/src/scripts/command_suggester/types.ts +4 -5
- package/src/scripts/compile_hook_manifest.ts +33 -1
- package/src/scripts/condense.ts +14 -27
- package/src/scripts/consumer_matrix.ts +102 -0
- package/src/scripts/context_hygiene_hook.ts +13 -23
- package/src/scripts/cost/preflight.mjs +12 -1
- package/src/scripts/council_cli.ts +7 -7
- package/src/scripts/dispatch_r2_reviewer.ts +24 -11
- package/src/scripts/estate_exemption_shape.ts +202 -0
- package/src/scripts/explain_run.ts +8 -13
- package/src/scripts/generate_git_convention_grammar.ts +127 -0
- package/src/scripts/generate_host_cost_table.ts +21 -4
- package/src/scripts/handoff_context_hook.ts +86 -37
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +175 -15
- package/src/scripts/hooks/block_config_weakening.ts +115 -7
- package/src/scripts/hooks/block_plumbing_writes.ts +37 -5
- package/src/scripts/hooks/code_graph_context_hook.ts +252 -23
- package/src/scripts/hooks/concern_failure_policy.ts +280 -0
- package/src/scripts/hooks/concern_registry.ts +2 -0
- package/src/scripts/hooks/delegation_nudge_hook.ts +2 -3
- package/src/scripts/hooks/dispatch_hook.ts +52 -52
- package/src/scripts/hooks/evidence_independence.ts +12 -4
- package/src/scripts/hooks/exit_codes.ts +2 -1
- package/src/scripts/hooks/host_lowering.json +1 -1
- package/src/scripts/hooks/host_lowering.ts +19 -1
- package/src/scripts/hooks/mcp_usage_observation_hook.ts +121 -0
- package/src/scripts/hooks/one_question_per_ask_hook.ts +41 -25
- package/src/scripts/hooks/payload_stub.ts +10 -1
- package/src/scripts/hooks/push_settle_hook.ts +4 -3
- package/src/scripts/hooks/rule_inject_hook.ts +795 -60
- package/src/scripts/hooks/state_io.ts +8 -4
- package/src/scripts/hooks/table_fingerprint.ts +21 -0
- package/src/scripts/hooks/telemetry_usage_hook.ts +24 -7
- package/src/scripts/hooks/turn_end_gate_hook.ts +85 -0
- package/src/scripts/hooks_status.ts +112 -0
- package/src/scripts/injection_scan_hook.ts +7 -0
- package/src/scripts/install-hooks.sh +42 -14
- package/src/scripts/install.ts +78 -215
- package/src/scripts/installed_layer_report.ts +103 -0
- package/src/scripts/language_mirror_hook.ts +15 -6
- package/src/scripts/learning_sidecar.ts +119 -8
- package/src/scripts/lint_blocker_citations.ts +198 -0
- package/src/scripts/lint_evidence_artifacts.ts +9 -0
- package/src/scripts/lint_hook_concern_budget.ts +10 -21
- package/src/scripts/lint_hook_manifest.ts +53 -0
- package/src/scripts/lint_major_migration_sections.ts +156 -26
- package/src/scripts/lint_pack_boundaries.ts +465 -29
- package/src/scripts/lint_roadmap_blockers.ts +282 -29
- package/src/scripts/lint_roadmap_complexity.ts +4 -2
- package/src/scripts/lint_rule_law_section.ts +647 -0
- package/src/scripts/lint_rule_skill_pack_reach.ts +17 -0
- package/src/scripts/lint_settings_classes.ts +1 -1
- package/src/scripts/lint_ticked_unmet_criteria.ts +165 -0
- package/src/scripts/lint_workflow_security.ts +173 -6
- package/src/scripts/mcp_server/consumer_tool_catalog.json +53 -0
- package/src/scripts/mcp_server/graph_tools.ts +72 -3
- package/src/scripts/measure_routing_signal.ts +54 -16
- package/src/scripts/measure_skill_ranker_baseline.ts +221 -8
- package/src/scripts/memory_learn_hook.ts +91 -6
- package/src/scripts/model_rule_injection.ts +55 -8
- package/src/scripts/onboarding_gate_hook.ts +7 -6
- package/src/scripts/print_required_checks.ts +15 -3
- package/src/scripts/probe_host_compliance.ts +33 -5
- package/src/scripts/probe_turnaround.ts +183 -3
- package/src/scripts/project_thin_rules.ts +34 -327
- package/src/scripts/ratification_header.ts +77 -0
- package/src/scripts/refusal_envelope.ts +138 -0
- package/src/scripts/release_publication.ts +29 -3
- package/src/scripts/report_dispatch_import_closure.ts +76 -0
- package/src/scripts/report_evidence_temperature.ts +657 -0
- package/src/scripts/report_installed_rule_links.ts +274 -0
- package/src/scripts/report_label_agreement.ts +233 -0
- package/src/scripts/report_module_reach.ts +212 -0
- package/src/scripts/report_obligation_mechanism.ts +228 -0
- package/src/scripts/report_parked_blockers.ts +222 -0
- package/src/scripts/report_required_checks_drift.ts +196 -0
- package/src/scripts/report_single_token_triggers.ts +277 -0
- package/src/scripts/report_skill_ranker_confusion.ts +543 -0
- package/src/scripts/report_standing_payload_by_host.ts +188 -15
- package/src/scripts/report_verification_record_coverage.ts +306 -0
- package/src/scripts/routing_doctor.ts +2 -2
- package/src/scripts/rule_activation_census.ts +5 -2
- package/src/scripts/rule_trigger_eval.ts +9 -9
- package/src/scripts/schemas/agent-settings.schema.json +17 -31
- package/src/scripts/schemas/command.schema.json +1 -1
- package/src/scripts/schemas/review-findings.schema.json +3 -2
- package/src/scripts/schemas/rule.schema.json +5 -0
- package/src/scripts/schemas/skill.schema.json +1 -1
- package/src/scripts/self_review_gate.ts +101 -9
- package/src/scripts/skill_tools/score_skill_relevance.ts +314 -40
- package/src/scripts/sweep_skill_ranker_signals.ts +129 -0
- package/src/scripts/sync_gitignore.ts +29 -235
- package/src/scripts/sync_pr_branch.ts +403 -132
- package/src/scripts/trigger_eval_grandfather.json +0 -21
- package/src/server/schemas/settings.ts +42 -176
- package/src/shared/skillRanking.ts +169 -14
- package/dist/cli-delegate/chunk-5R7HOLAF.js +0 -99
- package/dist/cli-delegate/chunk-JNMVLWLR.js +0 -4457
- package/dist/cli-delegate/chunk-YDPILCOL.js +0 -152
- package/src/scripts/_lib/config_chain.ts +0 -477
- package/src/scripts/_lib/conformance_report.ts +0 -403
- package/src/scripts/_lib/eval_discrimination.ts +0 -95
- package/src/scripts/_lib/evaluator_promotion.ts +0 -225
- package/src/scripts/_lib/file_slicer.ts +0 -117
- package/src/scripts/_lib/legacy_boundary_map.ts +0 -228
- package/src/scripts/_lib/minimality_tiebreak.ts +0 -168
- package/src/scripts/_lib/overbuild_lens_contract.ts +0 -196
- package/src/scripts/_lib/role_split.ts +0 -338
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contradiction review between a project's instructions and its neighbours'
|
|
3
|
+
* (road-to-neighbours-that-pull-their-weight step 2.2).
|
|
4
|
+
*
|
|
5
|
+
* REPORT-ONLY. Nothing here edits, gates or suppresses an instruction — the
|
|
6
|
+
* output is a table a human reads. `neighbour-precedence` (step 2.1) is the
|
|
7
|
+
* rubric: the "which wins" column is its source order applied to the two
|
|
8
|
+
* sources, computed here, and the verdict column is one council seat's reading
|
|
9
|
+
* of whether the pair actually disagrees.
|
|
10
|
+
*
|
|
11
|
+
* NO STATIC DETECTOR DECIDES ANYTHING. A static contradiction detector was
|
|
12
|
+
* measured at 67 % false positives (`docs/contracts/rule-interactions.md`), so
|
|
13
|
+
* this module only FORMS candidate pairs, and only from strong signals: a
|
|
14
|
+
* shared subject word with opposite modality, or a skill name collision the
|
|
15
|
+
* census already found. Whether a candidate is a contradiction is the seat's
|
|
16
|
+
* call, never this file's.
|
|
17
|
+
*
|
|
18
|
+
* A neighbour body the shape scan refused (step 1.3) is never read here: the
|
|
19
|
+
* caller passes only scanned sources, and a refused body contributes no line.
|
|
20
|
+
*/
|
|
21
|
+
import * as fs from 'node:fs';
|
|
22
|
+
|
|
23
|
+
/** Rank in `neighbour-precedence`'s source order — lower wins. */
|
|
24
|
+
export const RANK_PROJECT = 3;
|
|
25
|
+
export const RANK_SUITE = 4;
|
|
26
|
+
export const RANK_NEIGHBOUR_ALWAYS_ON = 6;
|
|
27
|
+
export const RANK_NEIGHBOUR_SKILL = 7;
|
|
28
|
+
|
|
29
|
+
const RANK_LABEL: Record<number, string> = {
|
|
30
|
+
[RANK_PROJECT]: 'project instruction file',
|
|
31
|
+
[RANK_SUITE]: "this suite's routed guidance",
|
|
32
|
+
[RANK_NEIGHBOUR_ALWAYS_ON]: "neighbour's always-on text",
|
|
33
|
+
[RANK_NEIGHBOUR_SKILL]: 'neighbour skill body (discoverable)',
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export interface InstructionSource {
|
|
37
|
+
/** Where the text came from, as printed. */
|
|
38
|
+
label: string;
|
|
39
|
+
file: string;
|
|
40
|
+
rank: number;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface InstructionLine {
|
|
44
|
+
source: InstructionSource;
|
|
45
|
+
line: number;
|
|
46
|
+
text: string;
|
|
47
|
+
negative: boolean;
|
|
48
|
+
stems: Set<string>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface CandidatePair {
|
|
52
|
+
kind: 'opposite-modality' | 'name-collision';
|
|
53
|
+
a: { label: string; rank: number; text: string };
|
|
54
|
+
b: { label: string; rank: number; text: string };
|
|
55
|
+
/** The shared subject word, or the colliding name. */
|
|
56
|
+
subject: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export type Verdict = 'contradiction' | 'compatible' | 'unclear';
|
|
60
|
+
|
|
61
|
+
const NEGATION = /\b(never|don't|do not|must not|mustn't|should not|shouldn't|avoid|stop)\b/iu;
|
|
62
|
+
const DIRECTIVE_WORD = /\b(always|must|should|never|don't|do not|avoid)\b/iu;
|
|
63
|
+
const NON_IMPERATIVE_START = new Set([
|
|
64
|
+
'the', 'a', 'an', 'this', 'that', 'these', 'those', 'it', 'its', 'we', 'you', 'i', 'our',
|
|
65
|
+
'they', 'there', 'when', 'if', 'see', 'for', 'in', 'on', 'as', 'by', 'with', 'from',
|
|
66
|
+
]);
|
|
67
|
+
const STOP = new Set([
|
|
68
|
+
'always', 'never', 'must', 'should', 'avoid', 'stop', 'don', 'with', 'without', 'that', 'this',
|
|
69
|
+
'they', 'them', 'then', 'than', 'into', 'from', 'your', 'when', 'what', 'will', 'each', 'every',
|
|
70
|
+
'only', 'also', 'here', 'there', 'have', 'been', 'more', 'some', 'such', 'like', 'just',
|
|
71
|
+
]);
|
|
72
|
+
|
|
73
|
+
/** A crude stem — enough to meet `wait` and `waiting`, never a lemmatiser. */
|
|
74
|
+
export function stem(word: string): string {
|
|
75
|
+
let w = word.toLowerCase().replace(/[^a-z]/gu, '');
|
|
76
|
+
for (const suffix of ['ing', 'ed', 'es', 's']) {
|
|
77
|
+
if (w.length > suffix.length + 3 && w.endsWith(suffix)) {
|
|
78
|
+
w = w.slice(0, -suffix.length);
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return w;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Is this line phrased as an instruction at all? */
|
|
86
|
+
export function isDirective(text: string): boolean {
|
|
87
|
+
if (DIRECTIVE_WORD.test(text)) return true;
|
|
88
|
+
const first = (text.split(/\s+/u)[0] ?? '').toLowerCase().replace(/[^a-z']/gu, '');
|
|
89
|
+
return first.length > 0 && !NON_IMPERATIVE_START.has(first);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Directive lines of one markdown source, outside fences and frontmatter. */
|
|
93
|
+
export function extractInstructions(source: InstructionSource, body?: string): InstructionLine[] {
|
|
94
|
+
let text: string;
|
|
95
|
+
try {
|
|
96
|
+
text = body ?? fs.readFileSync(source.file, 'utf-8');
|
|
97
|
+
} catch {
|
|
98
|
+
return [];
|
|
99
|
+
}
|
|
100
|
+
const out: InstructionLine[] = [];
|
|
101
|
+
let fenced = false;
|
|
102
|
+
let frontmatter = false;
|
|
103
|
+
text.split('\n').forEach((raw, i) => {
|
|
104
|
+
const line = raw.trim();
|
|
105
|
+
if (i === 0 && line === '---') {
|
|
106
|
+
frontmatter = true;
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (frontmatter) {
|
|
110
|
+
if (line === '---') frontmatter = false;
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (line.startsWith('```') || line.startsWith('~~~')) {
|
|
114
|
+
fenced = !fenced;
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
if (fenced || line === '' || line.startsWith('#') || line.startsWith('|') || line.startsWith('>')) return;
|
|
118
|
+
const clean = line.replace(/^([-*+]|\d+\.)\s+/u, '').replace(/[*_`]/gu, '');
|
|
119
|
+
if (clean.split(/\s+/u).length < 3 || !isDirective(clean)) return;
|
|
120
|
+
const stems = new Set(
|
|
121
|
+
clean
|
|
122
|
+
.split(/\s+/u)
|
|
123
|
+
.map(stem)
|
|
124
|
+
.filter((s) => s.length >= 4 && !STOP.has(s)),
|
|
125
|
+
);
|
|
126
|
+
out.push({ source, line: i + 1, text: clean, negative: NEGATION.test(clean), stems });
|
|
127
|
+
});
|
|
128
|
+
return out;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function where(l: InstructionLine): string {
|
|
132
|
+
return `${l.source.label}:${l.line}`;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Candidate pairs from opposite modality over a shared subject word.
|
|
137
|
+
*
|
|
138
|
+
* Only ACROSS ranks — two lines of one source rank are one author's
|
|
139
|
+
* contradiction, which is not what this review is for — and only where one
|
|
140
|
+
* side is negative and the other is not. One pair per line pair, naming the
|
|
141
|
+
* first shared stem in sorted order so the output is stable.
|
|
142
|
+
*/
|
|
143
|
+
export function modalityPairs(lines: readonly InstructionLine[], cap = 20): CandidatePair[] {
|
|
144
|
+
const out: CandidatePair[] = [];
|
|
145
|
+
for (let i = 0; i < lines.length && out.length < cap; i += 1) {
|
|
146
|
+
for (let j = i + 1; j < lines.length && out.length < cap; j += 1) {
|
|
147
|
+
const x = lines[i] as InstructionLine;
|
|
148
|
+
const y = lines[j] as InstructionLine;
|
|
149
|
+
if (x.source.rank === y.source.rank || x.negative === y.negative) continue;
|
|
150
|
+
const shared = [...x.stems].filter((s) => y.stems.has(s)).sort();
|
|
151
|
+
if (shared.length === 0) continue;
|
|
152
|
+
const [hi, lo] = x.source.rank <= y.source.rank ? [x, y] : [y, x];
|
|
153
|
+
out.push({
|
|
154
|
+
kind: 'opposite-modality',
|
|
155
|
+
a: { label: where(hi), rank: hi.source.rank, text: hi.text },
|
|
156
|
+
b: { label: where(lo), rank: lo.source.rank, text: lo.text },
|
|
157
|
+
subject: shared[0] as string,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return out;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** A neighbour skill that shares a name with one of ours — the census's `shadowed`. */
|
|
165
|
+
export function collisionPair(neighbourQualified: string, ourName: string): CandidatePair {
|
|
166
|
+
return {
|
|
167
|
+
kind: 'name-collision',
|
|
168
|
+
a: { label: `suite:${ourName}`, rank: RANK_SUITE, text: `this suite's skill \`${ourName}\`` },
|
|
169
|
+
b: {
|
|
170
|
+
label: neighbourQualified,
|
|
171
|
+
rank: RANK_NEIGHBOUR_SKILL,
|
|
172
|
+
text: `neighbour skill \`${neighbourQualified}\``,
|
|
173
|
+
},
|
|
174
|
+
subject: ourName,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** "Which wins by 2.1" — the lower rank, named. */
|
|
179
|
+
export function winnerBy21(p: CandidatePair): string {
|
|
180
|
+
const w = p.a.rank <= p.b.rank ? p.a : p.b;
|
|
181
|
+
return `${w.label} (rank ${w.rank}, ${RANK_LABEL[w.rank] ?? 'unranked'})`;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** The single-seat prompt. States no expected verdict (evaluator-independence). */
|
|
185
|
+
export function seatPrompt(pairs: readonly CandidatePair[], rubric: string): string {
|
|
186
|
+
const rows = pairs.map(
|
|
187
|
+
(p, i) => `PAIR ${i + 1} (${p.kind}, subject "${p.subject}")\n A [${p.a.label}]: ${p.a.text}\n B [${p.b.label}]: ${p.b.text}`,
|
|
188
|
+
);
|
|
189
|
+
return [
|
|
190
|
+
'# Neighbour contradiction review',
|
|
191
|
+
'',
|
|
192
|
+
'For each pair, decide whether A and B give an agent instructions that cannot both be followed.',
|
|
193
|
+
'Answer one line per pair, exactly `PAIR <n>: contradiction`, `PAIR <n>: compatible` or `PAIR <n>: unclear`,',
|
|
194
|
+
'then at most one sentence of reason on the same line. The rubric for which source wins is below; you do not',
|
|
195
|
+
'need to restate it.',
|
|
196
|
+
'',
|
|
197
|
+
'## Rubric — neighbour-precedence',
|
|
198
|
+
'',
|
|
199
|
+
rubric.trim(),
|
|
200
|
+
'',
|
|
201
|
+
'## Pairs',
|
|
202
|
+
'',
|
|
203
|
+
...rows,
|
|
204
|
+
'',
|
|
205
|
+
].join('\n');
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** Read `PAIR <n>: <verdict>` lines; a pair the seat did not answer is `unclear`. */
|
|
209
|
+
export function parseVerdicts(text: string, count: number): Verdict[] {
|
|
210
|
+
const out: Verdict[] = Array.from({ length: count }, () => 'unclear');
|
|
211
|
+
for (const m of text.matchAll(/PAIR\s+(\d+)\s*:\s*(contradiction|compatible|unclear)/giu)) {
|
|
212
|
+
const n = Number(m[1]) - 1;
|
|
213
|
+
if (n >= 0 && n < count) out[n] = (m[2] ?? 'unclear').toLowerCase() as Verdict;
|
|
214
|
+
}
|
|
215
|
+
return out;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export interface ReviewRow {
|
|
219
|
+
pair: string;
|
|
220
|
+
verdict: Verdict;
|
|
221
|
+
wins: string;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/** The table, from pairs and a seat's verdicts. */
|
|
225
|
+
export function reviewRows(pairs: readonly CandidatePair[], verdicts: readonly Verdict[]): ReviewRow[] {
|
|
226
|
+
return pairs.map((p, i) => ({
|
|
227
|
+
pair: `${p.a.label} "${p.a.text}" × ${p.b.label} "${p.b.text}"`,
|
|
228
|
+
verdict: verdicts[i] ?? 'unclear',
|
|
229
|
+
wins: winnerBy21(p),
|
|
230
|
+
}));
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function renderReview(rows: readonly ReviewRow[]): string[] {
|
|
234
|
+
if (rows.length === 0) return [' no candidate pairs — nothing to review'];
|
|
235
|
+
return [' pair | verdict | which wins by 2.1', ...rows.map((r) => ` ${r.pair} | ${r.verdict} | ${r.wins}`)];
|
|
236
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shape scan a neighbour skill body passes before any of it may be indexed.
|
|
3
|
+
*
|
|
4
|
+
* SCAN BEFORE INJECT. A foreign `SKILL.md` is a file another package put on
|
|
5
|
+
* disk, and the ranker reaches it through the catalogue union. Nothing about
|
|
6
|
+
* being in `~/.claude/skills` makes its prose safe to fold into a term index
|
|
7
|
+
* whose output names skills to a model — the body is untrusted content in the
|
|
8
|
+
* sense `untrusted-input-defense` means it, and the floor here is the same one
|
|
9
|
+
* that rule states: it is data until something has looked at it.
|
|
10
|
+
*
|
|
11
|
+
* THE SHAPES ARE THE ONES THIS SUITE ALREADY SCANS ITS OWN CORPUS WITH. Four
|
|
12
|
+
* `security_lint` checks, reused as functions rather than re-implemented
|
|
13
|
+
* (decision D3 of the lane): instruction smuggling, hidden Unicode, mixed-script
|
|
14
|
+
* confusables and dangerous frontmatter. Re-declaring their patterns here would
|
|
15
|
+
* create a second copy to drift against the first, and the first is the one CI
|
|
16
|
+
* keeps honest.
|
|
17
|
+
*
|
|
18
|
+
* ANY FINDING REFUSES THE BODY — severity is NOT the discriminator. In the
|
|
19
|
+
* corpus linters, `HIGH` fails a build and `MED` warns, because the corpus is
|
|
20
|
+
* this package's own prose and a warning there is a maintainer's to weigh. A
|
|
21
|
+
* neighbour body is nobody's to weigh at rank time, and the cost of refusing is
|
|
22
|
+
* known and small: the skill still RANKS, by its name, with the finding kind
|
|
23
|
+
* printed. Risk-register row 4 is exactly this trade and resolves the same way —
|
|
24
|
+
* a shell snippet can be an installer skill's whole point, so the skill is never
|
|
25
|
+
* hidden, only un-indexed.
|
|
26
|
+
*
|
|
27
|
+
* WHY THIS IS NOT IMPORTED BY THE RANKER. It pulls four linter modules and,
|
|
28
|
+
* through one of them, `node:child_process`. The ranker runs inside the shared
|
|
29
|
+
* hook bundle on `user_prompt_submit`, where every byte is paid by every concern
|
|
30
|
+
* on every dispatch and `check_hook_bundle_composition` holds the ceiling. So
|
|
31
|
+
* the scan runs in the census CLI and its verdict reaches the ranker as a cached
|
|
32
|
+
* record — see `neighbour_census.scanVerdict`, where a missing record is a
|
|
33
|
+
* refusal rather than a pass.
|
|
34
|
+
*/
|
|
35
|
+
import * as fs from 'node:fs';
|
|
36
|
+
import * as path from 'node:path';
|
|
37
|
+
|
|
38
|
+
import * as sl from './security_lint.js';
|
|
39
|
+
import { fileDigest, type ScanCache, type ScanRecord } from './skill_origin.js';
|
|
40
|
+
import { _scan as scanSmuggling } from '../lint_instruction_smuggling.js';
|
|
41
|
+
import { _scan as scanHiddenUnicode } from '../lint_hidden_unicode.js';
|
|
42
|
+
import { _scan as scanConfusables } from '../lint_confusables.js';
|
|
43
|
+
import { _scan as scanFrontmatter } from '../lint_skill_frontmatter_safety.js';
|
|
44
|
+
|
|
45
|
+
/** The shape checks, in the order their findings are reported. */
|
|
46
|
+
const SHAPES: ReadonlyArray<readonly [string, (sf: sl.ScannedFile) => sl.Finding[]]> = [
|
|
47
|
+
['instruction-smuggling', scanSmuggling],
|
|
48
|
+
['dangerous-frontmatter', scanFrontmatter],
|
|
49
|
+
['hidden-unicode', scanHiddenUnicode],
|
|
50
|
+
['mixed-script-confusable', scanConfusables],
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
export interface ScanResult {
|
|
54
|
+
/** `null` when every shape passed; otherwise the first finding's check id. */
|
|
55
|
+
kind: string | null;
|
|
56
|
+
/** Every finding, for a report that wants more than the first kind. */
|
|
57
|
+
findings: sl.Finding[];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Scan one `SKILL.md`.
|
|
62
|
+
*
|
|
63
|
+
* An unreadable file is a refusal, not a pass (`unreadable`): a body nobody
|
|
64
|
+
* could open is in exactly the state this gate exists to keep out of the index,
|
|
65
|
+
* and returning `kind: null` for it would make an IO error look like a clean
|
|
66
|
+
* scan. A THROWING check is reported under its own id rather than swallowed, for
|
|
67
|
+
* the same reason.
|
|
68
|
+
*/
|
|
69
|
+
export function scanNeighbourBody(skillMdPath: string): ScanResult {
|
|
70
|
+
let sf: sl.ScannedFile;
|
|
71
|
+
try {
|
|
72
|
+
sf = sl.scan_file(skillMdPath);
|
|
73
|
+
} catch {
|
|
74
|
+
return { kind: 'unreadable', findings: [] };
|
|
75
|
+
}
|
|
76
|
+
const findings: sl.Finding[] = [];
|
|
77
|
+
for (const [id, scan] of SHAPES) {
|
|
78
|
+
try {
|
|
79
|
+
findings.push(...scan(sf));
|
|
80
|
+
} catch {
|
|
81
|
+
findings.push(
|
|
82
|
+
new sl.Finding(sf.rel, 0, id, 'HIGH', 'shape check threw on this body', 1.0),
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return { kind: findings.length === 0 ? null : (findings[0] as sl.Finding).check, findings };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Record what the scan found, for the ranker to read on the hook path.
|
|
91
|
+
*
|
|
92
|
+
* The digest is the FILE's, not the finding's: the ranker re-digests the body it
|
|
93
|
+
* is about to index and compares, so a body edited after its scan reads as
|
|
94
|
+
* `digest-changed` and goes back to name-only ranking until the census runs
|
|
95
|
+
* again. That compare is the whole mechanism behind "a changed digest rescans
|
|
96
|
+
* first" — without it a one-time pass would keep clearing a file forever.
|
|
97
|
+
*
|
|
98
|
+
* A write failure is reported to the caller rather than thrown: the scan is
|
|
99
|
+
* correct either way, and a read-only report directory must not fail a census.
|
|
100
|
+
*/
|
|
101
|
+
export function writeScanCache(file: string, entries: readonly ScanRecord[]): boolean {
|
|
102
|
+
const payload: ScanCache = {
|
|
103
|
+
schema_version: 1,
|
|
104
|
+
entries: [...entries].sort((a, b) => (a.qualified < b.qualified ? -1 : 1)),
|
|
105
|
+
};
|
|
106
|
+
try {
|
|
107
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
108
|
+
fs.writeFileSync(file, `${JSON.stringify(payload, null, 2)}\n`, 'utf-8');
|
|
109
|
+
return true;
|
|
110
|
+
} catch {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** A `ScanRecord` for one file, digest included. */
|
|
116
|
+
export function scanRecordFor(qualified: string, skillMdPath: string): ScanRecord {
|
|
117
|
+
return {
|
|
118
|
+
qualified,
|
|
119
|
+
digest: fileDigest(skillMdPath) ?? '',
|
|
120
|
+
unscanned: scanNeighbourBody(skillMdPath).kind,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The foreign-MCP-tool-name store: one writer on the post-tool hook path, one
|
|
3
|
+
* reader in the neighbour census.
|
|
4
|
+
*
|
|
5
|
+
* WHY A MODULE OF ITS OWN, AND NOT A CONSTANT IN EITHER SIDE. The writer lives
|
|
6
|
+
* in `hooks/mcp_usage_observation_hook.ts`, which is inlined into the shared hook
|
|
7
|
+
* bundle `check_hook_bundle_composition` caps; the reader lives in
|
|
8
|
+
* `_lib/neighbour_census.ts`, which is not. Putting the pair here lets esbuild
|
|
9
|
+
* tree-shake {@link toolsUsedByServer} out of the hook bundle while both sides
|
|
10
|
+
* still name one path — a constant copied into two files is a store that
|
|
11
|
+
* silently splits in two the first time one copy is edited.
|
|
12
|
+
*
|
|
13
|
+
* WHAT IS RECORDED, AND WHAT IS NOT. One map of `mcp__<server>__<tool>` to the
|
|
14
|
+
* ISO date it was last seen. No arguments, no responses, no session id, no
|
|
15
|
+
* project path beyond the file's own location. The names recorded are names the
|
|
16
|
+
* consumer's own `.mcp.json` already lists, and the file is written under
|
|
17
|
+
* `agents/runtime/`, which is gitignored and local-only — nothing here is sent
|
|
18
|
+
* anywhere, and no transport reads it.
|
|
19
|
+
*
|
|
20
|
+
* NOT GATED ON THE TELEMETRY OPT-IN, DELIBERATELY. `telemetry_usage_hook`'s
|
|
21
|
+
* Class-A capture is default-off because it feeds an outbound spool. This store
|
|
22
|
+
* feeds `doctor neighbours`, a local report the consumer runs on their own tree.
|
|
23
|
+
* Gating it on an org telemetry switch would make the census print `0` on every
|
|
24
|
+
* install that never enabled one — a number that looks like a measurement and is
|
|
25
|
+
* an instrumentation artifact, which is the exact failure the usage hook's own
|
|
26
|
+
* header records from the collector it replaced.
|
|
27
|
+
*
|
|
28
|
+
* GROWTH, DECLARED (scale-discipline R-A7). The store is a map keyed by tool
|
|
29
|
+
* NAME, not an append log: one entry per distinct `mcp__…` name the consumer
|
|
30
|
+
* has ever called, each rewritten in place at most once per day. The bound is
|
|
31
|
+
* therefore the size of the neighbour's tool surface — tens of entries, not a
|
|
32
|
+
* series — so it needs no TTL job. The reader applies the window; entries older
|
|
33
|
+
* than it stay on disk and stop counting, which is why a stale entry is inert
|
|
34
|
+
* rather than wrong.
|
|
35
|
+
*/
|
|
36
|
+
import * as fs from 'node:fs';
|
|
37
|
+
import * as path from 'node:path';
|
|
38
|
+
|
|
39
|
+
/** Host-side prefix of every MCP tool name: `mcp__<server>__<tool>`. */
|
|
40
|
+
export const MCP_TOOL_PREFIX = 'mcp__';
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The store, relative to the project root. Gitignored; see the header.
|
|
44
|
+
*
|
|
45
|
+
* A literal with forward slashes rather than `path.join`, and the reason is
|
|
46
|
+
* measured rather than stylistic: `path.join` here is a top-level call, so the
|
|
47
|
+
* `node:path` namespace import survives into the hook bundle even though every
|
|
48
|
+
* function in this module that needs it is tree-shaken out of that bundle.
|
|
49
|
+
* `path.join(root, REL)` at each call site normalises the separator on Windows,
|
|
50
|
+
* so the only thing lost is the authoring convention.
|
|
51
|
+
*/
|
|
52
|
+
export const FOREIGN_TOOL_USE_REL = 'agents/runtime/neighbour-tool-use.json';
|
|
53
|
+
|
|
54
|
+
/** The census window. 30 days, matching the step that asked for the number. */
|
|
55
|
+
export const TOOL_USE_WINDOW_DAYS = 30;
|
|
56
|
+
|
|
57
|
+
/** The filename the writer's root is the directory of. */
|
|
58
|
+
const SETTINGS_FILENAME = '.agent-settings.yml';
|
|
59
|
+
|
|
60
|
+
/** `YYYY-MM-DD` in UTC — the granularity the store keys on. */
|
|
61
|
+
export function usageDay(now: Date): string {
|
|
62
|
+
return now.toISOString().slice(0, 10);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const DAY_RE = /^\d{4}-\d{2}-\d{2}$/u;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Is this store value a day the window comparison can be trusted on?
|
|
69
|
+
*
|
|
70
|
+
* The comparison is lexicographic, which is exactly right for `YYYY-MM-DD` and
|
|
71
|
+
* quietly wrong for anything else: `'TODO'`, `'unknown'` and `'hand edited'`
|
|
72
|
+
* all sort ABOVE a real cutoff, so without this check a hand-edited or
|
|
73
|
+
* half-written store inflates the count instead of being ignored. The reader's
|
|
74
|
+
* promise is that a malformed store yields nothing, and a malformed VALUE is
|
|
75
|
+
* the likelier half of that.
|
|
76
|
+
*/
|
|
77
|
+
export function isUsageDay(value: unknown): value is string {
|
|
78
|
+
return typeof value === 'string' && DAY_RE.test(value);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* The `.mcp.json` key as it appears inside a tool name.
|
|
83
|
+
*
|
|
84
|
+
* Hosts sanitise the key before embedding it — a server registered as
|
|
85
|
+
* `claude.ai Claude Docs` arrives as `mcp__claude_ai_Claude_Docs__…`. Normalising
|
|
86
|
+
* BOTH sides the same way is what keeps a matched count from quietly being a
|
|
87
|
+
* zero: an exact-key match would report `0` for every server whose name carries
|
|
88
|
+
* a dot or a space, which reads as "never used" rather than as "never matched".
|
|
89
|
+
*/
|
|
90
|
+
export function serverSegment(key: string): string {
|
|
91
|
+
return key.replace(/[^A-Za-z0-9_-]/gu, '_');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Does `tool` belong to the server whose sanitised key is `segment`?
|
|
96
|
+
*
|
|
97
|
+
* Driven by the KNOWN segment rather than by parsing the tool name, and that
|
|
98
|
+
* direction is the whole correctness argument. Parsing — split at the first
|
|
99
|
+
* `__` after the prefix — is ambiguous exactly where {@link serverSegment} is
|
|
100
|
+
* lossy: `Acme Inc. Tools` sanitises to `Acme_Inc__Tools`, whose own name
|
|
101
|
+
* contains the separator, so a parser reads the server as `Acme_Inc` and the
|
|
102
|
+
* census reports `0` for a server in daily use. That is the same never-matched
|
|
103
|
+
* zero `serverSegment` exists to prevent, reintroduced one layer down. Matching
|
|
104
|
+
* against the keys `.mcp.json` actually lists cannot be ambiguous, because the
|
|
105
|
+
* candidate set is finite and known.
|
|
106
|
+
*/
|
|
107
|
+
export function toolBelongsTo(tool: string, segment: string): boolean {
|
|
108
|
+
if (!tool.startsWith(MCP_TOOL_PREFIX)) return false;
|
|
109
|
+
const rest = tool.slice(MCP_TOOL_PREFIX.length);
|
|
110
|
+
return rest === segment || rest.startsWith(`${segment}__`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* The directory the store lives under, from any directory inside the project.
|
|
115
|
+
*
|
|
116
|
+
* Both the writer and the reader call this one function, so the store is
|
|
117
|
+
* rooted at the directory holding `.agent-settings.yml` on both sides: a
|
|
118
|
+
* session started in a subdirectory would otherwise scatter one store per
|
|
119
|
+
* directory, and a monorepo with settings at the repo root and `.mcp.json`
|
|
120
|
+
* under `packages/web` would read `0` for a server in daily use. A fixture
|
|
121
|
+
* still pins the two sides equal end to end.
|
|
122
|
+
*
|
|
123
|
+
* No settings file on any ancestor → `start`.
|
|
124
|
+
*/
|
|
125
|
+
export function resolveStoreRoot(start: string): string {
|
|
126
|
+
let dir = path.resolve(start);
|
|
127
|
+
for (;;) {
|
|
128
|
+
try {
|
|
129
|
+
if (fs.statSync(path.join(dir, SETTINGS_FILENAME)).isFile()) return dir;
|
|
130
|
+
} catch {
|
|
131
|
+
// Not here — keep walking.
|
|
132
|
+
}
|
|
133
|
+
const parent = path.dirname(dir);
|
|
134
|
+
if (parent === dir) return path.resolve(start);
|
|
135
|
+
dir = parent;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Distinct tools seen per server inside the window, keyed by server segment.
|
|
141
|
+
*
|
|
142
|
+
* `segments` is the closed candidate set — the sanitised `.mcp.json` keys. Every
|
|
143
|
+
* one of them appears in the result, so a server with no sightings reads `0`
|
|
144
|
+
* rather than being absent. Longest segment wins where one is a prefix of
|
|
145
|
+
* another, so a tool is counted once.
|
|
146
|
+
*
|
|
147
|
+
* A missing, unreadable or malformed store yields all-zero — true of what was
|
|
148
|
+
* observed. It does not mean the server was unused, and the census says so next
|
|
149
|
+
* to the number.
|
|
150
|
+
*/
|
|
151
|
+
export function toolsUsedByServer(
|
|
152
|
+
projectRoot: string,
|
|
153
|
+
now: Date,
|
|
154
|
+
segments: Iterable<string>,
|
|
155
|
+
): Map<string, number> {
|
|
156
|
+
const known = [...new Set(segments)].sort((a, b) => b.length - a.length);
|
|
157
|
+
const counts = new Map<string, number>(known.map((s) => [s, 0]));
|
|
158
|
+
if (known.length === 0) return counts;
|
|
159
|
+
|
|
160
|
+
let decoded: unknown;
|
|
161
|
+
try {
|
|
162
|
+
const root = resolveStoreRoot(projectRoot);
|
|
163
|
+
decoded = JSON.parse(fs.readFileSync(path.join(root, FOREIGN_TOOL_USE_REL), 'utf-8'));
|
|
164
|
+
} catch {
|
|
165
|
+
return counts;
|
|
166
|
+
}
|
|
167
|
+
if (typeof decoded !== 'object' || decoded === null || Array.isArray(decoded)) return counts;
|
|
168
|
+
|
|
169
|
+
const today = usageDay(now);
|
|
170
|
+
// Inclusive of both ends, so the span is WINDOW days and not WINDOW+1: a
|
|
171
|
+
// floor computed at `now - 30d` would count 31 distinct days under a field
|
|
172
|
+
// named `tools_used_30d`.
|
|
173
|
+
const floor = usageDay(new Date(now.getTime() - (TOOL_USE_WINDOW_DAYS - 1) * 86_400_000));
|
|
174
|
+
|
|
175
|
+
for (const [tool, seen] of Object.entries(decoded as Record<string, unknown>)) {
|
|
176
|
+
// A future date is as much a sign of a bad write as a malformed one,
|
|
177
|
+
// and without the upper bound a single `9999-01-01` counts forever.
|
|
178
|
+
if (!isUsageDay(seen) || seen < floor || seen > today) continue;
|
|
179
|
+
for (const segment of known) {
|
|
180
|
+
if (toolBelongsTo(tool, segment)) {
|
|
181
|
+
counts.set(segment, (counts.get(segment) ?? 0) + 1);
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return counts;
|
|
187
|
+
}
|
|
@@ -41,6 +41,50 @@
|
|
|
41
41
|
* over-filtering would silently stop pinning real prompts, which is worse.
|
|
42
42
|
* Measured across 1 540 user-role entries: 509 open at character zero with a
|
|
43
43
|
* marker, and in 0 of them does human text survive wrapper removal.
|
|
44
|
+
*
|
|
45
|
+
* `<agent-message` added 2026-10-03, by the same under-filtering it warns
|
|
46
|
+
* about: a subagent hand-back reaches this slot as `<agent-message from="…">`,
|
|
47
|
+
* and 12 of the 153 hook-sourced user records in this project's capture open
|
|
48
|
+
* with it. It pinned a German session to `en`, after which the turn-end gate
|
|
49
|
+
* refused each German reply — a reply language derived from a frame nobody
|
|
50
|
+
* typed. Its literal stops before the `>` because the element carries an
|
|
51
|
+
* attribute; the anchor at character zero is unchanged.
|
|
52
|
+
*
|
|
53
|
+
* **The fourth shape has NO envelope tag, and it is the exception this file's
|
|
54
|
+
* own "structural, not linguistic" rule has to name rather than quietly break.**
|
|
55
|
+
* The host resumes a rate-limited session with a plain sentence —
|
|
56
|
+
* `Your claude.ai usage limit has reset. Continue the task you were working
|
|
57
|
+
* on…` — carrying no element, no bracket and no stamp. 5 of the 153
|
|
58
|
+
* hook-sourced user records in this project's capture open with it. Measured
|
|
59
|
+
* 2026-10-03: it pinned a German session to `en` with `prompt_chars: 148`,
|
|
60
|
+
* which is that sentence's exact length, and every German reply for the rest
|
|
61
|
+
* of the run was then refused by the turn-end gate as wrong-language.
|
|
62
|
+
*
|
|
63
|
+
* Matching prose is what the paragraph above warns against, so what makes this
|
|
64
|
+
* one admissible is stated rather than assumed: it is a FIXED host string, not
|
|
65
|
+
* a linguistic pattern, and the character-zero anchor does the same work a tag
|
|
66
|
+
* would — a human asking *why did I get "Your claude.ai usage limit has
|
|
67
|
+
* reset"* does not open with it. A test pins that direction. If the host ever
|
|
68
|
+
* reworded the sentence this guard would silently stop matching, which is the
|
|
69
|
+
* honest failure mode: it under-filters back to today's behaviour rather than
|
|
70
|
+
* misfiring on a real turn.
|
|
71
|
+
*
|
|
72
|
+
* It is deliberately NOT added to `machine_wake.ts`. There the same
|
|
73
|
+
* misclassification retains an authorization instead of mis-pinning a reply,
|
|
74
|
+
* and a prose match is too weak a warrant for that consequence — see the
|
|
75
|
+
* asymmetry note below, which this is the first real test of.
|
|
76
|
+
*
|
|
77
|
+
* **A SECOND VOCABULARY ANSWERS THIS SAME QUESTION AND THE TWO HAVE DRIFTED.**
|
|
78
|
+
* `_lib/machine_wake.ts`'s `humanTypedThisTurn` guards per-turn STATE on this
|
|
79
|
+
* slot (the git-authorization ledger, the suggestion latch) while this one
|
|
80
|
+
* guards the language pin. As of this change both know `<task-notification>`,
|
|
81
|
+
* `<system-reminder>` and `<agent-message`; only this one knows
|
|
82
|
+
* `[SYSTEM NOTIFICATION` and `<local-command-caveat>`. That asymmetry is NOT
|
|
83
|
+
* obviously a defect and was deliberately left: mistaking a real turn for a
|
|
84
|
+
* machine one costs a mis-pinned reply here and a RETAINED AUTHORIZATION
|
|
85
|
+
* there, so the two sides do not want the same error budget — and
|
|
86
|
+
* `<local-command-caveat>` in particular precedes a real typed prompt. Unify
|
|
87
|
+
* them only with that asymmetry answered, never as tidying.
|
|
44
88
|
*/
|
|
45
89
|
export function isSyntheticPrompt(prompt: string): boolean {
|
|
46
90
|
const head = prompt.trimStart().slice(0, 400);
|
|
@@ -49,6 +93,8 @@ export function isSyntheticPrompt(prompt: string): boolean {
|
|
|
49
93
|
head.startsWith("<task-notification>") ||
|
|
50
94
|
head.startsWith("<system-reminder>") ||
|
|
51
95
|
head.startsWith("<local-command-caveat>") ||
|
|
96
|
+
head.startsWith("<agent-message") ||
|
|
97
|
+
head.startsWith("Your claude.ai usage limit has reset") ||
|
|
52
98
|
/^\[SYSTEM NOTIFICATION\b/.test(head)
|
|
53
99
|
);
|
|
54
100
|
}
|