@hasna/skills 0.9.7 → 0.9.8

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/index.js CHANGED
@@ -36868,7 +36868,7 @@ var package_default;
36868
36868
  var init_package = __esm(() => {
36869
36869
  package_default = {
36870
36870
  name: "@hasna/skills",
36871
- version: "0.9.7",
36871
+ version: "0.9.8",
36872
36872
  description: "Skills library for AI coding agents",
36873
36873
  type: "module",
36874
36874
  bin: {
@@ -108256,11 +108256,33 @@ var init_codex_hook_trust_identity = __esm(() => {
108256
108256
  });
108257
108257
 
108258
108258
  // src/lib/agent-codex-trust.ts
108259
- import { lstatSync as lstatSync28, mkdirSync as mkdirSync25, readdirSync as readdirSync20 } from "fs";
108259
+ import { lstatSync as lstatSync28, mkdirSync as mkdirSync25, readdirSync as readdirSync20, readFileSync as readFileSync37, realpathSync as realpathSync13, statSync as statSync21 } from "fs";
108260
108260
  import { createHash as createHash26, randomUUID as randomUUID10 } from "crypto";
108261
- import { join as join56, resolve as resolve22 } from "path";
108261
+ import { execFileSync } from "child_process";
108262
+ import { dirname as dirname30, isAbsolute as isAbsolute25, join as join56, resolve as resolve22 } from "path";
108262
108263
  import { homedir as homedir11 } from "os";
108263
108264
  import { isDeepStrictEqual as isDeepStrictEqual6 } from "util";
108265
+ function verifyReleaseArtifact(proof, skillsCli) {
108266
+ need4(isAbsolute25(proof.packageTarPath) && !proof.packageTarPath.includes("\x00"), "RECONCILE_RELEASE_PROVENANCE_PATH");
108267
+ const tarPath = realpathSync13(proof.packageTarPath);
108268
+ need4(statSync21(tarPath).isFile(), "RECONCILE_RELEASE_PROVENANCE_PATH");
108269
+ const tarBytes = readFileSync37(tarPath);
108270
+ need4(tarBytes.byteLength === proof.packageTarBytes && createHash26("sha256").update(tarBytes).digest("hex") === proof.packageTarSha256, "RECONCILE_RELEASE_ARTIFACT_CHANGED");
108271
+ const packageRoot = dirname30(dirname30(realpathSync13(skillsCli.receipt.path)));
108272
+ const manifest = readFileSync37(join56(packageRoot, "package.json"));
108273
+ need4(createHash26("sha256").update(manifest).digest("hex") === skillsCli.receipt.manifestSha256 && skillsCli.receipt.manifestSha256 === proof.manifestSha256, "RECONCILE_RELEASE_MANIFEST_CHANGED");
108274
+ need4(createHash26("sha256").update(readFileSync37(skillsCli.receipt.path)).digest("hex") === proof.executableSha256 && skillsCli.receipt.sha256 === proof.executableSha256, "RECONCILE_RELEASE_EXECUTABLE_CHANGED");
108275
+ const members2 = [["package/package.json", manifest], ["package/bin/index.js", readFileSync37(skillsCli.receipt.path)]];
108276
+ for (const [member, expected] of members2) {
108277
+ let actual;
108278
+ try {
108279
+ actual = execFileSync("tar", ["-xOf", tarPath, member], { maxBuffer: 32 * 1024 * 1024 });
108280
+ } catch {
108281
+ throw new Error("RECONCILE_RELEASE_ARTIFACT_MEMBER_MISSING");
108282
+ }
108283
+ need4(Buffer.compare(actual, expected) === 0, "RECONCILE_RELEASE_ARTIFACT_MEMBER_CHANGED");
108284
+ }
108285
+ }
108264
108286
  async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc) {
108265
108287
  let rpc;
108266
108288
  try {
@@ -108283,26 +108305,54 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
108283
108305
  need4(makePlanDigest(digestInput) === intent.planDigest, "RECONCILE_PLAN_CHANGED");
108284
108306
  need4(isDeepStrictEqual6(intent.hooks, admitted.filter((hook) => !hook.enabled || hook.trustStatus !== "trusted")), "RECONCILE_INTENT_HOOKS_CHANGED");
108285
108307
  need4(configBefore.sha256 === intent.beforeSha256 && hooksBefore.sha256 === intent.hooksSha256 && policyBefore.sha256 === intent.policySha256, "RECONCILE_JOURNAL_CHANGED");
108308
+ const policyCurrent = snapshot(join56(dataDir3, "agent-policy.json"), true);
108309
+ let supersededBinding = false;
108310
+ if (policyCurrent.sha256 !== policyBefore.sha256) {
108311
+ need4(options.supersedeBinding === true, "RECONCILE_POLICY_CHANGED");
108312
+ const oldPolicy = JSON.parse(policyBefore.text), currentPolicy = JSON.parse(policyCurrent.text);
108313
+ need4(isDeepStrictEqual6(withoutCodexCommand(oldPolicy), withoutCodexCommand(currentPolicy)), "RECONCILE_POLICY_CHANGED");
108314
+ supersededBinding = true;
108315
+ }
108316
+ if (supersededBinding) {
108317
+ need4(Array.isArray(intent.nativeHooks) && intent.nativeHooks.length > 0, "RECONCILE_NATIVE_INVENTORY_REQUIRED");
108318
+ const proof = options.releaseProof;
108319
+ need4(!!proof && isAbsolute25(proof.packageTarPath) && /^[a-f0-9]{64}$/.test(proof.packageTarSha256) && Number.isInteger(proof.packageTarBytes) && proof.packageTarBytes > 0 && /^[a-f0-9]{64}$/.test(proof.manifestSha256) && /^[a-f0-9]{64}$/.test(proof.executableSha256), "RECONCILE_RELEASE_PROVENANCE_REQUIRED");
108320
+ }
108286
108321
  const currentConfig = snapshot(intent.configPath, true);
108287
108322
  const managedKeys = intent.hooks.map((h) => h.key);
108288
- need4(codexTrustReconcileWitness(configBefore.text, managedKeys) === codexTrustReconcileWitness(currentConfig.text, managedKeys), "RECONCILE_UNRELATED_CONFIG_CHANGED");
108289
- need4(codexTrustUnmanagedSemantic(configBefore.text, managedKeys) === codexTrustUnmanagedSemantic(currentConfig.text, managedKeys), "RECONCILE_UNRELATED_CONFIG_CHANGED");
108323
+ const witnessSame = codexTrustReconcileWitness(configBefore.text, managedKeys) === codexTrustReconcileWitness(currentConfig.text, managedKeys);
108324
+ const unmanagedSame = codexTrustUnmanagedSemantic(configBefore.text, managedKeys) === codexTrustUnmanagedSemantic(currentConfig.text, managedKeys);
108325
+ need4(unmanagedSame && (witnessSame || supersededBinding), "RECONCILE_UNRELATED_CONFIG_CHANGED");
108290
108326
  const before2 = structure(configBefore.text), current = structure(currentConfig.text), expectedConfig = structuredClone(before2);
108291
108327
  expectedConfig.hooks ??= {};
108292
108328
  expectedConfig.hooks.state ??= {};
108293
108329
  for (const hook of intent.hooks)
108294
108330
  expectedConfig.hooks.state[hook.key] = { ...expectedConfig.hooks.state[hook.key], enabled: true, trusted_hash: hook.currentHash };
108295
- need4(isDeepStrictEqual6(expectedConfig, current), "RECONCILE_CONFIG_DRIFT");
108296
- const policyCurrent = snapshot(join56(dataDir3, "agent-policy.json"), true);
108297
- need4(policyCurrent.sha256 === policyBefore.sha256, "RECONCILE_POLICY_CHANGED");
108298
- const policy = parseManagedSkillPolicy(policyBefore.text), binding = policy.bridge;
108331
+ if (supersededBinding) {
108332
+ for (const hook of intent.hooks) {
108333
+ const state = current.hooks?.state?.[hook.key];
108334
+ need4(state?.enabled === true && typeof state.trusted_hash === "string" && /^sha256:[a-f0-9]{64}$/.test(state.trusted_hash), "RECONCILE_CONFIG_DRIFT");
108335
+ }
108336
+ } else
108337
+ need4(isDeepStrictEqual6(expectedConfig, current), "RECONCILE_CONFIG_DRIFT");
108338
+ const policy = parseManagedSkillPolicy(policyCurrent.text), binding = policy.bridge;
108299
108339
  assertManagedAgentBridge("codex", { home, dataDir: dataDir3, projectDir: home });
108300
108340
  const skillsCli = bindSkillsCli(binding.commands.codex, options.reviewedSkillsCli);
108301
- need4(isDeepStrictEqual6(skillsCli.receipt, intent.skillsCli), "RECONCILE_SKILLS_BINDING_CHANGED");
108341
+ if (supersededBinding)
108342
+ verifyReleaseArtifact(options.releaseProof, skillsCli);
108343
+ const skillsBindingChanged = !isDeepStrictEqual6(skillsCli.receipt, intent.skillsCli);
108344
+ need4(!skillsBindingChanged || supersededBinding, "RECONCILE_SKILLS_BINDING_CHANGED");
108302
108345
  const alias = binding.rootAliases?.find((item) => item.agent === "codex"), codexHome = alias?.target ?? join56(home, ".codex"), hooksPath = join56(codexHome, "hooks.json");
108303
108346
  need4(resolve22(intent.configPath) === resolve22(join56(codexHome, "config.toml")), "RECONCILE_CONFIG_PATH_CHANGED");
108304
108347
  const hooksCurrent = snapshot(hooksPath);
108305
- need4(hooksCurrent.sha256 === intent.hooksSha256 || hooksCurrent.sha256 === hooksBefore.sha256, "RECONCILE_HOOKS_CHANGED");
108348
+ if (hooksCurrent.sha256 !== intent.hooksSha256 && hooksCurrent.sha256 !== hooksBefore.sha256) {
108349
+ need4(supersededBinding, "RECONCILE_HOOKS_CHANGED");
108350
+ const currentDeclarations = JSON.parse(hooksCurrent.text).hooks;
108351
+ for (const [event, [nativeEvent]] of Object.entries(eventNames)) {
108352
+ const command = renderAgentHookCommand(binding.commands.codex, "codex", binding.profiles.codex, event);
108353
+ need4(isDeepStrictEqual6(currentDeclarations?.[event], [{ hooks: [{ type: "command", command, timeout: 15 }] }]), "RECONCILE_BINDING_HOOK_CHANGED");
108354
+ }
108355
+ }
108306
108356
  rpc = await connect({ command: options.codexCommand ?? "codex", home, codexHome });
108307
108357
  need4(rpc.version === intent.nativeVersion, "RECONCILE_NATIVE_VERSION_CHANGED");
108308
108358
  const discovered = await rpc.request("hooks/list", { cwds: [home] });
@@ -108312,12 +108362,15 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
108312
108362
  if (recordedNativeHooks)
108313
108363
  need4(nativeHooks.length === recordedNativeHooks.length, "RECONCILE_NATIVE_HOOK_LIST_CHANGED");
108314
108364
  const plannedKeys = new Set(intent.hooks.map((hook) => hook.key));
108365
+ let nativeTrustPending = false;
108315
108366
  for (const expected of recordedNativeHooks ?? []) {
108316
108367
  const matches2 = nativeHooks.filter((hook) => hook.key === expected.key);
108317
108368
  need4(matches2.length === 1, "RECONCILE_NATIVE_HOOK_LIST_CHANGED");
108318
108369
  const actual = safeNativeHook(matches2[0]);
108319
108370
  const stable = (hook) => {
108320
108371
  const { enabled, trustStatus, ...identity4 } = hook;
108372
+ if (supersededBinding)
108373
+ delete identity4.command;
108321
108374
  return identity4;
108322
108375
  };
108323
108376
  need4(isDeepStrictEqual6(stable(actual), stable(expected)), "RECONCILE_NATIVE_IDENTITY_CHANGED");
@@ -108328,7 +108381,14 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
108328
108381
  const matches2 = nativeHooks.filter((h2) => h2.key === wanted.key || h2.key === wanted.key.replace(join56(home, ".codex/hooks.json"), hooksPath));
108329
108382
  need4(matches2.length === 1, "RECONCILE_NATIVE_IDENTITY_CHANGED");
108330
108383
  const h = matches2[0];
108331
- need4(h.enabled === true && h.trustStatus === "trusted" && h.currentHash === wanted.currentHash, "RECONCILE_NATIVE_STATE_INCOMPLETE");
108384
+ const trusted = h.trustStatus === "trusted";
108385
+ need4(h.enabled === true && (trusted || supersededBinding && h.trustStatus === "modified") && (h.currentHash === wanted.currentHash || supersededBinding && /^sha256:[a-f0-9]{64}$/.test(h.currentHash)), "RECONCILE_NATIVE_STATE_INCOMPLETE");
108386
+ if (!trusted)
108387
+ nativeTrustPending = true;
108388
+ if (supersededBinding) {
108389
+ const nativeToCliEvent = { userPromptSubmit: "UserPromptSubmit", sessionStart: "SessionStart", subagentStart: "SubagentStart" };
108390
+ need4(h.command === renderAgentHookCommand(binding.commands.codex, "codex", binding.profiles.codex, nativeToCliEvent[wanted.event] ?? ""), "RECONCILE_BINDING_HOOK_CHANGED");
108391
+ }
108332
108392
  }
108333
108393
  const config2 = await rpc.request("config/read", { includeLayers: true, cwd: home });
108334
108394
  const layers = config2?.layers?.filter((layer) => layer.name?.type === "user" && layer.name.file === intent.configPath && !layer.name.profile);
@@ -108348,7 +108408,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
108348
108408
  unchanged2(currentConfig);
108349
108409
  skillsCli.recheck();
108350
108410
  assertManagedAgentBridge("codex", { home, dataDir: dataDir3, projectDir: home });
108351
- const receipt = { version: 1, status: "reconciled", reconciled: true, automaticRollback: false, journal, planDigest: intent.planDigest, nativeVersion: rpc.version, nativeStateVerified: true, nativeExecutionVerified: false, configSha256: currentConfig.sha256, hooksSha256: hooksCurrent.sha256, policySha256: policyBefore.sha256, skillsCli: skillsCli.receipt, nativeInventoryScope: recordedNativeHooks ? "complete" : "declared-managed-hooks-only", unrelatedNativeHooksPreserved: !!recordedNativeHooks, unrelatedSettingsAndCommentsPreserved: true };
108411
+ const receipt = { version: 1, status: supersededBinding ? "superseded" : "reconciled", reconciled: true, supersededBinding, automaticRollback: false, journal, planDigest: intent.planDigest, nativeVersion: rpc.version, nativeStateVerified: !nativeTrustPending, nativeTrustPending, nativeExecutionVerified: false, configSha256: currentConfig.sha256, hooksSha256: hooksCurrent.sha256, policySha256: policyBefore.sha256, policyBeforeSha256: policyBefore.sha256, policyCurrentSha256: policyCurrent.sha256, previousSkillsCli: intent.skillsCli, skillsCli: skillsCli.receipt, releaseProof: options.releaseProof, nativeInventoryScope: recordedNativeHooks ? "complete" : "declared-managed-hooks-only", unrelatedNativeHooksPreserved: !!recordedNativeHooks, unrelatedSettingsAndCommentsPreserved: true };
108352
108412
  save2(join56(journal, "receipt.json"), json2(receipt));
108353
108413
  return receipt;
108354
108414
  } catch (error61) {
@@ -108478,7 +108538,12 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
108478
108538
  }
108479
108539
  }
108480
108540
  var json2 = (value) => JSON.stringify(value, null, 2) + `
108481
- `, structure = (text3) => JSON.parse(JSON.stringify(Bun.TOML.parse(text3))), eventNames, makePlanDigest = (value) => createHash26("sha256").update(json2(value)).digest("hex"), safeNativeHook = (hook) => ({ key: hook.key, eventName: hook.eventName, handlerType: hook.handlerType, command: hook.command, matcher: hook.matcher, timeoutSec: hook.timeoutSec, async: hook.async, statusMessage: hook.statusMessage, additionalContextLimit: hook.additionalContextLimit, sourcePath: hook.sourcePath, source: hook.source, pluginId: hook.pluginId, isManaged: hook.isManaged, currentHash: hook.currentHash, enabled: hook.enabled, trustStatus: hook.trustStatus });
108541
+ `, structure = (text3) => JSON.parse(JSON.stringify(Bun.TOML.parse(text3))), eventNames, makePlanDigest = (value) => createHash26("sha256").update(json2(value)).digest("hex"), safeNativeHook = (hook) => ({ key: hook.key, eventName: hook.eventName, handlerType: hook.handlerType, command: hook.command, matcher: hook.matcher, timeoutSec: hook.timeoutSec, async: hook.async, statusMessage: hook.statusMessage, additionalContextLimit: hook.additionalContextLimit, sourcePath: hook.sourcePath, source: hook.source, pluginId: hook.pluginId, isManaged: hook.isManaged, currentHash: hook.currentHash, enabled: hook.enabled, trustStatus: hook.trustStatus }), withoutCodexCommand = (value) => {
108542
+ const copy = structuredClone(value);
108543
+ if (copy?.bridge?.commands && Object.hasOwn(copy.bridge.commands, "codex"))
108544
+ copy.bridge.commands.codex = "<reviewed-codex-binding>";
108545
+ return copy;
108546
+ };
108482
108547
  var init_agent_codex_trust = __esm(() => {
108483
108548
  init_config();
108484
108549
  init_managed_policy();
@@ -108571,8 +108636,8 @@ var exports_agent_integration = {};
108571
108636
  __export(exports_agent_integration, {
108572
108637
  registerAgentIntegration: () => registerAgentIntegration
108573
108638
  });
108574
- import { readFileSync as readFileSync37 } from "fs";
108575
- import { isAbsolute as isAbsolute25 } from "path";
108639
+ import { readFileSync as readFileSync38 } from "fs";
108640
+ import { isAbsolute as isAbsolute26 } from "path";
108576
108641
  function pinnedHookProfile(input3, configuredProfile) {
108577
108642
  try {
108578
108643
  const parsed = parseSkillContextInput(JSON.stringify(input3));
@@ -108643,7 +108708,7 @@ function registerAgentIntegration(parent) {
108643
108708
  });
108644
108709
  hook.command("install").option("--agent <agent>", `Agent to configure: ${INTEGRATION_AGENTS.join(", ")}, all`, "all").option("--command <path>", "Skills executable used by the hook (preserves existing binding; new agents use skills)").option("--selection-profile <id>", "Selection profile (preserves existing binding; new agents use default)").option("--include-vendor", "Retained for compatibility; vendor system skills are always inventoried and disabled", false).option("--discovery-inputs <file>", "Advanced reviewed active plugin roots and source hashes for unsupported registrations").option("--allow-root-aliases", "Allow home .claude/.codex aliases to existing directories within this home", false).option("--apply", "Apply the plan, preserving prior configuration in private backups", false).option("--json", "Output a receipt as JSON", false).description("Plan or install one Skills CLI bridge plus native prompt hooks").action(async (options) => {
108645
108710
  try {
108646
- const discoveryInputs = options.discoveryInputs ? JSON.parse(readFileSync37(options.discoveryInputs, "utf8")) : undefined;
108711
+ const discoveryInputs = options.discoveryInputs ? JSON.parse(readFileSync38(options.discoveryInputs, "utf8")) : undefined;
108647
108712
  const plan = planAgentIntegration({ agents: agents(options.agent), command: options.command, profileId: options.selectionProfile, includeVendor: options.includeVendor, discoveryInputs, allowRootAliases: options.allowRootAliases });
108648
108713
  const result2 = options.apply ? applyAgentIntegration(plan) : { changed: [], backups: [] };
108649
108714
  const receipt = { applied: options.apply, planned: plan.changes.map((change) => change.path), ...result2, rootAliases: plan.rootAliases ?? [], discovery: plan.discoveryAfter, nativeSkills: plan.nativeSkills.map((entry) => ({ agent: entry.agent, path: entry.path, managed: entry.managed, vendor: entry.vendor, system: entry.system === true, bridge: entry.bridge === true })), requiresNativeRetirement: plan.nativeSkills.some((entry) => !entry.bridge && !entry.system) };
@@ -108672,11 +108737,15 @@ function registerAgentIntegration(parent) {
108672
108737
  process.exitCode = 1;
108673
108738
  }
108674
108739
  });
108675
- trust.command("reconcile").requiredOption("--agent <agent>", "Native trust adapter (codex)").requiredOption("--journal <path>", "Private incomplete native trust journal to inspect").option("--codex-command <path>", "Installed Codex executable used for its native configuration API", "codex").option("--json", "Output the reconciliation receipt", false).description("Verify an interrupted Codex native trust write and resolve its private journal").action(async (options) => {
108740
+ trust.command("reconcile").requiredOption("--agent <agent>", "Native trust adapter (codex)").requiredOption("--journal <path>", "Private incomplete native trust journal to inspect").option("--codex-command <path>", "Installed Codex executable used for its native configuration API", "codex").option("--supersede-binding", "Resolve a stale journal only when current policy differs solely by the reviewed Codex CLI binding", false).option("--skills-package-tar <path>", "Exact package tarball consumed by the reviewed executable").option("--skills-package-tar-sha256 <sha256>", "SHA-256 of the reviewed Skills package tarball").option("--skills-package-tar-bytes <bytes>", "Byte length of the reviewed Skills package tarball").option("--skills-package-manifest-sha256 <sha256>", "SHA-256 of the reviewed Skills package manifest").option("--skills-executable-sha256 <sha256>", "SHA-256 of the reviewed running Skills executable").option("--json", "Output the reconciliation receipt", false).description("Verify an interrupted Codex native trust write and resolve its private journal").action(async (options) => {
108676
108741
  try {
108677
108742
  if (options.agent !== "codex")
108678
108743
  throw new Error("Native trust reconciliation currently supports --agent codex only");
108679
- const result2 = await reconcileCodexNativeHooks({ journal: options.journal, codexCommand: options.codexCommand });
108744
+ const proofFields = [options.skillsPackageTar, options.skillsPackageTarSha256, options.skillsPackageTarBytes, options.skillsPackageManifestSha256, options.skillsExecutableSha256];
108745
+ if (proofFields.some((value) => value !== undefined) && proofFields.some((value) => value === undefined))
108746
+ throw new Error("Release provenance requires package tar path, tar hash/bytes, manifest hash, and executable hash together");
108747
+ const releaseProof = proofFields.every((value) => value !== undefined) ? { packageTarPath: options.skillsPackageTar, packageTarSha256: options.skillsPackageTarSha256, packageTarBytes: Number(options.skillsPackageTarBytes), manifestSha256: options.skillsPackageManifestSha256, executableSha256: options.skillsExecutableSha256 } : undefined;
108748
+ const result2 = await reconcileCodexNativeHooks({ journal: options.journal, codexCommand: options.codexCommand, supersedeBinding: options.supersedeBinding, releaseProof });
108680
108749
  if (options.json)
108681
108750
  await writeCliOutput(JSON.stringify(result2));
108682
108751
  else
@@ -108693,7 +108762,7 @@ function registerAgentIntegration(parent) {
108693
108762
  try {
108694
108763
  if (agents(options.agent).length !== 1)
108695
108764
  throw new Error("A hook invocation requires one agent");
108696
- const inputText = readFileSync37(0, "utf8");
108765
+ const inputText = readFileSync38(0, "utf8");
108697
108766
  if (inputText.length > 1024 * 1024)
108698
108767
  throw new Error("Hook input is too large");
108699
108768
  let input3 = JSON.parse(inputText);
@@ -108706,12 +108775,12 @@ function registerAgentIntegration(parent) {
108706
108775
  input3.hook_event_name = event;
108707
108776
  const projects = [process.cwd()];
108708
108777
  if (input3.cwd !== undefined) {
108709
- if (typeof input3.cwd !== "string" || !isAbsolute25(input3.cwd) || input3.cwd.includes("\x00"))
108778
+ if (typeof input3.cwd !== "string" || !isAbsolute26(input3.cwd) || input3.cwd.includes("\x00"))
108710
108779
  throw new Error("Invalid native hook working directory");
108711
108780
  projects.push(input3.cwd);
108712
108781
  }
108713
108782
  if (options.agent === "cursor") {
108714
- if (!Array.isArray(input3.workspace_roots) || input3.workspace_roots.length > 32 || input3.workspace_roots.some((path) => typeof path !== "string" || !isAbsolute25(path) || path.includes("\x00")))
108783
+ if (!Array.isArray(input3.workspace_roots) || input3.workspace_roots.length > 32 || input3.workspace_roots.some((path) => typeof path !== "string" || !isAbsolute26(path) || path.includes("\x00")))
108715
108784
  throw new Error("Invalid Cursor workspace roots");
108716
108785
  projects.push(...input3.workspace_roots);
108717
108786
  input3.cwd ??= input3.workspace_roots[0] ?? process.cwd();
@@ -108789,7 +108858,7 @@ function registerAgentIntegration(parent) {
108789
108858
  const migrate = parent.command("migrate").description("Preserve and retire native agent skill copies");
108790
108859
  migrate.command("native").option("--project <directory>", "Also inventory a project and its ancestors (the current directory and its ancestors are always included)").option("--include-unmanaged", "Archive user-authored skills as well as Skills-managed copies", false).option("--include-vendor", "Retire vendor SKILL.md discovery files while preserving plugin scripts and assets", false).option("--target-manifest <file>", "Reviewed exact native migration target manifest").option("--discovery-inputs <file>", "Advanced reviewed active plugin roots and source hashes").option("--allow-root-aliases", "Allow home .claude/.codex aliases to existing directories within this home", false).option("--apply", "Move selected skills to private archives outside agent discovery roots", false).option("--json", "Output inventory and archive receipt as JSON", false).description("Inventory native skill copies; preserve complete directories before retiring them").action(async (options) => {
108791
108860
  try {
108792
- const discoveryInputs = options.discoveryInputs ? JSON.parse(readFileSync37(options.discoveryInputs, "utf8")) : undefined;
108861
+ const discoveryInputs = options.discoveryInputs ? JSON.parse(readFileSync38(options.discoveryInputs, "utf8")) : undefined;
108793
108862
  if (options.targetManifest && (options.includeUnmanaged || options.includeVendor))
108794
108863
  throw new Error("--target-manifest cannot be combined with broad native migration selectors");
108795
108864
  const targetManifest = options.targetManifest ? readNativeMigrationTargetManifest(options.targetManifest) : undefined;
@@ -109094,11 +109163,11 @@ import {
109094
109163
  copyFileSync as copyFileSync2,
109095
109164
  mkdirSync as mkdirSync26,
109096
109165
  readdirSync as readdirSync21,
109097
- readFileSync as readFileSync38,
109098
- statSync as statSync21,
109166
+ readFileSync as readFileSync39,
109167
+ statSync as statSync22,
109099
109168
  writeFileSync as writeFileSync26
109100
109169
  } from "fs";
109101
- import { dirname as dirname30, join as join57, resolve as resolve23, sep as sep10 } from "path";
109170
+ import { dirname as dirname31, join as join57, resolve as resolve23, sep as sep10 } from "path";
109102
109171
  function fail5(code2, message, detail = []) {
109103
109172
  throw new StationSnapshotError(code2, message, detail);
109104
109173
  }
@@ -109110,7 +109179,7 @@ function readSnapshotManifest(repoRoot, stationId) {
109110
109179
  const manifestPath = join57(snapshotRoot, "sync-manifest.json");
109111
109180
  let manifest;
109112
109181
  try {
109113
- manifest = JSON.parse(readFileSync38(manifestPath, "utf8"));
109182
+ manifest = JSON.parse(readFileSync39(manifestPath, "utf8"));
109114
109183
  } catch (error61) {
109115
109184
  fail5("MANIFEST_UNREADABLE", `cannot read snapshot manifest: ${manifestPath}: ${error61.message}`);
109116
109185
  }
@@ -109192,7 +109261,7 @@ function planStationHydration(stationId, repoRoot) {
109192
109261
  });
109193
109262
  continue;
109194
109263
  }
109195
- const info = statSync21(entry.fullPath);
109264
+ const info = statSync22(entry.fullPath);
109196
109265
  const manifestHash = manifestHashes.get(`${agent}${MANIFEST_HASH_KEY_SEP}${homeRelative}`) ?? null;
109197
109266
  let verified = false;
109198
109267
  if (manifestHash !== null) {
@@ -109246,7 +109315,7 @@ function planStationHydration(stationId, repoRoot) {
109246
109315
  for (const copy of copies) {
109247
109316
  let isStub = false;
109248
109317
  try {
109249
- isStub = isPointerSkillMd(readFileSync38(copy.fullPath, "utf8"));
109318
+ isStub = isPointerSkillMd(readFileSync39(copy.fullPath, "utf8"));
109250
109319
  } catch {
109251
109320
  isStub = false;
109252
109321
  }
@@ -109351,7 +109420,7 @@ function writeStationHydration(options) {
109351
109420
  }
109352
109421
  let written = 0;
109353
109422
  for (const entry of toWrite) {
109354
- mkdirSync26(dirname30(entry.destination), { recursive: true });
109423
+ mkdirSync26(dirname31(entry.destination), { recursive: true });
109355
109424
  copyFileSync2(entry.fullPath, entry.destination);
109356
109425
  written += 1;
109357
109426
  }
@@ -109372,8 +109441,8 @@ function writeStationHydration(options) {
109372
109441
  },
109373
109442
  skills: resultSkills
109374
109443
  };
109375
- const hydrationManifestPath = join57(dirname30(cacheRoot), `hydration-${options.stationId}.json`);
109376
- mkdirSync26(dirname30(hydrationManifestPath), { recursive: true });
109444
+ const hydrationManifestPath = join57(dirname31(cacheRoot), `hydration-${options.stationId}.json`);
109445
+ mkdirSync26(dirname31(hydrationManifestPath), { recursive: true });
109377
109446
  writeFileSync26(hydrationManifestPath, `${JSON.stringify(hydration, null, 2)}
109378
109447
  `);
109379
109448
  return {
@@ -109450,9 +109519,9 @@ var init_hydrate = __esm(() => {
109450
109519
 
109451
109520
  // src/lib/prepare-skill.ts
109452
109521
  import { createHash as createHash28, randomUUID as randomUUID11 } from "crypto";
109453
- import { chmodSync as chmodSync3, existsSync as existsSync34, lstatSync as lstatSync29, mkdirSync as mkdirSync27, mkdtempSync as mkdtempSync6, readFileSync as readFileSync39, readdirSync as readdirSync22, renameSync as renameSync12, rmSync as rmSync9, writeFileSync as writeFileSync27 } from "fs";
109522
+ import { chmodSync as chmodSync3, existsSync as existsSync34, lstatSync as lstatSync29, mkdirSync as mkdirSync27, mkdtempSync as mkdtempSync6, readFileSync as readFileSync40, readdirSync as readdirSync22, renameSync as renameSync12, rmSync as rmSync9, writeFileSync as writeFileSync27 } from "fs";
109454
109523
  import { tmpdir as tmpdir2 } from "os";
109455
- import { dirname as dirname31, isAbsolute as isAbsolute26, join as join58, relative as relative9, resolve as resolve24, sep as sep11 } from "path";
109524
+ import { dirname as dirname32, isAbsolute as isAbsolute27, join as join58, relative as relative9, resolve as resolve24, sep as sep11 } from "path";
109456
109525
  function prepareSkill(name, options) {
109457
109526
  const normalized = normalizePortableSkillName(name);
109458
109527
  if (!new RegExp(SEMVER_PATTERN).test(options.version))
@@ -109497,7 +109566,7 @@ function prepareSkill(name, options) {
109497
109566
  mkdirSync27(join58(staging, directory2), { recursive: true });
109498
109567
  for (const file2 of before2.files) {
109499
109568
  const destination = join58(staging, file2.path);
109500
- mkdirSync27(dirname31(destination), { recursive: true });
109569
+ mkdirSync27(dirname32(destination), { recursive: true });
109501
109570
  writeFileSync27(destination, file2.bytes, { mode: file2.mode });
109502
109571
  }
109503
109572
  const stagedManifest = join58(staging, "skill.json");
@@ -109558,7 +109627,7 @@ function validateRuntimeEntrypoint(candidate2, root2) {
109558
109627
  if (typeof entry !== "string")
109559
109628
  return;
109560
109629
  const target = resolve24(root2, entry), rel = relative9(root2, target);
109561
- if (isAbsolute26(entry) || /^[a-zA-Z]:/.test(entry) || entry.includes("\\") || !rel || rel === ".." || rel.startsWith(`..${sep11}`) || isAbsolute26(rel) || !existsSync34(target) || !lstatSync29(target).isFile()) {
109630
+ if (isAbsolute27(entry) || /^[a-zA-Z]:/.test(entry) || entry.includes("\\") || !rel || rel === ".." || rel.startsWith(`..${sep11}`) || isAbsolute27(rel) || !existsSync34(target) || !lstatSync29(target).isFile()) {
109562
109631
  throw new Error("Executable runtime.entrypoint must name an existing regular file inside the skill directory.");
109563
109632
  }
109564
109633
  }
@@ -109669,7 +109738,7 @@ function snapshot2(root2) {
109669
109738
  bytes += stat3.size;
109670
109739
  if (bytes > 50000000)
109671
109740
  throw new Error("Skill exceeds the preparation size limit (50000000 bytes).");
109672
- const content = readFileSync39(absolute);
109741
+ const content = readFileSync40(absolute);
109673
109742
  if (content.length !== stat3.size)
109674
109743
  throw new Error("Skill changed during preparation.");
109675
109744
  files.push({ path, bytes: content, mode: stat3.mode & 511 });
@@ -110071,7 +110140,7 @@ var init_schedule = __esm(() => {
110071
110140
 
110072
110141
  // src/lib/registry-sync.ts
110073
110142
  import { mkdirSync as mkdirSync28, writeFileSync as writeFileSync28 } from "fs";
110074
- import { dirname as dirname32, relative as relative10 } from "path";
110143
+ import { dirname as dirname33, relative as relative10 } from "path";
110075
110144
  function createRegistrySyncArtifact(options = {}) {
110076
110145
  const profile = options.profile ?? "all";
110077
110146
  const includeDocs = options.includeDocs ?? true;
@@ -110128,7 +110197,7 @@ function createRegistrySyncArtifact(options = {}) {
110128
110197
  };
110129
110198
  }
110130
110199
  function writeRegistrySyncArtifact(path, artifact) {
110131
- mkdirSync28(dirname32(path), { recursive: true });
110200
+ mkdirSync28(dirname33(path), { recursive: true });
110132
110201
  writeFileSync28(path, `${JSON.stringify(artifact, null, 2)}
110133
110202
  `);
110134
110203
  }
@@ -110349,8 +110418,8 @@ __export(exports_publish, {
110349
110418
  buildVersionManifest: () => buildVersionManifest,
110350
110419
  PushSkillError: () => PushSkillError
110351
110420
  });
110352
- import { execFileSync } from "child_process";
110353
- import { existsSync as existsSync35, readFileSync as readFileSync40 } from "fs";
110421
+ import { execFileSync as execFileSync2 } from "child_process";
110422
+ import { existsSync as existsSync35, readFileSync as readFileSync41 } from "fs";
110354
110423
  import { hostname as hostname4 } from "os";
110355
110424
  import { join as join59 } from "path";
110356
110425
  function registerPublish(parent) {
@@ -110405,7 +110474,7 @@ async function pushSkill(name, options = {}) {
110405
110474
  await verifyPackedCanonicalHash(skill.name, packed);
110406
110475
  const versionManifest = buildVersionManifest(skill.path, packed);
110407
110476
  const skillMdPath = join59(skill.path, "SKILL.md");
110408
- const skillMd = existsSync35(skillMdPath) ? readFileSync40(skillMdPath, "utf-8") : undefined;
110477
+ const skillMd = existsSync35(skillMdPath) ? readFileSync41(skillMdPath, "utf-8") : undefined;
110409
110478
  const base3 = {
110410
110479
  slug: skill.name,
110411
110480
  path: skill.path,
@@ -110562,7 +110631,7 @@ function sanitizeGitRemote(url2) {
110562
110631
  }
110563
110632
  function gitValue(dir, args2) {
110564
110633
  try {
110565
- const out = execFileSync("git", ["-C", dir, ...args2], { stdio: ["ignore", "pipe", "ignore"], encoding: "utf-8" }).trim();
110634
+ const out = execFileSync2("git", ["-C", dir, ...args2], { stdio: ["ignore", "pipe", "ignore"], encoding: "utf-8" }).trim();
110566
110635
  return out || null;
110567
110636
  } catch {
110568
110637
  return null;
@@ -112026,11 +112095,11 @@ var init_storage2 = __esm(() => {
112026
112095
  });
112027
112096
 
112028
112097
  // src/lib/registry-reconcile.ts
112029
- import { existsSync as existsSync36, readFileSync as readFileSync41, statSync as statSync22, writeFileSync as writeFileSync29 } from "fs";
112098
+ import { existsSync as existsSync36, readFileSync as readFileSync42, statSync as statSync23, writeFileSync as writeFileSync29 } from "fs";
112030
112099
  import { join as join60 } from "path";
112031
112100
  function isDirectory2(path) {
112032
112101
  try {
112033
- return statSync22(path).isDirectory();
112102
+ return statSync23(path).isDirectory();
112034
112103
  } catch {
112035
112104
  return false;
112036
112105
  }
@@ -112046,7 +112115,7 @@ function readBaseline(skillDir) {
112046
112115
  if (!existsSync36(markerPath))
112047
112116
  return;
112048
112117
  try {
112049
- const marker = JSON.parse(readFileSync41(markerPath, "utf-8"));
112118
+ const marker = JSON.parse(readFileSync42(markerPath, "utf-8"));
112050
112119
  if (!isSkillsOwnershipMarker(marker))
112051
112120
  return;
112052
112121
  return {
@@ -112062,7 +112131,7 @@ function readCursor(root2) {
112062
112131
  if (!existsSync36(path))
112063
112132
  return { runCount: 0 };
112064
112133
  try {
112065
- const cursor2 = JSON.parse(readFileSync41(path, "utf-8"));
112134
+ const cursor2 = JSON.parse(readFileSync42(path, "utf-8"));
112066
112135
  return { runCount: typeof cursor2.runCount === "number" ? cursor2.runCount : 0 };
112067
112136
  } catch {
112068
112137
  return { runCount: 0 };
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.9.7",
20022
+ version: "0.9.8",
20023
20023
  description: "Skills library for AI coding agents",
20024
20024
  type: "module",
20025
20025
  bin: {
package/bin/migrate.js CHANGED
@@ -7,7 +7,7 @@ import { join as join5 } from "path";
7
7
  // package.json
8
8
  var package_default = {
9
9
  name: "@hasna/skills",
10
- version: "0.9.7",
10
+ version: "0.9.8",
11
11
  description: "Skills library for AI coding agents",
12
12
  type: "module",
13
13
  bin: {
package/bin/server.js CHANGED
@@ -24122,7 +24122,7 @@ var init_dist_es9 = __esm(() => {
24122
24122
  // package.json
24123
24123
  var package_default = {
24124
24124
  name: "@hasna/skills",
24125
- version: "0.9.7",
24125
+ version: "0.9.8",
24126
24126
  description: "Skills library for AI coding agents",
24127
24127
  type: "module",
24128
24128
  bin: {
package/bin/worker.js CHANGED
@@ -24125,7 +24125,7 @@ import { randomUUID as randomUUID6 } from "crypto";
24125
24125
  // package.json
24126
24126
  var package_default = {
24127
24127
  name: "@hasna/skills",
24128
- version: "0.9.7",
24128
+ version: "0.9.8",
24129
24129
  description: "Skills library for AI coding agents",
24130
24130
  type: "module",
24131
24131
  bin: {
package/dist/index.js CHANGED
@@ -11448,7 +11448,7 @@ import { isAbsolute as isAbsolute10, join as join20, resolve as resolve10 } from
11448
11448
  // package.json
11449
11449
  var package_default = {
11450
11450
  name: "@hasna/skills",
11451
- version: "0.9.7",
11451
+ version: "0.9.8",
11452
11452
  description: "Skills library for AI coding agents",
11453
11453
  type: "module",
11454
11454
  bin: {
@@ -8,12 +8,21 @@ export interface CodexNativeHookTrustOptions {
8
8
  reviewedPlanDigest?: string;
9
9
  reviewedSkillsCli?: ReviewedSkillsCli;
10
10
  }
11
+ export interface SkillsCliReleaseProof {
12
+ packageTarPath: string;
13
+ packageTarSha256: string;
14
+ packageTarBytes: number;
15
+ manifestSha256: string;
16
+ executableSha256: string;
17
+ }
11
18
  export interface CodexNativeHookReconcileOptions {
12
19
  home?: string;
13
20
  dataDir?: string;
14
21
  codexCommand?: string;
15
22
  journal: string;
16
23
  reviewedSkillsCli?: ReviewedSkillsCli;
24
+ releaseProof?: SkillsCliReleaseProof;
25
+ supersedeBinding?: boolean;
17
26
  }
18
27
  type Connect = typeof connectCodexHookRpc;
19
28
  /** Resolve an interrupted native write only after the native consumer and every
@@ -24,21 +33,27 @@ export declare function reconcileCodexNativeHooks(options: CodexNativeHookReconc
24
33
  version: number;
25
34
  status: string;
26
35
  reconciled: boolean;
36
+ supersededBinding: boolean;
27
37
  automaticRollback: boolean;
28
38
  journal: string;
29
39
  planDigest: any;
30
40
  nativeVersion: string;
31
41
  nativeStateVerified: boolean;
42
+ nativeTrustPending: boolean;
32
43
  nativeExecutionVerified: boolean;
33
44
  configSha256: string;
34
45
  hooksSha256: string;
35
46
  policySha256: string;
47
+ policyBeforeSha256: string;
48
+ policyCurrentSha256: string;
49
+ previousSkillsCli: any;
36
50
  skillsCli: {
37
51
  path: string;
38
52
  version: string;
39
53
  sha256: string;
40
54
  manifestSha256: string;
41
55
  };
56
+ releaseProof: SkillsCliReleaseProof | undefined;
42
57
  nativeInventoryScope: string;
43
58
  unrelatedNativeHooksPreserved: boolean;
44
59
  unrelatedSettingsAndCommentsPreserved: boolean;
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.9.7",
32376
+ version: "0.9.8",
32377
32377
  description: "Skills library for AI coding agents",
32378
32378
  type: "module",
32379
32379
  bin: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/skills",
3
- "version": "0.9.7",
3
+ "version": "0.9.8",
4
4
  "description": "Skills library for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {