@hasna/skills 0.10.72 → 0.10.74

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/bin/mcp.js CHANGED
@@ -23,7 +23,7 @@ var package_default;
23
23
  var init_package = __esm(() => {
24
24
  package_default = {
25
25
  name: "@hasna/skills",
26
- version: "0.10.72",
26
+ version: "0.10.74",
27
27
  description: "Skills library for AI coding agents",
28
28
  type: "module",
29
29
  bin: {
@@ -22021,8 +22021,8 @@ function ensureInstructionSkillFiles(skillPath, manifest) {
22021
22021
  displayName: manifest.displayName ?? displayName(manifest.name),
22022
22022
  category: manifest.category ?? "Development Tools",
22023
22023
  tags: manifest.tags?.length ? manifest.tags : ["custom", manifest.name],
22024
- inputs: [],
22025
- commands: []
22024
+ inputs: Array.isArray(manifest.inputs) ? manifest.inputs : [],
22025
+ commands: Array.isArray(manifest.commands) ? manifest.commands : []
22026
22026
  };
22027
22027
  if (!existsSync6(join9(skillPath, "SKILL.md"))) {
22028
22028
  writeFileSync2(join9(skillPath, "SKILL.md"), renderSkillMd(next));
@@ -32219,6 +32219,10 @@ function canonical4(value, depth = 0, budget2 = { remaining: 65536 }) {
32219
32219
  function hashCodexSettingsReplacement(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
32220
32220
  need4(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
32221
32221
  budget2.remaining -= Buffer.byteLength(text2);
32222
+ const prepared = prepareCodexSettingsV1(text2);
32223
+ return finishCodexSettingsV1(prepared.config, prepared.tokens);
32224
+ }
32225
+ function prepareCodexSettingsV1(text2) {
32222
32226
  let config2;
32223
32227
  try {
32224
32228
  config2 = Bun.TOML.parse(text2);
@@ -32226,6 +32230,9 @@ function hashCodexSettingsReplacement(text2, budget2 = { remaining: 256 * 1024 *
32226
32230
  throw new Error("Invalid Codex settings witness");
32227
32231
  }
32228
32232
  need4(config2 && typeof config2 === "object" && !Array.isArray(config2));
32233
+ return { config: config2, tokens: numericTokens(text2) };
32234
+ }
32235
+ function finishCodexSettingsV1(config2, tokens) {
32229
32236
  if (Object.hasOwn(config2, "model")) {
32230
32237
  need4(typeof config2.model === "string" && /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/.test(config2.model));
32231
32238
  delete config2.model;
@@ -32235,11 +32242,7 @@ function hashCodexSettingsReplacement(text2, budget2 = { remaining: 256 * 1024 *
32235
32242
  need4(typeof config2[key] === "string" && values.has(config2[key]));
32236
32243
  delete config2[key];
32237
32244
  }
32238
- return sha(`hasna.skills.codex-settings.v1\x00${JSON.stringify([canonical4(config2), numericTokens(text2)])}`);
32239
- }
32240
- function captureCodexSettings(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
32241
- const text2 = readNativeSettingsWitnessFile(path, budget2, "config.toml");
32242
- return { path, hashMode: "codex-settings-v1", sha256: hashCodexSettingsReplacement(text2) };
32245
+ return sha(`hasna.skills.codex-settings.v1\x00${JSON.stringify([canonical4(config2), tokens])}`);
32243
32246
  }
32244
32247
  var sha = (text2) => createHash11("sha256").update(text2).digest("hex"), EFFORT, VERBOSITY;
32245
32248
  var init_codex_settings_witness_v1 = __esm(() => {
@@ -32953,7 +32956,7 @@ function projectStatusLine(config2) {
32953
32956
  if (!Object.keys(tui).length)
32954
32957
  delete config2.tui;
32955
32958
  }
32956
- function settingsDigest2(text2, version2 = 2, dependencies) {
32959
+ function prepareSettings(text2, version2, dependencies) {
32957
32960
  let config2;
32958
32961
  try {
32959
32962
  config2 = Bun.TOML.parse(text2);
@@ -32992,51 +32995,106 @@ function settingsDigest2(text2, version2 = 2, dependencies) {
32992
32995
  delete config2.tui.model_availability_nux;
32993
32996
  if (isRecord3(config2.tui) && typeof config2.tui.screen_reader_detection_done === "boolean")
32994
32997
  delete config2.tui.screen_reader_detection_done;
32995
- config2 = embedCodexNumericTokens(config2, tokens);
32998
+ return { config: embedCodexNumericTokens(config2, tokens), numericSource: text2, dependencies };
32999
+ }
33000
+ const mcp = version2 >= 3 ? projectRegularCodexMcp(config2, text2) : { config: config2, numericSource: text2 };
33001
+ return version2 >= 4 ? projectModelAvailabilityNux(mcp.config, mcp.numericSource) : mcp;
33002
+ }
33003
+ function finishSettings(prepared, version2) {
33004
+ const config2 = prepared.config;
33005
+ if (version2 === 6) {
32996
33006
  normalize2(config2, 6);
32997
33007
  if (isRecord3(config2.tui) && !Object.keys(config2.tui).length)
32998
33008
  delete config2.tui;
32999
- return sha2(`hasna.skills.codex-settings.v6\x00${JSON.stringify([canonical5(config2), canonical5(dependencies)])}`);
33009
+ return sha2(`hasna.skills.codex-settings.v6\x00${JSON.stringify([canonical5(config2), canonical5(prepared.dependencies)])}`);
33000
33010
  }
33001
- const mcp = version2 >= 3 ? projectRegularCodexMcp(config2, text2) : { config: config2, numericSource: text2 };
33002
- const projected = version2 >= 4 ? projectModelAvailabilityNux(mcp.config, mcp.numericSource) : mcp;
33003
- normalize2(projected.config, version2);
33004
- const values = [canonical5(projected.config), numericTokens2(projected.numericSource)];
33011
+ normalize2(config2, version2);
33012
+ const values = [canonical5(config2), numericTokens2(prepared.numericSource)];
33005
33013
  return sha2(`hasna.skills.codex-settings.v${version2}\x00${JSON.stringify(values)}`);
33006
33014
  }
33015
+ function settingsDigest2(text2, version2 = 2, dependencies) {
33016
+ return finishSettings(prepareSettings(text2, version2, dependencies), version2);
33017
+ }
33018
+ function cloneTree(value) {
33019
+ if (Array.isArray(value))
33020
+ return value.map(cloneTree);
33021
+ if (isRecord3(value) && [Object.prototype, null].includes(Object.getPrototypeOf(value)))
33022
+ return Object.fromEntries(Object.entries(value).map(([key2, child2]) => [key2, cloneTree(child2)]));
33023
+ return value;
33024
+ }
33025
+ function receiptEdits(config2) {
33026
+ const tui = config2.tui;
33027
+ const set2 = (value) => (target) => {
33028
+ if (!isRecord3(target.tui))
33029
+ target.tui = {};
33030
+ target.tui[SCREEN_READER_RECEIPT] = value;
33031
+ };
33032
+ if (tui === undefined || isRecord3(tui) && !Object.hasOwn(tui, SCREEN_READER_RECEIPT))
33033
+ return [set2(true), set2(false)];
33034
+ if (!isRecord3(tui) || typeof tui[SCREEN_READER_RECEIPT] !== "boolean")
33035
+ return [];
33036
+ const remove = (dropEmpty) => (target) => {
33037
+ delete target.tui[SCREEN_READER_RECEIPT];
33038
+ if (dropEmpty && !Object.keys(target.tui).length)
33039
+ delete target.tui;
33040
+ };
33041
+ return [remove(true), ...Object.keys(tui).length === 1 ? [remove(false)] : [], set2(!tui[SCREEN_READER_RECEIPT])];
33042
+ }
33043
+ function codexSettingsToleratesScreenReaderReceipt(text2, hashMode, expected) {
33044
+ try {
33045
+ if (Buffer.byteLength(text2) > 1024 * 1024 || !/^[a-f0-9]{64}$/.test(expected))
33046
+ return false;
33047
+ if (hashMode === "codex-settings-v1") {
33048
+ const prepared2 = prepareCodexSettingsV1(text2);
33049
+ return receiptEdits(prepared2.config).some((edit) => {
33050
+ const candidate = cloneTree(prepared2.config);
33051
+ edit(candidate);
33052
+ return finishCodexSettingsV1(candidate, prepared2.tokens) === expected;
33053
+ });
33054
+ }
33055
+ const version2 = Number(hashMode.slice("codex-settings-v".length));
33056
+ if (![2, 3, 4, 5].includes(version2))
33057
+ return false;
33058
+ const prepared = prepareSettings(text2, version2);
33059
+ return receiptEdits(prepared.config).some((edit) => {
33060
+ const candidate = cloneTree(prepared.config);
33061
+ edit(candidate);
33062
+ if (version2 >= 4 && isRecord3(candidate.tui) && !Object.keys(candidate.tui).length)
33063
+ delete candidate.tui;
33064
+ return finishSettings({ ...prepared, config: candidate }, version2) === expected;
33065
+ });
33066
+ } catch {
33067
+ return false;
33068
+ }
33069
+ }
33070
+ function codexSettingsWitnessMatch(path, hashMode, expected, budget2 = { remaining: 256 * 1024 * 1024 }) {
33071
+ const text2 = readNativeSettingsWitnessFile(path, budget2, "config.toml");
33072
+ need7(Buffer.byteLength(text2) <= 1024 * 1024);
33073
+ const exact = hashMode === "codex-settings-v1" ? hashCodexSettingsReplacement(text2) : settingsDigest2(text2, Number(hashMode.slice("codex-settings-v".length)));
33074
+ if (exact === expected)
33075
+ return "exact";
33076
+ return codexSettingsToleratesScreenReaderReceipt(text2, hashMode, expected) ? "screen-reader-receipt" : null;
33077
+ }
33007
33078
  function hashCodexSettingsReplacementV2(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
33008
33079
  need7(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
33009
33080
  budget2.remaining -= Buffer.byteLength(text2);
33010
33081
  return settingsDigest2(text2);
33011
33082
  }
33012
- function captureCodexSettingsV2(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
33013
- const text2 = readNativeSettingsWitnessFile(path, budget2, "config.toml");
33014
- return { path, hashMode: "codex-settings-v2", sha256: settingsDigest2(text2) };
33015
- }
33016
33083
  function hashCodexSettingsReplacementV3(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
33017
33084
  need7(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
33018
33085
  budget2.remaining -= Buffer.byteLength(text2);
33019
33086
  return settingsDigest2(text2, 3);
33020
33087
  }
33021
- function captureCodexSettingsV3(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
33022
- return { path, hashMode: "codex-settings-v3", sha256: settingsDigest2(readNativeSettingsWitnessFile(path, budget2, "config.toml"), 3) };
33023
- }
33024
33088
  function hashCodexSettingsReplacementV4(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
33025
33089
  need7(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
33026
33090
  budget2.remaining -= Buffer.byteLength(text2);
33027
33091
  return settingsDigest2(text2, 4);
33028
33092
  }
33029
- function captureCodexSettingsV4(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
33030
- return { path, hashMode: "codex-settings-v4", sha256: settingsDigest2(readNativeSettingsWitnessFile(path, budget2, "config.toml"), 4) };
33031
- }
33032
33093
  function hashCodexSettingsReplacementV5(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
33033
33094
  need7(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
33034
33095
  budget2.remaining -= Buffer.byteLength(text2);
33035
33096
  return settingsDigest2(text2, 5);
33036
33097
  }
33037
- function captureCodexSettingsV5(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
33038
- return { path, hashMode: "codex-settings-v5", sha256: settingsDigest2(readNativeSettingsWitnessFile(path, budget2, "config.toml"), 5) };
33039
- }
33040
33098
  function captureCodexSettingsV6(path, reviewedSources, budget2 = { remaining: 256 * 1024 * 1024 }, changes) {
33041
33099
  const original = readNativeSettingsWitnessFile(path, budget2, "config.toml");
33042
33100
  const text2 = changes?.get(path) ?? original;
@@ -33046,7 +33104,7 @@ function captureCodexSettingsV6(path, reviewedSources, budget2 = { remaining: 25
33046
33104
  need7(dependencies.servers.every((server) => !["codex_apps", "_codex_apps"].includes(server) && isCodexLocalStdioMcpServer(server, config2.mcp_servers?.[server])));
33047
33105
  return { path, hashMode: "codex-settings-v6", sha256: settingsDigest2(text2, 6, dependencies), hookSources: dependencies.sources };
33048
33106
  }
33049
- var sha2 = (text2) => createHash13("sha256").update(text2).digest("hex"), EFFORT2, VERBOSITY2, MODEL, CODEX_DISCOVERY_PROJECTION_FIELDS, INFERENCE;
33107
+ var sha2 = (text2) => createHash13("sha256").update(text2).digest("hex"), EFFORT2, VERBOSITY2, MODEL, CODEX_DISCOVERY_PROJECTION_FIELDS, INFERENCE, SCREEN_READER_RECEIPT = "screen_reader_detection_done";
33050
33108
  var init_codex_settings_witness = __esm(() => {
33051
33109
  init_codex_settings_witness_v1();
33052
33110
  init_codex_settings_witness_v1();
@@ -35310,6 +35368,13 @@ var init_codex_semantic_cache_witness = __esm(() => {
35310
35368
  ]);
35311
35369
  });
35312
35370
 
35371
+ // src/lib/codex-node-modules.ts
35372
+ var PLUGIN_MANIFESTS;
35373
+ var init_codex_node_modules = __esm(() => {
35374
+ init_claude_settings_witness();
35375
+ PLUGIN_MANIFESTS = Object.freeze(["plugin.json", ".codex-plugin/plugin.json", ".claude-plugin/plugin.json", ".cursor-plugin/plugin.json"]);
35376
+ });
35377
+
35313
35378
  // src/lib/managed-policy.ts
35314
35379
  import { closeSync as closeSync14, constants as constants11, fstatSync as fstatSync14, lstatSync as lstatSync17, openSync as openSync14, readSync as readSync8 } from "fs";
35315
35380
  import { join as join22 } from "path";
@@ -35838,6 +35903,7 @@ var init_agent_integration = __esm(() => {
35838
35903
  init_native_discovery_roots();
35839
35904
  init_codex_vendor_refresh();
35840
35905
  init_codex_native_skill_catalog();
35906
+ init_codex_node_modules();
35841
35907
  init_config();
35842
35908
  init_managed_policy();
35843
35909
  init_agent_bridge();
@@ -37313,10 +37379,9 @@ function projected(source, changes, budget2 = discoveryByteBudget()) {
37313
37379
  if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4" || source.hashMode === "codex-settings-v5")) {
37314
37380
  if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
37315
37381
  throw new Error("Codex settings witnesses require exact typed metadata");
37316
- const current = (source.hashMode === "codex-settings-v5" ? captureCodexSettingsV5 : source.hashMode === "codex-settings-v4" ? captureCodexSettingsV4 : source.hashMode === "codex-settings-v3" ? captureCodexSettingsV3 : source.hashMode === "codex-settings-v2" ? captureCodexSettingsV2 : captureCodexSettings)(source.path, budget2).sha256;
37317
- if (current !== source.sha256)
37382
+ if (codexSettingsWitnessMatch(source.path, source.hashMode, source.sha256, budget2) === null)
37318
37383
  throw new NativeDiscoveryWitnessChange("input", source.path, `Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
37319
- return changes?.has(source.path) ? (source.hashMode === "codex-settings-v5" ? hashCodexSettingsReplacementV5 : 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;
37384
+ return changes?.has(source.path) ? (source.hashMode === "codex-settings-v5" ? hashCodexSettingsReplacementV5 : source.hashMode === "codex-settings-v4" ? hashCodexSettingsReplacementV4 : source.hashMode === "codex-settings-v3" ? hashCodexSettingsReplacementV3 : source.hashMode === "codex-settings-v2" ? hashCodexSettingsReplacementV2 : hashCodexSettingsReplacement)(changes.get(source.path), budget2) : source.sha256;
37320
37385
  }
37321
37386
  if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4")) {
37322
37387
  if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
@@ -37429,10 +37494,22 @@ function retiredCodexRoots(binding, tolerance) {
37429
37494
  }
37430
37495
  return proof.roots;
37431
37496
  }
37432
- function retainableAbsentSource(source) {
37497
+ function retainableAbsentWitness(source) {
37433
37498
  if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined)
37434
- return false;
37435
- return [undefined, "bytes"].includes(source.hashMode) || source.hashMode === "claude-plugin-manifest-v1" && source.sha256 !== null && source.path.endsWith(join30(sep11, ".claude-plugin", "plugin.json")) || source.hashMode === CLAUDE_ORPHAN_MARKER_MODE && source.sha256 !== null && isClaudePluginCacheOrphanMarker(source.path);
37499
+ return null;
37500
+ if ([undefined, "bytes"].includes(source.hashMode))
37501
+ return "bytes";
37502
+ if (source.hashMode === "claude-plugin-manifest-v1" && source.sha256 !== null && source.path.endsWith(join30(sep11, ".claude-plugin", "plugin.json")))
37503
+ return "claude-plugin-manifest-v1";
37504
+ if (source.hashMode === CLAUDE_ORPHAN_MARKER_MODE && source.sha256 !== null && isClaudePluginCacheOrphanMarker(source.path))
37505
+ return CLAUDE_ORPHAN_MARKER_MODE;
37506
+ return null;
37507
+ }
37508
+ function retainableAbsentSource(source) {
37509
+ return retainableAbsentWitness(source) !== null;
37510
+ }
37511
+ function retainedAbsentRoot(absentRoots, source) {
37512
+ return retainableAbsentSource(source) ? absentRoots.find((root) => source.path.startsWith(root + sep11)) : undefined;
37436
37513
  }
37437
37514
  function absentRetiredClaudeRoots(binding) {
37438
37515
  if (binding.agent !== "claude" || binding.method !== "reviewed")
@@ -37625,7 +37702,7 @@ function verifyAgentDiscovery(binding, codexRecovery, reviewedRootAliases, witne
37625
37702
  safe6(source.path);
37626
37703
  continue;
37627
37704
  }
37628
- if (absentRoots.some((root) => source.path.startsWith(root + sep11)) && retainableAbsentSource(source)) {
37705
+ if (retainedAbsentRoot(absentRoots, source) !== undefined) {
37629
37706
  safe6(source.path);
37630
37707
  continue;
37631
37708
  }
@@ -37649,6 +37726,12 @@ function verifyAgentDiscovery(binding, codexRecovery, reviewedRootAliases, witne
37649
37726
  throw new Error("Retired native materialization changed during discovery verification");
37650
37727
  }
37651
37728
  }
37729
+ function claudeControlSource(source) {
37730
+ return CLAUDE_CONTROL_WITNESS_MODES.has(source.hashMode ?? "") || CLAUDE_CONTROL_FILES.has(basename12(source.path)) && !source.path.includes(`${sep11}.claude${sep11}plugins${sep11}cache${sep11}`);
37731
+ }
37732
+ function retainedAbsentClaudeRoot(absentRoots, source) {
37733
+ return claudeControlSource(source) ? undefined : retainedAbsentRoot(absentRoots, source);
37734
+ }
37652
37735
  function codexInstallationRoots(binding) {
37653
37736
  const proof = binding.codexInstallationInputs;
37654
37737
  if (proof === undefined)
@@ -37844,7 +37927,10 @@ function resolveAgentDiscovery(options) {
37844
37927
  if (!review.sources.some((source) => source.path === canonical9(configPath)))
37845
37928
  throw new Error("Discovery review must include the agent configuration source");
37846
37929
  const reviewedSources = supersedesSettingsProjection(agent, review.sources, canonical9(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical9(configPath), source)) : sources;
37930
+ const absentRoots = absentRetiredClaudeRoots(supplied);
37847
37931
  const reviewedSourcesWithManifestProjection = review.sources.map((source) => {
37932
+ if (retainedAbsentClaudeRoot(absentRoots, source) !== undefined)
37933
+ return source;
37848
37934
  if (agent === "claude" && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined && source.reviewArtifact === undefined && [undefined, "bytes", "path-bytes"].includes(source.hashMode) && isClaudePluginCacheOrphanMarker(source.path)) {
37849
37935
  return { path: source.path, hashMode: CLAUDE_ORPHAN_MARKER_MODE, sha256: hashClaudePluginCacheOrphanMarker(source.path) };
37850
37936
  }
@@ -37877,6 +37963,11 @@ function resolveAgentDiscovery(options) {
37877
37963
  });
37878
37964
  if (agent === "claude")
37879
37965
  assertReviewedClaudeCacheClosures(home, config2, review, canonical9);
37966
+ for (const root of absentRoots) {
37967
+ safe6(root);
37968
+ if (lstatSync23(root, { throwIfNoEntry: false }))
37969
+ throw new Error("Retired native materialization changed during discovery review");
37970
+ }
37880
37971
  const combined = [...reviewedSources, ...reviewedSourcesWithManifestProjection];
37881
37972
  const unique = combined.filter((source, index) => combined.findIndex((candidate) => isDeepStrictEqual5(candidate, source)) === index);
37882
37973
  return { ...supplied, roots: [...new Set([...roots, ...supplied.roots])].sort(), sources: unique, ...agent === "gemini" ? { builtinNames } : {} };
@@ -37999,7 +38090,7 @@ var DISCOVERY_RUNTIME_COMMANDS, realpathOrNull = (path) => {
37999
38090
  } catch {
38000
38091
  return null;
38001
38092
  }
38002
- }, digest2 = (text2) => createHash24("sha256").update(text2).digest("hex");
38093
+ }, digest2 = (text2) => createHash24("sha256").update(text2).digest("hex"), CLAUDE_CONTROL_WITNESS_MODES, CLAUDE_CONTROL_FILES, ADOPTABLE_SETTINGS_MATCHES, CLAUDE_PROJECT_DISCOVERY_KEYS;
38003
38094
  var init_agent_discovery = __esm(() => {
38004
38095
  init_sumi_settings_witness();
38005
38096
  init_agent_policy_limits();
@@ -38029,6 +38120,10 @@ var init_agent_discovery = __esm(() => {
38029
38120
  init_codex_semantic_cache_witness();
38030
38121
  init_agent_discovery_directories();
38031
38122
  DISCOVERY_RUNTIME_COMMANDS = Object.freeze({ gemini: "gemini" });
38123
+ CLAUDE_CONTROL_WITNESS_MODES = new Set(["claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-settings-v4", "claude-marketplace-registry", "claude-marketplace-registry-v2", "claude-marketplace-entry-v1", "claude-plugin-registry"]);
38124
+ CLAUDE_CONTROL_FILES = new Set(["settings.json", "settings.local.json", "installed_plugins.json", "known_marketplaces.json"]);
38125
+ ADOPTABLE_SETTINGS_MATCHES = Object.freeze(["exact", "screen-reader-receipt"]);
38126
+ CLAUDE_PROJECT_DISCOVERY_KEYS = Object.freeze(["enabledPlugins", "extraKnownMarketplaces", "additionalMarketplaces", "skillOverrides"]);
38032
38127
  });
38033
38128
 
38034
38129
  // src/lib/authoring-path.ts
@@ -46318,7 +46413,11 @@ var exports_selected_run = {};
46318
46413
  __export(exports_selected_run, {
46319
46414
  resolveSelectedRun: () => resolveSelectedRun,
46320
46415
  prepareSelectedSecretBindings: () => prepareSelectedSecretBindings,
46321
- executeSelectedLocal: () => executeSelectedLocal
46416
+ instructionRunRefusal: () => instructionRunRefusal,
46417
+ instructionEntrypointMismatch: () => instructionEntrypointMismatch,
46418
+ instructionCommandEntry: () => instructionCommandEntry,
46419
+ executeSelectedLocal: () => executeSelectedLocal,
46420
+ declaredOperation: () => declaredOperation
46322
46421
  });
46323
46422
  import { createHash as createHash28 } from "crypto";
46324
46423
  import { spawn as spawn2 } from "child_process";
@@ -46331,12 +46430,51 @@ async function resolveSelectedRun(spec, profileId, options = {}) {
46331
46430
  const entries = await readSelectedEntries(selection2, context2, options);
46332
46431
  return { selection: selection2, profileId: context2.receipt.profile.profileId, entries, ...describeEntries(entries), cacheDir: selectionCacheRoot(options) };
46333
46432
  }
46433
+ function declaredOperation(manifest) {
46434
+ const entry = instructionCommandEntry(manifest);
46435
+ if (!entry)
46436
+ return null;
46437
+ const runtime = manifest.runtime;
46438
+ if (!runtime || typeof runtime !== "object" || Array.isArray(runtime))
46439
+ return null;
46440
+ if (runtime.runtime !== "bun" && runtime.runtime !== "node" && runtime.runtime !== "python3")
46441
+ return null;
46442
+ if (instructionEntrypointMismatch(manifest))
46443
+ return null;
46444
+ return { entry };
46445
+ }
46446
+ function instructionCommandEntry(manifest) {
46447
+ const commands = Array.isArray(manifest.commands) ? manifest.commands : [];
46448
+ const command = commands[0];
46449
+ if (!command || typeof command !== "object" || typeof command.entry !== "string" || command.entry.length === 0)
46450
+ return null;
46451
+ return command.entry;
46452
+ }
46453
+ function instructionEntrypointMismatch(manifest) {
46454
+ const entry = instructionCommandEntry(manifest);
46455
+ const declared = manifest.runtime;
46456
+ if (!entry || !declared || typeof declared !== "object" || Array.isArray(declared))
46457
+ return null;
46458
+ const runtimeEntrypoint = declared.entrypoint;
46459
+ return typeof runtimeEntrypoint === "string" && runtimeEntrypoint !== entry ? runtimeEntrypoint : null;
46460
+ }
46461
+ function instructionRunRefusal(manifest, isBundledFile) {
46462
+ const declaredEntry = instructionCommandEntry(manifest);
46463
+ if (declaredEntry && !isBundledFile(declaredEntry))
46464
+ return { code: "LOCAL_ENTRYPOINT_MISSING", message: `This selected instruction skill declares its operation entry as ${declaredEntry}, which is not a regular file in the selected bundle; a different runtime.entrypoint never substitutes for it.` };
46465
+ const mismatch = instructionEntrypointMismatch(manifest);
46466
+ if (mismatch)
46467
+ return { code: "LOCAL_ENTRYPOINT_MISMATCH", message: `This selected instruction skill declares its operation entry as ${declaredEntry}, but its runtime.entrypoint is ${mismatch}; the two must agree.` };
46468
+ if (!declaredOperation(manifest))
46469
+ return { code: "INSTRUCTION_SKILL", message: "This selected skill contains instructions. Use skills load instead of skills run." };
46470
+ return null;
46471
+ }
46334
46472
  async function prepareSelectedSecretBindings(selected, cwd = process.cwd()) {
46335
46473
  const entries = await readCachedSelection(selected.selection, { cacheDir: selected.cacheDir });
46336
46474
  if (!entries)
46337
46475
  throw new SkillSelectionError("CACHED_BUNDLE_MISSING", "The selected execution bundle is not cached.");
46338
46476
  const { kind, manifest } = describeEntries(entries);
46339
- if (kind !== "executable")
46477
+ if (kind !== "executable" && !declaredOperation(manifest))
46340
46478
  throw new SkillSelectionError("INSTRUCTION_SKILL", "Instruction skills do not accept execution secret bindings.");
46341
46479
  const names = declaredSecretNames(manifest.runtime?.env);
46342
46480
  if (!names.length)
@@ -46349,8 +46487,12 @@ async function executeSelectedLocal(selected, options = {}) {
46349
46487
  if (!entries)
46350
46488
  throw new SkillSelectionError("CACHED_BUNDLE_MISSING", "The selected local execution bundle is not cached.");
46351
46489
  const { kind, manifest, packageJson } = describeEntries(entries);
46352
- if (kind === "instruction")
46353
- throw new SkillSelectionError("INSTRUCTION_SKILL", "This selected skill contains instructions. Use skills load instead of skills run.");
46490
+ const operation = declaredOperation(manifest);
46491
+ if (kind === "instruction") {
46492
+ const refusal3 = instructionRunRefusal(manifest, (path) => entries.some((entry) => entry.path === path));
46493
+ if (refusal3)
46494
+ throw new SkillSelectionError(refusal3.code, refusal3.message);
46495
+ }
46354
46496
  const runtime = manifest.runtime ?? {};
46355
46497
  if (!runtime || typeof runtime !== "object" || Array.isArray(runtime))
46356
46498
  throw new SkillSelectionError("INVALID_SKILL_MANIFEST", "The selected runtime declaration is invalid.");
@@ -46369,9 +46511,9 @@ async function executeSelectedLocal(selected, options = {}) {
46369
46511
  const executable = runtimeName === "bun" ? process.execPath : Bun.which(runtimeName);
46370
46512
  if (!executable)
46371
46513
  throw new SkillSelectionError("LOCAL_RUNTIME_UNAVAILABLE", "The selected runtime is not installed on this station.");
46372
- const declaredEntry = runtime.entrypoint ?? (typeof packageJson.bin === "string" ? packageJson.bin : packageJson.bin && typeof packageJson.bin === "object" ? Object.values(packageJson.bin)[0] : undefined);
46514
+ const declaredEntry = (kind === "instruction" ? operation?.entry : undefined) ?? runtime.entrypoint ?? (typeof packageJson.bin === "string" ? packageJson.bin : packageJson.bin && typeof packageJson.bin === "object" ? Object.values(packageJson.bin)[0] : undefined);
46373
46515
  if (typeof declaredEntry !== "string" || !entries.some((entry) => entry.path === declaredEntry))
46374
- throw new SkillSelectionError("LOCAL_ENTRYPOINT_MISSING", "The selected bundle must declare a regular-file entrypoint in its runtime manifest or package bin.");
46516
+ throw new SkillSelectionError("LOCAL_ENTRYPOINT_MISSING", kind === "instruction" ? "The selected instruction skill must declare the operation entrypoint it ships in its manifest, and that declared entry must be a regular file in the selected bundle; a different runtime.entrypoint never substitutes for it." : "The selected bundle must declare a regular-file entrypoint in its runtime manifest or package bin.");
46375
46517
  const args = options.args ?? [];
46376
46518
  if (args.length > 128 || args.some((arg) => typeof arg !== "string" || arg.length > 16384 || arg.includes("\x00")))
46377
46519
  throw new SkillSelectionError("INVALID_RUN_INPUT", "Local skill arguments exceed their limits.");
@@ -46446,6 +46588,7 @@ async function executeSelectedLocal(selected, options = {}) {
46446
46588
  selection: selection2,
46447
46589
  target: "local",
46448
46590
  runtime: runtimeName,
46591
+ entrypoint: declaredEntry,
46449
46592
  inputDigest: `sha256:${createHash28("sha256").update(input2).digest("hex")}`,
46450
46593
  runDirectory,
46451
46594
  ...executionGrant ? { executionGrant } : {},
@@ -46892,13 +47035,18 @@ function registerOperationTools(server) {
46892
47035
  return mcpError("CONFLICTING_EXECUTION_MODES", "Use remote for the configured server catalog and quote, or target for selected execution, not both.");
46893
47036
  if (managed && !remote) {
46894
47037
  try {
46895
- const { resolveSelectedRun: resolveSelectedRun2, executeSelectedLocal: executeSelectedLocal2 } = await Promise.resolve().then(() => (init_selected_run(), exports_selected_run));
47038
+ const { resolveSelectedRun: resolveSelectedRun2, executeSelectedLocal: executeSelectedLocal2, instructionRunRefusal: instructionRunRefusal2 } = await Promise.resolve().then(() => (init_selected_run(), exports_selected_run));
46896
47039
  const resolved = await resolveSelectedRun2(name, selectedProfileId(), { projectDir: process.cwd() });
46897
- if (resolved.kind === "instruction")
46898
- return mcpError("INSTRUCTION_SKILL", "This selected skill contains instructions. Load it with skills load instead of running it.");
47040
+ if (resolved.kind === "instruction") {
47041
+ const refusal3 = instructionRunRefusal2(resolved.manifest, (path) => resolved.entries.some((entry) => entry.path === path));
47042
+ if (refusal3)
47043
+ return mcpError(refusal3.code, refusal3.message);
47044
+ }
46899
47045
  if (files?.length)
46900
47046
  return mcpError("SELECTED_INPUT_REQUIRED", "Selected executions require their declared JSON input; use the CLI cloud execution input contract for files.");
46901
47047
  if (target === "cloud") {
47048
+ if (resolved.kind === "instruction")
47049
+ return mcpError("INSTRUCTION_SKILL", "Cloud execution requires an executable skill version; run the declared operation locally on a managed station, or load the instructions with skills load.");
46902
47050
  if (args?.length || quoteReceipt || maxCredits !== undefined || maxCostCents !== undefined)
46903
47051
  return mcpError("SELECTED_INPUT_REQUIRED", "The selected cloud execution contract accepts declared JSON input, not legacy run arguments or quote receipts.");
46904
47052
  const { CloudExecutionClient: CloudExecutionClient2 } = await Promise.resolve().then(() => (init_cloud_executions(), exports_cloud_executions));
package/bin/migrate.js CHANGED
@@ -25,7 +25,7 @@ import { join as join5 } from "path";
25
25
  // package.json
26
26
  var package_default = {
27
27
  name: "@hasna/skills",
28
- version: "0.10.72",
28
+ version: "0.10.74",
29
29
  description: "Skills library for AI coding agents",
30
30
  type: "module",
31
31
  bin: {
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.72",
24143
+ version: "0.10.74",
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.72",
24146
+ version: "0.10.74",
24147
24147
  description: "Skills library for AI coding agents",
24148
24148
  type: "module",
24149
24149
  bin: {
@@ -0,0 +1,18 @@
1
+ import type { Command } from "commander";
2
+ import { type SelectionProvenanceReceipt } from "../../lib/selection-provenance.js";
3
+ export interface ProvenanceCommandOptions {
4
+ selectionProfile?: string;
5
+ station?: string;
6
+ session?: string;
7
+ cached?: boolean;
8
+ json?: boolean;
9
+ }
10
+ /**
11
+ * One read-only receipt for the selection chain: selected, activated, applied,
12
+ * pinned and consumed are named separately, and every consumer carries its
13
+ * status and reason. An exit code here only reports a bad request: a read that
14
+ * could not be answered is recorded as `unverified`, because that is the fact
15
+ * the caller came for.
16
+ */
17
+ export declare function provenanceLines(receipt: SelectionProvenanceReceipt): string[];
18
+ export declare function registerProvenance(parent: Command): void;