@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.
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +255 -145
- package/README.md +1 -1
- package/dist/agent-src/commands/analyze/inbox.md +264 -10
- package/dist/agent-src/commands/analyze.md +1 -1
- package/dist/agent-src/commands/evals/git-pr-merge.json +13 -0
- package/dist/agent-src/commands/evals/roadmap.json +17 -1
- package/dist/agent-src/commands/pr/create.md +14 -2
- package/dist/agent-src/commands/pr/merge.md +379 -0
- package/dist/agent-src/commands/roadmap/process-full.md +272 -32
- package/dist/agent-src/commands/roadmap.md +1 -1
- package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +46 -36
- package/dist/agent-src/contexts/execution/autonomy-mechanics.md +37 -2
- package/dist/agent-src/contexts/execution/roadmap-execution-contract.md +95 -2
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +289 -106
- package/dist/agent-src/rules/artifact-drafting-protocol.md +5 -1
- package/dist/agent-src/rules/context-hygiene.md +2 -0
- package/dist/agent-src/rules/decision-revisit-gate.md +67 -32
- package/dist/agent-src/rules/fix-what-you-see.md +108 -0
- package/dist/agent-src/rules/no-roadmap-references.md +0 -5
- package/dist/agent-src/rules/recurring-criticism.md +110 -0
- package/dist/agent-src/rules/roadmap-progress-sync.md +20 -1
- package/dist/agent-src/rules/rule-type-governance.md +0 -2
- package/dist/agent-src/rules/self-repair-loop.md +17 -0
- package/dist/agent-src/rules/skill-quality.md +0 -2
- package/dist/agent-src/rules/source-confidentiality.md +37 -7
- package/dist/agent-src/scripts/archival_sweep.ts +117 -0
- package/dist/agent-src/scripts/archive_completed_roadmaps.ts +26 -7
- package/dist/agent-src/scripts/dashboard_mode.ts +212 -0
- package/dist/agent-src/scripts/gate_budget.ts +282 -0
- package/dist/agent-src/scripts/gate_execute.ts +72 -31
- package/dist/agent-src/scripts/roadmap_gates.ts +74 -3
- package/dist/agent-src/scripts/update_roadmap_progress.ts +296 -53
- package/dist/agent-src/skills/adr-create/SKILL.md +124 -5
- package/dist/agent-src/skills/ai-council/SKILL.md +48 -807
- package/dist/agent-src/skills/ai-council/references/advanced-modes.md +320 -0
- package/dist/agent-src/skills/ai-council/references/cost-and-redaction.md +99 -0
- package/dist/agent-src/skills/ai-council/references/execution-modes.md +162 -0
- package/dist/agent-src/skills/ai-council/references/output-and-synthesis.md +125 -0
- package/dist/agent-src/skills/ai-council/references/procedure.md +143 -0
- package/dist/agent-src/skills/decision-record/SKILL.md +61 -3
- package/dist/agent-src/skills/decision-review/SKILL.md +65 -0
- package/dist/agent-src/skills/merge-conflicts/SKILL.md +26 -0
- package/dist/agent-src/skills/quality-tools/SKILL.md +34 -326
- package/dist/agent-src/skills/quality-tools/references/js-ts-tools.md +113 -0
- package/dist/agent-src/skills/quality-tools/references/php-tools.md +222 -0
- package/dist/agent-src/skills/roadmap-management/SKILL.md +15 -409
- package/dist/agent-src/skills/roadmap-management/references/archival.md +226 -0
- package/dist/agent-src/skills/roadmap-management/references/authoring.md +107 -0
- package/dist/agent-src/skills/roadmap-management/references/dashboard.md +47 -0
- package/dist/agent-src/skills/roadmap-management/references/lifecycle.md +58 -0
- package/dist/agent-src/skills/security/SKILL.md +8 -0
- package/dist/agent-src/skills/security-audit/SKILL.md +12 -1
- package/dist/agent-src/skills/skill-writing/SKILL.md +34 -570
- package/dist/agent-src/skills/skill-writing/references/procedure.md +275 -0
- package/dist/agent-src/skills/skill-writing/references/read-only-scripts.md +18 -0
- package/dist/agent-src/skills/skill-writing/references/section-patterns.md +298 -0
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/roadmaps.md +65 -4
- package/dist/agent-src/templates/scripts/telemetry/flush_sender.mjs +163 -0
- package/dist/agent-src/templates/scripts/telemetry/remote.ts +160 -7
- package/dist/agent-src/templates/scripts/telemetry/transport.ts +169 -0
- package/dist/cli/registry.js +2 -1
- package/dist/cli/registry.js.map +1 -1
- package/dist/cli-delegate/chunk-2GNWGMAL.js +172 -0
- package/dist/cli-delegate/{chunk-P6LQDIWK.js → chunk-7PRE6ZV2.js} +338 -0
- package/dist/cli-delegate/{chunk-BKO2AZ7Z.js → chunk-B2ERU2VA.js} +1 -1
- package/dist/cli-delegate/chunk-E4XIZDIB.js +402 -0
- package/dist/cli-delegate/{chunk-3UCJWMMP.js → chunk-EEJUMCDZ.js} +16 -16
- package/dist/cli-delegate/{chunk-NXFWFAYV.js → chunk-G23AT6GD.js} +1 -0
- package/dist/cli-delegate/{chunk-XURJ4AQC.js → chunk-H4VIVD4C.js} +385 -284
- package/dist/cli-delegate/chunk-I3EIN2YG.js +36 -0
- package/dist/cli-delegate/{chunk-RLN73PR5.js → chunk-LPPWO5CE.js} +12 -8
- package/dist/cli-delegate/chunk-MWRD5YOH.js +100 -0
- package/dist/cli-delegate/{chunk-UEZ7D35A.js → chunk-N7CDDGI3.js} +4 -23
- package/dist/cli-delegate/{chunk-63WSP6YZ.js → chunk-PMEKGDED.js} +74 -167
- package/dist/cli-delegate/{chunk-GRRPEPRN.js → chunk-T7L2QK3K.js} +24 -5
- package/dist/cli-delegate/{chunk-EVTY7ISR.js → chunk-YM4GZDJG.js} +9 -1
- package/dist/cli-delegate/cmd_adr_effective.js +1162 -0
- package/dist/cli-delegate/cmd_conformance.js +466 -716
- package/dist/cli-delegate/cmd_converge.js +3 -3
- package/dist/cli-delegate/cmd_doctor.js +10 -10
- package/dist/cli-delegate/cmd_explain.js +1 -1
- package/dist/cli-delegate/cmd_export.js +10 -9
- package/dist/cli-delegate/cmd_handoff.js +10 -9
- package/dist/cli-delegate/cmd_memory_get.js +2 -1
- package/dist/cli-delegate/cmd_migrate.js +2 -2
- package/dist/cli-delegate/cmd_packs_active.js +1 -1
- package/dist/cli-delegate/cmd_preflight.js +5 -5
- package/dist/cli-delegate/cmd_prune.js +11 -10
- package/dist/cli-delegate/cmd_route_audit.js +6 -5
- package/dist/cli-delegate/cmd_route_explain.js +3 -3
- package/dist/cli-delegate/cmd_session_recycle.js +840 -30
- package/dist/cli-delegate/cmd_settings_get.js +3 -3
- package/dist/cli-delegate/cmd_settings_migrate.js +10 -9
- package/dist/cli-delegate/cmd_settings_set.js +1 -1
- package/dist/cli-delegate/cmd_sync.js +11 -10
- package/dist/cli-delegate/cmd_uninstall.js +13 -11
- package/dist/cli-delegate/cmd_update.js +4 -4
- package/dist/cli-delegate/cmd_upgrade.js +4 -4
- package/dist/cli-delegate/cmd_validate.js +11 -10
- package/dist/cli-delegate/cmd_versions.js +3 -3
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +111 -37
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +5 -5
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +15 -12
- package/dist/discovery/trust-report.md +3 -3
- package/dist/discovery/workspaces.json +11 -8
- package/dist/hooks/dispatch.js +3165 -2192
- package/dist/install/claudePathsPlan.js +187 -0
- package/dist/install/claudePathsPlan.js.map +1 -0
- package/dist/install/globalRuleLayers.js +169 -0
- package/dist/install/globalRuleLayers.js.map +1 -0
- package/dist/install/hostLayerFingerprint.js +136 -0
- package/dist/install/hostLayerFingerprint.js.map +1 -0
- package/dist/install/install.mjs +553 -425
- package/dist/install/partitionEligibility.js +432 -0
- package/dist/install/partitionEligibility.js.map +1 -0
- package/dist/install/ruleLayerPartition.js +157 -0
- package/dist/install/ruleLayerPartition.js.map +1 -0
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +1863 -1234
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/agent_src.js +41 -0
- package/dist/scripts/_lib/agent_src.js.map +1 -1
- package/dist/scripts/_lib/install_layout.js +83 -0
- package/dist/scripts/_lib/install_layout.js.map +1 -0
- package/dist/scripts/_lib/installed_lock.js +444 -0
- package/dist/scripts/_lib/installed_lock.js.map +1 -0
- package/dist/scripts/_lib/prune_empty_dirs.js +85 -0
- package/dist/scripts/_lib/prune_empty_dirs.js.map +1 -0
- package/dist/scripts/condense.js +63 -251
- package/dist/scripts/condense.js.map +1 -1
- package/dist/server/schemas/settings.js +4 -0
- package/dist/server/schemas/settings.js.map +1 -1
- package/docs/contracts/settings-classes.md +12 -6
- package/docs/guidelines/agent-infra/agent-interaction-and-decision-quality.md +8 -3
- package/docs/guidelines/agent-infra/artifact-drafting-protocol-mechanics.md +45 -1
- package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +26 -0
- package/docs/guidelines/agent-infra/false-green.md +47 -1
- package/docs/guidelines/agent-infra/memory-access.md +17 -0
- package/docs/guidelines/agent-infra/recurring-criticism-mechanics.md +96 -0
- package/package.json +1 -1
- package/src/agent-src/scripts/archival_sweep.ts +117 -0
- package/src/agent-src/scripts/archive_completed_roadmaps.ts +26 -7
- package/src/agent-src/scripts/dashboard_mode.ts +212 -0
- package/src/agent-src/scripts/gate_budget.ts +282 -0
- package/src/agent-src/scripts/gate_execute.ts +72 -31
- package/src/agent-src/scripts/roadmap_gates.ts +74 -3
- package/src/agent-src/scripts/update_roadmap_progress.ts +296 -53
- package/src/agent-src/templates/scripts/telemetry/flush_sender.mjs +163 -0
- package/src/agent-src/templates/scripts/telemetry/remote.ts +160 -7
- package/src/agent-src/templates/scripts/telemetry/transport.ts +169 -0
- package/src/config/agent-settings.template.yml +27 -2
- package/src/config/ci-local-parity.yml +39 -0
- package/src/config/estate-count-budget.json +612 -3
- package/src/config/evaluator-budgets.json +3 -3
- package/src/config/gate-coverage.yml +220 -12
- package/src/config/gate-violation-baselines.json +73 -63
- package/src/config/gitignore-block.txt +15 -2
- package/src/config/hook-latency-budget.json +5 -0
- package/src/config/rule-activation-census.json +14 -6
- package/src/config/rule-stub-ceilings.json +34 -7
- package/src/install/claudePathsPlan.ts +196 -0
- package/src/install/globalRuleLayers.ts +197 -0
- package/src/install/hostLayerFingerprint.ts +145 -0
- package/src/install/partitionEligibility.ts +502 -0
- package/src/install/ruleLayerPartition.ts +212 -0
- package/src/scripts/_cli/cmd_adr_effective.ts +484 -0
- package/src/scripts/_cli/cmd_session_recycle.ts +9 -1
- package/src/scripts/_dispatch.bash +17 -1
- package/src/scripts/_lib/ac_heading.ts +30 -0
- package/src/scripts/_lib/adr_frontmatter.test.ts +365 -0
- package/src/scripts/_lib/adr_frontmatter.ts +459 -0
- package/src/scripts/_lib/agent_src.ts +42 -0
- package/src/scripts/_lib/bench_ab_pinned_repo.ts +162 -0
- package/src/scripts/_lib/bench_ab_workspace.ts +10 -1
- package/src/scripts/_lib/envelope_grounding.ts +49 -9
- package/src/scripts/_lib/installed_lock.ts +50 -5
- package/src/scripts/_lib/layer_overlap_notice.ts +11 -2
- package/src/scripts/_lib/md_section.ts +129 -0
- package/src/scripts/_lib/prune_empty_dirs.ts +80 -0
- package/src/scripts/_lib/scope_guard.sh +26 -3
- package/src/scripts/_lib/self_repair.ts +19 -0
- package/src/scripts/_lib/self_repair_class_b.ts +171 -0
- package/src/scripts/adr/evidence_census.ts +899 -0
- package/src/scripts/adr/regenerate_index.ts +12 -32
- package/src/scripts/adr_cite_check.ts +540 -39
- package/src/scripts/ai_council/cli_help.ts +55 -0
- package/src/scripts/audit_adr_coverage.ts +103 -17
- package/src/scripts/before_complete_hook.ts +153 -28
- package/src/scripts/bench_ab_v2_complexity.ts +27 -7
- package/src/scripts/bench_hook_latency.ts +204 -0
- package/src/scripts/build_archive_index.ts +34 -0
- package/src/scripts/check_adr_frontmatter.ts +574 -39
- package/src/scripts/check_hook_bundle_content.ts +219 -0
- package/src/scripts/check_new_adr_evidence.ts +529 -0
- package/src/scripts/check_references.ts +78 -0
- package/src/scripts/check_release_published.ts +32 -11
- package/src/scripts/check_rule_layer_partition.ts +531 -0
- package/src/scripts/check_single_delivery.ts +194 -17
- package/src/scripts/check_standing_rule_delivery.ts +8 -2
- package/src/scripts/ci_settle.ts +170 -0
- package/src/scripts/condense.ts +79 -259
- package/src/scripts/council_cli.ts +3 -3
- package/src/scripts/dispatch_r2_reviewer.ts +6 -14
- package/src/scripts/external_sources_denylist.json +4 -1
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +77 -14
- package/src/scripts/hooks/block_no_verify.ts +61 -10
- package/src/scripts/hooks/block_unauthorized_git.ts +21 -1
- package/src/scripts/hooks/concern_registry.ts +4 -0
- package/src/scripts/hooks/dispatch_hook.ts +154 -187
- package/src/scripts/hooks/dispatch_issues.ts +38 -19
- package/src/scripts/hooks/fallback_yaml.ts +66 -0
- package/src/scripts/hooks/host_semantics.ts +21 -0
- package/src/scripts/hooks/injection_budget.ts +13 -3
- package/src/scripts/hooks/py_json_dumps.ts +103 -0
- package/src/scripts/hooks/rtk_wrap_hook.ts +8 -3
- package/src/scripts/hooks/run_continuation_hook.ts +8 -5
- package/src/scripts/hooks/ship_diff_volume_hook.ts +81 -15
- package/src/scripts/hooks/source_first_gate_hook.ts +464 -0
- package/src/scripts/hooks/state_io.ts +870 -21
- package/src/scripts/hooks/stdin_failure_policy.ts +96 -0
- package/src/scripts/hooks/subagent_ledger_hook.ts +44 -15
- package/src/scripts/hooks/telemetry_flush_hook.ts +139 -0
- package/src/scripts/hooks/telemetry_self_repair.ts +128 -0
- package/src/scripts/hooks/telemetry_usage_hook.ts +22 -4
- package/src/scripts/hooks/turn_end_gate_hook.ts +81 -15
- package/src/scripts/injection_scan_hook.ts +137 -5
- package/src/scripts/install.ts +16 -11
- package/src/scripts/language_mirror_hook.ts +561 -25
- package/src/scripts/lint_framework_leakage_allowlist.json +2 -2
- package/src/scripts/lint_hook_manifest.ts +49 -0
- package/src/scripts/lint_plan_risk_register.ts +4 -3
- package/src/scripts/lint_provenance_vocabulary.ts +472 -4
- package/src/scripts/lint_skill_router_head.ts +4 -4
- package/src/scripts/mcp_server/tools.ts +2 -2
- package/src/scripts/new_roadmap.ts +136 -0
- package/src/scripts/pack_mcp_content.ts +20 -5
- package/src/scripts/release.ts +37 -38
- package/src/scripts/report_layer_overlap.ts +6 -0
- package/src/scripts/routing_doctor.ts +5 -1
- package/src/scripts/self_repair_hook.ts +37 -5
- package/src/scripts/session_register_hook.ts +11 -0
- package/src/scripts/skill_usage_collect.ts +11 -1
- package/src/scripts/skill_usage_report.ts +278 -8
- package/src/scripts/sync_pr_branch.ts +170 -20
- package/src/server/schemas/settings.ts +8 -0
- package/dist/cli-delegate/chunk-EJITJBJX.js +0 -172
- package/dist/cli-delegate/chunk-PZWRPRWP.js +0 -38
- package/dist/cli-delegate/{chunk-PPQO4NGR.js → chunk-6RJA2F5A.js} +3 -3
- package/dist/cli-delegate/{chunk-RYNADUFG.js → chunk-LWTYTX6F.js} +0 -0
- 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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"}
|