@event4u/agent-config 14.6.0 → 14.8.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.
Files changed (258) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +255 -145
  3. package/README.md +1 -1
  4. package/dist/agent-src/commands/analyze/inbox.md +264 -10
  5. package/dist/agent-src/commands/analyze.md +1 -1
  6. package/dist/agent-src/commands/evals/git-pr-merge.json +13 -0
  7. package/dist/agent-src/commands/evals/roadmap.json +17 -1
  8. package/dist/agent-src/commands/pr/create.md +14 -2
  9. package/dist/agent-src/commands/pr/merge.md +379 -0
  10. package/dist/agent-src/commands/roadmap/process-full.md +272 -32
  11. package/dist/agent-src/commands/roadmap.md +1 -1
  12. package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +46 -36
  13. package/dist/agent-src/contexts/execution/autonomy-mechanics.md +37 -2
  14. package/dist/agent-src/contexts/execution/roadmap-execution-contract.md +95 -2
  15. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +289 -106
  16. package/dist/agent-src/rules/artifact-drafting-protocol.md +5 -1
  17. package/dist/agent-src/rules/context-hygiene.md +2 -0
  18. package/dist/agent-src/rules/decision-revisit-gate.md +67 -32
  19. package/dist/agent-src/rules/fix-what-you-see.md +108 -0
  20. package/dist/agent-src/rules/no-roadmap-references.md +0 -5
  21. package/dist/agent-src/rules/recurring-criticism.md +110 -0
  22. package/dist/agent-src/rules/roadmap-progress-sync.md +20 -1
  23. package/dist/agent-src/rules/rule-type-governance.md +0 -2
  24. package/dist/agent-src/rules/self-repair-loop.md +17 -0
  25. package/dist/agent-src/rules/skill-quality.md +0 -2
  26. package/dist/agent-src/rules/source-confidentiality.md +37 -7
  27. package/dist/agent-src/scripts/archival_sweep.ts +117 -0
  28. package/dist/agent-src/scripts/archive_completed_roadmaps.ts +26 -7
  29. package/dist/agent-src/scripts/dashboard_mode.ts +212 -0
  30. package/dist/agent-src/scripts/gate_budget.ts +282 -0
  31. package/dist/agent-src/scripts/gate_execute.ts +72 -31
  32. package/dist/agent-src/scripts/roadmap_gates.ts +74 -3
  33. package/dist/agent-src/scripts/update_roadmap_progress.ts +296 -53
  34. package/dist/agent-src/skills/adr-create/SKILL.md +124 -5
  35. package/dist/agent-src/skills/ai-council/SKILL.md +48 -807
  36. package/dist/agent-src/skills/ai-council/references/advanced-modes.md +320 -0
  37. package/dist/agent-src/skills/ai-council/references/cost-and-redaction.md +99 -0
  38. package/dist/agent-src/skills/ai-council/references/execution-modes.md +162 -0
  39. package/dist/agent-src/skills/ai-council/references/output-and-synthesis.md +125 -0
  40. package/dist/agent-src/skills/ai-council/references/procedure.md +143 -0
  41. package/dist/agent-src/skills/decision-record/SKILL.md +61 -3
  42. package/dist/agent-src/skills/decision-review/SKILL.md +65 -0
  43. package/dist/agent-src/skills/merge-conflicts/SKILL.md +26 -0
  44. package/dist/agent-src/skills/quality-tools/SKILL.md +34 -326
  45. package/dist/agent-src/skills/quality-tools/references/js-ts-tools.md +113 -0
  46. package/dist/agent-src/skills/quality-tools/references/php-tools.md +222 -0
  47. package/dist/agent-src/skills/roadmap-management/SKILL.md +15 -409
  48. package/dist/agent-src/skills/roadmap-management/references/archival.md +226 -0
  49. package/dist/agent-src/skills/roadmap-management/references/authoring.md +107 -0
  50. package/dist/agent-src/skills/roadmap-management/references/dashboard.md +47 -0
  51. package/dist/agent-src/skills/roadmap-management/references/lifecycle.md +58 -0
  52. package/dist/agent-src/skills/security/SKILL.md +8 -0
  53. package/dist/agent-src/skills/security-audit/SKILL.md +12 -1
  54. package/dist/agent-src/skills/skill-writing/SKILL.md +34 -570
  55. package/dist/agent-src/skills/skill-writing/references/procedure.md +275 -0
  56. package/dist/agent-src/skills/skill-writing/references/read-only-scripts.md +18 -0
  57. package/dist/agent-src/skills/skill-writing/references/section-patterns.md +298 -0
  58. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  59. package/dist/agent-src/templates/roadmaps.md +65 -4
  60. package/dist/agent-src/templates/scripts/telemetry/flush_sender.mjs +163 -0
  61. package/dist/agent-src/templates/scripts/telemetry/remote.ts +160 -7
  62. package/dist/agent-src/templates/scripts/telemetry/transport.ts +169 -0
  63. package/dist/cli/registry.js +2 -1
  64. package/dist/cli/registry.js.map +1 -1
  65. package/dist/cli-delegate/chunk-2GNWGMAL.js +172 -0
  66. package/dist/cli-delegate/{chunk-P6LQDIWK.js → chunk-7PRE6ZV2.js} +338 -0
  67. package/dist/cli-delegate/{chunk-BKO2AZ7Z.js → chunk-B2ERU2VA.js} +1 -1
  68. package/dist/cli-delegate/chunk-E4XIZDIB.js +402 -0
  69. package/dist/cli-delegate/{chunk-3UCJWMMP.js → chunk-EEJUMCDZ.js} +16 -16
  70. package/dist/cli-delegate/{chunk-NXFWFAYV.js → chunk-G23AT6GD.js} +1 -0
  71. package/dist/cli-delegate/{chunk-XURJ4AQC.js → chunk-H4VIVD4C.js} +385 -284
  72. package/dist/cli-delegate/chunk-I3EIN2YG.js +36 -0
  73. package/dist/cli-delegate/{chunk-RLN73PR5.js → chunk-LPPWO5CE.js} +12 -8
  74. package/dist/cli-delegate/chunk-MWRD5YOH.js +100 -0
  75. package/dist/cli-delegate/{chunk-UEZ7D35A.js → chunk-N7CDDGI3.js} +4 -23
  76. package/dist/cli-delegate/{chunk-63WSP6YZ.js → chunk-PMEKGDED.js} +74 -167
  77. package/dist/cli-delegate/{chunk-GRRPEPRN.js → chunk-T7L2QK3K.js} +24 -5
  78. package/dist/cli-delegate/{chunk-EVTY7ISR.js → chunk-YM4GZDJG.js} +9 -1
  79. package/dist/cli-delegate/cmd_adr_effective.js +1162 -0
  80. package/dist/cli-delegate/cmd_conformance.js +466 -716
  81. package/dist/cli-delegate/cmd_converge.js +3 -3
  82. package/dist/cli-delegate/cmd_doctor.js +10 -10
  83. package/dist/cli-delegate/cmd_explain.js +1 -1
  84. package/dist/cli-delegate/cmd_export.js +10 -9
  85. package/dist/cli-delegate/cmd_handoff.js +10 -9
  86. package/dist/cli-delegate/cmd_memory_get.js +2 -1
  87. package/dist/cli-delegate/cmd_migrate.js +2 -2
  88. package/dist/cli-delegate/cmd_packs_active.js +1 -1
  89. package/dist/cli-delegate/cmd_preflight.js +5 -5
  90. package/dist/cli-delegate/cmd_prune.js +11 -10
  91. package/dist/cli-delegate/cmd_route_audit.js +6 -5
  92. package/dist/cli-delegate/cmd_route_explain.js +3 -3
  93. package/dist/cli-delegate/cmd_session_recycle.js +840 -30
  94. package/dist/cli-delegate/cmd_settings_get.js +3 -3
  95. package/dist/cli-delegate/cmd_settings_migrate.js +10 -9
  96. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  97. package/dist/cli-delegate/cmd_sync.js +11 -10
  98. package/dist/cli-delegate/cmd_uninstall.js +13 -11
  99. package/dist/cli-delegate/cmd_update.js +4 -4
  100. package/dist/cli-delegate/cmd_upgrade.js +4 -4
  101. package/dist/cli-delegate/cmd_validate.js +11 -10
  102. package/dist/cli-delegate/cmd_versions.js +3 -3
  103. package/dist/discovery/deprecation-report.md +1 -1
  104. package/dist/discovery/discovery-manifest.json +111 -37
  105. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  106. package/dist/discovery/discovery-manifest.summary.md +5 -5
  107. package/dist/discovery/dormancy-report.md +1 -1
  108. package/dist/discovery/orphan-report.md +1 -1
  109. package/dist/discovery/packs.json +15 -12
  110. package/dist/discovery/trust-report.md +3 -3
  111. package/dist/discovery/workspaces.json +11 -8
  112. package/dist/hooks/dispatch.js +3165 -2192
  113. package/dist/install/claudePathsPlan.js +187 -0
  114. package/dist/install/claudePathsPlan.js.map +1 -0
  115. package/dist/install/globalRuleLayers.js +169 -0
  116. package/dist/install/globalRuleLayers.js.map +1 -0
  117. package/dist/install/hostLayerFingerprint.js +136 -0
  118. package/dist/install/hostLayerFingerprint.js.map +1 -0
  119. package/dist/install/install.mjs +553 -425
  120. package/dist/install/partitionEligibility.js +432 -0
  121. package/dist/install/partitionEligibility.js.map +1 -0
  122. package/dist/install/ruleLayerPartition.js +157 -0
  123. package/dist/install/ruleLayerPartition.js.map +1 -0
  124. package/dist/mcp/registry-manifest.json +3 -3
  125. package/dist/mcp/server.json +2 -2
  126. package/dist/mcp/server.mjs +1863 -1234
  127. package/dist/router.json +1 -1
  128. package/dist/scripts/_lib/agent_src.js +41 -0
  129. package/dist/scripts/_lib/agent_src.js.map +1 -1
  130. package/dist/scripts/_lib/install_layout.js +83 -0
  131. package/dist/scripts/_lib/install_layout.js.map +1 -0
  132. package/dist/scripts/_lib/installed_lock.js +444 -0
  133. package/dist/scripts/_lib/installed_lock.js.map +1 -0
  134. package/dist/scripts/_lib/prune_empty_dirs.js +85 -0
  135. package/dist/scripts/_lib/prune_empty_dirs.js.map +1 -0
  136. package/dist/scripts/condense.js +63 -251
  137. package/dist/scripts/condense.js.map +1 -1
  138. package/dist/server/schemas/settings.js +4 -0
  139. package/dist/server/schemas/settings.js.map +1 -1
  140. package/docs/contracts/settings-classes.md +12 -6
  141. package/docs/guidelines/agent-infra/agent-interaction-and-decision-quality.md +8 -3
  142. package/docs/guidelines/agent-infra/artifact-drafting-protocol-mechanics.md +45 -1
  143. package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +26 -0
  144. package/docs/guidelines/agent-infra/false-green.md +47 -1
  145. package/docs/guidelines/agent-infra/memory-access.md +17 -0
  146. package/docs/guidelines/agent-infra/recurring-criticism-mechanics.md +96 -0
  147. package/package.json +1 -1
  148. package/src/agent-src/scripts/archival_sweep.ts +117 -0
  149. package/src/agent-src/scripts/archive_completed_roadmaps.ts +26 -7
  150. package/src/agent-src/scripts/dashboard_mode.ts +212 -0
  151. package/src/agent-src/scripts/gate_budget.ts +282 -0
  152. package/src/agent-src/scripts/gate_execute.ts +72 -31
  153. package/src/agent-src/scripts/roadmap_gates.ts +74 -3
  154. package/src/agent-src/scripts/update_roadmap_progress.ts +296 -53
  155. package/src/agent-src/templates/scripts/telemetry/flush_sender.mjs +163 -0
  156. package/src/agent-src/templates/scripts/telemetry/remote.ts +160 -7
  157. package/src/agent-src/templates/scripts/telemetry/transport.ts +169 -0
  158. package/src/config/agent-settings.template.yml +27 -2
  159. package/src/config/ci-local-parity.yml +39 -0
  160. package/src/config/estate-count-budget.json +612 -3
  161. package/src/config/evaluator-budgets.json +3 -3
  162. package/src/config/gate-coverage.yml +220 -12
  163. package/src/config/gate-violation-baselines.json +73 -63
  164. package/src/config/gitignore-block.txt +15 -2
  165. package/src/config/hook-latency-budget.json +5 -0
  166. package/src/config/rule-activation-census.json +14 -6
  167. package/src/config/rule-stub-ceilings.json +34 -7
  168. package/src/install/claudePathsPlan.ts +196 -0
  169. package/src/install/globalRuleLayers.ts +197 -0
  170. package/src/install/hostLayerFingerprint.ts +145 -0
  171. package/src/install/partitionEligibility.ts +502 -0
  172. package/src/install/ruleLayerPartition.ts +212 -0
  173. package/src/scripts/_cli/cmd_adr_effective.ts +484 -0
  174. package/src/scripts/_cli/cmd_session_recycle.ts +9 -1
  175. package/src/scripts/_dispatch.bash +17 -1
  176. package/src/scripts/_lib/ac_heading.ts +30 -0
  177. package/src/scripts/_lib/adr_frontmatter.test.ts +365 -0
  178. package/src/scripts/_lib/adr_frontmatter.ts +459 -0
  179. package/src/scripts/_lib/agent_src.ts +42 -0
  180. package/src/scripts/_lib/bench_ab_pinned_repo.ts +162 -0
  181. package/src/scripts/_lib/bench_ab_workspace.ts +10 -1
  182. package/src/scripts/_lib/envelope_grounding.ts +49 -9
  183. package/src/scripts/_lib/installed_lock.ts +50 -5
  184. package/src/scripts/_lib/layer_overlap_notice.ts +11 -2
  185. package/src/scripts/_lib/md_section.ts +129 -0
  186. package/src/scripts/_lib/prune_empty_dirs.ts +80 -0
  187. package/src/scripts/_lib/scope_guard.sh +26 -3
  188. package/src/scripts/_lib/self_repair.ts +19 -0
  189. package/src/scripts/_lib/self_repair_class_b.ts +171 -0
  190. package/src/scripts/adr/evidence_census.ts +899 -0
  191. package/src/scripts/adr/regenerate_index.ts +12 -32
  192. package/src/scripts/adr_cite_check.ts +540 -39
  193. package/src/scripts/ai_council/cli_help.ts +55 -0
  194. package/src/scripts/audit_adr_coverage.ts +103 -17
  195. package/src/scripts/before_complete_hook.ts +153 -28
  196. package/src/scripts/bench_ab_v2_complexity.ts +27 -7
  197. package/src/scripts/bench_hook_latency.ts +204 -0
  198. package/src/scripts/build_archive_index.ts +34 -0
  199. package/src/scripts/check_adr_frontmatter.ts +574 -39
  200. package/src/scripts/check_hook_bundle_content.ts +219 -0
  201. package/src/scripts/check_new_adr_evidence.ts +529 -0
  202. package/src/scripts/check_references.ts +78 -0
  203. package/src/scripts/check_release_published.ts +32 -11
  204. package/src/scripts/check_rule_layer_partition.ts +531 -0
  205. package/src/scripts/check_single_delivery.ts +194 -17
  206. package/src/scripts/check_standing_rule_delivery.ts +8 -2
  207. package/src/scripts/ci_settle.ts +170 -0
  208. package/src/scripts/condense.ts +79 -259
  209. package/src/scripts/council_cli.ts +3 -3
  210. package/src/scripts/dispatch_r2_reviewer.ts +6 -14
  211. package/src/scripts/external_sources_denylist.json +4 -1
  212. package/src/scripts/hook_manifest.json +1 -1
  213. package/src/scripts/hook_manifest.yaml +77 -14
  214. package/src/scripts/hooks/block_no_verify.ts +61 -10
  215. package/src/scripts/hooks/block_unauthorized_git.ts +21 -1
  216. package/src/scripts/hooks/concern_registry.ts +4 -0
  217. package/src/scripts/hooks/dispatch_hook.ts +154 -187
  218. package/src/scripts/hooks/dispatch_issues.ts +38 -19
  219. package/src/scripts/hooks/fallback_yaml.ts +66 -0
  220. package/src/scripts/hooks/host_semantics.ts +21 -0
  221. package/src/scripts/hooks/injection_budget.ts +13 -3
  222. package/src/scripts/hooks/py_json_dumps.ts +103 -0
  223. package/src/scripts/hooks/rtk_wrap_hook.ts +8 -3
  224. package/src/scripts/hooks/run_continuation_hook.ts +8 -5
  225. package/src/scripts/hooks/ship_diff_volume_hook.ts +81 -15
  226. package/src/scripts/hooks/source_first_gate_hook.ts +464 -0
  227. package/src/scripts/hooks/state_io.ts +870 -21
  228. package/src/scripts/hooks/stdin_failure_policy.ts +96 -0
  229. package/src/scripts/hooks/subagent_ledger_hook.ts +44 -15
  230. package/src/scripts/hooks/telemetry_flush_hook.ts +139 -0
  231. package/src/scripts/hooks/telemetry_self_repair.ts +128 -0
  232. package/src/scripts/hooks/telemetry_usage_hook.ts +22 -4
  233. package/src/scripts/hooks/turn_end_gate_hook.ts +81 -15
  234. package/src/scripts/injection_scan_hook.ts +137 -5
  235. package/src/scripts/install.ts +16 -11
  236. package/src/scripts/language_mirror_hook.ts +561 -25
  237. package/src/scripts/lint_framework_leakage_allowlist.json +2 -2
  238. package/src/scripts/lint_hook_manifest.ts +49 -0
  239. package/src/scripts/lint_plan_risk_register.ts +4 -3
  240. package/src/scripts/lint_provenance_vocabulary.ts +472 -4
  241. package/src/scripts/lint_skill_router_head.ts +4 -4
  242. package/src/scripts/mcp_server/tools.ts +2 -2
  243. package/src/scripts/new_roadmap.ts +136 -0
  244. package/src/scripts/pack_mcp_content.ts +20 -5
  245. package/src/scripts/release.ts +37 -38
  246. package/src/scripts/report_layer_overlap.ts +6 -0
  247. package/src/scripts/routing_doctor.ts +5 -1
  248. package/src/scripts/self_repair_hook.ts +37 -5
  249. package/src/scripts/session_register_hook.ts +11 -0
  250. package/src/scripts/skill_usage_collect.ts +11 -1
  251. package/src/scripts/skill_usage_report.ts +278 -8
  252. package/src/scripts/sync_pr_branch.ts +170 -20
  253. package/src/server/schemas/settings.ts +8 -0
  254. package/dist/cli-delegate/chunk-EJITJBJX.js +0 -172
  255. package/dist/cli-delegate/chunk-PZWRPRWP.js +0 -38
  256. package/dist/cli-delegate/{chunk-PPQO4NGR.js → chunk-6RJA2F5A.js} +3 -3
  257. package/dist/cli-delegate/{chunk-RYNADUFG.js → chunk-LWTYTX6F.js} +0 -0
  258. package/dist/cli-delegate/{chunk-FBKL6JWT.js → chunk-MMSGVZKV.js} +3 -3
@@ -0,0 +1,187 @@
1
+ /**
2
+ * The Claude Code `paths:` emission plan — which globs a rule projects, and which
3
+ * it deliberately does not.
4
+ *
5
+ * Extracted from `condense.ts` on 2026-08-20 (road-to-single-delivery Phase 5).
6
+ * Two reasons, and the second is the one that made it worth doing now.
7
+ *
8
+ * It belongs here on its own merits: this is host-activation-key logic, the same
9
+ * subject as `emit_host_rules_cli.ts` beside it, and `condense.ts` is the
10
+ * orchestrator that calls it rather than the place it lives.
11
+ *
12
+ * And it pays for the phase that moved it. Phase 5.1 removed the path triggers
13
+ * from four package-only rules so they load unconditionally — a change ABOUT
14
+ * this plan — and wiring the delivery partition into `condense.ts` and
15
+ * `install.ts` added lines to two files already past the 1,500-line source
16
+ * ceiling, where `check_source_size_budget` counts every added line as a
17
+ * violation. That gate's own convention is that the total moves down through
18
+ * extraction and never up through a baseline raise, so the extraction is the fix
19
+ * rather than a favour: the plan lands in a module under the ceiling, and the
20
+ * ratchet sees a net improvement instead of a regression.
21
+ *
22
+ * Side-effect-free, no CLI entry, no `process.exit`.
23
+ */
24
+ /**
25
+ * Derive host-native activation globs from a rule's `triggers:` frontmatter
26
+ * (road-to-request-scoped-rule-load Phase 2). `file_pattern` triggers map
27
+ * verbatim; `path_prefix` triggers map to `<prefix>**`. Keyword / phrase /
28
+ * command triggers produce no glob — those rules stay
29
+ * description-activated (Cursor Agent-Requested / Windsurf model_decision).
30
+ */
31
+ export function derive_trigger_globs(meta) {
32
+ const triggers = meta['triggers'];
33
+ if (!Array.isArray(triggers)) {
34
+ return [];
35
+ }
36
+ const globs = [];
37
+ for (const t of triggers) {
38
+ if (t === null || typeof t !== 'object' || Array.isArray(t))
39
+ continue;
40
+ const obj = t;
41
+ if (typeof obj['file_pattern'] === 'string' && obj['file_pattern']) {
42
+ globs.push(obj['file_pattern']);
43
+ }
44
+ else if (typeof obj['path_prefix'] === 'string' && obj['path_prefix']) {
45
+ const prefix = obj['path_prefix'];
46
+ globs.push(prefix.endsWith('/') ? `${prefix}**` : `${prefix}**`);
47
+ }
48
+ }
49
+ // De-dup, keep declaration order (deterministic output).
50
+ return [...new Set(globs)];
51
+ }
52
+ /**
53
+ * A brace group the host expands, versus an agent-config placeholder that only
54
+ * LOOKS like one.
55
+ *
56
+ * `path_prefix: "{module_root}/"` in `roadmap-ci-steps-policy` is resolved by
57
+ * this suite from `modules.root_paths`. Emitted verbatim into a host `paths:`
58
+ * list it is a brace group with no comma, which the host expands to the literal
59
+ * string `{module_root}` — a pattern that matches no file. The rule would then
60
+ * reach the model **never** instead of on-demand, and nothing would say so: the
61
+ * probed contract records this exact degradation as silent
62
+ * (`claude-code-rules-dir-contract.md` § constraints).
63
+ *
64
+ * Dropping the pattern is fail-safe in the one direction that matters. A rule
65
+ * left with no pattern at all carries no `paths:` key and loads
66
+ * unconditionally — the status quo, not a regression. A rule carrying a
67
+ * no-op pattern is invisible.
68
+ *
69
+ * The discriminator is the comma, not a placeholder allowlist: `{ts,tsx}` is a
70
+ * real alternation, `{module_root}` cannot be one.
71
+ */
72
+ export function _is_unresolved_placeholder(pattern) {
73
+ for (const m of pattern.matchAll(/\{([^}]*)\}/g)) {
74
+ if (!m[1].includes(',')) {
75
+ return true;
76
+ }
77
+ }
78
+ return false;
79
+ }
80
+ /**
81
+ * `[` opens a bracket expression for the host. An unparseable one makes that
82
+ * pattern match nothing while the rule's other patterns keep working — so a
83
+ * literal `[` in a trigger is escaped rather than trusted.
84
+ */
85
+ export function _escape_claude_bracket(pattern) {
86
+ return pattern.replace(/\[/g, '\\[');
87
+ }
88
+ /**
89
+ * Expanded-pattern cost of one glob: the product of its brace alternations.
90
+ * The host budgets a rule's whole `paths:` list at 1,000 expanded patterns and
91
+ * 4 MiB, and a list over budget is used UNEXPANDED — braces then match
92
+ * literally, i.e. nothing. Counting keeps the emitter under the cliff instead
93
+ * of discovering it as a silent no-op.
94
+ */
95
+ export function _expanded_pattern_count(pattern) {
96
+ let n = 1;
97
+ for (const m of pattern.matchAll(/\{([^}]*)\}/g)) {
98
+ n *= m[1].split(',').length;
99
+ }
100
+ return n;
101
+ }
102
+ /** Host-documented budget for one rule's whole `paths:` list. */
103
+ export const CLAUDE_PATHS_PATTERN_BUDGET = 1000;
104
+ /**
105
+ * Does this rule declare any trigger that is NOT path-shaped?
106
+ *
107
+ * Load-bearing for `_claude_paths_plan` below, because one `triggers:` list
108
+ * means two different things on two hosts. Cursor and Windsurf treat globs as
109
+ * ADDITIVE — a rule with empty globs still reaches the agent through its
110
+ * description (see `_emit_cursor_mdc`'s own comment). Claude Code treats
111
+ * `paths:` as EXCLUSIVE: with it the rule loads on a path match; without it the
112
+ * rule loads unconditionally. The emitter fed both semantics from one list, so
113
+ * a rule that declared keywords *and* one path glob silently became
114
+ * path-gated-only on Claude.
115
+ */
116
+ export function _has_non_path_trigger(meta) {
117
+ const triggers = meta['triggers'];
118
+ if (!Array.isArray(triggers))
119
+ return false;
120
+ for (const t of triggers) {
121
+ if (t === null || typeof t !== 'object' || Array.isArray(t))
122
+ continue;
123
+ const obj = t;
124
+ if (typeof obj['keyword'] === 'string' && obj['keyword'])
125
+ return true;
126
+ if (typeof obj['phrase'] === 'string' && obj['phrase'])
127
+ return true;
128
+ }
129
+ return false;
130
+ }
131
+ /**
132
+ * Derive the host's `paths:` list for one rule, with every drop recorded.
133
+ *
134
+ * Kernel / always-apply rules get an EMPTY list on purpose: absent `paths:` is
135
+ * how this host says "load unconditionally", which is exactly what an Iron-Law
136
+ * rule needs. Scoping one would be a correctness regression dressed as a byte
137
+ * saving — and the same contract notes path-scoped rules are not re-injected
138
+ * after `/compact`, so an obligation that must survive compaction cannot be
139
+ * path-scoped at all.
140
+ */
141
+ export function _claude_paths_plan(meta) {
142
+ const always_apply = Boolean(meta['alwaysApply'] || meta['type'] === 'always');
143
+ if (always_apply) {
144
+ return { globs: [], dropped: [] };
145
+ }
146
+ const globs = [];
147
+ const dropped = [];
148
+ // A rule that ALSO declares keyword / phrase triggers gets no `paths:` at
149
+ // all, and therefore keeps loading unconditionally on Claude Code.
150
+ //
151
+ // Emitting `paths:` here would narrow the rule to a path match and discard
152
+ // every keyword the author wrote, because this host reads the list as the
153
+ // whole gate rather than as one way in. Measured before this guard: 25 of
154
+ // 110 projected rules carried `paths:` and 19 of them also carried keyword
155
+ // triggers — `design-fidelity` lost 21 of them, in exactly the pasted-
156
+ // screenshot and capability-URL handovers its own routing section names as
157
+ // the classes it exists to catch. Nothing in the tree recorded a decision
158
+ // to make that trade, which is why it is treated as a defect.
159
+ //
160
+ // The drop is REPORTED rather than silent, so a rule whose obligation
161
+ // really is path-bound is visible as a candidate for removing its keyword
162
+ // triggers — the narrowing then becomes an authoring decision instead of an
163
+ // emitter side effect. Cursor and Windsurf are untouched: globs are
164
+ // additive there, so the same rule keeps both routes.
165
+ if (_has_non_path_trigger(meta)) {
166
+ for (const raw of derive_trigger_globs(meta)) {
167
+ dropped.push({ pattern: raw, reason: 'mixed-triggers' });
168
+ }
169
+ return { globs, dropped };
170
+ }
171
+ let spent = 0;
172
+ for (const raw of derive_trigger_globs(meta)) {
173
+ if (_is_unresolved_placeholder(raw)) {
174
+ dropped.push({ pattern: raw, reason: 'unresolved-placeholder' });
175
+ continue;
176
+ }
177
+ const cost = _expanded_pattern_count(raw);
178
+ if (spent + cost > CLAUDE_PATHS_PATTERN_BUDGET) {
179
+ dropped.push({ pattern: raw, reason: 'over-budget' });
180
+ continue;
181
+ }
182
+ spent += cost;
183
+ globs.push(_escape_claude_bracket(raw));
184
+ }
185
+ return { globs, dropped };
186
+ }
187
+ //# sourceMappingURL=claudePathsPlan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claudePathsPlan.js","sourceRoot":"","sources":["../../src/install/claudePathsPlan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Where each host's GLOBAL rule layer lives on disk, and whether it carries a
3
+ * given set of rules.
4
+ *
5
+ * ## Why this module exists
6
+ *
7
+ * ADR-236 partitions rules between the global and the project layer: an artefact
8
+ * scoped to this package alone stays project-local, everything else is delivered
9
+ * globally and withheld from the project tree. `partitionEligibility.ts` decides
10
+ * WHETHER the partition is on, by verifying the claude host layer against
11
+ * `installed.lock`. Its own docstring then draws the only conclusion that
12
+ * evidence supports — it "says nothing about `~/.cursor`", so withholding a
13
+ * cursor artefact on the strength of a claude fingerprint "would deliver it
14
+ * nowhere".
15
+ *
16
+ * That reasoning is right and its scope was too narrow for rules. The rule
17
+ * filter sits in `condense.ts::_scoped_rule_basenames()`, which is per-RUN and
18
+ * not per-DIRECTORY, so it already withholds from `.clinerules` on a claude
19
+ * fingerprint — the exact extrapolation the docstring refuses. Measured
20
+ * 2026-08-22 in a freshly generated worktree with the partition active:
21
+ *
22
+ * ```
23
+ * .claude/rules 13 files, 13 package-only, 0 global-only
24
+ * .clinerules 14 files, 13 package-only, 0 global-only
25
+ * .cursor/rules 126 files, 26 package-only, 100 global-only
26
+ * .windsurf/rules 113 files, 13 package-only, 100 global-only
27
+ * .augment/rules 118 files, 15 package-only, 103 global-only
28
+ * ```
29
+ *
30
+ * Two hosts partition on borrowed evidence; three do not partition at all. This
31
+ * module supplies the evidence per host so neither case has to be guessed:
32
+ * {@link globalRuleLayerNames} reads the host's own global directory, and
33
+ * {@link hostLayerCarries} answers the one question a withhold depends on.
34
+ *
35
+ * ## Why the paths are written out rather than imported
36
+ *
37
+ * `USER_SCOPE_PATHS` in `src/scripts/install.ts` is the authority for a host's
38
+ * global scope ROOT, and it is the right authority. It is not imported here for
39
+ * two reasons: `install.ts` is a multi-thousand-line CLI entry point that pulls
40
+ * in the whole installer graph, and a scope root is not a rules path — cline's
41
+ * root already IS its rules directory (`~/Documents/Cline/Rules/`) while every
42
+ * other host appends a subdirectory. So the mapping is stated once here and
43
+ * PINNED against `USER_SCOPE_PATHS` by `tests/install/global_rule_layers.test.ts`,
44
+ * which fails if a root moves. A pinned restatement drifts loudly; an import
45
+ * would have cost the bundle.
46
+ */
47
+ import * as fs from 'node:fs';
48
+ import * as os from 'node:os';
49
+ import * as path from 'node:path';
50
+ /**
51
+ * Tool id → its global rule directory, relative to the user's home.
52
+ *
53
+ * Keys are the ids `USER_SCOPE_PATHS` and `TOOL_SIGNALS` use, so a row here is
54
+ * traceable to the installer's own scope table rather than invented.
55
+ *
56
+ * The five hosts below are the ones that receive a projected rule TREE. A host
57
+ * whose rules arrive as a single concatenated file (`.windsurfrules`,
58
+ * `GEMINI.md`, copilot instructions) has no directory to compare and is
59
+ * deliberately absent: {@link globalRuleLayerPath} returns null for it, which
60
+ * reads as "no evidence" and keeps the full projection.
61
+ */
62
+ export const GLOBAL_RULE_DIRS = {
63
+ 'claude-code': '.claude/rules',
64
+ cursor: '.cursor/rules',
65
+ augment: '.augment/rules',
66
+ // Windsurf's scope root is `~/.codeium/windsurf/`, not `~/.windsurf/`.
67
+ windsurf: '.codeium/windsurf/rules',
68
+ // Cline's scope root IS the rules directory — no `rules/` suffix.
69
+ cline: 'Documents/Cline/Rules',
70
+ };
71
+ /**
72
+ * Project rule directory → the tool id whose global layer covers it.
73
+ *
74
+ * Five entries, because five directories receive a projected rule TREE. Two are
75
+ * symlink trees (`condense.ts::TOOL_DIRS`); three are written by their own
76
+ * emitters — `generate_cursor_mdc_rules`, `generate_windsurf_modern_rules`, and
77
+ * `project_to_augment`. The `.cursor/rules` row covers both the `.mdc` files and
78
+ * the legacy `.md` symlinks that share that directory.
79
+ *
80
+ * It lives here rather than in either consumer so the gate that reports a
81
+ * duplicate and the emitters that must stop creating one read the same list. A
82
+ * sixth host directory added to one and not the other is exactly how a gate goes
83
+ * green over a tree nobody checks.
84
+ */
85
+ export const PROJECT_RULE_DIRS = {
86
+ '.claude/rules': 'claude-code',
87
+ '.cursor/rules': 'cursor',
88
+ '.clinerules': 'cline',
89
+ '.windsurf/rules': 'windsurf',
90
+ '.augment/rules': 'augment',
91
+ };
92
+ /** The tool id whose global layer covers a project rule directory, or null. */
93
+ export function toolIdForProjectRuleDir(dir) {
94
+ return PROJECT_RULE_DIRS[dir] ?? null;
95
+ }
96
+ /** Every tool id that has a global rule directory this module can read. */
97
+ export function hostsWithGlobalRuleLayer() {
98
+ return Object.keys(GLOBAL_RULE_DIRS).sort();
99
+ }
100
+ /**
101
+ * Absolute path to a host's global rule directory, or null when the host has no
102
+ * directory-shaped global layer.
103
+ *
104
+ * Null is a real answer and not an error: it means this module cannot supply
105
+ * evidence for that host, and every caller must read that as "do not withhold".
106
+ */
107
+ export function globalRuleLayerPath(toolId, home = process.env['HOME'] ?? os.homedir()) {
108
+ const rel = GLOBAL_RULE_DIRS[toolId];
109
+ return rel === undefined ? null : path.join(home, rel);
110
+ }
111
+ /**
112
+ * The rule basenames a host's global layer actually holds, normalised to `.md`.
113
+ *
114
+ * Returns null when the directory does not exist or cannot be read — the
115
+ * absent-tolerant contract Phase 1.1 requires. Normalisation matters because
116
+ * Cursor's tree is `.mdc`: comparing raw filenames would report every rule as
117
+ * missing there and turn the partition off for the one host whose duplication is
118
+ * largest.
119
+ */
120
+ export function globalRuleLayerNames(toolId, home = process.env['HOME'] ?? os.homedir()) {
121
+ const dir = globalRuleLayerPath(toolId, home);
122
+ if (dir === null) {
123
+ return null;
124
+ }
125
+ let entries;
126
+ try {
127
+ entries = fs.readdirSync(dir);
128
+ }
129
+ catch {
130
+ return null;
131
+ }
132
+ return entries
133
+ .filter((f) => f.endsWith('.md') || f.endsWith('.mdc'))
134
+ .map((f) => (f.endsWith('.mdc') ? `${f.slice(0, -4)}.md` : f))
135
+ .sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
136
+ }
137
+ /**
138
+ * Does this host's global layer carry every rule that would be withheld from its
139
+ * project directory?
140
+ *
141
+ * This is the property `personaPartition`'s docstring states as the precondition
142
+ * for any withhold — "withholding is only safe once the surviving layer is known
143
+ * to carry what is withheld" — asked per host instead of inferred from claude.
144
+ *
145
+ * Fails safe in all three negative directions: a host with no directory layer, a
146
+ * directory that is absent, and a directory missing even one name all return
147
+ * `carries: false`, so the caller keeps the full projection. An empty
148
+ * `ruleNames` is vacuously carried, which is correct — withholding nothing needs
149
+ * no evidence.
150
+ */
151
+ export function hostLayerCarries(toolId, ruleNames, home = process.env['HOME'] ?? os.homedir()) {
152
+ const layerPath = globalRuleLayerPath(toolId, home);
153
+ if (layerPath === null) {
154
+ return { carries: false, layerPath: null, missing: [...ruleNames], reason: 'no-layer-for-host' };
155
+ }
156
+ const have = globalRuleLayerNames(toolId, home);
157
+ if (have === null) {
158
+ return { carries: false, layerPath, missing: [...ruleNames], reason: 'layer-absent' };
159
+ }
160
+ const present = new Set(have);
161
+ const missing = ruleNames.filter((n) => !present.has(n));
162
+ return {
163
+ carries: missing.length === 0,
164
+ layerPath,
165
+ missing,
166
+ reason: missing.length === 0 ? 'carries' : 'missing-names',
167
+ };
168
+ }
169
+ //# sourceMappingURL=globalRuleLayers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"globalRuleLayers.js","sourceRoot":"","sources":["../../src/install/globalRuleLayers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Deterministic content fingerprint of a host artefact layer.
3
+ *
4
+ * ## Why a fingerprint and not a version number
5
+ *
6
+ * The single-delivery partition is a *removal*: after it, `task generate-tools`
7
+ * writes 16 rules and zero skills into `<repo>/.claude/` and every other
8
+ * artefact is delivered ONLY by the host-global layer. That removes the build's
9
+ * own repair path — regeneration can no longer heal a stale global layer,
10
+ * because it stops writing the affected files at all.
11
+ *
12
+ * A package version proves *which installer claims to have written* the host
13
+ * directory. It does not prove that the directory holds the artefacts this
14
+ * checkout is about to omit. Measured on the maintainer machine 2026-08-20:
15
+ * `package.json` and the published release both read `14.6.0`, while **153
16
+ * skills existed only in the project layer** and 37 only in the global one. A
17
+ * version-equality predicate would have authorised the partition there and
18
+ * silently dropped 153 skills. So the predicate compares content.
19
+ *
20
+ * ## Cost — measured, and the earlier figure was wrong
21
+ *
22
+ * A prior revision of the blocker recorded 5.85 s per generation for a
23
+ * content comparison and used that number to argue the approach was too
24
+ * expensive. That figure was a **shell artefact**: it spawned one `cat` per
25
+ * file. Re-measured in Node on the same tree: **61 ms** over 664 source files,
26
+ * **103 ms** over 1019 host files. The comparison is cheap; the 5.85 s never
27
+ * described this mechanism.
28
+ *
29
+ * ## Contract
30
+ *
31
+ * Side-effect-free, no CLI entry, no `process.exit` — this module ships inside
32
+ * the consumer installer bundle, same constraint as `ruleInScope.ts`. Keep it
33
+ * that way.
34
+ */
35
+ import { createHash } from 'node:crypto';
36
+ import * as fs from 'node:fs';
37
+ import * as path from 'node:path';
38
+ /**
39
+ * Fingerprint wire version. Bump when the digest input changes shape, so an
40
+ * old lockfile compares unequal instead of comparing wrong.
41
+ */
42
+ export const FINGERPRINT_SCHEMA = 1;
43
+ /** Files under `dir`, recursively, sorted by path — deterministic order. */
44
+ function collectFiles(dir) {
45
+ const out = [];
46
+ const walk = (d) => {
47
+ let entries;
48
+ try {
49
+ entries = fs.readdirSync(d, { withFileTypes: true });
50
+ }
51
+ catch {
52
+ return; // absent or unreadable → contributes nothing
53
+ }
54
+ const sorted = [...entries].sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
55
+ for (const e of sorted) {
56
+ const p = path.join(d, e.name);
57
+ if (e.isDirectory()) {
58
+ walk(p);
59
+ }
60
+ else if (e.isFile()) {
61
+ out.push(p);
62
+ }
63
+ // Symlinks are deliberately skipped: the project layer symlinks into
64
+ // `dist/`, the global layer holds real files, and following links
65
+ // would make two layers of identical CONTENT fingerprint differently
66
+ // depending on which side was read.
67
+ }
68
+ };
69
+ walk(dir);
70
+ return out.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
71
+ }
72
+ /**
73
+ * Content fingerprint over the given layers.
74
+ *
75
+ * The digest covers, per file, its **layer-relative** path and its bytes — so a
76
+ * rename, a deletion, an addition, and an edit each change it, while the
77
+ * absolute location of the layer does not. Layers are folded in the order
78
+ * given; pass them in a stable order.
79
+ *
80
+ * An absent layer root is not an error: it contributes zero files. That makes
81
+ * the fingerprint of "nothing installed" well-defined and unequal to any real
82
+ * layer, which is what the fail-safe branch of the partition predicate wants.
83
+ */
84
+ export function fingerprintLayers(layers) {
85
+ const h = createHash('sha256');
86
+ h.update(`v${FINGERPRINT_SCHEMA}\0`);
87
+ for (const layer of layers) {
88
+ h.update(`layer:${layer.label}\0`);
89
+ for (const file of collectFiles(layer.root)) {
90
+ h.update(path.relative(layer.root, file));
91
+ h.update('\0');
92
+ try {
93
+ h.update(fs.readFileSync(file));
94
+ }
95
+ catch {
96
+ // A file that vanished between listing and reading is a partial
97
+ // install by definition. Fold a marker rather than throwing, so
98
+ // the digest differs from a complete layer instead of aborting
99
+ // the caller.
100
+ h.update('\0unreadable\0');
101
+ }
102
+ h.update('\0');
103
+ }
104
+ }
105
+ return h.digest('hex');
106
+ }
107
+ /**
108
+ * The host-global artefact layers the partition depends on, in a FIXED order.
109
+ *
110
+ * **This is the one definition, and it has to be.** The installer fingerprints
111
+ * what it wrote; the build fingerprints what it finds and compares. Two inline
112
+ * lists would drift — and a drift here does not fail loudly, it makes the
113
+ * partition permanently unreachable while every fail-safe branch reports a
114
+ * plausible reason. Import this; never re-list the layers.
115
+ *
116
+ * Only `claude-code` is covered, and the narrowness is the measurement rather
117
+ * than an omission: the 110-rule / 203,873-token duplication ADR-236 partitions
118
+ * was measured on `~/.claude/`. Widening this list widens what must be verified
119
+ * before a single artefact is withheld, so it is a deliberate addition.
120
+ *
121
+ * `commands` is in the list because the project layer writes skills AND commands
122
+ * into ONE directory (`generate_claude_commands` targets `CLAUDE_SKILLS_DIR`)
123
+ * while the host layer keeps them apart in `~/.claude/skills` and
124
+ * `~/.claude/commands`. Emptying the project directory withholds commands too,
125
+ * so commands must be part of what gets verified first. Measured 2026-08-20:
126
+ * 414 project entries against 298 host skills plus 93 host commands.
127
+ */
128
+ export function hostLayerInputs(userHome) {
129
+ const claude = path.join(userHome, '.claude');
130
+ return [
131
+ { label: 'rules', root: path.join(claude, 'rules') },
132
+ { label: 'skills', root: path.join(claude, 'skills') },
133
+ { label: 'commands', root: path.join(claude, 'commands') },
134
+ ];
135
+ }
136
+ //# sourceMappingURL=hostLayerFingerprint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hostLayerFingerprint.js","sourceRoot":"","sources":["../../src/install/hostLayerFingerprint.ts"],"names":[],"mappings":"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"}