@event4u/agent-config 16.2.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 +89 -237
- 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 +4 -4
- 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/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/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/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/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-system-capture/references/import-procedure.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/iconography/SKILL.md +1 -1
- package/dist/agent-src/skills/js-library-packaging/SKILL.md +2 -2
- package/dist/agent-src/skills/monorepo-workspace/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/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 +2 -2
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +48 -6
- package/dist/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
- 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-IGD6ERX6.js → chunk-JQVYKIZA.js} +10 -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-7RQP4UDH.js → chunk-KR6EM23A.js} +235 -1455
- package/dist/cli-delegate/{chunk-LTYVIY76.js → chunk-LIYTLH6O.js} +1 -1
- package/dist/cli-delegate/{chunk-YJ72UIAK.js → chunk-NQ7WT43P.js} +1186 -244
- 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-7XGBX6ZH.js → chunk-RQNNW7IF.js} +1314 -560
- package/dist/cli-delegate/{chunk-4XFP2Y3E.js → chunk-S4UMZNES.js} +1 -1
- package/dist/cli-delegate/{chunk-6KM6ORRY.js → chunk-S4YWNRKO.js} +24 -8
- package/dist/cli-delegate/{chunk-FP36SWJD.js → chunk-TRI6DQZ6.js} +2781 -340
- 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 +1167 -234
- 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 +127 -75
- 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 +5357 -6656
- 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 +2080 -1024
- 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 +31965 -27154
- 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 +6 -0
- package/dist/shared/skillRanking.js +74 -13
- package/docs/contracts/settings-classes.md +7 -5
- 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/update_roadmap_progress.ts +4 -1
- package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +48 -6
- package/src/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
- package/src/config/agent-settings.template.yml +62 -22
- package/src/config/continuity-surface.json +7 -0
- package/src/config/evaluator-budgets.json +4 -4
- package/src/config/gate-coverage.yml +96 -3
- package/src/config/gate-violation-baselines.json +17 -9
- 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/routing-coverage-seed.json +24 -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 +23 -71
- package/src/scripts/_cli/cmd_doctor_neighbours.ts +293 -0
- package/src/scripts/_dispatch.bash +16 -2
- package/src/scripts/_lib/agent_settings.ts +11 -0
- package/src/scripts/_lib/claude_settings_hooks.ts +10 -8
- 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/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_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/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/prompt_shape.ts +46 -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/skill_origin.ts +254 -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 +77 -2
- 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/audit_skill_overlap.ts +261 -0
- package/src/scripts/before_complete_hook.ts +243 -12
- package/src/scripts/bench_touched_file_quality.ts +238 -0
- package/src/scripts/check_enforcement_matrix.ts +377 -20
- package/src/scripts/check_estate_count.ts +130 -153
- package/src/scripts/check_finding_dispositions.ts +116 -4
- package/src/scripts/check_hook_bundle_composition.ts +370 -0
- package/src/scripts/check_kernel_edit_ratified.ts +49 -11
- package/src/scripts/check_memory.ts +7 -1
- package/src/scripts/check_release_highlights.ts +10 -0
- package/src/scripts/check_release_pr_shape.ts +38 -15
- package/src/scripts/check_standing_rule_delivery.ts +33 -13
- package/src/scripts/check_verify_expectation_delta.ts +521 -0
- 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 +33 -1
- package/src/scripts/condense.ts +14 -27
- package/src/scripts/estate_exemption_shape.ts +202 -0
- package/src/scripts/generate_host_cost_table.ts +1 -1
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +150 -9
- package/src/scripts/hooks/block_config_weakening.ts +67 -2
- package/src/scripts/hooks/block_plumbing_writes.ts +29 -1
- 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/dispatch_hook.ts +52 -52
- 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/one_question_per_ask_hook.ts +41 -25
- package/src/scripts/hooks/payload_stub.ts +10 -1
- package/src/scripts/hooks/rule_inject_hook.ts +757 -63
- package/src/scripts/hooks/table_fingerprint.ts +21 -0
- package/src/scripts/hooks/telemetry_usage_hook.ts +64 -8
- package/src/scripts/hooks/turn_end_gate_hook.ts +84 -0
- package/src/scripts/hooks_status.ts +112 -0
- package/src/scripts/injection_scan_hook.ts +7 -0
- package/src/scripts/install.ts +72 -185
- 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_hook_manifest.ts +53 -0
- package/src/scripts/lint_major_migration_sections.ts +156 -26
- package/src/scripts/lint_pack_boundaries.ts +63 -5
- package/src/scripts/lint_rule_law_section.ts +517 -0
- 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/measure_routing_signal.ts +54 -16
- package/src/scripts/measure_skill_ranker_baseline.ts +216 -8
- package/src/scripts/memory_learn_hook.ts +91 -6
- package/src/scripts/model_rule_injection.ts +52 -5
- package/src/scripts/project_thin_rules.ts +34 -327
- package/src/scripts/report_evidence_temperature.ts +657 -0
- package/src/scripts/report_installed_rule_links.ts +157 -0
- package/src/scripts/report_label_agreement.ts +233 -0
- package/src/scripts/report_obligation_mechanism.ts +230 -0
- package/src/scripts/report_single_token_triggers.ts +277 -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/rule_activation_census.ts +5 -2
- package/src/scripts/rule_trigger_eval.ts +9 -9
- package/src/scripts/schemas/review-findings.schema.json +3 -2
- package/src/scripts/schemas/rule.schema.json +5 -0
- package/src/scripts/self_review_gate.ts +62 -7
- package/src/scripts/skill_tools/score_skill_relevance.ts +290 -25
- 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 -5
- package/src/server/schemas/settings.ts +10 -0
- package/src/shared/skillRanking.ts +117 -11
- package/dist/cli-delegate/chunk-JNMVLWLR.js +0 -4457
- package/dist/cli-delegate/chunk-YDPILCOL.js +0 -152
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The high-consequence rule class — one reader for the config, the projector
|
|
3
|
+
* and the test.
|
|
4
|
+
*
|
|
5
|
+
* The criterion itself is prose and lives where an author meets it: the
|
|
6
|
+
* `consequence_class` property of `src/scripts/schemas/rule.schema.json`. This
|
|
7
|
+
* module does not restate it. It reads the list the criterion produced
|
|
8
|
+
* (`src/config/rule-consequence-class.json`), joins it to what each rule file
|
|
9
|
+
* actually declares, and answers the one question `project_thin_rules` asks:
|
|
10
|
+
* may this rule's stub carry its own law, and if not, has that been declared?
|
|
11
|
+
*
|
|
12
|
+
* WHY A DECLARED `no_stub` SUBSET RATHER THAN A SHORTER CLASS. A member whose
|
|
13
|
+
* law section is missing or over the ceiling cannot ship as stub-plus-law —
|
|
14
|
+
* there is nothing short enough to copy. Two ways to handle that: drop it from
|
|
15
|
+
* the class, or keep it in the class and record why it cannot stand. Dropping
|
|
16
|
+
* it makes the class shrinkable by NOT writing a law, which is the incentive a
|
|
17
|
+
* criterion must not create. Keeping it leaves the obligation visible and costs
|
|
18
|
+
* only a full-bodied projection, which is what those rules get today anyway.
|
|
19
|
+
*/
|
|
20
|
+
import * as fs from 'node:fs';
|
|
21
|
+
import * as path from 'node:path';
|
|
22
|
+
|
|
23
|
+
export const CLASS_CONFIG_REL = 'src/config/rule-consequence-class.json';
|
|
24
|
+
|
|
25
|
+
export const CLAUSES = ['irreversible-external', 'security-boundary', 'authority-bypass'] as const;
|
|
26
|
+
export type Clause = (typeof CLAUSES)[number];
|
|
27
|
+
|
|
28
|
+
export interface ClassMember {
|
|
29
|
+
clause: Clause;
|
|
30
|
+
/** What this rule's own law obligates, and what violating it costs. */
|
|
31
|
+
why: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface NoStubEntry extends ClassMember {
|
|
35
|
+
/** Why the law cannot stand in a stub today. */
|
|
36
|
+
reason: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface ConsequenceClassConfig {
|
|
40
|
+
criterion_ref: string;
|
|
41
|
+
members: Record<string, ClassMember>;
|
|
42
|
+
/** Subset of `members` that projects full-bodied, each with its reason. */
|
|
43
|
+
no_stub: Record<string, NoStubEntry>;
|
|
44
|
+
/** Near misses the criterion rejected, each with the reason it was rejected. */
|
|
45
|
+
excluded: Record<string, string>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function readConsequenceClass(repoRoot: string): ConsequenceClassConfig {
|
|
49
|
+
const raw = JSON.parse(fs.readFileSync(path.join(repoRoot, CLASS_CONFIG_REL), 'utf-8')) as Partial<
|
|
50
|
+
ConsequenceClassConfig
|
|
51
|
+
>;
|
|
52
|
+
return {
|
|
53
|
+
criterion_ref: raw.criterion_ref ?? '',
|
|
54
|
+
members: raw.members ?? {},
|
|
55
|
+
no_stub: raw.no_stub ?? {},
|
|
56
|
+
excluded: raw.excluded ?? {},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Ids whose stub carries their law — members minus the declared `no_stub` set. */
|
|
61
|
+
export function stubLawIds(cfg: ConsequenceClassConfig): Set<string> {
|
|
62
|
+
return new Set(Object.keys(cfg.members).filter((id) => cfg.no_stub[id] === undefined));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** The `consequence_class:` value a rule file declares, or `null`. */
|
|
66
|
+
export function declaredClause(ruleText: string): Clause | null {
|
|
67
|
+
const m = /^consequence_class:\s*"?([a-z-]+)"?\s*$/m.exec(ruleText);
|
|
68
|
+
if (m === null) return null;
|
|
69
|
+
const v = m[1] as string;
|
|
70
|
+
return (CLAUSES as readonly string[]).includes(v) ? (v as Clause) : null;
|
|
71
|
+
}
|
|
@@ -72,8 +72,115 @@ export interface TierRuleMatch {
|
|
|
72
72
|
/** Kinds of trigger a prompt alone can never fire. */
|
|
73
73
|
export const PATH_TRIGGER_KINDS = ['path_prefix', 'file_pattern'] as const;
|
|
74
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Set by `dispatch_hook` to its own resolved package root. Read here rather
|
|
77
|
+
* than threaded through every caller — see `ruleSourceDirs`.
|
|
78
|
+
*/
|
|
79
|
+
export const PACKAGE_ROOT_ENV = 'AGENT_CONFIG_PACKAGE_ROOT';
|
|
80
|
+
|
|
81
|
+
function statKind(p: string): 'dir' | 'file' | null {
|
|
82
|
+
try {
|
|
83
|
+
const s = fs.statSync(p);
|
|
84
|
+
return s.isDirectory() ? 'dir' : s.isFile() ? 'file' : null;
|
|
85
|
+
} catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Every tree a rule body may come from, highest precedence first.
|
|
92
|
+
*
|
|
93
|
+
* THE DEFECT THIS CLOSES, reproduced at `main` @ `9bc8cd4f2` and again on
|
|
94
|
+
* 2026-10-01. This module resolved bodies and the router under ONE root, and
|
|
95
|
+
* the carrier passed the envelope's workspace or `process.cwd()`. In this
|
|
96
|
+
* checkout those are the same directory and everything works. In a consumer
|
|
97
|
+
* project they are not: there is no `dist/` under the consumer's tree at all,
|
|
98
|
+
* so `loadRouter` threw, the carrier caught it and returned allow, and the
|
|
99
|
+
* concern delivered NOTHING while reporting no error. Every consumer runs it
|
|
100
|
+
* in someone else's project, so the shipped carrier was silent for every
|
|
101
|
+
* consumer and loud only for its author. `dispatch_hook.ts:722` has passed
|
|
102
|
+
* `AGENT_CONFIG_PACKAGE_ROOT` to every concern since the ADR-020 global-only
|
|
103
|
+
* install landed — the fix was one environment read away and nothing read it.
|
|
104
|
+
*
|
|
105
|
+
* THE ORDER IS THE CONTRACT:
|
|
106
|
+
*
|
|
107
|
+
* 1. `<root>/agents/overrides/<id>.md` — the developer's own layer.
|
|
108
|
+
* `agents_overlay.ts` already makes `overrides/` the one cascade kind
|
|
109
|
+
* that is both project- and user-global-eligible, and a consumer who has
|
|
110
|
+
* overridden a rule means it. It wins, which is what step 1.1's "with
|
|
111
|
+
* `agents/overrides/` keeping precedence" asks for.
|
|
112
|
+
* 2. `<root>/dist/agent-src/rules/<id>.md` — the maintainer checkout. Kept
|
|
113
|
+
* AHEAD of the package root and not behind it, so this repository's own
|
|
114
|
+
* behaviour is byte-identical to what it was before. A change that
|
|
115
|
+
* repairs consumers by moving the maintainer tree is one nobody can
|
|
116
|
+
* review against a known-good reading.
|
|
117
|
+
* 3. `<packageRoot>/dist/agent-src/rules/<id>.md` — the install. The only
|
|
118
|
+
* one of the three that exists in a foreign project.
|
|
119
|
+
*
|
|
120
|
+
* WHY THE ENVIRONMENT IS READ HERE rather than passed in. Nine call sites
|
|
121
|
+
* across the model, the shortlist, the arm experiment and two hooks pass a
|
|
122
|
+
* single `repoRoot`, and none of them has a second tree to offer. Threading a
|
|
123
|
+
* resolved source object through all of them would be a wide diff whose only
|
|
124
|
+
* real consumer is the carrier, and it costs runtime bytes in the composed
|
|
125
|
+
* hook bundle — which every hook event on every slot loads, under a hard
|
|
126
|
+
* ceiling (`src/config/hook-bundle-budget.json`). `roadmap_progress_hook`
|
|
127
|
+
* already reads this same variable this same way; this follows it rather than
|
|
128
|
+
* inventing a second convention. No `~` expansion: the one writer is
|
|
129
|
+
* `dispatch_hook`, which sets it from a `path.resolve` result.
|
|
130
|
+
*/
|
|
131
|
+
export interface RuleSources {
|
|
132
|
+
/** Body directories, highest precedence first. */
|
|
133
|
+
readonly dirs: readonly string[];
|
|
134
|
+
/** The resolved package root, or `null` when the variable named nothing. */
|
|
135
|
+
readonly pkg: string | null;
|
|
136
|
+
/** The `dist/router.json` to route against, or `null`. */
|
|
137
|
+
readonly router: string | null;
|
|
138
|
+
/**
|
|
139
|
+
* One line naming why nothing resolved, or `null` when something did.
|
|
140
|
+
*
|
|
141
|
+
* A caller that found nothing must be able to SAY so. An empty delivery is
|
|
142
|
+
* indistinguishable from "no rule matched", which is exactly the silence
|
|
143
|
+
* this chain exists to end; reproducing it one layer up would be the whole
|
|
144
|
+
* defect again. The three wordings are distinguishable because the
|
|
145
|
+
* operator's next action differs: the variable was never set (a host or
|
|
146
|
+
* wrapper problem), it points somewhere that is gone (a move after
|
|
147
|
+
* install), or both resolved and the package carries no built corpus.
|
|
148
|
+
*/
|
|
149
|
+
readonly gap: string | null;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Resolve the body dirs, the router and the gap wording in ONE filesystem pass. */
|
|
153
|
+
export function ruleSources(repoRoot: string): RuleSources {
|
|
154
|
+
const env = (process.env[PACKAGE_ROOT_ENV] ?? '').trim();
|
|
155
|
+
const pkg = env ? path.resolve(env) : null;
|
|
156
|
+
const dirs: string[] = [];
|
|
157
|
+
const add = (d: string): void => {
|
|
158
|
+
if (statKind(d) === 'dir' && !dirs.includes(d)) dirs.push(d);
|
|
159
|
+
};
|
|
160
|
+
add(path.join(repoRoot, 'agents', 'overrides'));
|
|
161
|
+
add(path.join(repoRoot, 'dist', 'agent-src', 'rules'));
|
|
162
|
+
if (pkg !== null) add(path.join(pkg, 'dist', 'agent-src', 'rules'));
|
|
163
|
+
|
|
164
|
+
const local = path.join(repoRoot, 'dist', 'router.json');
|
|
165
|
+
let router: string | null = statKind(local) === 'file' ? local : null;
|
|
166
|
+
if (router === null && pkg !== null) {
|
|
167
|
+
const shipped = path.join(pkg, 'dist', 'router.json');
|
|
168
|
+
if (statKind(shipped) === 'file') router = shipped;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
let gap: string | null = null;
|
|
172
|
+
if (router === null || dirs.length === 0) {
|
|
173
|
+
const miss =
|
|
174
|
+
pkg === null
|
|
175
|
+
? `${PACKAGE_ROOT_ENV} unset`
|
|
176
|
+
: `no ${router === null ? 'dist/router.json' : 'dist/agent-src/rules'} under ${repoRoot} or ${pkg}`;
|
|
177
|
+
gap = `rule-inject: no rule source — ${miss}`;
|
|
178
|
+
}
|
|
179
|
+
return { dirs, pkg, router, gap };
|
|
180
|
+
}
|
|
181
|
+
|
|
75
182
|
export function loadRouter(repoRoot: string): Router {
|
|
76
|
-
const p = path.join(repoRoot, 'dist', 'router.json');
|
|
183
|
+
const p = ruleSources(repoRoot).router ?? path.join(repoRoot, 'dist', 'router.json');
|
|
77
184
|
return JSON.parse(fs.readFileSync(p, 'utf8')) as Router;
|
|
78
185
|
}
|
|
79
186
|
|
|
@@ -163,16 +270,29 @@ export function matchTierRules(
|
|
|
163
270
|
return out;
|
|
164
271
|
}
|
|
165
272
|
|
|
166
|
-
/**
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
273
|
+
/**
|
|
274
|
+
* The projected body from the highest-precedence tree carrying it, or `null`.
|
|
275
|
+
*
|
|
276
|
+
* `ruleBodyPath` used to sit in front of this and is gone: it returned the
|
|
277
|
+
* single-root path unconditionally, this diff changed its contract to "the
|
|
278
|
+
* first tree that has the file", and nothing outside this module ever called
|
|
279
|
+
* it. An exported helper whose last caller disappeared in the same diff is an
|
|
280
|
+
* own-orphan, and the composed hook bundle charges for it on every dispatch
|
|
281
|
+
* (`src/config/hook-bundle-budget.json`), so it is removed rather than left as
|
|
282
|
+
* a wrapper nobody uses.
|
|
283
|
+
*/
|
|
172
284
|
export function loadRuleBody(repoRoot: string, id: string): string | null {
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
285
|
+
for (const dir of ruleSources(repoRoot).dirs) {
|
|
286
|
+
const p = path.join(dir, `${id}.md`);
|
|
287
|
+
if (statKind(p) === 'file') {
|
|
288
|
+
try {
|
|
289
|
+
return fs.readFileSync(p, 'utf8');
|
|
290
|
+
} catch {
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return null;
|
|
176
296
|
}
|
|
177
297
|
|
|
178
298
|
/**
|
|
@@ -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.
|