@hasna/skills 0.10.27 → 0.10.30

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/dist/index.js CHANGED
@@ -6988,8 +6988,8 @@ var require_public_api = __commonJS((exports) => {
6988
6988
  });
6989
6989
 
6990
6990
  // src/lib/registry.ts
6991
- import { existsSync as existsSync14, readFileSync as readFileSync18, readdirSync as readdirSync12 } from "fs";
6992
- import { join as join30 } from "path";
6991
+ import { existsSync as existsSync15, readFileSync as readFileSync18, readdirSync as readdirSync12 } from "fs";
6992
+ import { join as join31 } from "path";
6993
6993
 
6994
6994
  // src/lib/config.ts
6995
6995
  import { existsSync as existsSync2, readFileSync, writeFileSync, mkdirSync } from "fs";
@@ -7252,17 +7252,17 @@ function unsetConfig(key, scope = "project") {
7252
7252
 
7253
7253
  // src/lib/portable-skills.ts
7254
7254
  import {
7255
- existsSync as existsSync13,
7255
+ existsSync as existsSync14,
7256
7256
  mkdirSync as mkdirSync9,
7257
7257
  mkdtempSync as mkdtempSync2,
7258
7258
  readdirSync as readdirSync11,
7259
- realpathSync as realpathSync9,
7259
+ realpathSync as realpathSync10,
7260
7260
  renameSync as renameSync5,
7261
7261
  rmSync as rmSync4,
7262
7262
  statSync as statSync10,
7263
7263
  writeFileSync as writeFileSync8
7264
7264
  } from "fs";
7265
- import { basename as basename11, dirname as dirname19, isAbsolute as isAbsolute17, join as join29, normalize as normalize4, relative as relative6, resolve as resolve17, sep as sep8 } from "path";
7265
+ import { basename as basename11, dirname as dirname19, isAbsolute as isAbsolute17, join as join30, normalize as normalize4, relative as relative6, resolve as resolve18, sep as sep8 } from "path";
7266
7266
 
7267
7267
  // src/lib/registry-data/index.ts
7268
7268
  var SKILLS = [];
@@ -8904,6 +8904,8 @@ var NATIVE_SKILL_ROOTS = [
8904
8904
  ["codex", ".codex/skills"],
8905
8905
  ["codex", ".agents/skills"],
8906
8906
  ["gemini", ".gemini/skills"],
8907
+ ["sumi", ".sumi/skills"],
8908
+ ["sumi", ".sumi/skill"],
8907
8909
  ["codewith", ".codewith/skills"],
8908
8910
  ["opencode", ".config/opencode/skills"],
8909
8911
  ["opencode", ".opencode/skills"],
@@ -8918,12 +8920,12 @@ var NATIVE_SKILL_ROOTS = [
8918
8920
  ];
8919
8921
 
8920
8922
  // src/lib/authoring-path.ts
8921
- import { lstatSync as lstatSync22, realpathSync as realpathSync8 } from "fs";
8923
+ import { lstatSync as lstatSync23, realpathSync as realpathSync9 } from "fs";
8922
8924
  import { homedir as homedir6 } from "os";
8923
- import { basename as basename10, dirname as dirname18, join as join28, resolve as resolve16, sep as sep7 } from "path";
8925
+ import { basename as basename10, dirname as dirname18, join as join29, resolve as resolve17, sep as sep7 } from "path";
8924
8926
 
8925
8927
  // src/lib/agent-adapters.ts
8926
- var INTEGRATION_AGENTS = ["claude", "codex", "gemini", "opencode", "cursor", "hermes"];
8928
+ var INTEGRATION_AGENTS = ["claude", "codex", "gemini", "opencode", "cursor", "hermes", "sumi"];
8927
8929
  function renderAgentHookCommand(command, agent, profileId, event) {
8928
8930
  if (typeof profileId !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(profileId) || profileId.includes(".."))
8929
8931
  throw new Error("Invalid selection profile id");
@@ -8934,6 +8936,7 @@ var AGENT_ADAPTERS = {
8934
8936
  codex: { root: ".codex/skills", config: ".codex/hooks.json", events: ["UserPromptSubmit", "SessionStart", "SubagentStart"], promptContext: true },
8935
8937
  gemini: { root: ".gemini/skills", config: ".gemini/settings.json", events: ["BeforeAgent", "SessionStart"], promptContext: true },
8936
8938
  opencode: { root: ".config/opencode/skills", config: ".config/opencode/opencode.json", events: ["chat.message"], promptContext: true },
8939
+ sumi: { root: ".hasna-internal/sumi/config/skills", config: ".hasna-internal/sumi/config/sumi.json", events: ["prompt", "context"], promptContext: true },
8937
8940
  hermes: { root: ".hermes/skills", config: ".hermes/config.yaml", events: ["pre_llm_call", "pre_tool_call"], promptContext: true, promptFailureMode: "open", toolFailureMode: "supervised-child-errors-block", requiresNativeTrust: true },
8938
8941
  cursor: { root: ".cursor/skills", config: ".cursor/hooks.json", events: ["beforeSubmitPrompt", "sessionStart"], promptContext: false }
8939
8942
  };
@@ -9139,8 +9142,8 @@ function assertAgentPolicyCollections(policy) {
9139
9142
 
9140
9143
  // src/lib/agent-discovery.ts
9141
9144
  import { createHash as createHash18 } from "crypto";
9142
- import { lstatSync as lstatSync21, readFileSync as readFileSync17, readdirSync as readdirSync10, realpathSync as realpathSync7 } from "fs";
9143
- import { basename as basename9, dirname as dirname17, isAbsolute as isAbsolute16, join as join27, resolve as resolve15, sep as sep6 } from "path";
9145
+ import { lstatSync as lstatSync22, readFileSync as readFileSync17, readdirSync as readdirSync10, realpathSync as realpathSync8 } from "fs";
9146
+ import { basename as basename9, dirname as dirname17, isAbsolute as isAbsolute16, join as join28, resolve as resolve16, sep as sep6 } from "path";
9144
9147
  import { isDeepStrictEqual as isDeepStrictEqual5 } from "util";
9145
9148
 
9146
9149
  // node_modules/yaml/dist/index.js
@@ -9652,12 +9655,12 @@ function hashDiscoveryPathFile(path, budget, changes) {
9652
9655
  }
9653
9656
 
9654
9657
  // src/lib/plugin-discovery.ts
9655
- import { closeSync as closeSync10, constants as constants9, fstatSync as fstatSync10, lstatSync as lstatSync18, openSync as openSync10, opendirSync as opendirSync5, readSync as readSync8 } from "fs";
9656
- import { basename as basename7, dirname as dirname14, isAbsolute as isAbsolute14, join as join26, resolve as resolve13 } from "path";
9658
+ import { closeSync as closeSync10, constants as constants9, fstatSync as fstatSync10, lstatSync as lstatSync19, openSync as openSync10, opendirSync as opendirSync5, readSync as readSync8 } from "fs";
9659
+ import { basename as basename7, dirname as dirname14, isAbsolute as isAbsolute14, join as join27, resolve as resolve14 } from "path";
9657
9660
 
9658
9661
  // src/lib/plugin-admission.ts
9659
9662
  import { homedir as homedir5 } from "os";
9660
- import { isAbsolute as isAbsolute13, join as join25, resolve as resolve12 } from "path";
9663
+ import { isAbsolute as isAbsolute13, join as join26, resolve as resolve13 } from "path";
9661
9664
 
9662
9665
  // src/lib/selection-cache.ts
9663
9666
  import { createHash as createHash6, randomUUID } from "crypto";
@@ -10914,7 +10917,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
10914
10917
  // package.json
10915
10918
  var package_default = {
10916
10919
  name: "@hasna/skills",
10917
- version: "0.10.27",
10920
+ version: "0.10.30",
10918
10921
  description: "Skills library for AI coding agents",
10919
10922
  type: "module",
10920
10923
  bin: {
@@ -12724,19 +12727,19 @@ async function prepareSkillDependencies(skillPath, env, timeoutMs = DEFAULT_PREP
12724
12727
  }
12725
12728
 
12726
12729
  // src/lib/skillinfo.ts
12727
- import { existsSync as existsSync12, readFileSync as readFileSync16 } from "fs";
12728
- import { join as join21 } from "path";
12730
+ import { existsSync as existsSync13, readFileSync as readFileSync16 } from "fs";
12731
+ import { join as join23 } from "path";
12729
12732
 
12730
12733
  // src/lib/installer.ts
12731
- import { existsSync as existsSync11, readFileSync as readFileSync15, rmSync as rmSync2 } from "fs";
12732
- import { join as join20 } from "path";
12734
+ import { existsSync as existsSync12, readFileSync as readFileSync15, rmSync as rmSync2 } from "fs";
12735
+ import { join as join21 } from "path";
12733
12736
  import { homedir as homedir4 } from "os";
12734
12737
 
12735
12738
  // src/lib/agent-sync.ts
12736
12739
  import {
12737
12740
  cpSync as cpSync2,
12738
- existsSync as existsSync9,
12739
- lstatSync as lstatSync16,
12741
+ existsSync as existsSync10,
12742
+ lstatSync as lstatSync17,
12740
12743
  mkdirSync as mkdirSync6,
12741
12744
  mkdtempSync,
12742
12745
  readFileSync as readFileSync13,
@@ -12747,7 +12750,7 @@ import {
12747
12750
  writeFileSync as writeFileSync5
12748
12751
  } from "fs";
12749
12752
  import { homedir as homedir3 } from "os";
12750
- import { basename as basename6, dirname as dirname12, join as join18 } from "path";
12753
+ import { basename as basename6, dirname as dirname12, join as join19 } from "path";
12751
12754
 
12752
12755
  // src/lib/codex-local-mcp-controls.ts
12753
12756
  function isCodexLocalStdioMcpServer(name, server) {
@@ -14050,12 +14053,14 @@ function disablesOneSkill(entry) {
14050
14053
  return selector.length === 1 && typeof entry[selector[0]] === "string" && entry[selector[0]].length > 0;
14051
14054
  }
14052
14055
  function inference(target, version) {
14053
- const keys = version === 3 ? [...INFERENCE, "service_tier", "plan_mode_reasoning_effort"] : INFERENCE;
14056
+ const keys = version === 3 ? [...INFERENCE, "service_tier", "plan_mode_reasoning_effort", "personality"] : INFERENCE;
14054
14057
  for (const key of keys)
14055
14058
  if (Object.hasOwn(target, key)) {
14056
14059
  const value = target[key];
14057
14060
  if (key === "model")
14058
14061
  need4(typeof value === "string" && MODEL.test(value));
14062
+ else if (key === "personality")
14063
+ need4(typeof value === "string" && ["none", "friendly", "pragmatic"].includes(value));
14059
14064
  else if (version === 3 && key !== "model_verbosity") {
14060
14065
  need4(typeof value === "string" && value.length <= 128 && !/[\x00-\x1f\x7f]/.test(value));
14061
14066
  if (key !== "service_tier")
@@ -14122,8 +14127,8 @@ function upgradeCodexSettingsWitnessV3(previous, reviewedSettingsPath) {
14122
14127
  // src/lib/agent-integration.ts
14123
14128
  import { createHash as createHash12, randomUUID as randomUUID2 } from "crypto";
14124
14129
  import { isDeepStrictEqual as isDeepStrictEqual4 } from "util";
14125
- import { existsSync as existsSync8, lstatSync as lstatSync15, statSync as statSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync12, readdirSync as readdirSync8, opendirSync as opendirSync3, readlinkSync as readlinkSync4, realpathSync as realpathSync6, renameSync as renameSync2, rmdirSync as rmdirSync2, writeFileSync as writeFileSync4, unlinkSync as unlinkSync3, chmodSync, openSync as openSync8, closeSync as closeSync8, fsyncSync as fsyncSync2, fstatSync as fstatSync8, readSync as readSync6, constants as constants7, linkSync } from "fs";
14126
- import { dirname as dirname11, join as join17, resolve as resolve10, relative as relative5, isAbsolute as isAbsolute11, sep as sep5 } from "path";
14130
+ import { existsSync as existsSync9, lstatSync as lstatSync16, statSync as statSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync12, readdirSync as readdirSync8, opendirSync as opendirSync3, readlinkSync as readlinkSync4, realpathSync as realpathSync7, renameSync as renameSync2, rmdirSync as rmdirSync2, writeFileSync as writeFileSync4, unlinkSync as unlinkSync3, chmodSync, openSync as openSync8, closeSync as closeSync8, fsyncSync as fsyncSync2, fstatSync as fstatSync8, readSync as readSync6, constants as constants7, linkSync } from "fs";
14131
+ import { dirname as dirname11, join as join18, resolve as resolve11, relative as relative5, isAbsolute as isAbsolute11, sep as sep5 } from "path";
14127
14132
  import { homedir as homedir2 } from "os";
14128
14133
 
14129
14134
  // src/lib/managed-policy.ts
@@ -14517,6 +14522,114 @@ function disableCodexBundledSkills(text2) {
14517
14522
  return [...candidates][0];
14518
14523
  }
14519
14524
 
14525
+ // src/lib/agent-sumi.ts
14526
+ import { existsSync as existsSync8, lstatSync as lstatSync15, realpathSync as realpathSync6 } from "fs";
14527
+ import { join as join17, resolve as resolve10 } from "path";
14528
+ function sumiConfigDirectory(home, env = process.env) {
14529
+ for (const key of ["SUMI_CONFIG", "SUMI_CONFIG_CONTENT"])
14530
+ if (env[key] !== undefined)
14531
+ throw new Error(`Sumi ${key} requires a dedicated discovery adapter; unset it before Skills integration`);
14532
+ const selected = (key) => env[key]?.trim() ? env[key] : undefined;
14533
+ const expand = (value) => resolve10(value === "~" ? home : value.startsWith("~/") ? join17(home, value.slice(2)) : value);
14534
+ const explicit = selected("SUMI_CONFIG_DIR");
14535
+ const xdg = selected("XDG_CONFIG_HOME");
14536
+ const custom = selected("SUMI_HOME");
14537
+ const canonical5 = explicit ? expand(explicit) : xdg ? join17(expand(xdg), "sumi") : join17(custom ? expand(custom) : join17(home, ".hasna-internal/sumi"), "config");
14538
+ if (explicit || xdg || custom)
14539
+ return canonical5;
14540
+ const legacy = join17(home, ".config/sumi");
14541
+ const a = lstatSync15(canonical5, { throwIfNoEntry: false }), b = lstatSync15(legacy, { throwIfNoEntry: false });
14542
+ if (a && b && realpathSync6(canonical5) !== realpathSync6(legacy))
14543
+ throw new Error("Sumi config roots conflict; complete native home adoption before Skills integration");
14544
+ const path = a ? canonical5 : b ? legacy : canonical5;
14545
+ if (!existsSync8(path))
14546
+ return path;
14547
+ const physical = realpathSync6(path);
14548
+ if ((a?.isSymbolicLink() || b?.isSymbolicLink()) && ![canonical5, legacy].includes(physical))
14549
+ throw new Error("Sumi config alias must resolve to its canonical or legacy root");
14550
+ return physical;
14551
+ }
14552
+ function sumiBridgeRoot(home) {
14553
+ return join17(sumiConfigDirectory(home), "skills");
14554
+ }
14555
+ function sumiConfigPath(home) {
14556
+ return join17(sumiConfigDirectory(home), "sumi.json");
14557
+ }
14558
+ function renderSumiPlugin(command, profile) {
14559
+ return `// Managed by @hasna/skills. Regenerate with skills hook install --agent sumi.
14560
+ import { spawn } from "node:child_process";
14561
+ const command = ${JSON.stringify(command)};
14562
+ const profile = ${JSON.stringify(profile)};
14563
+ function context(input) {
14564
+ return new Promise((resolve, reject) => {
14565
+ const child = spawn(command, ["hook", "user-prompt", "--agent", "sumi", "--selection-profile", profile], { cwd: input.cwd, stdio: ["pipe", "pipe", "pipe"] });
14566
+ const chunks = []; let bytes = 0, failure;
14567
+ const timer = setTimeout(() => { failure = new Error("Skills prompt hook timed out"); child.kill("SIGKILL"); }, 15000);
14568
+ child.stdout.on("data", chunk => { bytes += chunk.length; if (bytes > 1048576) { failure = new Error("Skills prompt hook output exceeded its limit"); child.kill("SIGKILL"); } else chunks.push(chunk); });
14569
+ child.stderr.on("data", () => {});
14570
+ child.stdin.on("error", () => {});
14571
+ child.on("error", error => { clearTimeout(timer); reject(error); });
14572
+ child.on("close", code => {
14573
+ clearTimeout(timer);
14574
+ if (failure || code !== 0) return reject(failure ?? new Error("Skills prompt hook failed"));
14575
+ try {
14576
+ const result = JSON.parse(Buffer.concat(chunks).toString("utf8"));
14577
+ if (!result || typeof result !== "object" || Array.isArray(result)) throw new Error("Invalid Skills hook response");
14578
+ if (result.decision === "block" || result.continue === false) throw new Error("Skills prompt hook refused");
14579
+ if (Object.keys(result).length && (!result.hookSpecificOutput || result.hookSpecificOutput.hookEventName !== input.hook_event_name)) throw new Error("Invalid Skills hook response");
14580
+ const text = result.hookSpecificOutput?.additionalContext ?? "";
14581
+ if (typeof text !== "string") throw new Error("Invalid Skills prompt context");
14582
+ resolve(text);
14583
+ } catch (error) { reject(error); }
14584
+ });
14585
+ child.stdin.end(JSON.stringify(input));
14586
+ });
14587
+ }
14588
+ export default {
14589
+ id: "local.hasna-skills-cli",
14590
+ async setup(ctx) {
14591
+ if (!ctx?.session?.hook || !ctx?.session?.get || !ctx?.skill?.transform || !ctx?.tool?.hook) throw new Error("Unsupported Sumi native Skills contract");
14592
+ const registrations = [];
14593
+ const sessions = new Set();
14594
+ async function input(sessionID, prompt, first) {
14595
+ // The Promise adapter unwraps single-data endpoint responses.
14596
+ const session = await ctx.session.get({ sessionID });
14597
+ if (!session || session.id !== sessionID || typeof session.location?.directory !== "string" || (session.parentID !== undefined && typeof session.parentID !== "string") || session.parentID === sessionID) throw new Error("Invalid Sumi session custody");
14598
+ return { hook_event_name: first ? session.parentID ? "SubagentStart" : "SessionStart" : "UserPromptSubmit", cwd: session.location.directory, session_id: sessionID, parent_session_id: session.parentID ?? null, prompt, restore: true };
14599
+ }
14600
+ registrations.push(await ctx.skill.transform(editor => {
14601
+ for (const skill of editor.list()) if (skill.id !== "skills-cli" || skill.name !== "skills-cli") editor.remove(skill.id);
14602
+ }));
14603
+ // Native config transforms may add skills after this transform. Guard
14604
+ // both direct tool calls and prompt attachments at the consumption edge.
14605
+ registrations.push(await ctx.tool.hook("execute.before", event => {
14606
+ if (event.tool === "skill" && event.input?.id !== "skills-cli") throw new Error("Native Sumi skill payload refused; use Skills CLI");
14607
+ }));
14608
+ registrations.push(await ctx.session.hook("prompt", async event => {
14609
+ if (typeof event.prompt?.text !== "string") throw new Error("Invalid Sumi prompt");
14610
+ if (event.prompt.skills !== undefined && (!Array.isArray(event.prompt.skills) || event.prompt.skills.some(skill => skill?.id !== "skills-cli"))) throw new Error("Native Sumi skill attachment refused; use Skills CLI");
14611
+ await context(await input(event.sessionID, event.prompt.text, !sessions.has(event.sessionID)));
14612
+ sessions.add(event.sessionID);
14613
+ }));
14614
+ registrations.push(await ctx.session.hook("context", async event => {
14615
+ const last = event.messages.findLast(message => message.role === "user");
14616
+ const prompt = (last?.content ?? []).filter(part => part.type === "text").map(part => part.text).join("\\n");
14617
+ // Recheck on every primary request, including resumed and synthetic
14618
+ // sessions. The CLI owns optional failures versus explicit refusals.
14619
+ const text = await context(await input(event.sessionID, prompt, !sessions.has(event.sessionID)));
14620
+ if (text) event.system.push({ type: "text", text });
14621
+ sessions.add(event.sessionID);
14622
+ }));
14623
+ return async () => { for (const registration of registrations) await registration.dispose(); };
14624
+ },
14625
+ };
14626
+ `;
14627
+ }
14628
+ var SUMI_SKILL_PERMISSIONS = [
14629
+ { action: "skill", resource: "*", effect: "deny" },
14630
+ { action: "skill", resource: "skills-cli", effect: "allow" }
14631
+ ];
14632
+
14520
14633
  // src/lib/agent-integration.ts
14521
14634
  var sha3 = (data) => createHash12("sha256").update(data).digest("hex");
14522
14635
  var ROOTS = NATIVE_SKILL_ROOTS;
@@ -14551,11 +14664,11 @@ function isSystemRootAlias(path) {
14551
14664
  const target = `/private${path}`, link = `private${path}`;
14552
14665
  const identity4 = (stat) => `${stat.dev}:${stat.ino}:${stat.ctimeNs}:${stat.uid}:${stat.mode}`;
14553
14666
  try {
14554
- const before = lstatSync15(path, { bigint: true });
14667
+ const before = lstatSync16(path, { bigint: true });
14555
14668
  if (!before.isSymbolicLink() || before.uid !== 0n || readlinkSync4(path) !== link)
14556
14669
  return false;
14557
14670
  const directories = ["/", "/private", target].map((directory) => {
14558
- const stat = lstatSync15(directory, { bigint: true });
14671
+ const stat = lstatSync16(directory, { bigint: true });
14559
14672
  if (!stat.isDirectory() || stat.uid !== 0n)
14560
14673
  throw new Error("Untrusted system alias directory");
14561
14674
  if (directory !== "/private/tmp") {
@@ -14572,23 +14685,23 @@ function isSystemRootAlias(path) {
14572
14685
  }
14573
14686
  return { directory, identity: identity4(stat) };
14574
14687
  });
14575
- return realpathSync6(path) === target && readlinkSync4(path) === link && identity4(lstatSync15(path, { bigint: true })) === identity4(before) && directories.every((item) => identity4(lstatSync15(item.directory, { bigint: true })) === item.identity);
14688
+ return realpathSync7(path) === target && readlinkSync4(path) === link && identity4(lstatSync16(path, { bigint: true })) === identity4(before) && directories.every((item) => identity4(lstatSync16(item.directory, { bigint: true })) === item.identity);
14576
14689
  } catch {
14577
14690
  return false;
14578
14691
  }
14579
14692
  }
14580
14693
  function canonicalSystemPath(path) {
14581
- const absolute = resolve10(path), root = `/${absolute.split(sep5)[1]}`;
14582
- if (process.platform !== "darwin" || !["/var", "/tmp", "/etc"].includes(root) || !lstatSync15(root, { throwIfNoEntry: false })?.isSymbolicLink())
14694
+ const absolute = resolve11(path), root = `/${absolute.split(sep5)[1]}`;
14695
+ if (process.platform !== "darwin" || !["/var", "/tmp", "/etc"].includes(root) || !lstatSync16(root, { throwIfNoEntry: false })?.isSymbolicLink())
14583
14696
  return absolute;
14584
14697
  if (!isSystemRootAlias(root))
14585
14698
  throw new Error(`Refusing symlink path: ${root}`);
14586
14699
  return `/private${absolute}`;
14587
14700
  }
14588
14701
  function assertSafePath(path) {
14589
- let cursor = resolve10(path);
14702
+ let cursor = resolve11(path);
14590
14703
  while (true) {
14591
- if (lstatSync15(cursor, { throwIfNoEntry: false })?.isSymbolicLink() && !isSystemRootAlias(cursor))
14704
+ if (lstatSync16(cursor, { throwIfNoEntry: false })?.isSymbolicLink() && !isSystemRootAlias(cursor))
14592
14705
  throw new Error(`Refusing symlink path: ${cursor}`);
14593
14706
  const parent = dirname11(cursor);
14594
14707
  if (parent === cursor)
@@ -14598,16 +14711,16 @@ function assertSafePath(path) {
14598
14711
  }
14599
14712
  function readRootAlias(home, agent) {
14600
14713
  assertSafePath(home);
14601
- const realHome = realpathSync6(home), alias = join17(realHome, `.${agent}`);
14602
- const stat = lstatSync15(alias, { throwIfNoEntry: false, bigint: true });
14714
+ const realHome = realpathSync7(home), alias = join18(realHome, `.${agent}`);
14715
+ const stat = lstatSync16(alias, { throwIfNoEntry: false, bigint: true });
14603
14716
  if (!stat?.isSymbolicLink())
14604
14717
  throw new Error(`Agent root alias changed after planning: ${alias}`);
14605
- const link = readlinkSync4(alias), targetPath = resolve10(dirname11(alias), link);
14718
+ const link = readlinkSync4(alias), targetPath = resolve11(dirname11(alias), link);
14606
14719
  assertSafePath(targetPath);
14607
- const targetStat = lstatSync15(targetPath, { throwIfNoEntry: false });
14720
+ const targetStat = lstatSync16(targetPath, { throwIfNoEntry: false });
14608
14721
  if (!targetStat?.isDirectory())
14609
14722
  throw new Error(`Agent root alias must target an existing directory: ${alias}`);
14610
- const target = realpathSync6(targetPath), inside = relative5(realHome, target);
14723
+ const target = realpathSync7(targetPath), inside = relative5(realHome, target);
14611
14724
  if (!inside || inside === ".." || inside.startsWith(`..${sep5}`) || isAbsolute11(inside))
14612
14725
  throw new Error(`Agent root alias target must be inside the real home: ${alias}`);
14613
14726
  return { agent, home: realHome, alias, target, link, aliasIdentity: `${stat.dev}:${stat.ino}:${stat.ctimeNs}`, targetIdentity: `${targetStat.dev}:${targetStat.ino}` };
@@ -14617,7 +14730,7 @@ function rootAliases(home, allowed = false) {
14617
14730
  return [];
14618
14731
  const result = [];
14619
14732
  for (const agent of ["claude", "codex"])
14620
- if (lstatSync15(join17(home, `.${agent}`), { throwIfNoEntry: false })?.isSymbolicLink())
14733
+ if (lstatSync16(join18(home, `.${agent}`), { throwIfNoEntry: false })?.isSymbolicLink())
14621
14734
  result.push(readRootAlias(home, agent));
14622
14735
  if (result.some((item, index) => result.slice(index + 1).some((other) => item.target === other.target || item.target.startsWith(other.target + sep5) || other.target.startsWith(item.target + sep5))))
14623
14736
  throw new Error("Agent root alias targets must not overlap");
@@ -14625,7 +14738,7 @@ function rootAliases(home, allowed = false) {
14625
14738
  }
14626
14739
  function canonicalAgentPath(path, aliases) {
14627
14740
  const absolute = canonicalSystemPath(path), binding = aliases.find((item) => absolute === item.alias || absolute.startsWith(item.alias + sep5));
14628
- return binding ? join17(binding.target, relative5(binding.alias, absolute)) : absolute;
14741
+ return binding ? join18(binding.target, relative5(binding.alias, absolute)) : absolute;
14629
14742
  }
14630
14743
  function disabledCodexSkillPaths(config, aliases) {
14631
14744
  const skills = config.skills;
@@ -14644,7 +14757,7 @@ function disabledCodexSkillPaths(config, aliases) {
14644
14757
  continue;
14645
14758
  let document2;
14646
14759
  try {
14647
- document2 = realpathSync6(canonicalAgentPath(entry.path, aliases));
14760
+ document2 = realpathSync7(canonicalAgentPath(entry.path, aliases));
14648
14761
  } catch {
14649
14762
  continue;
14650
14763
  }
@@ -14656,7 +14769,7 @@ function disabledCodexSkillPaths(config, aliases) {
14656
14769
  }
14657
14770
  function recheckRootAliases(aliases) {
14658
14771
  for (const binding of aliases) {
14659
- if (!["claude", "codex"].includes(binding.agent) || binding.alias !== join17(binding.home, `.${binding.agent}`))
14772
+ if (!["claude", "codex"].includes(binding.agent) || binding.alias !== join18(binding.home, `.${binding.agent}`))
14660
14773
  throw new Error("Invalid agent root alias binding");
14661
14774
  const current = readRootAlias(binding.home, binding.agent);
14662
14775
  if (JSON.stringify(current) !== JSON.stringify(binding))
@@ -14665,7 +14778,7 @@ function recheckRootAliases(aliases) {
14665
14778
  }
14666
14779
  function readNativeBytes(path, maximum = 64 * 1024 * 1024) {
14667
14780
  assertSafePath(path);
14668
- const before = lstatSync15(path);
14781
+ const before = lstatSync16(path);
14669
14782
  if (!before.isFile() || before.size > maximum)
14670
14783
  throw new Error("Unsupported or oversized native skill file");
14671
14784
  const descriptor = openSync8(path, constants7.O_RDONLY | constants7.O_NOFOLLOW | constants7.O_NONBLOCK);
@@ -14681,7 +14794,7 @@ function readNativeBytes(path, maximum = 64 * 1024 * 1024) {
14681
14794
  break;
14682
14795
  length += count;
14683
14796
  }
14684
- const after = fstatSync8(descriptor), current = lstatSync15(path);
14797
+ const after = fstatSync8(descriptor), current = lstatSync16(path);
14685
14798
  if (current.dev !== opened.dev || current.ino !== opened.ino || current.size !== after.size || current.mtimeMs !== after.mtimeMs || current.ctimeMs !== after.ctimeMs || length !== before.size || after.size !== before.size || after.mtimeMs !== opened.mtimeMs || after.ctimeMs !== opened.ctimeMs)
14686
14799
  throw new Error("Native skill file changed while reading");
14687
14800
  return bytes.subarray(0, length);
@@ -14693,14 +14806,14 @@ function treeHash(root) {
14693
14806
  const hash2 = createHash12("sha256");
14694
14807
  let bytes = 0, files = 0;
14695
14808
  function visit2(path) {
14696
- const stat = lstatSync15(path);
14809
+ const stat = lstatSync16(path);
14697
14810
  if (stat.isSymbolicLink())
14698
14811
  throw new Error(`Refusing symlink in skill: ${path}`);
14699
14812
  const rel = relative5(root, path);
14700
14813
  if (stat.isDirectory()) {
14701
14814
  hash2.update(`d\x00${rel}\x00`);
14702
14815
  for (const name of readdirSync8(path).sort())
14703
- visit2(join17(path, name));
14816
+ visit2(join18(path, name));
14704
14817
  return;
14705
14818
  }
14706
14819
  if (!stat.isFile())
@@ -14722,10 +14835,10 @@ function selectNativeMigrationTargets(inventory, manifest) {
14722
14835
  const identities = new Set;
14723
14836
  const selected = [];
14724
14837
  for (const target of manifest.targets) {
14725
- if (!isAbsolute11(target.projectRoot) || resolve10(target.projectRoot) !== target.projectRoot)
14838
+ if (!isAbsolute11(target.projectRoot) || resolve11(target.projectRoot) !== target.projectRoot)
14726
14839
  throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
14727
- const projectRoot = canonicalSystemPath(target.projectRoot), normalized = relative5(projectRoot, resolve10(projectRoot, target.path));
14728
- if (!existsSync8(projectRoot) || !lstatSync15(projectRoot).isDirectory())
14840
+ const projectRoot = canonicalSystemPath(target.projectRoot), normalized = relative5(projectRoot, resolve11(projectRoot, target.path));
14841
+ if (!existsSync9(projectRoot) || !lstatSync16(projectRoot).isDirectory())
14729
14842
  throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
14730
14843
  assertSafePath(projectRoot);
14731
14844
  if (isAbsolute11(target.path) || target.path.includes("\\") || target.path.includes("\x00") || !normalized || normalized === ".." || normalized.startsWith(`..${sep5}`) || normalized !== target.path || !/^[0-9a-f]{64}$/.test(target.treeSha256))
@@ -14734,8 +14847,8 @@ function selectNativeMigrationTargets(inventory, manifest) {
14734
14847
  if (identities.has(identity4))
14735
14848
  throw new Error(`Duplicate native migration target: ${target.path}`);
14736
14849
  identities.add(identity4);
14737
- const expected = canonicalSystemPath(resolve10(target.projectRoot, target.path));
14738
- const matches = inventory.filter((entry2) => entry2.agent === target.agent && resolve10(entry2.path) === expected);
14850
+ const expected = canonicalSystemPath(resolve11(target.projectRoot, target.path));
14851
+ const matches = inventory.filter((entry2) => entry2.agent === target.agent && resolve11(entry2.path) === expected);
14739
14852
  if (matches.length !== 1)
14740
14853
  throw new Error(`Native migration target was not found exactly once: ${target.agent} ${target.projectRoot}/${target.path}`);
14741
14854
  const entry = matches[0];
@@ -14764,9 +14877,9 @@ function projectAncestorDirectories(projects) {
14764
14877
  }
14765
14878
  function inventoryNativeSkills(home = homedir2(), options = {}) {
14766
14879
  home = canonicalSystemPath(home);
14767
- if (options.reviewedCacheAlias !== undefined && (!options.includeVendor || !isAbsolute11(options.reviewedCacheAlias) || resolve10(options.reviewedCacheAlias) !== options.reviewedCacheAlias))
14880
+ if (options.reviewedCacheAlias !== undefined && (!options.includeVendor || !isAbsolute11(options.reviewedCacheAlias) || resolve11(options.reviewedCacheAlias) !== options.reviewedCacheAlias))
14768
14881
  throw new Error("A reviewed cache alias requires vendor inventory and an exact absolute path");
14769
- if (options.disabledVendorPaths?.some((path) => !isAbsolute11(path) || resolve10(path) !== path))
14882
+ if (options.disabledVendorPaths?.some((path) => !isAbsolute11(path) || resolve11(path) !== path))
14770
14883
  throw new Error("Disabled vendor paths must be exact absolute paths");
14771
14884
  const reviewedCacheAlias = options.reviewedCacheAlias === undefined ? undefined : canonicalSystemPath(options.reviewedCacheAlias);
14772
14885
  const disabledVendorPaths = new Set((options.disabledVendorPaths ?? []).map(canonicalSystemPath));
@@ -14774,14 +14887,18 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
14774
14887
  const selectedAgents = options.agents ? new Set(options.agents) : undefined;
14775
14888
  const includesAgent = (agent) => !selectedAgents || selectedAgents.has(agent);
14776
14889
  const rootDefinitions = selectedAgents ? ROOTS.filter(([agent, path]) => selectedAgents.has(agent) || selectedAgents.has("hermes") && agent === "codex" && path === ".agents/skills") : ROOTS;
14777
- const roots = rootDefinitions.map(([agent, path]) => [agent, canonicalAgentPath(join17(home, path), aliases)]);
14778
- const bridgePaths = Object.values(AGENT_ADAPTERS).map((adapter) => canonicalAgentPath(join17(home, adapter.root, CLI_BRIDGE_NAME), aliases));
14890
+ const roots = rootDefinitions.map(([agent, path]) => [agent, canonicalAgentPath(join18(home, path), aliases)]);
14891
+ const bridgePaths = Object.entries(AGENT_ADAPTERS).filter(([agent]) => agent !== "sumi" || includesAgent("sumi")).map(([agent, adapter]) => canonicalAgentPath(join18(agent === "sumi" ? sumiBridgeRoot(home) : join18(home, adapter.root), CLI_BRIDGE_NAME), aliases));
14779
14892
  for (const project of projectAncestorDirectories([...options.projectDirs ?? [], ...options.projectDir ? [options.projectDir] : []])) {
14780
14893
  for (const [agent, path] of rootDefinitions)
14781
- roots.push([agent, canonicalAgentPath(join17(project, path), aliases)]);
14894
+ roots.push([agent, canonicalAgentPath(join18(project, path), aliases)]);
14895
+ if (selectedAgents?.has("sumi"))
14896
+ for (const directory of [".claude", ".agents"])
14897
+ for (const name of ["skill", "skills"])
14898
+ roots.push(["sumi", canonicalAgentPath(join18(project, directory, name), aliases)]);
14782
14899
  }
14783
14900
  const installationInputs = options.codexInstallationInputRoots ?? [];
14784
- if (installationInputs.some((input) => resolve10(input) !== input || realpathSync6(input) !== input || roots.some(([agent, path]) => agent === "codex" && (path === input || path.startsWith(input + sep5) || input.startsWith(path + sep5)))))
14901
+ if (installationInputs.some((input) => resolve11(input) !== input || realpathSync7(input) !== input || roots.some(([agent, path]) => agent === "codex" && (path === input || path.startsWith(input + sep5) || input.startsWith(path + sep5)))))
14785
14902
  throw new Error("Native installation input overlaps an unconditional skill-loading root");
14786
14903
  const entries = [], seen = new Set;
14787
14904
  const cacheScans = new Map, maxAliasProofEntries = 1e4;
@@ -14795,13 +14912,13 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
14795
14912
  }
14796
14913
  let reviewedCacheAliasFound = false;
14797
14914
  function verifiedSiblingCacheAlias(agent, path, parent) {
14798
- const link = readlinkSync4(path), lexicalTarget = resolve10(parent, link);
14915
+ const link = readlinkSync4(path), lexicalTarget = resolve11(parent, link);
14799
14916
  if (isAbsolute11(link) ? link !== lexicalTarget : dirname11(link) !== ".")
14800
14917
  return false;
14801
14918
  const target = canonicalSystemPath(lexicalTarget);
14802
14919
  if (dirname11(target) !== parent)
14803
14920
  return false;
14804
- if (!lstatSync15(target, { throwIfNoEntry: false })?.isDirectory())
14921
+ if (!lstatSync16(target, { throwIfNoEntry: false })?.isDirectory())
14805
14922
  return false;
14806
14923
  const scan = cacheScans.get(target);
14807
14924
  if (!scan?.complete || scan.entries > maxAliasProofEntries)
@@ -14824,37 +14941,40 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
14824
14941
  assertSafePath(path);
14825
14942
  if (configuredRoot && agent === "codex" && installationInputs.some((root) => path === root || path.startsWith(`${root}${sep5}`)))
14826
14943
  return scan;
14827
- if (!existsSync8(path))
14944
+ if (!existsSync9(path))
14828
14945
  return scan;
14829
- const stat = lstatSync15(path);
14830
- if (stat.isFile())
14946
+ const stat = lstatSync16(path);
14947
+ if (stat.isFile()) {
14948
+ if (agent === "sumi" && depth === 1 && path.endsWith(".md"))
14949
+ throw new Error("NATIVE_SKILL_DRIFT: flat Sumi skill documents require a dedicated migration adapter");
14831
14950
  return scan;
14951
+ }
14832
14952
  if (!stat.isDirectory())
14833
14953
  throw new Error(`Unsupported native discovery entry: ${path}`);
14834
- if (existsSync8(join17(path, "SKILL.md"))) {
14954
+ if (existsSync9(join18(path, "SKILL.md"))) {
14835
14955
  scan.hasSkills = true;
14836
14956
  if (seen.has(path))
14837
14957
  return scan;
14838
14958
  seen.add(path);
14839
14959
  let managed = false;
14840
- const marker = join17(path, ".hasna-skills.json");
14841
- if (existsSync8(marker)) {
14960
+ const marker = join18(path, ".hasna-skills.json");
14961
+ if (existsSync9(marker)) {
14842
14962
  try {
14843
14963
  managed = JSON.parse(readFileSync12(marker, "utf8")).managedBy === "@hasna/skills";
14844
14964
  } catch {}
14845
14965
  }
14846
- const bridge = !vendor && isOwnedCliBridge(path, bridgePaths);
14966
+ const bridge = (!vendor || agent === "sumi") && isOwnedCliBridge(path, bridgePaths);
14847
14967
  const rootAlias = aliases.find((item) => path === item.target || path.startsWith(item.target + sep5));
14848
- entries.push({ agent, path, hash: treeHash(path), managed, vendor, ...agent === "codex" && path.startsWith(canonicalAgentPath(join17(home, ".codex", "skills", ".system"), aliases) + sep5) ? { system: true } : {}, ...bridge ? { bridge: true, bridgeHome: resolve10(home) } : {}, ...rootAlias ? { rootAlias } : {} });
14968
+ entries.push({ agent, path, hash: treeHash(path), managed, vendor, ...agent === "codex" && path.startsWith(canonicalAgentPath(join18(home, ".codex", "skills", ".system"), aliases) + sep5) ? { system: true } : {}, ...bridge ? { bridge: true, bridgeHome: resolve11(home) } : {}, ...rootAlias ? { rootAlias } : {} });
14849
14969
  return scan;
14850
14970
  }
14851
- if (depth > (vendor ? 32 : 3))
14971
+ if (depth > (agent === "sumi" ? 32 : vendor ? 32 : 3))
14852
14972
  return { ...scan, complete: false };
14853
14973
  const children = [], directory = opendirSync3(path);
14854
14974
  try {
14855
14975
  let child;
14856
14976
  while ((child = directory.readSync()) !== null) {
14857
- admit(join17(path, child.name));
14977
+ admit(join18(path, child.name));
14858
14978
  children.push(child);
14859
14979
  }
14860
14980
  } finally {
@@ -14866,13 +14986,13 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
14866
14986
  for (const { name } of children) {
14867
14987
  if (name === "node_modules")
14868
14988
  continue;
14869
- if (name.startsWith(".") && name !== ".system" && agent !== "hermes" && !(options.guardHermes && `${path}${sep5}`.includes(`${sep5}.agents${sep5}skills${sep5}`)))
14989
+ if (name.startsWith(".") && name !== ".system" && agent !== "hermes" && agent !== "sumi" && !(options.guardHermes && `${path}${sep5}`.includes(`${sep5}.agents${sep5}skills${sep5}`)))
14870
14990
  continue;
14871
14991
  const isVendor = vendor || name === ".system";
14872
14992
  if (isVendor && !options.includeVendor)
14873
14993
  continue;
14874
- const child = join17(path, name);
14875
- if (isVendor && lstatSync15(child, { throwIfNoEntry: false })?.isSymbolicLink()) {
14994
+ const child = join18(path, name);
14995
+ if (isVendor && lstatSync16(child, { throwIfNoEntry: false })?.isSymbolicLink()) {
14876
14996
  const target = statSync8(child, { throwIfNoEntry: false });
14877
14997
  if (target?.isFile() || pluginCache && target?.isDirectory() && verifiedSiblingCacheAlias(agent, child, path)) {
14878
14998
  scan.entries++;
@@ -14898,11 +15018,11 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
14898
15018
  scanRoot(agent, path);
14899
15019
  if (options.includeVendor) {
14900
15020
  for (const agent of ["codex", "claude"].filter(includesAgent))
14901
- scanRoot(agent, canonicalAgentPath(join17(home, `.${agent}`, "plugins", "cache"), aliases), true, true);
15021
+ scanRoot(agent, canonicalAgentPath(join18(home, `.${agent}`, "plugins", "cache"), aliases), true, true);
14902
15022
  if (includesAgent("claude"))
14903
- scanRoot("claude", canonicalAgentPath(join17(home, ".claude", "plugins", "synced"), aliases), true);
15023
+ scanRoot("claude", canonicalAgentPath(join18(home, ".claude", "plugins", "synced"), aliases), true);
14904
15024
  if (includesAgent("gemini"))
14905
- scanRoot("gemini", join17(home, ".gemini", "extensions"), true);
15025
+ scanRoot("gemini", join18(home, ".gemini", "extensions"), true);
14906
15026
  }
14907
15027
  const configured = options.configured ? INTEGRATION_AGENTS.filter(includesAgent).flatMap((agent) => resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, canonical: (path) => canonicalAgentPath(path, aliases) }).roots.map((path) => ({ agent, path }))) : [];
14908
15028
  for (const root of [...options.agentRoots ?? [], ...configured])
@@ -14915,7 +15035,7 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
14915
15035
  }
14916
15036
  function readOptional(path) {
14917
15037
  assertSafePath(path);
14918
- return existsSync8(path) ? readFileSync12(path, "utf8") : null;
15038
+ return existsSync9(path) ? readFileSync12(path, "utf8") : null;
14919
15039
  }
14920
15040
  function jsonObject(text2, path) {
14921
15041
  if (text2 === null)
@@ -15015,19 +15135,19 @@ function configureHooks(config, agent, command, profileId, nativeReady) {
15015
15135
  function disableCodexSkills(text2, skills, aliases, bridgePath) {
15016
15136
  const bundled = disableCodexBundledSkills(text2);
15017
15137
  let result = normalizeCodexInlinePathConfig(bundled);
15018
- const bridgeSelector = (entry) => typeof entry.name === "string" && entry.name.trim() === CLI_BRIDGE_NAME || typeof entry.path === "string" && [resolve10(bridgePath), resolve10(join17(bridgePath, "SKILL.md"))].includes(canonicalAgentPath(entry.path, aliases));
15138
+ const bridgeSelector = (entry) => typeof entry.name === "string" && entry.name.trim() === CLI_BRIDGE_NAME || typeof entry.path === "string" && [resolve11(bridgePath), resolve11(join18(bridgePath, "SKILL.md"))].includes(canonicalAgentPath(entry.path, aliases));
15019
15139
  const previousConfig = Bun.TOML.parse(bundled).skills?.config;
15020
15140
  if (previousConfig !== undefined && (!Array.isArray(previousConfig) || previousConfig.some((entry) => !entry || typeof entry !== "object" || entry.path !== undefined && entry.name !== undefined)))
15021
15141
  throw new Error("Codex skill controls require one path or name selector per [[skills.config]] entry; review ambiguous entries before running skills hook install");
15022
15142
  const bridgeNeedsRepair = previousConfig?.some((entry) => entry.enabled === false && bridgeSelector(entry));
15023
15143
  const selections = [...skills.filter((entry) => entry.agent === "codex" && !entry.bridge).map((skill) => ({ path: skill.path, enabled: false })), { path: bridgePath, enabled: true }];
15024
15144
  for (const skill of selections) {
15025
- const path = join17(skill.path, "SKILL.md");
15145
+ const path = join18(skill.path, "SKILL.md");
15026
15146
  let found = false;
15027
15147
  result = result.replace(CODEX_SKILL_CONFIG_SECTIONS, (section) => {
15028
15148
  const parsed = Bun.TOML.parse(section);
15029
15149
  const declared = parsed.skills?.config?.[0];
15030
- if (!declared || !(skill.enabled && bridgeSelector(declared)) && !(typeof declared.path === "string" && [resolve10(path), resolve10(skill.path)].includes(canonicalAgentPath(declared.path, aliases))))
15150
+ if (!declared || !(skill.enabled && bridgeSelector(declared)) && !(typeof declared.path === "string" && [resolve11(path), resolve11(skill.path)].includes(canonicalAgentPath(declared.path, aliases))))
15031
15151
  return section;
15032
15152
  found = true;
15033
15153
  return /^\s*enabled\s*=/m.test(section) ? section.replace(/^\s*enabled\s*=.*$/m, `enabled = ${skill.enabled}`) : `${section.trimEnd()}
@@ -15048,14 +15168,14 @@ enabled = false
15048
15168
  return result;
15049
15169
  }
15050
15170
  function assertCodexInstallationInputs(home, projectDirs, inputs) {
15051
- const nativeRoots = [home, ...projectAncestorDirectories(projectDirs)].flatMap((directory) => ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => resolve10(directory, path)));
15171
+ const nativeRoots = [home, ...projectAncestorDirectories(projectDirs)].flatMap((directory) => ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => resolve11(directory, path)));
15052
15172
  if (inputs.some((input) => nativeRoots.some((root) => root === input || root.startsWith(input + sep5) || input.startsWith(root + sep5))))
15053
15173
  throw new Error("Native installation input overlaps an unconditional skill-loading root");
15054
15174
  }
15055
15175
  function planAgentIntegration(options) {
15056
15176
  const home = options.home ?? homedir2(), dataDir2 = options.dataDir ?? getDataDirReadOnly();
15057
15177
  const aliases = rootAliases(home, options.allowRootAliases);
15058
- const policyPath = join17(dataDir2, "agent-policy.json");
15178
+ const policyPath = join18(dataDir2, "agent-policy.json");
15059
15179
  assertSafePath(policyPath);
15060
15180
  const priorSnapshot = readManagedSkillPolicySnapshot(dataDir2), previousPolicy = priorSnapshot?.text ?? null, policy = priorSnapshot?.value ?? {};
15061
15181
  if (policy.bridge !== undefined && (!policy.bridge || typeof policy.bridge !== "object" || Array.isArray(policy.bridge)))
@@ -15087,10 +15207,10 @@ function planAgentIntegration(options) {
15087
15207
  }
15088
15208
  return resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, retainedReview, canonical: (path) => canonicalAgentPath(path, aliases) });
15089
15209
  });
15090
- const codexConfig = options.agents.includes("codex") ? Bun.TOML.parse(readOptional(canonicalAgentPath(join17(home, ".codex", "config.toml"), aliases)) ?? "") : {};
15210
+ const codexConfig = options.agents.includes("codex") ? Bun.TOML.parse(readOptional(canonicalAgentPath(join18(home, ".codex", "config.toml"), aliases)) ?? "") : {};
15091
15211
  const nativeSkills = inventoryNativeSkills(home, { includeVendor: true, guardHermes: options.agents.includes("hermes"), agents: options.agents, projectDir: options.projectDir, agentRoots: discoveries.flatMap((binding) => binding.roots.map((path) => ({ agent: binding.agent, path }))), allowRootAliases: options.allowRootAliases, reviewedCacheAlias: options.reviewedCacheAlias, disabledVendorPaths: disabledCodexSkillPaths(codexConfig, aliases) });
15092
15212
  const observedNativeSources = [];
15093
- const codexPluginControls = options.codexNativeCatalog ? reviewCodexPluginControls(options.codexNativeCatalog, nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor).map((entry) => join17(entry.path, "SKILL.md")), canonicalAgentPath(join17(home, ".codex/plugins/cache"), aliases), resolve10(options.projectDir ?? home), (path) => {
15213
+ const codexPluginControls = options.codexNativeCatalog ? reviewCodexPluginControls(options.codexNativeCatalog, nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor).map((entry) => join18(entry.path, "SKILL.md")), canonicalAgentPath(join18(home, ".codex/plugins/cache"), aliases), resolve11(options.projectDir ?? home), (path) => {
15094
15214
  const bytes = readNativeBytes(path, 1024 * 1024);
15095
15215
  observedNativeSources.push({ path, sha256: sha3(bytes) });
15096
15216
  return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
@@ -15098,7 +15218,7 @@ function planAgentIntegration(options) {
15098
15218
  const codexPluginSkills = codexPluginControls.skills;
15099
15219
  const codexInactivePlugins = codexPluginControls.inactivePlugins;
15100
15220
  const codexDiscovery = discoveries.find((binding) => binding.agent === "codex");
15101
- const codexPluginSourceInputs = codexPluginControls.sourceInputs.filter((input) => codexDiscovery?.method === "reviewed" && codexDiscovery.roots.some((root) => root === input.sourceRoot || root.startsWith(input.sourceRoot + sep5) || input.sourceRoot.startsWith(root + sep5)) && codexDiscovery.sources.some((source) => source.path === join17(input.sourceRoot, ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined));
15221
+ const codexPluginSourceInputs = codexPluginControls.sourceInputs.filter((input) => codexDiscovery?.method === "reviewed" && codexDiscovery.roots.some((root) => root === input.sourceRoot || root.startsWith(input.sourceRoot + sep5) || input.sourceRoot.startsWith(root + sep5)) && codexDiscovery.sources.some((source) => source.path === join18(input.sourceRoot, ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined));
15102
15222
  assertCodexInstallationInputs(home, [options.projectDir ?? home], codexPluginSourceInputs.map((input) => input.sourceRoot));
15103
15223
  const changes = [];
15104
15224
  for (const agent of [...new Set(options.agents)]) {
@@ -15106,19 +15226,19 @@ function planAgentIntegration(options) {
15106
15226
  throw new Error(`Unsupported agent: ${agent}`);
15107
15227
  const { command, profileId: profileId2 } = bindings.get(agent);
15108
15228
  const adapter = AGENT_ADAPTERS[agent];
15109
- const bridgePath = canonicalAgentPath(join17(home, adapter.root, CLI_BRIDGE_NAME), aliases);
15229
+ const bridgePath = canonicalAgentPath(join18(agent === "sumi" ? sumiBridgeRoot(home) : join18(home, adapter.root), CLI_BRIDGE_NAME), aliases);
15110
15230
  assertSafePath(bridgePath);
15111
- if (existsSync8(bridgePath) && !isOwnedCliBridge(bridgePath, [bridgePath]))
15231
+ if (existsSync9(bridgePath) && !isOwnedCliBridge(bridgePath, [bridgePath]))
15112
15232
  throw new Error(`Refusing to overwrite an unrecognized or modified Skills bridge: ${bridgePath}`);
15113
15233
  for (const [name, after2] of Object.entries(CLI_BRIDGE_FILES)) {
15114
- const path2 = join17(bridgePath, name), before2 = readOptional(path2);
15234
+ const path2 = join18(bridgePath, name), before2 = readOptional(path2);
15115
15235
  if (before2 !== after2)
15116
15236
  changes.push({ path: path2, before: before2, after: after2 });
15117
15237
  }
15118
- const path = canonicalAgentPath(join17(home, adapter.config), aliases);
15238
+ const path = canonicalAgentPath(agent === "sumi" ? sumiConfigPath(home) : join18(home, adapter.config), aliases);
15119
15239
  const before = readOptional(path);
15120
15240
  if (agent === "hermes") {
15121
- const supervisorPath = join17(dataDir2, "agent-hooks", "hermes.js"), supervisorBefore = readOptional(supervisorPath), supervisorAfter = renderHermesSupervisor(command, profileId2);
15241
+ const supervisorPath = join18(dataDir2, "agent-hooks", "hermes.js"), supervisorBefore = readOptional(supervisorPath), supervisorAfter = renderHermesSupervisor(command, profileId2);
15122
15242
  const priorCommand = policy.bridge?.commands?.hermes, priorProfile = policy.bridge?.profiles?.hermes ?? policy.profileId;
15123
15243
  const previous = policy.bridge?.supervisors?.hermes;
15124
15244
  if (supervisorBefore !== null && supervisorBefore !== supervisorAfter && !(previous?.path === supervisorPath && typeof priorCommand === "string" && typeof priorProfile === "string" && supervisorBefore === renderHermesSupervisor(priorCommand, priorProfile)))
@@ -15129,19 +15249,30 @@ function planAgentIntegration(options) {
15129
15249
  const after2 = configureHermesHooks(before, supervisor, previous);
15130
15250
  if (before !== after2)
15131
15251
  changes.push({ path, before, after: after2 });
15132
- const optOut = join17(home, ".hermes", HERMES_OPT_OUT), optOutBefore = readOptional(optOut);
15252
+ const optOut = join18(home, ".hermes", HERMES_OPT_OUT), optOutBefore = readOptional(optOut);
15133
15253
  if (optOutBefore === null)
15134
15254
  changes.push({ path: optOut, before: null, after: `Managed by @hasna/skills: bundled native skill reseeding is disabled.
15135
15255
  ` });
15136
15256
  continue;
15137
15257
  }
15138
15258
  const config = jsonObject(before, path);
15139
- if (agent === "opencode") {
15259
+ if (agent === "sumi") {
15260
+ if (config.permissions !== undefined && (!Array.isArray(config.permissions) || config.permissions.some((rule) => !rule || typeof rule.action !== "string" || typeof rule.resource !== "string" || !["allow", "deny", "ask"].includes(rule.effect))))
15261
+ throw new Error("Expected Sumi native permission rules");
15262
+ config.permissions = [...(config.permissions ?? []).filter((rule) => rule.action !== "skill"), ...SUMI_SKILL_PERMISSIONS];
15263
+ const pluginPath = join18(sumiConfigDirectory(home), "plugins", "skills-cli.js"), pluginBefore = readOptional(pluginPath);
15264
+ const pluginAfter = renderSumiPlugin(command, profileId2);
15265
+ const priorCommand = policy.bridge?.commands?.sumi, priorProfile = policy.bridge?.profiles?.sumi ?? policy.profileId;
15266
+ if (pluginBefore !== null && pluginBefore !== pluginAfter && !(typeof priorCommand === "string" && typeof priorProfile === "string" && pluginBefore === renderSumiPlugin(priorCommand, priorProfile)))
15267
+ throw new Error("Refusing to overwrite a modified Sumi Skills plugin; preserve and review it first");
15268
+ if (pluginBefore !== pluginAfter)
15269
+ changes.push({ path: pluginPath, before: pluginBefore, after: pluginAfter });
15270
+ } else if (agent === "opencode") {
15140
15271
  const permission = config.permission ?? {};
15141
15272
  if (!permission || typeof permission !== "object" || Array.isArray(permission))
15142
15273
  throw new Error("Expected OpenCode permission object");
15143
15274
  config.permission = { ...permission, skill: { "*": "deny", [CLI_BRIDGE_NAME]: "allow" } };
15144
- const pluginPath = join17(home, ".config", "opencode", "plugins", "skills-cli.js"), pluginBefore = readOptional(pluginPath);
15275
+ const pluginPath = join18(home, ".config", "opencode", "plugins", "skills-cli.js"), pluginBefore = readOptional(pluginPath);
15145
15276
  const pluginAfter = renderOpenCodePlugin(command, profileId2);
15146
15277
  const priorCommand = policy.bridge?.commands?.opencode;
15147
15278
  const ownedPrevious = typeof priorCommand === "string" && typeof policy.profileId === "string" && pluginBefore === renderOpenCodePlugin(priorCommand, policy.bridge?.profiles?.opencode ?? policy.profileId);
@@ -15158,13 +15289,13 @@ function planAgentIntegration(options) {
15158
15289
  if (before !== after)
15159
15290
  changes.push({ path, before, after });
15160
15291
  if (agent === "codex") {
15161
- const configPath = canonicalAgentPath(join17(home, ".codex", "config.toml"), aliases), previous = readOptional(configPath);
15292
+ const configPath = canonicalAgentPath(join18(home, ".codex", "config.toml"), aliases), previous = readOptional(configPath);
15162
15293
  const next = disableReviewedCodexPluginNames(disableCodexSkills(previous ?? "", nativeSkills, aliases, bridgePath), codexPluginSkills);
15163
15294
  if (next !== (previous ?? ""))
15164
15295
  changes.push({ path: configPath, before: previous, after: next });
15165
15296
  }
15166
15297
  }
15167
- const codexPluginSkillReview = options.codexNativeCatalog ? { version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, catalogSha256: sha3(JSON.stringify({ version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, skills: projectCodexNativeSkillCatalog({ data: [{ cwd: options.codexNativeCatalog.cwd, errors: [], skills: options.codexNativeCatalog.skills }] }, options.codexNativeCatalog.cwd), ...options.codexNativeCatalog.plugins === undefined ? {} : { plugins: projectCodexInstalledPluginEntries(options.codexNativeCatalog.plugins) } })), configSha256: changes.find((change) => change.path === canonicalAgentPath(join17(home, ".codex/config.toml"), aliases))?.before === null ? null : sha3(readOptional(canonicalAgentPath(join17(home, ".codex/config.toml"), aliases)) ?? "") } : policy.bridge?.codexPluginSkillReview;
15298
+ const codexPluginSkillReview = options.codexNativeCatalog ? { version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, catalogSha256: sha3(JSON.stringify({ version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, skills: projectCodexNativeSkillCatalog({ data: [{ cwd: options.codexNativeCatalog.cwd, errors: [], skills: options.codexNativeCatalog.skills }] }, options.codexNativeCatalog.cwd), ...options.codexNativeCatalog.plugins === undefined ? {} : { plugins: projectCodexInstalledPluginEntries(options.codexNativeCatalog.plugins) } })), configSha256: changes.find((change) => change.path === canonicalAgentPath(join18(home, ".codex/config.toml"), aliases))?.before === null ? null : sha3(readOptional(canonicalAgentPath(join18(home, ".codex/config.toml"), aliases)) ?? "") } : policy.bridge?.codexPluginSkillReview;
15168
15299
  const discoveryAfter = discoveries.map((binding) => {
15169
15300
  const rebound = rebindAgentDiscovery(binding, new Map(changes.map((change) => [change.path, change.after])));
15170
15301
  if (binding.agent !== "codex")
@@ -15179,12 +15310,12 @@ function planAgentIntegration(options) {
15179
15310
  version: CLI_BRIDGE_VERSION,
15180
15311
  digest: CLI_BRIDGE_DIGEST,
15181
15312
  agents: [...new Set([...priorAgents, ...options.agents])].sort(),
15182
- home: resolve10(home),
15313
+ home: resolve11(home),
15183
15314
  includeVendor: true,
15184
- ...options.agents.includes("hermes") ? { supervisors: { ...policy.bridge?.supervisors, hermes: { path: join17(dataDir2, "agent-hooks", "hermes.js"), runtime: process.execPath, sha256: sha3(renderHermesSupervisor(bindings.get("hermes").command, bindings.get("hermes").profileId)) } } } : {},
15315
+ ...options.agents.includes("hermes") ? { supervisors: { ...policy.bridge?.supervisors, hermes: { path: join18(dataDir2, "agent-hooks", "hermes.js"), runtime: process.execPath, sha256: sha3(renderHermesSupervisor(bindings.get("hermes").command, bindings.get("hermes").profileId)) } } } : {},
15185
15316
  commands: { ...policy.bridge?.commands, ...Object.fromEntries([...bindings].map(([agent, binding]) => [agent, binding.command])) },
15186
15317
  profiles: Object.fromEntries([...new Set([...priorAgents, ...options.agents])].sort().map((agent) => [agent, bindings.get(agent)?.profileId ?? policy.bridge?.profiles?.[agent] ?? policy.profileId])),
15187
- disabledBuiltins: options.agents.includes("codex") ? nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor && entry.path.startsWith(canonicalAgentPath(join17(home, ".codex", "skills", ".system"), aliases) + sep5)).map((entry) => ({ path: entry.path, hash: entry.hash })) : policy.bridge?.disabledBuiltins ?? [],
15318
+ disabledBuiltins: options.agents.includes("codex") ? nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor && entry.path.startsWith(canonicalAgentPath(join18(home, ".codex", "skills", ".system"), aliases) + sep5)).map((entry) => ({ path: entry.path, hash: entry.hash })) : policy.bridge?.disabledBuiltins ?? [],
15188
15319
  rootAliases: aliases,
15189
15320
  ...options.codexNativeCatalog || codexPluginSkills.length || codexInactivePlugins.length || codexPluginSourceInputs.length ? { codexPluginSkills, codexInactivePlugins, codexPluginSkillReview } : {},
15190
15321
  discovery: { ...policy.bridge?.discovery, ...Object.fromEntries(discoveryAfter.map((binding) => [binding.agent, binding])) }
@@ -15207,7 +15338,7 @@ function claudeSettingsConsumers(home, path, discoveries, aliases) {
15207
15338
  const distance = relative5(canonical5(root), path);
15208
15339
  return distance === "" || distance !== ".." && !distance.startsWith(`..${sep5}`) && !isAbsolute11(distance);
15209
15340
  };
15210
- return discoveries.filter((binding) => binding.agent === "claude" || binding.agent === "opencode" || canonical5(join17(home, AGENT_ADAPTERS[binding.agent].config)) === path || binding.sources.some((source) => canonical5(source.path) === path) || binding.roots.some(containsSettings) || binding.directories?.some((directory) => containsSettings(directory.path))).map((binding) => binding.agent);
15341
+ return discoveries.filter((binding) => binding.agent === "claude" || binding.agent === "opencode" || canonical5(join18(home, AGENT_ADAPTERS[binding.agent].config)) === path || binding.sources.some((source) => canonical5(source.path) === path) || binding.roots.some(containsSettings) || binding.directories?.some((directory) => containsSettings(directory.path))).map((binding) => binding.agent);
15211
15342
  }
15212
15343
  function verifyCoordinatedDiscovery(binding, home, aliases) {
15213
15344
  try {
@@ -15224,8 +15355,8 @@ function verifyCoordinatedDiscovery(binding, home, aliases) {
15224
15355
  }
15225
15356
  }
15226
15357
  function planClaudeHookEventsUpdate(options) {
15227
- const home = resolve10(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
15228
- const policyPath = join17(dataDir2, "agent-policy.json");
15358
+ const home = resolve11(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
15359
+ const policyPath = join18(dataDir2, "agent-policy.json");
15229
15360
  assertSafePath(policyPath);
15230
15361
  const snapshot = readManagedSkillPolicySnapshot(dataDir2);
15231
15362
  if (!snapshot)
@@ -15236,9 +15367,9 @@ function planClaudeHookEventsUpdate(options) {
15236
15367
  }
15237
15368
  const aliases = bridge.rootAliases ?? [];
15238
15369
  recheckRootAliases(aliases);
15239
- const path = canonicalAgentPath(join17(home, ".claude", "settings.json"), aliases);
15370
+ const path = canonicalAgentPath(join18(home, ".claude", "settings.json"), aliases);
15240
15371
  assertSafePath(path);
15241
- const before = lstatSync15(path, { throwIfNoEntry: false }) ? new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(readNativeBytes(path, 1024 * 1024)) : null;
15372
+ const before = lstatSync16(path, { throwIfNoEntry: false }) ? new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(readNativeBytes(path, 1024 * 1024)) : null;
15242
15373
  if ((before === null ? "absent" : sha3(before)) !== options.expectedSettingsSha256)
15243
15374
  throw new Error("Claude settings preimage changed");
15244
15375
  assertClaudeHookEventsReplacement(before, options.replacement, options.events);
@@ -15298,14 +15429,14 @@ function atomicWrite2(path, content) {
15298
15429
  writeFileSync4(temporary, content, { mode: 384, flag: "wx" });
15299
15430
  renameSync2(temporary, path);
15300
15431
  } finally {
15301
- if (existsSync8(temporary))
15432
+ if (existsSync9(temporary))
15302
15433
  unlinkSync3(temporary);
15303
15434
  }
15304
15435
  }
15305
15436
  function planAgentSettingsWitnessUpgrade(options) {
15306
15437
  if (options.targetCodexVersion !== undefined && (options.agent !== "codex" || ![2, 3].includes(options.targetCodexVersion)))
15307
15438
  throw new Error("Invalid Codex witness target version");
15308
- const home = resolve10(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
15439
+ const home = resolve11(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
15309
15440
  const snapshot = readManagedSkillPolicySnapshot(dataDir2);
15310
15441
  if (!snapshot || sha3(snapshot.text) !== options.expectedPolicySha256)
15311
15442
  throw new Error("Managed policy preimage changed");
@@ -15314,7 +15445,7 @@ function planAgentSettingsWitnessUpgrade(options) {
15314
15445
  const binding = policy.bridge?.discovery?.[options.agent];
15315
15446
  if (!binding || binding.agent !== options.agent || binding.method !== "reviewed")
15316
15447
  throw new Error("Settings witness upgrade requires an existing reviewed native binding");
15317
- const configPath = canonicalAgentPath(join17(home, options.agent === "claude" ? ".claude/settings.json" : ".codex/config.toml"), aliases);
15448
+ const configPath = canonicalAgentPath(join18(home, options.agent === "claude" ? ".claude/settings.json" : ".codex/config.toml"), aliases);
15318
15449
  const settings = readOptional(configPath);
15319
15450
  if (settings === null || sha3(settings) !== options.expectedSettingsSha256)
15320
15451
  throw new Error("Native settings preimage changed");
@@ -15353,7 +15484,7 @@ function planAgentSettingsWitnessUpgrade(options) {
15353
15484
  const replacement = { ...binding, sources: rebound };
15354
15485
  verifyAgentDiscovery(replacement);
15355
15486
  const after = serializeManagedSkillPolicy({ ...policy, bridge: { ...policy.bridge, discovery: { ...policy.bridge.discovery, [options.agent]: replacement } } });
15356
- return { dataDir: dataDir2, profileId: policy.profileId, changes: [{ path: join17(dataDir2, "agent-policy.json"), before: snapshot.text, after }], nativeSkills: [], observedPolicy: { path: join17(dataDir2, "agent-policy.json"), before: snapshot.text }, observedSettings: { path: configPath, before: settings }, discoveryBefore: [replacement], discoveryAfter: [replacement], rootAliases: aliases, managedAgentChecks: { home, agents: [options.agent] }, settingsWitnessUpgrade: { agent: options.agent, path: configPath, fromHashMode: previous.hashMode ?? "bytes", fromSha256: previous.sha256, toHashMode: next.hashMode, toSha256: next.sha256, reviewedPreimage: options.reviewedPreimage, currentSettingsSha256: options.expectedSettingsSha256, replacedWitnesses: replaced.map((source) => ({ hashMode: source.hashMode ?? "bytes", sha256: source.sha256 })) } };
15487
+ return { dataDir: dataDir2, profileId: policy.profileId, changes: [{ path: join18(dataDir2, "agent-policy.json"), before: snapshot.text, after }], nativeSkills: [], observedPolicy: { path: join18(dataDir2, "agent-policy.json"), before: snapshot.text }, observedSettings: { path: configPath, before: settings }, discoveryBefore: [replacement], discoveryAfter: [replacement], rootAliases: aliases, managedAgentChecks: { home, agents: [options.agent] }, settingsWitnessUpgrade: { agent: options.agent, path: configPath, fromHashMode: previous.hashMode ?? "bytes", fromSha256: previous.sha256, toHashMode: next.hashMode, toSha256: next.sha256, reviewedPreimage: options.reviewedPreimage, currentSettingsSha256: options.expectedSettingsSha256, replacedWitnesses: replaced.map((source) => ({ hashMode: source.hashMode ?? "bytes", sha256: source.sha256 })) } };
15357
15488
  }
15358
15489
  function applyAgentIntegration(plan) {
15359
15490
  for (const source of plan.observedNativeSources ?? [])
@@ -15361,12 +15492,12 @@ function applyAgentIntegration(plan) {
15361
15492
  throw new Error("Native identity source changed after planning");
15362
15493
  if (plan.observedSettings && readOptional(plan.observedSettings.path) !== plan.observedSettings.before)
15363
15494
  throw new Error("Native settings changed after witness planning");
15364
- const policyPath = join17(plan.dataDir, "agent-policy.json");
15495
+ const policyPath = join18(plan.dataDir, "agent-policy.json");
15365
15496
  assertSafePath(policyPath);
15366
- const currentText = (path) => resolve10(path) === resolve10(policyPath) ? readManagedSkillPolicySnapshot(plan.dataDir)?.text ?? null : readOptional(path);
15497
+ const currentText = (path) => resolve11(path) === resolve11(policyPath) ? readManagedSkillPolicySnapshot(plan.dataDir)?.text ?? null : readOptional(path);
15367
15498
  readManagedSkillPolicySnapshot(plan.dataDir);
15368
15499
  for (const change of plan.changes)
15369
- if (resolve10(change.path) === resolve10(policyPath))
15500
+ if (resolve11(change.path) === resolve11(policyPath))
15370
15501
  parseManagedSkillPolicy(change.after);
15371
15502
  const aliases = plan.rootAliases ?? [];
15372
15503
  recheckRootAliases(aliases);
@@ -15395,7 +15526,7 @@ function applyAgentIntegration(plan) {
15395
15526
  for (const change of plan.changes)
15396
15527
  if (currentText(change.path) !== change.before)
15397
15528
  throw new Error(`Configuration changed after planning: ${change.path}`);
15398
- const backupRoot = join17(plan.dataDir, "migration", randomUUID2()), backups = [], written = [], createdDirectories = new Set;
15529
+ const backupRoot = join18(plan.dataDir, "migration", randomUUID2()), backups = [], written = [], createdDirectories = new Set;
15399
15530
  if (!plan.changes.length) {
15400
15531
  recheckRootAliases(aliases);
15401
15532
  return { changed: [], backups, ...aliases.length ? { rootAliases: aliases } : {} };
@@ -15405,7 +15536,7 @@ function applyAgentIntegration(plan) {
15405
15536
  chmodSync(backupRoot, 448);
15406
15537
  for (const [index, change] of plan.changes.entries())
15407
15538
  if (change.before !== null) {
15408
- const backup = join17(backupRoot, `${index}-${sha3(change.path).slice(0, 12)}.backup`);
15539
+ const backup = join18(backupRoot, `${index}-${sha3(change.path).slice(0, 12)}.backup`);
15409
15540
  writeFileSync4(backup, change.before, { mode: 384, flag: "wx" });
15410
15541
  if (readFileSync12(backup, "utf8") !== change.before)
15411
15542
  throw new Error("Native configuration preservation readback failed");
@@ -15416,7 +15547,7 @@ function applyAgentIntegration(plan) {
15416
15547
  recheckRootAliases(aliases);
15417
15548
  if (currentText(change.path) !== change.before)
15418
15549
  throw new Error(`Configuration changed after planning: ${change.path}`);
15419
- for (let directory = dirname11(change.path);!existsSync8(directory); directory = dirname11(directory))
15550
+ for (let directory = dirname11(change.path);!existsSync9(directory); directory = dirname11(directory))
15420
15551
  createdDirectories.add(directory);
15421
15552
  const after = change.after;
15422
15553
  atomicWrite2(change.path, after);
@@ -15436,7 +15567,7 @@ function applyAgentIntegration(plan) {
15436
15567
  }
15437
15568
  for (const agent of plan.managedAgentChecks?.agents ?? [])
15438
15569
  assertManagedAgentBridge(agent, { home: plan.managedAgentChecks.home, dataDir: plan.dataDir, projectDir: plan.managedAgentChecks.home });
15439
- atomicWrite2(join17(backupRoot, "receipt.json"), JSON.stringify({ version: 1, changes: written.map((change) => ({ path: change.path, beforeHash: change.before === null ? null : sha3(change.before), afterHash: sha3(change.after) })), backups, ...aliases.length ? { rootAliases: aliases } : {} }) + `
15570
+ atomicWrite2(join18(backupRoot, "receipt.json"), JSON.stringify({ version: 1, changes: written.map((change) => ({ path: change.path, beforeHash: change.before === null ? null : sha3(change.before), afterHash: sha3(change.after) })), backups, ...aliases.length ? { rootAliases: aliases } : {} }) + `
15440
15571
  `);
15441
15572
  } catch (error) {
15442
15573
  for (const change of written.reverse()) {
@@ -15490,7 +15621,7 @@ function writeArchiveJournal(path, value) {
15490
15621
  } finally {
15491
15622
  if (descriptor !== undefined)
15492
15623
  closeSync8(descriptor);
15493
- if (lstatSync15(temporary, { throwIfNoEntry: false }))
15624
+ if (lstatSync16(temporary, { throwIfNoEntry: false }))
15494
15625
  unlinkSync3(temporary);
15495
15626
  }
15496
15627
  }
@@ -15501,7 +15632,7 @@ function archiveNativeSkills(inventory, options) {
15501
15632
  recheckRootAliases(aliases);
15502
15633
  for (const entry of inventory.filter((entry2) => entry2.bridge)) {
15503
15634
  const adapter = AGENT_ADAPTERS[entry.agent];
15504
- const expected = adapter && entry.bridgeHome ? canonicalAgentPath(join17(entry.bridgeHome, adapter.root, CLI_BRIDGE_NAME), aliases) : undefined;
15635
+ const expected = adapter && entry.bridgeHome ? canonicalAgentPath(join18(entry.agent === "sumi" ? sumiBridgeRoot(entry.bridgeHome) : join18(entry.bridgeHome, adapter.root), CLI_BRIDGE_NAME), aliases) : undefined;
15505
15636
  if (!expected || treeHash(entry.path) !== entry.hash || !isOwnedCliBridge(entry.path, [expected]))
15506
15637
  throw new Error(`Skills bridge changed after planning: ${entry.path}`);
15507
15638
  }
@@ -15512,11 +15643,11 @@ function archiveNativeSkills(inventory, options) {
15512
15643
  const targetManifest = options.targetManifest ? { schema: options.targetManifest.schema, digest: options.targetManifest.digest, targetCount: options.targetManifest.targets.length } : undefined;
15513
15644
  if (!selected.length)
15514
15645
  return { entries: [], ...targetManifest ? { targetManifest } : {}, ...aliases.length ? { rootAliases: aliases } : {} };
15515
- const operationId = randomUUID2(), archiveRoot = join17(options.dataDir ?? getDataDir(), "migration", operationId, "native"), receiptPath = join17(archiveRoot, "receipt.json");
15646
+ const operationId = randomUUID2(), archiveRoot = join18(options.dataDir ?? getDataDir(), "migration", operationId, "native"), receiptPath = join18(archiveRoot, "receipt.json");
15516
15647
  const entries = selected.map((entry, index) => ({
15517
- source: entry.vendor ? join17(entry.path, "SKILL.md") : entry.path,
15518
- archive: join17(archiveRoot, `${index}-${sha3(entry.path).slice(0, 12)}`),
15519
- hash: entry.vendor ? sha3(readNativeBytes(join17(entry.path, "SKILL.md"))) : entry.hash,
15648
+ source: entry.vendor ? join18(entry.path, "SKILL.md") : entry.path,
15649
+ archive: join18(archiveRoot, `${index}-${sha3(entry.path).slice(0, 12)}`),
15650
+ hash: entry.vendor ? sha3(readNativeBytes(join18(entry.path, "SKILL.md"))) : entry.hash,
15520
15651
  ...entry.vendor ? { discoveryOnly: true } : {},
15521
15652
  status: "planned"
15522
15653
  }));
@@ -15524,7 +15655,7 @@ function archiveNativeSkills(inventory, options) {
15524
15655
  const moved = [];
15525
15656
  const verifyArchive = (entry) => {
15526
15657
  assertSafePath(entry.archive);
15527
- const stat = lstatSync15(entry.archive);
15658
+ const stat = lstatSync16(entry.archive);
15528
15659
  if (entry.discoveryOnly ? !stat.isFile() : !stat.isDirectory())
15529
15660
  throw new Error("Archive type changed");
15530
15661
  if ((entry.discoveryOnly ? sha3(readNativeBytes(entry.archive)) : treeHash(entry.archive)) !== entry.hash)
@@ -15532,7 +15663,7 @@ function archiveNativeSkills(inventory, options) {
15532
15663
  };
15533
15664
  assertSafePath(archiveRoot);
15534
15665
  const created = [];
15535
- for (let path = archiveRoot;!existsSync8(path); path = dirname11(path))
15666
+ for (let path = archiveRoot;!existsSync9(path); path = dirname11(path))
15536
15667
  created.push(path);
15537
15668
  mkdirSync5(archiveRoot, { recursive: true, mode: 448 });
15538
15669
  try {
@@ -15572,7 +15703,7 @@ function archiveNativeSkills(inventory, options) {
15572
15703
  recheckRootAliases(aliases);
15573
15704
  assertSafePath(entry.source);
15574
15705
  assertSafePath(entry.archive);
15575
- if (lstatSync15(entry.source, { throwIfNoEntry: false })) {
15706
+ if (lstatSync16(entry.source, { throwIfNoEntry: false })) {
15576
15707
  entry.status = "recovery-required";
15577
15708
  entry.conflict = "source-occupied";
15578
15709
  continue;
@@ -15589,7 +15720,7 @@ function archiveNativeSkills(inventory, options) {
15589
15720
  syncArchiveDirectory(dirname11(entry.source));
15590
15721
  unlinkSync3(entry.archive);
15591
15722
  } else {
15592
- if (lstatSync15(entry.source, { throwIfNoEntry: false })) {
15723
+ if (lstatSync16(entry.source, { throwIfNoEntry: false })) {
15593
15724
  entry.status = "recovery-required";
15594
15725
  entry.conflict = "source-occupied";
15595
15726
  continue;
@@ -15620,8 +15751,8 @@ function assertManagedAgentBridge(agent, options = {}) {
15620
15751
  assertManagedAgentBridgeWithDiscovery(agent, options);
15621
15752
  }
15622
15753
  function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscovery) {
15623
- const home = resolve10(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
15624
- assertSafePath(join17(dataDir2, "agent-policy.json"));
15754
+ const home = resolve11(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
15755
+ assertSafePath(join18(dataDir2, "agent-policy.json"));
15625
15756
  const snapshot = readManagedSkillPolicySnapshot(dataDir2);
15626
15757
  if (!snapshot)
15627
15758
  throw new Error("NATIVE_SKILL_DRIFT: install the Skills bridge with skills hook install");
@@ -15632,14 +15763,14 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
15632
15763
  if (!Array.isArray(aliases))
15633
15764
  throw new Error("NATIVE_SKILL_DRIFT: invalid root alias binding");
15634
15765
  recheckRootAliases(aliases);
15635
- const expected = canonicalAgentPath(join17(home, AGENT_ADAPTERS[agent].root, CLI_BRIDGE_NAME), aliases);
15766
+ const expected = canonicalAgentPath(join18(agent === "sumi" ? sumiBridgeRoot(home) : join18(home, AGENT_ADAPTERS[agent].root), CLI_BRIDGE_NAME), aliases);
15636
15767
  if (!isOwnedCliBridge(expected, [expected]))
15637
15768
  throw new Error("NATIVE_SKILL_DRIFT: the native Skills bridge is missing or modified; repair it before continuing");
15638
15769
  const roots = projectAncestorDirectories([options.projectDir ?? process.cwd(), ...options.projectDirs ?? [], ...agent === "hermes" && process.env.TERMINAL_CWD ? [process.cwd()] : []]);
15639
- const visible = (entry) => entry.agent === agent || ["codex", "gemini", "opencode", "hermes"].includes(agent) && entry.path.includes(`${sep5}.agents${sep5}skills${sep5}`) || agent === "opencode" && entry.agent === "claude";
15770
+ const visible = (entry) => entry.agent === agent || ["codex", "gemini", "opencode", "hermes", "sumi"].includes(agent) && entry.path.includes(`${sep5}.agents${sep5}skills${sep5}`) || ["opencode", "sumi"].includes(agent) && entry.agent === "claude";
15640
15771
  const discovery = provenDiscovery ?? binding.discovery?.[agent];
15641
15772
  assertProjectDiscovery(agent, [...roots], home, (path) => canonicalAgentPath(path, aliases), discovery);
15642
- const configPath = canonicalAgentPath(join17(home, AGENT_ADAPTERS[agent].config), aliases), config = agent === "hermes" ? parseHermesConfig(readOptional(configPath)) : jsonObject(readOptional(configPath), configPath);
15773
+ const configPath = canonicalAgentPath(agent === "sumi" ? sumiConfigPath(home) : join18(home, AGENT_ADAPTERS[agent].config), aliases), config = agent === "hermes" ? parseHermesConfig(readOptional(configPath)) : jsonObject(readOptional(configPath), configPath);
15643
15774
  const command = binding.commands?.[agent], profile = binding.profiles?.[agent];
15644
15775
  if (typeof command !== "string" || typeof profile !== "string")
15645
15776
  throw new Error("NATIVE_SKILL_DRIFT: the native hook command/profile binding is missing");
@@ -15647,11 +15778,14 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
15647
15778
  throw new Error("NATIVE_SKILL_DRIFT: the hook selection profile differs from its managed binding; restart the native client after reviewing skills hook install");
15648
15779
  if (agent === "hermes") {
15649
15780
  const supervisor = binding.supervisors?.hermes;
15650
- if (supervisor?.path !== join17(dataDir2, "agent-hooks", "hermes.js") || supervisor?.sha256 !== sha3(renderHermesSupervisor(command, profile)))
15781
+ if (supervisor?.path !== join18(dataDir2, "agent-hooks", "hermes.js") || supervisor?.sha256 !== sha3(renderHermesSupervisor(command, profile)))
15651
15782
  throw new Error("NATIVE_SKILL_DRIFT: Hermes supervisor binding changed; run skills hook install");
15652
15783
  assertHermesProtection(home, config, command, profile, supervisor);
15784
+ } else if (agent === "sumi") {
15785
+ if (JSON.stringify(config.permissions?.filter((rule) => rule.action === "skill")) !== JSON.stringify(SUMI_SKILL_PERMISSIONS) || readOptional(join18(sumiConfigDirectory(home), "plugins", "skills-cli.js")) !== renderSumiPlugin(command, profile))
15786
+ throw new Error("NATIVE_SKILL_DRIFT: Sumi bridge protection changed; run skills hook install");
15653
15787
  } else if (agent === "opencode") {
15654
- if (JSON.stringify(config.permission?.skill) !== JSON.stringify({ "*": "deny", [CLI_BRIDGE_NAME]: "allow" }) || readOptional(join17(home, ".config", "opencode", "plugins", "skills-cli.js")) !== renderOpenCodePlugin(command, profile))
15788
+ if (JSON.stringify(config.permission?.skill) !== JSON.stringify({ "*": "deny", [CLI_BRIDGE_NAME]: "allow" }) || readOptional(join18(home, ".config", "opencode", "plugins", "skills-cli.js")) !== renderOpenCodePlugin(command, profile))
15655
15789
  throw new Error("NATIVE_SKILL_DRIFT: OpenCode bridge protection changed; run skills hook install");
15656
15790
  } else {
15657
15791
  const required = {};
@@ -15668,21 +15802,21 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
15668
15802
  if (agent === "gemini" && (config.hooksConfig?.enabled === false || config.skills?.enabled !== true || !["antigravity-support", "skill-creator"].every((name) => config.skills?.disabled?.includes(name)) || config.skills?.disabled?.includes(CLI_BRIDGE_NAME)))
15669
15803
  throw new Error("NATIVE_SKILL_DRIFT: Gemini bridge or bundled-skill protection changed; run skills hook install");
15670
15804
  }
15671
- const codexPath = canonicalAgentPath(join17(home, ".codex", "config.toml"), aliases);
15805
+ const codexPath = canonicalAgentPath(join18(home, ".codex", "config.toml"), aliases);
15672
15806
  if (options.codexDiscoveryRecovery && (agent !== "codex" || options.codexDiscoveryRecovery.configPath !== codexPath))
15673
15807
  throw new Error("NATIVE_SKILL_DRIFT: Codex trust recovery names a different configuration root");
15674
15808
  const codexConfig = agent === "codex" ? Bun.TOML.parse(readOptional(codexPath) ?? "") : {};
15675
15809
  if (agent === "codex" && codexConfig.skills?.bundled?.enabled !== false)
15676
15810
  throw new Error("NATIVE_SKILL_DRIFT: Codex bundled skill reseeding is not disabled (skills.bundled.enabled); run skills hook install");
15677
- const setting = (path) => (codexConfig.skills?.config ?? []).filter((entry) => typeof entry.path === "string" && [path, join17(path, "SKILL.md")].includes(canonicalAgentPath(entry.path, aliases)));
15811
+ const setting = (path) => (codexConfig.skills?.config ?? []).filter((entry) => typeof entry.path === "string" && [path, join18(path, "SKILL.md")].includes(canonicalAgentPath(entry.path, aliases)));
15678
15812
  if (agent === "codex" && [...setting(expected), ...(codexConfig.skills?.config ?? []).filter((entry) => typeof entry.name === "string" && entry.name.trim() === CLI_BRIDGE_NAME)].some((entry) => entry.enabled === false))
15679
15813
  throw new Error("NATIVE_SKILL_DRIFT: the Codex CLI bridge is disabled; run skills hook install");
15680
- if (agent === "codex" && !reviewedCodexPluginCapabilitiesUnchanged(canonicalAgentPath(join17(home, ".codex/plugins/cache"), aliases), binding.codexPluginSkills ?? [], (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024))))
15814
+ if (agent === "codex" && !reviewedCodexPluginCapabilitiesUnchanged(canonicalAgentPath(join18(home, ".codex/plugins/cache"), aliases), binding.codexPluginSkills ?? [], (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024))))
15681
15815
  throw new Error("NATIVE_SKILL_DRIFT: reviewed native plugin capability controls changed; run skills hook install with a fresh discovery review");
15682
15816
  const disabledPlugin = (entry) => {
15683
15817
  if (agent !== "codex" || !entry.vendor)
15684
15818
  return false;
15685
- const document2 = join17(entry.path, "SKILL.md"), cache = canonicalAgentPath(join17(home, ".codex/plugins/cache"), aliases), read2 = (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024));
15819
+ const document2 = join18(entry.path, "SKILL.md"), cache = canonicalAgentPath(join18(home, ".codex/plugins/cache"), aliases), read2 = (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024));
15686
15820
  return isReviewedCodexPluginInactive(document2, cache, binding.codexInactivePlugins ?? [], codexConfig.plugins, codexConfig.skills?.config ?? [], read2) || isReviewedCodexPluginSkillDisabled(document2, cache, binding.codexPluginSkills ?? [], codexConfig.skills?.config, read2);
15687
15821
  };
15688
15822
  const disabledPaths = new Set(disabledCodexSkillPaths(codexConfig, aliases));
@@ -15695,15 +15829,15 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
15695
15829
  return true;
15696
15830
  if (!disabledPaths.has(entry.path))
15697
15831
  return false;
15698
- if (entry.path.startsWith(canonicalAgentPath(join17(home, ".codex", "plugins", "cache"), aliases) + sep5))
15832
+ if (entry.path.startsWith(canonicalAgentPath(join18(home, ".codex", "plugins", "cache"), aliases) + sep5))
15699
15833
  return true;
15700
- const runtimeCache = canonicalAgentPath(join17(home, ".cache", "codex-runtimes"), aliases);
15834
+ const runtimeCache = canonicalAgentPath(join18(home, ".cache", "codex-runtimes"), aliases);
15701
15835
  if (entry.path.startsWith(runtimeCache + sep5) && discovery?.method === "automatic" && discovery.roots.some((root) => entry.path.startsWith(root + sep5)))
15702
15836
  return true;
15703
- const bundledMarketplaces = canonicalAgentPath(join17(home, ".codex", ".tmp", "bundled-marketplaces"), aliases);
15837
+ const bundledMarketplaces = canonicalAgentPath(join18(home, ".codex", ".tmp", "bundled-marketplaces"), aliases);
15704
15838
  if (entry.path.startsWith(bundledMarketplaces + sep5) && discovery?.method === "automatic" && discovery.roots.some((root) => entry.path.startsWith(root + sep5)))
15705
15839
  return true;
15706
- if (!entry.path.startsWith(canonicalAgentPath(join17(home, ".codex", "skills", ".system"), aliases) + sep5))
15840
+ if (!entry.path.startsWith(canonicalAgentPath(join18(home, ".codex", "skills", ".system"), aliases) + sep5))
15707
15841
  return false;
15708
15842
  return Array.isArray(binding.disabledBuiltins) && binding.disabledBuiltins.some((item) => item.path === entry.path && item.hash === entry.hash);
15709
15843
  };
@@ -15713,6 +15847,8 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
15713
15847
  throw new Error("NATIVE_SKILL_DRIFT: an installed Gemini builtin is not disabled");
15714
15848
  try {
15715
15849
  verifyAgentDiscovery(discovery, options.codexDiscoveryRecovery);
15850
+ if (agent === "sumi" && discovery.method === "reviewed")
15851
+ resolveAgentDiscovery({ home, agent, retainedReview: discovery, canonical: (path) => canonicalAgentPath(path, aliases) });
15716
15852
  if (discovery.method === "automatic") {
15717
15853
  const current = resolveAgentDiscovery({ home, agent, canonical: (path) => canonicalAgentPath(path, aliases) });
15718
15854
  if (JSON.stringify(current) !== JSON.stringify(discovery))
@@ -15724,7 +15860,7 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
15724
15860
  let installationInputRoots = [];
15725
15861
  if (agent === "codex") {
15726
15862
  try {
15727
- installationInputRoots = reviewedCodexPluginSourceRoots(canonicalAgentPath(join17(home, ".codex/plugins/cache"), aliases), discovery.codexInstallationInputs?.plugins ?? [], (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)));
15863
+ installationInputRoots = reviewedCodexPluginSourceRoots(canonicalAgentPath(join18(home, ".codex/plugins/cache"), aliases), discovery.codexInstallationInputs?.plugins ?? [], (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)));
15728
15864
  } catch {
15729
15865
  throw new Error("NATIVE_SKILL_DRIFT: native installation input identity changed; review discovery and catalog");
15730
15866
  }
@@ -15759,9 +15895,9 @@ function isSkillsOwnershipMarker(marker) {
15759
15895
  return typeof marker === "object" && marker !== null && Object.hasOwn(marker, "managedBy") && marker.managedBy === SYNC_MARKER_MANAGED_BY;
15760
15896
  }
15761
15897
  function hasSkillsOwnershipMarker(dir) {
15762
- const path = join18(dir, SYNC_MARKER_FILE);
15898
+ const path = join19(dir, SYNC_MARKER_FILE);
15763
15899
  try {
15764
- if (!lstatSync16(path).isFile())
15900
+ if (!lstatSync17(path).isFile())
15765
15901
  return false;
15766
15902
  const marker = JSON.parse(readFileSync13(path, "utf8"));
15767
15903
  return isSkillsOwnershipMarker(marker);
@@ -15783,9 +15919,9 @@ function resolveSyncAgents(arg) {
15783
15919
  function agentGlobalSkillsDir(agent, homeDir2 = homedir3()) {
15784
15920
  switch (agent) {
15785
15921
  case "opencode":
15786
- return join18(homeDir2, ".config", "opencode", "skills");
15922
+ return join19(homeDir2, ".config", "opencode", "skills");
15787
15923
  default:
15788
- return join18(homeDir2, `.${agent}`, "skills");
15924
+ return join19(homeDir2, `.${agent}`, "skills");
15789
15925
  }
15790
15926
  }
15791
15927
  function adaptSkillMdForAgent(skillMd, agent) {
@@ -15844,8 +15980,8 @@ function resolveSyncCorpus(options = {}) {
15844
15980
  function packageSourceRoots(source) {
15845
15981
  const roots = [];
15846
15982
  for (const sub of ["skills"]) {
15847
- const candidate = join18(source, sub);
15848
- if (existsSync9(candidate) && isDirectory(candidate))
15983
+ const candidate = join19(source, sub);
15984
+ if (existsSync10(candidate) && isDirectory(candidate))
15849
15985
  roots.push(candidate);
15850
15986
  }
15851
15987
  if (roots.length > 0)
@@ -15860,10 +15996,10 @@ function containsSkillDirectories(path) {
15860
15996
  return false;
15861
15997
  }
15862
15998
  return entries.some((entry) => {
15863
- const candidate = join18(path, entry);
15999
+ const candidate = join19(path, entry);
15864
16000
  if (!isDirectory(candidate))
15865
16001
  return false;
15866
- return existsSync9(join18(candidate, "SKILL.md")) || existsSync9(join18(candidate, "skill.json")) || existsSync9(join18(candidate, "package.json"));
16002
+ return existsSync10(join19(candidate, "SKILL.md")) || existsSync10(join19(candidate, "skill.json")) || existsSync10(join19(candidate, "package.json"));
15867
16003
  });
15868
16004
  }
15869
16005
  function isDirectory(path) {
@@ -15903,7 +16039,7 @@ function syncSkillsToAgents(options = {}) {
15903
16039
  actions.push({
15904
16040
  skill: name,
15905
16041
  agent,
15906
- path: join18(agentGlobalSkillsDir(agent, homeDir2), name, "SKILL.md"),
16042
+ path: join19(agentGlobalSkillsDir(agent, homeDir2), name, "SKILL.md"),
15907
16043
  action: "skip",
15908
16044
  reason: "not found in this machine's corpus"
15909
16045
  });
@@ -15933,7 +16069,7 @@ function syncSkillsToAgents(options = {}) {
15933
16069
  }
15934
16070
  function writeManagedAgentSkill(params) {
15935
16071
  const homeDir2 = params.homeDir ?? homedir3();
15936
- const dir = join18(agentGlobalSkillsDir(params.agent, homeDir2), params.skill);
16072
+ const dir = join19(agentGlobalSkillsDir(params.agent, homeDir2), params.skill);
15937
16073
  const result = writeManagedSkillDir(dir, params.skillMd, {
15938
16074
  skill: params.skill,
15939
16075
  source: params.source,
@@ -15950,11 +16086,11 @@ function writeManagedAgentSkill(params) {
15950
16086
  };
15951
16087
  }
15952
16088
  function writeManagedSkillDir(dir, skillMd, options) {
15953
- const skillMdPath = join18(dir, "SKILL.md");
15954
- const markerPath = join18(dir, SYNC_MARKER_FILE);
15955
- const dirExists = existsSync9(dir);
16089
+ const skillMdPath = join19(dir, "SKILL.md");
16090
+ const markerPath = join19(dir, SYNC_MARKER_FILE);
16091
+ const dirExists = existsSync10(dir);
15956
16092
  const managed = hasSkillsOwnershipMarker(dir);
15957
- const hasSkillMd = existsSync9(skillMdPath);
16093
+ const hasSkillMd = existsSync10(skillMdPath);
15958
16094
  if (dirExists && !managed && !hasSkillMd) {
15959
16095
  return {
15960
16096
  action: "skip",
@@ -15966,7 +16102,7 @@ function writeManagedSkillDir(dir, skillMd, options) {
15966
16102
  return {
15967
16103
  action: "skip",
15968
16104
  path: skillMdPath,
15969
- reason: existsSync9(markerPath) ? "an unmanaged SKILL.md already exists here (invalid or foreign ownership marker); pass --force to overwrite" : "an unmanaged SKILL.md already exists here (hand-authored); pass --force to overwrite"
16105
+ reason: existsSync10(markerPath) ? "an unmanaged SKILL.md already exists here (invalid or foreign ownership marker); pass --force to overwrite" : "an unmanaged SKILL.md already exists here (hand-authored); pass --force to overwrite"
15970
16106
  };
15971
16107
  }
15972
16108
  if (dirExists && managed && hasSkillMd && !options.force && isPointerSkillMd(skillMd)) {
@@ -15989,11 +16125,11 @@ function writeManagedSkillDir(dir, skillMd, options) {
15989
16125
  return { action, path: skillMdPath };
15990
16126
  const parentDir = dirname12(dir);
15991
16127
  mkdirSync6(parentDir, { recursive: true });
15992
- const transactionDir = mkdtempSync(join18(parentDir, `.hasna-skills-write-${basename6(dir)}-`));
15993
- const candidateDir = join18(transactionDir, "candidate");
15994
- const backupDir = join18(transactionDir, "backup");
15995
- const candidateSkillMdPath = join18(candidateDir, "SKILL.md");
15996
- const candidateMarkerPath = join18(candidateDir, SYNC_MARKER_FILE);
16128
+ const transactionDir = mkdtempSync(join19(parentDir, `.hasna-skills-write-${basename6(dir)}-`));
16129
+ const candidateDir = join19(transactionDir, "candidate");
16130
+ const backupDir = join19(transactionDir, "backup");
16131
+ const candidateSkillMdPath = join19(candidateDir, "SKILL.md");
16132
+ const candidateMarkerPath = join19(candidateDir, SYNC_MARKER_FILE);
15997
16133
  const marker = {
15998
16134
  managedBy: SYNC_MARKER_MANAGED_BY,
15999
16135
  skill: options.skill,
@@ -16020,9 +16156,9 @@ function writeManagedSkillDir(dir, skillMd, options) {
16020
16156
  }
16021
16157
  renameDirectory(candidateDir, dir);
16022
16158
  } catch (error) {
16023
- if (originalMoved && existsSync9(backupDir)) {
16159
+ if (originalMoved && existsSync10(backupDir)) {
16024
16160
  try {
16025
- if (existsSync9(dir))
16161
+ if (existsSync10(dir))
16026
16162
  rmSync(dir, { recursive: true, force: true });
16027
16163
  renameDirectory(backupDir, dir);
16028
16164
  originalMoved = false;
@@ -16042,7 +16178,7 @@ function writeManagedSkillDir(dir, skillMd, options) {
16042
16178
  return { action, path: skillMdPath };
16043
16179
  }
16044
16180
  function removeManagedAgentSkill(skill, agent, homeDir2 = homedir3()) {
16045
- const dir = join18(agentGlobalSkillsDir(agent, homeDir2), skill);
16181
+ const dir = join19(agentGlobalSkillsDir(agent, homeDir2), skill);
16046
16182
  if (!hasSkillsOwnershipMarker(dir))
16047
16183
  return false;
16048
16184
  rmSync(dir, { recursive: true, force: true });
@@ -16050,8 +16186,8 @@ function removeManagedAgentSkill(skill, agent, homeDir2 = homedir3()) {
16050
16186
  }
16051
16187
  function sourceSkillMd(skillPath, name, description, kind, preferBundledDocs = false) {
16052
16188
  if (kind === undefined || kind === "instruction" || preferBundledDocs) {
16053
- const skillMdPath = join18(skillPath, "SKILL.md");
16054
- if (existsSync9(skillMdPath))
16189
+ const skillMdPath = join19(skillPath, "SKILL.md");
16190
+ if (existsSync10(skillMdPath))
16055
16191
  return readFileSync13(skillMdPath, "utf-8");
16056
16192
  }
16057
16193
  return pointerSkillMd(name, description);
@@ -16092,8 +16228,8 @@ function resolveSkillAlias(name) {
16092
16228
  }
16093
16229
 
16094
16230
  // src/lib/project-state.ts
16095
- import { existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync14, writeFileSync as writeFileSync6 } from "fs";
16096
- import { join as join19 } from "path";
16231
+ import { existsSync as existsSync11, mkdirSync as mkdirSync7, readFileSync as readFileSync14, writeFileSync as writeFileSync6 } from "fs";
16232
+ import { join as join20 } from "path";
16097
16233
  var VALID_PIN_SOURCES = [
16098
16234
  "official",
16099
16235
  "custom",
@@ -16108,14 +16244,14 @@ var SKILLS_PROJECT_DIR = ".skills";
16108
16244
  var PROJECT_CONFIG_FILE = "project.json";
16109
16245
  var DEFAULT_EXPORT_DIR = ".skills/exports";
16110
16246
  function getProjectStateDir(targetDir = process.cwd()) {
16111
- return join19(targetDir, SKILLS_PROJECT_DIR);
16247
+ return join20(targetDir, SKILLS_PROJECT_DIR);
16112
16248
  }
16113
16249
  function getProjectConfigPath(targetDir = process.cwd()) {
16114
- return join19(getProjectStateDir(targetDir), PROJECT_CONFIG_FILE);
16250
+ return join20(getProjectStateDir(targetDir), PROJECT_CONFIG_FILE);
16115
16251
  }
16116
16252
  function loadProjectConfig(targetDir = process.cwd()) {
16117
16253
  const path = getProjectConfigPath(targetDir);
16118
- if (!existsSync10(path))
16254
+ if (!existsSync11(path))
16119
16255
  return null;
16120
16256
  try {
16121
16257
  return normalizeProjectConfig(JSON.parse(readFileSync14(path, "utf-8")));
@@ -16240,19 +16376,19 @@ function getSkillPath(name) {
16240
16376
  const extensionPath = findExtensionSkillPath(skillName);
16241
16377
  if (extensionPath)
16242
16378
  return extensionPath;
16243
- return join20(getPortableSkillsRoot(), skillName);
16379
+ return join21(getPortableSkillsRoot(), skillName);
16244
16380
  }
16245
16381
  function getCanonicalSkillName(name) {
16246
16382
  return getSkill(name)?.name ?? resolveSkillAlias(normalizeSkillSlug(name));
16247
16383
  }
16248
16384
  function skillExists(name) {
16249
- return existsSync11(getSkillPath(name));
16385
+ return existsSync12(getSkillPath(name));
16250
16386
  }
16251
16387
  function installSkill(name, options = {}) {
16252
16388
  const { targetDir = process.cwd(), overwrite = false } = options;
16253
16389
  const canonicalName = getCanonicalSkillName(name);
16254
16390
  const skillName = normalizeSkillName(canonicalName);
16255
- if (!existsSync11(getSkillPath(name))) {
16391
+ if (!existsSync12(getSkillPath(name))) {
16256
16392
  const knownOfficial = Boolean(getSkill(name));
16257
16393
  return {
16258
16394
  skill: canonicalName,
@@ -16279,7 +16415,7 @@ function installSkill(name, options = {}) {
16279
16415
  }
16280
16416
  function installSkillSource(name, _options = {}) {
16281
16417
  const canonicalName = getCanonicalSkillName(name);
16282
- if (!existsSync11(getSkillPath(name))) {
16418
+ if (!existsSync12(getSkillPath(name))) {
16283
16419
  return { skill: canonicalName, success: false, error: `Skill '${name}' not found`, mode: "source" };
16284
16420
  }
16285
16421
  return {
@@ -16300,11 +16436,11 @@ function installSkillManifest(manifest, _options = {}) {
16300
16436
  }
16301
16437
  function createLocalSkillManifest(name, generateSkillMd) {
16302
16438
  const sourcePath = getSkillPath(name);
16303
- if (!existsSync11(sourcePath))
16439
+ if (!existsSync12(sourcePath))
16304
16440
  return null;
16305
16441
  let skillMd = "";
16306
- const skillMdPath = join20(sourcePath, "SKILL.md");
16307
- if (existsSync11(skillMdPath)) {
16442
+ const skillMdPath = join21(sourcePath, "SKILL.md");
16443
+ if (existsSync12(skillMdPath)) {
16308
16444
  skillMd = readFileSync15(skillMdPath, "utf-8");
16309
16445
  } else if (generateSkillMd) {
16310
16446
  skillMd = generateSkillMd(name) ?? "";
@@ -16377,21 +16513,21 @@ function getAgentSkillsDir(agent, scope = "global", projectDir) {
16377
16513
  const base = projectDir || process.cwd();
16378
16514
  switch (agent) {
16379
16515
  case "pi":
16380
- return scope === "project" ? join20(base, ".pi", "skills") : join20(homedir4(), ".pi", "agent", "skills");
16516
+ return scope === "project" ? join21(base, ".pi", "skills") : join21(homedir4(), ".pi", "agent", "skills");
16381
16517
  case "opencode":
16382
- return scope === "project" ? join20(base, ".opencode", "skills") : join20(homedir4(), ".config", "opencode", "skills");
16518
+ return scope === "project" ? join21(base, ".opencode", "skills") : join21(homedir4(), ".config", "opencode", "skills");
16383
16519
  default:
16384
- return scope === "project" ? join20(base, `.${agent}`, "skills") : join20(homedir4(), `.${agent}`, "skills");
16520
+ return scope === "project" ? join21(base, `.${agent}`, "skills") : join21(homedir4(), `.${agent}`, "skills");
16385
16521
  }
16386
16522
  }
16387
16523
  function getAgentSkillPath(name, agent, scope = "global", projectDir) {
16388
16524
  const skillName = normalizeSkillName(getCanonicalSkillName(name));
16389
- return join20(getAgentSkillsDir(agent, scope, projectDir), skillName);
16525
+ return join21(getAgentSkillsDir(agent, scope, projectDir), skillName);
16390
16526
  }
16391
16527
  function installSkillForAgent(name, options, generateSkillMd) {
16392
16528
  assertNativeExportAllowed();
16393
16529
  const canonicalName = getCanonicalSkillName(name);
16394
- if (!existsSync11(getSkillPath(name))) {
16530
+ if (!existsSync12(getSkillPath(name))) {
16395
16531
  return { skill: canonicalName, success: false, error: `Skill '${name}' not found` };
16396
16532
  }
16397
16533
  const scope = options.scope ?? "global";
@@ -16422,8 +16558,8 @@ function removeSkillForAgent(name, options) {
16422
16558
  }
16423
16559
  function resolveAgentSkillMd(name, generateSkillMd) {
16424
16560
  const sourcePath = getSkillPath(name);
16425
- const skillMdPath = join20(sourcePath, "SKILL.md");
16426
- if (existsSync11(skillMdPath))
16561
+ const skillMdPath = join21(sourcePath, "SKILL.md");
16562
+ if (existsSync12(skillMdPath))
16427
16563
  return readFileSync15(skillMdPath, "utf-8");
16428
16564
  if (generateSkillMd)
16429
16565
  return generateSkillMd(name);
@@ -16442,7 +16578,7 @@ function warnMissingDependencies(name, targetDir) {
16442
16578
  }
16443
16579
  function generateMinimalSkillMd(name) {
16444
16580
  const sourcePath = getSkillPath(name);
16445
- if (!existsSync11(sourcePath))
16581
+ if (!existsSync12(sourcePath))
16446
16582
  return null;
16447
16583
  const canonicalName = getCanonicalSkillName(name);
16448
16584
  const meta = getSkill(canonicalName);
@@ -16457,7 +16593,7 @@ function generateMinimalSkillMd(name) {
16457
16593
  "---",
16458
16594
  ""
16459
16595
  ].filter(Boolean);
16460
- const fallbackDoc = readFileIfExists(join20(sourcePath, "README.md")) || readFileIfExists(join20(sourcePath, "CLAUDE.md"));
16596
+ const fallbackDoc = readFileIfExists(join21(sourcePath, "README.md")) || readFileIfExists(join21(sourcePath, "CLAUDE.md"));
16461
16597
  if (fallbackDoc)
16462
16598
  return `${frontmatter.join(`
16463
16599
  `)}${fallbackDoc.trim()}
@@ -16476,8 +16612,8 @@ skills run ${canonicalName}
16476
16612
  `;
16477
16613
  }
16478
16614
  function readBundledSkillVersion(name) {
16479
- const pkgPath = join20(getSkillPath(name), "package.json");
16480
- if (!existsSync11(pkgPath))
16615
+ const pkgPath = join21(getSkillPath(name), "package.json");
16616
+ if (!existsSync12(pkgPath))
16481
16617
  return "unknown";
16482
16618
  try {
16483
16619
  const pkg = JSON.parse(readFileSync15(pkgPath, "utf-8"));
@@ -16487,7 +16623,7 @@ function readBundledSkillVersion(name) {
16487
16623
  }
16488
16624
  }
16489
16625
  function readFileIfExists(path) {
16490
- return existsSync11(path) ? readFileSync15(path, "utf-8") : null;
16626
+ return existsSync12(path) ? readFileSync15(path, "utf-8") : null;
16491
16627
  }
16492
16628
  function loadProjectConfigCompat(targetDir) {
16493
16629
  return loadProjectConfig(targetDir);
@@ -16531,8 +16667,8 @@ function selectSkillDoc(docs, file) {
16531
16667
  function isInstructionSkillDir(skillPath, meta) {
16532
16668
  if (meta?.kind === "instruction")
16533
16669
  return true;
16534
- const skillMdPath = join21(skillPath, "SKILL.md");
16535
- if (!existsSync12(skillMdPath))
16670
+ const skillMdPath = join23(skillPath, "SKILL.md");
16671
+ if (!existsSync13(skillMdPath))
16536
16672
  return false;
16537
16673
  try {
16538
16674
  return parseSkillFrontmatter(readFileSync16(skillMdPath, "utf-8"))?.kind === "instruction";
@@ -16558,12 +16694,12 @@ var HOSTED_PROVIDER_ENV_PREFIXES = [
16558
16694
  ];
16559
16695
  function getSkillDocs(name) {
16560
16696
  const skillPath = getSkillPath(name);
16561
- if (!existsSync12(skillPath))
16697
+ if (!existsSync13(skillPath))
16562
16698
  return null;
16563
16699
  return {
16564
- skillMd: readIfExists(join21(skillPath, "SKILL.md")),
16565
- readme: readIfExists(join21(skillPath, "README.md")),
16566
- claudeMd: readIfExists(join21(skillPath, "CLAUDE.md"))
16700
+ skillMd: readIfExists(join23(skillPath, "SKILL.md")),
16701
+ readme: readIfExists(join23(skillPath, "README.md")),
16702
+ claudeMd: readIfExists(join23(skillPath, "CLAUDE.md"))
16567
16703
  };
16568
16704
  }
16569
16705
  function getSkillBestDoc(name, file) {
@@ -16575,18 +16711,18 @@ function getSkillBestDoc(name, file) {
16575
16711
  }
16576
16712
  function getSkillRequirements(name) {
16577
16713
  const skillPath = getSkillPath(name);
16578
- if (!existsSync12(skillPath))
16714
+ if (!existsSync13(skillPath))
16579
16715
  return null;
16580
16716
  const texts = [];
16581
16717
  for (const file of ["SKILL.md", "README.md", "CLAUDE.md", ".env.example", ".env.local.example"]) {
16582
- const content = readIfExists(join21(skillPath, file));
16718
+ const content = readIfExists(join23(skillPath, file));
16583
16719
  if (content)
16584
16720
  texts.push(content);
16585
16721
  }
16586
16722
  const meta = getSkill(name);
16587
16723
  let dependencies = {};
16588
16724
  try {
16589
- dependencies = JSON.parse(readFileSync16(join21(skillPath, "package.json"), "utf8")).dependencies || {};
16725
+ dependencies = JSON.parse(readFileSync16(join23(skillPath, "package.json"), "utf8")).dependencies || {};
16590
16726
  } catch {}
16591
16727
  return getSkillRequirementsFromContent(name, texts, dependencies, meta);
16592
16728
  }
@@ -16639,7 +16775,7 @@ async function runSkill(name, args, options = {}) {
16639
16775
  const meta = getSkill(name);
16640
16776
  const canonicalName = meta?.name ?? name;
16641
16777
  const skillPath = getSkillPath(canonicalName);
16642
- if (!existsSync12(skillPath)) {
16778
+ if (!existsSync13(skillPath)) {
16643
16779
  return { exitCode: 1, error: `Skill '${name}' not found` };
16644
16780
  }
16645
16781
  if (isInstructionSkillDir(skillPath, meta)) {
@@ -16648,8 +16784,8 @@ async function runSkill(name, args, options = {}) {
16648
16784
  error: `Skill '${name}' is an instruction skill (kind: instruction) and is not runnable. Instruction skills are consumed by coding agents via SKILL.md, not executed with 'skills run'.`
16649
16785
  };
16650
16786
  }
16651
- const pkgPath = join21(skillPath, "package.json");
16652
- if (!existsSync12(pkgPath)) {
16787
+ const pkgPath = join23(skillPath, "package.json");
16788
+ if (!existsSync13(pkgPath)) {
16653
16789
  return { exitCode: 1, error: `No package.json in skill '${name}'` };
16654
16790
  }
16655
16791
  let entryPoint2;
@@ -16668,12 +16804,12 @@ async function runSkill(name, args, options = {}) {
16668
16804
  } catch {
16669
16805
  return { exitCode: 1, error: `Failed to parse package.json for skill '${name}'` };
16670
16806
  }
16671
- const entryPath = join21(skillPath, entryPoint2);
16672
- if (!existsSync12(entryPath)) {
16807
+ const entryPath = join23(skillPath, entryPoint2);
16808
+ if (!existsSync13(entryPath)) {
16673
16809
  return { exitCode: 1, error: `Entry point '${entryPoint2}' not found in skill '${name}'` };
16674
16810
  }
16675
16811
  const env = { ...process.env, ...options.env };
16676
- if (!existsSync12(join21(skillPath, "node_modules")) || hasDependencyPreparationMarker(skillPath)) {
16812
+ if (!existsSync13(join23(skillPath, "node_modules")) || hasDependencyPreparationMarker(skillPath)) {
16677
16813
  const failure = await prepareSkillDependencies(skillPath, env, options.preparationTimeoutMs);
16678
16814
  if (failure)
16679
16815
  return failure;
@@ -16742,7 +16878,7 @@ function generateSkillMd(name) {
16742
16878
  if (!meta)
16743
16879
  return null;
16744
16880
  const skillPath = getSkillPath(name);
16745
- if (!existsSync12(skillPath))
16881
+ if (!existsSync13(skillPath))
16746
16882
  return null;
16747
16883
  const frontmatter = [
16748
16884
  "---",
@@ -16751,11 +16887,11 @@ function generateSkillMd(name) {
16751
16887
  "---"
16752
16888
  ].join(`
16753
16889
  `);
16754
- const readme = readIfExists(join21(skillPath, "README.md"));
16755
- const claudeMd = readIfExists(join21(skillPath, "CLAUDE.md"));
16890
+ const readme = readIfExists(join23(skillPath, "README.md"));
16891
+ const claudeMd = readIfExists(join23(skillPath, "CLAUDE.md"));
16756
16892
  let cliCommand = null;
16757
- const pkgPath = join21(skillPath, "package.json");
16758
- if (existsSync12(pkgPath)) {
16893
+ const pkgPath = join23(skillPath, "package.json");
16894
+ if (existsSync13(pkgPath)) {
16759
16895
  try {
16760
16896
  const pkg = JSON.parse(readFileSync16(pkgPath, "utf-8"));
16761
16897
  if (pkg.bin) {
@@ -16832,7 +16968,7 @@ function extractEnvVars(text2) {
16832
16968
  }
16833
16969
  function readIfExists(path) {
16834
16970
  try {
16835
- if (existsSync12(path)) {
16971
+ if (existsSync13(path)) {
16836
16972
  return readFileSync16(path, "utf-8");
16837
16973
  }
16838
16974
  } catch {}
@@ -16899,6 +17035,8 @@ async function syncSelectionProfile(profileId, options = {}) {
16899
17035
  async function resolveSelectionContext(profileId, options = {}) {
16900
17036
  const sessionState = options.sessionId ? readSkillSessionSnapshotIfExists(options.sessionId, options) : null;
16901
17037
  const session = sessionState?.receipt ?? null;
17038
+ if (options.nativeParentSessionId !== undefined && session && (session.parent?.sessionId ?? null) !== options.nativeParentSessionId)
17039
+ throw new SkillSelectionError("SESSION_PARENT_MISMATCH", "The native session id is already bound to different parent custody.");
16902
17040
  const parentState = !session && options.parentSessionId ? readSkillSessionSnapshotIfExists(options.parentSessionId, options) : null;
16903
17041
  if (!session && options.parentSessionId && !parentState) {
16904
17042
  throw new SkillSelectionError("SESSION_PARENT_NOT_FOUND", "A child Skills session requires an existing parent receipt so its inherited pin can be bound exactly.");
@@ -17158,15 +17296,15 @@ function buildPluginProjection(entries) {
17158
17296
 
17159
17297
  // src/lib/plugin-projection-store.ts
17160
17298
  import { createHash as createHash14, randomUUID as randomUUID3 } from "crypto";
17161
- import { constants as constants8, closeSync as closeSync9, fchmodSync, fstatSync as fstatSync9, fsyncSync as fsyncSync3, linkSync as linkSync2, lstatSync as lstatSync17, mkdirSync as mkdirSync8, openSync as openSync9, opendirSync as opendirSync4, readSync as readSync7, renameSync as renameSync4, rmdirSync as rmdirSync3, rmSync as rmSync3, unlinkSync as unlinkSync4, writeSync as writeSync2 } from "fs";
17162
- import { dirname as dirname13, isAbsolute as isAbsolute12, join as join23, parse as parse5, resolve as resolve11 } from "path";
17299
+ import { constants as constants8, closeSync as closeSync9, fchmodSync, fstatSync as fstatSync9, fsyncSync as fsyncSync3, linkSync as linkSync2, lstatSync as lstatSync18, mkdirSync as mkdirSync8, openSync as openSync9, opendirSync as opendirSync4, readSync as readSync7, renameSync as renameSync4, rmdirSync as rmdirSync3, rmSync as rmSync3, unlinkSync as unlinkSync4, writeSync as writeSync2 } from "fs";
17300
+ import { dirname as dirname13, isAbsolute as isAbsolute12, join as join25, parse as parse5, resolve as resolve12 } from "path";
17163
17301
  var READ_FLAGS = constants8.O_RDONLY | constants8.O_NOFOLLOW | constants8.O_NONBLOCK;
17164
17302
  var WRITE_FLAGS = constants8.O_WRONLY | constants8.O_CREAT | constants8.O_EXCL | constants8.O_NOFOLLOW | constants8.O_NONBLOCK;
17165
17303
  var uid = () => typeof process.getuid === "function" ? BigInt(process.getuid()) : undefined;
17166
17304
  var missing = (error) => Boolean(error && typeof error === "object" && "code" in error && error.code === "ENOENT");
17167
17305
  function stat(path) {
17168
17306
  try {
17169
- return lstatSync17(path, { bigint: true });
17307
+ return lstatSync18(path, { bigint: true });
17170
17308
  } catch (error) {
17171
17309
  if (missing(error))
17172
17310
  return null;
@@ -17174,7 +17312,7 @@ function stat(path) {
17174
17312
  }
17175
17313
  }
17176
17314
  function canonical5(path) {
17177
- pluginNeed(typeof path === "string" && path.length <= 8192 && isAbsolute12(path) && resolve11(path) === path && !/[\x00-\x1f\x7f]/.test(path), "Plugin storage paths must be canonical absolute paths");
17315
+ pluginNeed(typeof path === "string" && path.length <= 8192 && isAbsolute12(path) && resolve12(path) === path && !/[\x00-\x1f\x7f]/.test(path), "Plugin storage paths must be canonical absolute paths");
17178
17316
  }
17179
17317
  function owner(stats, system = false) {
17180
17318
  const current = uid();
@@ -17189,7 +17327,7 @@ function ancestors(path, create = false) {
17189
17327
  let current = filesystemRoot;
17190
17328
  for (const segment of ["", ...parent.slice(filesystemRoot.length).split("/").filter(Boolean)]) {
17191
17329
  if (segment)
17192
- current = join23(current, segment);
17330
+ current = join25(current, segment);
17193
17331
  let currentStat = stat(current);
17194
17332
  if (!currentStat && create) {
17195
17333
  try {
@@ -17304,7 +17442,7 @@ function snapshotPluginTree(root, options = {}) {
17304
17442
  pluginNeed(++visited <= PLUGIN_PROJECTION_LIMITS.files * 50, "Plugin tree membership exceeds its limit");
17305
17443
  const path = relative6 ? `${relative6}/${name}` : name;
17306
17444
  pluginNeed(new TextEncoder().encode(path).byteLength <= 100 && !/[\\:\x00-\x1f\x7f]/u.test(path), "Plugin entry path exceeds its limit or is unsafe");
17307
- const absolute = join23(directory, name), entryStat = stat(absolute);
17445
+ const absolute = join25(directory, name), entryStat = stat(absolute);
17308
17446
  pluginNeed(entryStat, "Plugin tree member disappeared");
17309
17447
  if (relative6 === "" && name === ".orphaned_at" && (options.nativeRuntime === "claude-2.1.274" || options.nativeRuntime === "claude-2.1.276")) {
17310
17448
  const bytes2 = readBytes(absolute, 32, "native-pin");
@@ -17321,7 +17459,7 @@ function snapshotPluginTree(root, options = {}) {
17321
17459
  directories.push({ path: absolute, stat: entryStat, names: pins });
17322
17460
  for (const pin of pins) {
17323
17461
  pluginNeed(/^[1-9][0-9]{0,9}$/.test(pin) && Number(pin) <= 2147483647, "Unrecognized native plugin pin name");
17324
- const pinPath = join23(absolute, pin), pinStat = stat(pinPath);
17462
+ const pinPath = join25(absolute, pin), pinStat = stat(pinPath);
17325
17463
  pluginNeed(pinStat, "Native plugin pin disappeared");
17326
17464
  const bytes2 = readBytes(pinPath, 4096, "native-pin");
17327
17465
  let value;
@@ -17419,7 +17557,7 @@ function materializePluginTree(root, entries) {
17419
17557
  mkdirSync8(staging, { mode: 448 });
17420
17558
  staged = stat(staging);
17421
17559
  for (const entry of entries) {
17422
- const path = join23(staging, entry.path);
17560
+ const path = join25(staging, entry.path);
17423
17561
  ancestors(path, true);
17424
17562
  writeExclusive(path, entry.bytes, entry.mode);
17425
17563
  }
@@ -17546,7 +17684,7 @@ function describeEntries(entries) {
17546
17684
 
17547
17685
  // src/lib/plugin-admission.ts
17548
17686
  function pluginAdmissionRoot() {
17549
- return join25(homedir5(), ".hasna", "skills", "plugin-admission");
17687
+ return join26(homedir5(), ".hasna", "skills", "plugin-admission");
17550
17688
  }
17551
17689
  function validatePluginTarget(value) {
17552
17690
  pluginKeys(value, ["schemaVersion", "pluginId", "registrations", "native", "resolver"]);
@@ -17557,7 +17695,7 @@ function validatePluginTarget(value) {
17557
17695
  for (const registration of value.registrations) {
17558
17696
  pluginKeys(registration, ["scope", "projectPath"]);
17559
17697
  pluginNeed(registration.scope === "user" || registration.scope === "project", "Unsupported plugin registration scope");
17560
- pluginNeed(registration.scope === "user" ? registration.projectPath === null : typeof registration.projectPath === "string" && isAbsolute13(registration.projectPath) && resolve12(registration.projectPath) === registration.projectPath, "Plugin scope requires its exact project path");
17698
+ pluginNeed(registration.scope === "user" ? registration.projectPath === null : typeof registration.projectPath === "string" && isAbsolute13(registration.projectPath) && resolve13(registration.projectPath) === registration.projectPath, "Plugin scope requires its exact project path");
17561
17699
  const key = JSON.stringify([registration.scope, registration.projectPath]);
17562
17700
  pluginNeed(!scopes.has(key), "Duplicate plugin registration scope");
17563
17701
  scopes.add(key);
@@ -17565,7 +17703,7 @@ function validatePluginTarget(value) {
17565
17703
  for (const key of ["native", "resolver"]) {
17566
17704
  pluginKeys(value[key], key === "native" ? ["version", "executable", "digest"] : ["executable", "digest"]);
17567
17705
  pluginText(value[key].executable);
17568
- pluginNeed(isAbsolute13(value[key].executable) && resolve12(value[key].executable) === value[key].executable, "Plugin executable must have a canonical absolute path");
17706
+ pluginNeed(isAbsolute13(value[key].executable) && resolve13(value[key].executable) === value[key].executable, "Plugin executable must have a canonical absolute path");
17569
17707
  pluginDigest(value[key].digest);
17570
17708
  }
17571
17709
  pluginNeed(["2.1.274", "2.1.276"].includes(value.native.version), "Plugin admission requires a certified Claude command-source runtime (2.1.274 or 2.1.276)");
@@ -17717,17 +17855,17 @@ function pluginResolverCommand(binding) {
17717
17855
  }
17718
17856
  function root(options) {
17719
17857
  const path = options.storeRoot ?? pluginAdmissionRoot();
17720
- pluginNeed(isAbsolute13(path) && resolve12(path) === path, "Invalid plugin admission store root");
17858
+ pluginNeed(isAbsolute13(path) && resolve13(path) === path, "Invalid plugin admission store root");
17721
17859
  return path;
17722
17860
  }
17723
17861
  function pluginBindingPath(storeRoot, id) {
17724
17862
  pluginNeed(/^[a-f0-9]{64}$/.test(id), "Invalid plugin binding identifier");
17725
- return join25(storeRoot, "bindings", `${id}.json`);
17863
+ return join26(storeRoot, "bindings", `${id}.json`);
17726
17864
  }
17727
17865
  function pluginReceiptPath(storeRoot, id, digest) {
17728
17866
  pluginNeed(/^[a-f0-9]{64}$/.test(id), "Invalid plugin binding identifier");
17729
17867
  pluginDigest(digest);
17730
- return join25(storeRoot, "receipts", id, `${digest.slice(7)}.json`);
17868
+ return join26(storeRoot, "receipts", id, `${digest.slice(7)}.json`);
17731
17869
  }
17732
17870
  function readPluginBinding(storeRoot, id) {
17733
17871
  const value = readPluginJson(pluginBindingPath(storeRoot, id));
@@ -17747,7 +17885,7 @@ function readPluginAdmissionReceipt(storeRoot, id, digest) {
17747
17885
  pluginNeed(receipt.plan.bindingId === id && receipt.plan.planDigest === digest, "Plugin receipt identity differs from its filename");
17748
17886
  const binding = readPluginBinding(storeRoot, id);
17749
17887
  pluginNeed(canonicalJson(binding) === canonicalJson(receipt.plan.binding) && receipt.plan.sourceCommand === pluginResolverCommand(binding), "Plugin receipt binding changed");
17750
- pluginNeed(receipt.materializedPath === join25(storeRoot, "objects", id, digest.slice(7)), "Plugin receipt points outside its immutable materialization");
17888
+ pluginNeed(receipt.materializedPath === join26(storeRoot, "objects", id, digest.slice(7)), "Plugin receipt points outside its immutable materialization");
17751
17889
  pluginText(receipt.admittedAt, 64);
17752
17890
  pluginNeed(Number.isFinite(Date.parse(receipt.admittedAt)), "Invalid plugin admission time");
17753
17891
  return receipt;
@@ -17888,7 +18026,7 @@ async function admitPlugin(spec, profileId, target, approvedDigest, approvedEvid
17888
18026
  const { plan, projection } = await candidate(spec, profileId, target, options);
17889
18027
  pluginNeed(plan.planDigest === approvedDigest, "The plugin plan changed since review; obtain and review a new plan");
17890
18028
  pluginNeed(plan.evidenceDigest === approvedEvidenceDigest, "The plugin routing evidence changed since review; obtain and review a new plan");
17891
- const store = root(options), materializedPath = join25(store, "objects", plan.bindingId, plan.planDigest.slice(7));
18029
+ const store = root(options), materializedPath = join26(store, "objects", plan.bindingId, plan.planDigest.slice(7));
17892
18030
  materializePluginTree(materializedPath, projection.files);
17893
18031
  writePluginJsonImmutable(pluginBindingPath(store, plan.bindingId), plan.binding);
17894
18032
  const path = pluginReceiptPath(store, plan.bindingId, plan.planDigest), existing = readPluginJson(path);
@@ -17922,21 +18060,21 @@ function validateManagedPluginWitnesses(value) {
17922
18060
  pluginNeed(typeof item.bindingId === "string" && /^[a-f0-9]{64}$/.test(item.bindingId) && !seen.has(item.bindingId), "Invalid or duplicate managed plugin witness");
17923
18061
  seen.add(item.bindingId);
17924
18062
  pluginText(item.storeRoot);
17925
- pluginNeed(isAbsolute14(item.storeRoot) && resolve13(item.storeRoot) === item.storeRoot, "Invalid plugin receipt store path");
18063
+ pluginNeed(isAbsolute14(item.storeRoot) && resolve14(item.storeRoot) === item.storeRoot, "Invalid plugin receipt store path");
17926
18064
  }
17927
18065
  }
17928
18066
  function safe3(path) {
17929
18067
  pluginText(path);
17930
- pluginNeed(isAbsolute14(path) && resolve13(path) === path, "Invalid plugin registry path");
18068
+ pluginNeed(isAbsolute14(path) && resolve14(path) === path, "Invalid plugin registry path");
17931
18069
  for (let at = path;; at = dirname14(at)) {
17932
- pluginNeed(!lstatSync18(at, { throwIfNoEntry: false })?.isSymbolicLink(), "Plugin registry path traverses a symlink");
18070
+ pluginNeed(!lstatSync19(at, { throwIfNoEntry: false })?.isSymbolicLink(), "Plugin registry path traverses a symlink");
17933
18071
  if (at === dirname14(at))
17934
18072
  break;
17935
18073
  }
17936
18074
  }
17937
18075
  function readRegistry(path) {
17938
18076
  safe3(path);
17939
- const initial = lstatSync18(path);
18077
+ const initial = lstatSync19(path);
17940
18078
  pluginNeed(initial.isFile() && initial.size <= 1024 * 1024, "Plugin registry is not a bounded regular file");
17941
18079
  const fd = openSync10(path, constants9.O_RDONLY | constants9.O_NOFOLLOW | constants9.O_NONBLOCK);
17942
18080
  try {
@@ -17950,7 +18088,7 @@ function readRegistry(path) {
17950
18088
  break;
17951
18089
  length += count;
17952
18090
  }
17953
- const after = fstatSync10(fd), current = lstatSync18(path);
18091
+ const after = fstatSync10(fd), current = lstatSync19(path);
17954
18092
  safe3(path);
17955
18093
  pluginNeed(length <= 1024 * 1024 && length === opened.size && after.size === opened.size && after.mtimeMs === opened.mtimeMs && after.ctimeMs === opened.ctimeMs && current.dev === opened.dev && current.ino === opened.ino && current.mtimeMs === opened.mtimeMs && current.ctimeMs === opened.ctimeMs, "Plugin registry changed during read");
17956
18094
  let value;
@@ -17980,7 +18118,7 @@ function hashManagedPluginRegistry(path, managed) {
17980
18118
  seen.add(target.pluginId);
17981
18119
  const rows = plugins[target.pluginId];
17982
18120
  pluginNeed(Array.isArray(rows) && rows.length === target.registrations.length, "Managed plugin registration membership changed");
17983
- const [name, market] = target.pluginId.split("@"), cacheRoot = join26(dirname14(path), "cache", market, name);
18121
+ const [name, market] = target.pluginId.split("@"), cacheRoot = join27(dirname14(path), "cache", market, name);
17984
18122
  const scopes = new Set, approvals = new Map, currentFiles = new Map;
17985
18123
  plugins[target.pluginId] = rows.map((row) => {
17986
18124
  pluginNeed(pluginObject(row), "Invalid managed plugin registration");
@@ -17993,7 +18131,7 @@ function hashManagedPluginRegistry(path, managed) {
17993
18131
  pluginText(row.sourceProducerPath);
17994
18132
  pluginText(row.lastUpdated, 64);
17995
18133
  pluginNeed(Number.isFinite(Date.parse(row.lastUpdated)), "Invalid managed plugin update timestamp");
17996
- pluginNeed(/^(?:[A-Za-z0-9][A-Za-z0-9._+-]*-)?[a-f0-9]{12}$/.test(row.version) && row.installPath === join26(cacheRoot, row.version), "Managed plugin cache path or version is outside its registration");
18134
+ pluginNeed(/^(?:[A-Za-z0-9][A-Za-z0-9._+-]*-)?[a-f0-9]{12}$/.test(row.version) && row.installPath === join27(cacheRoot, row.version), "Managed plugin cache path or version is outside its registration");
17997
18135
  const approval = (producer) => {
17998
18136
  pluginText(producer);
17999
18137
  const digest = `sha256:${basename7(producer)}`;
@@ -18023,7 +18161,7 @@ function hashManagedPluginRegistry(path, managed) {
18023
18161
  return Object.fromEntries(Object.entries(row).filter(([key]) => !["version", "installPath", "sourceProducerPath", "previousProducerPaths", "lastUpdated"].includes(key)));
18024
18162
  });
18025
18163
  safe3(cacheRoot);
18026
- const before = lstatSync18(cacheRoot);
18164
+ const before = lstatSync19(cacheRoot);
18027
18165
  pluginNeed(before.isDirectory(), "Invalid managed plugin cache root");
18028
18166
  const directory = opendirSync5(cacheRoot), names = [];
18029
18167
  try {
@@ -18036,17 +18174,17 @@ function hashManagedPluginRegistry(path, managed) {
18036
18174
  directory.closeSync();
18037
18175
  }
18038
18176
  for (const version of names.sort()) {
18039
- const files = pluginFileWitnesses(snapshotPluginTree(join26(cacheRoot, version), { nativeRuntime: `claude-${target.native.version}` }));
18177
+ const files = pluginFileWitnesses(snapshotPluginTree(join27(cacheRoot, version), { nativeRuntime: `claude-${target.native.version}` }));
18040
18178
  verifiedBytes += files.reduce((total, file) => total + file.size, 0);
18041
18179
  pluginNeed(verifiedBytes <= 256 * 1024 * 1024, "Managed plugin verification exceeds its aggregate byte budget");
18042
18180
  const serialized = JSON.stringify(files);
18043
- const expected = currentFiles.get(join26(cacheRoot, version));
18181
+ const expected = currentFiles.get(join27(cacheRoot, version));
18044
18182
  pluginNeed(expected === undefined || serialized === expected, "Current native cache differs from its exact registered producer receipt");
18045
18183
  pluginNeed([...approvals.values()].some((receipt) => nativeVersionMatches(version, receipt) && JSON.stringify(receipt.plan.files) === serialized), "A retained native plugin cache version differs from every approved projection");
18046
18184
  }
18047
18185
  for (const row of rows)
18048
18186
  pluginNeed(names.includes(row.version), "Managed native plugin installation is missing");
18049
- const after = lstatSync18(cacheRoot);
18187
+ const after = lstatSync19(cacheRoot);
18050
18188
  safe3(cacheRoot);
18051
18189
  pluginNeed(before.dev === after.dev && before.ino === after.ino && before.mtimeMs === after.mtimeMs && before.ctimeMs === after.ctimeMs, "Managed cache version membership changed during verification");
18052
18190
  }
@@ -18059,21 +18197,21 @@ function captureManagedPluginRegistry(path, managedPlugins) {
18059
18197
 
18060
18198
  // src/lib/claude-marketplace-registry.ts
18061
18199
  import { createHash as createHash15 } from "crypto";
18062
- import { closeSync as closeSync11, constants as constants10, fstatSync as fstatSync11, lstatSync as lstatSync19, openSync as openSync11, readSync as readSync9 } from "fs";
18063
- import { basename as basename8, dirname as dirname15, isAbsolute as isAbsolute15, resolve as resolve14 } from "path";
18200
+ import { closeSync as closeSync11, constants as constants10, fstatSync as fstatSync11, lstatSync as lstatSync20, openSync as openSync11, readSync as readSync9 } from "fs";
18201
+ import { basename as basename8, dirname as dirname15, isAbsolute as isAbsolute15, resolve as resolve15 } from "path";
18064
18202
  var CLAUDE_MARKETPLACE_REGISTRY_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
18065
18203
  function need5(value, reason) {
18066
18204
  if (!value)
18067
18205
  throw new Error(`Claude marketplace registry ${reason}`);
18068
18206
  }
18069
18207
  function absolutePath3(value) {
18070
- need5(typeof value === "string" && value.length > 0 && value.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.pathCharacters && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute15(value) && resolve14(value) === value, "requires a normalized absolute path");
18208
+ need5(typeof value === "string" && value.length > 0 && value.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.pathCharacters && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute15(value) && resolve15(value) === value, "requires a normalized absolute path");
18071
18209
  }
18072
18210
  var sameFile2 = (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;
18073
18211
  function parents2(path) {
18074
18212
  const result = [];
18075
18213
  for (let at = dirname15(path);; at = dirname15(at)) {
18076
- const stat2 = lstatSync19(at, { bigint: true });
18214
+ const stat2 = lstatSync20(at, { bigint: true });
18077
18215
  need5(stat2.isDirectory() && !stat2.isSymbolicLink(), "path traverses a symlink or non-directory");
18078
18216
  result.push([at, stat2]);
18079
18217
  if (at === dirname15(at))
@@ -18084,7 +18222,7 @@ function readRegistry2(path, budget) {
18084
18222
  absolutePath3(path);
18085
18223
  need5(basename8(path) === "known_marketplaces.json", "requires known_marketplaces.json");
18086
18224
  need5(Number.isSafeInteger(budget.remaining) && budget.remaining >= 0, "has an invalid byte budget");
18087
- const ancestors2 = parents2(path), initial = lstatSync19(path, { bigint: true });
18225
+ const ancestors2 = parents2(path), initial = lstatSync20(path, { bigint: true });
18088
18226
  need5(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_MARKETPLACE_REGISTRY_LIMITS.bytes), "is not a bounded regular file");
18089
18227
  need5(initial.size <= BigInt(budget.remaining), "exceeds its aggregate byte limit");
18090
18228
  const fd = openSync11(path, constants10.O_RDONLY | constants10.O_NOFOLLOW | constants10.O_NONBLOCK);
@@ -18099,10 +18237,10 @@ function readRegistry2(path, budget) {
18099
18237
  break;
18100
18238
  length += count;
18101
18239
  }
18102
- const after = fstatSync11(fd, { bigint: true }), current = lstatSync19(path, { bigint: true });
18240
+ const after = fstatSync11(fd, { bigint: true }), current = lstatSync20(path, { bigint: true });
18103
18241
  need5(current.isFile() && sameFile2(opened, after) && sameFile2(after, current) && BigInt(length) === opened.size, "changed during read");
18104
18242
  for (const [ancestor, before] of ancestors2) {
18105
- const now = lstatSync19(ancestor, { bigint: true });
18243
+ const now = lstatSync20(ancestor, { bigint: true });
18106
18244
  need5(now.isDirectory() && before.dev === now.dev && before.ino === now.ino && before.mode === now.mode, "parent changed during read");
18107
18245
  }
18108
18246
  budget.remaining -= length;
@@ -18242,7 +18380,7 @@ function captureClaudeMarketplaceRegistryV2(path, budget = { remaining: 256 * 10
18242
18380
  import { createHash as createHash17 } from "crypto";
18243
18381
 
18244
18382
  // src/lib/codex-hook-trust-files.ts
18245
- import { closeSync as closeSync12, constants as constants11, fstatSync as fstatSync12, fsyncSync as fsyncSync4, lstatSync as lstatSync20, openSync as openSync12, readSync as readSync10, writeFileSync as writeFileSync7 } from "fs";
18383
+ import { closeSync as closeSync12, constants as constants11, fstatSync as fstatSync12, fsyncSync as fsyncSync4, lstatSync as lstatSync21, openSync as openSync12, readSync as readSync10, writeFileSync as writeFileSync7 } from "fs";
18246
18384
  import { createHash as createHash16 } from "crypto";
18247
18385
  import { dirname as dirname16 } from "path";
18248
18386
  function need6(value, code) {
@@ -18252,7 +18390,7 @@ function need6(value, code) {
18252
18390
  var sameStat = (a, b) => ["dev", "ino", "uid", "nlink", "mode", "size", "mtimeNs", "ctimeNs"].every((key) => a[key] === b[key]);
18253
18391
  function safeParents(file) {
18254
18392
  for (let p = dirname16(file);; p = dirname16(p)) {
18255
- const s = lstatSync20(p);
18393
+ const s = lstatSync21(p);
18256
18394
  need6(s.isDirectory() && !s.isSymbolicLink(), "UNSAFE_PARENT");
18257
18395
  need6((s.mode & 18) === 0 || s.uid === 0 && (s.mode & 512) !== 0, "UNSAFE_PARENT");
18258
18396
  if (dirname16(p) === p)
@@ -18262,7 +18400,7 @@ function safeParents(file) {
18262
18400
  function snapshot(file, privateFile = false, options = {}) {
18263
18401
  const readOnlyPackage = options.readOnlyPackage === true && !privateFile;
18264
18402
  safeParents(file);
18265
- const initial = lstatSync20(file, { bigint: true });
18403
+ const initial = lstatSync21(file, { bigint: true });
18266
18404
  need6(initial.isFile() && initial.uid === BigInt(process.getuid()) && (initial.nlink === 1n || readOnlyPackage && initial.nlink > 1n) && (initial.mode & 0o022n) === 0n && (!privateFile || (initial.mode & 0o777n) === 0o600n), "UNSAFE_FILE");
18267
18405
  need6(initial.size <= 8n * 1024n * 1024n, "FILE_BOUND");
18268
18406
  const fd = openSync12(file, constants11.O_RDONLY | constants11.O_NOFOLLOW | constants11.O_NONBLOCK);
@@ -18277,7 +18415,7 @@ function snapshot(file, privateFile = false, options = {}) {
18277
18415
  break;
18278
18416
  length += n2;
18279
18417
  }
18280
- need6(length === Number(before.size) && sameStat(before, fstatSync12(fd, { bigint: true })) && sameStat(before, lstatSync20(file, { bigint: true })), "INPUT_CHANGED");
18418
+ need6(length === Number(before.size) && sameStat(before, fstatSync12(fd, { bigint: true })) && sameStat(before, lstatSync21(file, { bigint: true })), "INPUT_CHANGED");
18281
18419
  const content = bytes.subarray(0, length), text2 = new TextDecoder("utf-8", { fatal: true }).decode(content);
18282
18420
  return { file, privateFile, readOnlyPackage, text: text2, bytes: content, stat: before, sha256: createHash16("sha256").update(content).digest("hex") };
18283
18421
  } finally {
@@ -18329,7 +18467,7 @@ function safe4(path) {
18329
18467
  if (!isAbsolute16(path) || path.includes("\x00"))
18330
18468
  throw new Error("Expected an absolute native discovery path");
18331
18469
  for (let cursor = path;; cursor = dirname17(cursor)) {
18332
- if (lstatSync21(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
18470
+ if (lstatSync22(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
18333
18471
  throw new Error(`Refusing symlink native discovery input: ${cursor}`);
18334
18472
  if (dirname17(cursor) === cursor)
18335
18473
  break;
@@ -18339,7 +18477,7 @@ function read2(path, changes) {
18339
18477
  safe4(path);
18340
18478
  if (changes?.has(path))
18341
18479
  return changes.get(path);
18342
- const stat2 = lstatSync21(path, { throwIfNoEntry: false });
18480
+ const stat2 = lstatSync22(path, { throwIfNoEntry: false });
18343
18481
  if (!stat2)
18344
18482
  return null;
18345
18483
  if (!stat2.isFile() || stat2.size > 16 * 1024 * 1024)
@@ -18450,12 +18588,12 @@ function codexInstallationRoots(binding) {
18450
18588
  if (binding.agent !== "codex" || binding.method !== "reviewed" || proof.version !== "codex-cli 0.160.0" || !/^[a-f0-9]{64}$/.test(proof.catalogSha256) || !Array.isArray(proof.plugins) || !proof.plugins.length)
18451
18589
  throw new Error("Invalid native installation input proof");
18452
18590
  const cache = dirname17(dirname17(proof.plugins[0].pluginParent));
18453
- const config = join27(dirname17(dirname17(cache)), "config.toml"), home = dirname17(dirname17(dirname17(cache)));
18591
+ const config = join28(dirname17(dirname17(cache)), "config.toml"), home = dirname17(dirname17(dirname17(cache)));
18454
18592
  const configSource = binding.sources.find((source) => source.path === config && source.sha256 !== null && (!source.format || source.format === "toml" && source.fields?.includes("plugins")));
18455
- const nativeRoots = NATIVE_SKILL_ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => join27(home, path));
18593
+ const nativeRoots = NATIVE_SKILL_ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => join28(home, path));
18456
18594
  if (!configSource || proof.plugins.some((input) => nativeRoots.some((root2) => root2 === input.sourceRoot || root2.startsWith(input.sourceRoot + sep6) || input.sourceRoot.startsWith(root2 + sep6)) || binding.sources.some((source) => basename9(source.path) === "config.toml" && (source.path === input.sourceRoot || source.path.startsWith(input.sourceRoot + sep6)))))
18457
18595
  throw new Error("Native installation input overlaps configuration or loading roots");
18458
- if (proof.plugins.some((input) => dirname17(dirname17(input.pluginParent)) !== cache || !binding.sources.some((source) => source.path === join27(input.sourceRoot, ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined)))
18596
+ if (proof.plugins.some((input) => dirname17(dirname17(input.pluginParent)) !== cache || !binding.sources.some((source) => source.path === join28(input.sourceRoot, ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined)))
18459
18597
  throw new Error("Missing native installation input source witness");
18460
18598
  return reviewedCodexPluginSourceRoots(cache, proof.plugins, (path) => {
18461
18599
  const text2 = read2(path);
@@ -18497,19 +18635,19 @@ function captureDiscoveryPathSources(paths) {
18497
18635
  const budget = discoveryByteBudget();
18498
18636
  return paths.map((path) => ({ path, hashMode: "path-bytes", sha256: hashDiscoveryPathFile(path, budget) }));
18499
18637
  }
18500
- function assertProjectDiscovery(agent, directories, home, canonical6 = resolve15, reviewed) {
18638
+ function assertProjectDiscovery(agent, directories, home, canonical6 = resolve16, reviewed) {
18501
18639
  const homeConfigPath = canonical6(agentDiscoveryConfigPath(home, agent));
18502
18640
  for (const directory of directories) {
18503
- const isHome = resolve15(directory) === resolve15(home);
18641
+ const isHome = resolve16(directory) === resolve16(home);
18504
18642
  if (agent === "hermes") {
18505
18643
  assertHermesEnvironment(home);
18506
18644
  continue;
18507
18645
  }
18508
- if (isHome && agent !== "claude")
18646
+ if (isHome && agent !== "claude" && agent !== "sumi")
18509
18647
  continue;
18510
- const paths = agent === "claude" ? [...isHome ? [] : [".claude/settings.json"], ".claude/settings.local.json"] : agent === "codex" ? [".codex/config.toml"] : agent === "gemini" ? [".gemini/settings.json"] : agent === "opencode" ? ["opencode.json", "opencode.jsonc", ".opencode/opencode.json", ".opencode/opencode.jsonc"] : [".cursor/hooks.json"];
18648
+ const paths = agent === "claude" ? [...isHome ? [] : [".claude/settings.json"], ".claude/settings.local.json"] : agent === "codex" ? [".codex/config.toml"] : agent === "gemini" ? [".gemini/settings.json"] : agent === "opencode" ? ["opencode.json", "opencode.jsonc", ".opencode/opencode.json", ".opencode/opencode.jsonc"] : agent === "sumi" ? ["sumi.json", "sumi.jsonc", ".sumi/sumi.json", ".sumi/sumi.jsonc"] : [".cursor/hooks.json"];
18511
18649
  for (const suffix of paths) {
18512
- const path = canonical6(join27(directory, suffix)), raw = read2(path);
18650
+ const path = canonical6(join28(directory, suffix)), raw = read2(path);
18513
18651
  if (raw === null)
18514
18652
  continue;
18515
18653
  const isRootAliasedHomeConfig = agent === "claude" && suffix === ".claude/settings.json" || agent === "codex" && suffix === ".codex/config.toml";
@@ -18518,7 +18656,7 @@ function assertProjectDiscovery(agent, directories, home, canonical6 = resolve15
18518
18656
  if (suffix.endsWith(".jsonc"))
18519
18657
  throw new Error(`NATIVE_SKILL_DRIFT: project JSONC discovery configuration requires review: ${path}`);
18520
18658
  const config = parseConfig(raw, path, suffix.endsWith(".toml"));
18521
- const keys = agent === "claude" ? ["enabledPlugins", "extraKnownMarketplaces", "skillOverrides"] : agent === "codex" ? ["plugins", "marketplaces", "skills"] : agent === "gemini" ? ["skills", "extensions"] : agent === "opencode" ? ["plugin", "skills"] : ["hooks"];
18659
+ const keys = agent === "claude" ? ["enabledPlugins", "extraKnownMarketplaces", "skillOverrides"] : agent === "codex" ? ["plugins", "marketplaces", "skills"] : agent === "gemini" ? ["skills", "extensions"] : agent === "opencode" ? ["plugin", "skills"] : agent === "sumi" ? ["plugin", "plugins", "skills", "permissions"] : ["hooks"];
18522
18660
  let admittedPlugins = false;
18523
18661
  if (agent === "claude" && reviewed?.agent === agent && reviewed.method === "reviewed" && config.enabledPlugins && typeof config.enabledPlugins === "object" && !Array.isArray(config.enabledPlugins)) {
18524
18662
  const exact = reviewed.sources.find((source) => source.path === path && source.format === undefined && source.fields === undefined && (source.hashMode === undefined || source.hashMode === "bytes"));
@@ -18531,19 +18669,28 @@ function assertProjectDiscovery(agent, directories, home, canonical6 = resolve15
18531
18669
  if (keys.some((key) => config[key] !== undefined && !(key === "enabledPlugins" && admittedPlugins)) || config.disableAllHooks === true || config.disableBundledSkills === false || config.hooksConfig?.enabled === false || config.permission?.skill !== undefined || config.permissions?.deny?.some((rule) => typeof rule === "string" && /^Skill(?:\(|$)/.test(rule)))
18532
18670
  throw new Error(`NATIVE_SKILL_DRIFT: higher-precedence project skill or hook configuration requires review: ${path}`);
18533
18671
  }
18672
+ if (agent === "sumi")
18673
+ for (const name of ["plugin", "plugins"]) {
18674
+ const path = canonical6(join28(directory, ".sumi", name));
18675
+ safe4(path);
18676
+ if (lstatSync22(path, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(path).length)
18677
+ throw new Error(`NATIVE_SKILL_DRIFT: project Sumi plugins require a dedicated discovery review: ${path}`);
18678
+ }
18534
18679
  if (agent === "claude") {
18535
- const commands = canonical6(join27(directory, ".claude/commands"));
18680
+ const commands = canonical6(join28(directory, ".claude/commands"));
18536
18681
  safe4(commands);
18537
- if (lstatSync21(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(commands).length)
18682
+ if (lstatSync22(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(commands).length)
18538
18683
  throw new Error("NATIVE_SKILL_DRIFT: legacy project command discovery requires a dedicated format adapter");
18539
18684
  }
18540
18685
  }
18541
18686
  }
18542
18687
  function agentDiscoveryConfigPath(home, agent) {
18543
- return join27(home, agent === "hermes" ? ".hermes/config.yaml" : agent === "opencode" ? ".config/opencode/opencode.json" : `.${agent}/${agent === "codex" ? "config.toml" : agent === "cursor" ? "hooks.json" : "settings.json"}`);
18688
+ if (agent === "sumi")
18689
+ return sumiConfigPath(home);
18690
+ return join28(home, agent === "hermes" ? ".hermes/config.yaml" : agent === "opencode" ? ".config/opencode/opencode.json" : `.${agent}/${agent === "codex" ? "config.toml" : agent === "cursor" ? "hooks.json" : "settings.json"}`);
18544
18691
  }
18545
18692
  function resolveAgentDiscovery(options) {
18546
- const canonical6 = options.canonical ?? resolve15, home = resolve15(options.home), agent = options.agent;
18693
+ const canonical6 = options.canonical ?? resolve16, home = resolve16(options.home), agent = options.agent;
18547
18694
  const sources = [], roots = new Set, builtinNames = [];
18548
18695
  const witness = (path, format, fields2) => {
18549
18696
  const source = { path: canonical6(path), sha256: null, ...format ? { format, fields: fields2 } : {} };
@@ -18554,13 +18701,49 @@ function resolveAgentDiscovery(options) {
18554
18701
  if (agent === "hermes")
18555
18702
  assertHermesEnvironment(home);
18556
18703
  const configPath = agentDiscoveryConfigPath(home, agent);
18557
- const configText = witness(configPath, agent === "hermes" ? "yaml" : agent === "codex" ? "toml" : "json", agent === "hermes" ? ["skills", "plugins", "hooks"] : agent === "claude" ? ["enabledPlugins", "extraKnownMarketplaces"] : agent === "codex" ? [...CODEX_DISCOVERY_PROJECTION_FIELDS] : agent === "gemini" ? ["skills", "extensions", "security"] : agent === "opencode" ? ["plugin", "skills"] : ["version"]);
18704
+ const configText = witness(configPath, agent === "hermes" ? "yaml" : agent === "codex" ? "toml" : "json", agent === "hermes" ? ["skills", "plugins", "hooks"] : agent === "claude" ? ["enabledPlugins", "extraKnownMarketplaces"] : agent === "codex" ? [...CODEX_DISCOVERY_PROJECTION_FIELDS] : agent === "gemini" ? ["skills", "extensions", "security"] : agent === "opencode" ? ["plugin", "skills"] : agent === "sumi" ? ["skills", "plugin", "plugins", "permissions"] : ["version"]);
18558
18705
  const config = configText === null ? {} : agent === "hermes" ? parseHermesConfig(configText) : parseConfig(configText, configPath, agent === "codex");
18559
18706
  const unresolved = (detail) => {
18560
18707
  throw new Error(`Native discovery is unresolved (${agent}: ${detail}); provide a reviewed --discovery-inputs file`);
18561
18708
  };
18709
+ if (agent === "sumi") {
18710
+ const directory = sumiConfigDirectory(home);
18711
+ if (witness(join28(directory, "sumi.jsonc")) !== null)
18712
+ unresolved("JSONC configuration requires a supported format adapter");
18713
+ for (const root2 of [directory, join28(home, ".claude"), join28(home, ".agents")])
18714
+ for (const name of ["skill", "skills"])
18715
+ roots.add(join28(root2, name));
18716
+ if (config.skills !== undefined && (!Array.isArray(config.skills) || config.skills.some((path) => typeof path !== "string")))
18717
+ unresolved("skills must be a string array");
18718
+ for (const value of config.skills ?? []) {
18719
+ if (typeof value !== "string" || /[\0$]/.test(value) || !value || !(isAbsolute16(value) || value.startsWith("~/")))
18720
+ unresolved("remote or relative skill sources need a dedicated discovery adapter");
18721
+ roots.add(value.startsWith("~/") ? join28(home, value.slice(2)) : value);
18722
+ }
18723
+ for (const key of ["plugin", "plugins"])
18724
+ if (config[key] !== undefined && !Array.isArray(config[key]))
18725
+ unresolved(`${key} must be an array`);
18726
+ const plugins = [join28(directory, "plugin"), join28(directory, "plugins")];
18727
+ for (const path of plugins) {
18728
+ safe4(path);
18729
+ if (lstatSync22(path, { throwIfNoEntry: false })?.isDirectory())
18730
+ for (const name of readdirSync10(path).sort()) {
18731
+ if (name === "skills-cli.js" && path === plugins[1])
18732
+ continue;
18733
+ const target = join28(path, name);
18734
+ if (!lstatSync22(target).isFile())
18735
+ unresolved("local plugin packages need a dedicated discovery adapter");
18736
+ witness(target);
18737
+ }
18738
+ }
18739
+ for (const root2 of roots) {
18740
+ safe4(root2);
18741
+ if (lstatSync22(root2, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(root2).some((name) => name.endsWith(".md")))
18742
+ unresolved("flat markdown skills require a dedicated native migration adapter");
18743
+ }
18744
+ }
18562
18745
  if (agent === "hermes") {
18563
- const profile = witness(join27(home, ".hermes/active_profile"));
18746
+ const profile = witness(join28(home, ".hermes/active_profile"));
18564
18747
  if (profile?.trim() && profile.trim() !== "default")
18565
18748
  unresolved("a non-default profile is active");
18566
18749
  const declared = config.skills?.external_dirs ?? [], paths = typeof declared === "string" ? [declared] : declared;
@@ -18574,7 +18757,7 @@ function resolveAgentDiscovery(options) {
18574
18757
  continue;
18575
18758
  if (normalized.startsWith("~") && normalized !== "~" && !normalized.startsWith("~/"))
18576
18759
  unresolved("user-specific tilde expansion requires a separate discovery adapter");
18577
- const path = normalized === "~" ? home : normalized.startsWith("~/") ? join27(home, normalized.slice(2)) : resolve15(home, ".hermes", normalized);
18760
+ const path = normalized === "~" ? home : normalized.startsWith("~/") ? join28(home, normalized.slice(2)) : resolve16(home, ".hermes", normalized);
18578
18761
  safe4(path);
18579
18762
  roots.add(path);
18580
18763
  }
@@ -18583,8 +18766,8 @@ function resolveAgentDiscovery(options) {
18583
18766
  const executable = Bun.which("gemini");
18584
18767
  if (executable) {
18585
18768
  let packageRoot;
18586
- for (let directory = dirname17(realpathSync7(executable)), depth = 0;depth < 8; directory = dirname17(directory), depth++) {
18587
- const path = join27(directory, "package.json"), raw = read2(path);
18769
+ for (let directory = dirname17(realpathSync8(executable)), depth = 0;depth < 8; directory = dirname17(directory), depth++) {
18770
+ const path = join28(directory, "package.json"), raw = read2(path);
18588
18771
  if (raw !== null && parseConfig(raw, path).name === "@google/gemini-cli") {
18589
18772
  packageRoot = directory;
18590
18773
  witness(path);
@@ -18595,12 +18778,12 @@ function resolveAgentDiscovery(options) {
18595
18778
  }
18596
18779
  if (!packageRoot)
18597
18780
  unresolved("installed Gemini package layout is not recognized");
18598
- const builtinRoot = join27(packageRoot, "bundle/builtin");
18781
+ const builtinRoot = join28(packageRoot, "bundle/builtin");
18599
18782
  safe4(builtinRoot);
18600
- if (!lstatSync21(builtinRoot, { throwIfNoEntry: false })?.isDirectory())
18783
+ if (!lstatSync22(builtinRoot, { throwIfNoEntry: false })?.isDirectory())
18601
18784
  unresolved("installed Gemini builtin root is missing");
18602
18785
  for (const name of readdirSync10(builtinRoot).sort()) {
18603
- const text2 = witness(join27(builtinRoot, name, "SKILL.md"));
18786
+ const text2 = witness(join28(builtinRoot, name, "SKILL.md"));
18604
18787
  const match = text2?.match(/^---\r?\n[\s\S]*?^name:\s*([a-z0-9][a-z0-9-]*)\s*$/m);
18605
18788
  if (!match?.[1] || builtinNames.includes(match[1]))
18606
18789
  unresolved("installed Gemini builtin name is missing or ambiguous");
@@ -18642,39 +18825,39 @@ function resolveAgentDiscovery(options) {
18642
18825
  function plugin(root2) {
18643
18826
  root2 = canonical6(root2);
18644
18827
  safe4(root2);
18645
- const manifestPath = join27(root2, agent === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json");
18828
+ const manifestPath = join28(root2, agent === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json");
18646
18829
  const raw = witness(manifestPath);
18647
18830
  if (raw === null)
18648
18831
  unresolved("plugin manifest missing");
18649
18832
  const manifest = parseConfig(raw, manifestPath);
18650
- const hooks = witness(join27(root2, "hooks/hooks.json"));
18833
+ const hooks = witness(join28(root2, "hooks/hooks.json"));
18651
18834
  if (manifest.hooks !== undefined || hooks !== null)
18652
18835
  unresolved("plugin hooks require a separate no-skill-injection review");
18653
- const commands = join27(root2, "commands");
18836
+ const commands = join28(root2, "commands");
18654
18837
  safe4(commands);
18655
- if (agent === "claude" && (manifest.commands !== undefined || lstatSync21(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(commands).length))
18838
+ if (agent === "claude" && (manifest.commands !== undefined || lstatSync22(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(commands).length))
18656
18839
  unresolved("legacy command discovery requires retirement or a dedicated format adapter");
18657
- const rootSkill = witness(join27(root2, "SKILL.md"));
18840
+ const rootSkill = witness(join28(root2, "SKILL.md"));
18658
18841
  if (rootSkill !== null)
18659
18842
  roots.add(root2);
18660
18843
  const declared = manifest.skills === undefined ? ["./skills"] : typeof manifest.skills === "string" ? [manifest.skills] : manifest.skills;
18661
18844
  if (!Array.isArray(declared) || declared.some((path) => typeof path !== "string"))
18662
18845
  unresolved("unsupported plugin skill paths");
18663
18846
  if (agent === "claude")
18664
- roots.add(join27(root2, "skills"));
18847
+ roots.add(join28(root2, "skills"));
18665
18848
  for (const value of declared) {
18666
- const path = resolve15(root2, value);
18849
+ const path = resolve16(root2, value);
18667
18850
  if (path !== root2 && !path.startsWith(root2 + sep6))
18668
18851
  unresolved("plugin skill path escapes its root");
18669
18852
  roots.add(path.endsWith(`${sep6}SKILL.md`) ? dirname17(path) : path);
18670
18853
  }
18671
18854
  }
18672
18855
  if (agent === "claude") {
18673
- const registrations = witness(join27(home, ".claude/plugins/installed_plugins.json"));
18674
- const installed = registrations === null ? {} : parseConfig(registrations, join27(home, ".claude/plugins/installed_plugins.json")).plugins;
18675
- const commands = canonical6(join27(home, ".claude/commands"));
18856
+ const registrations = witness(join28(home, ".claude/plugins/installed_plugins.json"));
18857
+ const installed = registrations === null ? {} : parseConfig(registrations, join28(home, ".claude/plugins/installed_plugins.json")).plugins;
18858
+ const commands = canonical6(join28(home, ".claude/commands"));
18676
18859
  safe4(commands);
18677
- if (lstatSync21(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(commands).length)
18860
+ if (lstatSync22(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(commands).length)
18678
18861
  unresolved("legacy home command discovery requires a dedicated format adapter");
18679
18862
  for (const [id, enabled] of Object.entries(config.enabledPlugins ?? {})) {
18680
18863
  if (enabled === false)
@@ -18696,16 +18879,16 @@ function resolveAgentDiscovery(options) {
18696
18879
  const market = config.marketplaces?.[marketplace] ?? (marketplace === "personal" ? { source_type: "local", source: home } : undefined);
18697
18880
  if (split < 1 || market?.source_type !== "local" || typeof market.source !== "string" || !isAbsolute16(market.source))
18698
18881
  unresolved("enabled plugin marketplace is not an explicit local source");
18699
- const catalogText = witness(join27(market.source, ".agents/plugins/marketplace.json"));
18882
+ const catalogText = witness(join28(market.source, ".agents/plugins/marketplace.json"));
18700
18883
  if (catalogText === null)
18701
18884
  unresolved("marketplace catalog missing");
18702
- const catalog = parseConfig(catalogText, join27(market.source, ".agents/plugins/marketplace.json")), matches = Array.isArray(catalog.plugins) ? catalog.plugins.filter((item) => item.name === name) : [];
18885
+ const catalog = parseConfig(catalogText, join28(market.source, ".agents/plugins/marketplace.json")), matches = Array.isArray(catalog.plugins) ? catalog.plugins.filter((item) => item.name === name) : [];
18703
18886
  if (matches.length !== 1 || matches[0]?.source?.source !== "local" || typeof matches[0]?.source?.path !== "string")
18704
18887
  unresolved("plugin catalog source is missing or ambiguous");
18705
- plugin(resolve15(market.source, matches[0].source.path));
18888
+ plugin(resolve16(market.source, matches[0].source.path));
18706
18889
  }
18707
18890
  } else if (agent === "opencode") {
18708
- if (witness(join27(home, ".config/opencode/opencode.jsonc")) !== null || witness(join27(home, ".config/opencode/config.json")) !== null)
18891
+ if (witness(join28(home, ".config/opencode/opencode.jsonc")) !== null || witness(join28(home, ".config/opencode/config.json")) !== null)
18709
18892
  unresolved("additional config layers require review");
18710
18893
  if ((config.plugin?.length ?? 0) > 0 || config.skills?.urls?.length)
18711
18894
  unresolved("external plugin or remote skill source");
@@ -18714,14 +18897,17 @@ function resolveAgentDiscovery(options) {
18714
18897
  unresolved("relative or malformed added skill root");
18715
18898
  roots.add(path);
18716
18899
  }
18717
- const localPlugins = join27(home, ".config/opencode/plugins");
18900
+ const localPlugins = join28(home, ".config/opencode/plugins");
18718
18901
  safe4(localPlugins);
18719
- if (lstatSync21(localPlugins, { throwIfNoEntry: false }) && readdirSync10(localPlugins).some((name) => name !== "skills-cli.js"))
18902
+ if (lstatSync22(localPlugins, { throwIfNoEntry: false }) && readdirSync10(localPlugins).some((name) => name !== "skills-cli.js"))
18720
18903
  unresolved("additional local plugin hooks require review");
18904
+ } else if (agent === "sumi") {
18905
+ if ((config.plugin?.length ?? 0) > 0 || (config.plugins?.length ?? 0) > 0 || sources.some((source) => source.path !== canonical6(configPath) && source.path !== canonical6(join28(sumiConfigDirectory(home), "sumi.jsonc")) && source.sha256 !== null))
18906
+ unresolved("external or foreign local plugins require review");
18721
18907
  } else if (agent === "hermes") {
18722
- for (const directory of [join27(home, ".hermes/plugins"), join27(home, ".hermes/hermes-agent")]) {
18908
+ for (const directory of [join28(home, ".hermes/plugins"), join28(home, ".hermes/hermes-agent")]) {
18723
18909
  safe4(directory);
18724
- if (lstatSync21(directory, { throwIfNoEntry: false }))
18910
+ if (lstatSync22(directory, { throwIfNoEntry: false }))
18725
18911
  unresolved("installed runtime/plugin discovery requires reviewed source bindings");
18726
18912
  }
18727
18913
  if (Bun.which("hermes") || Object.keys(config.plugins ?? {}).length)
@@ -18729,30 +18915,30 @@ function resolveAgentDiscovery(options) {
18729
18915
  } else if (agent === "gemini") {
18730
18916
  if (Object.keys(config.extensions ?? {}).length || config.skills?.paths?.length)
18731
18917
  unresolved("custom extension or skill paths");
18732
- const directory = join27(home, ".gemini/extensions");
18918
+ const directory = join28(home, ".gemini/extensions");
18733
18919
  safe4(directory);
18734
- if (lstatSync21(directory, { throwIfNoEntry: false }))
18920
+ if (lstatSync22(directory, { throwIfNoEntry: false }))
18735
18921
  for (const name of readdirSync10(directory)) {
18736
- const root2 = join27(directory, name);
18922
+ const root2 = join28(directory, name);
18737
18923
  safe4(root2);
18738
- if (!lstatSync21(root2)?.isDirectory())
18924
+ if (!lstatSync22(root2)?.isDirectory())
18739
18925
  continue;
18740
- const raw = witness(join27(root2, "gemini-extension.json"));
18926
+ const raw = witness(join28(root2, "gemini-extension.json"));
18741
18927
  if (raw === null)
18742
18928
  unresolved("extension registration missing");
18743
- const manifest = parseConfig(raw, join27(root2, "gemini-extension.json"));
18744
- if (manifest.hooks !== undefined || witness(join27(root2, "hooks/hooks.json")) !== null || witness(join27(root2, ".gemini-extension-install.json")) !== null)
18929
+ const manifest = parseConfig(raw, join28(root2, "gemini-extension.json"));
18930
+ if (manifest.hooks !== undefined || witness(join28(root2, "hooks/hooks.json")) !== null || witness(join28(root2, ".gemini-extension-install.json")) !== null)
18745
18931
  unresolved("extension hooks or linked registration require review");
18746
- roots.add(join27(root2, "skills"));
18932
+ roots.add(join28(root2, "skills"));
18747
18933
  }
18748
18934
  }
18749
- return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([join27(home, ".hermes/plugins"), join27(home, ".hermes/hermes-agent")].map((path) => canonical6(path))) } : {} };
18935
+ return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([join28(home, ".hermes/plugins"), join28(home, ".hermes/hermes-agent")].map((path) => canonical6(path))) } : {} };
18750
18936
  }
18751
18937
 
18752
18938
  // src/lib/authoring-path.ts
18753
18939
  var NATIVE_DISCOVERY_ROOTS = NATIVE_SKILL_ROOTS.map(([, path]) => path.split("/"));
18754
18940
  function pathParts(path) {
18755
- return resolve16(path).split(sep7).filter(Boolean);
18941
+ return resolve17(path).split(sep7).filter(Boolean);
18756
18942
  }
18757
18943
  function nativeRootMatch(path) {
18758
18944
  const parts = pathParts(path);
@@ -18766,24 +18952,24 @@ function nativeRootMatch(path) {
18766
18952
  return;
18767
18953
  }
18768
18954
  function physicalPath(path) {
18769
- let cursor = resolve16(path);
18955
+ let cursor = resolve17(path);
18770
18956
  const suffix = [];
18771
- while (!lstatSync22(cursor, { throwIfNoEntry: false })) {
18957
+ while (!lstatSync23(cursor, { throwIfNoEntry: false })) {
18772
18958
  suffix.unshift(basename10(cursor));
18773
18959
  const parent = dirname18(cursor);
18774
18960
  if (parent === cursor)
18775
- return resolve16(path);
18961
+ return resolve17(path);
18776
18962
  cursor = parent;
18777
18963
  }
18778
- return resolve16(realpathSync8(cursor), ...suffix);
18964
+ return resolve17(realpathSync9(cursor), ...suffix);
18779
18965
  }
18780
18966
  function contains(root2, path) {
18781
18967
  return path === root2 || path.startsWith(root2 + sep7);
18782
18968
  }
18783
18969
  function configuredRoots(home, explicitHome) {
18784
- const homeData = join28(home, ".hasna", "skills");
18970
+ const homeData = join29(home, ".hasna", "skills");
18785
18971
  const homePolicy = readManagedSkillPolicySnapshot(homeData);
18786
- const configuredPolicy = !explicitHome && resolve16(getDataDirReadOnly()) !== resolve16(homeData) ? readManagedSkillPolicySnapshot(getDataDirReadOnly()) : null;
18972
+ const configuredPolicy = !explicitHome && resolve17(getDataDirReadOnly()) !== resolve17(homeData) ? readManagedSkillPolicySnapshot(getDataDirReadOnly()) : null;
18787
18973
  const policy = homePolicy ?? configuredPolicy;
18788
18974
  const bridge = policy?.value.bridge;
18789
18975
  if (bridge && (typeof bridge.home !== "string" || physicalPath(bridge.home) !== physicalPath(home))) {
@@ -18812,8 +18998,8 @@ function configuredRoots(home, explicitHome) {
18812
18998
  admitBinding(binding);
18813
18999
  continue;
18814
19000
  }
18815
- const nativeHome = join28(home, agent === "opencode" ? ".config/opencode" : `.${agent}`);
18816
- if (!lstatSync22(nativeHome, { throwIfNoEntry: false }))
19001
+ const nativeHome = join29(home, agent === "opencode" ? ".config/opencode" : `.${agent}`);
19002
+ if (!lstatSync23(nativeHome, { throwIfNoEntry: false }))
18817
19003
  continue;
18818
19004
  roots.push(...resolveAgentDiscovery({ home, agent, canonical: physicalPath }).roots);
18819
19005
  }
@@ -18822,22 +19008,22 @@ function configuredRoots(home, explicitHome) {
18822
19008
  function assertPortableAuthoringPath(path, options = {}) {
18823
19009
  if (typeof path !== "string" || !path || path.includes("\x00"))
18824
19010
  throw new Error("Invalid portable authoring path");
18825
- const lexical = resolve16(path);
19011
+ const lexical = resolve17(path);
18826
19012
  const physical = physicalPath(lexical);
18827
19013
  const candidates = [lexical, physical];
18828
19014
  let native = candidates.map(nativeRootMatch).find(Boolean);
18829
- const home = resolve16(options.homeDir ?? homedir6());
18830
- const defaults = NATIVE_DISCOVERY_ROOTS.map((parts) => join28(home, ...parts));
18831
- defaults.push(join28(home, ".codex/plugins/cache"), join28(home, ".claude/plugins/cache"), join28(home, ".claude/plugins/synced"), join28(home, ".gemini/extensions"));
19015
+ const home = resolve17(options.homeDir ?? homedir6());
19016
+ const defaults = NATIVE_DISCOVERY_ROOTS.map((parts) => join29(home, ...parts));
19017
+ defaults.push(join29(home, ".codex/plugins/cache"), join29(home, ".claude/plugins/cache"), join29(home, ".claude/plugins/synced"), join29(home, ".gemini/extensions"));
18832
19018
  for (const root2 of defaults) {
18833
- if (candidates.some((candidate2) => contains(resolve16(root2), candidate2) || contains(physicalPath(root2), candidate2)))
19019
+ if (candidates.some((candidate2) => contains(resolve17(root2), candidate2) || contains(physicalPath(root2), candidate2)))
18834
19020
  native ??= root2;
18835
19021
  }
18836
19022
  if (native) {
18837
19023
  throw new Error(`Refusing portable Skills authoring below native discovery root '${native}': ${lexical}. Set HASNA_SKILLS_DIR to a private authoring corpus outside native roots.`);
18838
19024
  }
18839
19025
  for (const root2 of configuredRoots(home, options.homeDir !== undefined)) {
18840
- if (candidates.some((candidate2) => contains(resolve16(root2), candidate2) || contains(physicalPath(root2), candidate2))) {
19026
+ if (candidates.some((candidate2) => contains(resolve17(root2), candidate2) || contains(physicalPath(root2), candidate2))) {
18841
19027
  throw new Error(`Refusing portable Skills authoring below configured native discovery root: ${lexical}`);
18842
19028
  }
18843
19029
  }
@@ -18849,15 +19035,15 @@ function getPortableSkillsRoot(options = {}) {
18849
19035
  function resolvePortableSkillsRoot(options, migrate) {
18850
19036
  if (options.rootDir)
18851
19037
  return options.rootDir;
18852
- const appDir = options.homeDir ? join29(options.homeDir, ".hasna", "skills") : migrate ? getDataDir() : getDataDirReadOnly();
18853
- const cache = join29(appDir, SKILLS_CACHE_DIRNAME);
19038
+ const appDir = options.homeDir ? join30(options.homeDir, ".hasna", "skills") : migrate ? getDataDir() : getDataDirReadOnly();
19039
+ const cache = join30(appDir, SKILLS_CACHE_DIRNAME);
18854
19040
  if (isOwnerLayoutMigrated(appDir) && safeIsDirectory(cache))
18855
19041
  return cache;
18856
- const installed = join29(appDir, INSTALLED_SKILLS_DIRNAME);
19042
+ const installed = join30(appDir, INSTALLED_SKILLS_DIRNAME);
18857
19043
  return installed;
18858
19044
  }
18859
19045
  function getPortableSkillPath(name, options = {}) {
18860
- return join29(getPortableSkillsRoot(options), normalizePortableSkillName(name));
19046
+ return join30(getPortableSkillsRoot(options), normalizePortableSkillName(name));
18861
19047
  }
18862
19048
  function findPortableSkill(name, options = {}) {
18863
19049
  let normalized;
@@ -18867,7 +19053,7 @@ function findPortableSkill(name, options = {}) {
18867
19053
  return null;
18868
19054
  }
18869
19055
  const path = getPortableSkillPath(normalized, options);
18870
- if (!existsSync13(path) || !statSync10(path).isDirectory())
19056
+ if (!existsSync14(path) || !statSync10(path).isDirectory())
18871
19057
  return null;
18872
19058
  try {
18873
19059
  return summarizePortableSkill(path, normalized);
@@ -18883,7 +19069,7 @@ function listPortableSkills(options = {}) {
18883
19069
  for (const entry of readdirSync11(root2).sort()) {
18884
19070
  if (entry.startsWith("."))
18885
19071
  continue;
18886
- const path = join29(root2, entry);
19072
+ const path = join30(root2, entry);
18887
19073
  if (!safeIsDirectory(path))
18888
19074
  continue;
18889
19075
  try {
@@ -18917,11 +19103,11 @@ function isOfficialSkillName(name) {
18917
19103
  function scaffoldPortableSkill(name, options = {}) {
18918
19104
  const skillName = normalizeNewPortableSkillName(name);
18919
19105
  const readOnlyRoot = resolvePortableSkillsRoot(options, false);
18920
- assertPortableAuthoringPath(join29(readOnlyRoot, skillName), options);
19106
+ assertPortableAuthoringPath(join30(readOnlyRoot, skillName), options);
18921
19107
  const root2 = getPortableSkillsRoot(options);
18922
- const skillPath = join29(root2, skillName);
19108
+ const skillPath = join30(root2, skillName);
18923
19109
  assertPortableAuthoringPath(skillPath, options);
18924
- if (existsSync13(skillPath)) {
19110
+ if (existsSync14(skillPath)) {
18925
19111
  if (!options.overwrite)
18926
19112
  throw new Error(`Skill '${skillName}' already exists at ${skillPath}`);
18927
19113
  rmSync4(skillPath, { recursive: true, force: true });
@@ -18939,7 +19125,7 @@ function scaffoldPortableSkill(name, options = {}) {
18939
19125
  }
18940
19126
  function portPortableSkillDirectory(sourceDir, options = {}) {
18941
19127
  const absoluteSource = normalize4(sourceDir);
18942
- if (!existsSync13(absoluteSource) || !statSync10(absoluteSource).isDirectory()) {
19128
+ if (!existsSync14(absoluteSource) || !statSync10(absoluteSource).isDirectory()) {
18943
19129
  throw new Error(`Import directory not found: ${sourceDir}`);
18944
19130
  }
18945
19131
  const continueOnError = options.continueOnError ?? true;
@@ -18953,7 +19139,7 @@ function portPortableSkillDirectory(sourceDir, options = {}) {
18953
19139
  const skipped = [];
18954
19140
  const entries = readdirSync11(absoluteSource, { withFileTypes: true }).map((entry) => entry.name).filter((entryName) => !entryName.startsWith(".")).sort();
18955
19141
  for (const entryName of entries) {
18956
- const childPath = join29(absoluteSource, entryName);
19142
+ const childPath = join30(absoluteSource, entryName);
18957
19143
  if (!safeIsDirectory(childPath))
18958
19144
  continue;
18959
19145
  if (!isSkillCandidate(childPath)) {
@@ -18982,19 +19168,19 @@ function portPortableSkillDirectory(sourceDir, options = {}) {
18982
19168
  };
18983
19169
  }
18984
19170
  function isSkillCandidate(dir) {
18985
- return existsSync13(join29(dir, "SKILL.md")) || existsSync13(join29(dir, "skill.json")) || existsSync13(join29(dir, "package.json"));
19171
+ return existsSync14(join30(dir, "SKILL.md")) || existsSync14(join30(dir, "skill.json")) || existsSync14(join30(dir, "package.json"));
18986
19172
  }
18987
19173
  function physicalImportPath(path) {
18988
- let ancestor = resolve17(path);
19174
+ let ancestor = resolve18(path);
18989
19175
  const suffix = [];
18990
- while (!existsSync13(ancestor)) {
19176
+ while (!existsSync14(ancestor)) {
18991
19177
  suffix.unshift(basename11(ancestor));
18992
19178
  ancestor = dirname19(ancestor);
18993
19179
  }
18994
- return join29(realpathSync9(ancestor), ...suffix);
19180
+ return join30(realpathSync10(ancestor), ...suffix);
18995
19181
  }
18996
19182
  function assertDisjointImportPaths(source, destination) {
18997
- const sourcePath = realpathSync9(source);
19183
+ const sourcePath = realpathSync10(source);
18998
19184
  const destinationPath = physicalImportPath(destination);
18999
19185
  const contains2 = (parent, child) => {
19000
19186
  const path = relative6(parent, child);
@@ -19006,7 +19192,7 @@ function assertDisjointImportPaths(source, destination) {
19006
19192
  }
19007
19193
  function portPortableSkill(sourcePath, options = {}) {
19008
19194
  const absoluteSource = normalize4(sourcePath);
19009
- if (!existsSync13(absoluteSource) || !statSync10(absoluteSource).isDirectory()) {
19195
+ if (!existsSync14(absoluteSource) || !statSync10(absoluteSource).isDirectory()) {
19010
19196
  throw new Error(`Skill source directory not found: ${sourcePath}`);
19011
19197
  }
19012
19198
  const inferred = readPortableSkillManifestForImport(absoluteSource);
@@ -19017,14 +19203,14 @@ function portPortableSkill(sourcePath, options = {}) {
19017
19203
  const via = explicitName ? `Name '${skillName}' matches a bundled official skill.` : `Inferred name '${skillName}'${sourceSlug && sourceSlug !== skillName ? ` (from source folder '${basename11(absoluteSource)}')` : ""} matches a bundled official skill.`;
19018
19204
  throw new Error(`${via} Importing it would shadow the official '${skillName}'. ` + `Pass --name to choose a different name, or --allow-shadow to override deliberately.`);
19019
19205
  }
19020
- const readOnlyDestination = join29(resolvePortableSkillsRoot(options, false), skillName);
19206
+ const readOnlyDestination = join30(resolvePortableSkillsRoot(options, false), skillName);
19021
19207
  assertPortableAuthoringPath(readOnlyDestination, options);
19022
19208
  assertDisjointImportPaths(absoluteSource, readOnlyDestination);
19023
19209
  const root2 = getPortableSkillsRoot(options);
19024
- const destination = join29(root2, skillName);
19210
+ const destination = join30(root2, skillName);
19025
19211
  assertPortableAuthoringPath(destination, options);
19026
19212
  assertDisjointImportPaths(absoluteSource, destination);
19027
- if (existsSync13(destination)) {
19213
+ if (existsSync14(destination)) {
19028
19214
  if (!options.overwrite)
19029
19215
  throw new Error(`Skill '${skillName}' already exists at ${destination}`);
19030
19216
  rmSync4(destination, { recursive: true, force: true });
@@ -19066,13 +19252,13 @@ function buildCorpusManifest(input, name) {
19066
19252
  function writeCorpusSkill(input, options = {}) {
19067
19253
  const name = normalizePortableSkillName(input.name);
19068
19254
  const readOnlyRoot = resolvePortableSkillsRoot(options, false);
19069
- assertPortableAuthoringPath(join29(readOnlyRoot, name), options);
19255
+ assertPortableAuthoringPath(join30(readOnlyRoot, name), options);
19070
19256
  const root2 = getPortableSkillsRoot(options);
19071
- const skillPath = join29(root2, name);
19257
+ const skillPath = join30(root2, name);
19072
19258
  assertPortableAuthoringPath(skillPath, options);
19073
- const created = !existsSync13(skillPath);
19259
+ const created = !existsSync14(skillPath);
19074
19260
  mkdirSync9(skillPath, { recursive: true });
19075
- writeFileSync8(join29(skillPath, "SKILL.md"), input.skillMd);
19261
+ writeFileSync8(join30(skillPath, "SKILL.md"), input.skillMd);
19076
19262
  const manifest = buildCorpusManifest(input, name);
19077
19263
  writeSkillJsonWithHash(skillPath, manifest);
19078
19264
  return { name, path: skillPath, manifest, created };
@@ -19080,22 +19266,22 @@ function writeCorpusSkill(input, options = {}) {
19080
19266
  function installCorpusSkillAtomically(input, options = {}) {
19081
19267
  const name = normalizePortableSkillName(input.name);
19082
19268
  const readOnlyRoot = resolvePortableSkillsRoot(options, false);
19083
- assertPortableAuthoringPath(join29(readOnlyRoot, name), options);
19269
+ assertPortableAuthoringPath(join30(readOnlyRoot, name), options);
19084
19270
  const root2 = getPortableSkillsRoot(options);
19085
- const target = join29(root2, name);
19271
+ const target = join30(root2, name);
19086
19272
  assertPortableAuthoringPath(target, options);
19087
- const created = !existsSync13(target);
19273
+ const created = !existsSync14(target);
19088
19274
  mkdirSync9(root2, { recursive: true });
19089
- const staging = mkdtempSync2(join29(root2, `.pull-${name}-`));
19275
+ const staging = mkdtempSync2(join30(root2, `.pull-${name}-`));
19090
19276
  let moved = false;
19091
19277
  let backup = null;
19092
19278
  try {
19093
- writeFileSync8(join29(staging, "SKILL.md"), input.skillMd);
19279
+ writeFileSync8(join30(staging, "SKILL.md"), input.skillMd);
19094
19280
  const manifest = buildCorpusManifest(input, name);
19095
19281
  writeSkillJsonWithHash(staging, manifest);
19096
- if (existsSync13(target)) {
19097
- backup = mkdtempSync2(join29(root2, `.pull-backup-${name}-`));
19098
- renameSync5(target, join29(backup, name));
19282
+ if (existsSync14(target)) {
19283
+ backup = mkdtempSync2(join30(root2, `.pull-backup-${name}-`));
19284
+ renameSync5(target, join30(backup, name));
19099
19285
  moved = true;
19100
19286
  }
19101
19287
  renameSync5(staging, target);
@@ -19104,9 +19290,9 @@ function installCorpusSkillAtomically(input, options = {}) {
19104
19290
  return { name, path: target, manifest, created };
19105
19291
  } catch (error) {
19106
19292
  rmSync4(staging, { recursive: true, force: true });
19107
- if (moved && backup && existsSync13(join29(backup, name))) {
19293
+ if (moved && backup && existsSync14(join30(backup, name))) {
19108
19294
  try {
19109
- renameSync5(join29(backup, name), target);
19295
+ renameSync5(join30(backup, name), target);
19110
19296
  } catch {}
19111
19297
  }
19112
19298
  throw error;
@@ -19118,10 +19304,10 @@ function validatePortableSkillDirectory(name, skillPath) {
19118
19304
  const issues = [...base.issues];
19119
19305
  const warnings = [...base.warnings];
19120
19306
  let manifest;
19121
- if (existsSync13(skillPath)) {
19122
- const skillJsonPath = join29(skillPath, "skill.json");
19123
- const skillMdPath = join29(skillPath, "SKILL.md");
19124
- if (!existsSync13(skillJsonPath) && !existsSync13(skillMdPath)) {
19307
+ if (existsSync14(skillPath)) {
19308
+ const skillJsonPath = join30(skillPath, "skill.json");
19309
+ const skillMdPath = join30(skillPath, "SKILL.md");
19310
+ if (!existsSync14(skillJsonPath) && !existsSync14(skillMdPath)) {
19125
19311
  add3(issues, "portable.manifest_missing", "Missing portable manifest: expected SKILL.md frontmatter and/or skill.json");
19126
19312
  }
19127
19313
  try {
@@ -19140,7 +19326,7 @@ function validatePortableSkillDirectory(name, skillPath) {
19140
19326
  add3(issues, "portable.version_missing", "Portable manifest missing version");
19141
19327
  }
19142
19328
  const contractIssues = validatePortableManifestContract(manifest, {
19143
- strict: existsSync13(join29(skillPath, "skill.json")),
19329
+ strict: existsSync14(join30(skillPath, "skill.json")),
19144
19330
  skillPath
19145
19331
  });
19146
19332
  for (const issue of contractIssues)
@@ -19180,8 +19366,8 @@ function validatePortableSkillDirectory(name, skillPath) {
19180
19366
  add3(issues, "portable.command_entry_unsafe", `Command '${command.name}' entry '${command.entry}' must stay inside the skill directory`);
19181
19367
  continue;
19182
19368
  }
19183
- const entryPath = join29(skillPath, command.entry);
19184
- if (!existsSync13(entryPath))
19369
+ const entryPath = join30(skillPath, command.entry);
19370
+ if (!existsSync14(entryPath))
19185
19371
  add3(issues, "portable.command_entry_missing", `Command '${command.name}' entry '${command.entry}' is missing`);
19186
19372
  else if (statSync10(entryPath).isDirectory())
19187
19373
  add3(issues, "portable.command_entry_directory", `Command '${command.name}' entry '${command.entry}' must be a file`);
@@ -19191,7 +19377,7 @@ function validatePortableSkillDirectory(name, skillPath) {
19191
19377
  } catch (error) {
19192
19378
  add3(issues, "portable.manifest_invalid", error.message);
19193
19379
  }
19194
- if (manifest?.kind !== "instruction" && !existsSync13(join29(skillPath, "AGENTS.md"))) {
19380
+ if (manifest?.kind !== "instruction" && !existsSync14(join30(skillPath, "AGENTS.md"))) {
19195
19381
  add3(issues, "portable.agents_missing", "Missing AGENTS.md with build-out instructions for coding agents");
19196
19382
  }
19197
19383
  }
@@ -19227,13 +19413,13 @@ async function runPortableSkill(name, args, options = {}) {
19227
19413
  if (!isSafeRelativePath2(command.entry)) {
19228
19414
  return { exitCode: 1, error: `Portable skill '${name}' command entry is unsafe` };
19229
19415
  }
19230
- const entryPath = join29(skill.path, command.entry);
19231
- if (!existsSync13(entryPath)) {
19416
+ const entryPath = join30(skill.path, command.entry);
19417
+ if (!existsSync14(entryPath)) {
19232
19418
  return { exitCode: 1, error: `Entry point '${command.entry}' not found in portable skill '${name}'` };
19233
19419
  }
19234
- const pkgPath = join29(skill.path, "package.json");
19235
- const nodeModules = join29(skill.path, "node_modules");
19236
- if (existsSync13(pkgPath) && !existsSync13(nodeModules) && hasPackageDependencies(pkgPath)) {
19420
+ const pkgPath = join30(skill.path, "package.json");
19421
+ const nodeModules = join30(skill.path, "node_modules");
19422
+ if (existsSync14(pkgPath) && !existsSync14(nodeModules) && hasPackageDependencies(pkgPath)) {
19237
19423
  const install = Bun.spawn(["bun", "install", "--no-save"], {
19238
19424
  cwd: skill.path,
19239
19425
  stdout: "pipe",
@@ -19481,7 +19667,7 @@ function parseSkillMdFrontmatter(content) {
19481
19667
  return Object.keys(result).length > 0 ? result : null;
19482
19668
  }
19483
19669
  function discoverSkillsInDir(dir, source = "custom") {
19484
- if (!existsSync14(dir))
19670
+ if (!existsSync15(dir))
19485
19671
  return [];
19486
19672
  const result = [];
19487
19673
  try {
@@ -19489,8 +19675,8 @@ function discoverSkillsInDir(dir, source = "custom") {
19489
19675
  for (const entry of entries) {
19490
19676
  if (!entry.isDirectory())
19491
19677
  continue;
19492
- const skillMdPath = join30(dir, entry.name, "SKILL.md");
19493
- if (!existsSync14(skillMdPath))
19678
+ const skillMdPath = join31(dir, entry.name, "SKILL.md");
19679
+ if (!existsSync15(skillMdPath))
19494
19680
  continue;
19495
19681
  let content;
19496
19682
  try {
@@ -19509,7 +19695,7 @@ function discoverSkillsInDir(dir, source = "custom") {
19509
19695
  category: fm.category || "Development Tools",
19510
19696
  tags: fm.tags || [],
19511
19697
  ...fm.kind ? { kind: fm.kind } : {},
19512
- ...isHostedMetadataSkillDir(join30(dir, entry.name)) ? { serverOwned: true } : {},
19698
+ ...isHostedMetadataSkillDir(join31(dir, entry.name)) ? { serverOwned: true } : {},
19513
19699
  source
19514
19700
  });
19515
19701
  }
@@ -19518,16 +19704,16 @@ function discoverSkillsInDir(dir, source = "custom") {
19518
19704
  }
19519
19705
  function findExtensionSkillPath(name) {
19520
19706
  const config = loadConfig();
19521
- if (!config.extensionsDir || !existsSync14(config.extensionsDir))
19707
+ if (!config.extensionsDir || !existsSync15(config.extensionsDir))
19522
19708
  return null;
19523
19709
  try {
19524
19710
  const entries = readdirSync12(config.extensionsDir, { withFileTypes: true });
19525
19711
  for (const entry of entries) {
19526
19712
  if (!entry.isDirectory())
19527
19713
  continue;
19528
- const skillDir = join30(config.extensionsDir, entry.name);
19529
- const skillMdPath = join30(skillDir, "SKILL.md");
19530
- if (!existsSync14(skillMdPath))
19714
+ const skillDir = join31(config.extensionsDir, entry.name);
19715
+ const skillMdPath = join31(skillDir, "SKILL.md");
19716
+ if (!existsSync15(skillMdPath))
19531
19717
  continue;
19532
19718
  let content;
19533
19719
  try {
@@ -19574,21 +19760,21 @@ function clearRegistryCache() {
19574
19760
  registryCacheTime = 0;
19575
19761
  registryCacheKey = null;
19576
19762
  }
19577
- function getSkillsByCategory(category) {
19578
- return loadRegistry().filter((s) => s.category === category);
19763
+ function getSkillsByCategory(category, catalog) {
19764
+ return (catalog ?? loadRegistry()).filter((s) => s.category === category);
19579
19765
  }
19580
19766
  function getSkill(name) {
19581
19767
  const registry = loadRegistry();
19582
19768
  const slug = normalizeSkillSlug(name);
19583
19769
  return registry.find((s) => s.name === slug) ?? registry.find((s) => s.name === resolveSkillAlias(slug));
19584
19770
  }
19585
- function getSkillsByTag(tag) {
19771
+ function getSkillsByTag(tag, catalog) {
19586
19772
  const needle = tag.toLowerCase();
19587
- return loadRegistry().filter((s) => s.tags.some((t2) => t2.toLowerCase().includes(needle)));
19773
+ return (catalog ?? loadRegistry()).filter((s) => s.tags.some((t2) => t2.toLowerCase().includes(needle)));
19588
19774
  }
19589
- function getAllTags() {
19775
+ function getAllTags(catalog) {
19590
19776
  const tagSet = new Set;
19591
- for (const skill of loadRegistry()) {
19777
+ for (const skill of catalog ?? loadRegistry()) {
19592
19778
  for (const tag of skill.tags)
19593
19779
  tagSet.add(tag.toLowerCase());
19594
19780
  }
@@ -19596,20 +19782,20 @@ function getAllTags() {
19596
19782
  }
19597
19783
  // src/lib/run-state.ts
19598
19784
  import { createHash as createHash19, randomBytes } from "crypto";
19599
- import { existsSync as existsSync15, mkdirSync as mkdirSync10, readFileSync as readFileSync19, readdirSync as readdirSync13, statSync as statSync11, writeFileSync as writeFileSync9 } from "fs";
19600
- import { extname, join as join31, relative as relative7 } from "path";
19785
+ import { existsSync as existsSync16, mkdirSync as mkdirSync10, readFileSync as readFileSync19, readdirSync as readdirSync13, statSync as statSync11, writeFileSync as writeFileSync9 } from "fs";
19786
+ import { extname, join as join32, relative as relative7 } from "path";
19601
19787
  function createSkillRun(params, targetDir = process.cwd()) {
19602
19788
  const now = new Date;
19603
19789
  const id = createRunId(now);
19604
19790
  const day = now.toISOString().slice(0, 10);
19605
19791
  const skillName = normalizeSkillName(params.skill);
19606
19792
  const root2 = getProjectStateDir(targetDir);
19607
- const runDir = join31(root2, "runs", day, id);
19608
- const logsDir = join31(runDir, "logs");
19609
- const exportDir = join31(root2, "exports", skillName, id);
19793
+ const runDir = join32(root2, "runs", day, id);
19794
+ const logsDir = join32(runDir, "logs");
19795
+ const exportDir = join32(root2, "exports", skillName, id);
19610
19796
  mkdirSync10(logsDir, { recursive: true });
19611
19797
  mkdirSync10(exportDir, { recursive: true });
19612
- mkdirSync10(join31(root2, "tmp"), { recursive: true });
19798
+ mkdirSync10(join32(root2, "tmp"), { recursive: true });
19613
19799
  const record3 = {
19614
19800
  id,
19615
19801
  skill: skillName,
@@ -19661,27 +19847,27 @@ function updateSkillRun(context, patch) {
19661
19847
  return context.record;
19662
19848
  }
19663
19849
  function writeRunLogs(context, stdout = "", stderr = "") {
19664
- writeFileSync9(join31(context.logsDir, "stdout.log"), stdout);
19665
- writeFileSync9(join31(context.logsDir, "stderr.log"), stderr);
19850
+ writeFileSync9(join32(context.logsDir, "stdout.log"), stdout);
19851
+ writeFileSync9(join32(context.logsDir, "stderr.log"), stderr);
19666
19852
  }
19667
19853
  function appendRunEvent(context, event, data = {}) {
19668
19854
  const line = JSON.stringify({ ts: new Date().toISOString(), event, ...data }) + `
19669
19855
  `;
19670
- const path = join31(context.runDir, "events.ndjson");
19671
- const previous = existsSync15(path) ? readFileSync19(path, "utf-8") : "";
19856
+ const path = join32(context.runDir, "events.ndjson");
19857
+ const previous = existsSync16(path) ? readFileSync19(path, "utf-8") : "";
19672
19858
  writeFileSync9(path, previous + line);
19673
19859
  }
19674
19860
  function listSkillRuns(targetDir = process.cwd(), limit = 50) {
19675
- const runsRoot = join31(getProjectStateDir(targetDir), "runs");
19676
- if (!existsSync15(runsRoot))
19861
+ const runsRoot = join32(getProjectStateDir(targetDir), "runs");
19862
+ if (!existsSync16(runsRoot))
19677
19863
  return [];
19678
19864
  const records = [];
19679
19865
  for (const day of readdirSync13(runsRoot).sort().reverse()) {
19680
- const dayDir = join31(runsRoot, day);
19866
+ const dayDir = join32(runsRoot, day);
19681
19867
  if (!statSync11(dayDir).isDirectory())
19682
19868
  continue;
19683
19869
  for (const runId of readdirSync13(dayDir).sort().reverse()) {
19684
- const record3 = readRunRecord(join31(dayDir, runId));
19870
+ const record3 = readRunRecord(join32(dayDir, runId));
19685
19871
  if (record3)
19686
19872
  records.push(record3);
19687
19873
  if (records.length >= limit)
@@ -19691,29 +19877,29 @@ function listSkillRuns(targetDir = process.cwd(), limit = 50) {
19691
19877
  return records;
19692
19878
  }
19693
19879
  function findSkillRun(runId, targetDir = process.cwd()) {
19694
- const runsRoot = join31(getProjectStateDir(targetDir), "runs");
19695
- if (!existsSync15(runsRoot))
19880
+ const runsRoot = join32(getProjectStateDir(targetDir), "runs");
19881
+ if (!existsSync16(runsRoot))
19696
19882
  return null;
19697
19883
  for (const day of readdirSync13(runsRoot)) {
19698
- const record3 = readRunRecord(join31(runsRoot, day, runId));
19884
+ const record3 = readRunRecord(join32(runsRoot, day, runId));
19699
19885
  if (record3)
19700
19886
  return record3;
19701
19887
  }
19702
19888
  return null;
19703
19889
  }
19704
19890
  function getRunExportDir(runId, skill, targetDir = process.cwd()) {
19705
- return join31(getProjectStateDir(targetDir), "exports", normalizeSkillName(skill), runId);
19891
+ return join32(getProjectStateDir(targetDir), "exports", normalizeSkillName(skill), runId);
19706
19892
  }
19707
19893
  function writeRunRecord(context) {
19708
- writeFileSync9(join31(context.runDir, "run.json"), JSON.stringify(context.record, null, 2) + `
19894
+ writeFileSync9(join32(context.runDir, "run.json"), JSON.stringify(context.record, null, 2) + `
19709
19895
  `);
19710
19896
  }
19711
19897
  function writeArtifactsManifest(context, artifacts) {
19712
- writeFileSync9(join31(context.runDir, "artifacts.json"), JSON.stringify({ runId: context.record.id, artifacts }, null, 2) + `
19898
+ writeFileSync9(join32(context.runDir, "artifacts.json"), JSON.stringify({ runId: context.record.id, artifacts }, null, 2) + `
19713
19899
  `);
19714
19900
  }
19715
19901
  function collectRunArtifacts(context) {
19716
- if (!existsSync15(context.exportDir))
19902
+ if (!existsSync16(context.exportDir))
19717
19903
  return [];
19718
19904
  const artifacts = [];
19719
19905
  for (const path of walkFiles(context.exportDir)) {
@@ -19729,8 +19915,8 @@ function collectRunArtifacts(context) {
19729
19915
  return artifacts.sort((a, b) => a.path.localeCompare(b.path));
19730
19916
  }
19731
19917
  function readRunRecord(runDir) {
19732
- const path = join31(runDir, "run.json");
19733
- if (!existsSync15(path))
19918
+ const path = join32(runDir, "run.json");
19919
+ if (!existsSync16(path))
19734
19920
  return null;
19735
19921
  try {
19736
19922
  return JSON.parse(readFileSync19(path, "utf-8"));
@@ -19741,7 +19927,7 @@ function readRunRecord(runDir) {
19741
19927
  function walkFiles(dir) {
19742
19928
  const files = [];
19743
19929
  for (const entry of readdirSync13(dir)) {
19744
- const full = join31(dir, entry);
19930
+ const full = join32(dir, entry);
19745
19931
  if (statSync11(full).isDirectory())
19746
19932
  files.push(...walkFiles(full));
19747
19933
  else
@@ -40146,10 +40332,10 @@ async function readQuoteUnavailableCode(response) {
40146
40332
  let timer;
40147
40333
  let deadlineExceeded = false;
40148
40334
  const expired = Symbol("quote body deadline");
40149
- const deadline = new Promise((resolve18) => {
40335
+ const deadline = new Promise((resolve19) => {
40150
40336
  timer = setTimeout(() => {
40151
40337
  deadlineExceeded = true;
40152
- resolve18(expired);
40338
+ resolve19(expired);
40153
40339
  reader.cancel().catch(() => {});
40154
40340
  }, 1500);
40155
40341
  });
@@ -41248,14 +41434,14 @@ async function createRemoteSkillsClient(env = process.env) {
41248
41434
  return connection ? new RemoteSkillsClient(connection.apiKey, connection.apiOrigin) : null;
41249
41435
  }
41250
41436
  // src/lib/scheduler.ts
41251
- import { existsSync as existsSync16, readFileSync as readFileSync20, writeFileSync as writeFileSync10, mkdirSync as mkdirSync11 } from "fs";
41252
- import { join as join32 } from "path";
41437
+ import { existsSync as existsSync17, readFileSync as readFileSync20, writeFileSync as writeFileSync10, mkdirSync as mkdirSync11 } from "fs";
41438
+ import { join as join33 } from "path";
41253
41439
  function getSchedulesPath(targetDir = process.cwd()) {
41254
- return join32(targetDir, ".skills", "schedules.json");
41440
+ return join33(targetDir, ".skills", "schedules.json");
41255
41441
  }
41256
41442
  function loadSchedules(targetDir = process.cwd()) {
41257
41443
  const path = getSchedulesPath(targetDir);
41258
- if (existsSync16(path)) {
41444
+ if (existsSync17(path)) {
41259
41445
  try {
41260
41446
  return JSON.parse(readFileSync20(path, "utf-8"));
41261
41447
  } catch {}
@@ -41264,8 +41450,8 @@ function loadSchedules(targetDir = process.cwd()) {
41264
41450
  }
41265
41451
  function saveSchedules(data, targetDir = process.cwd()) {
41266
41452
  const path = getSchedulesPath(targetDir);
41267
- const dir = join32(targetDir, ".skills");
41268
- if (!existsSync16(dir))
41453
+ const dir = join33(targetDir, ".skills");
41454
+ if (!existsSync17(dir))
41269
41455
  mkdirSync11(dir, { recursive: true });
41270
41456
  writeFileSync10(path, JSON.stringify(data, null, 2));
41271
41457
  }
@@ -41480,8 +41666,8 @@ function revisionIdOfRecord(record8) {
41480
41666
  return revisionIdOf(record8);
41481
41667
  }
41482
41668
  // src/lib/pull.ts
41483
- import { existsSync as existsSync17, mkdirSync as mkdirSync12, mkdtempSync as mkdtempSync3, readFileSync as readFileSync21, renameSync as renameSync6, rmSync as rmSync5, writeFileSync as writeFileSync11 } from "fs";
41484
- import { dirname as dirname20, join as join33 } from "path";
41669
+ import { existsSync as existsSync18, mkdirSync as mkdirSync12, mkdtempSync as mkdtempSync3, readFileSync as readFileSync21, renameSync as renameSync6, rmSync as rmSync5, writeFileSync as writeFileSync11 } from "fs";
41670
+ import { dirname as dirname20, join as join34 } from "path";
41485
41671
 
41486
41672
  // src/lib/skill-bundles.ts
41487
41673
  import { createHmac, timingSafeEqual } from "crypto";
@@ -41575,7 +41761,7 @@ async function pullOne(client2, rawName, corpusOptions, verify) {
41575
41761
  return reconcileTombstone(slug2, corpusOptions);
41576
41762
  }
41577
41763
  if (bundleResponse.status === 404) {
41578
- const marker = readPullMarker(join33(getPortableSkillsRoot(corpusOptions), slug2));
41764
+ const marker = readPullMarker(join34(getPortableSkillsRoot(corpusOptions), slug2));
41579
41765
  if (isPublishedInstallMarker(marker)) {
41580
41766
  return { name: slug2, success: true, purged: true, removed: false };
41581
41767
  }
@@ -41614,7 +41800,7 @@ async function pullOne(client2, rawName, corpusOptions, verify) {
41614
41800
  return { name: slug2, success: false, error: `Skill '${slug2}' was not found on the configured Skills instance.` };
41615
41801
  }
41616
41802
  if (!meta3?.revisionId) {
41617
- const marker = readPullMarker(join33(getPortableSkillsRoot(corpusOptions), slug2));
41803
+ const marker = readPullMarker(join34(getPortableSkillsRoot(corpusOptions), slug2));
41618
41804
  if (isPublishedInstallMarker(marker)) {
41619
41805
  return { name: slug2, success: true, purged: true, removed: false };
41620
41806
  }
@@ -41671,7 +41857,7 @@ function provenRevision(meta3, slug2, bundle2) {
41671
41857
  return declared;
41672
41858
  }
41673
41859
  function reconcileTombstone(slug2, corpusOptions) {
41674
- const target = join33(getPortableSkillsRoot(corpusOptions), slug2);
41860
+ const target = join34(getPortableSkillsRoot(corpusOptions), slug2);
41675
41861
  if (!hasSkillsOwnershipMarker(target)) {
41676
41862
  return { name: slug2, success: true, tombstoned: true, removed: false, leftInPlace: true };
41677
41863
  }
@@ -41680,7 +41866,7 @@ function reconcileTombstone(slug2, corpusOptions) {
41680
41866
  }
41681
41867
  function readPullMarker(dir) {
41682
41868
  try {
41683
- return JSON.parse(readFileSync21(join33(dir, PULL_MARKER_FILE), "utf-8"));
41869
+ return JSON.parse(readFileSync21(join34(dir, PULL_MARKER_FILE), "utf-8"));
41684
41870
  } catch {
41685
41871
  return null;
41686
41872
  }
@@ -41815,14 +42001,14 @@ function verifyBundleResponseBytes(buffer, response, verify = {}) {
41815
42001
  function installBundleAtomically(name, entries, options = {}, marker = {}) {
41816
42002
  const root2 = getPortableSkillsRoot(options);
41817
42003
  mkdirSync12(root2, { recursive: true });
41818
- const target = join33(root2, name);
41819
- const created = !existsSync17(target);
41820
- const staging = mkdtempSync3(join33(root2, `.pull-${name}-`));
42004
+ const target = join34(root2, name);
42005
+ const created = !existsSync18(target);
42006
+ const staging = mkdtempSync3(join34(root2, `.pull-${name}-`));
41821
42007
  let moved = false;
41822
42008
  let backup = null;
41823
42009
  try {
41824
42010
  for (const entry of entries) {
41825
- const destination = join33(staging, entry.path);
42011
+ const destination = join34(staging, entry.path);
41826
42012
  mkdirSync12(dirname20(destination), { recursive: true });
41827
42013
  writeFileSync11(destination, entry.bytes, { mode: entry.mode });
41828
42014
  }
@@ -41834,9 +42020,9 @@ function installBundleAtomically(name, entries, options = {}, marker = {}) {
41834
42020
  ...marker.signature ? { signature: marker.signature } : {},
41835
42021
  ...marker.revisionId ? { revisionId: marker.revisionId } : {}
41836
42022
  });
41837
- if (existsSync17(target)) {
41838
- backup = mkdtempSync3(join33(root2, `.pull-backup-${name}-`));
41839
- renameSync6(target, join33(backup, name));
42023
+ if (existsSync18(target)) {
42024
+ backup = mkdtempSync3(join34(root2, `.pull-backup-${name}-`));
42025
+ renameSync6(target, join34(backup, name));
41840
42026
  moved = true;
41841
42027
  }
41842
42028
  renameSync6(staging, target);
@@ -41844,9 +42030,9 @@ function installBundleAtomically(name, entries, options = {}, marker = {}) {
41844
42030
  rmSync5(backup, { recursive: true, force: true });
41845
42031
  } catch (error61) {
41846
42032
  rmSync5(staging, { recursive: true, force: true });
41847
- if (moved && backup && existsSync17(join33(backup, name))) {
42033
+ if (moved && backup && existsSync18(join34(backup, name))) {
41848
42034
  try {
41849
- renameSync6(join33(backup, name), target);
42035
+ renameSync6(join34(backup, name), target);
41850
42036
  } catch {}
41851
42037
  }
41852
42038
  throw error61;
@@ -41865,7 +42051,7 @@ function writePullMarker(dir, record8) {
41865
42051
  ...record8.revisionId ? { revisionId: record8.revisionId } : {},
41866
42052
  syncedAt: new Date().toISOString()
41867
42053
  };
41868
- writeFileSync11(join33(dir, PULL_MARKER_FILE), `${JSON.stringify(marker, null, 2)}
42054
+ writeFileSync11(join34(dir, PULL_MARKER_FILE), `${JSON.stringify(marker, null, 2)}
41869
42055
  `);
41870
42056
  }
41871
42057
  async function safeMeta(client2, slug2) {
@@ -42307,12 +42493,12 @@ class RemotePrivatePublicationsClient {
42307
42493
  previous = view;
42308
42494
  if (!["queued", "verifying"].includes(view.state) || Date.now() >= until)
42309
42495
  return view;
42310
- await new Promise((resolve18) => {
42496
+ await new Promise((resolve19) => {
42311
42497
  const timer = setTimeout(done, Math.min(1000, until - Date.now()));
42312
42498
  function done() {
42313
42499
  clearTimeout(timer);
42314
42500
  options.signal?.removeEventListener("abort", done);
42315
- resolve18();
42501
+ resolve19();
42316
42502
  }
42317
42503
  options.signal?.addEventListener("abort", done, { once: true });
42318
42504
  });
@@ -43868,16 +44054,16 @@ function clone3(value) {
43868
44054
  return JSON.parse(JSON.stringify(value));
43869
44055
  }
43870
44056
  // src/lib/feedback.ts
43871
- import { appendFileSync, existsSync as existsSync18, mkdirSync as mkdirSync14 } from "fs";
43872
- import { dirname as dirname22, join as join34 } from "path";
44057
+ import { appendFileSync, existsSync as existsSync19, mkdirSync as mkdirSync14 } from "fs";
44058
+ import { dirname as dirname22, join as join35 } from "path";
43873
44059
  import { Database } from "bun:sqlite";
43874
44060
  function getFeedbackDbPath() {
43875
- return join34(getDataDir(), "skills.db");
44061
+ return join35(getDataDir(), "skills.db");
43876
44062
  }
43877
44063
  function getFeedbackDb() {
43878
44064
  const dbPath = getFeedbackDbPath();
43879
44065
  const dir = dirname22(dbPath);
43880
- if (!existsSync18(dir))
44066
+ if (!existsSync19(dir))
43881
44067
  mkdirSync14(dir, { recursive: true });
43882
44068
  const db = new Database(dbPath);
43883
44069
  db.exec("PRAGMA journal_mode = WAL");
@@ -43904,9 +44090,9 @@ function saveFeedback(input2) {
43904
44090
  throw new Error("Feedback message is required");
43905
44091
  const category = input2.category ?? "general";
43906
44092
  if (isApiMode()) {
43907
- const path = join34(getDataDir(), "feedback.jsonl");
44093
+ const path = join35(getDataDir(), "feedback.jsonl");
43908
44094
  const dir = dirname22(path);
43909
- if (!existsSync18(dir))
44095
+ if (!existsSync19(dir))
43910
44096
  mkdirSync14(dir, { recursive: true });
43911
44097
  appendFileSync(path, JSON.stringify({ message, category, email: input2.email ?? null, agent: input2.agent ?? null, version: input2.version ?? null, createdAt: new Date().toISOString() }) + `
43912
44098
  `);
@@ -43930,14 +44116,14 @@ function isApiMode(env = process.env) {
43930
44116
  // src/lib/native-storage.ts
43931
44117
  import { createHash as createHash23, createHmac as createHmac2 } from "crypto";
43932
44118
  import {
43933
- existsSync as existsSync19,
44119
+ existsSync as existsSync20,
43934
44120
  mkdirSync as mkdirSync15,
43935
44121
  readFileSync as readFileSync23,
43936
44122
  readdirSync as readdirSync14,
43937
44123
  statSync as statSync12,
43938
44124
  writeFileSync as writeFileSync13
43939
44125
  } from "fs";
43940
- import { dirname as dirname23, join as join35, normalize as normalize5, relative as relative9, sep as sep9 } from "path";
44126
+ import { dirname as dirname23, join as join36, normalize as normalize5, relative as relative9, sep as sep9 } from "path";
43941
44127
  var SKILLS_STORAGE_TABLES = [
43942
44128
  "skills_sync_records",
43943
44129
  "skills_sync_cursors"
@@ -44074,7 +44260,7 @@ function getSkillsNativeStorageStatus(options = {}) {
44074
44260
  local: {
44075
44261
  dataDir: getDataDir(),
44076
44262
  projectStateDir: getProjectStateDir(targetDir),
44077
- feedbackDbPath: join35(getDataDir(), "skills.db")
44263
+ feedbackDbPath: join36(getDataDir(), "skills.db")
44078
44264
  },
44079
44265
  remote: {
44080
44266
  databaseConfigured: Boolean(config2.databaseUrl),
@@ -44097,7 +44283,7 @@ function getStorageStatus(options = {}) {
44097
44283
  function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
44098
44284
  const projectStateDir = getProjectStateDir(targetDir);
44099
44285
  const files = [];
44100
- if (existsSync19(projectStateDir)) {
44286
+ if (existsSync20(projectStateDir)) {
44101
44287
  for (const filePath of walkFiles2(projectStateDir)) {
44102
44288
  const bytes = readFileSync23(filePath);
44103
44289
  const relativePath = toPosix(relative9(targetDir, filePath));
@@ -44124,7 +44310,7 @@ function importSkillsLocalSnapshot(snapshot2, targetDir = process.cwd(), options
44124
44310
  continue;
44125
44311
  }
44126
44312
  const absolutePath4 = resolveSnapshotPath(targetDir, file2.path);
44127
- if (existsSync19(absolutePath4) && !options.overwrite) {
44313
+ if (existsSync20(absolutePath4) && !options.overwrite) {
44128
44314
  skipped += 1;
44129
44315
  continue;
44130
44316
  }
@@ -44431,7 +44617,7 @@ function parsePositiveInteger(value) {
44431
44617
  function walkFiles2(dir) {
44432
44618
  const files = [];
44433
44619
  for (const entry of readdirSync14(dir)) {
44434
- const full = join35(dir, entry);
44620
+ const full = join36(dir, entry);
44435
44621
  const stats = statSync12(full);
44436
44622
  if (stats.isDirectory())
44437
44623
  files.push(...walkFiles2(full));
@@ -44448,7 +44634,7 @@ function resolveSnapshotPath(targetDir, snapshotPath) {
44448
44634
  if (!toPosix(normalizedPath).startsWith(".skills/")) {
44449
44635
  throw new Error(`Snapshot path must stay inside .skills: ${snapshotPath}`);
44450
44636
  }
44451
- return join35(targetDir, normalizedPath);
44637
+ return join36(targetDir, normalizedPath);
44452
44638
  }
44453
44639
  function normalizeS3Prefix(prefix) {
44454
44640
  return (prefix ?? "").trim().replace(/^\/+|\/+$/g, "");
@@ -44521,12 +44707,12 @@ import {
44521
44707
  statSync as statSync14,
44522
44708
  writeFileSync as writeFileSync14
44523
44709
  } from "fs";
44524
- import { dirname as dirname24, isAbsolute as isAbsolute19, relative as relative10, resolve as resolve18, sep as sep11 } from "path";
44710
+ import { dirname as dirname24, isAbsolute as isAbsolute19, relative as relative10, resolve as resolve19, sep as sep11 } from "path";
44525
44711
 
44526
44712
  // src/lib/portable-snapshot-filter.ts
44527
44713
  import { readdirSync as readdirSync15, statSync as statSync13 } from "fs";
44528
44714
  import { homedir as homedir7 } from "os";
44529
- import { join as join36, sep as sep10 } from "path";
44715
+ import { join as join37, sep as sep10 } from "path";
44530
44716
  var SYNC_HOMES = [
44531
44717
  { name: "skills", subClass: "skills", agent: null },
44532
44718
  { name: "custom", subClass: "custom", agent: null },
@@ -44619,16 +44805,16 @@ function isPortableWithinSkill(relativeParts) {
44619
44805
  function homePathFor(definition, homesRoot) {
44620
44806
  const home = homesRoot ?? homedir7();
44621
44807
  if (definition.subClass === "skills" || definition.subClass === "custom") {
44622
- return join36(skillsDataRootForHome(home), definition.name);
44808
+ return join37(skillsDataRootForHome(home), definition.name);
44623
44809
  }
44624
44810
  if (definition.agent === "opencode") {
44625
- return join36(home, ".config", "opencode", "skills");
44811
+ return join37(home, ".config", "opencode", "skills");
44626
44812
  }
44627
- return join36(home, `.${definition.agent}`, "skills");
44813
+ return join37(home, `.${definition.agent}`, "skills");
44628
44814
  }
44629
44815
  function destinationFor(definition, stationId, relativePath) {
44630
- const category = definition.subClass === "agent-homes" ? join36("agent-homes", definition.agent ?? "") : definition.name;
44631
- return join36("resources", stationId, "skills", category, ...relativePath.split(sep10));
44816
+ const category = definition.subClass === "agent-homes" ? join37("agent-homes", definition.agent ?? "") : definition.name;
44817
+ return join37("resources", stationId, "skills", category, ...relativePath.split(sep10));
44632
44818
  }
44633
44819
  function walkEntries(absoluteRoot) {
44634
44820
  let entries;
@@ -44639,7 +44825,7 @@ function walkEntries(absoluteRoot) {
44639
44825
  }
44640
44826
  const output2 = [];
44641
44827
  for (const entry of entries) {
44642
- const childFull = join36(absoluteRoot, entry.name);
44828
+ const childFull = join37(absoluteRoot, entry.name);
44643
44829
  if (entry.isSymbolicLink()) {
44644
44830
  output2.push({ kind: "symlink", relativePath: entry.name, fullPath: childFull });
44645
44831
  continue;
@@ -44650,7 +44836,7 @@ function walkEntries(absoluteRoot) {
44650
44836
  }
44651
44837
  const nested = walkEntries(childFull);
44652
44838
  for (const item of nested) {
44653
- output2.push({ ...item, relativePath: join36(entry.name, item.relativePath) });
44839
+ output2.push({ ...item, relativePath: join37(entry.name, item.relativePath) });
44654
44840
  }
44655
44841
  continue;
44656
44842
  }
@@ -44759,7 +44945,7 @@ function humanHomes(scanned) {
44759
44945
  }));
44760
44946
  }
44761
44947
  function writeStationSnapshot(options) {
44762
- const repoRoot = resolve18(options.repoRoot ?? process.cwd());
44948
+ const repoRoot = resolve19(options.repoRoot ?? process.cwd());
44763
44949
  const { scanned, plans, totalBytes } = planStationSnapshot(options);
44764
44950
  const manifestFiles = plans.map((plan) => ({
44765
44951
  relativePath: plan.source.relativePath,
@@ -44785,7 +44971,7 @@ function writeStationSnapshot(options) {
44785
44971
  const conflicts = [];
44786
44972
  const untouched = [];
44787
44973
  for (const plan of plans) {
44788
- const destination = resolve18(repoRoot, plan.destination);
44974
+ const destination = resolve19(repoRoot, plan.destination);
44789
44975
  const destinationRelative = relative10(repoRoot, destination);
44790
44976
  if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep11) || isAbsolute19(destinationRelative)) {
44791
44977
  throw new StationSnapshotError("DESTINATION_ESCAPE", `destination escapes repo root: ${plan.destination}`);
@@ -44808,7 +44994,7 @@ function writeStationSnapshot(options) {
44808
44994
  }
44809
44995
  let written = 0;
44810
44996
  for (const plan of untouched) {
44811
- const destination = resolve18(repoRoot, plan.destination);
44997
+ const destination = resolve19(repoRoot, plan.destination);
44812
44998
  mkdirSync16(dirname24(destination), { recursive: true });
44813
44999
  copyFileSync(plan.source.fullPath, destination);
44814
45000
  written += 1;
@@ -44827,7 +45013,7 @@ function writeStationSnapshot(options) {
44827
45013
  },
44828
45014
  files: manifestFiles
44829
45015
  };
44830
- const manifestPath = resolve18(repoRoot, "resources", options.stationId, "skills", "sync-manifest.json");
45016
+ const manifestPath = resolve19(repoRoot, "resources", options.stationId, "skills", "sync-manifest.json");
44831
45017
  mkdirSync16(dirname24(manifestPath), { recursive: true });
44832
45018
  writeFileSync14(manifestPath, `${JSON.stringify(manifest, null, 2)}
44833
45019
  `);
@@ -44848,7 +45034,7 @@ import {
44848
45034
  statSync as statSync15,
44849
45035
  writeFileSync as writeFileSync15
44850
45036
  } from "fs";
44851
- import { dirname as dirname25, join as join37, resolve as resolve19, sep as sep12 } from "path";
45037
+ import { dirname as dirname25, join as join38, resolve as resolve20, sep as sep12 } from "path";
44852
45038
  var STATION_HYDRATION_MANIFEST_SCHEMA = "hasna.fleet-resources.skills-hydration-manifest/v1";
44853
45039
  var STATION_HYDRATION_PRODUCER = { name: "@hasna/skills", version: package_default.version };
44854
45040
  var MANIFEST_HASH_KEY_SEP = String.fromCharCode(0);
@@ -44856,11 +45042,11 @@ function fail(code2, message, detail = []) {
44856
45042
  throw new StationSnapshotError(code2, message, detail);
44857
45043
  }
44858
45044
  function snapshotRootFor(repoRoot, stationId) {
44859
- return join37(repoRoot, "resources", stationId, "skills");
45045
+ return join38(repoRoot, "resources", stationId, "skills");
44860
45046
  }
44861
45047
  function readSnapshotManifest(repoRoot, stationId) {
44862
45048
  const snapshotRoot = snapshotRootFor(repoRoot, stationId);
44863
- const manifestPath = join37(snapshotRoot, "sync-manifest.json");
45049
+ const manifestPath = join38(snapshotRoot, "sync-manifest.json");
44864
45050
  let manifest;
44865
45051
  try {
44866
45052
  manifest = JSON.parse(readFileSync26(manifestPath, "utf8"));
@@ -44887,7 +45073,7 @@ function planStationHydration(stationId, repoRoot) {
44887
45073
  const hashMismatches = [];
44888
45074
  const skippedByRule = [];
44889
45075
  for (const agent of SYNC_AGENTS) {
44890
- const agentRoot = join37(snapshotRoot, "agent-homes", agent);
45076
+ const agentRoot = join38(snapshotRoot, "agent-homes", agent);
44891
45077
  let identEntries;
44892
45078
  try {
44893
45079
  identEntries = readdirSync16(agentRoot, { withFileTypes: true });
@@ -44898,7 +45084,7 @@ function planStationHydration(stationId, repoRoot) {
44898
45084
  if (!identEntry.isDirectory() || identEntry.name.startsWith(".")) {
44899
45085
  continue;
44900
45086
  }
44901
- const identRoot = join37(agentRoot, identEntry.name);
45087
+ const identRoot = join38(agentRoot, identEntry.name);
44902
45088
  const entries = walkEntries(identRoot);
44903
45089
  for (const entry of entries) {
44904
45090
  const relativeParts = [identEntry.name, ...entry.relativePath.split(sep12)];
@@ -45049,8 +45235,8 @@ function skillSha256(skill) {
45049
45235
  `)).digest("hex");
45050
45236
  }
45051
45237
  function writeStationHydration(options) {
45052
- const repoRoot = resolve19(options.repoRoot ?? process.cwd());
45053
- const cacheRoot = resolve19(options.cacheRoot ?? resolveCorpusRoot());
45238
+ const repoRoot = resolve20(options.repoRoot ?? process.cwd());
45239
+ const cacheRoot = resolve20(options.cacheRoot ?? resolveCorpusRoot());
45054
45240
  const plan = planStationHydration(options.stationId, repoRoot);
45055
45241
  const resultSkills = plan.winners.map((skill) => ({
45056
45242
  ident: skill.ident,
@@ -45083,7 +45269,7 @@ function writeStationHydration(options) {
45083
45269
  const toWrite = [];
45084
45270
  for (const skill of plan.winners) {
45085
45271
  for (const file2 of skill.files) {
45086
- const destination = join37(cacheRoot, skill.ident, file2.withinIdent);
45272
+ const destination = join38(cacheRoot, skill.ident, file2.withinIdent);
45087
45273
  const digest3 = sha256File(file2.winner.fullPath);
45088
45274
  let existingDigest = null;
45089
45275
  try {
@@ -45125,7 +45311,7 @@ function writeStationHydration(options) {
45125
45311
  },
45126
45312
  skills: resultSkills
45127
45313
  };
45128
- const hydrationManifestPath = join37(dirname25(cacheRoot), `hydration-${options.stationId}.json`);
45314
+ const hydrationManifestPath = join38(dirname25(cacheRoot), `hydration-${options.stationId}.json`);
45129
45315
  mkdirSync17(dirname25(hydrationManifestPath), { recursive: true });
45130
45316
  writeFileSync15(hydrationManifestPath, `${JSON.stringify(hydration, null, 2)}
45131
45317
  `);
@@ -45137,23 +45323,23 @@ function writeStationHydration(options) {
45137
45323
  };
45138
45324
  }
45139
45325
  // src/lib/private-publication-recovery.ts
45140
- import { constants as constants12, closeSync as closeSync13, fsyncSync as fsyncSync5, fstatSync as fstatSync13, lstatSync as lstatSync23, mkdirSync as mkdirSync18, openSync as openSync13, readSync as readSync11, realpathSync as realpathSync10, renameSync as renameSync7, unlinkSync as unlinkSync5, writeFileSync as writeFileSync16 } from "fs";
45141
- import { dirname as dirname26, isAbsolute as isAbsolute20, join as join38, resolve as resolve20 } from "path";
45326
+ import { constants as constants12, closeSync as closeSync13, fsyncSync as fsyncSync5, fstatSync as fstatSync13, lstatSync as lstatSync24, mkdirSync as mkdirSync18, openSync as openSync13, readSync as readSync11, realpathSync as realpathSync11, renameSync as renameSync7, unlinkSync as unlinkSync5, writeFileSync as writeFileSync16 } from "fs";
45327
+ import { dirname as dirname26, isAbsolute as isAbsolute20, join as join39, resolve as resolve21 } from "path";
45142
45328
  import { randomUUID as randomUUID4 } from "crypto";
45143
45329
  var fail2 = () => {
45144
45330
  throw new PrivatePublicationError("PUBLICATION_RECOVERY_INVALID", "The recovery directory is invalid or changed. Preserve it and inspect the existing intent; do not start a replacement automatically.");
45145
45331
  };
45146
45332
  function safeDirectory(directory2) {
45147
- if (!isAbsolute20(directory2) || directory2 !== resolve20(directory2) || realpathSync10(directory2) !== directory2)
45333
+ if (!isAbsolute20(directory2) || directory2 !== resolve21(directory2) || realpathSync11(directory2) !== directory2)
45148
45334
  return fail2();
45149
45335
  for (let path = directory2;; path = dirname26(path)) {
45150
- const stat2 = lstatSync23(path);
45336
+ const stat2 = lstatSync24(path);
45151
45337
  if (!stat2.isDirectory() || stat2.isSymbolicLink())
45152
45338
  return fail2();
45153
45339
  if (path === dirname26(path))
45154
45340
  break;
45155
45341
  }
45156
- const own2 = lstatSync23(directory2);
45342
+ const own2 = lstatSync24(directory2);
45157
45343
  if ((own2.mode & 63) !== 0 || process.getuid && own2.uid !== process.getuid())
45158
45344
  return fail2();
45159
45345
  return { dev: own2.dev, ino: own2.ino };
@@ -45164,7 +45350,7 @@ function unchangedDirectory(directory2, identity4) {
45164
45350
  return fail2();
45165
45351
  }
45166
45352
  function readOwned(directory2, name, max) {
45167
- const identity4 = safeDirectory(directory2), file2 = join38(directory2, name);
45353
+ const identity4 = safeDirectory(directory2), file2 = join39(directory2, name);
45168
45354
  const fd = openSync13(file2, constants12.O_RDONLY | constants12.O_NOFOLLOW);
45169
45355
  try {
45170
45356
  const stat2 = fstatSync13(fd);
@@ -45188,7 +45374,7 @@ function readOwned(directory2, name, max) {
45188
45374
  }
45189
45375
  }
45190
45376
  function writeOwned(directory2, name, bytes) {
45191
- const fd = openSync13(join38(directory2, name), constants12.O_WRONLY | constants12.O_CREAT | constants12.O_EXCL | constants12.O_NOFOLLOW, 384);
45377
+ const fd = openSync13(join39(directory2, name), constants12.O_WRONLY | constants12.O_CREAT | constants12.O_EXCL | constants12.O_NOFOLLOW, 384);
45192
45378
  try {
45193
45379
  writeFileSync16(fd, bytes);
45194
45380
  fsyncSync5(fd);
@@ -45201,7 +45387,7 @@ function save(directory2, value) {
45201
45387
  writeOwned(directory2, temporary, JSON.stringify(value) + `
45202
45388
  `);
45203
45389
  unchangedDirectory(directory2, identity4);
45204
- renameSync7(join38(directory2, temporary), join38(directory2, "receipt.json"));
45390
+ renameSync7(join39(directory2, temporary), join39(directory2, "receipt.json"));
45205
45391
  const fd = openSync13(directory2, constants12.O_RDONLY | constants12.O_NOFOLLOW);
45206
45392
  try {
45207
45393
  fsyncSync5(fd);
@@ -45214,7 +45400,7 @@ function bind(client2, receipt) {
45214
45400
  throw new PrivatePublicationError("PUBLICATION_IDENTITY_CHANGED", "The fresh session does not match the recovery directory's server, account and membership.");
45215
45401
  }
45216
45402
  async function locked(directory2, action) {
45217
- const identity4 = safeDirectory(directory2), file2 = join38(directory2, "operation.lock");
45403
+ const identity4 = safeDirectory(directory2), file2 = join39(directory2, "operation.lock");
45218
45404
  let fd;
45219
45405
  try {
45220
45406
  fd = openSync13(file2, constants12.O_WRONLY | constants12.O_CREAT | constants12.O_EXCL | constants12.O_NOFOLLOW, 384);
@@ -45230,7 +45416,7 @@ async function locked(directory2, action) {
45230
45416
  } finally {
45231
45417
  closeSync13(fd);
45232
45418
  unchangedDirectory(directory2, identity4);
45233
- const now = lstatSync23(file2);
45419
+ const now = lstatSync24(file2);
45234
45420
  if (now.dev !== lock.dev || now.ino !== lock.ino || !now.isFile() || now.isSymbolicLink())
45235
45421
  fail2();
45236
45422
  unlinkSync5(file2);
@@ -45282,7 +45468,7 @@ async function preparePrivatePublication(client2, sourceDirectory, recoveryDirec
45282
45468
  phase: "prepared",
45283
45469
  intent: null
45284
45470
  };
45285
- if (!isAbsolute20(recoveryDirectory) || resolve20(recoveryDirectory) !== recoveryDirectory || realpathSync10(dirname26(recoveryDirectory)) !== dirname26(recoveryDirectory))
45471
+ if (!isAbsolute20(recoveryDirectory) || resolve21(recoveryDirectory) !== recoveryDirectory || realpathSync11(dirname26(recoveryDirectory)) !== dirname26(recoveryDirectory))
45286
45472
  return fail2();
45287
45473
  mkdirSync18(recoveryDirectory, { mode: 448 });
45288
45474
  safeDirectory(recoveryDirectory);
@@ -45374,7 +45560,7 @@ async function inspectLocked(client2, directory2, cancel) {
45374
45560
  }
45375
45561
  // src/lib/skill-context.ts
45376
45562
  import { createHash as createHash26 } from "crypto";
45377
- import { resolve as resolve21 } from "path";
45563
+ import { resolve as resolve22 } from "path";
45378
45564
  function words(value) {
45379
45565
  return new Set(value.toLowerCase().match(/[a-z0-9]+/g) ?? []);
45380
45566
  }
@@ -45396,7 +45582,11 @@ async function buildSkillContext(input2, options = {}) {
45396
45582
  }
45397
45583
  const profileId = input2.profileId ?? "default";
45398
45584
  const sessionId = input2.sessionId ? input2.agentId ? `${input2.sessionId}:${input2.agentId}` : input2.sessionId : undefined;
45399
- const resolverOptions = { ...options, projectDir: input2.cwd ?? options.projectDir, sessionId, parentSessionId: input2.agentId ? input2.sessionId : undefined };
45585
+ if (input2.parentSessionId !== undefined) {
45586
+ if (input2.agentId !== undefined || !sessionId || input2.parentSessionId === sessionId || input2.parentSessionId !== null && (typeof input2.parentSessionId !== "string" || !input2.parentSessionId.trim()))
45587
+ throw new SkillSelectionError("INVALID_SESSION", "Native session custody requires its own session id and a distinct parent, without a legacy agent id.");
45588
+ }
45589
+ const resolverOptions = { ...options, projectDir: input2.cwd ?? options.projectDir, sessionId, nativeParentSessionId: input2.parentSessionId, parentSessionId: input2.parentSessionId ?? (input2.agentId ? input2.sessionId : undefined) };
45400
45590
  const resolved = await resolveSelectionContext(profileId, resolverOptions);
45401
45591
  const profile = resolved.receipt.profile;
45402
45592
  const explicit = new Set((input2.skills ?? []).map((spec) => selectionKey(exactProfileSelection(spec, profile))));
@@ -45409,7 +45599,7 @@ async function buildSkillContext(input2, options = {}) {
45409
45599
  const promptWords = words(prompt);
45410
45600
  const promptNames = new Set(prompt.toLowerCase().match(/[a-z0-9]+(?:-[a-z0-9]+)*/g) ?? []);
45411
45601
  const loaded = new Set(resolved.session?.loaded ?? []);
45412
- const paths = [...input2.paths ?? [], ...input2.cwd ? [input2.cwd, resolve21(input2.cwd)] : []];
45602
+ const paths = [...input2.paths ?? [], ...input2.cwd ? [input2.cwd, resolve22(input2.cwd)] : []];
45413
45603
  const candidates = profile.selections.map((selection) => {
45414
45604
  const key = selectionKey(selection);
45415
45605
  if (input2.restore && loaded.has(key))