@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,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The rule scope governing a GLOBAL deploy.
|
|
3
|
+
*
|
|
4
|
+
* Moved out of `src/scripts/install.ts` by
|
|
5
|
+
* `road-to-an-installed-layer-that-is-thinned` step 1.1, which adds the thinning
|
|
6
|
+
* call to the same deploy loop. `check_source_size_budget` is a one-way ratchet
|
|
7
|
+
* with no per-change headroom, and this repository's stated convention is that
|
|
8
|
+
* the total falls through EXTRACTION and never rises through a baseline edit —
|
|
9
|
+
* the same reason `claudeRuleRewrite` holds the claude-code post-copy reporting
|
|
10
|
+
* rather than the installer. This function is the nearest self-contained block
|
|
11
|
+
* to the change: it is the predicate the deploy loop resolves once per run, it
|
|
12
|
+
* reaches nothing else in the installer, and its four environment touches are
|
|
13
|
+
* already parameters in all but name.
|
|
14
|
+
*
|
|
15
|
+
* BEHAVIOUR IS UNCHANGED, DELIBERATELY. The body is the installer's, verbatim
|
|
16
|
+
* apart from the four injected seams below; `install.ts` keeps a wrapper of the
|
|
17
|
+
* same name and signature, so its re-export and every caller and test see what
|
|
18
|
+
* they saw before. A behaviour change smuggled into an extraction is the one
|
|
19
|
+
* thing that would make the line saving not worth having.
|
|
20
|
+
*
|
|
21
|
+
* The seams are injected rather than imported because each one is an
|
|
22
|
+
* install-time decision the installer owns: where the global settings doc lives,
|
|
23
|
+
* how this build reads and parses YAML, what the packaged template says, and
|
|
24
|
+
* where a warning goes.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { LEGACY_ALL, ruleScopeFromSettings, type RuleScope } from './rule_scope.js';
|
|
28
|
+
|
|
29
|
+
export interface GlobalRuleScopeInputs {
|
|
30
|
+
/** The global settings file, or `null` when no artefact exists at all. */
|
|
31
|
+
readonly settingsPath: string | null;
|
|
32
|
+
/** The package being installed from. */
|
|
33
|
+
readonly packageRoot: string;
|
|
34
|
+
/** Read a file as text; throws like `fs.readFileSync` when it cannot. */
|
|
35
|
+
readonly readText: (p: string) => string;
|
|
36
|
+
/** Parse YAML permissively — anything unparseable comes back non-object. */
|
|
37
|
+
readonly parseYaml: (text: string) => unknown;
|
|
38
|
+
/** The packaged template settings, read only on a genuinely fresh machine. */
|
|
39
|
+
readonly loadDefaults: () => Record<string, unknown>;
|
|
40
|
+
/** Where a loud fallback goes. */
|
|
41
|
+
readonly warn: (message: string) => void;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function isPlainObject(v: unknown): boolean {
|
|
45
|
+
return typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Resolve the rule scope governing THIS global deploy — the same way the wizard
|
|
50
|
+
* does (`src/server/routes/install.ts::_resolveRuleScope`), so the two global
|
|
51
|
+
* paths cannot ship different rule sets for the same settings.
|
|
52
|
+
*
|
|
53
|
+
* Settings resolution mirrors `_resolve_scoped_projection` above: an existing
|
|
54
|
+
* global settings doc is authoritative, and only a genuinely fresh machine falls
|
|
55
|
+
* through to the packaged template. Any read or parse failure resolves to
|
|
56
|
+
* `LEGACY_ALL` — over-shipping is the safe direction, and the compat exclusion
|
|
57
|
+
* (`source-of-truth.md`) still applies even then.
|
|
58
|
+
*/
|
|
59
|
+
export function resolveGlobalRuleScope(inputs: GlobalRuleScopeInputs): RuleScope {
|
|
60
|
+
const { packageRoot, warn } = inputs;
|
|
61
|
+
const settings_path = inputs.settingsPath;
|
|
62
|
+
|
|
63
|
+
// No global settings artefact at all — a genuinely fresh machine. Fall
|
|
64
|
+
// through to the packaged template silently: there is no user decision to
|
|
65
|
+
// contradict, and this is the documented upgrade-compat path.
|
|
66
|
+
if (settings_path === null) {
|
|
67
|
+
try {
|
|
68
|
+
return ruleScopeFromSettings(inputs.loadDefaults(), packageRoot);
|
|
69
|
+
} catch {
|
|
70
|
+
return LEGACY_ALL;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// A doc EXISTS, so the user has expressed a configuration. Failing to read
|
|
75
|
+
// it is not the same as not having one: falling back to legacy-all here
|
|
76
|
+
// ships the maintainer-only rules the user may have deliberately scoped out,
|
|
77
|
+
// and `_load_yaml_doc` would report that as an indistinguishable `{}`. So
|
|
78
|
+
// parse it explicitly and be LOUD when it does not parse — over-shipping
|
|
79
|
+
// stays the safe direction, but it must not be a silent one.
|
|
80
|
+
let text: string;
|
|
81
|
+
try {
|
|
82
|
+
text = inputs.readText(settings_path);
|
|
83
|
+
} catch (e) {
|
|
84
|
+
warn(
|
|
85
|
+
`could not read ${settings_path} (${String(e)}) — rule scoping falls back ` +
|
|
86
|
+
'to legacy-all, so ALL rules including maintainer-only ones will be ' +
|
|
87
|
+
'installed. Fix the file to restore scoping.',
|
|
88
|
+
);
|
|
89
|
+
return LEGACY_ALL;
|
|
90
|
+
}
|
|
91
|
+
const parsed = inputs.parseYaml(text);
|
|
92
|
+
if (!isPlainObject(parsed)) {
|
|
93
|
+
warn(
|
|
94
|
+
`${settings_path} is not a YAML mapping — rule scoping falls back to ` +
|
|
95
|
+
'legacy-all, so ALL rules including maintainer-only ones will be ' +
|
|
96
|
+
'installed. Fix the file to restore scoping.',
|
|
97
|
+
);
|
|
98
|
+
return LEGACY_ALL;
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
return ruleScopeFromSettings(parsed as Record<string, unknown>, packageRoot);
|
|
102
|
+
} catch (e) {
|
|
103
|
+
warn(
|
|
104
|
+
`could not derive rule scope from ${settings_path} (${String(e)}) — ` +
|
|
105
|
+
'falling back to legacy-all; ALL rules will be installed.',
|
|
106
|
+
);
|
|
107
|
+
return LEGACY_ALL;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which host-rule files this package may delete or overwrite.
|
|
3
|
+
*
|
|
4
|
+
* The defect this closes: `_cleanDir` removed every entry under
|
|
5
|
+
* `.cursor/rules` and `.windsurf/rules` whose NAME was not one this run had
|
|
6
|
+
* just emitted, and `.windsurfrules` was overwritten unconditionally. A rule
|
|
7
|
+
* file another agent package wrote — or one the consumer wrote by hand — was
|
|
8
|
+
* destroyed on every install, which is the opposite of what the uninstall
|
|
9
|
+
* claim promises.
|
|
10
|
+
*
|
|
11
|
+
* Ownership is read from the installed-tools manifest's `files[]` inventory,
|
|
12
|
+
* never from a marker inside the file. The `.mdc` emitter writes no package
|
|
13
|
+
* tag (`condense.ts` `_emit_cursor_mdc`), so a tag-based gate would classify
|
|
14
|
+
* every file this package ever wrote as foreign and therefore never remove a
|
|
15
|
+
* stale one — it would read as conservative and in fact be inert. The manifest
|
|
16
|
+
* is the record of what this installer actually put on disk, so it is the
|
|
17
|
+
* thing that can answer the question.
|
|
18
|
+
*
|
|
19
|
+
* This module only READS that manifest. It never rewrites it: the available
|
|
20
|
+
* reader (`installed_tools.read_manifest`) drops nested `files[]` by design,
|
|
21
|
+
* so a read-modify-write through it would erase every other tool's inventory —
|
|
22
|
+
* the failure `manifestFiles.ts` documents at length.
|
|
23
|
+
*
|
|
24
|
+
* The consequence, stated rather than implied: a consumer whose manifest does
|
|
25
|
+
* not claim a path gets that path KEPT. Unproven means kept, never deleted.
|
|
26
|
+
* That is the safe direction for this lane and it is also a real limit — a
|
|
27
|
+
* stale file of ours sitting under an unclaimed path stays until something
|
|
28
|
+
* claims it.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import * as fs from 'node:fs';
|
|
32
|
+
import * as path from 'node:path';
|
|
33
|
+
|
|
34
|
+
import { sha256OfFile } from './fsPrimitives.js';
|
|
35
|
+
import { classifyOwnership, recordedHashesForRoot, type RecordedHashes } from './recordedOwnership.js';
|
|
36
|
+
|
|
37
|
+
/** The first line every `.windsurfrules` this package generates carries. */
|
|
38
|
+
export const WINDSURFRULES_HEADER = '# Auto-generated from .augment/rules/ — do not edit directly';
|
|
39
|
+
|
|
40
|
+
export interface CleanResult {
|
|
41
|
+
/** Entry names removed from the directory. */
|
|
42
|
+
removed: string[];
|
|
43
|
+
/** Entries kept because nothing proves this package wrote them. */
|
|
44
|
+
kept: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Every file path beneath `full`, or `[full]` when it is a file. */
|
|
48
|
+
function filesUnder(full: string): string[] {
|
|
49
|
+
let stat: fs.Stats;
|
|
50
|
+
try {
|
|
51
|
+
stat = fs.lstatSync(full);
|
|
52
|
+
} catch {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
if (!stat.isDirectory()) return [full];
|
|
56
|
+
const out: string[] = [];
|
|
57
|
+
for (const name of fs.readdirSync(full)) {
|
|
58
|
+
out.push(...filesUnder(path.join(full, name)));
|
|
59
|
+
}
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Is this exact file ours AND still carrying the bytes we wrote?
|
|
65
|
+
*
|
|
66
|
+
* The recorded digest is the point, not the path. A path-membership test
|
|
67
|
+
* answers "did we ever write here", which merges two materially different
|
|
68
|
+
* trees: a generated file still as we left it, and one the consumer has since
|
|
69
|
+
* edited. Deleting the second loses their work, and `recordedOwnership` exists
|
|
70
|
+
* to tell them apart — importing its map and then ignoring its hashes was the
|
|
71
|
+
* defect an independent review named here.
|
|
72
|
+
*/
|
|
73
|
+
function isUnchangedOurs(file: string, owned: RecordedHashes): boolean {
|
|
74
|
+
const target = path.resolve(file);
|
|
75
|
+
if (!owned.has(target)) return false;
|
|
76
|
+
return classifyOwnership(owned.get(target), sha256OfFile(target)) === 'recorded-unchanged';
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Remove the stale entries of ours from `dir`, keeping everything else.
|
|
81
|
+
*
|
|
82
|
+
* An entry is removable only when every file it contains is ours AND
|
|
83
|
+
* unmodified: one unclaimed or edited file inside a directory keeps the whole
|
|
84
|
+
* directory, because removing it would take that file with it.
|
|
85
|
+
*/
|
|
86
|
+
export function cleanOwnedOnly(
|
|
87
|
+
dir: string,
|
|
88
|
+
valid: ReadonlySet<string>,
|
|
89
|
+
owned: RecordedHashes,
|
|
90
|
+
): CleanResult {
|
|
91
|
+
let entries: string[];
|
|
92
|
+
try {
|
|
93
|
+
entries = fs.readdirSync(dir);
|
|
94
|
+
} catch {
|
|
95
|
+
return { removed: [], kept: 0 };
|
|
96
|
+
}
|
|
97
|
+
const removed: string[] = [];
|
|
98
|
+
let kept = 0;
|
|
99
|
+
for (const name of entries) {
|
|
100
|
+
if (name === 'README.md' || valid.has(name)) continue;
|
|
101
|
+
const full = path.join(dir, name);
|
|
102
|
+
const contained = filesUnder(full);
|
|
103
|
+
const ours = contained.length > 0 && contained.every((f) => isUnchangedOurs(f, owned));
|
|
104
|
+
if (!ours) {
|
|
105
|
+
kept += 1;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
fs.rmSync(full, { recursive: true, force: true });
|
|
110
|
+
removed.push(name);
|
|
111
|
+
} catch {
|
|
112
|
+
kept += 1;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return { removed, kept };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* May this package write `.windsurfrules` at `target`?
|
|
120
|
+
*
|
|
121
|
+
* Yes when the file is absent, or when it still carries the header this
|
|
122
|
+
* package generates. Nothing else.
|
|
123
|
+
*
|
|
124
|
+
* A manifest claim is deliberately NOT a second route in. The file is
|
|
125
|
+
* generated and overwritten on every install, so the header IS the ownership
|
|
126
|
+
* signal and it is content-derived: if the header is gone, something replaced
|
|
127
|
+
* the file, and "the manifest says we wrote here once" is exactly the stale
|
|
128
|
+
* fact that would let us overwrite a neighbour's replacement. An independent
|
|
129
|
+
* review caught that route open, with a fixture asserting it.
|
|
130
|
+
*/
|
|
131
|
+
export function mayWriteWindsurfRules(target: string, owned: RecordedHashes): boolean {
|
|
132
|
+
void owned;
|
|
133
|
+
if (!fs.existsSync(target)) return true;
|
|
134
|
+
let head: string;
|
|
135
|
+
try {
|
|
136
|
+
head = fs.readFileSync(target, 'utf-8').split('\n', 1)[0] ?? '';
|
|
137
|
+
} catch {
|
|
138
|
+
// Unreadable is unproven, and unproven means do not touch it.
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
return head.trim() === WINDSURFRULES_HEADER;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* May this package write a rule file at `target`?
|
|
146
|
+
*
|
|
147
|
+
* The cleanup pass is not enough on its own, and an independent review found
|
|
148
|
+
* the hole: `_cleanDir` skips every name this run just emitted, so a
|
|
149
|
+
* neighbour's `foo.mdc` that happens to share a basename with one of our
|
|
150
|
+
* rules was already OVERWRITTEN by the time ownership was consulted. The
|
|
151
|
+
* check has to run BEFORE the write, which is what this is for.
|
|
152
|
+
*
|
|
153
|
+
* Absent, or ours and unmodified → write. Anything else is someone's file or
|
|
154
|
+
* someone's edit, and is left exactly as it is.
|
|
155
|
+
*/
|
|
156
|
+
export function mayWriteRuleFile(target: string, owned: RecordedHashes): boolean {
|
|
157
|
+
if (!fs.existsSync(target)) return true;
|
|
158
|
+
return isUnchangedOurs(target, owned);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** The manifest's claimed paths for a consumer root; empty when none resolves. */
|
|
162
|
+
export function ownedPaths(projectRoot: string): RecordedHashes {
|
|
163
|
+
return recordedHashesForRoot(projectRoot);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** The one line the emitter prints when it kept a neighbour's files. */
|
|
167
|
+
export function keptLine(kept: number): string | null {
|
|
168
|
+
if (kept <= 0) return null;
|
|
169
|
+
return `kept: ${kept} neighbour file(s)`;
|
|
170
|
+
}
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin an INSTALLED Claude Code rule layer, in place, after the copy.
|
|
3
|
+
*
|
|
4
|
+
* `road-to-an-installed-layer-that-is-thinned` step 1.1. The global install step
|
|
5
|
+
* copies `dist/agent-src/rules` verbatim into `~/.claude/rules`
|
|
6
|
+
* (`install.ts::_copy_dir_dereferencing_symlinks`), so a consumer receives every
|
|
7
|
+
* rule body whether or not any trigger ever fires. The round that opened that
|
|
8
|
+
* roadmap measured a fresh install at 102 unconditional files and 338,225
|
|
9
|
+
* characters against a host that reports a 150,000 budget. Thinning existed —
|
|
10
|
+
* `project_thin_rules::build_thin` — but ran only on the maintainer projection,
|
|
11
|
+
* which no consumer receives.
|
|
12
|
+
*
|
|
13
|
+
* Three properties, each a defect this would otherwise reintroduce.
|
|
14
|
+
*
|
|
15
|
+
* **The stub is built from the SOURCE tree, never from the installed file.**
|
|
16
|
+
* `claudeRuleRewrite` runs immediately before this and reduces each installed
|
|
17
|
+
* rule to the host's activation vocabulary — `paths:` plus the ownership keys —
|
|
18
|
+
* dropping `description:` and the `triggers:` block. Those two are exactly what
|
|
19
|
+
* `thin_entry` reads to write the stub's matching signal, so a stub built from
|
|
20
|
+
* the rewritten file would carry an empty description and no `Fires on:` hint.
|
|
21
|
+
* Reading `<packageRoot>/dist/agent-src/rules/<name>` keeps the signal intact.
|
|
22
|
+
*
|
|
23
|
+
* **The installed frontmatter is kept verbatim.** The roadmap's step 1.1 ends
|
|
24
|
+
* "Ownership keys survive", and it is not decoration: `reap_tagged_orphans`
|
|
25
|
+
* calls the literal `package:` line "the only path with ownership proof
|
|
26
|
+
* independent of inventory history", and doctor's stale-orphan check reads the
|
|
27
|
+
* same tag. A stub that replaced the whole file would delete the reaper's only
|
|
28
|
+
* evidence for every thinned rule. So only the BODY is replaced; the header the
|
|
29
|
+
* copy and the rewrite produced is written back unchanged, which also preserves
|
|
30
|
+
* any `paths:` block — harmless, since a path-only rule is never thinned.
|
|
31
|
+
*
|
|
32
|
+
* **A user-modified file is skipped, not overwritten.** The preserved set comes
|
|
33
|
+
* from the conflict tracker, the same one the copy consulted. Step 1.3 requires
|
|
34
|
+
* an upgrade to preserve and report those files; thinning one would destroy an
|
|
35
|
+
* edit the copy had just declined to destroy.
|
|
36
|
+
*
|
|
37
|
+
* The rollback of step 1.2 needs nothing here, and the reason is the ordering.
|
|
38
|
+
*
|
|
39
|
+
* The install manifest records each deployed file's digest from the bytes ON
|
|
40
|
+
* DISK at manifest-write time, which is after this pass. So the next run sees
|
|
41
|
+
* `recorded-unchanged` for a thinned file, `decideDeployWrite` returns `write`,
|
|
42
|
+
* and the verbatim copy restores the full body. Switching the mode back to
|
|
43
|
+
* `eager-all` therefore un-thins by simply not running this pass — no inverse
|
|
44
|
+
* operation, and no stub left behind. The property that carries it is the
|
|
45
|
+
* ordering, which `tests/scripts/install_thin_layer.test.ts` pins directly.
|
|
46
|
+
*
|
|
47
|
+
* Side-effect-free apart from the writes it is asked to make. No CLI entry, no
|
|
48
|
+
* `process.exit` — this module is reachable from the `build:install-bundle`
|
|
49
|
+
* entry and `check_installer_import_purity` forbids both.
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
import * as fs from 'node:fs';
|
|
53
|
+
import * as path from 'node:path';
|
|
54
|
+
|
|
55
|
+
import * as os from 'node:os';
|
|
56
|
+
|
|
57
|
+
import {
|
|
58
|
+
buildHostLimitRows,
|
|
59
|
+
loadHostInstructionLimits,
|
|
60
|
+
readLayer,
|
|
61
|
+
renderHostLimitRows,
|
|
62
|
+
type LayerReading,
|
|
63
|
+
} from '../scripts/_lib/installed_layer.js';
|
|
64
|
+
import { GLOBAL_RULE_DIRS, globalRuleLayerPath } from './globalRuleLayers.js';
|
|
65
|
+
import { manifest_path } from '../scripts/_lib/installed_tools.js';
|
|
66
|
+
import { NO_RECORDED_HASHES, readRecordedHashes } from './recordedOwnership.js';
|
|
67
|
+
import {
|
|
68
|
+
absoluteBodyLinkPrefix,
|
|
69
|
+
build_thin,
|
|
70
|
+
split_frontmatter,
|
|
71
|
+
} from '../scripts/_lib/thin_rules.js';
|
|
72
|
+
|
|
73
|
+
export interface ThinInstalledLayerResult {
|
|
74
|
+
/** Files that stand as a stub after this pass, whether or not it wrote them. */
|
|
75
|
+
readonly thinned: number;
|
|
76
|
+
/**
|
|
77
|
+
* Of those, the ones whose bytes actually changed.
|
|
78
|
+
*
|
|
79
|
+
* Separate from {@link thinned} so a repeat install's receipt does not claim
|
|
80
|
+
* work it did not do: on an already-thinned layer every file is `thinned`
|
|
81
|
+
* and none is `rewritten`.
|
|
82
|
+
*/
|
|
83
|
+
readonly rewritten: number;
|
|
84
|
+
/** Installed rules the predicate keeps full-bodied (kernel, trigger-less, path-only, `no_stub`). */
|
|
85
|
+
readonly kept: number;
|
|
86
|
+
/** Basenames skipped because the conflict tracker preserved a user-modified file. */
|
|
87
|
+
readonly preserved: readonly string[];
|
|
88
|
+
/** Basenames the stub map names that this layer does not carry — out of rule scope. */
|
|
89
|
+
readonly absent: readonly string[];
|
|
90
|
+
/** Per-rule write failures, reported rather than thrown. */
|
|
91
|
+
readonly failed: ReadonlyArray<{ readonly rule: string; readonly reason: string }>;
|
|
92
|
+
/** The absolute prefix every stub's `Body:` link was built on. */
|
|
93
|
+
readonly bodyLinkPrefix: string;
|
|
94
|
+
/** Characters the layer held before and after, over the files this pass examined. */
|
|
95
|
+
readonly charsBefore: number;
|
|
96
|
+
readonly charsAfter: number;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface ThinInstalledLayerOptions {
|
|
100
|
+
/** The installed rule directory, e.g. `~/.claude/rules`. */
|
|
101
|
+
readonly rulesDir: string;
|
|
102
|
+
/** The package this install was made from — carries `dist/`, `src/config/`. */
|
|
103
|
+
readonly packageRoot: string;
|
|
104
|
+
/** Absolute paths the conflict tracker preserved; never rewritten here. */
|
|
105
|
+
readonly preserved?: ReadonlySet<string>;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Replace the body of every thinnable rule in `rulesDir` with its stub.
|
|
110
|
+
*
|
|
111
|
+
* Idempotent: a layer already thinned is rewritten to byte-identical content,
|
|
112
|
+
* so a repeated install neither grows nor drifts.
|
|
113
|
+
*/
|
|
114
|
+
export function thinInstalledRuleLayer(opts: ThinInstalledLayerOptions): ThinInstalledLayerResult {
|
|
115
|
+
const packageRoot = path.resolve(opts.packageRoot);
|
|
116
|
+
const bodySourceDir = path.join(packageRoot, 'dist', 'agent-src', 'rules');
|
|
117
|
+
const bodyLinkPrefix = absoluteBodyLinkPrefix(packageRoot);
|
|
118
|
+
const preserved = opts.preserved ?? new Set<string>();
|
|
119
|
+
|
|
120
|
+
// Scope is `null` on purpose. The copy already applied the install's rule
|
|
121
|
+
// scope, so the files that EXIST in `rulesDir` are the scoped set; asking
|
|
122
|
+
// `build_thin` to resolve the scope a second time would be a second reader
|
|
123
|
+
// of the same decision, and the two could disagree. Anything the stub map
|
|
124
|
+
// names that is not on disk is simply out of scope and is reported as such.
|
|
125
|
+
const stubs = build_thin(bodySourceDir, null, null, null, { packageRoot, bodyLinkPrefix });
|
|
126
|
+
|
|
127
|
+
let thinned = 0;
|
|
128
|
+
let rewritten = 0;
|
|
129
|
+
let kept = 0;
|
|
130
|
+
let charsBefore = 0;
|
|
131
|
+
let charsAfter = 0;
|
|
132
|
+
const preservedHits: string[] = [];
|
|
133
|
+
const absent: string[] = [];
|
|
134
|
+
const failed: Array<{ rule: string; reason: string }> = [];
|
|
135
|
+
|
|
136
|
+
for (const [name, stubText] of stubs) {
|
|
137
|
+
const target = path.join(opts.rulesDir, name);
|
|
138
|
+
let installed: string;
|
|
139
|
+
try {
|
|
140
|
+
installed = fs.readFileSync(target, 'utf-8');
|
|
141
|
+
} catch (e) {
|
|
142
|
+
// ENOENT is the documented meaning of `absent` — the copy's rule
|
|
143
|
+
// scope did not place this rule here. Anything else (EACCES, EISDIR,
|
|
144
|
+
// EIO) is a FAILURE wearing a scoping outcome's clothes, and the
|
|
145
|
+
// receipt prints failures and does not print absences, so the two
|
|
146
|
+
// must not share a bucket.
|
|
147
|
+
if ((e as NodeJS.ErrnoException).code === 'ENOENT') absent.push(name);
|
|
148
|
+
else failed.push({ rule: name, reason: String(e) });
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
// KEPT-VS-THINNED IS DECIDED BY IDENTITY, NOT BY A CONTENT SNIFF.
|
|
152
|
+
// `build_thin` returns either a stub or the SOURCE TEXT VERBATIM, so
|
|
153
|
+
// the full-bodied branch is exactly "the map value equals the source
|
|
154
|
+
// file". Testing `is_thin_entry(stubText)` instead would misread a
|
|
155
|
+
// source rule whose own body happened to contain the marker — and that
|
|
156
|
+
// rule would then be written as `installedFrontmatter + fullSourceText`,
|
|
157
|
+
// duplicating the frontmatter block. Zero such rules ship today, which
|
|
158
|
+
// makes it latent rather than live, and `THIN_ENTRY_MARKER`'s own
|
|
159
|
+
// docstring argues against exactly this kind of inferred detection.
|
|
160
|
+
let sourceText: string | null;
|
|
161
|
+
try {
|
|
162
|
+
sourceText = fs.readFileSync(path.join(bodySourceDir, name), 'utf-8');
|
|
163
|
+
} catch {
|
|
164
|
+
sourceText = null;
|
|
165
|
+
}
|
|
166
|
+
if (stubText === sourceText) {
|
|
167
|
+
// Kept full-bodied by `build_thin`'s own predicate — kernel,
|
|
168
|
+
// trigger-less, path-only, or a declared `no_stub` member. The
|
|
169
|
+
// installed copy is already the right shape; touching it would be
|
|
170
|
+
// this pass inventing a decision the projector did not make.
|
|
171
|
+
kept += 1;
|
|
172
|
+
charsBefore += installed.length;
|
|
173
|
+
charsAfter += installed.length;
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
if (preserved.has(path.resolve(target))) {
|
|
177
|
+
preservedHits.push(name);
|
|
178
|
+
charsBefore += installed.length;
|
|
179
|
+
charsAfter += installed.length;
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
const [frontmatter] = split_frontmatter(installed);
|
|
183
|
+
charsBefore += installed.length;
|
|
184
|
+
// `split_frontmatter` answers `['', text]` for a file that OPENS a
|
|
185
|
+
// frontmatter block and never closes it — a truncated write, a file
|
|
186
|
+
// ending exactly on the closing fence with no trailing newline, CRLF.
|
|
187
|
+
// Writing `'' + stub` there would delete the `package:` line this
|
|
188
|
+
// module's header calls the reaper's only evidence, which is the one
|
|
189
|
+
// corruption this pass could cause. A file that claims a block it does
|
|
190
|
+
// not have is left exactly as found and reported.
|
|
191
|
+
//
|
|
192
|
+
// The test is a REGEX rather than `startsWith('---')` because the
|
|
193
|
+
// claim can be preceded by a BOM or by leading whitespace, and either
|
|
194
|
+
// one took the prefix check's "no block here" branch straight into the
|
|
195
|
+
// write it exists to prevent.
|
|
196
|
+
if (frontmatter === '' && /^\uFEFF?[\s]*---/.test(installed)) {
|
|
197
|
+
failed.push({
|
|
198
|
+
rule: name,
|
|
199
|
+
reason: 'opens a frontmatter block that never closes — left untouched so its ' +
|
|
200
|
+
'ownership keys survive',
|
|
201
|
+
});
|
|
202
|
+
charsAfter += installed.length;
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
const next = `${frontmatter}${stubText}`;
|
|
206
|
+
try {
|
|
207
|
+
if (next !== installed) {
|
|
208
|
+
fs.writeFileSync(target, next, 'utf-8');
|
|
209
|
+
rewritten += 1;
|
|
210
|
+
}
|
|
211
|
+
thinned += 1;
|
|
212
|
+
charsAfter += next.length;
|
|
213
|
+
} catch (e) {
|
|
214
|
+
failed.push({ rule: name, reason: String(e) });
|
|
215
|
+
charsAfter += installed.length;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (stubs.size === 0) {
|
|
220
|
+
// A ZERO-STUB MAP AND A ZERO-THINNABLE LAYER PRINT THE SAME RECEIPT, so
|
|
221
|
+
// the one that is a failure has to say so. `_globSortedMd` swallows an
|
|
222
|
+
// unreadable directory and answers `[]`, which is what a wrongly
|
|
223
|
+
// resolved package root produces — the hazard `ThinRoots` exists for.
|
|
224
|
+
// Without this the feature would be silently not applied behind a
|
|
225
|
+
// success-shaped line, and the installer's try/catch sees only throws.
|
|
226
|
+
failed.push({
|
|
227
|
+
rule: bodySourceDir,
|
|
228
|
+
reason: 'no rule bodies found to build stubs from — the layer was left full-bodied',
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return {
|
|
233
|
+
thinned,
|
|
234
|
+
rewritten,
|
|
235
|
+
kept,
|
|
236
|
+
preserved: preservedHits,
|
|
237
|
+
absent,
|
|
238
|
+
failed,
|
|
239
|
+
bodyLinkPrefix,
|
|
240
|
+
charsBefore,
|
|
241
|
+
charsAfter,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* One receipt line per outcome, for a caller that prints.
|
|
247
|
+
*
|
|
248
|
+
* Returns the lines rather than writing them, so the installer's `--quiet` and
|
|
249
|
+
* its progress-event emitter stay the only things deciding what a consumer sees.
|
|
250
|
+
*/
|
|
251
|
+
export function describeThinInstalledLayer(res: ThinInstalledLayerResult): string[] {
|
|
252
|
+
const out: string[] = [];
|
|
253
|
+
out.push(
|
|
254
|
+
res.rewritten === 0 && res.thinned > 0
|
|
255
|
+
? ` claude-code: ${String(res.thinned)} rule(s) already thinned, ` +
|
|
256
|
+
`${String(res.kept)} kept full-bodied; ` +
|
|
257
|
+
`${String(res.charsAfter)} chars — nothing to rewrite`
|
|
258
|
+
: ` claude-code: thinned ${String(res.rewritten)} rule(s) to stubs, ` +
|
|
259
|
+
`${String(res.kept)} kept full-bodied; ` +
|
|
260
|
+
`${String(res.charsBefore)} -> ${String(res.charsAfter)} chars`,
|
|
261
|
+
);
|
|
262
|
+
if (res.preserved.length > 0) {
|
|
263
|
+
out.push(
|
|
264
|
+
` claude-code: ${String(res.preserved.length)} rule(s) left full — you edited them ` +
|
|
265
|
+
`and they are preserved, never overwritten (${res.preserved.join(', ')})`,
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
for (const f of res.failed) {
|
|
269
|
+
out.push(` claude-code: ${f.rule}: could not write stub — ${f.reason}`);
|
|
270
|
+
}
|
|
271
|
+
return out;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* The install receipt's instruction-budget block — step 1.4's "at install".
|
|
276
|
+
*
|
|
277
|
+
* The step asks for the limit, the split and the 80 % warning AT INSTALL, and
|
|
278
|
+
* the measurement existed only behind the standalone `installed_layer_report`
|
|
279
|
+
* CLI: `buildHostLimitRows`'s own docstring named "the install receipt" as the
|
|
280
|
+
* caller it was exported for, and that caller did not exist. A consumer whose
|
|
281
|
+
* layer is over budget learned it from the host, which is the notice the
|
|
282
|
+
* roadmap exists to remove.
|
|
283
|
+
*
|
|
284
|
+
* Global layers only, deliberately. The install writes `~/.claude/rules` and
|
|
285
|
+
* its siblings; a project layer is not what this run just changed, and folding
|
|
286
|
+
* one in would make the receipt's number disagree with the thing it is a
|
|
287
|
+
* receipt FOR.
|
|
288
|
+
*
|
|
289
|
+
* Never throws. A receipt that cannot be measured is silent rather than fatal —
|
|
290
|
+
* the deploy it describes has already happened correctly.
|
|
291
|
+
*/
|
|
292
|
+
export function installReceiptBudgetLines(packageRoot: string, home: string = os.homedir()): string[] {
|
|
293
|
+
try {
|
|
294
|
+
const recorded = (() => {
|
|
295
|
+
try {
|
|
296
|
+
return readRecordedHashes(manifest_path(packageRoot), packageRoot);
|
|
297
|
+
} catch {
|
|
298
|
+
return NO_RECORDED_HASHES;
|
|
299
|
+
}
|
|
300
|
+
})();
|
|
301
|
+
const layers: LayerReading[] = [];
|
|
302
|
+
for (const host of Object.keys(GLOBAL_RULE_DIRS).sort()) {
|
|
303
|
+
const dir = globalRuleLayerPath(host, home);
|
|
304
|
+
if (dir === null) continue;
|
|
305
|
+
layers.push(readLayer(host, 'global', dir, recorded));
|
|
306
|
+
}
|
|
307
|
+
const rows = buildHostLimitRows(layers, loadHostInstructionLimits());
|
|
308
|
+
return rows.length === 0 ? [] : renderHostLimitRows(rows).map((l) => ` ${l}`);
|
|
309
|
+
} catch {
|
|
310
|
+
return [];
|
|
311
|
+
}
|
|
312
|
+
}
|