@event4u/agent-config 14.21.0 → 14.23.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 +164 -187
- package/CREDITS.md +37 -0
- package/README.md +1 -1
- package/dist/agent-src/commands/analyze/inbox.md +380 -5
- package/dist/agent-src/commands/analyze/repo.md +4 -2
- package/dist/agent-src/commands/pr/merge.md +47 -38
- package/dist/agent-src/commands/roadmap/next.md +8 -0
- 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/contexts/execution/autonomy-mechanics.md +12 -0
- package/dist/agent-src/rules/external-code-graph-interop.md +19 -14
- package/dist/agent-src/rules/notes-first-reasoning.md +36 -25
- 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 +120 -27
- 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 +82 -1
- package/dist/agent-src/skills/git-workflow/references/push-closes-its-loop.md +9 -0
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +17 -0
- package/dist/agent-src/skills/tailwind-engineer/SKILL.md +52 -2
- 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/registry.js +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 +542 -0
- package/dist/cli-delegate/check_update_banner.js +923 -0
- package/dist/cli-delegate/{chunk-ZVBOHSVF.js → chunk-2KKXJW25.js} +23 -9
- package/dist/cli-delegate/chunk-5EK5WQN6.js +28 -0
- package/dist/cli-delegate/chunk-7XGD2OEU.js +96 -0
- package/dist/cli-delegate/chunk-DBH4AHDK.js +155 -0
- package/dist/cli-delegate/{chunk-4FS4WRXP.js → chunk-IAI3XL43.js} +15 -3
- package/dist/cli-delegate/chunk-JO25G5H7.js +20 -0
- package/dist/cli-delegate/{chunk-643Z7FIC.js → chunk-KCNTUSPI.js} +7 -1
- package/dist/cli-delegate/chunk-LTYVIY76.js +7961 -0
- package/dist/cli-delegate/chunk-QAJS5YWY.js +999 -0
- 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 +2 -2
- package/dist/cli-delegate/cmd_handoff.js +45 -13
- package/dist/cli-delegate/cmd_prune.js +2 -2
- package/dist/cli-delegate/cmd_session_recycle.js +4 -14
- package/dist/cli-delegate/cmd_settings_migrate.js +2 -2
- package/dist/cli-delegate/cmd_settings_set.js +1 -1
- package/dist/cli-delegate/cmd_sync.js +2 -2
- package/dist/cli-delegate/cmd_uninstall.js +2 -2
- package/dist/cli-delegate/cmd_validate.js +2 -2
- 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 +13 -13
- 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 +1813 -946
- 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 +21 -3
- 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 +10870 -7641
- package/dist/scripts/_lib/lean_projection_mode.js +108 -0
- package/dist/scripts/_lib/router_match.js +243 -0
- package/dist/scripts/_lib/rule_injection.js +216 -0
- package/dist/scripts/condense.js +52 -45
- package/dist/scripts/project_thin_rules.js +139 -19
- package/dist/server/schemas/settings.js +16 -3
- package/docs/MIGRATION.md +105 -1
- package/docs/contracts/settings-classes.md +40 -8
- package/docs/guidelines/agent-infra/minimal-safe-diff-mechanics.md +73 -0
- package/docs/guidelines/agent-infra/notes-horizon-mechanics.md +90 -0
- package/llms.txt +1 -1
- package/package.json +7 -4
- package/src/agent-src/scripts/_cli_entry.ts +51 -0
- package/src/agent-src/scripts/archive_completed_roadmaps.ts +120 -27
- 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/agent-settings.template.yml +47 -1
- package/src/config/continuity-surface.json +516 -0
- package/src/config/estate-count-budget.json +1 -1
- package/src/config/evaluator-budgets.json +4 -4
- 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/hook-token-budget.json +7 -6
- 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/config/routing-coverage-seed.json +9 -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/_cli/cmd_session_recycle.ts +2 -25
- package/src/scripts/_cli/handoff_sessions.ts +14 -44
- 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_slot.ts +295 -0
- package/src/scripts/_lib/continuity_surface.ts +296 -0
- package/src/scripts/_lib/continuity_writer.ts +253 -0
- package/src/scripts/_lib/corpus_manifest.ts +26 -9
- package/src/scripts/_lib/hook_settings.ts +86 -1
- package/src/scripts/_lib/injection_effect.ts +151 -0
- package/src/scripts/_lib/layer_overlap_notice.ts +50 -6
- package/src/scripts/_lib/lean_projection_mode.ts +127 -0
- package/src/scripts/_lib/packed_binary_predicate.ts +262 -0
- package/src/scripts/_lib/pin_resolver.ts +13 -0
- package/src/scripts/_lib/recycle_envelope_paths.ts +109 -21
- package/src/scripts/_lib/regression_neighbourhood.ts +159 -22
- package/src/scripts/_lib/retired_status.ts +74 -0
- package/src/scripts/_lib/session_eol.ts +44 -0
- package/src/scripts/_lib/session_index_trust.ts +346 -0
- package/src/scripts/_lib/skill_catalogue.ts +156 -21
- package/src/scripts/_lib/standing_bound_ratchet.ts +154 -0
- package/src/scripts/_lib/subagent_capsule.ts +117 -8
- package/src/scripts/_lib/vendored_grammar_upstream.ts +280 -0
- 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/audit_mcp_tools.ts +69 -25
- package/src/scripts/build_mcp_catalog.ts +24 -1
- 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_host_tree_parity.ts +291 -0
- package/src/scripts/check_pack_size.ts +262 -23
- package/src/scripts/check_preamble_payload_budget.ts +136 -7
- package/src/scripts/check_roadmap_trackable.ts +19 -16
- package/src/scripts/check_rule_layer_partition.ts +54 -34
- package/src/scripts/check_rule_projection_integrity.ts +102 -2
- package/src/scripts/check_single_delivery.ts +97 -31
- package/src/scripts/check_update_banner.ts +13 -0
- package/src/scripts/ci_settle.ts +205 -5
- package/src/scripts/code_graph/build.ts +217 -15
- package/src/scripts/code_graph/cli.ts +334 -15
- package/src/scripts/code_graph/detect.ts +38 -0
- 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 +144 -11
- package/src/scripts/code_graph/resolution_tiers.ts +238 -0
- package/src/scripts/code_graph/sqlite_store.ts +262 -6
- package/src/scripts/code_graph/types.ts +117 -3
- package/src/scripts/code_graph/validate.ts +26 -1
- package/src/scripts/code_graph/verbs.ts +590 -0
- package/src/scripts/condense.ts +54 -42
- 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 +118 -1
- package/src/scripts/hook_effect_doctor.ts +82 -1
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +87 -12
- package/src/scripts/hooks/block_speaking_inbox_dir.ts +182 -16
- package/src/scripts/hooks/chain_nudge_hook.ts +282 -0
- package/src/scripts/hooks/code_graph_context_hook.ts +201 -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/push_settle_hook.ts +3 -2
- package/src/scripts/hooks/rule_inject_hook.ts +108 -21
- package/src/scripts/hooks/session_eol_hook.ts +120 -2
- package/src/scripts/hooks/skill_route_hook.ts +11 -4
- package/src/scripts/hot_context_hook.ts +62 -7
- 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/lint_roadmap_blockers.ts +164 -1
- package/src/scripts/mcp_server/consumer_tool_catalog.json +152 -2
- package/src/scripts/mcp_server/graph_tools.ts +294 -0
- package/src/scripts/mcp_server/path_util.ts +31 -0
- package/src/scripts/mcp_server/suggest_skill.ts +72 -0
- package/src/scripts/mcp_server/tool_catalog_source.ts +7 -1
- package/src/scripts/mcp_server/tools.ts +5 -69
- 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/model_rule_injection.ts +61 -4
- package/src/scripts/project_thin_rules.ts +149 -16
- 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/report_standing_payload_by_host.ts +650 -0
- package/src/scripts/schemas/agent-settings.schema.json +7 -1
- package/src/scripts/session_memory_index.ts +81 -8
- 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/server/schemas/settings.ts +23 -4
- package/src/vendor/grammars/README.md +86 -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
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* ## Why the decision moved from per-run to per-directory
|
|
6
6
|
*
|
|
7
7
|
* The filter used to sit in `condense.ts::_scoped_rule_basenames()`, which runs
|
|
8
|
-
* once per generation, so `partitionActive` — a claude-only fingerprint against
|
|
8
|
+
* once per generation, so the then-current `partitionActive` — a claude-only fingerprint against
|
|
9
9
|
* `installed.lock` — decided for every host at once. That contradicted
|
|
10
10
|
* `partitionEligibility.ts`'s own fail-safe reasoning, which refuses to withhold a
|
|
11
11
|
* cursor artefact on claude's evidence because it "would deliver it nowhere", and
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
* Four call sites consume it — the symlink emit plan, the cursor `.mdc` emitter,
|
|
33
33
|
* the windsurf emitter, the augment projector — and four sites deciding this
|
|
34
34
|
* separately is exactly how three of them came to disagree. It is in `src/install/`
|
|
35
|
-
* beside {@link
|
|
35
|
+
* beside {@link hostLayerCarries}, the predicate
|
|
36
36
|
* it composes, rather than in the 2,700-line generator that calls it.
|
|
37
37
|
*
|
|
38
38
|
* ## The classification source is the PROJECTION, not the authored tree
|
|
@@ -48,7 +48,7 @@ import * as fs from 'node:fs';
|
|
|
48
48
|
import * as os from 'node:os';
|
|
49
49
|
import * as path from 'node:path';
|
|
50
50
|
|
|
51
|
-
import { isExclusivelyPackageOnly
|
|
51
|
+
import { isExclusivelyPackageOnly } from './partitionEligibility.js';
|
|
52
52
|
import { hostLayerCarries, toolIdForProjectRuleDir } from './globalRuleLayers.js';
|
|
53
53
|
|
|
54
54
|
export interface PartitionRulesOptions {
|
|
@@ -56,28 +56,21 @@ export interface PartitionRulesOptions {
|
|
|
56
56
|
readonly toolDir: string;
|
|
57
57
|
/** Candidate rule basenames, already scope- and manual-filtered. */
|
|
58
58
|
readonly rules: readonly string[];
|
|
59
|
-
/**
|
|
59
|
+
/**
|
|
60
|
+
* Absolute project root.
|
|
61
|
+
*
|
|
62
|
+
* Retained after the 2026-09-07 change removed the only reader that needed
|
|
63
|
+
* it (`partitionActive`, which verified it against `installed.lock`). It
|
|
64
|
+
* stays because every call site already passes it and because a rule
|
|
65
|
+
* partition that cannot name the tree it is partitioning is harder to debug,
|
|
66
|
+
* not because a branch below reads it. Stated rather than quietly dropped.
|
|
67
|
+
*/
|
|
60
68
|
readonly projectRoot: string;
|
|
61
69
|
/** Absolute directory the basenames resolve against for classification. */
|
|
62
70
|
readonly rulesSource: string;
|
|
63
71
|
readonly userHome?: string;
|
|
64
72
|
/** Where the kept-full notice goes. Silent when omitted. */
|
|
65
73
|
readonly announce?: (message: string) => void;
|
|
66
|
-
/**
|
|
67
|
-
* Override the partition-active decision instead of reading `installed.lock`.
|
|
68
|
-
*
|
|
69
|
-
* Split out for the reason `partitionEligibility.ts` states about its own
|
|
70
|
-
* `personaWithheldFor`: "so the decision is testable in BOTH directions". A
|
|
71
|
-
* test over the un-injected path can only assert whatever the machine happens
|
|
72
|
-
* to be — and this one bit differs between a maintainer checkout (installed,
|
|
73
|
-
* so active) and CI (nothing installed, so inactive), which made the first
|
|
74
|
-
* version of `rule_partition_per_host.test.ts` green locally and red on every
|
|
75
|
-
* CI shard. That is the environment-dependent gate this whole change exists to
|
|
76
|
-
* remove, reproduced in its own test suite.
|
|
77
|
-
*
|
|
78
|
-
* Production callers omit it.
|
|
79
|
-
*/
|
|
80
|
-
readonly active?: boolean;
|
|
81
74
|
}
|
|
82
75
|
|
|
83
76
|
/**
|
|
@@ -90,10 +83,13 @@ export interface PartitionRulesOptions {
|
|
|
90
83
|
*/
|
|
91
84
|
export function partitionRulesForDir(opts: PartitionRulesOptions): string[] {
|
|
92
85
|
const { toolDir, rules, projectRoot, rulesSource } = opts;
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
86
|
+
void projectRoot;
|
|
87
|
+
// There is no repo-wide gate above this any more (owner decision, 2026-09-07).
|
|
88
|
+
// `partitionActive` used to run first and veto every directory on one
|
|
89
|
+
// claude-only version-and-fingerprint check against `installed.lock` — the
|
|
90
|
+
// exact borrowed-evidence extrapolation this module's own header refuses. The
|
|
91
|
+
// per-directory evidence below was always the stronger test; it is now the
|
|
92
|
+
// only one.
|
|
97
93
|
const toolId = toolIdForProjectRuleDir(toolDir);
|
|
98
94
|
if (toolId === null) {
|
|
99
95
|
// An unmapped directory has no global layer this code can point at, so
|
|
@@ -33,8 +33,8 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
|
33
33
|
import { ORIGIN_CWD_FALLBACK, resolve_project_root } from '../_lib/agent_settings.js';
|
|
34
34
|
import {
|
|
35
35
|
RECYCLE_ENVELOPE_MAX_BYTES,
|
|
36
|
-
recycle_consumed_rel,
|
|
37
36
|
recycle_envelope_rel,
|
|
37
|
+
resolvePredecessor,
|
|
38
38
|
} from '../_lib/recycle_envelope_paths.js';
|
|
39
39
|
import {
|
|
40
40
|
CAPSULE_SCHEMA_VERSION,
|
|
@@ -84,30 +84,7 @@ export function templateEnvelope(): Record<string, unknown> {
|
|
|
84
84
|
};
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
* Which session this one continues — read, never composed.
|
|
89
|
-
*
|
|
90
|
-
* The chain is derivable without any new artifact: when this session started,
|
|
91
|
-
* its reader consumed the predecessor's record and MOVED it to this session's
|
|
92
|
-
* consumed path (`recycle_consumed_rel`). So the file sitting there is, by
|
|
93
|
-
* construction, the record this session resumed from, and its `session_id` is
|
|
94
|
-
* the predecessor.
|
|
95
|
-
*
|
|
96
|
-
* Returns the explicit string `none` when there is nothing there. `none` is a
|
|
97
|
-
* CLAIM — "this session starts a chain" — and is deliberately not an empty
|
|
98
|
-
* value: a reader must be able to tell "no predecessor" from "nobody wrote the
|
|
99
|
-
* field", because only the second one is a reason to go looking.
|
|
100
|
-
*/
|
|
101
|
-
export function resolvePredecessor(projectRoot: string, sessionId: string | null): string {
|
|
102
|
-
try {
|
|
103
|
-
const consumed = path.join(projectRoot, recycle_consumed_rel(sessionId));
|
|
104
|
-
const raw = JSON.parse(fs.readFileSync(consumed, 'utf-8')) as { session_id?: unknown };
|
|
105
|
-
const prior = String(raw.session_id ?? '').trim();
|
|
106
|
-
return prior === '' ? 'none' : prior;
|
|
107
|
-
} catch {
|
|
108
|
-
return 'none';
|
|
109
|
-
}
|
|
110
|
-
}
|
|
87
|
+
export { resolvePredecessor };
|
|
111
88
|
|
|
112
89
|
interface ParsedArgv {
|
|
113
90
|
ok: boolean;
|
|
@@ -32,7 +32,13 @@ import * as path from 'node:path';
|
|
|
32
32
|
|
|
33
33
|
import { projectStoreSlug } from '../_lib/cc_transcript.js';
|
|
34
34
|
import { derive_session_tag, list_sessions, read_entries } from '../chat_history.js';
|
|
35
|
-
import {
|
|
35
|
+
import {
|
|
36
|
+
eolSessionKey,
|
|
37
|
+
is_substantive,
|
|
38
|
+
readEolCounters,
|
|
39
|
+
type StoredEolCounters,
|
|
40
|
+
SUBSTANTIVE_TOKEN_FLOOR,
|
|
41
|
+
} from '../_lib/session_eol.js';
|
|
36
42
|
|
|
37
43
|
export const DEFAULT_CAP = 15;
|
|
38
44
|
/**
|
|
@@ -42,24 +48,16 @@ export const DEFAULT_CAP = 15;
|
|
|
42
48
|
* on the observed store 10 of 217 sessions are filtered (~5%), so 3× covers
|
|
43
49
|
* that comfortably while keeping the per-file scan bounded.
|
|
44
50
|
*/
|
|
51
|
+
/**
|
|
52
|
+
* Re-exported from `_lib/session_eol.ts`, where both now live so the Stop-path
|
|
53
|
+
* hook can reach them without importing this CLI module. Kept exported HERE
|
|
54
|
+
* because the resume picker is the surface every existing importer names.
|
|
55
|
+
*/
|
|
56
|
+
export { is_substantive, SUBSTANTIVE_TOKEN_FLOOR };
|
|
57
|
+
|
|
45
58
|
const FETCH_OVERSHOOT = 3;
|
|
46
59
|
const SUMMARY_SNIPPET_CHARS = 120;
|
|
47
60
|
|
|
48
|
-
/**
|
|
49
|
-
* Committed substantive-content floor, in parsed transcript tokens (billable
|
|
50
|
-
* input of the last main-chain assistant record, per `cc_transcript.ts`) — the
|
|
51
|
-
* OR-arm for a session that produced real discussion without ever calling a
|
|
52
|
-
* tool.
|
|
53
|
-
*
|
|
54
|
-
* Derivation (`agents/evidence/analysis/handoff-substantive-threshold.md`,
|
|
55
|
-
* 217 sessions of the local Claude store, 2026-08-10): 206 of the 207 sessions
|
|
56
|
-
* carrying an assistant record also carry a `tool_use` block, so this arm
|
|
57
|
-
* serves the ~0.5 % tail and the hosts whose transcripts log no tool blocks at
|
|
58
|
-
* all. 10 000 sits **25× below** the p10 of real working sessions (254 939),
|
|
59
|
-
* so it cannot hide one, and above a single trivial exchange. Changing it is a
|
|
60
|
-
* PR citing evidence, never a drive-by edit.
|
|
61
|
-
*/
|
|
62
|
-
export const SUBSTANTIVE_TOKEN_FLOOR = 10_000;
|
|
63
61
|
|
|
64
62
|
export type HandoffSource = 'chat-history' | 'claude-transcript' | 'codex-session';
|
|
65
63
|
|
|
@@ -454,34 +452,6 @@ function _sort_key(s: HandoffSession): string {
|
|
|
454
452
|
return s.endedAt ?? s.startedAt ?? '';
|
|
455
453
|
}
|
|
456
454
|
|
|
457
|
-
/**
|
|
458
|
-
* Does this session hold anything worth resuming?
|
|
459
|
-
*
|
|
460
|
-
* `≥ 1 assistant turn AND (≥ 1 tool call OR parsed tokens ≥ the committed
|
|
461
|
-
* floor)`, read from the counts-only session-eol state.
|
|
462
|
-
*
|
|
463
|
-
* **Fail-open, deliberately** (Phase 1.2): absent, unreadable or mis-shaped
|
|
464
|
-
* state LISTS the candidate rather than filtering it, and a `tool_calls` key
|
|
465
|
-
* missing from a state file written before that counter existed reads as
|
|
466
|
-
* *unknown*, never as zero. A wrongly listed candidate is noise the user
|
|
467
|
-
* scrolls past; a wrongly hidden one is data loss they cannot even see.
|
|
468
|
-
*/
|
|
469
|
-
export function is_substantive(counters: StoredEolCounters | null): boolean {
|
|
470
|
-
if (counters === null) return true;
|
|
471
|
-
// Every "unknown" below is a NUMBER test, not an `=== undefined` test.
|
|
472
|
-
// Absent, null and NaN are all unknown, and each one reaches this code by
|
|
473
|
-
// a real path: a state file written before the counter existed is absent,
|
|
474
|
-
// `JSON.stringify` turns NaN into null on the way to disk, and a
|
|
475
|
-
// half-written file yields a parseable object with missing keys. Reading
|
|
476
|
-
// any of them as "counted zero" hides a session that did real work.
|
|
477
|
-
if (!Number.isFinite(counters.assistant_records)) return true;
|
|
478
|
-
if ((counters.assistant_records as number) < 1) return false;
|
|
479
|
-
if (!Number.isFinite(counters.tool_calls)) return true;
|
|
480
|
-
if ((counters.tool_calls as number) >= 1) return true;
|
|
481
|
-
if (!Number.isFinite(counters.final_context_tokens)) return true;
|
|
482
|
-
return (counters.final_context_tokens as number) >= SUBSTANTIVE_TOKEN_FLOOR;
|
|
483
|
-
}
|
|
484
|
-
|
|
485
455
|
function _derive_tag(id: string): string | null {
|
|
486
456
|
try {
|
|
487
457
|
return derive_session_tag(id);
|
|
@@ -463,8 +463,9 @@ print_version() {
|
|
|
463
463
|
# dependency since the road-to-credible-install Phase 1 flip — the consumer
|
|
464
464
|
# runtime tree ships no tsx, so every consumer-reachable surface must resolve
|
|
465
465
|
# a precompiled bundle instead (hooks → dist/hooks/dispatch.js, mcp:run →
|
|
466
|
-
# dist/mcp/server.mjs, the `_cli`
|
|
467
|
-
# see exec_ts below). This resolver therefore
|
|
466
|
+
# dist/mcp/server.mjs, the `_cli` AND `dist/agent-src/scripts/` delegate
|
|
467
|
+
# commands → dist/cli-delegate/, see exec_ts below). This resolver therefore
|
|
468
|
+
# only ever fires in a DEV tree
|
|
468
469
|
# (node_modules/.bin/tsx present) or as a genuine last-resort fallback.
|
|
469
470
|
# `npx tsx` is that last resort: npx runs against the CONSUMER project's cwd,
|
|
470
471
|
# so its npm config and devEngines/engines constraints apply — a consumer
|
|
@@ -495,14 +496,23 @@ require_tsx() {
|
|
|
495
496
|
exit 127
|
|
496
497
|
}
|
|
497
498
|
|
|
498
|
-
# Map a
|
|
499
|
-
#
|
|
500
|
-
#
|
|
499
|
+
# Map a delegate source path to its precompiled bundle entry under
|
|
500
|
+
# `dist/cli-delegate/<name>.js`, echoing the bundle path when it exists and node
|
|
501
|
+
# is available. Empty output means "no bundle — use the tsx path".
|
|
501
502
|
#
|
|
502
|
-
#
|
|
503
|
-
#
|
|
504
|
-
#
|
|
505
|
-
#
|
|
503
|
+
# TWO source roots are mapped, both bundled into that one outdir:
|
|
504
|
+
# - `src/scripts/_cli/` — the consumer-tier delegate commands (`sync`,
|
|
505
|
+
# `doctor`, `validate`, `upgrade`, …), per ADR-204.
|
|
506
|
+
# - `dist/agent-src/scripts/` — the roadmap command family (`roadmap:progress`,
|
|
507
|
+
# `roadmap:set-step`, `roadmap:archive`, `roadmap:gates`, `stubs:due`).
|
|
508
|
+
# ADR-204 closed the `npx tsx` exposure for `_cli/` and left this root on it:
|
|
509
|
+
# these commands still reached `require_tsx`, and `npx` resolves against the
|
|
510
|
+
# CONSUMER's cwd, so a project pinning `devEngines.runtime` away from the
|
|
511
|
+
# local Node hard-failed with EBADDEVENGINES.
|
|
512
|
+
#
|
|
513
|
+
# Basenames are unique ACROSS both roots (`_cli/` is all `cmd_*`, agent-src is
|
|
514
|
+
# none), so one flat outdir stays collision-free. Other exec_ts callers (the
|
|
515
|
+
# remaining resolve_script results, the work engine, …) are unaffected.
|
|
506
516
|
#
|
|
507
517
|
# In a DEV tree the bundle is also suppressed when any `src/**/*.ts` is newer
|
|
508
518
|
# than it — see cli_delegate_bundle_is_stale.
|
|
@@ -510,6 +520,7 @@ cli_delegate_bundle() {
|
|
|
510
520
|
local ts_abs="$1"
|
|
511
521
|
case "$ts_abs" in
|
|
512
522
|
"$PACKAGE_ROOT/src/scripts/_cli/"*) ;;
|
|
523
|
+
"$PACKAGE_ROOT/dist/agent-src/scripts/"*) ;;
|
|
513
524
|
*) return 0 ;;
|
|
514
525
|
esac
|
|
515
526
|
local name
|
|
@@ -540,12 +551,40 @@ cli_delegate_bundle() {
|
|
|
540
551
|
# edit is never silently ignored in favour of a stale compile.
|
|
541
552
|
cli_delegate_bundle_is_stale() {
|
|
542
553
|
local bundle="$1"
|
|
554
|
+
# The dev-tree test is package-local tsx, NOT `-d src`. The published tarball
|
|
555
|
+
# SHIPS `src/` (files[] carries `src/scripts/` and `src/agent-src/scripts/`),
|
|
556
|
+
# so the `-d src` test called every consumer install a dev tree — and npm's
|
|
557
|
+
# extraction leaves shipped sources newer than the prepack-built bundle.
|
|
558
|
+
# Measured 2026-09-07 on a global 14.21.0 install: 1348 shipped `src/**/*.ts`
|
|
559
|
+
# were newer than `dist/cli-delegate/cmd_versions.js`, so `cli_delegate_bundle`
|
|
560
|
+
# returned empty and `agent-config versions` fell through to `npx tsx` —
|
|
561
|
+
# ADR-204's fast path was inert in every published install, which is also why
|
|
562
|
+
# it never protected the roadmap family this change adds.
|
|
563
|
+
# Without a local tsx the only alternative to a "stale" bundle is `npx tsx`
|
|
564
|
+
# against the CONSUMER's npm config, which is strictly worse than a compile
|
|
565
|
+
# that may lag a source edit no consumer can make.
|
|
566
|
+
[[ -x "$PACKAGE_ROOT/node_modules/.bin/tsx" ]] || return 1
|
|
543
567
|
[[ -d "$PACKAGE_ROOT/src" ]] || return 1
|
|
544
568
|
local newer
|
|
545
569
|
newer="$(find "$PACKAGE_ROOT/src" -name '*.ts' -newer "$bundle" -print -quit 2>/dev/null)"
|
|
546
570
|
[[ -n "$newer" ]]
|
|
547
571
|
}
|
|
548
572
|
|
|
573
|
+
# Echo a usable `dist/cli-delegate/` bundle for a path RELATIVE to that dir, or
|
|
574
|
+
# nothing. Used by the two probes that run on EVERY invocation — the pin
|
|
575
|
+
# resolver and the update banner — which have their own soft tsx lookup rather
|
|
576
|
+
# than going through exec_ts. Both reached `npx tsx` in a consumer install and
|
|
577
|
+
# therefore printed the consumer's own npm failure (EBADDEVENGINES on a
|
|
578
|
+
# mismatched `devEngines`) on stderr before every single command.
|
|
579
|
+
delegate_aux_bundle() {
|
|
580
|
+
local rel="$1"
|
|
581
|
+
local bundle="$PACKAGE_ROOT/dist/cli-delegate/$rel"
|
|
582
|
+
[[ -f "$bundle" ]] || return 0
|
|
583
|
+
command -v node >/dev/null 2>&1 || return 0
|
|
584
|
+
cli_delegate_bundle_is_stale "$bundle" && return 0
|
|
585
|
+
printf '%s' "$bundle"
|
|
586
|
+
}
|
|
587
|
+
|
|
549
588
|
# Run an absolute script path. The argument is an absolute path that may carry
|
|
550
589
|
# a `.py` (legacy) or `.ts` extension; we resolve the `.ts` twin.
|
|
551
590
|
#
|
|
@@ -1622,20 +1661,28 @@ maybe_pin_reexec() {
|
|
|
1622
1661
|
help|--help|-h|--version|-V|update|migrate|init|"") return 0 ;;
|
|
1623
1662
|
esac
|
|
1624
1663
|
# Best-effort: soft tsx probe (no hard-exit — this runs around dispatch).
|
|
1625
|
-
local
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1664
|
+
local runner=""
|
|
1665
|
+
local target=""
|
|
1666
|
+
local aux
|
|
1667
|
+
aux="$(delegate_aux_bundle '_lib/pin_resolver.js')"
|
|
1668
|
+
if [[ -n "$aux" ]]; then
|
|
1669
|
+
runner="node"
|
|
1670
|
+
target="$aux"
|
|
1671
|
+
elif [[ -x "$PACKAGE_ROOT/node_modules/.bin/tsx" ]]; then
|
|
1672
|
+
runner="$PACKAGE_ROOT/node_modules/.bin/tsx"
|
|
1673
|
+
target="$PACKAGE_ROOT/src/scripts/_lib/pin_resolver.ts"
|
|
1630
1674
|
else
|
|
1675
|
+
# Deliberately NO `npx tsx` last resort here: this probe runs before EVERY
|
|
1676
|
+
# command, and npx resolves against the CONSUMER's cwd, so a project whose
|
|
1677
|
+
# `devEngines` rejects the local Node made every invocation print npm's
|
|
1678
|
+
# error. A missing runner means no pin re-exec, which is the same
|
|
1679
|
+
# best-effort outcome the `|| true` below already accepts.
|
|
1631
1680
|
return 0
|
|
1632
1681
|
fi
|
|
1633
1682
|
local installed
|
|
1634
1683
|
installed="$(print_version)"
|
|
1635
1684
|
[[ -z "$installed" || "$installed" == "unknown" ]] && return 0
|
|
1636
|
-
|
|
1637
|
-
$tsx_bin "$PACKAGE_ROOT/src/scripts/_lib/pin_resolver.ts" \
|
|
1638
|
-
--cwd "$CONSUMER_ROOT" --installed "$installed" -- "$@" || true
|
|
1685
|
+
"$runner" "$target" --cwd "$CONSUMER_ROOT" --installed "$installed" -- "$@" || true
|
|
1639
1686
|
}
|
|
1640
1687
|
|
|
1641
1688
|
# Post-subcommand banner: best-effort daily update-check notice on
|
|
@@ -1649,19 +1696,22 @@ run_update_check_banner() {
|
|
|
1649
1696
|
case "$cmd" in
|
|
1650
1697
|
help|--help|-h|--version|-V|"") return 0 ;;
|
|
1651
1698
|
esac
|
|
1652
|
-
# Best-effort:
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1699
|
+
# Best-effort: bundle first, then a dev-tree tsx. No `npx tsx` last resort —
|
|
1700
|
+
# same reason as maybe_pin_reexec: npx runs against the CONSUMER's npm config.
|
|
1701
|
+
local runner=""
|
|
1702
|
+
local target=""
|
|
1703
|
+
local aux
|
|
1704
|
+
aux="$(delegate_aux_bundle 'check_update_banner.js')"
|
|
1705
|
+
if [[ -n "$aux" ]]; then
|
|
1706
|
+
runner="node"
|
|
1707
|
+
target="$aux"
|
|
1708
|
+
elif [[ -x "$PACKAGE_ROOT/node_modules/.bin/tsx" && -f "$PACKAGE_ROOT/src/scripts/check_update_banner.ts" ]]; then
|
|
1709
|
+
runner="$PACKAGE_ROOT/node_modules/.bin/tsx"
|
|
1710
|
+
target="$PACKAGE_ROOT/src/scripts/check_update_banner.ts"
|
|
1658
1711
|
else
|
|
1659
1712
|
return 0
|
|
1660
1713
|
fi
|
|
1661
|
-
|
|
1662
|
-
[[ -f "$banner_script" ]] || return 0
|
|
1663
|
-
# shellcheck disable=SC2086
|
|
1664
|
-
$tsx_bin "$banner_script" --cwd "$CONSUMER_ROOT" 2>/dev/null || true
|
|
1714
|
+
"$runner" "$target" --cwd "$CONSUMER_ROOT" 2>/dev/null || true
|
|
1665
1715
|
}
|
|
1666
1716
|
|
|
1667
1717
|
# Global `--root <path>` / `--root=<path>` parsing (Step 8 A3).
|
|
@@ -191,8 +191,8 @@ export function classifyLookup(
|
|
|
191
191
|
reason:
|
|
192
192
|
primitive === p.primitive
|
|
193
193
|
? `lookup-class ${p.cls} — deterministic primitive, no spawn`
|
|
194
|
-
: `lookup-class ${p.cls} — capped grep; the code-graph accelerant
|
|
195
|
-
'
|
|
194
|
+
: `lookup-class ${p.cls} — capped grep; the code-graph accelerant needs a ` +
|
|
195
|
+
'usable graph and the caller passed none',
|
|
196
196
|
};
|
|
197
197
|
}
|
|
198
198
|
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Do the two lists that name `~/.claude/<family>` agree?
|
|
3
|
+
*
|
|
4
|
+
* **The defect this exists to make visible.**
|
|
5
|
+
*
|
|
6
|
+
* Two independently authored constants name the SAME directories and, until
|
|
7
|
+
* 2026-09-07, referenced each other nowhere:
|
|
8
|
+
*
|
|
9
|
+
* - the DELIVERY side — `claudeLayerCarriage.CLAUDE_ARTEFACT_DIRS` and
|
|
10
|
+
* `hostLayerFingerprint.hostLayerInputs` — which decides what the project
|
|
11
|
+
* layer withholds because the host layer already carries it;
|
|
12
|
+
* - the READER side — `skill_catalogue.DEFAULT_CATALOGUE_ROOTS` — which
|
|
13
|
+
* decides which trees the skill ranker sees.
|
|
14
|
+
*
|
|
15
|
+
* `git grep` over the dependency sets of both returned two disjoint sets. So
|
|
16
|
+
* `~/.claude/skills` was a directory the delivery side treated as authoritative
|
|
17
|
+
* and the reader side did not read AT ALL — for as long as both existed, and
|
|
18
|
+
* with nothing anywhere that could notice.
|
|
19
|
+
*
|
|
20
|
+
* The cost was measured on 2026-09-07: the reader resolved ONE root and stopped,
|
|
21
|
+
* so `suggest_skill_for_task` and the `skill-route` concern ranked whichever
|
|
22
|
+
* single tree came first and reported it as if it were the catalogue. A consumer
|
|
23
|
+
* whose project carries its own skills while the shipped set sits in
|
|
24
|
+
* `~/.claude/skills` got an answer over half its catalogue, with no signal that
|
|
25
|
+
* it was half.
|
|
26
|
+
*
|
|
27
|
+
* **Why a comparison and not an instruction.**
|
|
28
|
+
*
|
|
29
|
+
* The reasoning failure behind it — repairing a mechanism's PARAMETER while
|
|
30
|
+
* inheriting its SHAPE — is model-carried and nothing can observe it. But the
|
|
31
|
+
* RESULT is two lists of strings, and two lists of strings can be compared. This
|
|
32
|
+
* module is that comparison, so the omission stops being a thing someone has to
|
|
33
|
+
* notice and becomes a thing that reds.
|
|
34
|
+
*
|
|
35
|
+
* **What it does NOT claim.**
|
|
36
|
+
*
|
|
37
|
+
* It compares the directories the two sides NAME. It does not establish that a
|
|
38
|
+
* reader ranks well, that a family needs a reader, or that the host actually
|
|
39
|
+
* loads any of it. A family with no reader is legal and common — it just has to
|
|
40
|
+
* SAY so, which is what {@link FAMILIES_WITHOUT_READER} is for.
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
import { CLAUDE_ARTEFACT_DIRS, type ClaudeArtefactFamily } from '../../install/claudeLayerCarriage.js';
|
|
44
|
+
import { hostLayerInputs } from '../../install/hostLayerFingerprint.js';
|
|
45
|
+
import {
|
|
46
|
+
DEFAULT_CATALOGUE_ROOTS,
|
|
47
|
+
HOST_CATALOGUE_ROOT,
|
|
48
|
+
PROJECT_CATALOGUE_ROOTS,
|
|
49
|
+
} from './skill_catalogue.js';
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Families the delivery side knows and NO reader resolves — each with the reason.
|
|
53
|
+
*
|
|
54
|
+
* An entry here is a declaration, not a suppression: a family absent from both
|
|
55
|
+
* this map and the reader coverage below is the omission this module reds on.
|
|
56
|
+
* Adding a reader later means deleting one line here.
|
|
57
|
+
*/
|
|
58
|
+
export const FAMILIES_WITHOUT_READER: Readonly<Partial<Record<ClaudeArtefactFamily, string>>> = {
|
|
59
|
+
personas: 'personas are selected by name from a catalog, never ranked over a tree',
|
|
60
|
+
commands: 'commands reach the host by path; no ranker reads the commands tree',
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/** Family → the project- and host-relative roots a reader must cover. */
|
|
64
|
+
const READER_COVERAGE: Readonly<Partial<Record<ClaudeArtefactFamily, () => string[]>>> = {
|
|
65
|
+
skills: () => [...PROJECT_CATALOGUE_ROOTS, `~/${HOST_CATALOGUE_ROOT}`],
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/** One disagreement between the two sides. */
|
|
69
|
+
export interface ParityFinding {
|
|
70
|
+
readonly family: string;
|
|
71
|
+
readonly reason: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Every disagreement, or an empty array.
|
|
76
|
+
*
|
|
77
|
+
* Total function, no I/O: it reads constants, so the verdict is the same on every
|
|
78
|
+
* machine — which is the point. The reader lists are injectable so the polarity
|
|
79
|
+
* of each branch is testable without editing a production constant.
|
|
80
|
+
*/
|
|
81
|
+
export function catalogueLayerParity(
|
|
82
|
+
deliveryDirs: Readonly<Record<string, string>> = CLAUDE_ARTEFACT_DIRS,
|
|
83
|
+
coverage: Readonly<Record<string, () => string[]>> = READER_COVERAGE as Readonly<
|
|
84
|
+
Record<string, () => string[]>
|
|
85
|
+
>,
|
|
86
|
+
withoutReader: Readonly<Record<string, string>> = FAMILIES_WITHOUT_READER as Readonly<
|
|
87
|
+
Record<string, string>
|
|
88
|
+
>,
|
|
89
|
+
): ParityFinding[] {
|
|
90
|
+
const findings: ParityFinding[] = [];
|
|
91
|
+
for (const [family, projectDir] of Object.entries(deliveryDirs)) {
|
|
92
|
+
const cover = coverage[family];
|
|
93
|
+
if (cover === undefined) {
|
|
94
|
+
if (withoutReader[family] === undefined) {
|
|
95
|
+
findings.push({
|
|
96
|
+
family,
|
|
97
|
+
reason:
|
|
98
|
+
`the delivery side treats \`${projectDir}\` as an artefact source, but no ` +
|
|
99
|
+
'reader covers it and no reason is declared in FAMILIES_WITHOUT_READER',
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const roots = cover();
|
|
105
|
+
// The PROJECT-relative directory the delivery side names must be a root.
|
|
106
|
+
if (!roots.includes(projectDir)) {
|
|
107
|
+
findings.push({
|
|
108
|
+
family,
|
|
109
|
+
reason: `the reader does not read \`${projectDir}\`, which the delivery side names`,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
// And so must the HOST-relative twin — the leg that was missing entirely.
|
|
113
|
+
const hostForm = `~/${projectDir.replace(/^\.claude\//u, '.claude/')}`;
|
|
114
|
+
if (!roots.includes(hostForm)) {
|
|
115
|
+
findings.push({
|
|
116
|
+
family,
|
|
117
|
+
reason: `the reader does not read \`${hostForm}\`, the host layer the partition withholds against`,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return findings;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* The three constants that must name the same `~/.claude/skills`.
|
|
126
|
+
*
|
|
127
|
+
* `hostLayerInputs` is the third author of that path — the fingerprint side —
|
|
128
|
+
* and it is included because a rename there would silently un-verify the layer
|
|
129
|
+
* the reader reads. Returns the resolved absolute paths for one home so a caller
|
|
130
|
+
* can compare them directly.
|
|
131
|
+
*/
|
|
132
|
+
export function hostSkillsDirTriple(home: string): {
|
|
133
|
+
readonly fingerprint: string | null;
|
|
134
|
+
readonly delivery: string;
|
|
135
|
+
readonly reader: string;
|
|
136
|
+
} {
|
|
137
|
+
const fp = hostLayerInputs(home).find((l) => l.label === 'skills');
|
|
138
|
+
return {
|
|
139
|
+
fingerprint: fp?.root ?? null,
|
|
140
|
+
delivery: `${home}/${CLAUDE_ARTEFACT_DIRS.skills}`,
|
|
141
|
+
reader: `${home}/${HOST_CATALOGUE_ROOT}`,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** For the test's own message, and to prove the list is read rather than assumed. */
|
|
146
|
+
export const READER_ROOT_LABELS: readonly string[] = [...DEFAULT_CATALOGUE_ROOTS];
|