@event4u/agent-config 16.1.0 → 16.3.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 +98 -257
- package/README.md +14 -12
- package/dist/agent-src/commands/analyze/inbox.md +1 -1
- 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/feature/plan.md +1 -1
- package/dist/agent-src/commands/feature/roadmap.md +1 -1
- package/dist/agent-src/commands/fix/ci.md +1 -1
- package/dist/agent-src/commands/implement-ticket.md +1 -1
- package/dist/agent-src/commands/jira-ticket.md +1 -1
- 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/process-full.md +5 -5
- package/dist/agent-src/commands/roadmap/resolve-blockers.md +361 -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/work.md +4 -0
- 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/execution/auto-dispatch-classification.md +6 -6
- package/dist/agent-src/contexts/execution/autonomy-mechanics.md +4 -8
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +32 -2
- package/dist/agent-src/contexts/execution/session-canary-enforcement-history.md +67 -0
- package/dist/agent-src/contexts/execution/toolchain-resolver.md +101 -7
- package/dist/agent-src/rules/active-remediation.md +1 -1
- package/dist/agent-src/rules/autonomous-execution.md +1 -0
- package/dist/agent-src/rules/broken-access-control.md +1 -0
- package/dist/agent-src/rules/command-suggestion-policy.md +1 -0
- package/dist/agent-src/rules/context-hygiene.md +1 -1
- package/dist/agent-src/rules/decision-revisit-gate.md +16 -33
- package/dist/agent-src/rules/design-fidelity.md +13 -16
- package/dist/agent-src/rules/doc-screenshot-hygiene.md +1 -0
- package/dist/agent-src/rules/domain-safety-disclaimer.md +1 -0
- package/dist/agent-src/rules/domain-safety-pii.md +1 -0
- package/dist/agent-src/rules/domain-safety-retention.md +1 -0
- package/dist/agent-src/rules/engineering-safety-floor.md +1 -0
- package/dist/agent-src/rules/evaluator-independence.md +3 -2
- package/dist/agent-src/rules/fast-path-marker-visibility.md +1 -0
- package/dist/agent-src/rules/finance-safety-floor.md +1 -0
- package/dist/agent-src/rules/git-history-discipline.md +1 -0
- package/dist/agent-src/rules/image-likeness-and-rights.md +1 -0
- package/dist/agent-src/rules/legal-safety-floor.md +3 -2
- package/dist/agent-src/rules/lethal-trifecta-guard.md +1 -0
- package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +1 -0
- package/dist/agent-src/rules/media-governance-routing.md +14 -17
- package/dist/agent-src/rules/persona-governance.md +2 -2
- package/dist/agent-src/rules/provider-lifecycle-discipline.md +1 -1
- package/dist/agent-src/rules/question-not-instruction.md +1 -0
- package/dist/agent-src/rules/roadmap-progress-sync.md +1 -1
- package/dist/agent-src/rules/runtime-safety.md +1 -0
- package/dist/agent-src/rules/secret-vcs-guard.md +1 -0
- package/dist/agent-src/rules/security-sensitive-stop.md +3 -2
- package/dist/agent-src/rules/session-canary.md +8 -62
- package/dist/agent-src/rules/settings-ask-protocol.md +1 -0
- package/dist/agent-src/rules/source-confidentiality.md +1 -0
- package/dist/agent-src/rules/strategy-safety-floor.md +1 -0
- package/dist/agent-src/rules/tool-safety.md +1 -0
- package/dist/agent-src/rules/ui-audit-gate.md +1 -2
- package/dist/agent-src/rules/untrusted-input-defense.md +1 -0
- package/dist/agent-src/rules/user-interrupt-priority.md +1 -0
- 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/update_roadmap_progress.ts +4 -1
- package/dist/agent-src/skills/accessibility-auditor/data/aria-patterns.csv +1 -1
- package/dist/agent-src/skills/accessibility-auditor/data/manifest.json +2 -2
- 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/code-intelligence/SKILL.md +14 -2
- package/dist/agent-src/skills/design-review/SKILL.md +30 -4
- package/dist/agent-src/skills/design-review/references/verification-automation.md +32 -0
- package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +1 -1
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +18 -3
- package/dist/agent-src/skills/existing-ui-audit/references/component-taxonomy-detection.md +56 -0
- package/dist/agent-src/skills/fe-design/SKILL.md +1 -1
- package/dist/agent-src/skills/iconography/SKILL.md +1 -1
- package/dist/agent-src/skills/js-library-packaging/SKILL.md +2 -2
- package/dist/agent-src/skills/markitdown/SKILL.md +4 -0
- package/dist/agent-src/skills/monorepo-workspace/SKILL.md +1 -1
- package/dist/agent-src/skills/playwright-testing/SKILL.md +30 -6
- 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/review-routing/SKILL.md +2 -2
- package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +1 -1
- package/dist/agent-src/skills/storybook-workshop/SKILL.md +3 -3
- package/dist/agent-src/skills/tailwind-engineer/SKILL.md +1 -1
- 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/ui-component-architect/SKILL.md +25 -2
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/roadmaps.md +30 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +302 -21
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
- package/dist/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
- package/dist/agent-src/templates/scripts/work_engine/state.ts +57 -7
- package/dist/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
- package/dist/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
- package/dist/cli/python/knowledge_ingest.js +1 -1
- package/dist/cli/python/workspace_drive.js +1 -1
- package/dist/cli/registry.js +1 -1
- package/dist/cli-delegate/archive_completed_roadmaps.js +92 -12
- package/dist/cli-delegate/check_update_banner.js +12 -1
- package/dist/cli-delegate/chunk-523HCFYO.js +90 -0
- package/dist/cli-delegate/{chunk-M2DPGFZ4.js → chunk-6V6TA25H.js} +22 -152
- package/dist/cli-delegate/chunk-7IQE3D5S.js +191 -0
- package/dist/cli-delegate/{chunk-6KFEC3G7.js → chunk-CQWX336L.js} +1 -1
- package/dist/cli-delegate/chunk-DEIV7XP7.js +108 -0
- package/dist/cli-delegate/chunk-EEHDXSYD.js +2040 -0
- package/dist/cli-delegate/{chunk-P6ZHP5W2.js → chunk-GKE7ZVFA.js} +4 -4
- package/dist/cli-delegate/chunk-JQOM7TTM.js +1201 -0
- package/dist/cli-delegate/{chunk-UNCRJBT3.js → chunk-JQVYKIZA.js} +41 -0
- package/dist/cli-delegate/{chunk-FC75PGJE.js → chunk-JY2I2DXV.js} +12 -1
- package/dist/cli-delegate/chunk-KB5FZ655.js +208 -0
- package/dist/cli-delegate/{chunk-YUM5KURG.js → chunk-KR6EM23A.js} +235 -1455
- package/dist/cli-delegate/{chunk-LTYVIY76.js → chunk-LIYTLH6O.js} +1 -1
- package/dist/cli-delegate/chunk-NQ7WT43P.js +2729 -0
- package/dist/cli-delegate/chunk-NXEILF2K.js +2160 -0
- package/dist/cli-delegate/{chunk-5R7HOLAF.js → chunk-NYX3L2PZ.js} +1 -1
- 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-RGNIWTEA.js → chunk-QLDHIPWR.js} +1 -1
- package/dist/cli-delegate/{chunk-HDVVGPQW.js → chunk-RQNNW7IF.js} +1405 -567
- package/dist/cli-delegate/{chunk-S3VFXMTM.js → chunk-S4UMZNES.js} +1 -1
- package/dist/cli-delegate/{chunk-6KM6ORRY.js → chunk-S4YWNRKO.js} +24 -8
- package/dist/cli-delegate/{chunk-GJCBB2UG.js → chunk-TRI6DQZ6.js} +3054 -325
- package/dist/cli-delegate/chunk-ULQRUR23.js +208 -0
- 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 +1 -1
- package/dist/cli-delegate/cmd_brand_status.js +1 -1
- package/dist/cli-delegate/cmd_conformance.js +1862 -809
- package/dist/cli-delegate/cmd_doctor.js +13 -6
- package/dist/cli-delegate/cmd_doctor_neighbours.js +23 -0
- package/dist/cli-delegate/cmd_explain.js +5 -5
- package/dist/cli-delegate/cmd_export.js +11 -8
- package/dist/cli-delegate/cmd_fleet.js +3 -3
- package/dist/cli-delegate/cmd_handoff.js +10 -7
- package/dist/cli-delegate/cmd_mcp_available.js +1 -1
- package/dist/cli-delegate/cmd_memory_get.js +3 -1
- package/dist/cli-delegate/cmd_migrate.js +2 -2
- package/dist/cli-delegate/cmd_packs_active.js +3 -3
- package/dist/cli-delegate/cmd_preflight.js +6 -6
- package/dist/cli-delegate/cmd_prune.js +11 -8
- package/dist/cli-delegate/cmd_refresh.js +2 -1
- package/dist/cli-delegate/cmd_route_audit.js +8 -6
- package/dist/cli-delegate/cmd_route_explain.js +4 -4
- package/dist/cli-delegate/cmd_session_recycle.js +4 -3
- package/dist/cli-delegate/cmd_settings_get.js +1 -1
- package/dist/cli-delegate/cmd_settings_migrate.js +11 -8
- package/dist/cli-delegate/cmd_settings_set.js +4 -4
- package/dist/cli-delegate/cmd_sync.js +11 -8
- package/dist/cli-delegate/cmd_uninstall.js +11 -8
- package/dist/cli-delegate/cmd_update.js +1 -1
- package/dist/cli-delegate/cmd_upgrade.js +3 -2
- package/dist/cli-delegate/cmd_validate.js +11 -8
- package/dist/cli-delegate/cmd_versions.js +1 -1
- package/dist/cli-delegate/deferral_carry.js +16 -0
- package/dist/cli-delegate/gate_execute.js +2 -2
- package/dist/cli-delegate/roadmap_gates.js +5 -5
- package/dist/cli-delegate/update_roadmap_progress.js +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +132 -80
- 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 +7 -5
- package/dist/discovery/trust-report.md +2 -2
- package/dist/discovery/workspaces.json +6 -4
- package/dist/hooks/dispatch.js +7972 -5186
- package/dist/hooks/dispatch.sha256 +1 -0
- 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 +2226 -1055
- package/dist/install/installThinLayer.js +261 -0
- package/dist/install/installedRuleLinks.js +176 -0
- package/dist/install/wizard-plan.js +24 -0
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +40913 -34865
- package/dist/scripts/_lib/agent_settings.js +11 -0
- package/dist/scripts/_lib/claude_settings_hooks.js +10 -8
- package/dist/scripts/_lib/installed_layer.js +378 -0
- package/dist/scripts/_lib/lean_projection_mode.js +218 -15
- 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/thin_rules.js +458 -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/schemas/settings.js +29 -0
- package/dist/shared/skillRanking.js +74 -13
- package/dist/ui/assets/index-Cwq4d0hX.js +23 -0
- package/dist/ui/assets/index-Cyjy8bWO.css +1 -0
- package/dist/ui/index.html +2 -2
- package/docs/contracts/settings-classes.md +10 -6
- package/package.json +4 -4
- 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/update_roadmap_progress.ts +4 -1
- package/src/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
- package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +302 -21
- package/src/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
- package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
- package/src/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
- package/src/agent-src/templates/scripts/work_engine/state.ts +57 -7
- package/src/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
- package/src/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
- package/src/config/agent-settings.template.yml +91 -22
- package/src/config/ci-local-parity.yml +18 -0
- package/src/config/continuity-surface.json +9 -2
- package/src/config/evaluator-budgets.json +4 -4
- package/src/config/gate-coverage.yml +327 -3
- package/src/config/gate-violation-baselines.json +21 -9
- package/src/config/hook-bundle-budget.json +25 -0
- package/src/config/hook-latency-budget.json +21 -0
- package/src/config/hook-token-budget.json +8 -3
- package/src/config/host-instruction-limits.json +33 -0
- package/src/config/metric-registry.yml +114 -0
- package/src/config/placeholder-drift-budget.json +10 -0
- package/src/config/routing-coverage-seed.json +38 -3
- 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 +99 -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 +312 -0
- package/src/install/installedRuleLinks.ts +220 -0
- package/src/install/wizard-plan.ts +24 -0
- package/src/scripts/_cli/cmd_doctor.ts +28 -90
- package/src/scripts/_cli/cmd_doctor_neighbours.ts +293 -0
- package/src/scripts/_cli/doctor_execution.ts +100 -17
- package/src/scripts/_cli/doctor_network_posture.ts +226 -0
- package/src/scripts/_dispatch.bash +16 -2
- package/src/scripts/_lib/agent_settings.ts +11 -0
- package/src/scripts/_lib/body_portable.ts +102 -0
- package/src/scripts/_lib/cc_transcript.ts +132 -1
- package/src/scripts/_lib/claude_settings_hooks.ts +10 -8
- package/src/scripts/_lib/concern_sla_window.ts +200 -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/forge_protection.ts +114 -0
- package/src/scripts/_lib/forge_reader.ts +558 -0
- package/src/scripts/_lib/gitignore_block.ts +314 -0
- package/src/scripts/_lib/global_deploy_inventory.ts +32 -0
- package/src/scripts/_lib/graph_feeder_record.ts +214 -0
- package/src/scripts/_lib/hook_settings.ts +73 -0
- package/src/scripts/_lib/host_capability.ts +40 -19
- package/src/scripts/_lib/host_env_write.ts +111 -0
- package/src/scripts/_lib/host_hook_merge.ts +165 -0
- package/src/scripts/_lib/installed_layer.ts +538 -0
- package/src/scripts/_lib/json_merge.ts +82 -0
- package/src/scripts/_lib/json_pointers.ts +93 -11
- package/src/scripts/_lib/lean_projection_mode.ts +275 -15
- package/src/scripts/_lib/llm_proposer_transport.ts +14 -4
- package/src/scripts/_lib/machine_wake.ts +16 -3
- package/src/scripts/_lib/neighbour_census.ts +760 -0
- package/src/scripts/_lib/neighbour_scan.ts +122 -0
- package/src/scripts/_lib/neighbour_tool_use.ts +189 -0
- package/src/scripts/_lib/obligations.ts +119 -8
- package/src/scripts/_lib/probe_inputs.ts +271 -0
- package/src/scripts/_lib/prompt_shape.ts +46 -0
- package/src/scripts/_lib/read_surface_scan.ts +322 -0
- package/src/scripts/_lib/release_highlights.ts +2 -1
- package/src/scripts/_lib/release_migration_gate.ts +19 -3
- package/src/scripts/_lib/reserved_name_sweep.ts +73 -0
- package/src/scripts/_lib/retrieval_sanitize.ts +77 -16
- 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/shell_write_shapes.ts +108 -0
- package/src/scripts/_lib/skill_origin.ts +254 -0
- package/src/scripts/_lib/structural_hiding.ts +622 -0
- package/src/scripts/_lib/structured_ask.ts +64 -21
- package/src/scripts/_lib/thin_rules.ts +540 -0
- package/src/scripts/_lib/touched_file_quality.ts +434 -0
- package/src/scripts/_lib/turn_end_refusals.ts +600 -4
- package/src/scripts/_lib/turn_end_transcript.ts +216 -0
- package/src/scripts/_lib/verification_command.ts +324 -0
- package/src/scripts/_lib/verification_evidence.ts +647 -0
- package/src/scripts/_lib/verify_clause.ts +280 -0
- package/src/scripts/_lib/windsurf_render.ts +63 -0
- 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 +1 -1
- package/src/scripts/ai_council/vendor_defaults.ts +45 -0
- package/src/scripts/ai_team/team_dispatch.ts +4 -0
- package/src/scripts/audit_skill_overlap.ts +261 -0
- package/src/scripts/before_complete_hook.ts +573 -25
- package/src/scripts/bench_hook_latency.ts +412 -5
- package/src/scripts/bench_touched_file_quality.ts +238 -0
- package/src/scripts/cache_realization_report.ts +61 -1
- package/src/scripts/check_argument_hint.ts +287 -0
- package/src/scripts/check_claims.ts +125 -1
- package/src/scripts/check_enforcement_coverage.ts +38 -9
- package/src/scripts/check_enforcement_matrix.ts +621 -22
- package/src/scripts/check_estate_count.ts +130 -153
- package/src/scripts/check_finding_dispositions.ts +138 -7
- package/src/scripts/check_generator_sync.ts +71 -0
- package/src/scripts/check_hook_bundle_composition.ts +370 -0
- package/src/scripts/check_host_docs_digest.ts +786 -0
- package/src/scripts/check_host_format_column.ts +559 -0
- package/src/scripts/check_input_references.ts +228 -0
- package/src/scripts/check_inputs_parity.ts +235 -0
- package/src/scripts/check_kernel_edit_ratified.ts +89 -9
- package/src/scripts/check_kill_switch_table.ts +294 -0
- package/src/scripts/check_memory.ts +7 -1
- package/src/scripts/check_placeholder_drift.ts +221 -0
- package/src/scripts/check_read_surface_coverage.ts +515 -0
- package/src/scripts/check_release_highlights.ts +10 -0
- package/src/scripts/check_release_holds.ts +2 -1
- package/src/scripts/check_release_pr_shape.ts +38 -15
- package/src/scripts/check_routing_coverage.ts +378 -11
- package/src/scripts/check_standing_rule_delivery.ts +33 -13
- package/src/scripts/check_static_parity.ts +17 -0
- package/src/scripts/check_verify_expectation_delta.ts +521 -0
- package/src/scripts/ci_settle.ts +145 -19
- package/src/scripts/closure_scan.ts +115 -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/compile_hook_manifest.ts +90 -14
- package/src/scripts/condense.ts +14 -27
- package/src/scripts/estate_exemption_shape.ts +202 -0
- package/src/scripts/external_sources_denylist.json +29 -4
- package/src/scripts/generate_host_cost_table.ts +1 -1
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +192 -14
- package/src/scripts/hooks/block_config_weakening.ts +294 -4
- package/src/scripts/hooks/block_kernel_rule_writes.ts +9 -78
- package/src/scripts/hooks/block_plumbing_writes.ts +342 -0
- package/src/scripts/hooks/bundle_integrity.ts +291 -0
- package/src/scripts/hooks/chain_nudge_hook.ts +1 -2
- package/src/scripts/hooks/code_graph_context_hook.ts +253 -25
- package/src/scripts/hooks/comment_discipline_hook.ts +1 -3
- package/src/scripts/hooks/concern_failure_policy.ts +280 -0
- package/src/scripts/hooks/concern_registry.ts +2 -0
- package/src/scripts/hooks/concern_timings.ts +94 -0
- package/src/scripts/hooks/delegation_nudge_hook.ts +1 -2
- package/src/scripts/hooks/design_pass_hook.ts +100 -9
- package/src/scripts/hooks/design_slop_hook.ts +1 -2
- package/src/scripts/hooks/dispatch_hook.ts +77 -71
- package/src/scripts/hooks/edit_shape_hook.ts +1 -2
- package/src/scripts/hooks/end_review_nudge_hook.ts +1 -1
- package/src/scripts/hooks/evidence_independence.ts +1 -2
- package/src/scripts/hooks/exit_codes.ts +129 -0
- package/src/scripts/hooks/host_lowering.json +1 -0
- package/src/scripts/hooks/host_lowering.ts +146 -3
- package/src/scripts/hooks/host_lowering.yaml +262 -46
- package/src/scripts/hooks/host_semantics.ts +30 -2
- package/src/scripts/hooks/injection_budget.ts +9 -2
- package/src/scripts/hooks/interruption_ledger_hook.ts +1 -2
- package/src/scripts/hooks/journal_record_hook.ts +1 -2
- package/src/scripts/hooks/memory_recall_hook.ts +1 -2
- package/src/scripts/hooks/obligation_settle_hook.ts +151 -5
- package/src/scripts/hooks/one_question_per_ask_hook.ts +43 -6
- package/src/scripts/hooks/orchestration_record_hook.ts +1 -2
- package/src/scripts/hooks/payload_stub.ts +10 -1
- package/src/scripts/hooks/permission_gate.ts +1 -2
- package/src/scripts/hooks/push_settle_hook.ts +1 -3
- package/src/scripts/hooks/replay_hook.ts +2 -1
- package/src/scripts/hooks/reread_guard_hook.ts +1 -2
- package/src/scripts/hooks/review_baseline_hook.ts +1 -2
- package/src/scripts/hooks/rtk_wrap_hook.ts +1 -2
- package/src/scripts/hooks/rule_inject_hook.ts +765 -68
- package/src/scripts/hooks/run_continuation_hook.ts +13 -15
- package/src/scripts/hooks/skill_route_hook.ts +1 -2
- package/src/scripts/hooks/source_first_gate_hook.ts +1 -2
- package/src/scripts/hooks/spawn_guard_shadow_hook.ts +1 -2
- package/src/scripts/hooks/subagent_ledger_hook.ts +1 -2
- package/src/scripts/hooks/suggestion_capture_hook.ts +1 -1
- package/src/scripts/hooks/table_fingerprint.ts +70 -0
- package/src/scripts/hooks/telemetry_flush_hook.ts +1 -2
- package/src/scripts/hooks/telemetry_usage_hook.ts +65 -10
- package/src/scripts/hooks/tool_result_bytes_hook.ts +93 -9
- package/src/scripts/hooks/turn_end_gate_hook.ts +421 -324
- package/src/scripts/hooks/ui_route_nudge_hook.ts +1 -2
- package/src/scripts/hooks_status.ts +112 -0
- package/src/scripts/injection_scan_hook.ts +7 -0
- package/src/scripts/install.ts +75 -189
- 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_deny_text.ts +419 -0
- package/src/scripts/lint_exit_codes.ts +262 -0
- package/src/scripts/lint_hook_manifest.ts +108 -0
- package/src/scripts/lint_major_migration_sections.ts +156 -26
- package/src/scripts/lint_pack_boundaries.ts +63 -5
- package/src/scripts/lint_roadmap_blockers.ts +78 -12
- package/src/scripts/lint_rule_law_section.ts +517 -0
- package/src/scripts/lint_skill_descriptions.ts +1 -1
- package/src/scripts/lint_skill_scripts_readonly.ts +1 -1
- package/src/scripts/lint_workflow_security.ts +161 -5
- package/src/scripts/mcp_server/consumer_tool_catalog.json +53 -0
- package/src/scripts/mcp_server/graph_tools.ts +72 -3
- package/src/scripts/mcp_server/prompts.ts +142 -1
- package/src/scripts/measure_routing_signal.ts +54 -16
- package/src/scripts/measure_skill_ranker_baseline.ts +515 -53
- package/src/scripts/measure_turn_end_gate.ts +193 -0
- package/src/scripts/memory_learn_hook.ts +91 -6
- package/src/scripts/memory_lookup.ts +1 -1
- package/src/scripts/model_rule_injection.ts +52 -5
- package/src/scripts/prepack-check.mjs +53 -0
- package/src/scripts/project_thin_rules.ts +34 -327
- package/src/scripts/release_publication.ts +13 -1
- package/src/scripts/report_evidence_temperature.ts +657 -0
- package/src/scripts/report_installed_rule_links.ts +157 -0
- package/src/scripts/report_invocation_surface.ts +383 -0
- package/src/scripts/report_label_agreement.ts +233 -0
- package/src/scripts/report_obligation_carriers.ts +699 -0
- package/src/scripts/report_obligation_mechanism.ts +230 -0
- package/src/scripts/report_obligation_writer_split.ts +236 -0
- package/src/scripts/report_single_token_triggers.ts +277 -0
- package/src/scripts/report_skill_menu_census.ts +208 -0
- package/src/scripts/report_skill_ranker_confusion.ts +442 -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/roadmap_verify_share.ts +213 -0
- package/src/scripts/rule_activation_census.ts +5 -2
- package/src/scripts/rule_trigger_eval.ts +9 -9
- package/src/scripts/schemas/command.schema.json +44 -0
- package/src/scripts/schemas/review-findings.schema.json +7 -2
- package/src/scripts/schemas/rule.schema.json +5 -0
- package/src/scripts/schemas/skill.schema.json +44 -0
- package/src/scripts/self_review_gate.ts +421 -17
- package/src/scripts/semantic_parity_census.ts +314 -0
- package/src/scripts/session_eol_report.ts +54 -1
- package/src/scripts/skill_tools/score_skill_relevance.ts +290 -25
- package/src/scripts/sweep_dead_scan_roots.ts +1 -1
- package/src/scripts/sweep_skill_ranker_signals.ts +129 -0
- package/src/scripts/sync_gitignore.ts +29 -235
- package/src/scripts/trigger_eval_grandfather.json +0 -7
- package/src/scripts/trigger_eval_rotation.ts +122 -27
- package/src/scripts/ui_conformance_probe.ts +156 -4
- package/src/scripts/update_prices.ts +85 -3
- package/src/scripts/write_bundle_digest.mjs +74 -0
- package/src/server/schemas/settings.ts +41 -0
- package/src/shared/skillRanking.ts +117 -11
- package/src/templates/consumer-settings/ONBOARDING.md +1 -0
- package/dist/cli-delegate/chunk-FXYUYU4N.js +0 -4416
- package/dist/cli-delegate/chunk-QJWAUMJW.js +0 -1072
- package/dist/cli-delegate/chunk-YDPILCOL.js +0 -152
- package/dist/ui/assets/index-DH1xc62v.js +0 -29
- package/dist/ui/assets/index-jSfMW8_t.css +0 -1
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One spelling of "a rule's law section" — the unit three gates and one
|
|
3
|
+
* projector all price.
|
|
4
|
+
*
|
|
5
|
+
* A **law section** is a heading whose title matches `\biron\s+laws?\b` plus
|
|
6
|
+
* everything under it up to the next heading of the same or a shallower depth.
|
|
7
|
+
* A rule may carry several (`direct-answers` has three, `no-cheap-questions`
|
|
8
|
+
* six); the rule's law is all of them, joined by a blank line, because each one
|
|
9
|
+
* states an obligation and a stub carrying only the first would ship a rule
|
|
10
|
+
* whose other laws silently vanished.
|
|
11
|
+
*
|
|
12
|
+
* WHY A SHARED MODULE RATHER THAN A REGEX PER CALLER. The projector
|
|
13
|
+
* (`project_thin_rules.ts`) byte-copies this text into a stub and digests it;
|
|
14
|
+
* the lint (`lint_rule_law_section.ts`) holds it under a ceiling. If those two
|
|
15
|
+
* disagreed about where a section ends, the lint would certify a length the
|
|
16
|
+
* projection does not carry — the exact drift `THIN_ENTRY_MARKER`'s own comment
|
|
17
|
+
* argues against one file over.
|
|
18
|
+
*
|
|
19
|
+
* The heading regex is deliberately the SAME one `check_iron_law_prominence.ts`
|
|
20
|
+
* uses, so "has a law section" here and "is an Iron Law heading" there cannot
|
|
21
|
+
* diverge. That gate owns prominence (H2, near the top); this module owns
|
|
22
|
+
* extent and size.
|
|
23
|
+
*
|
|
24
|
+
* WHAT THIS DOES NOT CLAIM. Not that the extracted text IS the rule's obligation. A rule can state its
|
|
25
|
+
* law in bold prose or a bare fence under no heading at all — several of the 35
|
|
26
|
+
* routed rules without a heading match do exactly that. A heading match is a
|
|
27
|
+
* STRUCTURAL property, checkable; "this sentence is the obligation" is not, and
|
|
28
|
+
* `report_obligation_carriers` plus the `# obligation: line N` marker is where
|
|
29
|
+
* that question already lives.
|
|
30
|
+
*/
|
|
31
|
+
import * as fs from 'node:fs';
|
|
32
|
+
|
|
33
|
+
/** Same literal as `check_iron_law_prominence.ts` — one definition, two readers. */
|
|
34
|
+
export const IRON_LAW_RE = /\biron\s+laws?\b/i;
|
|
35
|
+
|
|
36
|
+
const HEADING_RE = /^(#{1,6})\s+(.+?)\s*$/;
|
|
37
|
+
const FENCE_RE = /^\s*```/;
|
|
38
|
+
|
|
39
|
+
export interface LawSection {
|
|
40
|
+
/** Heading title, verbatim. */
|
|
41
|
+
title: string;
|
|
42
|
+
/** Heading depth (1–6). */
|
|
43
|
+
depth: number;
|
|
44
|
+
/** 1-based line of the heading within the body. */
|
|
45
|
+
line: number;
|
|
46
|
+
/** Heading line plus its section, trimmed. */
|
|
47
|
+
text: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Return `[frontmatter_including_fences, body]`. Empty frontmatter if none. */
|
|
51
|
+
export function splitFrontmatter(text: string): [string, string] {
|
|
52
|
+
if (text.startsWith('---\n')) {
|
|
53
|
+
const end = text.indexOf('\n---\n', 4);
|
|
54
|
+
if (end !== -1) {
|
|
55
|
+
return [text.slice(0, end + 5), text.slice(end + 5)];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return ['', text];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The body a runtime delivery carries: frontmatter removed, HTML comments
|
|
63
|
+
* removed, trimmed.
|
|
64
|
+
*
|
|
65
|
+
* Comments are stripped because they are authoring scaffolding
|
|
66
|
+
* (`<!-- risk-review: … -->`, `<!-- harvest:… -->`, `ref-ignore` markers) that
|
|
67
|
+
* no host renders to a model, so charging a rule for them would price the wrong
|
|
68
|
+
* thing. Frontmatter is excluded for the reason `check_rule_stub_ceiling` gives
|
|
69
|
+
* for the same exclusion: it is the routing surface, and a rule that gains a
|
|
70
|
+
* trigger must not trip a ceiling whose message tells the author to move prose.
|
|
71
|
+
*/
|
|
72
|
+
export function ruleBody(text: string): string {
|
|
73
|
+
const [, body] = splitFrontmatter(text);
|
|
74
|
+
return body.replace(/<!--[\s\S]*?-->/g, '').trim();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Every law section in a rule body, in document order. */
|
|
78
|
+
export function lawSections(body: string): LawSection[] {
|
|
79
|
+
const lines = body.split('\n');
|
|
80
|
+
let inFence = false;
|
|
81
|
+
const headings: Array<{ i: number; depth: number; title: string }> = [];
|
|
82
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
83
|
+
const raw = lines[i] as string;
|
|
84
|
+
if (FENCE_RE.test(raw)) {
|
|
85
|
+
inFence = !inFence;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (inFence) continue;
|
|
89
|
+
const m = HEADING_RE.exec(raw);
|
|
90
|
+
if (m) headings.push({ i, depth: (m[1] as string).length, title: (m[2] as string).trim() });
|
|
91
|
+
}
|
|
92
|
+
const out: LawSection[] = [];
|
|
93
|
+
for (let k = 0; k < headings.length; k += 1) {
|
|
94
|
+
const h = headings[k] as { i: number; depth: number; title: string };
|
|
95
|
+
if (!IRON_LAW_RE.test(h.title)) continue;
|
|
96
|
+
let end = lines.length;
|
|
97
|
+
for (let j = k + 1; j < headings.length; j += 1) {
|
|
98
|
+
const n = headings[j] as { i: number; depth: number; title: string };
|
|
99
|
+
if (n.depth <= h.depth) {
|
|
100
|
+
end = n.i;
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
out.push({
|
|
105
|
+
title: h.title,
|
|
106
|
+
depth: h.depth,
|
|
107
|
+
line: h.i + 1,
|
|
108
|
+
text: lines.slice(h.i, end).join('\n').trim(),
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
return out;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* The rule's law, as one byte string — all sections joined by a blank line.
|
|
116
|
+
* `null` when the rule has none, which is a different answer from `''`.
|
|
117
|
+
*/
|
|
118
|
+
export function lawText(body: string): string | null {
|
|
119
|
+
const sections = lawSections(body);
|
|
120
|
+
if (sections.length === 0) return null;
|
|
121
|
+
return sections.map((s) => s.text).join('\n\n');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface RuleLawMeasure {
|
|
125
|
+
id: string;
|
|
126
|
+
/** Characters of the law text, 0 when there is none. */
|
|
127
|
+
lawChars: number;
|
|
128
|
+
/** Number of law sections found. */
|
|
129
|
+
sections: number;
|
|
130
|
+
/** Characters of the comment-stripped body. */
|
|
131
|
+
bodyChars: number;
|
|
132
|
+
/** The law text itself, `null` when absent. */
|
|
133
|
+
law: string | null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Measure one rule file. */
|
|
137
|
+
export function measureRuleFile(id: string, filePath: string): RuleLawMeasure {
|
|
138
|
+
const body = ruleBody(fs.readFileSync(filePath, 'utf-8'));
|
|
139
|
+
const law = lawText(body);
|
|
140
|
+
return {
|
|
141
|
+
id,
|
|
142
|
+
lawChars: law === null ? 0 : law.length,
|
|
143
|
+
sections: lawSections(body).length,
|
|
144
|
+
bodyChars: body.length,
|
|
145
|
+
law,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
27
|
import * as fs from 'node:fs';
|
|
28
|
+
import * as os from 'node:os';
|
|
28
29
|
import * as path from 'node:path';
|
|
29
30
|
|
|
30
31
|
/**
|
|
@@ -140,6 +141,63 @@ export function compareLayers(
|
|
|
140
141
|
};
|
|
141
142
|
}
|
|
142
143
|
|
|
144
|
+
/**
|
|
145
|
+
* The two directories Claude Code loads rules from, user layer first.
|
|
146
|
+
*
|
|
147
|
+
* ONE SOURCE OF TRUTH FOR SCOPE (`road-to-a-rule-carrier-that-works-outside-the-repo`
|
|
148
|
+
* step 1.3). Two readers need this pair and both used to spell it out inline:
|
|
149
|
+
* the installer's overlap gate, and — once the carrier started resolving bodies
|
|
150
|
+
* from the package rather than from the workspace — the delivery scope itself.
|
|
151
|
+
* A carrier scoping on one list while the installer gates on another would
|
|
152
|
+
* deliver a rule the install never wrote, which is the state step 1.3 exists to
|
|
153
|
+
* end.
|
|
154
|
+
*
|
|
155
|
+
* It is a UNION and not a precedence chain. The host loads both with no dedup —
|
|
156
|
+
* that is the whole reason this module exists — so a rule present in either
|
|
157
|
+
* layer is loaded, and "what the install carries" is the union of the two.
|
|
158
|
+
*
|
|
159
|
+
* `os.homedir()` rather than a settings key: the installer resolves the user
|
|
160
|
+
* layer the same way, and a second spelling of "where is home" is a second
|
|
161
|
+
* thing to disagree about.
|
|
162
|
+
*/
|
|
163
|
+
export function hostRuleLayerDirs(projectRoot: string): [string, string] {
|
|
164
|
+
return [
|
|
165
|
+
path.join(os.homedir(), '.claude', 'rules'),
|
|
166
|
+
path.join(projectRoot, '.claude', 'rules'),
|
|
167
|
+
];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Rule ids present in either host rule layer, or `null` when NEITHER directory
|
|
172
|
+
* exists.
|
|
173
|
+
*
|
|
174
|
+
* `null` IS A DIFFERENT ANSWER FROM AN EMPTY SET, and the difference is the
|
|
175
|
+
* contract. An empty set means the install wrote a rule layer and it carries
|
|
176
|
+
* nothing, so nothing is in scope. `null` means no rule layer exists to declare
|
|
177
|
+
* a scope at all: a maintainer checkout, or a host this install writes no rules
|
|
178
|
+
* directory for. Scoping to the empty set there would re-create exactly the
|
|
179
|
+
* silence step 1.1 repaired, so the caller applies no filter instead.
|
|
180
|
+
*
|
|
181
|
+
* Names only, never bodies. This runs on the `user_prompt_submit` hot path, and
|
|
182
|
+
* {@link readRuleLayer} beside it reads every file's contents because ITS
|
|
183
|
+
* caller compares them. A scope check never needs a byte of any of them.
|
|
184
|
+
*/
|
|
185
|
+
export function hostRuleLayerIds(projectRoot: string): Set<string> | null {
|
|
186
|
+
const ids = new Set<string>();
|
|
187
|
+
let found = false;
|
|
188
|
+
for (const dir of hostRuleLayerDirs(projectRoot)) {
|
|
189
|
+
let names: string[];
|
|
190
|
+
try {
|
|
191
|
+
names = fs.readdirSync(dir);
|
|
192
|
+
} catch {
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
found = true;
|
|
196
|
+
for (const n of names) if (n.endsWith('.md')) ids.add(n.slice(0, -3));
|
|
197
|
+
}
|
|
198
|
+
return found ? ids : null;
|
|
199
|
+
}
|
|
200
|
+
|
|
143
201
|
/**
|
|
144
202
|
* Read every `.md` in `dir` into a {@link RuleLayer}, or `null` when the
|
|
145
203
|
* directory does not exist.
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shell shapes that mean "this path is being WRITTEN", shared by the
|
|
3
|
+
* PreToolUse guards that refuse a write to a governed file.
|
|
4
|
+
*
|
|
5
|
+
* WHY IT IS SHARED. `block_kernel_rule_writes.ts` grew this parser after the
|
|
6
|
+
* S0.2 spike measured a real gap: a guard keyed on the TOOL NAME let
|
|
7
|
+
* `Bash sed -i … src/rules/commit-policy.md` reach the outcome the Write
|
|
8
|
+
* branch refuses, and a two-step `Write` → `Bash mv` sequence did the same with
|
|
9
|
+
* every individual step allowed. `block_plumbing_writes.ts`
|
|
10
|
+
* (road-to-a-kernel-that-guards-its-plumbing 1.2) refuses a different file set
|
|
11
|
+
* against the same shapes. Two copies of a shell-shape parser is two places a
|
|
12
|
+
* verb can be added to one and forgotten in the other, and the guard that
|
|
13
|
+
* silently stopped recognising `tee` would look exactly like the guard that
|
|
14
|
+
* never had to.
|
|
15
|
+
*
|
|
16
|
+
* DELIBERATELY NARROW (council 2026-08-02 cut, option ii, recorded on the
|
|
17
|
+
* kernel guard and inherited here unchanged): only redirection into the path,
|
|
18
|
+
* an in-place `sed`, a `tee`/`truncate`/`rm`/`shred` naming it, or a
|
|
19
|
+
* `mv`/`cp`/`install`/`rsync` whose DESTINATION it is. Reads stay allowed —
|
|
20
|
+
* `cat`, `grep`, `head`, `diff`, `git show` carry none of these shapes — and no
|
|
21
|
+
* attempt is made to understand arbitrary shell. Recognising every conceivable
|
|
22
|
+
* write verb would make this a shell sandbox, which is the failure mode the
|
|
23
|
+
* council named.
|
|
24
|
+
*
|
|
25
|
+
* KNOWN-OPEN, stated rather than implied: a write routed through an
|
|
26
|
+
* interpreter (`python -c`, a heredoc fed to `sh`) carries none of these
|
|
27
|
+
* shapes and is not detected. This is a guard, not write mediation.
|
|
28
|
+
*/
|
|
29
|
+
import * as path from 'node:path';
|
|
30
|
+
|
|
31
|
+
/** In-place mutators that name their target as a plain argument. */
|
|
32
|
+
const MUTATOR_VERBS: ReadonlySet<string> = new Set(['sed', 'tee', 'truncate', 'rm', 'shred']);
|
|
33
|
+
|
|
34
|
+
/** Verbs whose LAST positional argument is the destination. */
|
|
35
|
+
const DEST_LAST_VERBS: ReadonlySet<string> = new Set(['mv', 'cp', 'install', 'rsync']);
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The first governed target a command WRITES, or `null`.
|
|
39
|
+
*
|
|
40
|
+
* @param command the raw shell string off the tool envelope
|
|
41
|
+
* @param match classifies one token: a non-null return names the governed
|
|
42
|
+
* target that token resolves to, and is what this returns
|
|
43
|
+
*/
|
|
44
|
+
export function shellWriteTarget<T>(command: string, match: (token: string) => T | null): T | null {
|
|
45
|
+
const tokens = command.split(/\s+/).filter((t) => t.length > 0);
|
|
46
|
+
const stripped = tokens.map((t) => t.replace(/^['"]|['"]$/g, ''));
|
|
47
|
+
|
|
48
|
+
// Cheap reject first: no governed path in the string at all.
|
|
49
|
+
if (!stripped.some((t) => match(t) !== null)) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// 1. Redirection into the path: `> p`, `>> p`, `>p`, `>>p`.
|
|
54
|
+
for (let i = 0; i < stripped.length; i += 1) {
|
|
55
|
+
const tok = stripped[i] as string;
|
|
56
|
+
const inline = /^>{1,2}(.+)$/.exec(tok);
|
|
57
|
+
if (inline) {
|
|
58
|
+
const r = match(inline[1] as string);
|
|
59
|
+
if (r !== null) {
|
|
60
|
+
return r;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (tok === '>' || tok === '>>') {
|
|
64
|
+
const next = stripped[i + 1];
|
|
65
|
+
if (typeof next === 'string') {
|
|
66
|
+
const r = match(next);
|
|
67
|
+
if (r !== null) {
|
|
68
|
+
return r;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 2. A mutator verb anywhere in the pipeline, with the path as an argument.
|
|
75
|
+
// `sed` only counts in its in-place form — `sed 's/x/y/' file` prints.
|
|
76
|
+
const verbs = new Set<string>();
|
|
77
|
+
let prevWasSeparator = true;
|
|
78
|
+
for (const tok of stripped) {
|
|
79
|
+
if (['&&', '||', ';', '|'].includes(tok)) {
|
|
80
|
+
prevWasSeparator = true;
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (prevWasSeparator && !tok.startsWith('-')) {
|
|
84
|
+
verbs.add(path.basename(tok));
|
|
85
|
+
prevWasSeparator = false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const sedInPlace = verbs.has('sed') && stripped.some((t) => /^-i/.test(t));
|
|
89
|
+
for (const verb of verbs) {
|
|
90
|
+
if (verb === 'sed' && !sedInPlace) {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (MUTATOR_VERBS.has(verb)) {
|
|
94
|
+
const hit = stripped.map(match).find((r) => r !== null);
|
|
95
|
+
if (hit !== undefined && hit !== null) {
|
|
96
|
+
return hit;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (DEST_LAST_VERBS.has(verb)) {
|
|
100
|
+
const last = stripped[stripped.length - 1] as string;
|
|
101
|
+
const r = match(last);
|
|
102
|
+
if (r !== null) {
|
|
103
|
+
return r;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Who put a ranked skill on disk, and whether its body has been looked at.
|
|
3
|
+
*
|
|
4
|
+
* SPLIT OUT OF `neighbour_census.ts` FOR ONE CONCRETE REASON. The ranker
|
|
5
|
+
* (`skill_tools/score_skill_relevance`) needs these primitives, and the ranker
|
|
6
|
+
* runs inside the shared hook bundle on `user_prompt_submit`. The census module
|
|
7
|
+
* reaches the host-hook merger, the secret detector and the install-ownership
|
|
8
|
+
* reader; every byte of that would be inlined into one bundle that every concern
|
|
9
|
+
* loads on every dispatch, under a ceiling `check_hook_bundle_composition` holds.
|
|
10
|
+
* So the primitives live here, importing only the manifest reader, and the census
|
|
11
|
+
* re-exports them — one definition, two call sites, no second module graph.
|
|
12
|
+
*
|
|
13
|
+
* ORIGIN IS READ FROM A CLAIM RECORD, NEVER INFERRED FROM A ROOT. The temptation
|
|
14
|
+
* is to call `~/.claude/skills` foreign and the workspace ours. That is wrong in
|
|
15
|
+
* both directions: `agent-config install` writes this package's own skills into
|
|
16
|
+
* the host-global root, and a consumer's hand-written skills sit in the project
|
|
17
|
+
* root. The one artifact that records what this package actually wrote is the
|
|
18
|
+
* installed-tools manifest, so the manifest is the authority and the root is only
|
|
19
|
+
* a tie-break between the two NON-package labels.
|
|
20
|
+
*/
|
|
21
|
+
import { createHash } from 'node:crypto';
|
|
22
|
+
import * as fs from 'node:fs';
|
|
23
|
+
import * as os from 'node:os';
|
|
24
|
+
import * as path from 'node:path';
|
|
25
|
+
|
|
26
|
+
import { parse as parseYaml } from 'yaml';
|
|
27
|
+
|
|
28
|
+
import { manifest_path } from './installed_tools.js';
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Where a RANKED skill came from — a question about AUTHORSHIP.
|
|
32
|
+
*
|
|
33
|
+
* Deliberately not the census's `origin` id segment, which is WHERE a file was
|
|
34
|
+
* found (`project`, `user`, `cursor`, …). Location does not answer authorship in
|
|
35
|
+
* either direction, which is the whole reason this type exists beside that one.
|
|
36
|
+
*
|
|
37
|
+
* `home` is spelled `home` rather than the census's `user` because this value is
|
|
38
|
+
* printed on a ROUTE LINE beside a skill name, where it has to read as a place a
|
|
39
|
+
* reader can go and look.
|
|
40
|
+
*/
|
|
41
|
+
export type SkillOrigin = 'package' | 'project' | 'home';
|
|
42
|
+
|
|
43
|
+
/** One `compat` label per neighbour skill. Order decided in {@link classifyCompat}. */
|
|
44
|
+
export type Compat = 'shadowed' | 'overlapping' | 'unscanned' | 'unclassified';
|
|
45
|
+
|
|
46
|
+
/** The authored source tree, relative to a maintainer checkout's root. */
|
|
47
|
+
export const AUTHORED_SKILL_ROOT = path.join('src', 'skills');
|
|
48
|
+
|
|
49
|
+
/** Where the scan verdicts the ranker reads are written, relative to the root. */
|
|
50
|
+
export const SCAN_CACHE_RELATIVE = path.join('agents', 'reports', 'neighbour-scan.json');
|
|
51
|
+
|
|
52
|
+
/** Default scan-cache path for a project root. */
|
|
53
|
+
export function scanCachePath(projectRoot: string): string {
|
|
54
|
+
return path.join(projectRoot, SCAN_CACHE_RELATIVE);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* What a route line prints: bare for ours, `origin:name` for a neighbour.
|
|
59
|
+
*
|
|
60
|
+
* One function, so the ranker, the census and every report agree on the
|
|
61
|
+
* spelling. Two spellings of one name is how a collision stops being visible,
|
|
62
|
+
* which is the defect this whole step exists to close.
|
|
63
|
+
*/
|
|
64
|
+
export function qualifiedSkillName(name: string, origin: SkillOrigin): string {
|
|
65
|
+
return origin === 'package' ? name : `${origin}:${name}`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Split a qualified name back apart. A bare name resolves to `package`. */
|
|
69
|
+
export function splitQualifiedName(qualified: string): { origin: SkillOrigin; name: string } {
|
|
70
|
+
const m = /^(project|home):([\s\S]*)$/.exec(qualified);
|
|
71
|
+
if (m) return { origin: m[1] as SkillOrigin, name: m[2] as string };
|
|
72
|
+
return { origin: 'package', name: qualified };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Every absolute path this package's installed-tools manifest claims, or `null`
|
|
77
|
+
* when there is no manifest to claim anything.
|
|
78
|
+
*
|
|
79
|
+
* `null` and an empty set are different answers and callers must keep them
|
|
80
|
+
* apart. An empty set means the manifest was read and claims nothing; `null`
|
|
81
|
+
* means nothing recorded a claim at all, which is a tree that has never run an
|
|
82
|
+
* install — this repository, for one.
|
|
83
|
+
*
|
|
84
|
+
* WHY THIS PARSES THE MANIFEST ITSELF, which is a thing to justify rather than
|
|
85
|
+
* do quietly. Three readers of this file already exist and none answers this
|
|
86
|
+
* question at a cost this path can pay. `installed_tools.read_manifest` drops
|
|
87
|
+
* the nested `tools[].files[]` rows by design — its own header says so, and
|
|
88
|
+
* those rows are the entire input here. `install_drift.collect_drift` and
|
|
89
|
+
* `recordedOwnership.readRecordedHashes` both parse the YAML directly FOR THAT
|
|
90
|
+
* REASON, so parsing it directly is the established answer rather than a new
|
|
91
|
+
* idea; what is new is the fourth question — the PATHS only, with no sha, no
|
|
92
|
+
* tool grouping and no hash map. `readRecordedHashes` would have answered it
|
|
93
|
+
* and was measured costing 1,706 bytes of the shared hook bundle, which is the
|
|
94
|
+
* budget this whole module was split out to protect. The `~` expansion below is
|
|
95
|
+
* the same one all three use.
|
|
96
|
+
*/
|
|
97
|
+
export function packageClaimedPaths(projectRoot: string): ReadonlySet<string> | null {
|
|
98
|
+
const manifest = manifest_path(projectRoot);
|
|
99
|
+
let doc: unknown;
|
|
100
|
+
try {
|
|
101
|
+
doc = parseYaml(fs.readFileSync(manifest, 'utf-8'), { version: '1.1' });
|
|
102
|
+
} catch {
|
|
103
|
+
// Unreadable and absent are the same answer: nothing recorded a claim.
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
if (doc === null || typeof doc !== 'object' || Array.isArray(doc)) return null;
|
|
107
|
+
const tools = (doc as Record<string, unknown>)['tools'];
|
|
108
|
+
const out = new Set<string>();
|
|
109
|
+
if (!Array.isArray(tools)) return out;
|
|
110
|
+
for (const tool of tools) {
|
|
111
|
+
if (tool === null || typeof tool !== 'object' || Array.isArray(tool)) continue;
|
|
112
|
+
const files = (tool as Record<string, unknown>)['files'];
|
|
113
|
+
if (!Array.isArray(files)) continue;
|
|
114
|
+
for (const entry of files) {
|
|
115
|
+
if (entry === null || typeof entry !== 'object' || Array.isArray(entry)) continue;
|
|
116
|
+
const raw = (entry as Record<string, unknown>)['path'];
|
|
117
|
+
if (typeof raw !== 'string' || raw === '') continue;
|
|
118
|
+
out.add(path.resolve(projectRoot, _expanduser(raw)));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return out;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Expand a leading `~`, matching the three sibling manifest readers. */
|
|
125
|
+
function _expanduser(p: string): string {
|
|
126
|
+
if (p === '~') return os.homedir();
|
|
127
|
+
if (p.startsWith('~/') || (process.platform === 'win32' && p.startsWith('~\\'))) {
|
|
128
|
+
return path.join(os.homedir(), p.slice(2));
|
|
129
|
+
}
|
|
130
|
+
return p;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export interface SkillOriginOptions {
|
|
134
|
+
/** Pre-read claim set, to avoid a second manifest read. `null` = no manifest. */
|
|
135
|
+
claims?: ReadonlySet<string> | null;
|
|
136
|
+
/** Maintainer checkout root whose `src/skills` is the authored tree. */
|
|
137
|
+
packageRoot?: string;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Resolve one `SKILL.md` path to the origin of the skill it defines.
|
|
142
|
+
*
|
|
143
|
+
* NO CLAIM RECORD MEANS NO QUALIFIER. With no manifest, nothing on disk carries
|
|
144
|
+
* evidence that any skill is foreign, so everything resolves to `package`, the
|
|
145
|
+
* ranker qualifies nothing, and its output is exactly what it was before this
|
|
146
|
+
* function existed. That is deliberate rather than a gap being tolerated: a
|
|
147
|
+
* `home:` prefix asserted without a claim record is a guess printed in the shape
|
|
148
|
+
* of a fact, and a reader cannot tell the two apart. The cost is stated rather
|
|
149
|
+
* than hidden — where no manifest exists a foreign skill is invisible here, and
|
|
150
|
+
* the thing that fixes it is running an install, not loosening this rule.
|
|
151
|
+
*
|
|
152
|
+
* The authored tree is the one exception, and it is a construction rather than a
|
|
153
|
+
* guess: `<root>/src/skills` exists only in a maintainer checkout of THIS
|
|
154
|
+
* package and holds its source of truth, so nothing foreign can be under it.
|
|
155
|
+
*/
|
|
156
|
+
export function makeSkillOriginResolver(
|
|
157
|
+
projectRoot: string,
|
|
158
|
+
opts: SkillOriginOptions = {},
|
|
159
|
+
): (skillMdPath: string) => SkillOrigin {
|
|
160
|
+
const claims = opts.claims !== undefined ? opts.claims : packageClaimedPaths(projectRoot);
|
|
161
|
+
const root = path.resolve(projectRoot);
|
|
162
|
+
const authored = path.join(path.resolve(opts.packageRoot ?? projectRoot), AUTHORED_SKILL_ROOT);
|
|
163
|
+
const under = (abs: string, dir: string): boolean =>
|
|
164
|
+
abs === dir || abs.startsWith(dir + path.sep);
|
|
165
|
+
return (skillMdPath: string): SkillOrigin => {
|
|
166
|
+
const abs = path.resolve(skillMdPath);
|
|
167
|
+
if (under(abs, authored)) return 'package';
|
|
168
|
+
if (claims === null) return 'package';
|
|
169
|
+
if (claims.has(abs)) return 'package';
|
|
170
|
+
return under(abs, root) ? 'project' : 'home';
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* The one label, and the order it is decided in.
|
|
176
|
+
*
|
|
177
|
+
* `unscanned` — the shape scan refused the body. FIRST, because an overlap
|
|
178
|
+
* score is derived FROM a body this census declined to read,
|
|
179
|
+
* and publishing it would be the refusal cancelling itself.
|
|
180
|
+
* `shadowed` — one of ours carries the same bare name. Name-derived, so it
|
|
181
|
+
* stays true under `unscanned`; it loses the slot only
|
|
182
|
+
* because `unscanned` is the stronger call to action.
|
|
183
|
+
* `overlapping` — content similarity against one of ours at or above the
|
|
184
|
+
* threshold `audit_skill_overlap` already calibrates.
|
|
185
|
+
* `unclassified` — none of the above fired. Not a clearance.
|
|
186
|
+
*/
|
|
187
|
+
export function classifyCompat(args: {
|
|
188
|
+
unscanned: string | null;
|
|
189
|
+
shadows: readonly string[];
|
|
190
|
+
overlaps: readonly string[];
|
|
191
|
+
}): Compat {
|
|
192
|
+
if (args.unscanned !== null) return 'unscanned';
|
|
193
|
+
if (args.shadows.length > 0) return 'shadowed';
|
|
194
|
+
if (args.overlaps.length > 0) return 'overlapping';
|
|
195
|
+
return 'unclassified';
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** One recorded scan verdict. `unscanned` is `null` when every shape passed. */
|
|
199
|
+
export interface ScanRecord {
|
|
200
|
+
qualified: string;
|
|
201
|
+
digest: string;
|
|
202
|
+
unscanned: string | null;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export interface ScanCache {
|
|
206
|
+
schema_version: 1;
|
|
207
|
+
entries: ScanRecord[];
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** Read the scan cache, or `null` when it is absent or unreadable. */
|
|
211
|
+
export function readScanCache(file: string): ScanCache | null {
|
|
212
|
+
let parsed: unknown;
|
|
213
|
+
try {
|
|
214
|
+
parsed = JSON.parse(fs.readFileSync(file, 'utf-8'));
|
|
215
|
+
} catch {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) return null;
|
|
219
|
+
const entries = (parsed as Record<string, unknown>)['entries'];
|
|
220
|
+
if (!Array.isArray(entries)) return null;
|
|
221
|
+
return { schema_version: 1, entries: entries as ScanRecord[] };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/** sha256 of a file's bytes, or `null` when it cannot be read. */
|
|
225
|
+
export function fileDigest(file: string): string | null {
|
|
226
|
+
try {
|
|
227
|
+
return createHash('sha256').update(fs.readFileSync(file)).digest('hex');
|
|
228
|
+
} catch {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Whether a neighbour body may be indexed, and why not when it may not.
|
|
235
|
+
*
|
|
236
|
+
* THREE REFUSALS, REPORTED SEPARATELY, because they ask the maintainer for three
|
|
237
|
+
* different things: run the census, re-run it, or read the finding. A missing
|
|
238
|
+
* record is a refusal and not a pass — a gate that passes on missing evidence is
|
|
239
|
+
* satisfied by deleting the evidence, which is the opposite of a floor. The
|
|
240
|
+
* digest compare is what "a changed digest rescans first" means from the reading
|
|
241
|
+
* side: a body edited after its scan is unscanned again until the census runs.
|
|
242
|
+
*/
|
|
243
|
+
export function scanVerdict(
|
|
244
|
+
cache: ScanCache | null,
|
|
245
|
+
qualified: string,
|
|
246
|
+
digest: string | null,
|
|
247
|
+
): { ok: true } | { ok: false; kind: string } {
|
|
248
|
+
if (cache === null) return { ok: false, kind: 'no-scan-record' };
|
|
249
|
+
const rec = cache.entries.find((e) => e.qualified === qualified);
|
|
250
|
+
if (rec === undefined) return { ok: false, kind: 'no-scan-record' };
|
|
251
|
+
if (digest === null || rec.digest !== digest) return { ok: false, kind: 'digest-changed' };
|
|
252
|
+
if (rec.unscanned !== null) return { ok: false, kind: rec.unscanned };
|
|
253
|
+
return { ok: true };
|
|
254
|
+
}
|