@hasna/skills 0.10.49 → 0.10.50
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/README.md +32 -1
- package/bin/index.js +48 -20
- package/bin/mcp.js +25 -11
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +40 -12
- package/dist/lib/agent-discovery.d.ts +1 -1
- package/dist/lib/agent-integration.d.ts +1 -0
- package/dist/lib/claude-settings-witness.d.ts +19 -0
- package/dist/sdk/index.js +38 -12
- package/docs/plugin-admission.md +39 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -559,7 +559,8 @@ The plan proves the preserved original matches every witness being replaced,
|
|
|
559
559
|
permits only the documented non-discovery differences, and labels the replacement
|
|
560
560
|
witness (the JSON receipt lists them under `replacedWitnesses`). `--apply`
|
|
561
561
|
preserves and reads back the original policy before writing, and never writes the
|
|
562
|
-
native configuration. Claude upgrades to `claude-settings-v3
|
|
562
|
+
native configuration. Claude upgrades to `claude-settings-v3`, or to
|
|
563
|
+
`claude-settings-v4` with `--claude-witness-version 4`; Codex uses
|
|
563
564
|
`codex-settings-v2`, which also replaces the narrower automatic
|
|
564
565
|
`plugins`/`marketplaces`/`skills` projection of the same file once the preserved
|
|
565
566
|
original is proved to explain it. That projection is order-sensitive, so it keeps
|
|
@@ -1304,6 +1305,21 @@ the backup keeps the exact previous launcher (symlink text or pinned bytes), and
|
|
|
1304
1305
|
projection and `skills self-update` read a managed pinned launcher as the exact
|
|
1305
1306
|
entry it runs and bind the launcher's own bytes as well.
|
|
1306
1307
|
|
|
1308
|
+
An updater from before pinned launchers (0.10.48 and earlier) writes bare symlinks,
|
|
1309
|
+
including when it installs a version that has them, and an exact-version update to
|
|
1310
|
+
the version already installed is refused. `skills self-update --adopt-aliases` pins
|
|
1311
|
+
those launchers in place: in `~/.local/bin`, `~/.bun/bin`, `/opt/homebrew/bin` and
|
|
1312
|
+
`/usr/local/bin`, a bare symlink that already reaches the current runtime's entry is
|
|
1313
|
+
adopted like an alias still pointing at an older install. The same refusals apply
|
|
1314
|
+
(an unsafe or unwritable directory, a launcher owned by another account, a foreign
|
|
1315
|
+
file, an unsupported link chain), the exact symlink is kept as
|
|
1316
|
+
`<launcher>.skills-alias-prev-<receipt id>`, the receipt records it as the old
|
|
1317
|
+
`symlink` shape, and `--rollback-aliases <receipt id>` restores it exactly. A
|
|
1318
|
+
launcher already pinned to the current runtime is left unchanged. Undo in reverse
|
|
1319
|
+
order: roll back the adoption before rolling back the runtime update that wrote
|
|
1320
|
+
those symlinks, because that update's `--rollback` refuses with
|
|
1321
|
+
`LAUNCHER_DRIFT_ROLLBACK_REFUSED` while the adopted pinned launchers are in place.
|
|
1322
|
+
|
|
1307
1323
|
To require a minimum dependency age, pass a positive integer number of days and
|
|
1308
1324
|
repeat the exclusion option for package names or package globs:
|
|
1309
1325
|
|
|
@@ -2568,3 +2584,18 @@ Upgrade an existing review with `skills hook rebind-settings --agent codex
|
|
|
2568
2584
|
Review the plan before adding `--apply`; the original review preimage must match
|
|
2569
2585
|
its original mode, and unrelated changes refuse. This preserves the native
|
|
2570
2586
|
configuration, root aliases, other witnesses and active session pins.
|
|
2587
|
+
|
|
2588
|
+
`claude-settings-v4` is an explicit successor to `claude-settings-v3`. It
|
|
2589
|
+
additionally ignores only a top-level `theme` whose value is exactly one of the
|
|
2590
|
+
documented built-in values (`auto`, `dark`, `light`, `dark-daltonized`,
|
|
2591
|
+
`light-daltonized`, `dark-ansi`, `light-ansi`). Custom and plugin themes, other
|
|
2592
|
+
values or types, nested `theme` keys, `skipDangerousModePermissionPrompt` and
|
|
2593
|
+
every permission, hook, environment, helper and unknown setting stay bound.
|
|
2594
|
+
Existing raw/V1/V2/V3 reviews retain their original meaning.
|
|
2595
|
+
|
|
2596
|
+
Capture with `skills hook witness --kind claude-settings-v4 --path <settings.json>`.
|
|
2597
|
+
Upgrade an existing review with `skills hook rebind-settings --agent claude
|
|
2598
|
+
--claude-witness-version 4 --reviewed-preimage <preserved-settings.json>
|
|
2599
|
+
--expected-policy-sha256 <policy-hash> --expected-settings-sha256 <settings-hash>`.
|
|
2600
|
+
Review the plan before adding `--apply`. See
|
|
2601
|
+
[built-in theme reviews](docs/plugin-admission.md#explicit-v4-built-in-theme-reviews).
|
package/bin/index.js
CHANGED
|
@@ -36888,7 +36888,7 @@ var package_default;
|
|
|
36888
36888
|
var init_package = __esm(() => {
|
|
36889
36889
|
package_default = {
|
|
36890
36890
|
name: "@hasna/skills",
|
|
36891
|
-
version: "0.10.
|
|
36891
|
+
version: "0.10.50",
|
|
36892
36892
|
description: "Skills library for AI coding agents",
|
|
36893
36893
|
type: "module",
|
|
36894
36894
|
bin: {
|
|
@@ -40066,7 +40066,9 @@ function inferencePreference(key, child, version) {
|
|
|
40066
40066
|
function settingsDigest(text, version = 1) {
|
|
40067
40067
|
const value = parse(text);
|
|
40068
40068
|
value.entries = value.entries.filter(([key, child]) => {
|
|
40069
|
-
if (version
|
|
40069
|
+
if (version === 4 && key === "theme" && child.kind === "string" && BUILTIN_THEMES.has(child.value))
|
|
40070
|
+
return false;
|
|
40071
|
+
if (version !== 1 && inferencePreference(key, child, version === 4 ? 3 : version))
|
|
40070
40072
|
return false;
|
|
40071
40073
|
if (key === "model") {
|
|
40072
40074
|
need(child.kind === "string", "contains an invalid model selection");
|
|
@@ -40124,6 +40126,26 @@ function upgradeClaudeSettingsWitness(previous, reviewedSettingsPath) {
|
|
|
40124
40126
|
need(settingsDigest(before2, 3) === sha256, "non-preference settings changed; explicit discovery review required");
|
|
40125
40127
|
return { path: previous.path, hashMode: "claude-settings-v3", sha256 };
|
|
40126
40128
|
}
|
|
40129
|
+
function captureClaudeSettingsV4(path, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
40130
|
+
return { path, hashMode: "claude-settings-v4", sha256: settingsDigest(readNativeSettingsWitnessFile(path, budget), 4) };
|
|
40131
|
+
}
|
|
40132
|
+
function hashClaudeSettingsReplacementV4(text, budget) {
|
|
40133
|
+
need(typeof text === "string", "requires settings text");
|
|
40134
|
+
const bytes = Buffer.byteLength(text);
|
|
40135
|
+
need(bytes <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes && Number.isSafeInteger(budget.remaining) && budget.remaining >= bytes, "exceeds its byte limit");
|
|
40136
|
+
budget.remaining -= bytes;
|
|
40137
|
+
return settingsDigest(text, 4);
|
|
40138
|
+
}
|
|
40139
|
+
function upgradeClaudeSettingsWitnessV4(previous, reviewedSettingsPath) {
|
|
40140
|
+
need(previous && Object.keys(previous).every((key) => ["path", "hashMode", "sha256"].includes(key)) && (previous.hashMode === undefined || previous.hashMode === "bytes" || previous.hashMode === "claude-settings-v1" || previous.hashMode === "claude-settings-v2" || previous.hashMode === "claude-settings-v3") && typeof previous.sha256 === "string" && /^[a-f0-9]{64}$/.test(previous.sha256), "requires an exact prior settings witness");
|
|
40141
|
+
const budget = { remaining: 2 * CLAUDE_SETTINGS_WITNESS_LIMITS.bytes };
|
|
40142
|
+
const before2 = readNativeSettingsWitnessFile(reviewedSettingsPath, budget), current = readNativeSettingsWitnessFile(previous.path, budget);
|
|
40143
|
+
const beforeDigest = previous.hashMode === undefined || previous.hashMode === "bytes" ? createHash2("sha256").update(before2).digest("hex") : settingsDigest(before2, previous.hashMode === "claude-settings-v1" ? 1 : previous.hashMode === "claude-settings-v2" ? 2 : 3);
|
|
40144
|
+
need(beforeDigest === previous.sha256, "reviewed preimage does not match the prior witness");
|
|
40145
|
+
const sha256 = settingsDigest(current, 4);
|
|
40146
|
+
need(settingsDigest(before2, 4) === sha256, "non-preference settings changed; explicit discovery review required");
|
|
40147
|
+
return { path: previous.path, hashMode: "claude-settings-v4", sha256 };
|
|
40148
|
+
}
|
|
40127
40149
|
function hashNativeJsonControls(text, excludedMetadata) {
|
|
40128
40150
|
need(Buffer.byteLength(text) <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes, "exceeds its byte limit");
|
|
40129
40151
|
const value = parse(text);
|
|
@@ -40156,7 +40178,7 @@ function hashSumiNativeJsonControls(text) {
|
|
|
40156
40178
|
});
|
|
40157
40179
|
return createHash2("sha256").update("hasna.skills.sumi-settings.v1\x00").update(JSON.stringify(value)).digest("hex");
|
|
40158
40180
|
}
|
|
40159
|
-
var CLAUDE_SETTINGS_WITNESS_LIMITS, sameFile = (a, b) => a.dev === b.dev && a.ino === b.ino && a.mode === b.mode && a.uid === b.uid && a.gid === b.gid && a.nlink === b.nlink && a.size === b.size && a.mtimeNs === b.mtimeNs && a.ctimeNs === b.ctimeNs, BOOLEAN_PREFERENCES, ENUM_PREFERENCES, BUILTIN_MODEL_SELECTIONS, PERSISTED_EFFORT_LEVELS;
|
|
40181
|
+
var CLAUDE_SETTINGS_WITNESS_LIMITS, sameFile = (a, b) => a.dev === b.dev && a.ino === b.ino && a.mode === b.mode && a.uid === b.uid && a.gid === b.gid && a.nlink === b.nlink && a.size === b.size && a.mtimeNs === b.mtimeNs && a.ctimeNs === b.ctimeNs, BOOLEAN_PREFERENCES, ENUM_PREFERENCES, BUILTIN_MODEL_SELECTIONS, CLAUDE_BUILTIN_THEMES, BUILTIN_THEMES, PERSISTED_EFFORT_LEVELS;
|
|
40160
40182
|
var init_claude_settings_witness = __esm(() => {
|
|
40161
40183
|
CLAUDE_SETTINGS_WITNESS_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
|
|
40162
40184
|
BOOLEAN_PREFERENCES = new Set([
|
|
@@ -40210,6 +40232,8 @@ var init_claude_settings_witness = __esm(() => {
|
|
|
40210
40232
|
"claude-opus-4-6[1m]",
|
|
40211
40233
|
"claude-sonnet-4-6[1m]"
|
|
40212
40234
|
]);
|
|
40235
|
+
CLAUDE_BUILTIN_THEMES = Object.freeze(["auto", "dark", "light", "dark-daltonized", "light-daltonized", "dark-ansi", "light-ansi"]);
|
|
40236
|
+
BUILTIN_THEMES = new Set(CLAUDE_BUILTIN_THEMES);
|
|
40213
40237
|
PERSISTED_EFFORT_LEVELS = new Set(["low", "medium", "high", "xhigh"]);
|
|
40214
40238
|
});
|
|
40215
40239
|
|
|
@@ -40387,14 +40411,14 @@ function assertAgentPolicyCollections(policy) {
|
|
|
40387
40411
|
text(source.path);
|
|
40388
40412
|
requireBound(source.sha256 === null || typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256));
|
|
40389
40413
|
if (source.hashMode !== undefined)
|
|
40390
|
-
requireBound(["bytes", "path-bytes", "claude-plugin-manifest-v1", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-marketplace-registry-v2", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3", "codex-settings-v4", "sumi-settings-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
|
|
40414
|
+
requireBound(["bytes", "path-bytes", "claude-plugin-manifest-v1", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-settings-v4", "claude-marketplace-registry-v2", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3", "codex-settings-v4", "sumi-settings-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
|
|
40391
40415
|
if (source.hashMode === "claude-plugin-manifest-v1")
|
|
40392
40416
|
requireBound(agent === "claude" && value.agent === "claude" && ["automatic", "reviewed"].includes(value.method) && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve4(source.path) === source.path && source.path.endsWith(join11(sep2, ".claude-plugin", "plugin.json")) && source.managedPlugins === undefined);
|
|
40393
40417
|
if (source.hashMode === "sumi-settings-v1")
|
|
40394
40418
|
requireBound(agent === "sumi" && value.agent === "sumi" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve4(source.path) === source.path && basename3(source.path) === "sumi.json");
|
|
40395
40419
|
if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4"))
|
|
40396
40420
|
requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve4(source.path) === source.path && basename3(source.path) === "config.toml");
|
|
40397
|
-
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3")
|
|
40421
|
+
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4"))
|
|
40398
40422
|
requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve4(source.path) === source.path && basename3(source.path) === "settings.json");
|
|
40399
40423
|
if (source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2")
|
|
40400
40424
|
requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve4(source.path) === source.path && basename3(source.path) === "known_marketplaces.json");
|
|
@@ -55262,6 +55286,8 @@ function atomicWrite2(path, content) {
|
|
|
55262
55286
|
function planAgentSettingsWitnessUpgrade(options) {
|
|
55263
55287
|
if (options.targetCodexVersion !== undefined && (options.agent !== "codex" || ![2, 3, 4].includes(options.targetCodexVersion)))
|
|
55264
55288
|
throw new Error("Invalid Codex witness target version");
|
|
55289
|
+
if (options.targetClaudeVersion !== undefined && (options.agent !== "claude" || ![3, 4].includes(options.targetClaudeVersion)))
|
|
55290
|
+
throw new Error("Invalid Claude witness target version");
|
|
55265
55291
|
const home = resolve14(options.home ?? homedir3()), dataDir3 = options.dataDir ?? getDataDirReadOnly();
|
|
55266
55292
|
const snapshot2 = readManagedSkillPolicySnapshot(dataDir3);
|
|
55267
55293
|
if (!snapshot2 || sha3(snapshot2.text) !== options.expectedPolicySha256)
|
|
@@ -55309,7 +55335,7 @@ function planAgentSettingsWitnessUpgrade(options) {
|
|
|
55309
55335
|
replaced.push(source);
|
|
55310
55336
|
}
|
|
55311
55337
|
}
|
|
55312
|
-
const next = options.agent === "sumi" ? upgradeSumiSettingsWitness(previous, options.reviewedPreimage) : options.agent === "claude" ? upgradeClaudeSettingsWitness(previous, options.reviewedPreimage) : options.targetCodexVersion === 4 ? upgradeCodexSettingsWitnessV4(previous, options.reviewedPreimage) : options.targetCodexVersion === 3 ? upgradeCodexSettingsWitnessV3(previous, options.reviewedPreimage) : upgradeCodexSettingsWitness(previous, options.reviewedPreimage);
|
|
55338
|
+
const next = options.agent === "sumi" ? upgradeSumiSettingsWitness(previous, options.reviewedPreimage) : options.agent === "claude" ? options.targetClaudeVersion === 4 ? upgradeClaudeSettingsWitnessV4(previous, options.reviewedPreimage) : upgradeClaudeSettingsWitness(previous, options.reviewedPreimage) : options.targetCodexVersion === 4 ? upgradeCodexSettingsWitnessV4(previous, options.reviewedPreimage) : options.targetCodexVersion === 3 ? upgradeCodexSettingsWitnessV3(previous, options.reviewedPreimage) : upgradeCodexSettingsWitness(previous, options.reviewedPreimage);
|
|
55313
55339
|
const rebound = [];
|
|
55314
55340
|
for (const source of binding.sources) {
|
|
55315
55341
|
if (!replaced.includes(source)) {
|
|
@@ -55355,7 +55381,7 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
|
|
|
55355
55381
|
const upgrade = plan.settingsWitnessUpgrade;
|
|
55356
55382
|
if (!plan.managedAgentChecks || !plan.observedPolicy?.before)
|
|
55357
55383
|
throw new Error("Invalid settings witness upgrade plan");
|
|
55358
|
-
const verified = planAgentSettingsWitnessUpgrade({ agent: upgrade.agent, ...upgrade.agent === "codex" ? { targetCodexVersion: upgrade.toHashMode === "codex-settings-v4" ? 4 : upgrade.toHashMode === "codex-settings-v3" ? 3 : 2 } : {}, reviewedPreimage: upgrade.reviewedPreimage, home: plan.managedAgentChecks.home, dataDir: plan.dataDir, expectedPolicySha256: sha3(plan.observedPolicy.before), expectedSettingsSha256: upgrade.currentSettingsSha256 });
|
|
55384
|
+
const verified = planAgentSettingsWitnessUpgrade({ agent: upgrade.agent, ...upgrade.agent === "codex" ? { targetCodexVersion: upgrade.toHashMode === "codex-settings-v4" ? 4 : upgrade.toHashMode === "codex-settings-v3" ? 3 : 2 } : {}, ...upgrade.agent === "claude" && upgrade.toHashMode === "claude-settings-v4" ? { targetClaudeVersion: 4 } : {}, reviewedPreimage: upgrade.reviewedPreimage, home: plan.managedAgentChecks.home, dataDir: plan.dataDir, expectedPolicySha256: sha3(plan.observedPolicy.before), expectedSettingsSha256: upgrade.currentSettingsSha256 });
|
|
55359
55385
|
if (JSON.stringify(verified.changes) !== JSON.stringify(plan.changes) || JSON.stringify(verified.settingsWitnessUpgrade) !== JSON.stringify(upgrade))
|
|
55360
55386
|
throw new Error("Settings witness upgrade plan changed");
|
|
55361
55387
|
provenDiscovery = verified.discoveryBefore[0];
|
|
@@ -59082,13 +59108,13 @@ function projected(source, changes, budget2 = discoveryByteBudget()) {
|
|
|
59082
59108
|
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
59083
59109
|
return changes?.has(source.path) ? (source.hashMode === "codex-settings-v4" ? hashCodexSettingsReplacementV4 : source.hashMode === "codex-settings-v3" ? hashCodexSettingsReplacementV3 : source.hashMode === "codex-settings-v2" ? hashCodexSettingsReplacementV2 : hashCodexSettingsReplacement)(changes.get(source.path), budget2) : current;
|
|
59084
59110
|
}
|
|
59085
|
-
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") {
|
|
59111
|
+
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4")) {
|
|
59086
59112
|
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
|
|
59087
59113
|
throw new Error("Claude settings witnesses require exact typed metadata");
|
|
59088
|
-
const current = (source.hashMode === "claude-settings-v3" ? captureClaudeSettingsV3 : source.hashMode === "claude-settings-v2" ? captureClaudeSettingsV2 : captureClaudeSettings)(source.path, budget2).sha256;
|
|
59114
|
+
const current = (source.hashMode === "claude-settings-v4" ? captureClaudeSettingsV4 : source.hashMode === "claude-settings-v3" ? captureClaudeSettingsV3 : source.hashMode === "claude-settings-v2" ? captureClaudeSettingsV2 : captureClaudeSettings)(source.path, budget2).sha256;
|
|
59089
59115
|
if (current !== source.sha256)
|
|
59090
59116
|
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
59091
|
-
return changes?.has(source.path) ? (source.hashMode === "claude-settings-v3" ? hashClaudeSettingsReplacementV3 : source.hashMode === "claude-settings-v2" ? hashClaudeSettingsReplacementV2 : hashClaudeSettingsReplacement)(changes.get(source.path), budget2) : current;
|
|
59117
|
+
return changes?.has(source.path) ? (source.hashMode === "claude-settings-v4" ? hashClaudeSettingsReplacementV4 : source.hashMode === "claude-settings-v3" ? hashClaudeSettingsReplacementV3 : source.hashMode === "claude-settings-v2" ? hashClaudeSettingsReplacementV2 : hashClaudeSettingsReplacement)(changes.get(source.path), budget2) : current;
|
|
59092
59118
|
}
|
|
59093
59119
|
if (source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2") {
|
|
59094
59120
|
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256) || changes?.has(source.path))
|
|
@@ -59323,7 +59349,7 @@ function assertMarketplaceBinding(binding, source) {
|
|
|
59323
59349
|
throw new Error("Sumi settings witnesses require explicit reviewed Sumi configuration");
|
|
59324
59350
|
if ((source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")) && (binding.agent !== "codex" || binding.method !== "reviewed" || basename10(source.path) !== "config.toml"))
|
|
59325
59351
|
throw new Error("Codex settings witnesses require explicit reviewed Codex configuration");
|
|
59326
|
-
if ((source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") && (binding.agent !== "claude" || binding.method !== "reviewed" || basename10(source.path) !== "settings.json"))
|
|
59352
|
+
if ((source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4")) && (binding.agent !== "claude" || binding.method !== "reviewed" || basename10(source.path) !== "settings.json"))
|
|
59327
59353
|
throw new Error("Claude settings witnesses require explicit reviewed Claude configuration");
|
|
59328
59354
|
if ((source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2") && (binding.agent !== "claude" || binding.method !== "reviewed"))
|
|
59329
59355
|
throw new Error("Claude marketplace witnesses require explicit reviewed Claude discovery");
|
|
@@ -59513,7 +59539,7 @@ function resolveAgentDiscovery(options) {
|
|
|
59513
59539
|
verifyAgentDiscovery(retained);
|
|
59514
59540
|
if (!retained.sources.some((source) => source.path === canonical7(configPath) && source.format === undefined && source.fields === undefined))
|
|
59515
59541
|
unresolved("retained review is missing the full agent configuration source");
|
|
59516
|
-
if (retained.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical7(configPath)))
|
|
59542
|
+
if (retained.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical7(configPath)))
|
|
59517
59543
|
unresolved("retained settings witness names another configuration source");
|
|
59518
59544
|
const covered = supersedesSettingsProjection(agent, retained.sources, canonical7(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical7(configPath), source)) : sources;
|
|
59519
59545
|
if (covered.some((source) => !retained.sources.some((saved) => isDeepStrictEqual11(saved, source))) || [...roots].some((root2) => !retained.roots.includes(root2)) || agent === "gemini" && JSON.stringify(retained.builtinNames) !== JSON.stringify(builtinNames))
|
|
@@ -59527,7 +59553,7 @@ function resolveAgentDiscovery(options) {
|
|
|
59527
59553
|
if (review.sources.some((source) => source.format !== undefined || source.fields !== undefined))
|
|
59528
59554
|
throw new Error("Explicit discovery reviews require full source-file hashes");
|
|
59529
59555
|
verifyAgentDiscovery(supplied);
|
|
59530
|
-
if (review.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical7(configPath)))
|
|
59556
|
+
if (review.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical7(configPath)))
|
|
59531
59557
|
throw new Error("Semantic settings witnesses must name the configured agent configuration source");
|
|
59532
59558
|
if (!review.sources.some((source) => source.path === canonical7(configPath)))
|
|
59533
59559
|
throw new Error("Discovery review must include the agent configuration source");
|
|
@@ -112470,7 +112496,7 @@ function adoptCopyfileAliases(options = {}) {
|
|
|
112470
112496
|
const actual = launcher.target;
|
|
112471
112497
|
const oldLinkTarget = launcher.kind === "symlink" ? launcher.linkTarget : actual;
|
|
112472
112498
|
const newTarget = join60(layout.currentPackageRoot, target);
|
|
112473
|
-
if (actual === newTarget)
|
|
112499
|
+
if (actual === newTarget && launcher.kind === "pinned")
|
|
112474
112500
|
continue;
|
|
112475
112501
|
const chain = inspectAliasPackageChain(home, path, oldLinkTarget, actual, target);
|
|
112476
112502
|
const source = validateAliasSource(home, name, actual);
|
|
@@ -112627,7 +112653,7 @@ function rollbackCopyfileAliases(receiptId, options = {}) {
|
|
|
112627
112653
|
if (entryExists(item.backupPath)) {
|
|
112628
112654
|
if (!launcherIs(item.backupPath, oldLauncherState(item)))
|
|
112629
112655
|
throw new Error("ALIAS_PREIMAGE_DRIFT");
|
|
112630
|
-
} else if (current !== item.oldTarget) {
|
|
112656
|
+
} else if (current !== item.oldTarget || !launcherIs(item.path, oldLauncherState(item))) {
|
|
112631
112657
|
throw new Error("ALIAS_BACKUP_MISSING_FOR_SWITCH");
|
|
112632
112658
|
}
|
|
112633
112659
|
}
|
|
@@ -116159,19 +116185,21 @@ function registerAgentIntegration(parent) {
|
|
|
116159
116185
|
process.exitCode = 1;
|
|
116160
116186
|
}
|
|
116161
116187
|
});
|
|
116162
|
-
hook.command("witness").requiredOption("--kind <kind>", "sumi-settings-v1, claude-settings-v3, claude-settings-v2, codex-settings-v4, codex-settings-v3, codex-settings-v2, codex-settings-v1 or claude-marketplace-registry-v2").requiredOption("--path <path>", "Canonical absolute path to the reviewed settings or registry file").option("--json", "Output the discovery witness as JSON", false).description("Capture an explicit versioned review witness without installing it").action(async (options) => {
|
|
116163
|
-
const capture2 = options.kind === "sumi-settings-v1" ? captureSumiSettings : options.kind === "codex-settings-v4" ? captureCodexSettingsV4 : options.kind === "codex-settings-v3" ? captureCodexSettingsV3 : options.kind === "codex-settings-v2" ? captureCodexSettingsV2 : options.kind === "codex-settings-v1" ? captureCodexSettings : options.kind === "claude-settings-v3" ? captureClaudeSettingsV3 : options.kind === "claude-settings-v2" ? captureClaudeSettingsV2 : options.kind === "claude-marketplace-registry-v2" ? captureClaudeMarketplaceRegistryV2 : null;
|
|
116188
|
+
hook.command("witness").requiredOption("--kind <kind>", "sumi-settings-v1, claude-settings-v4, claude-settings-v3, claude-settings-v2, codex-settings-v4, codex-settings-v3, codex-settings-v2, codex-settings-v1 or claude-marketplace-registry-v2").requiredOption("--path <path>", "Canonical absolute path to the reviewed settings or registry file").option("--json", "Output the discovery witness as JSON", false).description("Capture an explicit versioned review witness without installing it").action(async (options) => {
|
|
116189
|
+
const capture2 = options.kind === "sumi-settings-v1" ? captureSumiSettings : options.kind === "codex-settings-v4" ? captureCodexSettingsV4 : options.kind === "codex-settings-v3" ? captureCodexSettingsV3 : options.kind === "codex-settings-v2" ? captureCodexSettingsV2 : options.kind === "codex-settings-v1" ? captureCodexSettings : options.kind === "claude-settings-v4" ? captureClaudeSettingsV4 : options.kind === "claude-settings-v3" ? captureClaudeSettingsV3 : options.kind === "claude-settings-v2" ? captureClaudeSettingsV2 : options.kind === "claude-marketplace-registry-v2" ? captureClaudeMarketplaceRegistryV2 : null;
|
|
116164
116190
|
if (!capture2)
|
|
116165
|
-
throw new Error("Unsupported witness kind; select sumi-settings-v1, claude-settings-v3, claude-settings-v2, codex-settings-v4, codex-settings-v3, codex-settings-v2, codex-settings-v1 or claude-marketplace-registry-v2");
|
|
116191
|
+
throw new Error("Unsupported witness kind; select sumi-settings-v1, claude-settings-v4, claude-settings-v3, claude-settings-v2, codex-settings-v4, codex-settings-v3, codex-settings-v2, codex-settings-v1 or claude-marketplace-registry-v2");
|
|
116166
116192
|
await writeCliOutput(JSON.stringify(capture2(options.path), null, 2));
|
|
116167
116193
|
});
|
|
116168
|
-
hook.command("rebind-settings").requiredOption("--agent <agent>", "claude, codex or sumi").requiredOption("--reviewed-preimage <path>", "Exact preserved legacy settings.json, config.toml or sumi.json").requiredOption("--expected-policy-sha256 <sha256>", "Exact current managed policy bytes").requiredOption("--expected-settings-sha256 <sha256>", "Exact current native settings bytes").option("--codex-witness-version <version>", "Explicit target: 2 (legacy default) 3 (service tier and model-advertised effort), or 4 (native availability UI counts)").option("--apply", "Apply the explicit semantic witness upgrade with preservation and readback", false).option("--json", "Return the metadata-only plan or receipt", false).description("Explicitly migrate a legacy native settings witness after proving only known preferences changed").action(async (options) => {
|
|
116194
|
+
hook.command("rebind-settings").requiredOption("--agent <agent>", "claude, codex or sumi").requiredOption("--reviewed-preimage <path>", "Exact preserved legacy settings.json, config.toml or sumi.json").requiredOption("--expected-policy-sha256 <sha256>", "Exact current managed policy bytes").requiredOption("--expected-settings-sha256 <sha256>", "Exact current native settings bytes").option("--codex-witness-version <version>", "Explicit target: 2 (legacy default) 3 (service tier and model-advertised effort), or 4 (native availability UI counts)").option("--claude-witness-version <version>", "Explicit target: 3 (default) or 4 (also permits a built-in top-level theme)").option("--apply", "Apply the explicit semantic witness upgrade with preservation and readback", false).option("--json", "Return the metadata-only plan or receipt", false).description("Explicitly migrate a legacy native settings witness after proving only known preferences changed").action(async (options) => {
|
|
116169
116195
|
try {
|
|
116170
116196
|
if (!["claude", "codex", "sumi"].includes(options.agent))
|
|
116171
116197
|
throw new Error("Settings witness rebind accepts claude, codex or sumi");
|
|
116172
116198
|
if (options.codexWitnessVersion !== undefined && !["2", "3", "4"].includes(options.codexWitnessVersion))
|
|
116173
116199
|
throw new Error("Codex witness version accepts 2, 3 or 4");
|
|
116174
|
-
|
|
116200
|
+
if (options.claudeWitnessVersion !== undefined && !["3", "4"].includes(options.claudeWitnessVersion))
|
|
116201
|
+
throw new Error("Claude witness version accepts 3 or 4");
|
|
116202
|
+
const plan = planAgentSettingsWitnessUpgrade({ ...options.codexWitnessVersion !== undefined ? { targetCodexVersion: Number(options.codexWitnessVersion) } : {}, ...options.claudeWitnessVersion !== undefined ? { targetClaudeVersion: Number(options.claudeWitnessVersion) } : {}, agent: options.agent, reviewedPreimage: options.reviewedPreimage, expectedPolicySha256: options.expectedPolicySha256, expectedSettingsSha256: options.expectedSettingsSha256 });
|
|
116175
116203
|
const result2 = options.apply ? applyAgentIntegration(plan) : { changed: [], backups: [] };
|
|
116176
116204
|
const receipt = { applied: options.apply, settingsWitnessUpgrade: plan.settingsWitnessUpgrade, ...result2 };
|
|
116177
116205
|
await writeCliOutput(options.json ? JSON.stringify(receipt) : `Settings witness ${options.apply ? "upgraded" : "planned"} for ${options.agent}. Native configuration was not changed.`);
|
package/bin/mcp.js
CHANGED
|
@@ -20019,7 +20019,7 @@ var package_default;
|
|
|
20019
20019
|
var init_package = __esm(() => {
|
|
20020
20020
|
package_default = {
|
|
20021
20021
|
name: "@hasna/skills",
|
|
20022
|
-
version: "0.10.
|
|
20022
|
+
version: "0.10.50",
|
|
20023
20023
|
description: "Skills library for AI coding agents",
|
|
20024
20024
|
type: "module",
|
|
20025
20025
|
bin: {
|
|
@@ -38370,7 +38370,9 @@ function inferencePreference(key, child, version2) {
|
|
|
38370
38370
|
function settingsDigest(text, version2 = 1) {
|
|
38371
38371
|
const value = parse6(text);
|
|
38372
38372
|
value.entries = value.entries.filter(([key, child]) => {
|
|
38373
|
-
if (version2
|
|
38373
|
+
if (version2 === 4 && key === "theme" && child.kind === "string" && BUILTIN_THEMES.has(child.value))
|
|
38374
|
+
return false;
|
|
38375
|
+
if (version2 !== 1 && inferencePreference(key, child, version2 === 4 ? 3 : version2))
|
|
38374
38376
|
return false;
|
|
38375
38377
|
if (key === "model") {
|
|
38376
38378
|
need(child.kind === "string", "contains an invalid model selection");
|
|
@@ -38418,6 +38420,16 @@ function hashClaudeSettingsReplacementV3(text, budget) {
|
|
|
38418
38420
|
budget.remaining -= bytes;
|
|
38419
38421
|
return settingsDigest(text, 3);
|
|
38420
38422
|
}
|
|
38423
|
+
function captureClaudeSettingsV4(path, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
38424
|
+
return { path, hashMode: "claude-settings-v4", sha256: settingsDigest(readNativeSettingsWitnessFile(path, budget), 4) };
|
|
38425
|
+
}
|
|
38426
|
+
function hashClaudeSettingsReplacementV4(text, budget) {
|
|
38427
|
+
need(typeof text === "string", "requires settings text");
|
|
38428
|
+
const bytes = Buffer.byteLength(text);
|
|
38429
|
+
need(bytes <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes && Number.isSafeInteger(budget.remaining) && budget.remaining >= bytes, "exceeds its byte limit");
|
|
38430
|
+
budget.remaining -= bytes;
|
|
38431
|
+
return settingsDigest(text, 4);
|
|
38432
|
+
}
|
|
38421
38433
|
function hashNativeJsonControls(text, excludedMetadata) {
|
|
38422
38434
|
need(Buffer.byteLength(text) <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes, "exceeds its byte limit");
|
|
38423
38435
|
const value = parse6(text);
|
|
@@ -38450,7 +38462,7 @@ function hashSumiNativeJsonControls(text) {
|
|
|
38450
38462
|
});
|
|
38451
38463
|
return createHash2("sha256").update("hasna.skills.sumi-settings.v1\x00").update(JSON.stringify(value)).digest("hex");
|
|
38452
38464
|
}
|
|
38453
|
-
var CLAUDE_SETTINGS_WITNESS_LIMITS, sameFile = (a, b) => a.dev === b.dev && a.ino === b.ino && a.mode === b.mode && a.uid === b.uid && a.gid === b.gid && a.nlink === b.nlink && a.size === b.size && a.mtimeNs === b.mtimeNs && a.ctimeNs === b.ctimeNs, BOOLEAN_PREFERENCES, ENUM_PREFERENCES, BUILTIN_MODEL_SELECTIONS, PERSISTED_EFFORT_LEVELS;
|
|
38465
|
+
var CLAUDE_SETTINGS_WITNESS_LIMITS, sameFile = (a, b) => a.dev === b.dev && a.ino === b.ino && a.mode === b.mode && a.uid === b.uid && a.gid === b.gid && a.nlink === b.nlink && a.size === b.size && a.mtimeNs === b.mtimeNs && a.ctimeNs === b.ctimeNs, BOOLEAN_PREFERENCES, ENUM_PREFERENCES, BUILTIN_MODEL_SELECTIONS, CLAUDE_BUILTIN_THEMES, BUILTIN_THEMES, PERSISTED_EFFORT_LEVELS;
|
|
38454
38466
|
var init_claude_settings_witness = __esm(() => {
|
|
38455
38467
|
CLAUDE_SETTINGS_WITNESS_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
|
|
38456
38468
|
BOOLEAN_PREFERENCES = new Set([
|
|
@@ -38504,6 +38516,8 @@ var init_claude_settings_witness = __esm(() => {
|
|
|
38504
38516
|
"claude-opus-4-6[1m]",
|
|
38505
38517
|
"claude-sonnet-4-6[1m]"
|
|
38506
38518
|
]);
|
|
38519
|
+
CLAUDE_BUILTIN_THEMES = Object.freeze(["auto", "dark", "light", "dark-daltonized", "light-daltonized", "dark-ansi", "light-ansi"]);
|
|
38520
|
+
BUILTIN_THEMES = new Set(CLAUDE_BUILTIN_THEMES);
|
|
38507
38521
|
PERSISTED_EFFORT_LEVELS = new Set(["low", "medium", "high", "xhigh"]);
|
|
38508
38522
|
});
|
|
38509
38523
|
|
|
@@ -38666,14 +38680,14 @@ function assertAgentPolicyCollections(policy) {
|
|
|
38666
38680
|
text(source.path);
|
|
38667
38681
|
requireBound(source.sha256 === null || typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256));
|
|
38668
38682
|
if (source.hashMode !== undefined)
|
|
38669
|
-
requireBound(["bytes", "path-bytes", "claude-plugin-manifest-v1", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-marketplace-registry-v2", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3", "codex-settings-v4", "sumi-settings-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
|
|
38683
|
+
requireBound(["bytes", "path-bytes", "claude-plugin-manifest-v1", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-settings-v4", "claude-marketplace-registry-v2", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3", "codex-settings-v4", "sumi-settings-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
|
|
38670
38684
|
if (source.hashMode === "claude-plugin-manifest-v1")
|
|
38671
38685
|
requireBound(agent === "claude" && value.agent === "claude" && ["automatic", "reviewed"].includes(value.method) && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute4(source.path) && resolve3(source.path) === source.path && source.path.endsWith(join10(sep3, ".claude-plugin", "plugin.json")) && source.managedPlugins === undefined);
|
|
38672
38686
|
if (source.hashMode === "sumi-settings-v1")
|
|
38673
38687
|
requireBound(agent === "sumi" && value.agent === "sumi" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute4(source.path) && resolve3(source.path) === source.path && basename4(source.path) === "sumi.json");
|
|
38674
38688
|
if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4"))
|
|
38675
38689
|
requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute4(source.path) && resolve3(source.path) === source.path && basename4(source.path) === "config.toml");
|
|
38676
|
-
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3")
|
|
38690
|
+
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4"))
|
|
38677
38691
|
requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute4(source.path) && resolve3(source.path) === source.path && basename4(source.path) === "settings.json");
|
|
38678
38692
|
if (source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2")
|
|
38679
38693
|
requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute4(source.path) && resolve3(source.path) === source.path && basename4(source.path) === "known_marketplaces.json");
|
|
@@ -50580,13 +50594,13 @@ function projected(source, changes, budget2 = discoveryByteBudget()) {
|
|
|
50580
50594
|
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
50581
50595
|
return changes?.has(source.path) ? (source.hashMode === "codex-settings-v4" ? hashCodexSettingsReplacementV4 : source.hashMode === "codex-settings-v3" ? hashCodexSettingsReplacementV3 : source.hashMode === "codex-settings-v2" ? hashCodexSettingsReplacementV2 : hashCodexSettingsReplacement)(changes.get(source.path), budget2) : current;
|
|
50582
50596
|
}
|
|
50583
|
-
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") {
|
|
50597
|
+
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4")) {
|
|
50584
50598
|
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
|
|
50585
50599
|
throw new Error("Claude settings witnesses require exact typed metadata");
|
|
50586
|
-
const current = (source.hashMode === "claude-settings-v3" ? captureClaudeSettingsV3 : source.hashMode === "claude-settings-v2" ? captureClaudeSettingsV2 : captureClaudeSettings)(source.path, budget2).sha256;
|
|
50600
|
+
const current = (source.hashMode === "claude-settings-v4" ? captureClaudeSettingsV4 : source.hashMode === "claude-settings-v3" ? captureClaudeSettingsV3 : source.hashMode === "claude-settings-v2" ? captureClaudeSettingsV2 : captureClaudeSettings)(source.path, budget2).sha256;
|
|
50587
50601
|
if (current !== source.sha256)
|
|
50588
50602
|
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
50589
|
-
return changes?.has(source.path) ? (source.hashMode === "claude-settings-v3" ? hashClaudeSettingsReplacementV3 : source.hashMode === "claude-settings-v2" ? hashClaudeSettingsReplacementV2 : hashClaudeSettingsReplacement)(changes.get(source.path), budget2) : current;
|
|
50603
|
+
return changes?.has(source.path) ? (source.hashMode === "claude-settings-v4" ? hashClaudeSettingsReplacementV4 : source.hashMode === "claude-settings-v3" ? hashClaudeSettingsReplacementV3 : source.hashMode === "claude-settings-v2" ? hashClaudeSettingsReplacementV2 : hashClaudeSettingsReplacement)(changes.get(source.path), budget2) : current;
|
|
50590
50604
|
}
|
|
50591
50605
|
if (source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2") {
|
|
50592
50606
|
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256) || changes?.has(source.path))
|
|
@@ -50805,7 +50819,7 @@ function assertMarketplaceBinding(binding, source) {
|
|
|
50805
50819
|
throw new Error("Sumi settings witnesses require explicit reviewed Sumi configuration");
|
|
50806
50820
|
if ((source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")) && (binding.agent !== "codex" || binding.method !== "reviewed" || basename8(source.path) !== "config.toml"))
|
|
50807
50821
|
throw new Error("Codex settings witnesses require explicit reviewed Codex configuration");
|
|
50808
|
-
if ((source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") && (binding.agent !== "claude" || binding.method !== "reviewed" || basename8(source.path) !== "settings.json"))
|
|
50822
|
+
if ((source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4")) && (binding.agent !== "claude" || binding.method !== "reviewed" || basename8(source.path) !== "settings.json"))
|
|
50809
50823
|
throw new Error("Claude settings witnesses require explicit reviewed Claude configuration");
|
|
50810
50824
|
if ((source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2") && (binding.agent !== "claude" || binding.method !== "reviewed"))
|
|
50811
50825
|
throw new Error("Claude marketplace witnesses require explicit reviewed Claude discovery");
|
|
@@ -50927,7 +50941,7 @@ function resolveAgentDiscovery(options) {
|
|
|
50927
50941
|
verifyAgentDiscovery(retained);
|
|
50928
50942
|
if (!retained.sources.some((source) => source.path === canonical7(configPath) && source.format === undefined && source.fields === undefined))
|
|
50929
50943
|
unresolved("retained review is missing the full agent configuration source");
|
|
50930
|
-
if (retained.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical7(configPath)))
|
|
50944
|
+
if (retained.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical7(configPath)))
|
|
50931
50945
|
unresolved("retained settings witness names another configuration source");
|
|
50932
50946
|
const covered = supersedesSettingsProjection(agent, retained.sources, canonical7(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical7(configPath), source)) : sources;
|
|
50933
50947
|
if (covered.some((source) => !retained.sources.some((saved) => isDeepStrictEqual4(saved, source))) || [...roots].some((root) => !retained.roots.includes(root)) || agent === "gemini" && JSON.stringify(retained.builtinNames) !== JSON.stringify(builtinNames))
|
|
@@ -50941,7 +50955,7 @@ function resolveAgentDiscovery(options) {
|
|
|
50941
50955
|
if (review.sources.some((source) => source.format !== undefined || source.fields !== undefined))
|
|
50942
50956
|
throw new Error("Explicit discovery reviews require full source-file hashes");
|
|
50943
50957
|
verifyAgentDiscovery(supplied);
|
|
50944
|
-
if (review.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical7(configPath)))
|
|
50958
|
+
if (review.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical7(configPath)))
|
|
50945
50959
|
throw new Error("Semantic settings witnesses must name the configured agent configuration source");
|
|
50946
50960
|
if (!review.sources.some((source) => source.path === canonical7(configPath)))
|
|
50947
50961
|
throw new Error("Discovery review must include the agent configuration source");
|
package/bin/migrate.js
CHANGED
package/bin/server.js
CHANGED
|
@@ -24140,7 +24140,7 @@ restoreLaunchCwd();
|
|
|
24140
24140
|
// package.json
|
|
24141
24141
|
var package_default = {
|
|
24142
24142
|
name: "@hasna/skills",
|
|
24143
|
-
version: "0.10.
|
|
24143
|
+
version: "0.10.50",
|
|
24144
24144
|
description: "Skills library for AI coding agents",
|
|
24145
24145
|
type: "module",
|
|
24146
24146
|
bin: {
|
package/bin/worker.js
CHANGED
|
@@ -24143,7 +24143,7 @@ import { randomUUID as randomUUID6 } from "crypto";
|
|
|
24143
24143
|
// package.json
|
|
24144
24144
|
var package_default = {
|
|
24145
24145
|
name: "@hasna/skills",
|
|
24146
|
-
version: "0.10.
|
|
24146
|
+
version: "0.10.50",
|
|
24147
24147
|
description: "Skills library for AI coding agents",
|
|
24148
24148
|
type: "module",
|
|
24149
24149
|
bin: {
|
package/dist/index.d.ts
CHANGED
|
@@ -64,7 +64,7 @@ export { planAgentIntegration, planClaudeStopHookUpdate, planClaudePreToolUseHoo
|
|
|
64
64
|
export type { ClaudeManagedHookProjection, ClaudeManagedHookProjectionOptions } from "./lib/agent-integration.js";
|
|
65
65
|
export { captureDiscoveryDirectories, captureDiscoveryByteSources, captureDiscoveryPathSources, type DiscoveryDirectory, type DiscoverySource } from "./lib/agent-discovery.js";
|
|
66
66
|
export { captureClaudeMarketplaceRegistry, captureClaudeMarketplaceRegistryV2 } from "./lib/claude-marketplace-registry.js";
|
|
67
|
-
export { captureClaudeSettings, captureClaudeSettingsV2, captureClaudeSettingsV3, upgradeClaudeSettingsWitness } from "./lib/claude-settings-witness.js";
|
|
67
|
+
export { captureClaudeSettings, captureClaudeSettingsV2, captureClaudeSettingsV3, captureClaudeSettingsV4, upgradeClaudeSettingsWitness, upgradeClaudeSettingsWitnessV4 } from "./lib/claude-settings-witness.js";
|
|
68
68
|
export { buildPluginProjection, pluginTreeDigest, type PluginProjectionManifest, type PluginPayloadMapping, type PluginFileWitness } from "./lib/plugin-projection.js";
|
|
69
69
|
export { planPluginAdmission, admitPlugin, resolveAdmittedPlugin, pluginResolverCommand, type PluginAdmissionTarget, type PluginAdmissionBinding, type PluginAdmissionPrincipal, type PluginAdmissionPlan, type PluginAdmissionIdentity, type PluginBundleIdentity, type PluginRoutingIdentity, type PluginAdmissionReceipt } from "./lib/plugin-admission.js";
|
|
70
70
|
export { captureManagedPluginRegistry, type ManagedPluginRegistrationWitness } from "./lib/plugin-discovery.js";
|
package/dist/index.js
CHANGED
|
@@ -9177,6 +9177,8 @@ var BUILTIN_MODEL_SELECTIONS = new Set([
|
|
|
9177
9177
|
"claude-opus-4-6[1m]",
|
|
9178
9178
|
"claude-sonnet-4-6[1m]"
|
|
9179
9179
|
]);
|
|
9180
|
+
var CLAUDE_BUILTIN_THEMES = Object.freeze(["auto", "dark", "light", "dark-daltonized", "light-daltonized", "dark-ansi", "light-ansi"]);
|
|
9181
|
+
var BUILTIN_THEMES = new Set(CLAUDE_BUILTIN_THEMES);
|
|
9180
9182
|
function canonical(value) {
|
|
9181
9183
|
if (value.kind === "object")
|
|
9182
9184
|
return { kind: "object", entries: value.entries.map(([key, child]) => [key, canonical(child)]).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0) };
|
|
@@ -9235,7 +9237,9 @@ function inferencePreference(key, child, version) {
|
|
|
9235
9237
|
function settingsDigest(text, version = 1) {
|
|
9236
9238
|
const value = parse(text);
|
|
9237
9239
|
value.entries = value.entries.filter(([key, child]) => {
|
|
9238
|
-
if (version
|
|
9240
|
+
if (version === 4 && key === "theme" && child.kind === "string" && BUILTIN_THEMES.has(child.value))
|
|
9241
|
+
return false;
|
|
9242
|
+
if (version !== 1 && inferencePreference(key, child, version === 4 ? 3 : version))
|
|
9239
9243
|
return false;
|
|
9240
9244
|
if (key === "model") {
|
|
9241
9245
|
need(child.kind === "string", "contains an invalid model selection");
|
|
@@ -9293,6 +9297,26 @@ function upgradeClaudeSettingsWitness(previous, reviewedSettingsPath) {
|
|
|
9293
9297
|
need(settingsDigest(before, 3) === sha256, "non-preference settings changed; explicit discovery review required");
|
|
9294
9298
|
return { path: previous.path, hashMode: "claude-settings-v3", sha256 };
|
|
9295
9299
|
}
|
|
9300
|
+
function captureClaudeSettingsV4(path, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
9301
|
+
return { path, hashMode: "claude-settings-v4", sha256: settingsDigest(readNativeSettingsWitnessFile(path, budget), 4) };
|
|
9302
|
+
}
|
|
9303
|
+
function hashClaudeSettingsReplacementV4(text, budget) {
|
|
9304
|
+
need(typeof text === "string", "requires settings text");
|
|
9305
|
+
const bytes = Buffer.byteLength(text);
|
|
9306
|
+
need(bytes <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes && Number.isSafeInteger(budget.remaining) && budget.remaining >= bytes, "exceeds its byte limit");
|
|
9307
|
+
budget.remaining -= bytes;
|
|
9308
|
+
return settingsDigest(text, 4);
|
|
9309
|
+
}
|
|
9310
|
+
function upgradeClaudeSettingsWitnessV4(previous, reviewedSettingsPath) {
|
|
9311
|
+
need(previous && Object.keys(previous).every((key) => ["path", "hashMode", "sha256"].includes(key)) && (previous.hashMode === undefined || previous.hashMode === "bytes" || previous.hashMode === "claude-settings-v1" || previous.hashMode === "claude-settings-v2" || previous.hashMode === "claude-settings-v3") && typeof previous.sha256 === "string" && /^[a-f0-9]{64}$/.test(previous.sha256), "requires an exact prior settings witness");
|
|
9312
|
+
const budget = { remaining: 2 * CLAUDE_SETTINGS_WITNESS_LIMITS.bytes };
|
|
9313
|
+
const before = readNativeSettingsWitnessFile(reviewedSettingsPath, budget), current = readNativeSettingsWitnessFile(previous.path, budget);
|
|
9314
|
+
const beforeDigest = previous.hashMode === undefined || previous.hashMode === "bytes" ? createHash2("sha256").update(before).digest("hex") : settingsDigest(before, previous.hashMode === "claude-settings-v1" ? 1 : previous.hashMode === "claude-settings-v2" ? 2 : 3);
|
|
9315
|
+
need(beforeDigest === previous.sha256, "reviewed preimage does not match the prior witness");
|
|
9316
|
+
const sha256 = settingsDigest(current, 4);
|
|
9317
|
+
need(settingsDigest(before, 4) === sha256, "non-preference settings changed; explicit discovery review required");
|
|
9318
|
+
return { path: previous.path, hashMode: "claude-settings-v4", sha256 };
|
|
9319
|
+
}
|
|
9296
9320
|
function hashNativeJsonControls(text, excludedMetadata) {
|
|
9297
9321
|
need(Buffer.byteLength(text) <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes, "exceeds its byte limit");
|
|
9298
9322
|
const value = parse(text);
|
|
@@ -9497,14 +9521,14 @@ function assertAgentPolicyCollections(policy) {
|
|
|
9497
9521
|
text(source.path);
|
|
9498
9522
|
requireBound(source.sha256 === null || typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256));
|
|
9499
9523
|
if (source.hashMode !== undefined)
|
|
9500
|
-
requireBound(["bytes", "path-bytes", "claude-plugin-manifest-v1", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-marketplace-registry-v2", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3", "codex-settings-v4", "sumi-settings-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
|
|
9524
|
+
requireBound(["bytes", "path-bytes", "claude-plugin-manifest-v1", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-settings-v4", "claude-marketplace-registry-v2", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3", "codex-settings-v4", "sumi-settings-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
|
|
9501
9525
|
if (source.hashMode === "claude-plugin-manifest-v1")
|
|
9502
9526
|
requireBound(agent === "claude" && value.agent === "claude" && ["automatic", "reviewed"].includes(value.method) && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve3(source.path) === source.path && source.path.endsWith(join7(sep2, ".claude-plugin", "plugin.json")) && source.managedPlugins === undefined);
|
|
9503
9527
|
if (source.hashMode === "sumi-settings-v1")
|
|
9504
9528
|
requireBound(agent === "sumi" && value.agent === "sumi" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve3(source.path) === source.path && basename3(source.path) === "sumi.json");
|
|
9505
9529
|
if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4"))
|
|
9506
9530
|
requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve3(source.path) === source.path && basename3(source.path) === "config.toml");
|
|
9507
|
-
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3")
|
|
9531
|
+
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4"))
|
|
9508
9532
|
requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve3(source.path) === source.path && basename3(source.path) === "settings.json");
|
|
9509
9533
|
if (source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2")
|
|
9510
9534
|
requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve3(source.path) === source.path && basename3(source.path) === "known_marketplaces.json");
|
|
@@ -11572,7 +11596,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
|
|
|
11572
11596
|
// package.json
|
|
11573
11597
|
var package_default = {
|
|
11574
11598
|
name: "@hasna/skills",
|
|
11575
|
-
version: "0.10.
|
|
11599
|
+
version: "0.10.50",
|
|
11576
11600
|
description: "Skills library for AI coding agents",
|
|
11577
11601
|
type: "module",
|
|
11578
11602
|
bin: {
|
|
@@ -17254,6 +17278,8 @@ function atomicWrite2(path, content) {
|
|
|
17254
17278
|
function planAgentSettingsWitnessUpgrade(options) {
|
|
17255
17279
|
if (options.targetCodexVersion !== undefined && (options.agent !== "codex" || ![2, 3, 4].includes(options.targetCodexVersion)))
|
|
17256
17280
|
throw new Error("Invalid Codex witness target version");
|
|
17281
|
+
if (options.targetClaudeVersion !== undefined && (options.agent !== "claude" || ![3, 4].includes(options.targetClaudeVersion)))
|
|
17282
|
+
throw new Error("Invalid Claude witness target version");
|
|
17257
17283
|
const home = resolve13(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
|
|
17258
17284
|
const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
|
|
17259
17285
|
if (!snapshot2 || sha3(snapshot2.text) !== options.expectedPolicySha256)
|
|
@@ -17301,7 +17327,7 @@ function planAgentSettingsWitnessUpgrade(options) {
|
|
|
17301
17327
|
replaced.push(source);
|
|
17302
17328
|
}
|
|
17303
17329
|
}
|
|
17304
|
-
const next = options.agent === "sumi" ? upgradeSumiSettingsWitness(previous, options.reviewedPreimage) : options.agent === "claude" ? upgradeClaudeSettingsWitness(previous, options.reviewedPreimage) : options.targetCodexVersion === 4 ? upgradeCodexSettingsWitnessV4(previous, options.reviewedPreimage) : options.targetCodexVersion === 3 ? upgradeCodexSettingsWitnessV3(previous, options.reviewedPreimage) : upgradeCodexSettingsWitness(previous, options.reviewedPreimage);
|
|
17330
|
+
const next = options.agent === "sumi" ? upgradeSumiSettingsWitness(previous, options.reviewedPreimage) : options.agent === "claude" ? options.targetClaudeVersion === 4 ? upgradeClaudeSettingsWitnessV4(previous, options.reviewedPreimage) : upgradeClaudeSettingsWitness(previous, options.reviewedPreimage) : options.targetCodexVersion === 4 ? upgradeCodexSettingsWitnessV4(previous, options.reviewedPreimage) : options.targetCodexVersion === 3 ? upgradeCodexSettingsWitnessV3(previous, options.reviewedPreimage) : upgradeCodexSettingsWitness(previous, options.reviewedPreimage);
|
|
17305
17331
|
const rebound = [];
|
|
17306
17332
|
for (const source of binding.sources) {
|
|
17307
17333
|
if (!replaced.includes(source)) {
|
|
@@ -17347,7 +17373,7 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
|
|
|
17347
17373
|
const upgrade = plan.settingsWitnessUpgrade;
|
|
17348
17374
|
if (!plan.managedAgentChecks || !plan.observedPolicy?.before)
|
|
17349
17375
|
throw new Error("Invalid settings witness upgrade plan");
|
|
17350
|
-
const verified = planAgentSettingsWitnessUpgrade({ agent: upgrade.agent, ...upgrade.agent === "codex" ? { targetCodexVersion: upgrade.toHashMode === "codex-settings-v4" ? 4 : upgrade.toHashMode === "codex-settings-v3" ? 3 : 2 } : {}, reviewedPreimage: upgrade.reviewedPreimage, home: plan.managedAgentChecks.home, dataDir: plan.dataDir, expectedPolicySha256: sha3(plan.observedPolicy.before), expectedSettingsSha256: upgrade.currentSettingsSha256 });
|
|
17376
|
+
const verified = planAgentSettingsWitnessUpgrade({ agent: upgrade.agent, ...upgrade.agent === "codex" ? { targetCodexVersion: upgrade.toHashMode === "codex-settings-v4" ? 4 : upgrade.toHashMode === "codex-settings-v3" ? 3 : 2 } : {}, ...upgrade.agent === "claude" && upgrade.toHashMode === "claude-settings-v4" ? { targetClaudeVersion: 4 } : {}, reviewedPreimage: upgrade.reviewedPreimage, home: plan.managedAgentChecks.home, dataDir: plan.dataDir, expectedPolicySha256: sha3(plan.observedPolicy.before), expectedSettingsSha256: upgrade.currentSettingsSha256 });
|
|
17351
17377
|
if (JSON.stringify(verified.changes) !== JSON.stringify(plan.changes) || JSON.stringify(verified.settingsWitnessUpgrade) !== JSON.stringify(upgrade))
|
|
17352
17378
|
throw new Error("Settings witness upgrade plan changed");
|
|
17353
17379
|
provenDiscovery = verified.discoveryBefore[0];
|
|
@@ -20394,13 +20420,13 @@ function projected(source, changes, budget2 = discoveryByteBudget()) {
|
|
|
20394
20420
|
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
20395
20421
|
return changes?.has(source.path) ? (source.hashMode === "codex-settings-v4" ? hashCodexSettingsReplacementV4 : source.hashMode === "codex-settings-v3" ? hashCodexSettingsReplacementV3 : source.hashMode === "codex-settings-v2" ? hashCodexSettingsReplacementV2 : hashCodexSettingsReplacement)(changes.get(source.path), budget2) : current;
|
|
20396
20422
|
}
|
|
20397
|
-
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") {
|
|
20423
|
+
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4")) {
|
|
20398
20424
|
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
|
|
20399
20425
|
throw new Error("Claude settings witnesses require exact typed metadata");
|
|
20400
|
-
const current = (source.hashMode === "claude-settings-v3" ? captureClaudeSettingsV3 : source.hashMode === "claude-settings-v2" ? captureClaudeSettingsV2 : captureClaudeSettings)(source.path, budget2).sha256;
|
|
20426
|
+
const current = (source.hashMode === "claude-settings-v4" ? captureClaudeSettingsV4 : source.hashMode === "claude-settings-v3" ? captureClaudeSettingsV3 : source.hashMode === "claude-settings-v2" ? captureClaudeSettingsV2 : captureClaudeSettings)(source.path, budget2).sha256;
|
|
20401
20427
|
if (current !== source.sha256)
|
|
20402
20428
|
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
20403
|
-
return changes?.has(source.path) ? (source.hashMode === "claude-settings-v3" ? hashClaudeSettingsReplacementV3 : source.hashMode === "claude-settings-v2" ? hashClaudeSettingsReplacementV2 : hashClaudeSettingsReplacement)(changes.get(source.path), budget2) : current;
|
|
20429
|
+
return changes?.has(source.path) ? (source.hashMode === "claude-settings-v4" ? hashClaudeSettingsReplacementV4 : source.hashMode === "claude-settings-v3" ? hashClaudeSettingsReplacementV3 : source.hashMode === "claude-settings-v2" ? hashClaudeSettingsReplacementV2 : hashClaudeSettingsReplacement)(changes.get(source.path), budget2) : current;
|
|
20404
20430
|
}
|
|
20405
20431
|
if (source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2") {
|
|
20406
20432
|
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256) || changes?.has(source.path))
|
|
@@ -20635,7 +20661,7 @@ function assertMarketplaceBinding(binding, source) {
|
|
|
20635
20661
|
throw new Error("Sumi settings witnesses require explicit reviewed Sumi configuration");
|
|
20636
20662
|
if ((source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")) && (binding.agent !== "codex" || binding.method !== "reviewed" || basename10(source.path) !== "config.toml"))
|
|
20637
20663
|
throw new Error("Codex settings witnesses require explicit reviewed Codex configuration");
|
|
20638
|
-
if ((source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") && (binding.agent !== "claude" || binding.method !== "reviewed" || basename10(source.path) !== "settings.json"))
|
|
20664
|
+
if ((source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4")) && (binding.agent !== "claude" || binding.method !== "reviewed" || basename10(source.path) !== "settings.json"))
|
|
20639
20665
|
throw new Error("Claude settings witnesses require explicit reviewed Claude configuration");
|
|
20640
20666
|
if ((source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2") && (binding.agent !== "claude" || binding.method !== "reviewed"))
|
|
20641
20667
|
throw new Error("Claude marketplace witnesses require explicit reviewed Claude discovery");
|
|
@@ -20837,7 +20863,7 @@ function resolveAgentDiscovery(options) {
|
|
|
20837
20863
|
verifyAgentDiscovery(retained);
|
|
20838
20864
|
if (!retained.sources.some((source) => source.path === canonical7(configPath) && source.format === undefined && source.fields === undefined))
|
|
20839
20865
|
unresolved("retained review is missing the full agent configuration source");
|
|
20840
|
-
if (retained.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical7(configPath)))
|
|
20866
|
+
if (retained.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical7(configPath)))
|
|
20841
20867
|
unresolved("retained settings witness names another configuration source");
|
|
20842
20868
|
const covered = supersedesSettingsProjection(agent, retained.sources, canonical7(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical7(configPath), source)) : sources;
|
|
20843
20869
|
if (covered.some((source) => !retained.sources.some((saved) => isDeepStrictEqual7(saved, source))) || [...roots].some((root2) => !retained.roots.includes(root2)) || agent === "gemini" && JSON.stringify(retained.builtinNames) !== JSON.stringify(builtinNames))
|
|
@@ -20851,7 +20877,7 @@ function resolveAgentDiscovery(options) {
|
|
|
20851
20877
|
if (review.sources.some((source) => source.format !== undefined || source.fields !== undefined))
|
|
20852
20878
|
throw new Error("Explicit discovery reviews require full source-file hashes");
|
|
20853
20879
|
verifyAgentDiscovery(supplied);
|
|
20854
|
-
if (review.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical7(configPath)))
|
|
20880
|
+
if (review.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical7(configPath)))
|
|
20855
20881
|
throw new Error("Semantic settings witnesses must name the configured agent configuration source");
|
|
20856
20882
|
if (!review.sources.some((source) => source.path === canonical7(configPath)))
|
|
20857
20883
|
throw new Error("Discovery review must include the agent configuration source");
|
|
@@ -47854,6 +47880,7 @@ export {
|
|
|
47854
47880
|
upgradeCodexSettingsWitnessV4,
|
|
47855
47881
|
upgradeCodexSettingsWitnessV3,
|
|
47856
47882
|
upgradeCodexSettingsWitness,
|
|
47883
|
+
upgradeClaudeSettingsWitnessV4,
|
|
47857
47884
|
upgradeClaudeSettingsWitness,
|
|
47858
47885
|
updateSkillRun,
|
|
47859
47886
|
unsetConfig,
|
|
@@ -48045,6 +48072,7 @@ export {
|
|
|
48045
48072
|
captureCodexSettingsV2,
|
|
48046
48073
|
captureCodexSettings,
|
|
48047
48074
|
captureCodexNativeSkillCatalog,
|
|
48075
|
+
captureClaudeSettingsV4,
|
|
48048
48076
|
captureClaudeSettingsV3,
|
|
48049
48077
|
captureClaudeSettingsV2,
|
|
48050
48078
|
captureClaudeSettings,
|
|
@@ -7,7 +7,7 @@ export { captureDiscoveryDirectories, type DiscoveryDirectory } from "./agent-di
|
|
|
7
7
|
export interface DiscoverySource {
|
|
8
8
|
path: string;
|
|
9
9
|
sha256: string | null;
|
|
10
|
-
hashMode?: "bytes" | "path-bytes" | "claude-plugin-manifest-v1" | "claude-plugin-registry" | "claude-marketplace-registry" | "claude-settings-v1" | "claude-settings-v2" | "claude-settings-v3" | "claude-marketplace-registry-v2" | "codex-settings-v1" | "codex-settings-v2" | "codex-settings-v3" | "codex-settings-v4" | "sumi-settings-v1";
|
|
10
|
+
hashMode?: "bytes" | "path-bytes" | "claude-plugin-manifest-v1" | "claude-plugin-registry" | "claude-marketplace-registry" | "claude-settings-v1" | "claude-settings-v2" | "claude-settings-v3" | "claude-settings-v4" | "claude-marketplace-registry-v2" | "codex-settings-v1" | "codex-settings-v2" | "codex-settings-v3" | "codex-settings-v4" | "sumi-settings-v1";
|
|
11
11
|
managedPlugins?: ManagedPluginRegistrationWitness[];
|
|
12
12
|
format?: "json" | "toml" | "yaml";
|
|
13
13
|
fields?: string[];
|
|
@@ -190,6 +190,7 @@ export declare function planClaudeHookEventsUpdate(options: ClaudeHookUpdateOpti
|
|
|
190
190
|
export declare function planAgentSettingsWitnessUpgrade(options: {
|
|
191
191
|
agent: "claude" | "codex" | "sumi";
|
|
192
192
|
targetCodexVersion?: 2 | 3 | 4;
|
|
193
|
+
targetClaudeVersion?: 3 | 4;
|
|
193
194
|
reviewedPreimage: string;
|
|
194
195
|
home?: string;
|
|
195
196
|
dataDir?: string;
|
|
@@ -10,6 +10,7 @@ export interface ClaudeSettingsWitnessBudget {
|
|
|
10
10
|
remaining: number;
|
|
11
11
|
}
|
|
12
12
|
export declare function readNativeSettingsWitnessFile(path: string, budget: ClaudeSettingsWitnessBudget, filename?: "settings.json" | "config.toml" | "sumi.json"): string;
|
|
13
|
+
export declare const CLAUDE_BUILTIN_THEMES: readonly ["auto", "dark", "light", "dark-daltonized", "light-daltonized", "dark-ansi", "light-ansi"];
|
|
13
14
|
/** Cooperating installers may replace only their explicitly selected event. Compare the strict syntax
|
|
14
15
|
* trees, not JSON.parse values or the preference-tolerant witness digest: all
|
|
15
16
|
* other hooks, discovery settings, unknown values and number spellings remain
|
|
@@ -51,6 +52,24 @@ export declare function upgradeClaudeSettingsWitness(previous: {
|
|
|
51
52
|
hashMode?: "bytes" | "claude-settings-v1" | "claude-settings-v2";
|
|
52
53
|
sha256: string;
|
|
53
54
|
}, reviewedSettingsPath: string): ReturnType<typeof captureClaudeSettingsV3>;
|
|
55
|
+
/** Explicit review that additionally permits a built-in top-level theme.
|
|
56
|
+
* v1/v2/v3 witnesses keep their meaning; nothing selects v4 implicitly. */
|
|
57
|
+
export declare function captureClaudeSettingsV4(path: string, budget?: ClaudeSettingsWitnessBudget): {
|
|
58
|
+
path: string;
|
|
59
|
+
hashMode: "claude-settings-v4";
|
|
60
|
+
sha256: string;
|
|
61
|
+
};
|
|
62
|
+
export declare function hashClaudeSettingsReplacementV4(text: string, budget: ClaudeSettingsWitnessBudget): string;
|
|
63
|
+
/** Explicit opt-in upgrade to v4 with an exact preserved preimage of the prior
|
|
64
|
+
* review (raw bytes, v1, v2 or v3). It proves that every setting v4 binds is
|
|
65
|
+
* unchanged; any other drift, including skipDangerousModePermissionPrompt,
|
|
66
|
+
* refuses. A stored witness is never rewritten without this call.
|
|
67
|
+
*/
|
|
68
|
+
export declare function upgradeClaudeSettingsWitnessV4(previous: {
|
|
69
|
+
path: string;
|
|
70
|
+
hashMode?: "bytes" | "claude-settings-v1" | "claude-settings-v2" | "claude-settings-v3";
|
|
71
|
+
sha256: string;
|
|
72
|
+
}, reviewedSettingsPath: string): ReturnType<typeof captureClaudeSettingsV4>;
|
|
54
73
|
/** Strict bounded JSON control witness; metadata exclusion must be explicit. */
|
|
55
74
|
export declare function hashNativeJsonControls(text: string, excludedMetadata?: "version"): string;
|
|
56
75
|
/** Sumi's versioned review uses the same strict JSON parser and bounded syntax
|
package/dist/sdk/index.js
CHANGED
|
@@ -32373,7 +32373,7 @@ var init_event_sink = __esm(() => {
|
|
|
32373
32373
|
// package.json
|
|
32374
32374
|
var package_default = {
|
|
32375
32375
|
name: "@hasna/skills",
|
|
32376
|
-
version: "0.10.
|
|
32376
|
+
version: "0.10.50",
|
|
32377
32377
|
description: "Skills library for AI coding agents",
|
|
32378
32378
|
type: "module",
|
|
32379
32379
|
bin: {
|
|
@@ -38140,6 +38140,8 @@ var BUILTIN_MODEL_SELECTIONS = new Set([
|
|
|
38140
38140
|
"claude-opus-4-6[1m]",
|
|
38141
38141
|
"claude-sonnet-4-6[1m]"
|
|
38142
38142
|
]);
|
|
38143
|
+
var CLAUDE_BUILTIN_THEMES = Object.freeze(["auto", "dark", "light", "dark-daltonized", "light-daltonized", "dark-ansi", "light-ansi"]);
|
|
38144
|
+
var BUILTIN_THEMES = new Set(CLAUDE_BUILTIN_THEMES);
|
|
38143
38145
|
function canonical(value) {
|
|
38144
38146
|
if (value.kind === "object")
|
|
38145
38147
|
return { kind: "object", entries: value.entries.map(([key, child]) => [key, canonical(child)]).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0) };
|
|
@@ -38198,7 +38200,9 @@ function inferencePreference(key, child, version) {
|
|
|
38198
38200
|
function settingsDigest(text, version = 1) {
|
|
38199
38201
|
const value = parse2(text);
|
|
38200
38202
|
value.entries = value.entries.filter(([key, child]) => {
|
|
38201
|
-
if (version
|
|
38203
|
+
if (version === 4 && key === "theme" && child.kind === "string" && BUILTIN_THEMES.has(child.value))
|
|
38204
|
+
return false;
|
|
38205
|
+
if (version !== 1 && inferencePreference(key, child, version === 4 ? 3 : version))
|
|
38202
38206
|
return false;
|
|
38203
38207
|
if (key === "model") {
|
|
38204
38208
|
need(child.kind === "string", "contains an invalid model selection");
|
|
@@ -38256,6 +38260,26 @@ function upgradeClaudeSettingsWitness(previous, reviewedSettingsPath) {
|
|
|
38256
38260
|
need(settingsDigest(before, 3) === sha2562, "non-preference settings changed; explicit discovery review required");
|
|
38257
38261
|
return { path: previous.path, hashMode: "claude-settings-v3", sha256: sha2562 };
|
|
38258
38262
|
}
|
|
38263
|
+
function captureClaudeSettingsV4(path, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
38264
|
+
return { path, hashMode: "claude-settings-v4", sha256: settingsDigest(readNativeSettingsWitnessFile(path, budget), 4) };
|
|
38265
|
+
}
|
|
38266
|
+
function hashClaudeSettingsReplacementV4(text, budget) {
|
|
38267
|
+
need(typeof text === "string", "requires settings text");
|
|
38268
|
+
const bytes = Buffer.byteLength(text);
|
|
38269
|
+
need(bytes <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes && Number.isSafeInteger(budget.remaining) && budget.remaining >= bytes, "exceeds its byte limit");
|
|
38270
|
+
budget.remaining -= bytes;
|
|
38271
|
+
return settingsDigest(text, 4);
|
|
38272
|
+
}
|
|
38273
|
+
function upgradeClaudeSettingsWitnessV4(previous, reviewedSettingsPath) {
|
|
38274
|
+
need(previous && Object.keys(previous).every((key) => ["path", "hashMode", "sha256"].includes(key)) && (previous.hashMode === undefined || previous.hashMode === "bytes" || previous.hashMode === "claude-settings-v1" || previous.hashMode === "claude-settings-v2" || previous.hashMode === "claude-settings-v3") && typeof previous.sha256 === "string" && /^[a-f0-9]{64}$/.test(previous.sha256), "requires an exact prior settings witness");
|
|
38275
|
+
const budget = { remaining: 2 * CLAUDE_SETTINGS_WITNESS_LIMITS.bytes };
|
|
38276
|
+
const before = readNativeSettingsWitnessFile(reviewedSettingsPath, budget), current = readNativeSettingsWitnessFile(previous.path, budget);
|
|
38277
|
+
const beforeDigest = previous.hashMode === undefined || previous.hashMode === "bytes" ? createHash6("sha256").update(before).digest("hex") : settingsDigest(before, previous.hashMode === "claude-settings-v1" ? 1 : previous.hashMode === "claude-settings-v2" ? 2 : 3);
|
|
38278
|
+
need(beforeDigest === previous.sha256, "reviewed preimage does not match the prior witness");
|
|
38279
|
+
const sha2562 = settingsDigest(current, 4);
|
|
38280
|
+
need(settingsDigest(before, 4) === sha2562, "non-preference settings changed; explicit discovery review required");
|
|
38281
|
+
return { path: previous.path, hashMode: "claude-settings-v4", sha256: sha2562 };
|
|
38282
|
+
}
|
|
38259
38283
|
function hashNativeJsonControls(text, excludedMetadata) {
|
|
38260
38284
|
need(Buffer.byteLength(text) <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes, "exceeds its byte limit");
|
|
38261
38285
|
const value = parse2(text);
|
|
@@ -40405,14 +40429,14 @@ function assertAgentPolicyCollections(policy) {
|
|
|
40405
40429
|
text(source.path);
|
|
40406
40430
|
requireBound(source.sha256 === null || typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256));
|
|
40407
40431
|
if (source.hashMode !== undefined)
|
|
40408
|
-
requireBound(["bytes", "path-bytes", "claude-plugin-manifest-v1", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-marketplace-registry-v2", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3", "codex-settings-v4", "sumi-settings-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
|
|
40432
|
+
requireBound(["bytes", "path-bytes", "claude-plugin-manifest-v1", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-settings-v4", "claude-marketplace-registry-v2", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3", "codex-settings-v4", "sumi-settings-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
|
|
40409
40433
|
if (source.hashMode === "claude-plugin-manifest-v1")
|
|
40410
40434
|
requireBound(agent === "claude" && value.agent === "claude" && ["automatic", "reviewed"].includes(value.method) && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute8(source.path) && resolve7(source.path) === source.path && source.path.endsWith(join14(sep4, ".claude-plugin", "plugin.json")) && source.managedPlugins === undefined);
|
|
40411
40435
|
if (source.hashMode === "sumi-settings-v1")
|
|
40412
40436
|
requireBound(agent === "sumi" && value.agent === "sumi" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute8(source.path) && resolve7(source.path) === source.path && basename5(source.path) === "sumi.json");
|
|
40413
40437
|
if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4"))
|
|
40414
40438
|
requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute8(source.path) && resolve7(source.path) === source.path && basename5(source.path) === "config.toml");
|
|
40415
|
-
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3")
|
|
40439
|
+
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4"))
|
|
40416
40440
|
requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute8(source.path) && resolve7(source.path) === source.path && basename5(source.path) === "settings.json");
|
|
40417
40441
|
if (source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2")
|
|
40418
40442
|
requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute8(source.path) && resolve7(source.path) === source.path && basename5(source.path) === "known_marketplaces.json");
|
|
@@ -43246,13 +43270,13 @@ function projected(source, changes, budget2 = discoveryByteBudget()) {
|
|
|
43246
43270
|
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
43247
43271
|
return changes?.has(source.path) ? (source.hashMode === "codex-settings-v4" ? hashCodexSettingsReplacementV4 : source.hashMode === "codex-settings-v3" ? hashCodexSettingsReplacementV3 : source.hashMode === "codex-settings-v2" ? hashCodexSettingsReplacementV2 : hashCodexSettingsReplacement)(changes.get(source.path), budget2) : current;
|
|
43248
43272
|
}
|
|
43249
|
-
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") {
|
|
43273
|
+
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4")) {
|
|
43250
43274
|
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
|
|
43251
43275
|
throw new Error("Claude settings witnesses require exact typed metadata");
|
|
43252
|
-
const current = (source.hashMode === "claude-settings-v3" ? captureClaudeSettingsV3 : source.hashMode === "claude-settings-v2" ? captureClaudeSettingsV2 : captureClaudeSettings)(source.path, budget2).sha256;
|
|
43276
|
+
const current = (source.hashMode === "claude-settings-v4" ? captureClaudeSettingsV4 : source.hashMode === "claude-settings-v3" ? captureClaudeSettingsV3 : source.hashMode === "claude-settings-v2" ? captureClaudeSettingsV2 : captureClaudeSettings)(source.path, budget2).sha256;
|
|
43253
43277
|
if (current !== source.sha256)
|
|
43254
43278
|
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
43255
|
-
return changes?.has(source.path) ? (source.hashMode === "claude-settings-v3" ? hashClaudeSettingsReplacementV3 : source.hashMode === "claude-settings-v2" ? hashClaudeSettingsReplacementV2 : hashClaudeSettingsReplacement)(changes.get(source.path), budget2) : current;
|
|
43279
|
+
return changes?.has(source.path) ? (source.hashMode === "claude-settings-v4" ? hashClaudeSettingsReplacementV4 : source.hashMode === "claude-settings-v3" ? hashClaudeSettingsReplacementV3 : source.hashMode === "claude-settings-v2" ? hashClaudeSettingsReplacementV2 : hashClaudeSettingsReplacement)(changes.get(source.path), budget2) : current;
|
|
43256
43280
|
}
|
|
43257
43281
|
if (source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2") {
|
|
43258
43282
|
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256) || changes?.has(source.path))
|
|
@@ -43487,7 +43511,7 @@ function assertMarketplaceBinding(binding, source) {
|
|
|
43487
43511
|
throw new Error("Sumi settings witnesses require explicit reviewed Sumi configuration");
|
|
43488
43512
|
if ((source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")) && (binding.agent !== "codex" || binding.method !== "reviewed" || basename8(source.path) !== "config.toml"))
|
|
43489
43513
|
throw new Error("Codex settings witnesses require explicit reviewed Codex configuration");
|
|
43490
|
-
if ((source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") && (binding.agent !== "claude" || binding.method !== "reviewed" || basename8(source.path) !== "settings.json"))
|
|
43514
|
+
if ((source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4")) && (binding.agent !== "claude" || binding.method !== "reviewed" || basename8(source.path) !== "settings.json"))
|
|
43491
43515
|
throw new Error("Claude settings witnesses require explicit reviewed Claude configuration");
|
|
43492
43516
|
if ((source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2") && (binding.agent !== "claude" || binding.method !== "reviewed"))
|
|
43493
43517
|
throw new Error("Claude marketplace witnesses require explicit reviewed Claude discovery");
|
|
@@ -43689,7 +43713,7 @@ function resolveAgentDiscovery(options) {
|
|
|
43689
43713
|
verifyAgentDiscovery(retained);
|
|
43690
43714
|
if (!retained.sources.some((source) => source.path === canonical7(configPath) && source.format === undefined && source.fields === undefined))
|
|
43691
43715
|
unresolved("retained review is missing the full agent configuration source");
|
|
43692
|
-
if (retained.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical7(configPath)))
|
|
43716
|
+
if (retained.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical7(configPath)))
|
|
43693
43717
|
unresolved("retained settings witness names another configuration source");
|
|
43694
43718
|
const covered = supersedesSettingsProjection(agent, retained.sources, canonical7(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical7(configPath), source)) : sources;
|
|
43695
43719
|
if (covered.some((source) => !retained.sources.some((saved) => isDeepStrictEqual6(saved, source))) || [...roots].some((root2) => !retained.roots.includes(root2)) || agent === "gemini" && JSON.stringify(retained.builtinNames) !== JSON.stringify(builtinNames))
|
|
@@ -43703,7 +43727,7 @@ function resolveAgentDiscovery(options) {
|
|
|
43703
43727
|
if (review.sources.some((source) => source.format !== undefined || source.fields !== undefined))
|
|
43704
43728
|
throw new Error("Explicit discovery reviews require full source-file hashes");
|
|
43705
43729
|
verifyAgentDiscovery(supplied);
|
|
43706
|
-
if (review.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical7(configPath)))
|
|
43730
|
+
if (review.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical7(configPath)))
|
|
43707
43731
|
throw new Error("Semantic settings witnesses must name the configured agent configuration source");
|
|
43708
43732
|
if (!review.sources.some((source) => source.path === canonical7(configPath)))
|
|
43709
43733
|
throw new Error("Discovery review must include the agent configuration source");
|
|
@@ -45374,6 +45398,8 @@ function atomicWrite2(path, content) {
|
|
|
45374
45398
|
function planAgentSettingsWitnessUpgrade(options) {
|
|
45375
45399
|
if (options.targetCodexVersion !== undefined && (options.agent !== "codex" || ![2, 3, 4].includes(options.targetCodexVersion)))
|
|
45376
45400
|
throw new Error("Invalid Codex witness target version");
|
|
45401
|
+
if (options.targetClaudeVersion !== undefined && (options.agent !== "claude" || ![3, 4].includes(options.targetClaudeVersion)))
|
|
45402
|
+
throw new Error("Invalid Claude witness target version");
|
|
45377
45403
|
const home = resolve18(options.home ?? homedir3()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
|
|
45378
45404
|
const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
|
|
45379
45405
|
if (!snapshot2 || sha4(snapshot2.text) !== options.expectedPolicySha256)
|
|
@@ -45421,7 +45447,7 @@ function planAgentSettingsWitnessUpgrade(options) {
|
|
|
45421
45447
|
replaced.push(source);
|
|
45422
45448
|
}
|
|
45423
45449
|
}
|
|
45424
|
-
const next = options.agent === "sumi" ? upgradeSumiSettingsWitness(previous, options.reviewedPreimage) : options.agent === "claude" ? upgradeClaudeSettingsWitness(previous, options.reviewedPreimage) : options.targetCodexVersion === 4 ? upgradeCodexSettingsWitnessV4(previous, options.reviewedPreimage) : options.targetCodexVersion === 3 ? upgradeCodexSettingsWitnessV3(previous, options.reviewedPreimage) : upgradeCodexSettingsWitness(previous, options.reviewedPreimage);
|
|
45450
|
+
const next = options.agent === "sumi" ? upgradeSumiSettingsWitness(previous, options.reviewedPreimage) : options.agent === "claude" ? options.targetClaudeVersion === 4 ? upgradeClaudeSettingsWitnessV4(previous, options.reviewedPreimage) : upgradeClaudeSettingsWitness(previous, options.reviewedPreimage) : options.targetCodexVersion === 4 ? upgradeCodexSettingsWitnessV4(previous, options.reviewedPreimage) : options.targetCodexVersion === 3 ? upgradeCodexSettingsWitnessV3(previous, options.reviewedPreimage) : upgradeCodexSettingsWitness(previous, options.reviewedPreimage);
|
|
45425
45451
|
const rebound = [];
|
|
45426
45452
|
for (const source of binding.sources) {
|
|
45427
45453
|
if (!replaced.includes(source)) {
|
|
@@ -45467,7 +45493,7 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
|
|
|
45467
45493
|
const upgrade = plan.settingsWitnessUpgrade;
|
|
45468
45494
|
if (!plan.managedAgentChecks || !plan.observedPolicy?.before)
|
|
45469
45495
|
throw new Error("Invalid settings witness upgrade plan");
|
|
45470
|
-
const verified = planAgentSettingsWitnessUpgrade({ agent: upgrade.agent, ...upgrade.agent === "codex" ? { targetCodexVersion: upgrade.toHashMode === "codex-settings-v4" ? 4 : upgrade.toHashMode === "codex-settings-v3" ? 3 : 2 } : {}, reviewedPreimage: upgrade.reviewedPreimage, home: plan.managedAgentChecks.home, dataDir: plan.dataDir, expectedPolicySha256: sha4(plan.observedPolicy.before), expectedSettingsSha256: upgrade.currentSettingsSha256 });
|
|
45496
|
+
const verified = planAgentSettingsWitnessUpgrade({ agent: upgrade.agent, ...upgrade.agent === "codex" ? { targetCodexVersion: upgrade.toHashMode === "codex-settings-v4" ? 4 : upgrade.toHashMode === "codex-settings-v3" ? 3 : 2 } : {}, ...upgrade.agent === "claude" && upgrade.toHashMode === "claude-settings-v4" ? { targetClaudeVersion: 4 } : {}, reviewedPreimage: upgrade.reviewedPreimage, home: plan.managedAgentChecks.home, dataDir: plan.dataDir, expectedPolicySha256: sha4(plan.observedPolicy.before), expectedSettingsSha256: upgrade.currentSettingsSha256 });
|
|
45471
45497
|
if (JSON.stringify(verified.changes) !== JSON.stringify(plan.changes) || JSON.stringify(verified.settingsWitnessUpgrade) !== JSON.stringify(upgrade))
|
|
45472
45498
|
throw new Error("Settings witness upgrade plan changed");
|
|
45473
45499
|
provenDiscovery = verified.discoveryBefore[0];
|
package/docs/plugin-admission.md
CHANGED
|
@@ -239,8 +239,8 @@ Invalid values for the display preferences refuse capture and verification. Ever
|
|
|
239
239
|
other field remains bound, including unknown future fields, hooks and their
|
|
240
240
|
exact commands, permissions, native skill suppression and synchronization,
|
|
241
241
|
plugin roots, marketplaces, environment and configuration precedence. Language,
|
|
242
|
-
output style, theme
|
|
243
|
-
intentionally retained. These are narrow preference and inference-selection
|
|
242
|
+
output style, theme (v4 alone permits the built-in theme values), and
|
|
243
|
+
command-bearing status or file suggestion settings are intentionally retained. These are narrow preference and inference-selection
|
|
244
244
|
exceptions, not general permission to change Claude configuration. The native
|
|
245
245
|
bridge guard continues to check its exact commands and native Skill policy.
|
|
246
246
|
|
|
@@ -263,6 +263,43 @@ or guarantee when a running native client adopts a settings edit.
|
|
|
263
263
|
Those complementary checks retain their existing exact registration and hook
|
|
264
264
|
entry comparisons; this mode does not relax them for property-order changes.
|
|
265
265
|
|
|
266
|
+
### Explicit v4 built-in theme reviews
|
|
267
|
+
|
|
268
|
+
`claude-settings-v4` is an explicit successor to v3. It binds everything v3
|
|
269
|
+
binds, with one more omission: the top-level `theme` key, and only when its
|
|
270
|
+
value is exactly one of the built-in values `auto`, `dark`, `light`,
|
|
271
|
+
`dark-daltonized`, `light-daltonized`, `dark-ansi` or `light-ansi`. The list is
|
|
272
|
+
pinned in code (`CLAUDE_BUILTIN_THEMES`) from the
|
|
273
|
+
[`theme` setting reference](https://code.claude.com/docs/en/settings-reference#theme)
|
|
274
|
+
and the [built-in presets](https://code.claude.com/docs/en/terminal-config#match-the-color-theme),
|
|
275
|
+
read on 2026-10-07. Custom and plugin themes (`custom:<slug>`,
|
|
276
|
+
`custom:<plugin-name>:<slug>`) load theme files, so they stay bound, as does any
|
|
277
|
+
other value, type, spelling or letter case, and any `theme` key nested inside
|
|
278
|
+
another setting. `skipDangerousModePermissionPrompt`, permissions, hooks,
|
|
279
|
+
environment, `apiKeyHelper` and other helpers, plugins, marketplaces, custom
|
|
280
|
+
model values and unknown settings stay bound exactly as in v3. The digest is
|
|
281
|
+
domain-separated from v3.
|
|
282
|
+
|
|
283
|
+
Existing v1, v2 and v3 witnesses keep their meaning; nothing selects v4
|
|
284
|
+
implicitly, and a stored witness is never rewritten. A policy opts in through
|
|
285
|
+
either explicit path:
|
|
286
|
+
|
|
287
|
+
- a fresh review: `skills hook witness --kind claude-settings-v4 --path
|
|
288
|
+
<settings.json> --json` (or `captureClaudeSettingsV4(path)`), placed in the
|
|
289
|
+
reviewed discovery inputs and applied with the normal `skills hook install
|
|
290
|
+
--discovery-inputs <file>` plan/apply flow;
|
|
291
|
+
- a preserved-preimage upgrade: `skills hook rebind-settings --agent claude
|
|
292
|
+
--claude-witness-version 4 --reviewed-preimage <preserved-settings.json>
|
|
293
|
+
--expected-policy-sha256 <policy-hash> --expected-settings-sha256
|
|
294
|
+
<settings-hash>` (or `upgradeClaudeSettingsWitnessV4`). It accepts a raw, v1,
|
|
295
|
+
v2 or v3 witness, proves the preimage matches it in its original mode, and
|
|
296
|
+
refuses unless the current settings equal the preimage under v4. Without
|
|
297
|
+
`--claude-witness-version` the rebind target remains v3.
|
|
298
|
+
|
|
299
|
+
Install a runtime that recognizes `claude-settings-v4`, including any bundled
|
|
300
|
+
copy of the verifier, before a policy carries it; an older runtime refuses the
|
|
301
|
+
unknown hash mode.
|
|
302
|
+
|
|
266
303
|
## Verification and limits
|
|
267
304
|
|
|
268
305
|
Unit tests cover provenance and mapping failures, offline/revoked authorities,
|