@event4u/agent-config 14.21.0 → 14.22.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 +198 -0
- package/CREDITS.md +37 -0
- package/dist/agent-src/commands/analyze/inbox.md +380 -5
- package/dist/agent-src/commands/analyze/repo.md +4 -2
- package/dist/agent-src/contexts/communication/rules-auto/token-efficiency-mechanics.md +69 -0
- package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +5 -5
- package/dist/agent-src/rules/external-code-graph-interop.md +19 -14
- package/dist/agent-src/rules/token-efficiency.md +26 -3
- package/dist/agent-src/scripts/_cli_entry.ts +51 -0
- package/dist/agent-src/scripts/archive_completed_roadmaps.ts +14 -23
- package/dist/agent-src/scripts/roadmap_gates.ts +4 -20
- package/dist/agent-src/scripts/roadmap_set_step.ts +5 -2
- package/dist/agent-src/scripts/stubs_due.ts +4 -18
- package/dist/agent-src/scripts/update_roadmap_progress.ts +7 -24
- package/dist/agent-src/skills/code-intelligence/SKILL.md +25 -0
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +17 -0
- package/dist/agent-src/templates/agent-settings.md +0 -1
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/cli-delegate/_cli_entry.js +8 -0
- package/dist/cli-delegate/_lib/pin_resolver.js +170 -0
- package/dist/cli-delegate/archival_sweep.js +8 -0
- package/dist/cli-delegate/archive_completed_roadmaps.js +494 -0
- package/dist/cli-delegate/check_update_banner.js +923 -0
- package/dist/cli-delegate/chunk-5EK5WQN6.js +28 -0
- package/dist/cli-delegate/chunk-7XGD2OEU.js +96 -0
- package/dist/cli-delegate/{chunk-643Z7FIC.js → chunk-BIXEDQPZ.js} +6 -0
- package/dist/cli-delegate/chunk-DBH4AHDK.js +155 -0
- package/dist/cli-delegate/chunk-JO25G5H7.js +20 -0
- package/dist/cli-delegate/chunk-LTYVIY76.js +7961 -0
- package/dist/cli-delegate/chunk-QAJS5YWY.js +999 -0
- package/dist/cli-delegate/{chunk-ZVBOHSVF.js → chunk-TGIPZBCW.js} +1 -1
- package/dist/cli-delegate/{chunk-YS6XTLEK.js → chunk-TYSY33MD.js} +56 -6
- package/dist/cli-delegate/chunk-ULET4BYS.js +328 -0
- package/dist/cli-delegate/chunk-YDQSHTD7.js +187 -0
- package/dist/cli-delegate/chunk-YWVXLR44.js +48 -0
- package/dist/cli-delegate/chunk-ZZQBUHFK.js +273 -0
- package/dist/cli-delegate/cmd_conformance.js +2 -2
- package/dist/cli-delegate/cmd_export.js +1 -1
- package/dist/cli-delegate/cmd_handoff.js +26 -2
- package/dist/cli-delegate/cmd_prune.js +1 -1
- package/dist/cli-delegate/cmd_session_recycle.js +2 -2
- package/dist/cli-delegate/cmd_settings_migrate.js +1 -1
- package/dist/cli-delegate/cmd_sync.js +1 -1
- package/dist/cli-delegate/cmd_uninstall.js +1 -1
- package/dist/cli-delegate/cmd_validate.js +1 -1
- package/dist/cli-delegate/dashboard_mode.js +16 -0
- package/dist/cli-delegate/gate_budget.js +24 -0
- package/dist/cli-delegate/gate_execute.js +25 -0
- package/dist/cli-delegate/guarded_baseline.js +18 -0
- package/dist/cli-delegate/resume_probe.js +22 -0
- package/dist/cli-delegate/roadmap_gates.js +638 -0
- package/dist/cli-delegate/roadmap_set_step.js +173 -0
- package/dist/cli-delegate/stub_queue.js +47 -0
- package/dist/cli-delegate/stubs_due.js +38 -0
- package/dist/cli-delegate/ticket_bundles.js +59 -0
- package/dist/cli-delegate/update_roadmap_progress.js +60 -0
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +8 -8
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +1 -1
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +2 -2
- package/dist/discovery/trust-report.md +1 -1
- package/dist/discovery/workspaces.json +2 -2
- package/dist/hooks/dispatch.js +1193 -917
- package/dist/install/claudeLayerCarriage.js +237 -0
- package/dist/install/claudePathsPlan.js +79 -0
- package/dist/install/hostLayerFingerprint.js +1 -1
- package/dist/install/install.mjs +6 -0
- package/dist/install/partitionEligibility.js +231 -117
- package/dist/install/ruleLayerPartition.js +10 -7
- package/dist/mcp/registry-manifest.json +2 -2
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +39219 -38995
- package/dist/scripts/condense.js +32 -25
- package/docs/MIGRATION.md +49 -1
- package/docs/contracts/settings-classes.md +1 -1
- package/docs/guidelines/agent-infra/minimal-safe-diff-mechanics.md +73 -0
- package/package.json +7 -4
- package/src/agent-src/scripts/_cli_entry.ts +51 -0
- package/src/agent-src/scripts/archive_completed_roadmaps.ts +14 -23
- package/src/agent-src/scripts/roadmap_gates.ts +4 -20
- package/src/agent-src/scripts/roadmap_set_step.ts +5 -2
- package/src/agent-src/scripts/stubs_due.ts +4 -18
- package/src/agent-src/scripts/update_roadmap_progress.ts +7 -24
- package/src/config/continuity-surface.json +509 -0
- package/src/config/estate-count-budget.json +1 -1
- package/src/config/gate-coverage.yml +29 -8
- package/src/config/gate-violation-baselines.json +8 -8
- package/src/config/gitignore-block.txt +19 -0
- package/src/config/host-injection-effect.json +73 -0
- package/src/config/packed-binary-manifest.json +33 -0
- package/src/config/preamble-payload-budget.json +112 -105
- package/src/config/publish-surface.json +4 -2
- package/src/install/claudeLayerCarriage.ts +273 -0
- package/src/install/claudePathsPlan.ts +76 -0
- package/src/install/hostLayerFingerprint.ts +1 -1
- package/src/install/partitionEligibility.ts +261 -127
- package/src/install/ruleLayerPartition.ts +19 -23
- package/src/scripts/_dispatch.bash +77 -27
- package/src/scripts/_lib/auto_dispatch.ts +2 -2
- package/src/scripts/_lib/catalogue_layer_parity.ts +146 -0
- package/src/scripts/_lib/continuity_surface.ts +296 -0
- package/src/scripts/_lib/corpus_manifest.ts +26 -9
- package/src/scripts/_lib/hook_settings.ts +1 -1
- package/src/scripts/_lib/injection_effect.ts +151 -0
- package/src/scripts/_lib/layer_overlap_notice.ts +50 -6
- package/src/scripts/_lib/packed_binary_predicate.ts +225 -0
- package/src/scripts/_lib/pin_resolver.ts +13 -0
- package/src/scripts/_lib/regression_neighbourhood.ts +1 -1
- package/src/scripts/_lib/retired_status.ts +74 -0
- package/src/scripts/_lib/skill_catalogue.ts +156 -21
- package/src/scripts/_lib/subagent_capsule.ts +117 -8
- package/src/scripts/ai_council/api_transport.ts +5 -0
- package/src/scripts/ai_council/deliberation_metrics.ts +353 -0
- package/src/scripts/ai_council/prompts.ts +9 -0
- package/src/scripts/ai_council/synthesis_disagreement.ts +206 -0
- package/src/scripts/capture_skill_catalogue.ts +24 -11
- package/src/scripts/check_candidate_lines.ts +499 -0
- package/src/scripts/check_continuity_surface.ts +475 -0
- package/src/scripts/check_dependency_floors.ts +13 -6
- package/src/scripts/check_estate_count.ts +40 -79
- package/src/scripts/check_pack_size.ts +262 -23
- package/src/scripts/check_roadmap_trackable.ts +19 -16
- package/src/scripts/check_rule_layer_partition.ts +54 -34
- package/src/scripts/check_single_delivery.ts +97 -31
- package/src/scripts/check_update_banner.ts +13 -0
- package/src/scripts/code_graph/build.ts +138 -15
- package/src/scripts/code_graph/cli.ts +76 -8
- package/src/scripts/code_graph/extract.ts +11 -0
- package/src/scripts/code_graph/grammar_probe.ts +43 -0
- package/src/scripts/code_graph/loader.ts +88 -14
- package/src/scripts/code_graph/query.ts +121 -11
- package/src/scripts/code_graph/resolution_tiers.ts +238 -0
- package/src/scripts/code_graph/sqlite_store.ts +247 -6
- package/src/scripts/code_graph/types.ts +66 -1
- package/src/scripts/code_graph/validate.ts +23 -0
- package/src/scripts/condense.ts +34 -22
- package/src/scripts/corpus_manifest.ts +5 -5
- package/src/scripts/council_cli.ts +11 -12
- package/src/scripts/count_intent_disagreement.ts +426 -0
- package/src/scripts/handoff_context_hook.ts +79 -0
- package/src/scripts/hook_effect_doctor.ts +82 -1
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +64 -12
- package/src/scripts/hooks/chain_nudge_hook.ts +282 -0
- package/src/scripts/hooks/code_graph_context_hook.ts +212 -0
- package/src/scripts/hooks/concern_registry.ts +4 -2
- package/src/scripts/hooks/host_semantics.ts +1 -1
- package/src/scripts/hooks/orchestration_record_hook.ts +2 -2
- package/src/scripts/hooks/rule_inject_hook.ts +1 -1
- package/src/scripts/hooks/skill_route_hook.ts +11 -4
- package/src/scripts/inbox_source_census.ts +322 -0
- package/src/scripts/install-hooks.sh +82 -0
- package/src/scripts/{lint_carrier_integrity.ts → lint_deferral_integrity.ts} +60 -214
- package/src/scripts/lint_plan_risk_register.ts +13 -17
- package/src/scripts/mcp_server/suggest_skill.ts +72 -0
- package/src/scripts/mcp_server/tools.ts +2 -56
- package/src/scripts/media/lib/adapter-contract.md +7 -2
- package/src/scripts/media/lib/fixtures/flux/asset-0001.png +0 -0
- package/src/scripts/media/lib/fixtures/gemini-image/asset-0001.png +0 -0
- package/src/scripts/media/lib/fixtures/ideogram/asset-0001.png +0 -0
- package/src/scripts/rdp_candidates_delta.ts +276 -0
- package/src/scripts/rdp_quality_eval.ts +217 -19
- package/src/scripts/release_mix_taxonomy.json +3 -2
- package/src/scripts/report_host_injection_effect.ts +189 -0
- package/src/scripts/report_layer_overlap.ts +41 -0
- package/src/scripts/report_skill_menu_census.ts +341 -0
- package/src/scripts/skill_tools/score_skill_relevance.ts +70 -6
- package/src/scripts/skill_tools/suggest_skill_for_task.ts +37 -3
- package/src/vendor/grammars/README.md +61 -0
- package/src/vendor/grammars/tree-sitter-javascript.wasm +0 -0
- package/src/vendor/grammars/tree-sitter-php.wasm +0 -0
- package/src/vendor/grammars/tree-sitter-typescript.wasm +0 -0
- package/src/scripts/_lib/carrier_status.ts +0 -118
- package/src/scripts/_lib/estate_carrier_cases.ts +0 -72
- package/src/scripts/hooks/code_graph_nudge_hook.ts +0 -191
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Does the CLAUDE host-global layer already carry a given artefact?
|
|
3
|
+
*
|
|
4
|
+
* **Why this module exists — the veto it replaces.**
|
|
5
|
+
*
|
|
6
|
+
* ADR-236 partitions artefacts between `~/.claude/**` and `<repo>/.claude/**`.
|
|
7
|
+
* Its first implementation asked ONE question for the whole run: does
|
|
8
|
+
* `installed.lock` record the same version as this checkout, and does a content
|
|
9
|
+
* fingerprint of the host layer still match what the installer stamped? Any
|
|
10
|
+
* answer but yes fell back to the full projection.
|
|
11
|
+
*
|
|
12
|
+
* That veto is repo-wide, and it fires on facts that have nothing to do with any
|
|
13
|
+
* individual artefact. Measured 2026-09-07 on the maintainer machine, `installed.lock`
|
|
14
|
+
* at 14.19.0 against a 14.21.0 checkout:
|
|
15
|
+
*
|
|
16
|
+
* ```
|
|
17
|
+
* rules global 104 · project 15 · BOTH 0 ← already correct
|
|
18
|
+
* skills global 307 · project 414 · BOTH 261 ← the veto's cost
|
|
19
|
+
* personas global 32 · project 29 · BOTH 29
|
|
20
|
+
* commands global 94 · project 0 · BOTH 0
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* The rules row is correct because rules are partitioned by
|
|
24
|
+
* {@link hostLayerCarries} — per host directory, on that directory's own
|
|
25
|
+
* contents — while skills, personas and commands hung off the version veto and
|
|
26
|
+
* were therefore delivered twice, in every session, for as long as the install
|
|
27
|
+
* lagged a release. A version number is not evidence about an artefact.
|
|
28
|
+
*
|
|
29
|
+
* This module supplies the evidence the other three families were missing, in
|
|
30
|
+
* the shape rules already use: read the host directory, and answer per NAME.
|
|
31
|
+
*
|
|
32
|
+
* **Why per-name rather than per-family.**
|
|
33
|
+
*
|
|
34
|
+
* A partition is a removal and has no repair path — withholding a name the
|
|
35
|
+
* surviving layer does not hold deletes it from every session. Asking per name
|
|
36
|
+
* makes the fail-safe granular: an artefact the host layer lacks stays in the
|
|
37
|
+
* project layer on its own, and one absent name no longer keeps 300 duplicates
|
|
38
|
+
* alive with it. That is the property the repo-wide veto could not express.
|
|
39
|
+
*
|
|
40
|
+
* **Honest limits.**
|
|
41
|
+
*
|
|
42
|
+
* Presence of a NAME, never equality of CONTENT — and never PROVENANCE either.
|
|
43
|
+
* Two consequences, the second added 2026-09-07 after a neutral review named it.
|
|
44
|
+
*
|
|
45
|
+
* **Staleness.** A stale `~/.claude/skills/foo` satisfies this check, so an
|
|
46
|
+
* install that lags a release delivers that release's older copy of `foo` rather
|
|
47
|
+
* than two copies. That is the trade the
|
|
48
|
+
* owner chose on 2026-09-07, stated rather than implied: the remedy for a stale
|
|
49
|
+
* global layer is `agent-config install`, and {@link verifyHostLayer} still
|
|
50
|
+
* reports the staleness so a run says so out loud.
|
|
51
|
+
*
|
|
52
|
+
* **Shared namespace.** `~/.claude/skills` is not this package's directory. On
|
|
53
|
+
* the machine this was written, 307 entries = 299 package skills + 8 Cloudflare
|
|
54
|
+
* PLUGIN skills. No name collides today, but the mechanism needs no collision to
|
|
55
|
+
* be real: a plugin — or a hand-made `~/.claude/skills/<name>` — whose name
|
|
56
|
+
* matches a package skill makes this function withhold the package's project-layer
|
|
57
|
+
* copy, and the session then loads the foreign body under the package's skill
|
|
58
|
+
* name. The withhold is a removal with no repair path, which is what makes it
|
|
59
|
+
* worth naming rather than filing.
|
|
60
|
+
*
|
|
61
|
+
* **A NEIGHBOURING defect that WAS fixed, 2026-09-07.** This paragraph claimed
|
|
62
|
+
* "the skill is still delivered, under the right name, from one layer" — and that
|
|
63
|
+
* was false while `_readLayer` returned every directory name without checking
|
|
64
|
+
* that `SKILL.md` resolved: an empty `~/.claude/skills/<name>/` withheld the
|
|
65
|
+
* project copy and the artefact came from neither layer. A name now counts as
|
|
66
|
+
* carried only when the artefact behind it resolves, so the sentence above is
|
|
67
|
+
* true as written.
|
|
68
|
+
*
|
|
69
|
+
* NOT FIXED here, deliberately: distinguishing a package artefact from a foreign
|
|
70
|
+
* one needs a provenance marker the installer does not write today, and inventing
|
|
71
|
+
* one in this module would be a guess dressed as a check. What IS true is that
|
|
72
|
+
* the failure is loud in the direction that matters — the skill is still
|
|
73
|
+
* delivered, under the right name, from one layer — and silent only about whose
|
|
74
|
+
* body it is.
|
|
75
|
+
*
|
|
76
|
+
* Contract, same as its siblings in this directory: no `process.exit`, no CLI
|
|
77
|
+
* entry, node builtins only — it ships inside the consumer installer bundle.
|
|
78
|
+
*/
|
|
79
|
+
import * as fs from 'node:fs';
|
|
80
|
+
import * as os from 'node:os';
|
|
81
|
+
import * as path from 'node:path';
|
|
82
|
+
/** Family → its directory under the user's home. */
|
|
83
|
+
export const CLAUDE_ARTEFACT_DIRS = {
|
|
84
|
+
skills: '.claude/skills',
|
|
85
|
+
personas: '.claude/personas',
|
|
86
|
+
commands: '.claude/commands',
|
|
87
|
+
};
|
|
88
|
+
const _defaultHome = () => process.env['HOME'] ?? os.homedir();
|
|
89
|
+
/**
|
|
90
|
+
* One directory read per (family, home) per process.
|
|
91
|
+
*
|
|
92
|
+
* The generators ask per NAME — hundreds of times for skills, ~90 for commands —
|
|
93
|
+
* and the `commands` read walks a tree. Without the memo the per-name grain that
|
|
94
|
+
* makes the fail-safe granular would also make it quadratic. `null` is memoised
|
|
95
|
+
* too: an unreadable layer stays unreadable for the run, and re-probing it once
|
|
96
|
+
* per name would answer the same thing 300 times.
|
|
97
|
+
*/
|
|
98
|
+
const _memo = new Map();
|
|
99
|
+
/** Test seam — drop the memo so one process can read a layer it just changed. */
|
|
100
|
+
export function _resetClaudeLayerMemoForTest() {
|
|
101
|
+
_memo.clear();
|
|
102
|
+
}
|
|
103
|
+
/** Absolute path to the host-global directory for one family. */
|
|
104
|
+
export function claudeLayerPath(family, home = _defaultHome()) {
|
|
105
|
+
return path.join(home, CLAUDE_ARTEFACT_DIRS[family]);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* The names the host layer holds for one family, or null when it cannot be read.
|
|
109
|
+
*
|
|
110
|
+
* Null is a real answer and every caller must read it as "no evidence, withhold
|
|
111
|
+
* nothing" — not as an empty layer, which would authorise withholding everything.
|
|
112
|
+
*
|
|
113
|
+
* The name shape is the family's own, because that is what the caller compares
|
|
114
|
+
* against: a skill is a directory basename (`design-tokens`), a persona a
|
|
115
|
+
* filename (`ai-agent.md`), and a command a posix path relative to the commands
|
|
116
|
+
* root (`roadmap/process-full.md` for the colon form, `agent-status.md` for a
|
|
117
|
+
* flat one). `commands` therefore walks; the other two list one level.
|
|
118
|
+
*/
|
|
119
|
+
export function claudeLayerNames(family, home = _defaultHome()) {
|
|
120
|
+
const dir = claudeLayerPath(family, home);
|
|
121
|
+
const key = `${family}\u0000${dir}`;
|
|
122
|
+
const hit = _memo.get(key);
|
|
123
|
+
if (hit !== undefined) {
|
|
124
|
+
return hit;
|
|
125
|
+
}
|
|
126
|
+
const answer = _readLayer(family, dir);
|
|
127
|
+
_memo.set(key, answer);
|
|
128
|
+
return answer;
|
|
129
|
+
}
|
|
130
|
+
function _readLayer(family, dir) {
|
|
131
|
+
let entries;
|
|
132
|
+
try {
|
|
133
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
if (family !== 'commands') {
|
|
139
|
+
// A name is only CARRIED when the artefact behind it resolves. Corrected
|
|
140
|
+
// 2026-09-07 after a second neutral review: this returned every dirent
|
|
141
|
+
// name, so an empty `~/.claude/skills/<name>/` — no `SKILL.md` — made
|
|
142
|
+
// `claudeLayerHolds` true, the project layer withheld its copy, and the
|
|
143
|
+
// artefact was then delivered from NEITHER layer. That is the
|
|
144
|
+
// removal-with-no-repair-path this module's own limits section says it
|
|
145
|
+
// avoids, and the first fixture written for the wrapper gate created
|
|
146
|
+
// exactly that state and called the withhold correct.
|
|
147
|
+
//
|
|
148
|
+
// Reachable without a crash: a skill directory holding some other file
|
|
149
|
+
// survives `reap_stale`, and `install.ts` mkdirs before it writes.
|
|
150
|
+
// `readProjectedCatalogue` already keys on `SKILL.md` for this reason;
|
|
151
|
+
// the `commands` branch below already required a real `.md`. This is the
|
|
152
|
+
// same requirement for the two families that lacked it.
|
|
153
|
+
const carries = (name) => {
|
|
154
|
+
const abs = path.join(dir, name);
|
|
155
|
+
try {
|
|
156
|
+
if (fs.statSync(abs).isDirectory()) {
|
|
157
|
+
return fs.statSync(path.join(abs, 'SKILL.md')).isFile();
|
|
158
|
+
}
|
|
159
|
+
return name.endsWith('.md');
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
return new Set(entries
|
|
166
|
+
.map((e) => e.name)
|
|
167
|
+
.filter((n) => n !== 'README.md')
|
|
168
|
+
.filter(carries));
|
|
169
|
+
}
|
|
170
|
+
const out = new Set();
|
|
171
|
+
const walk = (abs, rel) => {
|
|
172
|
+
let kids;
|
|
173
|
+
try {
|
|
174
|
+
kids = fs.readdirSync(abs, { withFileTypes: true });
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
for (const k of kids) {
|
|
180
|
+
const childRel = rel === '' ? k.name : `${rel}/${k.name}`;
|
|
181
|
+
if (k.isDirectory()) {
|
|
182
|
+
walk(path.join(abs, k.name), childRel);
|
|
183
|
+
}
|
|
184
|
+
else if (k.name.endsWith('.md') && k.name !== 'README.md') {
|
|
185
|
+
out.add(childRel);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
walk(dir, '');
|
|
190
|
+
return out;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Does the host layer hold this one name?
|
|
194
|
+
*
|
|
195
|
+
* The single-name form of {@link keepInProjectLayer}, for a caller that decides
|
|
196
|
+
* inside a loop and already knows the host-side name. `false` on an unreadable
|
|
197
|
+
* layer — no evidence, so nothing is withheld.
|
|
198
|
+
*/
|
|
199
|
+
export function claudeLayerHolds(family, name, home = _defaultHome()) {
|
|
200
|
+
return claudeLayerNames(family, home)?.has(name) ?? false;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Which of `names` the host layer carries, and which it does not.
|
|
204
|
+
*
|
|
205
|
+
* `carries` is the all-or-nothing answer the rules path reports; `missing` is the
|
|
206
|
+
* one callers act on, because withholding is decided per name. An empty `names`
|
|
207
|
+
* is vacuously carried — withholding nothing needs no evidence.
|
|
208
|
+
*/
|
|
209
|
+
export function claudeLayerCarries(family, names, home = _defaultHome()) {
|
|
210
|
+
const layerPath = claudeLayerPath(family, home);
|
|
211
|
+
const have = claudeLayerNames(family, home);
|
|
212
|
+
if (have === null) {
|
|
213
|
+
return { carries: false, layerPath, missing: [...names], reason: 'layer-absent' };
|
|
214
|
+
}
|
|
215
|
+
const missing = names.filter((n) => !have.has(n));
|
|
216
|
+
return {
|
|
217
|
+
carries: missing.length === 0,
|
|
218
|
+
layerPath,
|
|
219
|
+
missing,
|
|
220
|
+
reason: missing.length === 0 ? 'carries' : 'missing-names',
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* The subset of `names` the project layer must still deliver.
|
|
225
|
+
*
|
|
226
|
+
* The one function the generators call. Returns the names the host layer does
|
|
227
|
+
* NOT hold, in the order given — so an unreadable layer returns everything
|
|
228
|
+
* (fail safe) and a complete layer returns nothing.
|
|
229
|
+
*/
|
|
230
|
+
export function keepInProjectLayer(family, names, home = _defaultHome()) {
|
|
231
|
+
const have = claudeLayerNames(family, home);
|
|
232
|
+
if (have === null) {
|
|
233
|
+
return [...names];
|
|
234
|
+
}
|
|
235
|
+
return names.filter((n) => !have.has(n));
|
|
236
|
+
}
|
|
237
|
+
//# sourceMappingURL=claudeLayerCarriage.js.map
|
|
@@ -49,6 +49,85 @@ export function derive_trigger_globs(meta) {
|
|
|
49
49
|
// De-dup, keep declaration order (deterministic output).
|
|
50
50
|
return [...new Set(globs)];
|
|
51
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* The ceiling for a description a native lazy form routes on.
|
|
54
|
+
*
|
|
55
|
+
* NOT a host-published limit, and saying so is the point: nothing in this tree
|
|
56
|
+
* records what Cursor or Windsurf actually truncate at, so a number claiming to
|
|
57
|
+
* be theirs would be invented. This is a PACKAGE cap, chosen against the corpus
|
|
58
|
+
* it applies to — the 119 projected rule descriptions measure max 187 and mean
|
|
59
|
+
* 115 characters, so 400 leaves every existing description intact and gives the
|
|
60
|
+
* appended trigger terms roughly twice the mean to work in.
|
|
61
|
+
*
|
|
62
|
+
* Lower it if a host publishes a real limit below 400, or if a rule's emitted
|
|
63
|
+
* description is ever observed truncated by one.
|
|
64
|
+
*/
|
|
65
|
+
export const APPLIES_WHEN_CAP = 400;
|
|
66
|
+
/** How the appended clause opens. Exported so a detector cannot re-spell it. */
|
|
67
|
+
export const APPLIES_WHEN_PREFIX = ' Applies when: ';
|
|
68
|
+
/**
|
|
69
|
+
* Keyword and phrase trigger terms, in declaration order, de-duplicated.
|
|
70
|
+
*
|
|
71
|
+
* Path-shaped triggers are deliberately excluded: they already activate the
|
|
72
|
+
* host natively through `globs`, so repeating them in prose spends description
|
|
73
|
+
* budget to say something the host is already acting on.
|
|
74
|
+
*/
|
|
75
|
+
export function trigger_terms(meta) {
|
|
76
|
+
const triggers = meta['triggers'];
|
|
77
|
+
if (!Array.isArray(triggers))
|
|
78
|
+
return [];
|
|
79
|
+
const out = [];
|
|
80
|
+
for (const t of triggers) {
|
|
81
|
+
if (t === null || typeof t !== 'object' || Array.isArray(t))
|
|
82
|
+
continue;
|
|
83
|
+
const obj = t;
|
|
84
|
+
for (const key of ['keyword', 'phrase']) {
|
|
85
|
+
const v = obj[key];
|
|
86
|
+
if (typeof v === 'string' && v.trim() !== '')
|
|
87
|
+
out.push(v.trim());
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return [...new Set(out)];
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Lower a rule's keyword and phrase triggers into the description a native lazy
|
|
94
|
+
* form routes on (road-to-delivery-on-hook-hosts 3.1).
|
|
95
|
+
*
|
|
96
|
+
* Cursor and Windsurf have no delivery mechanism: an `auto` rule with no
|
|
97
|
+
* path-shaped trigger reaches them as `alwaysApply: false` / `model_decision`
|
|
98
|
+
* with nothing but the description to route on. The rule's own trigger terms
|
|
99
|
+
* are the most routing-relevant text that exists for it, and they were going
|
|
100
|
+
* nowhere.
|
|
101
|
+
*
|
|
102
|
+
* Three properties, each of which is a way this could go wrong:
|
|
103
|
+
*
|
|
104
|
+
* · **Never truncates the original description.** Terms are appended and the
|
|
105
|
+
* clause is dropped WHOLE when it will not fit, so a description can only
|
|
106
|
+
* gain. A cap that ate the sentence would trade routing for readability
|
|
107
|
+
* without saying so.
|
|
108
|
+
* · **Whole terms only.** Terms are added one at a time while they fit; a
|
|
109
|
+
* half term is never emitted, because a truncated phrase routes on nothing
|
|
110
|
+
* and reads as a typo.
|
|
111
|
+
* · **Idempotent.** A description that already carries the clause is returned
|
|
112
|
+
* unchanged, so a re-run of the emitter cannot stack clauses.
|
|
113
|
+
*/
|
|
114
|
+
export function applies_when(description, meta, cap = APPLIES_WHEN_CAP) {
|
|
115
|
+
if (description.includes(APPLIES_WHEN_PREFIX.trim()))
|
|
116
|
+
return description;
|
|
117
|
+
const terms = trigger_terms(meta);
|
|
118
|
+
if (terms.length === 0)
|
|
119
|
+
return description;
|
|
120
|
+
const kept = [];
|
|
121
|
+
for (const term of terms) {
|
|
122
|
+
const candidate = `${description}${APPLIES_WHEN_PREFIX}${[...kept, term].join(', ')}.`;
|
|
123
|
+
if (candidate.length > cap)
|
|
124
|
+
break;
|
|
125
|
+
kept.push(term);
|
|
126
|
+
}
|
|
127
|
+
if (kept.length === 0)
|
|
128
|
+
return description;
|
|
129
|
+
return `${description}${APPLIES_WHEN_PREFIX}${kept.join(', ')}.`;
|
|
130
|
+
}
|
|
52
131
|
/**
|
|
53
132
|
* A brace group the host expands, versus an agent-config placeholder that only
|
|
54
133
|
* LOOKS like one.
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* ## Why a fingerprint and not a version number
|
|
5
5
|
*
|
|
6
6
|
* The single-delivery partition is a *removal*: after it, `task generate-tools`
|
|
7
|
-
* writes
|
|
7
|
+
* writes 13 rules and zero skills (15 package-only, 2 byte-identically deduped at user scope) plus 49 flat-command wrappers into `<repo>/.claude/` and every other
|
|
8
8
|
* artefact is delivered ONLY by the host-global layer. That removes the build's
|
|
9
9
|
* own repair path — regeneration can no longer heal a stale global layer,
|
|
10
10
|
* because it stops writing the affected files at all.
|
package/dist/install/install.mjs
CHANGED
|
@@ -9720,6 +9720,12 @@ function format_drift_report(entries) {
|
|
|
9720
9720
|
// src/scripts/_lib/skill_catalogue.ts
|
|
9721
9721
|
import * as fs11 from "node:fs";
|
|
9722
9722
|
import * as path10 from "node:path";
|
|
9723
|
+
var PROJECT_CATALOGUE_ROOTS = ["src/skills", ".claude/skills"];
|
|
9724
|
+
var HOST_CATALOGUE_ROOT = ".claude/skills";
|
|
9725
|
+
var DEFAULT_CATALOGUE_ROOTS = [
|
|
9726
|
+
...PROJECT_CATALOGUE_ROOTS,
|
|
9727
|
+
`~/${HOST_CATALOGUE_ROOT}`
|
|
9728
|
+
];
|
|
9723
9729
|
var OBSERVATION_LOG = path10.join(
|
|
9724
9730
|
"agents",
|
|
9725
9731
|
"evidence",
|