@hasna/skills 0.10.28 → 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.28",
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) {
@@ -14124,8 +14127,8 @@ function upgradeCodexSettingsWitnessV3(previous, reviewedSettingsPath) {
14124
14127
  // src/lib/agent-integration.ts
14125
14128
  import { createHash as createHash12, randomUUID as randomUUID2 } from "crypto";
14126
14129
  import { isDeepStrictEqual as isDeepStrictEqual4 } from "util";
14127
- 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";
14128
- 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";
14129
14132
  import { homedir as homedir2 } from "os";
14130
14133
 
14131
14134
  // src/lib/managed-policy.ts
@@ -14519,6 +14522,114 @@ function disableCodexBundledSkills(text2) {
14519
14522
  return [...candidates][0];
14520
14523
  }
14521
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
+
14522
14633
  // src/lib/agent-integration.ts
14523
14634
  var sha3 = (data) => createHash12("sha256").update(data).digest("hex");
14524
14635
  var ROOTS = NATIVE_SKILL_ROOTS;
@@ -14553,11 +14664,11 @@ function isSystemRootAlias(path) {
14553
14664
  const target = `/private${path}`, link = `private${path}`;
14554
14665
  const identity4 = (stat) => `${stat.dev}:${stat.ino}:${stat.ctimeNs}:${stat.uid}:${stat.mode}`;
14555
14666
  try {
14556
- const before = lstatSync15(path, { bigint: true });
14667
+ const before = lstatSync16(path, { bigint: true });
14557
14668
  if (!before.isSymbolicLink() || before.uid !== 0n || readlinkSync4(path) !== link)
14558
14669
  return false;
14559
14670
  const directories = ["/", "/private", target].map((directory) => {
14560
- const stat = lstatSync15(directory, { bigint: true });
14671
+ const stat = lstatSync16(directory, { bigint: true });
14561
14672
  if (!stat.isDirectory() || stat.uid !== 0n)
14562
14673
  throw new Error("Untrusted system alias directory");
14563
14674
  if (directory !== "/private/tmp") {
@@ -14574,23 +14685,23 @@ function isSystemRootAlias(path) {
14574
14685
  }
14575
14686
  return { directory, identity: identity4(stat) };
14576
14687
  });
14577
- 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);
14578
14689
  } catch {
14579
14690
  return false;
14580
14691
  }
14581
14692
  }
14582
14693
  function canonicalSystemPath(path) {
14583
- const absolute = resolve10(path), root = `/${absolute.split(sep5)[1]}`;
14584
- 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())
14585
14696
  return absolute;
14586
14697
  if (!isSystemRootAlias(root))
14587
14698
  throw new Error(`Refusing symlink path: ${root}`);
14588
14699
  return `/private${absolute}`;
14589
14700
  }
14590
14701
  function assertSafePath(path) {
14591
- let cursor = resolve10(path);
14702
+ let cursor = resolve11(path);
14592
14703
  while (true) {
14593
- if (lstatSync15(cursor, { throwIfNoEntry: false })?.isSymbolicLink() && !isSystemRootAlias(cursor))
14704
+ if (lstatSync16(cursor, { throwIfNoEntry: false })?.isSymbolicLink() && !isSystemRootAlias(cursor))
14594
14705
  throw new Error(`Refusing symlink path: ${cursor}`);
14595
14706
  const parent = dirname11(cursor);
14596
14707
  if (parent === cursor)
@@ -14600,16 +14711,16 @@ function assertSafePath(path) {
14600
14711
  }
14601
14712
  function readRootAlias(home, agent) {
14602
14713
  assertSafePath(home);
14603
- const realHome = realpathSync6(home), alias = join17(realHome, `.${agent}`);
14604
- 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 });
14605
14716
  if (!stat?.isSymbolicLink())
14606
14717
  throw new Error(`Agent root alias changed after planning: ${alias}`);
14607
- const link = readlinkSync4(alias), targetPath = resolve10(dirname11(alias), link);
14718
+ const link = readlinkSync4(alias), targetPath = resolve11(dirname11(alias), link);
14608
14719
  assertSafePath(targetPath);
14609
- const targetStat = lstatSync15(targetPath, { throwIfNoEntry: false });
14720
+ const targetStat = lstatSync16(targetPath, { throwIfNoEntry: false });
14610
14721
  if (!targetStat?.isDirectory())
14611
14722
  throw new Error(`Agent root alias must target an existing directory: ${alias}`);
14612
- const target = realpathSync6(targetPath), inside = relative5(realHome, target);
14723
+ const target = realpathSync7(targetPath), inside = relative5(realHome, target);
14613
14724
  if (!inside || inside === ".." || inside.startsWith(`..${sep5}`) || isAbsolute11(inside))
14614
14725
  throw new Error(`Agent root alias target must be inside the real home: ${alias}`);
14615
14726
  return { agent, home: realHome, alias, target, link, aliasIdentity: `${stat.dev}:${stat.ino}:${stat.ctimeNs}`, targetIdentity: `${targetStat.dev}:${targetStat.ino}` };
@@ -14619,7 +14730,7 @@ function rootAliases(home, allowed = false) {
14619
14730
  return [];
14620
14731
  const result = [];
14621
14732
  for (const agent of ["claude", "codex"])
14622
- if (lstatSync15(join17(home, `.${agent}`), { throwIfNoEntry: false })?.isSymbolicLink())
14733
+ if (lstatSync16(join18(home, `.${agent}`), { throwIfNoEntry: false })?.isSymbolicLink())
14623
14734
  result.push(readRootAlias(home, agent));
14624
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))))
14625
14736
  throw new Error("Agent root alias targets must not overlap");
@@ -14627,7 +14738,7 @@ function rootAliases(home, allowed = false) {
14627
14738
  }
14628
14739
  function canonicalAgentPath(path, aliases) {
14629
14740
  const absolute = canonicalSystemPath(path), binding = aliases.find((item) => absolute === item.alias || absolute.startsWith(item.alias + sep5));
14630
- return binding ? join17(binding.target, relative5(binding.alias, absolute)) : absolute;
14741
+ return binding ? join18(binding.target, relative5(binding.alias, absolute)) : absolute;
14631
14742
  }
14632
14743
  function disabledCodexSkillPaths(config, aliases) {
14633
14744
  const skills = config.skills;
@@ -14646,7 +14757,7 @@ function disabledCodexSkillPaths(config, aliases) {
14646
14757
  continue;
14647
14758
  let document2;
14648
14759
  try {
14649
- document2 = realpathSync6(canonicalAgentPath(entry.path, aliases));
14760
+ document2 = realpathSync7(canonicalAgentPath(entry.path, aliases));
14650
14761
  } catch {
14651
14762
  continue;
14652
14763
  }
@@ -14658,7 +14769,7 @@ function disabledCodexSkillPaths(config, aliases) {
14658
14769
  }
14659
14770
  function recheckRootAliases(aliases) {
14660
14771
  for (const binding of aliases) {
14661
- 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}`))
14662
14773
  throw new Error("Invalid agent root alias binding");
14663
14774
  const current = readRootAlias(binding.home, binding.agent);
14664
14775
  if (JSON.stringify(current) !== JSON.stringify(binding))
@@ -14667,7 +14778,7 @@ function recheckRootAliases(aliases) {
14667
14778
  }
14668
14779
  function readNativeBytes(path, maximum = 64 * 1024 * 1024) {
14669
14780
  assertSafePath(path);
14670
- const before = lstatSync15(path);
14781
+ const before = lstatSync16(path);
14671
14782
  if (!before.isFile() || before.size > maximum)
14672
14783
  throw new Error("Unsupported or oversized native skill file");
14673
14784
  const descriptor = openSync8(path, constants7.O_RDONLY | constants7.O_NOFOLLOW | constants7.O_NONBLOCK);
@@ -14683,7 +14794,7 @@ function readNativeBytes(path, maximum = 64 * 1024 * 1024) {
14683
14794
  break;
14684
14795
  length += count;
14685
14796
  }
14686
- const after = fstatSync8(descriptor), current = lstatSync15(path);
14797
+ const after = fstatSync8(descriptor), current = lstatSync16(path);
14687
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)
14688
14799
  throw new Error("Native skill file changed while reading");
14689
14800
  return bytes.subarray(0, length);
@@ -14695,14 +14806,14 @@ function treeHash(root) {
14695
14806
  const hash2 = createHash12("sha256");
14696
14807
  let bytes = 0, files = 0;
14697
14808
  function visit2(path) {
14698
- const stat = lstatSync15(path);
14809
+ const stat = lstatSync16(path);
14699
14810
  if (stat.isSymbolicLink())
14700
14811
  throw new Error(`Refusing symlink in skill: ${path}`);
14701
14812
  const rel = relative5(root, path);
14702
14813
  if (stat.isDirectory()) {
14703
14814
  hash2.update(`d\x00${rel}\x00`);
14704
14815
  for (const name of readdirSync8(path).sort())
14705
- visit2(join17(path, name));
14816
+ visit2(join18(path, name));
14706
14817
  return;
14707
14818
  }
14708
14819
  if (!stat.isFile())
@@ -14724,10 +14835,10 @@ function selectNativeMigrationTargets(inventory, manifest) {
14724
14835
  const identities = new Set;
14725
14836
  const selected = [];
14726
14837
  for (const target of manifest.targets) {
14727
- if (!isAbsolute11(target.projectRoot) || resolve10(target.projectRoot) !== target.projectRoot)
14838
+ if (!isAbsolute11(target.projectRoot) || resolve11(target.projectRoot) !== target.projectRoot)
14728
14839
  throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
14729
- const projectRoot = canonicalSystemPath(target.projectRoot), normalized = relative5(projectRoot, resolve10(projectRoot, target.path));
14730
- 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())
14731
14842
  throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
14732
14843
  assertSafePath(projectRoot);
14733
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))
@@ -14736,8 +14847,8 @@ function selectNativeMigrationTargets(inventory, manifest) {
14736
14847
  if (identities.has(identity4))
14737
14848
  throw new Error(`Duplicate native migration target: ${target.path}`);
14738
14849
  identities.add(identity4);
14739
- const expected = canonicalSystemPath(resolve10(target.projectRoot, target.path));
14740
- 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);
14741
14852
  if (matches.length !== 1)
14742
14853
  throw new Error(`Native migration target was not found exactly once: ${target.agent} ${target.projectRoot}/${target.path}`);
14743
14854
  const entry = matches[0];
@@ -14766,9 +14877,9 @@ function projectAncestorDirectories(projects) {
14766
14877
  }
14767
14878
  function inventoryNativeSkills(home = homedir2(), options = {}) {
14768
14879
  home = canonicalSystemPath(home);
14769
- 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))
14770
14881
  throw new Error("A reviewed cache alias requires vendor inventory and an exact absolute path");
14771
- if (options.disabledVendorPaths?.some((path) => !isAbsolute11(path) || resolve10(path) !== path))
14882
+ if (options.disabledVendorPaths?.some((path) => !isAbsolute11(path) || resolve11(path) !== path))
14772
14883
  throw new Error("Disabled vendor paths must be exact absolute paths");
14773
14884
  const reviewedCacheAlias = options.reviewedCacheAlias === undefined ? undefined : canonicalSystemPath(options.reviewedCacheAlias);
14774
14885
  const disabledVendorPaths = new Set((options.disabledVendorPaths ?? []).map(canonicalSystemPath));
@@ -14776,14 +14887,18 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
14776
14887
  const selectedAgents = options.agents ? new Set(options.agents) : undefined;
14777
14888
  const includesAgent = (agent) => !selectedAgents || selectedAgents.has(agent);
14778
14889
  const rootDefinitions = selectedAgents ? ROOTS.filter(([agent, path]) => selectedAgents.has(agent) || selectedAgents.has("hermes") && agent === "codex" && path === ".agents/skills") : ROOTS;
14779
- const roots = rootDefinitions.map(([agent, path]) => [agent, canonicalAgentPath(join17(home, path), aliases)]);
14780
- 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));
14781
14892
  for (const project of projectAncestorDirectories([...options.projectDirs ?? [], ...options.projectDir ? [options.projectDir] : []])) {
14782
14893
  for (const [agent, path] of rootDefinitions)
14783
- 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)]);
14784
14899
  }
14785
14900
  const installationInputs = options.codexInstallationInputRoots ?? [];
14786
- 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)))))
14787
14902
  throw new Error("Native installation input overlaps an unconditional skill-loading root");
14788
14903
  const entries = [], seen = new Set;
14789
14904
  const cacheScans = new Map, maxAliasProofEntries = 1e4;
@@ -14797,13 +14912,13 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
14797
14912
  }
14798
14913
  let reviewedCacheAliasFound = false;
14799
14914
  function verifiedSiblingCacheAlias(agent, path, parent) {
14800
- const link = readlinkSync4(path), lexicalTarget = resolve10(parent, link);
14915
+ const link = readlinkSync4(path), lexicalTarget = resolve11(parent, link);
14801
14916
  if (isAbsolute11(link) ? link !== lexicalTarget : dirname11(link) !== ".")
14802
14917
  return false;
14803
14918
  const target = canonicalSystemPath(lexicalTarget);
14804
14919
  if (dirname11(target) !== parent)
14805
14920
  return false;
14806
- if (!lstatSync15(target, { throwIfNoEntry: false })?.isDirectory())
14921
+ if (!lstatSync16(target, { throwIfNoEntry: false })?.isDirectory())
14807
14922
  return false;
14808
14923
  const scan = cacheScans.get(target);
14809
14924
  if (!scan?.complete || scan.entries > maxAliasProofEntries)
@@ -14826,37 +14941,40 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
14826
14941
  assertSafePath(path);
14827
14942
  if (configuredRoot && agent === "codex" && installationInputs.some((root) => path === root || path.startsWith(`${root}${sep5}`)))
14828
14943
  return scan;
14829
- if (!existsSync8(path))
14944
+ if (!existsSync9(path))
14830
14945
  return scan;
14831
- const stat = lstatSync15(path);
14832
- 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");
14833
14950
  return scan;
14951
+ }
14834
14952
  if (!stat.isDirectory())
14835
14953
  throw new Error(`Unsupported native discovery entry: ${path}`);
14836
- if (existsSync8(join17(path, "SKILL.md"))) {
14954
+ if (existsSync9(join18(path, "SKILL.md"))) {
14837
14955
  scan.hasSkills = true;
14838
14956
  if (seen.has(path))
14839
14957
  return scan;
14840
14958
  seen.add(path);
14841
14959
  let managed = false;
14842
- const marker = join17(path, ".hasna-skills.json");
14843
- if (existsSync8(marker)) {
14960
+ const marker = join18(path, ".hasna-skills.json");
14961
+ if (existsSync9(marker)) {
14844
14962
  try {
14845
14963
  managed = JSON.parse(readFileSync12(marker, "utf8")).managedBy === "@hasna/skills";
14846
14964
  } catch {}
14847
14965
  }
14848
- const bridge = !vendor && isOwnedCliBridge(path, bridgePaths);
14966
+ const bridge = (!vendor || agent === "sumi") && isOwnedCliBridge(path, bridgePaths);
14849
14967
  const rootAlias = aliases.find((item) => path === item.target || path.startsWith(item.target + sep5));
14850
- 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 } : {} });
14851
14969
  return scan;
14852
14970
  }
14853
- if (depth > (vendor ? 32 : 3))
14971
+ if (depth > (agent === "sumi" ? 32 : vendor ? 32 : 3))
14854
14972
  return { ...scan, complete: false };
14855
14973
  const children = [], directory = opendirSync3(path);
14856
14974
  try {
14857
14975
  let child;
14858
14976
  while ((child = directory.readSync()) !== null) {
14859
- admit(join17(path, child.name));
14977
+ admit(join18(path, child.name));
14860
14978
  children.push(child);
14861
14979
  }
14862
14980
  } finally {
@@ -14868,13 +14986,13 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
14868
14986
  for (const { name } of children) {
14869
14987
  if (name === "node_modules")
14870
14988
  continue;
14871
- 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}`)))
14872
14990
  continue;
14873
14991
  const isVendor = vendor || name === ".system";
14874
14992
  if (isVendor && !options.includeVendor)
14875
14993
  continue;
14876
- const child = join17(path, name);
14877
- if (isVendor && lstatSync15(child, { throwIfNoEntry: false })?.isSymbolicLink()) {
14994
+ const child = join18(path, name);
14995
+ if (isVendor && lstatSync16(child, { throwIfNoEntry: false })?.isSymbolicLink()) {
14878
14996
  const target = statSync8(child, { throwIfNoEntry: false });
14879
14997
  if (target?.isFile() || pluginCache && target?.isDirectory() && verifiedSiblingCacheAlias(agent, child, path)) {
14880
14998
  scan.entries++;
@@ -14900,11 +15018,11 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
14900
15018
  scanRoot(agent, path);
14901
15019
  if (options.includeVendor) {
14902
15020
  for (const agent of ["codex", "claude"].filter(includesAgent))
14903
- scanRoot(agent, canonicalAgentPath(join17(home, `.${agent}`, "plugins", "cache"), aliases), true, true);
15021
+ scanRoot(agent, canonicalAgentPath(join18(home, `.${agent}`, "plugins", "cache"), aliases), true, true);
14904
15022
  if (includesAgent("claude"))
14905
- scanRoot("claude", canonicalAgentPath(join17(home, ".claude", "plugins", "synced"), aliases), true);
15023
+ scanRoot("claude", canonicalAgentPath(join18(home, ".claude", "plugins", "synced"), aliases), true);
14906
15024
  if (includesAgent("gemini"))
14907
- scanRoot("gemini", join17(home, ".gemini", "extensions"), true);
15025
+ scanRoot("gemini", join18(home, ".gemini", "extensions"), true);
14908
15026
  }
14909
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 }))) : [];
14910
15028
  for (const root of [...options.agentRoots ?? [], ...configured])
@@ -14917,7 +15035,7 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
14917
15035
  }
14918
15036
  function readOptional(path) {
14919
15037
  assertSafePath(path);
14920
- return existsSync8(path) ? readFileSync12(path, "utf8") : null;
15038
+ return existsSync9(path) ? readFileSync12(path, "utf8") : null;
14921
15039
  }
14922
15040
  function jsonObject(text2, path) {
14923
15041
  if (text2 === null)
@@ -15017,19 +15135,19 @@ function configureHooks(config, agent, command, profileId, nativeReady) {
15017
15135
  function disableCodexSkills(text2, skills, aliases, bridgePath) {
15018
15136
  const bundled = disableCodexBundledSkills(text2);
15019
15137
  let result = normalizeCodexInlinePathConfig(bundled);
15020
- 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));
15021
15139
  const previousConfig = Bun.TOML.parse(bundled).skills?.config;
15022
15140
  if (previousConfig !== undefined && (!Array.isArray(previousConfig) || previousConfig.some((entry) => !entry || typeof entry !== "object" || entry.path !== undefined && entry.name !== undefined)))
15023
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");
15024
15142
  const bridgeNeedsRepair = previousConfig?.some((entry) => entry.enabled === false && bridgeSelector(entry));
15025
15143
  const selections = [...skills.filter((entry) => entry.agent === "codex" && !entry.bridge).map((skill) => ({ path: skill.path, enabled: false })), { path: bridgePath, enabled: true }];
15026
15144
  for (const skill of selections) {
15027
- const path = join17(skill.path, "SKILL.md");
15145
+ const path = join18(skill.path, "SKILL.md");
15028
15146
  let found = false;
15029
15147
  result = result.replace(CODEX_SKILL_CONFIG_SECTIONS, (section) => {
15030
15148
  const parsed = Bun.TOML.parse(section);
15031
15149
  const declared = parsed.skills?.config?.[0];
15032
- 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))))
15033
15151
  return section;
15034
15152
  found = true;
15035
15153
  return /^\s*enabled\s*=/m.test(section) ? section.replace(/^\s*enabled\s*=.*$/m, `enabled = ${skill.enabled}`) : `${section.trimEnd()}
@@ -15050,14 +15168,14 @@ enabled = false
15050
15168
  return result;
15051
15169
  }
15052
15170
  function assertCodexInstallationInputs(home, projectDirs, inputs) {
15053
- 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)));
15054
15172
  if (inputs.some((input) => nativeRoots.some((root) => root === input || root.startsWith(input + sep5) || input.startsWith(root + sep5))))
15055
15173
  throw new Error("Native installation input overlaps an unconditional skill-loading root");
15056
15174
  }
15057
15175
  function planAgentIntegration(options) {
15058
15176
  const home = options.home ?? homedir2(), dataDir2 = options.dataDir ?? getDataDirReadOnly();
15059
15177
  const aliases = rootAliases(home, options.allowRootAliases);
15060
- const policyPath = join17(dataDir2, "agent-policy.json");
15178
+ const policyPath = join18(dataDir2, "agent-policy.json");
15061
15179
  assertSafePath(policyPath);
15062
15180
  const priorSnapshot = readManagedSkillPolicySnapshot(dataDir2), previousPolicy = priorSnapshot?.text ?? null, policy = priorSnapshot?.value ?? {};
15063
15181
  if (policy.bridge !== undefined && (!policy.bridge || typeof policy.bridge !== "object" || Array.isArray(policy.bridge)))
@@ -15089,10 +15207,10 @@ function planAgentIntegration(options) {
15089
15207
  }
15090
15208
  return resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, retainedReview, canonical: (path) => canonicalAgentPath(path, aliases) });
15091
15209
  });
15092
- 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)) ?? "") : {};
15093
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) });
15094
15212
  const observedNativeSources = [];
15095
- 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) => {
15096
15214
  const bytes = readNativeBytes(path, 1024 * 1024);
15097
15215
  observedNativeSources.push({ path, sha256: sha3(bytes) });
15098
15216
  return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
@@ -15100,7 +15218,7 @@ function planAgentIntegration(options) {
15100
15218
  const codexPluginSkills = codexPluginControls.skills;
15101
15219
  const codexInactivePlugins = codexPluginControls.inactivePlugins;
15102
15220
  const codexDiscovery = discoveries.find((binding) => binding.agent === "codex");
15103
- 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));
15104
15222
  assertCodexInstallationInputs(home, [options.projectDir ?? home], codexPluginSourceInputs.map((input) => input.sourceRoot));
15105
15223
  const changes = [];
15106
15224
  for (const agent of [...new Set(options.agents)]) {
@@ -15108,19 +15226,19 @@ function planAgentIntegration(options) {
15108
15226
  throw new Error(`Unsupported agent: ${agent}`);
15109
15227
  const { command, profileId: profileId2 } = bindings.get(agent);
15110
15228
  const adapter = AGENT_ADAPTERS[agent];
15111
- 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);
15112
15230
  assertSafePath(bridgePath);
15113
- if (existsSync8(bridgePath) && !isOwnedCliBridge(bridgePath, [bridgePath]))
15231
+ if (existsSync9(bridgePath) && !isOwnedCliBridge(bridgePath, [bridgePath]))
15114
15232
  throw new Error(`Refusing to overwrite an unrecognized or modified Skills bridge: ${bridgePath}`);
15115
15233
  for (const [name, after2] of Object.entries(CLI_BRIDGE_FILES)) {
15116
- const path2 = join17(bridgePath, name), before2 = readOptional(path2);
15234
+ const path2 = join18(bridgePath, name), before2 = readOptional(path2);
15117
15235
  if (before2 !== after2)
15118
15236
  changes.push({ path: path2, before: before2, after: after2 });
15119
15237
  }
15120
- const path = canonicalAgentPath(join17(home, adapter.config), aliases);
15238
+ const path = canonicalAgentPath(agent === "sumi" ? sumiConfigPath(home) : join18(home, adapter.config), aliases);
15121
15239
  const before = readOptional(path);
15122
15240
  if (agent === "hermes") {
15123
- 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);
15124
15242
  const priorCommand = policy.bridge?.commands?.hermes, priorProfile = policy.bridge?.profiles?.hermes ?? policy.profileId;
15125
15243
  const previous = policy.bridge?.supervisors?.hermes;
15126
15244
  if (supervisorBefore !== null && supervisorBefore !== supervisorAfter && !(previous?.path === supervisorPath && typeof priorCommand === "string" && typeof priorProfile === "string" && supervisorBefore === renderHermesSupervisor(priorCommand, priorProfile)))
@@ -15131,19 +15249,30 @@ function planAgentIntegration(options) {
15131
15249
  const after2 = configureHermesHooks(before, supervisor, previous);
15132
15250
  if (before !== after2)
15133
15251
  changes.push({ path, before, after: after2 });
15134
- const optOut = join17(home, ".hermes", HERMES_OPT_OUT), optOutBefore = readOptional(optOut);
15252
+ const optOut = join18(home, ".hermes", HERMES_OPT_OUT), optOutBefore = readOptional(optOut);
15135
15253
  if (optOutBefore === null)
15136
15254
  changes.push({ path: optOut, before: null, after: `Managed by @hasna/skills: bundled native skill reseeding is disabled.
15137
15255
  ` });
15138
15256
  continue;
15139
15257
  }
15140
15258
  const config = jsonObject(before, path);
15141
- 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") {
15142
15271
  const permission = config.permission ?? {};
15143
15272
  if (!permission || typeof permission !== "object" || Array.isArray(permission))
15144
15273
  throw new Error("Expected OpenCode permission object");
15145
15274
  config.permission = { ...permission, skill: { "*": "deny", [CLI_BRIDGE_NAME]: "allow" } };
15146
- 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);
15147
15276
  const pluginAfter = renderOpenCodePlugin(command, profileId2);
15148
15277
  const priorCommand = policy.bridge?.commands?.opencode;
15149
15278
  const ownedPrevious = typeof priorCommand === "string" && typeof policy.profileId === "string" && pluginBefore === renderOpenCodePlugin(priorCommand, policy.bridge?.profiles?.opencode ?? policy.profileId);
@@ -15160,13 +15289,13 @@ function planAgentIntegration(options) {
15160
15289
  if (before !== after)
15161
15290
  changes.push({ path, before, after });
15162
15291
  if (agent === "codex") {
15163
- 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);
15164
15293
  const next = disableReviewedCodexPluginNames(disableCodexSkills(previous ?? "", nativeSkills, aliases, bridgePath), codexPluginSkills);
15165
15294
  if (next !== (previous ?? ""))
15166
15295
  changes.push({ path: configPath, before: previous, after: next });
15167
15296
  }
15168
15297
  }
15169
- 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;
15170
15299
  const discoveryAfter = discoveries.map((binding) => {
15171
15300
  const rebound = rebindAgentDiscovery(binding, new Map(changes.map((change) => [change.path, change.after])));
15172
15301
  if (binding.agent !== "codex")
@@ -15181,12 +15310,12 @@ function planAgentIntegration(options) {
15181
15310
  version: CLI_BRIDGE_VERSION,
15182
15311
  digest: CLI_BRIDGE_DIGEST,
15183
15312
  agents: [...new Set([...priorAgents, ...options.agents])].sort(),
15184
- home: resolve10(home),
15313
+ home: resolve11(home),
15185
15314
  includeVendor: true,
15186
- ...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)) } } } : {},
15187
15316
  commands: { ...policy.bridge?.commands, ...Object.fromEntries([...bindings].map(([agent, binding]) => [agent, binding.command])) },
15188
15317
  profiles: Object.fromEntries([...new Set([...priorAgents, ...options.agents])].sort().map((agent) => [agent, bindings.get(agent)?.profileId ?? policy.bridge?.profiles?.[agent] ?? policy.profileId])),
15189
- 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 ?? [],
15190
15319
  rootAliases: aliases,
15191
15320
  ...options.codexNativeCatalog || codexPluginSkills.length || codexInactivePlugins.length || codexPluginSourceInputs.length ? { codexPluginSkills, codexInactivePlugins, codexPluginSkillReview } : {},
15192
15321
  discovery: { ...policy.bridge?.discovery, ...Object.fromEntries(discoveryAfter.map((binding) => [binding.agent, binding])) }
@@ -15209,7 +15338,7 @@ function claudeSettingsConsumers(home, path, discoveries, aliases) {
15209
15338
  const distance = relative5(canonical5(root), path);
15210
15339
  return distance === "" || distance !== ".." && !distance.startsWith(`..${sep5}`) && !isAbsolute11(distance);
15211
15340
  };
15212
- 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);
15213
15342
  }
15214
15343
  function verifyCoordinatedDiscovery(binding, home, aliases) {
15215
15344
  try {
@@ -15226,8 +15355,8 @@ function verifyCoordinatedDiscovery(binding, home, aliases) {
15226
15355
  }
15227
15356
  }
15228
15357
  function planClaudeHookEventsUpdate(options) {
15229
- const home = resolve10(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
15230
- 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");
15231
15360
  assertSafePath(policyPath);
15232
15361
  const snapshot = readManagedSkillPolicySnapshot(dataDir2);
15233
15362
  if (!snapshot)
@@ -15238,9 +15367,9 @@ function planClaudeHookEventsUpdate(options) {
15238
15367
  }
15239
15368
  const aliases = bridge.rootAliases ?? [];
15240
15369
  recheckRootAliases(aliases);
15241
- const path = canonicalAgentPath(join17(home, ".claude", "settings.json"), aliases);
15370
+ const path = canonicalAgentPath(join18(home, ".claude", "settings.json"), aliases);
15242
15371
  assertSafePath(path);
15243
- 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;
15244
15373
  if ((before === null ? "absent" : sha3(before)) !== options.expectedSettingsSha256)
15245
15374
  throw new Error("Claude settings preimage changed");
15246
15375
  assertClaudeHookEventsReplacement(before, options.replacement, options.events);
@@ -15300,14 +15429,14 @@ function atomicWrite2(path, content) {
15300
15429
  writeFileSync4(temporary, content, { mode: 384, flag: "wx" });
15301
15430
  renameSync2(temporary, path);
15302
15431
  } finally {
15303
- if (existsSync8(temporary))
15432
+ if (existsSync9(temporary))
15304
15433
  unlinkSync3(temporary);
15305
15434
  }
15306
15435
  }
15307
15436
  function planAgentSettingsWitnessUpgrade(options) {
15308
15437
  if (options.targetCodexVersion !== undefined && (options.agent !== "codex" || ![2, 3].includes(options.targetCodexVersion)))
15309
15438
  throw new Error("Invalid Codex witness target version");
15310
- const home = resolve10(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
15439
+ const home = resolve11(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
15311
15440
  const snapshot = readManagedSkillPolicySnapshot(dataDir2);
15312
15441
  if (!snapshot || sha3(snapshot.text) !== options.expectedPolicySha256)
15313
15442
  throw new Error("Managed policy preimage changed");
@@ -15316,7 +15445,7 @@ function planAgentSettingsWitnessUpgrade(options) {
15316
15445
  const binding = policy.bridge?.discovery?.[options.agent];
15317
15446
  if (!binding || binding.agent !== options.agent || binding.method !== "reviewed")
15318
15447
  throw new Error("Settings witness upgrade requires an existing reviewed native binding");
15319
- 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);
15320
15449
  const settings = readOptional(configPath);
15321
15450
  if (settings === null || sha3(settings) !== options.expectedSettingsSha256)
15322
15451
  throw new Error("Native settings preimage changed");
@@ -15355,7 +15484,7 @@ function planAgentSettingsWitnessUpgrade(options) {
15355
15484
  const replacement = { ...binding, sources: rebound };
15356
15485
  verifyAgentDiscovery(replacement);
15357
15486
  const after = serializeManagedSkillPolicy({ ...policy, bridge: { ...policy.bridge, discovery: { ...policy.bridge.discovery, [options.agent]: replacement } } });
15358
- 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 })) } };
15359
15488
  }
15360
15489
  function applyAgentIntegration(plan) {
15361
15490
  for (const source of plan.observedNativeSources ?? [])
@@ -15363,12 +15492,12 @@ function applyAgentIntegration(plan) {
15363
15492
  throw new Error("Native identity source changed after planning");
15364
15493
  if (plan.observedSettings && readOptional(plan.observedSettings.path) !== plan.observedSettings.before)
15365
15494
  throw new Error("Native settings changed after witness planning");
15366
- const policyPath = join17(plan.dataDir, "agent-policy.json");
15495
+ const policyPath = join18(plan.dataDir, "agent-policy.json");
15367
15496
  assertSafePath(policyPath);
15368
- 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);
15369
15498
  readManagedSkillPolicySnapshot(plan.dataDir);
15370
15499
  for (const change of plan.changes)
15371
- if (resolve10(change.path) === resolve10(policyPath))
15500
+ if (resolve11(change.path) === resolve11(policyPath))
15372
15501
  parseManagedSkillPolicy(change.after);
15373
15502
  const aliases = plan.rootAliases ?? [];
15374
15503
  recheckRootAliases(aliases);
@@ -15397,7 +15526,7 @@ function applyAgentIntegration(plan) {
15397
15526
  for (const change of plan.changes)
15398
15527
  if (currentText(change.path) !== change.before)
15399
15528
  throw new Error(`Configuration changed after planning: ${change.path}`);
15400
- 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;
15401
15530
  if (!plan.changes.length) {
15402
15531
  recheckRootAliases(aliases);
15403
15532
  return { changed: [], backups, ...aliases.length ? { rootAliases: aliases } : {} };
@@ -15407,7 +15536,7 @@ function applyAgentIntegration(plan) {
15407
15536
  chmodSync(backupRoot, 448);
15408
15537
  for (const [index, change] of plan.changes.entries())
15409
15538
  if (change.before !== null) {
15410
- 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`);
15411
15540
  writeFileSync4(backup, change.before, { mode: 384, flag: "wx" });
15412
15541
  if (readFileSync12(backup, "utf8") !== change.before)
15413
15542
  throw new Error("Native configuration preservation readback failed");
@@ -15418,7 +15547,7 @@ function applyAgentIntegration(plan) {
15418
15547
  recheckRootAliases(aliases);
15419
15548
  if (currentText(change.path) !== change.before)
15420
15549
  throw new Error(`Configuration changed after planning: ${change.path}`);
15421
- for (let directory = dirname11(change.path);!existsSync8(directory); directory = dirname11(directory))
15550
+ for (let directory = dirname11(change.path);!existsSync9(directory); directory = dirname11(directory))
15422
15551
  createdDirectories.add(directory);
15423
15552
  const after = change.after;
15424
15553
  atomicWrite2(change.path, after);
@@ -15438,7 +15567,7 @@ function applyAgentIntegration(plan) {
15438
15567
  }
15439
15568
  for (const agent of plan.managedAgentChecks?.agents ?? [])
15440
15569
  assertManagedAgentBridge(agent, { home: plan.managedAgentChecks.home, dataDir: plan.dataDir, projectDir: plan.managedAgentChecks.home });
15441
- 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 } : {} }) + `
15442
15571
  `);
15443
15572
  } catch (error) {
15444
15573
  for (const change of written.reverse()) {
@@ -15492,7 +15621,7 @@ function writeArchiveJournal(path, value) {
15492
15621
  } finally {
15493
15622
  if (descriptor !== undefined)
15494
15623
  closeSync8(descriptor);
15495
- if (lstatSync15(temporary, { throwIfNoEntry: false }))
15624
+ if (lstatSync16(temporary, { throwIfNoEntry: false }))
15496
15625
  unlinkSync3(temporary);
15497
15626
  }
15498
15627
  }
@@ -15503,7 +15632,7 @@ function archiveNativeSkills(inventory, options) {
15503
15632
  recheckRootAliases(aliases);
15504
15633
  for (const entry of inventory.filter((entry2) => entry2.bridge)) {
15505
15634
  const adapter = AGENT_ADAPTERS[entry.agent];
15506
- 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;
15507
15636
  if (!expected || treeHash(entry.path) !== entry.hash || !isOwnedCliBridge(entry.path, [expected]))
15508
15637
  throw new Error(`Skills bridge changed after planning: ${entry.path}`);
15509
15638
  }
@@ -15514,11 +15643,11 @@ function archiveNativeSkills(inventory, options) {
15514
15643
  const targetManifest = options.targetManifest ? { schema: options.targetManifest.schema, digest: options.targetManifest.digest, targetCount: options.targetManifest.targets.length } : undefined;
15515
15644
  if (!selected.length)
15516
15645
  return { entries: [], ...targetManifest ? { targetManifest } : {}, ...aliases.length ? { rootAliases: aliases } : {} };
15517
- 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");
15518
15647
  const entries = selected.map((entry, index) => ({
15519
- source: entry.vendor ? join17(entry.path, "SKILL.md") : entry.path,
15520
- archive: join17(archiveRoot, `${index}-${sha3(entry.path).slice(0, 12)}`),
15521
- 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,
15522
15651
  ...entry.vendor ? { discoveryOnly: true } : {},
15523
15652
  status: "planned"
15524
15653
  }));
@@ -15526,7 +15655,7 @@ function archiveNativeSkills(inventory, options) {
15526
15655
  const moved = [];
15527
15656
  const verifyArchive = (entry) => {
15528
15657
  assertSafePath(entry.archive);
15529
- const stat = lstatSync15(entry.archive);
15658
+ const stat = lstatSync16(entry.archive);
15530
15659
  if (entry.discoveryOnly ? !stat.isFile() : !stat.isDirectory())
15531
15660
  throw new Error("Archive type changed");
15532
15661
  if ((entry.discoveryOnly ? sha3(readNativeBytes(entry.archive)) : treeHash(entry.archive)) !== entry.hash)
@@ -15534,7 +15663,7 @@ function archiveNativeSkills(inventory, options) {
15534
15663
  };
15535
15664
  assertSafePath(archiveRoot);
15536
15665
  const created = [];
15537
- for (let path = archiveRoot;!existsSync8(path); path = dirname11(path))
15666
+ for (let path = archiveRoot;!existsSync9(path); path = dirname11(path))
15538
15667
  created.push(path);
15539
15668
  mkdirSync5(archiveRoot, { recursive: true, mode: 448 });
15540
15669
  try {
@@ -15574,7 +15703,7 @@ function archiveNativeSkills(inventory, options) {
15574
15703
  recheckRootAliases(aliases);
15575
15704
  assertSafePath(entry.source);
15576
15705
  assertSafePath(entry.archive);
15577
- if (lstatSync15(entry.source, { throwIfNoEntry: false })) {
15706
+ if (lstatSync16(entry.source, { throwIfNoEntry: false })) {
15578
15707
  entry.status = "recovery-required";
15579
15708
  entry.conflict = "source-occupied";
15580
15709
  continue;
@@ -15591,7 +15720,7 @@ function archiveNativeSkills(inventory, options) {
15591
15720
  syncArchiveDirectory(dirname11(entry.source));
15592
15721
  unlinkSync3(entry.archive);
15593
15722
  } else {
15594
- if (lstatSync15(entry.source, { throwIfNoEntry: false })) {
15723
+ if (lstatSync16(entry.source, { throwIfNoEntry: false })) {
15595
15724
  entry.status = "recovery-required";
15596
15725
  entry.conflict = "source-occupied";
15597
15726
  continue;
@@ -15622,8 +15751,8 @@ function assertManagedAgentBridge(agent, options = {}) {
15622
15751
  assertManagedAgentBridgeWithDiscovery(agent, options);
15623
15752
  }
15624
15753
  function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscovery) {
15625
- const home = resolve10(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
15626
- assertSafePath(join17(dataDir2, "agent-policy.json"));
15754
+ const home = resolve11(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
15755
+ assertSafePath(join18(dataDir2, "agent-policy.json"));
15627
15756
  const snapshot = readManagedSkillPolicySnapshot(dataDir2);
15628
15757
  if (!snapshot)
15629
15758
  throw new Error("NATIVE_SKILL_DRIFT: install the Skills bridge with skills hook install");
@@ -15634,14 +15763,14 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
15634
15763
  if (!Array.isArray(aliases))
15635
15764
  throw new Error("NATIVE_SKILL_DRIFT: invalid root alias binding");
15636
15765
  recheckRootAliases(aliases);
15637
- 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);
15638
15767
  if (!isOwnedCliBridge(expected, [expected]))
15639
15768
  throw new Error("NATIVE_SKILL_DRIFT: the native Skills bridge is missing or modified; repair it before continuing");
15640
15769
  const roots = projectAncestorDirectories([options.projectDir ?? process.cwd(), ...options.projectDirs ?? [], ...agent === "hermes" && process.env.TERMINAL_CWD ? [process.cwd()] : []]);
15641
- 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";
15642
15771
  const discovery = provenDiscovery ?? binding.discovery?.[agent];
15643
15772
  assertProjectDiscovery(agent, [...roots], home, (path) => canonicalAgentPath(path, aliases), discovery);
15644
- 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);
15645
15774
  const command = binding.commands?.[agent], profile = binding.profiles?.[agent];
15646
15775
  if (typeof command !== "string" || typeof profile !== "string")
15647
15776
  throw new Error("NATIVE_SKILL_DRIFT: the native hook command/profile binding is missing");
@@ -15649,11 +15778,14 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
15649
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");
15650
15779
  if (agent === "hermes") {
15651
15780
  const supervisor = binding.supervisors?.hermes;
15652
- 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)))
15653
15782
  throw new Error("NATIVE_SKILL_DRIFT: Hermes supervisor binding changed; run skills hook install");
15654
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");
15655
15787
  } else if (agent === "opencode") {
15656
- 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))
15657
15789
  throw new Error("NATIVE_SKILL_DRIFT: OpenCode bridge protection changed; run skills hook install");
15658
15790
  } else {
15659
15791
  const required = {};
@@ -15670,21 +15802,21 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
15670
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)))
15671
15803
  throw new Error("NATIVE_SKILL_DRIFT: Gemini bridge or bundled-skill protection changed; run skills hook install");
15672
15804
  }
15673
- const codexPath = canonicalAgentPath(join17(home, ".codex", "config.toml"), aliases);
15805
+ const codexPath = canonicalAgentPath(join18(home, ".codex", "config.toml"), aliases);
15674
15806
  if (options.codexDiscoveryRecovery && (agent !== "codex" || options.codexDiscoveryRecovery.configPath !== codexPath))
15675
15807
  throw new Error("NATIVE_SKILL_DRIFT: Codex trust recovery names a different configuration root");
15676
15808
  const codexConfig = agent === "codex" ? Bun.TOML.parse(readOptional(codexPath) ?? "") : {};
15677
15809
  if (agent === "codex" && codexConfig.skills?.bundled?.enabled !== false)
15678
15810
  throw new Error("NATIVE_SKILL_DRIFT: Codex bundled skill reseeding is not disabled (skills.bundled.enabled); run skills hook install");
15679
- 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)));
15680
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))
15681
15813
  throw new Error("NATIVE_SKILL_DRIFT: the Codex CLI bridge is disabled; run skills hook install");
15682
- 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))))
15683
15815
  throw new Error("NATIVE_SKILL_DRIFT: reviewed native plugin capability controls changed; run skills hook install with a fresh discovery review");
15684
15816
  const disabledPlugin = (entry) => {
15685
15817
  if (agent !== "codex" || !entry.vendor)
15686
15818
  return false;
15687
- 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));
15688
15820
  return isReviewedCodexPluginInactive(document2, cache, binding.codexInactivePlugins ?? [], codexConfig.plugins, codexConfig.skills?.config ?? [], read2) || isReviewedCodexPluginSkillDisabled(document2, cache, binding.codexPluginSkills ?? [], codexConfig.skills?.config, read2);
15689
15821
  };
15690
15822
  const disabledPaths = new Set(disabledCodexSkillPaths(codexConfig, aliases));
@@ -15697,15 +15829,15 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
15697
15829
  return true;
15698
15830
  if (!disabledPaths.has(entry.path))
15699
15831
  return false;
15700
- 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))
15701
15833
  return true;
15702
- const runtimeCache = canonicalAgentPath(join17(home, ".cache", "codex-runtimes"), aliases);
15834
+ const runtimeCache = canonicalAgentPath(join18(home, ".cache", "codex-runtimes"), aliases);
15703
15835
  if (entry.path.startsWith(runtimeCache + sep5) && discovery?.method === "automatic" && discovery.roots.some((root) => entry.path.startsWith(root + sep5)))
15704
15836
  return true;
15705
- const bundledMarketplaces = canonicalAgentPath(join17(home, ".codex", ".tmp", "bundled-marketplaces"), aliases);
15837
+ const bundledMarketplaces = canonicalAgentPath(join18(home, ".codex", ".tmp", "bundled-marketplaces"), aliases);
15706
15838
  if (entry.path.startsWith(bundledMarketplaces + sep5) && discovery?.method === "automatic" && discovery.roots.some((root) => entry.path.startsWith(root + sep5)))
15707
15839
  return true;
15708
- 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))
15709
15841
  return false;
15710
15842
  return Array.isArray(binding.disabledBuiltins) && binding.disabledBuiltins.some((item) => item.path === entry.path && item.hash === entry.hash);
15711
15843
  };
@@ -15715,6 +15847,8 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
15715
15847
  throw new Error("NATIVE_SKILL_DRIFT: an installed Gemini builtin is not disabled");
15716
15848
  try {
15717
15849
  verifyAgentDiscovery(discovery, options.codexDiscoveryRecovery);
15850
+ if (agent === "sumi" && discovery.method === "reviewed")
15851
+ resolveAgentDiscovery({ home, agent, retainedReview: discovery, canonical: (path) => canonicalAgentPath(path, aliases) });
15718
15852
  if (discovery.method === "automatic") {
15719
15853
  const current = resolveAgentDiscovery({ home, agent, canonical: (path) => canonicalAgentPath(path, aliases) });
15720
15854
  if (JSON.stringify(current) !== JSON.stringify(discovery))
@@ -15726,7 +15860,7 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
15726
15860
  let installationInputRoots = [];
15727
15861
  if (agent === "codex") {
15728
15862
  try {
15729
- 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)));
15730
15864
  } catch {
15731
15865
  throw new Error("NATIVE_SKILL_DRIFT: native installation input identity changed; review discovery and catalog");
15732
15866
  }
@@ -15761,9 +15895,9 @@ function isSkillsOwnershipMarker(marker) {
15761
15895
  return typeof marker === "object" && marker !== null && Object.hasOwn(marker, "managedBy") && marker.managedBy === SYNC_MARKER_MANAGED_BY;
15762
15896
  }
15763
15897
  function hasSkillsOwnershipMarker(dir) {
15764
- const path = join18(dir, SYNC_MARKER_FILE);
15898
+ const path = join19(dir, SYNC_MARKER_FILE);
15765
15899
  try {
15766
- if (!lstatSync16(path).isFile())
15900
+ if (!lstatSync17(path).isFile())
15767
15901
  return false;
15768
15902
  const marker = JSON.parse(readFileSync13(path, "utf8"));
15769
15903
  return isSkillsOwnershipMarker(marker);
@@ -15785,9 +15919,9 @@ function resolveSyncAgents(arg) {
15785
15919
  function agentGlobalSkillsDir(agent, homeDir2 = homedir3()) {
15786
15920
  switch (agent) {
15787
15921
  case "opencode":
15788
- return join18(homeDir2, ".config", "opencode", "skills");
15922
+ return join19(homeDir2, ".config", "opencode", "skills");
15789
15923
  default:
15790
- return join18(homeDir2, `.${agent}`, "skills");
15924
+ return join19(homeDir2, `.${agent}`, "skills");
15791
15925
  }
15792
15926
  }
15793
15927
  function adaptSkillMdForAgent(skillMd, agent) {
@@ -15846,8 +15980,8 @@ function resolveSyncCorpus(options = {}) {
15846
15980
  function packageSourceRoots(source) {
15847
15981
  const roots = [];
15848
15982
  for (const sub of ["skills"]) {
15849
- const candidate = join18(source, sub);
15850
- if (existsSync9(candidate) && isDirectory(candidate))
15983
+ const candidate = join19(source, sub);
15984
+ if (existsSync10(candidate) && isDirectory(candidate))
15851
15985
  roots.push(candidate);
15852
15986
  }
15853
15987
  if (roots.length > 0)
@@ -15862,10 +15996,10 @@ function containsSkillDirectories(path) {
15862
15996
  return false;
15863
15997
  }
15864
15998
  return entries.some((entry) => {
15865
- const candidate = join18(path, entry);
15999
+ const candidate = join19(path, entry);
15866
16000
  if (!isDirectory(candidate))
15867
16001
  return false;
15868
- 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"));
15869
16003
  });
15870
16004
  }
15871
16005
  function isDirectory(path) {
@@ -15905,7 +16039,7 @@ function syncSkillsToAgents(options = {}) {
15905
16039
  actions.push({
15906
16040
  skill: name,
15907
16041
  agent,
15908
- path: join18(agentGlobalSkillsDir(agent, homeDir2), name, "SKILL.md"),
16042
+ path: join19(agentGlobalSkillsDir(agent, homeDir2), name, "SKILL.md"),
15909
16043
  action: "skip",
15910
16044
  reason: "not found in this machine's corpus"
15911
16045
  });
@@ -15935,7 +16069,7 @@ function syncSkillsToAgents(options = {}) {
15935
16069
  }
15936
16070
  function writeManagedAgentSkill(params) {
15937
16071
  const homeDir2 = params.homeDir ?? homedir3();
15938
- const dir = join18(agentGlobalSkillsDir(params.agent, homeDir2), params.skill);
16072
+ const dir = join19(agentGlobalSkillsDir(params.agent, homeDir2), params.skill);
15939
16073
  const result = writeManagedSkillDir(dir, params.skillMd, {
15940
16074
  skill: params.skill,
15941
16075
  source: params.source,
@@ -15952,11 +16086,11 @@ function writeManagedAgentSkill(params) {
15952
16086
  };
15953
16087
  }
15954
16088
  function writeManagedSkillDir(dir, skillMd, options) {
15955
- const skillMdPath = join18(dir, "SKILL.md");
15956
- const markerPath = join18(dir, SYNC_MARKER_FILE);
15957
- const dirExists = existsSync9(dir);
16089
+ const skillMdPath = join19(dir, "SKILL.md");
16090
+ const markerPath = join19(dir, SYNC_MARKER_FILE);
16091
+ const dirExists = existsSync10(dir);
15958
16092
  const managed = hasSkillsOwnershipMarker(dir);
15959
- const hasSkillMd = existsSync9(skillMdPath);
16093
+ const hasSkillMd = existsSync10(skillMdPath);
15960
16094
  if (dirExists && !managed && !hasSkillMd) {
15961
16095
  return {
15962
16096
  action: "skip",
@@ -15968,7 +16102,7 @@ function writeManagedSkillDir(dir, skillMd, options) {
15968
16102
  return {
15969
16103
  action: "skip",
15970
16104
  path: skillMdPath,
15971
- 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"
15972
16106
  };
15973
16107
  }
15974
16108
  if (dirExists && managed && hasSkillMd && !options.force && isPointerSkillMd(skillMd)) {
@@ -15991,11 +16125,11 @@ function writeManagedSkillDir(dir, skillMd, options) {
15991
16125
  return { action, path: skillMdPath };
15992
16126
  const parentDir = dirname12(dir);
15993
16127
  mkdirSync6(parentDir, { recursive: true });
15994
- const transactionDir = mkdtempSync(join18(parentDir, `.hasna-skills-write-${basename6(dir)}-`));
15995
- const candidateDir = join18(transactionDir, "candidate");
15996
- const backupDir = join18(transactionDir, "backup");
15997
- const candidateSkillMdPath = join18(candidateDir, "SKILL.md");
15998
- 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);
15999
16133
  const marker = {
16000
16134
  managedBy: SYNC_MARKER_MANAGED_BY,
16001
16135
  skill: options.skill,
@@ -16022,9 +16156,9 @@ function writeManagedSkillDir(dir, skillMd, options) {
16022
16156
  }
16023
16157
  renameDirectory(candidateDir, dir);
16024
16158
  } catch (error) {
16025
- if (originalMoved && existsSync9(backupDir)) {
16159
+ if (originalMoved && existsSync10(backupDir)) {
16026
16160
  try {
16027
- if (existsSync9(dir))
16161
+ if (existsSync10(dir))
16028
16162
  rmSync(dir, { recursive: true, force: true });
16029
16163
  renameDirectory(backupDir, dir);
16030
16164
  originalMoved = false;
@@ -16044,7 +16178,7 @@ function writeManagedSkillDir(dir, skillMd, options) {
16044
16178
  return { action, path: skillMdPath };
16045
16179
  }
16046
16180
  function removeManagedAgentSkill(skill, agent, homeDir2 = homedir3()) {
16047
- const dir = join18(agentGlobalSkillsDir(agent, homeDir2), skill);
16181
+ const dir = join19(agentGlobalSkillsDir(agent, homeDir2), skill);
16048
16182
  if (!hasSkillsOwnershipMarker(dir))
16049
16183
  return false;
16050
16184
  rmSync(dir, { recursive: true, force: true });
@@ -16052,8 +16186,8 @@ function removeManagedAgentSkill(skill, agent, homeDir2 = homedir3()) {
16052
16186
  }
16053
16187
  function sourceSkillMd(skillPath, name, description, kind, preferBundledDocs = false) {
16054
16188
  if (kind === undefined || kind === "instruction" || preferBundledDocs) {
16055
- const skillMdPath = join18(skillPath, "SKILL.md");
16056
- if (existsSync9(skillMdPath))
16189
+ const skillMdPath = join19(skillPath, "SKILL.md");
16190
+ if (existsSync10(skillMdPath))
16057
16191
  return readFileSync13(skillMdPath, "utf-8");
16058
16192
  }
16059
16193
  return pointerSkillMd(name, description);
@@ -16094,8 +16228,8 @@ function resolveSkillAlias(name) {
16094
16228
  }
16095
16229
 
16096
16230
  // src/lib/project-state.ts
16097
- import { existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync14, writeFileSync as writeFileSync6 } from "fs";
16098
- 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";
16099
16233
  var VALID_PIN_SOURCES = [
16100
16234
  "official",
16101
16235
  "custom",
@@ -16110,14 +16244,14 @@ var SKILLS_PROJECT_DIR = ".skills";
16110
16244
  var PROJECT_CONFIG_FILE = "project.json";
16111
16245
  var DEFAULT_EXPORT_DIR = ".skills/exports";
16112
16246
  function getProjectStateDir(targetDir = process.cwd()) {
16113
- return join19(targetDir, SKILLS_PROJECT_DIR);
16247
+ return join20(targetDir, SKILLS_PROJECT_DIR);
16114
16248
  }
16115
16249
  function getProjectConfigPath(targetDir = process.cwd()) {
16116
- return join19(getProjectStateDir(targetDir), PROJECT_CONFIG_FILE);
16250
+ return join20(getProjectStateDir(targetDir), PROJECT_CONFIG_FILE);
16117
16251
  }
16118
16252
  function loadProjectConfig(targetDir = process.cwd()) {
16119
16253
  const path = getProjectConfigPath(targetDir);
16120
- if (!existsSync10(path))
16254
+ if (!existsSync11(path))
16121
16255
  return null;
16122
16256
  try {
16123
16257
  return normalizeProjectConfig(JSON.parse(readFileSync14(path, "utf-8")));
@@ -16242,19 +16376,19 @@ function getSkillPath(name) {
16242
16376
  const extensionPath = findExtensionSkillPath(skillName);
16243
16377
  if (extensionPath)
16244
16378
  return extensionPath;
16245
- return join20(getPortableSkillsRoot(), skillName);
16379
+ return join21(getPortableSkillsRoot(), skillName);
16246
16380
  }
16247
16381
  function getCanonicalSkillName(name) {
16248
16382
  return getSkill(name)?.name ?? resolveSkillAlias(normalizeSkillSlug(name));
16249
16383
  }
16250
16384
  function skillExists(name) {
16251
- return existsSync11(getSkillPath(name));
16385
+ return existsSync12(getSkillPath(name));
16252
16386
  }
16253
16387
  function installSkill(name, options = {}) {
16254
16388
  const { targetDir = process.cwd(), overwrite = false } = options;
16255
16389
  const canonicalName = getCanonicalSkillName(name);
16256
16390
  const skillName = normalizeSkillName(canonicalName);
16257
- if (!existsSync11(getSkillPath(name))) {
16391
+ if (!existsSync12(getSkillPath(name))) {
16258
16392
  const knownOfficial = Boolean(getSkill(name));
16259
16393
  return {
16260
16394
  skill: canonicalName,
@@ -16281,7 +16415,7 @@ function installSkill(name, options = {}) {
16281
16415
  }
16282
16416
  function installSkillSource(name, _options = {}) {
16283
16417
  const canonicalName = getCanonicalSkillName(name);
16284
- if (!existsSync11(getSkillPath(name))) {
16418
+ if (!existsSync12(getSkillPath(name))) {
16285
16419
  return { skill: canonicalName, success: false, error: `Skill '${name}' not found`, mode: "source" };
16286
16420
  }
16287
16421
  return {
@@ -16302,11 +16436,11 @@ function installSkillManifest(manifest, _options = {}) {
16302
16436
  }
16303
16437
  function createLocalSkillManifest(name, generateSkillMd) {
16304
16438
  const sourcePath = getSkillPath(name);
16305
- if (!existsSync11(sourcePath))
16439
+ if (!existsSync12(sourcePath))
16306
16440
  return null;
16307
16441
  let skillMd = "";
16308
- const skillMdPath = join20(sourcePath, "SKILL.md");
16309
- if (existsSync11(skillMdPath)) {
16442
+ const skillMdPath = join21(sourcePath, "SKILL.md");
16443
+ if (existsSync12(skillMdPath)) {
16310
16444
  skillMd = readFileSync15(skillMdPath, "utf-8");
16311
16445
  } else if (generateSkillMd) {
16312
16446
  skillMd = generateSkillMd(name) ?? "";
@@ -16379,21 +16513,21 @@ function getAgentSkillsDir(agent, scope = "global", projectDir) {
16379
16513
  const base = projectDir || process.cwd();
16380
16514
  switch (agent) {
16381
16515
  case "pi":
16382
- 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");
16383
16517
  case "opencode":
16384
- 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");
16385
16519
  default:
16386
- return scope === "project" ? join20(base, `.${agent}`, "skills") : join20(homedir4(), `.${agent}`, "skills");
16520
+ return scope === "project" ? join21(base, `.${agent}`, "skills") : join21(homedir4(), `.${agent}`, "skills");
16387
16521
  }
16388
16522
  }
16389
16523
  function getAgentSkillPath(name, agent, scope = "global", projectDir) {
16390
16524
  const skillName = normalizeSkillName(getCanonicalSkillName(name));
16391
- return join20(getAgentSkillsDir(agent, scope, projectDir), skillName);
16525
+ return join21(getAgentSkillsDir(agent, scope, projectDir), skillName);
16392
16526
  }
16393
16527
  function installSkillForAgent(name, options, generateSkillMd) {
16394
16528
  assertNativeExportAllowed();
16395
16529
  const canonicalName = getCanonicalSkillName(name);
16396
- if (!existsSync11(getSkillPath(name))) {
16530
+ if (!existsSync12(getSkillPath(name))) {
16397
16531
  return { skill: canonicalName, success: false, error: `Skill '${name}' not found` };
16398
16532
  }
16399
16533
  const scope = options.scope ?? "global";
@@ -16424,8 +16558,8 @@ function removeSkillForAgent(name, options) {
16424
16558
  }
16425
16559
  function resolveAgentSkillMd(name, generateSkillMd) {
16426
16560
  const sourcePath = getSkillPath(name);
16427
- const skillMdPath = join20(sourcePath, "SKILL.md");
16428
- if (existsSync11(skillMdPath))
16561
+ const skillMdPath = join21(sourcePath, "SKILL.md");
16562
+ if (existsSync12(skillMdPath))
16429
16563
  return readFileSync15(skillMdPath, "utf-8");
16430
16564
  if (generateSkillMd)
16431
16565
  return generateSkillMd(name);
@@ -16444,7 +16578,7 @@ function warnMissingDependencies(name, targetDir) {
16444
16578
  }
16445
16579
  function generateMinimalSkillMd(name) {
16446
16580
  const sourcePath = getSkillPath(name);
16447
- if (!existsSync11(sourcePath))
16581
+ if (!existsSync12(sourcePath))
16448
16582
  return null;
16449
16583
  const canonicalName = getCanonicalSkillName(name);
16450
16584
  const meta = getSkill(canonicalName);
@@ -16459,7 +16593,7 @@ function generateMinimalSkillMd(name) {
16459
16593
  "---",
16460
16594
  ""
16461
16595
  ].filter(Boolean);
16462
- 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"));
16463
16597
  if (fallbackDoc)
16464
16598
  return `${frontmatter.join(`
16465
16599
  `)}${fallbackDoc.trim()}
@@ -16478,8 +16612,8 @@ skills run ${canonicalName}
16478
16612
  `;
16479
16613
  }
16480
16614
  function readBundledSkillVersion(name) {
16481
- const pkgPath = join20(getSkillPath(name), "package.json");
16482
- if (!existsSync11(pkgPath))
16615
+ const pkgPath = join21(getSkillPath(name), "package.json");
16616
+ if (!existsSync12(pkgPath))
16483
16617
  return "unknown";
16484
16618
  try {
16485
16619
  const pkg = JSON.parse(readFileSync15(pkgPath, "utf-8"));
@@ -16489,7 +16623,7 @@ function readBundledSkillVersion(name) {
16489
16623
  }
16490
16624
  }
16491
16625
  function readFileIfExists(path) {
16492
- return existsSync11(path) ? readFileSync15(path, "utf-8") : null;
16626
+ return existsSync12(path) ? readFileSync15(path, "utf-8") : null;
16493
16627
  }
16494
16628
  function loadProjectConfigCompat(targetDir) {
16495
16629
  return loadProjectConfig(targetDir);
@@ -16533,8 +16667,8 @@ function selectSkillDoc(docs, file) {
16533
16667
  function isInstructionSkillDir(skillPath, meta) {
16534
16668
  if (meta?.kind === "instruction")
16535
16669
  return true;
16536
- const skillMdPath = join21(skillPath, "SKILL.md");
16537
- if (!existsSync12(skillMdPath))
16670
+ const skillMdPath = join23(skillPath, "SKILL.md");
16671
+ if (!existsSync13(skillMdPath))
16538
16672
  return false;
16539
16673
  try {
16540
16674
  return parseSkillFrontmatter(readFileSync16(skillMdPath, "utf-8"))?.kind === "instruction";
@@ -16560,12 +16694,12 @@ var HOSTED_PROVIDER_ENV_PREFIXES = [
16560
16694
  ];
16561
16695
  function getSkillDocs(name) {
16562
16696
  const skillPath = getSkillPath(name);
16563
- if (!existsSync12(skillPath))
16697
+ if (!existsSync13(skillPath))
16564
16698
  return null;
16565
16699
  return {
16566
- skillMd: readIfExists(join21(skillPath, "SKILL.md")),
16567
- readme: readIfExists(join21(skillPath, "README.md")),
16568
- 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"))
16569
16703
  };
16570
16704
  }
16571
16705
  function getSkillBestDoc(name, file) {
@@ -16577,18 +16711,18 @@ function getSkillBestDoc(name, file) {
16577
16711
  }
16578
16712
  function getSkillRequirements(name) {
16579
16713
  const skillPath = getSkillPath(name);
16580
- if (!existsSync12(skillPath))
16714
+ if (!existsSync13(skillPath))
16581
16715
  return null;
16582
16716
  const texts = [];
16583
16717
  for (const file of ["SKILL.md", "README.md", "CLAUDE.md", ".env.example", ".env.local.example"]) {
16584
- const content = readIfExists(join21(skillPath, file));
16718
+ const content = readIfExists(join23(skillPath, file));
16585
16719
  if (content)
16586
16720
  texts.push(content);
16587
16721
  }
16588
16722
  const meta = getSkill(name);
16589
16723
  let dependencies = {};
16590
16724
  try {
16591
- dependencies = JSON.parse(readFileSync16(join21(skillPath, "package.json"), "utf8")).dependencies || {};
16725
+ dependencies = JSON.parse(readFileSync16(join23(skillPath, "package.json"), "utf8")).dependencies || {};
16592
16726
  } catch {}
16593
16727
  return getSkillRequirementsFromContent(name, texts, dependencies, meta);
16594
16728
  }
@@ -16641,7 +16775,7 @@ async function runSkill(name, args, options = {}) {
16641
16775
  const meta = getSkill(name);
16642
16776
  const canonicalName = meta?.name ?? name;
16643
16777
  const skillPath = getSkillPath(canonicalName);
16644
- if (!existsSync12(skillPath)) {
16778
+ if (!existsSync13(skillPath)) {
16645
16779
  return { exitCode: 1, error: `Skill '${name}' not found` };
16646
16780
  }
16647
16781
  if (isInstructionSkillDir(skillPath, meta)) {
@@ -16650,8 +16784,8 @@ async function runSkill(name, args, options = {}) {
16650
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'.`
16651
16785
  };
16652
16786
  }
16653
- const pkgPath = join21(skillPath, "package.json");
16654
- if (!existsSync12(pkgPath)) {
16787
+ const pkgPath = join23(skillPath, "package.json");
16788
+ if (!existsSync13(pkgPath)) {
16655
16789
  return { exitCode: 1, error: `No package.json in skill '${name}'` };
16656
16790
  }
16657
16791
  let entryPoint2;
@@ -16670,12 +16804,12 @@ async function runSkill(name, args, options = {}) {
16670
16804
  } catch {
16671
16805
  return { exitCode: 1, error: `Failed to parse package.json for skill '${name}'` };
16672
16806
  }
16673
- const entryPath = join21(skillPath, entryPoint2);
16674
- if (!existsSync12(entryPath)) {
16807
+ const entryPath = join23(skillPath, entryPoint2);
16808
+ if (!existsSync13(entryPath)) {
16675
16809
  return { exitCode: 1, error: `Entry point '${entryPoint2}' not found in skill '${name}'` };
16676
16810
  }
16677
16811
  const env = { ...process.env, ...options.env };
16678
- if (!existsSync12(join21(skillPath, "node_modules")) || hasDependencyPreparationMarker(skillPath)) {
16812
+ if (!existsSync13(join23(skillPath, "node_modules")) || hasDependencyPreparationMarker(skillPath)) {
16679
16813
  const failure = await prepareSkillDependencies(skillPath, env, options.preparationTimeoutMs);
16680
16814
  if (failure)
16681
16815
  return failure;
@@ -16744,7 +16878,7 @@ function generateSkillMd(name) {
16744
16878
  if (!meta)
16745
16879
  return null;
16746
16880
  const skillPath = getSkillPath(name);
16747
- if (!existsSync12(skillPath))
16881
+ if (!existsSync13(skillPath))
16748
16882
  return null;
16749
16883
  const frontmatter = [
16750
16884
  "---",
@@ -16753,11 +16887,11 @@ function generateSkillMd(name) {
16753
16887
  "---"
16754
16888
  ].join(`
16755
16889
  `);
16756
- const readme = readIfExists(join21(skillPath, "README.md"));
16757
- const claudeMd = readIfExists(join21(skillPath, "CLAUDE.md"));
16890
+ const readme = readIfExists(join23(skillPath, "README.md"));
16891
+ const claudeMd = readIfExists(join23(skillPath, "CLAUDE.md"));
16758
16892
  let cliCommand = null;
16759
- const pkgPath = join21(skillPath, "package.json");
16760
- if (existsSync12(pkgPath)) {
16893
+ const pkgPath = join23(skillPath, "package.json");
16894
+ if (existsSync13(pkgPath)) {
16761
16895
  try {
16762
16896
  const pkg = JSON.parse(readFileSync16(pkgPath, "utf-8"));
16763
16897
  if (pkg.bin) {
@@ -16834,7 +16968,7 @@ function extractEnvVars(text2) {
16834
16968
  }
16835
16969
  function readIfExists(path) {
16836
16970
  try {
16837
- if (existsSync12(path)) {
16971
+ if (existsSync13(path)) {
16838
16972
  return readFileSync16(path, "utf-8");
16839
16973
  }
16840
16974
  } catch {}
@@ -16901,6 +17035,8 @@ async function syncSelectionProfile(profileId, options = {}) {
16901
17035
  async function resolveSelectionContext(profileId, options = {}) {
16902
17036
  const sessionState = options.sessionId ? readSkillSessionSnapshotIfExists(options.sessionId, options) : null;
16903
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.");
16904
17040
  const parentState = !session && options.parentSessionId ? readSkillSessionSnapshotIfExists(options.parentSessionId, options) : null;
16905
17041
  if (!session && options.parentSessionId && !parentState) {
16906
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.");
@@ -17160,15 +17296,15 @@ function buildPluginProjection(entries) {
17160
17296
 
17161
17297
  // src/lib/plugin-projection-store.ts
17162
17298
  import { createHash as createHash14, randomUUID as randomUUID3 } from "crypto";
17163
- 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";
17164
- 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";
17165
17301
  var READ_FLAGS = constants8.O_RDONLY | constants8.O_NOFOLLOW | constants8.O_NONBLOCK;
17166
17302
  var WRITE_FLAGS = constants8.O_WRONLY | constants8.O_CREAT | constants8.O_EXCL | constants8.O_NOFOLLOW | constants8.O_NONBLOCK;
17167
17303
  var uid = () => typeof process.getuid === "function" ? BigInt(process.getuid()) : undefined;
17168
17304
  var missing = (error) => Boolean(error && typeof error === "object" && "code" in error && error.code === "ENOENT");
17169
17305
  function stat(path) {
17170
17306
  try {
17171
- return lstatSync17(path, { bigint: true });
17307
+ return lstatSync18(path, { bigint: true });
17172
17308
  } catch (error) {
17173
17309
  if (missing(error))
17174
17310
  return null;
@@ -17176,7 +17312,7 @@ function stat(path) {
17176
17312
  }
17177
17313
  }
17178
17314
  function canonical5(path) {
17179
- 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");
17180
17316
  }
17181
17317
  function owner(stats, system = false) {
17182
17318
  const current = uid();
@@ -17191,7 +17327,7 @@ function ancestors(path, create = false) {
17191
17327
  let current = filesystemRoot;
17192
17328
  for (const segment of ["", ...parent.slice(filesystemRoot.length).split("/").filter(Boolean)]) {
17193
17329
  if (segment)
17194
- current = join23(current, segment);
17330
+ current = join25(current, segment);
17195
17331
  let currentStat = stat(current);
17196
17332
  if (!currentStat && create) {
17197
17333
  try {
@@ -17306,7 +17442,7 @@ function snapshotPluginTree(root, options = {}) {
17306
17442
  pluginNeed(++visited <= PLUGIN_PROJECTION_LIMITS.files * 50, "Plugin tree membership exceeds its limit");
17307
17443
  const path = relative6 ? `${relative6}/${name}` : name;
17308
17444
  pluginNeed(new TextEncoder().encode(path).byteLength <= 100 && !/[\\:\x00-\x1f\x7f]/u.test(path), "Plugin entry path exceeds its limit or is unsafe");
17309
- const absolute = join23(directory, name), entryStat = stat(absolute);
17445
+ const absolute = join25(directory, name), entryStat = stat(absolute);
17310
17446
  pluginNeed(entryStat, "Plugin tree member disappeared");
17311
17447
  if (relative6 === "" && name === ".orphaned_at" && (options.nativeRuntime === "claude-2.1.274" || options.nativeRuntime === "claude-2.1.276")) {
17312
17448
  const bytes2 = readBytes(absolute, 32, "native-pin");
@@ -17323,7 +17459,7 @@ function snapshotPluginTree(root, options = {}) {
17323
17459
  directories.push({ path: absolute, stat: entryStat, names: pins });
17324
17460
  for (const pin of pins) {
17325
17461
  pluginNeed(/^[1-9][0-9]{0,9}$/.test(pin) && Number(pin) <= 2147483647, "Unrecognized native plugin pin name");
17326
- const pinPath = join23(absolute, pin), pinStat = stat(pinPath);
17462
+ const pinPath = join25(absolute, pin), pinStat = stat(pinPath);
17327
17463
  pluginNeed(pinStat, "Native plugin pin disappeared");
17328
17464
  const bytes2 = readBytes(pinPath, 4096, "native-pin");
17329
17465
  let value;
@@ -17421,7 +17557,7 @@ function materializePluginTree(root, entries) {
17421
17557
  mkdirSync8(staging, { mode: 448 });
17422
17558
  staged = stat(staging);
17423
17559
  for (const entry of entries) {
17424
- const path = join23(staging, entry.path);
17560
+ const path = join25(staging, entry.path);
17425
17561
  ancestors(path, true);
17426
17562
  writeExclusive(path, entry.bytes, entry.mode);
17427
17563
  }
@@ -17548,7 +17684,7 @@ function describeEntries(entries) {
17548
17684
 
17549
17685
  // src/lib/plugin-admission.ts
17550
17686
  function pluginAdmissionRoot() {
17551
- return join25(homedir5(), ".hasna", "skills", "plugin-admission");
17687
+ return join26(homedir5(), ".hasna", "skills", "plugin-admission");
17552
17688
  }
17553
17689
  function validatePluginTarget(value) {
17554
17690
  pluginKeys(value, ["schemaVersion", "pluginId", "registrations", "native", "resolver"]);
@@ -17559,7 +17695,7 @@ function validatePluginTarget(value) {
17559
17695
  for (const registration of value.registrations) {
17560
17696
  pluginKeys(registration, ["scope", "projectPath"]);
17561
17697
  pluginNeed(registration.scope === "user" || registration.scope === "project", "Unsupported plugin registration scope");
17562
- 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");
17563
17699
  const key = JSON.stringify([registration.scope, registration.projectPath]);
17564
17700
  pluginNeed(!scopes.has(key), "Duplicate plugin registration scope");
17565
17701
  scopes.add(key);
@@ -17567,7 +17703,7 @@ function validatePluginTarget(value) {
17567
17703
  for (const key of ["native", "resolver"]) {
17568
17704
  pluginKeys(value[key], key === "native" ? ["version", "executable", "digest"] : ["executable", "digest"]);
17569
17705
  pluginText(value[key].executable);
17570
- 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");
17571
17707
  pluginDigest(value[key].digest);
17572
17708
  }
17573
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)");
@@ -17719,17 +17855,17 @@ function pluginResolverCommand(binding) {
17719
17855
  }
17720
17856
  function root(options) {
17721
17857
  const path = options.storeRoot ?? pluginAdmissionRoot();
17722
- pluginNeed(isAbsolute13(path) && resolve12(path) === path, "Invalid plugin admission store root");
17858
+ pluginNeed(isAbsolute13(path) && resolve13(path) === path, "Invalid plugin admission store root");
17723
17859
  return path;
17724
17860
  }
17725
17861
  function pluginBindingPath(storeRoot, id) {
17726
17862
  pluginNeed(/^[a-f0-9]{64}$/.test(id), "Invalid plugin binding identifier");
17727
- return join25(storeRoot, "bindings", `${id}.json`);
17863
+ return join26(storeRoot, "bindings", `${id}.json`);
17728
17864
  }
17729
17865
  function pluginReceiptPath(storeRoot, id, digest) {
17730
17866
  pluginNeed(/^[a-f0-9]{64}$/.test(id), "Invalid plugin binding identifier");
17731
17867
  pluginDigest(digest);
17732
- return join25(storeRoot, "receipts", id, `${digest.slice(7)}.json`);
17868
+ return join26(storeRoot, "receipts", id, `${digest.slice(7)}.json`);
17733
17869
  }
17734
17870
  function readPluginBinding(storeRoot, id) {
17735
17871
  const value = readPluginJson(pluginBindingPath(storeRoot, id));
@@ -17749,7 +17885,7 @@ function readPluginAdmissionReceipt(storeRoot, id, digest) {
17749
17885
  pluginNeed(receipt.plan.bindingId === id && receipt.plan.planDigest === digest, "Plugin receipt identity differs from its filename");
17750
17886
  const binding = readPluginBinding(storeRoot, id);
17751
17887
  pluginNeed(canonicalJson(binding) === canonicalJson(receipt.plan.binding) && receipt.plan.sourceCommand === pluginResolverCommand(binding), "Plugin receipt binding changed");
17752
- 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");
17753
17889
  pluginText(receipt.admittedAt, 64);
17754
17890
  pluginNeed(Number.isFinite(Date.parse(receipt.admittedAt)), "Invalid plugin admission time");
17755
17891
  return receipt;
@@ -17890,7 +18026,7 @@ async function admitPlugin(spec, profileId, target, approvedDigest, approvedEvid
17890
18026
  const { plan, projection } = await candidate(spec, profileId, target, options);
17891
18027
  pluginNeed(plan.planDigest === approvedDigest, "The plugin plan changed since review; obtain and review a new plan");
17892
18028
  pluginNeed(plan.evidenceDigest === approvedEvidenceDigest, "The plugin routing evidence changed since review; obtain and review a new plan");
17893
- 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));
17894
18030
  materializePluginTree(materializedPath, projection.files);
17895
18031
  writePluginJsonImmutable(pluginBindingPath(store, plan.bindingId), plan.binding);
17896
18032
  const path = pluginReceiptPath(store, plan.bindingId, plan.planDigest), existing = readPluginJson(path);
@@ -17924,21 +18060,21 @@ function validateManagedPluginWitnesses(value) {
17924
18060
  pluginNeed(typeof item.bindingId === "string" && /^[a-f0-9]{64}$/.test(item.bindingId) && !seen.has(item.bindingId), "Invalid or duplicate managed plugin witness");
17925
18061
  seen.add(item.bindingId);
17926
18062
  pluginText(item.storeRoot);
17927
- 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");
17928
18064
  }
17929
18065
  }
17930
18066
  function safe3(path) {
17931
18067
  pluginText(path);
17932
- pluginNeed(isAbsolute14(path) && resolve13(path) === path, "Invalid plugin registry path");
18068
+ pluginNeed(isAbsolute14(path) && resolve14(path) === path, "Invalid plugin registry path");
17933
18069
  for (let at = path;; at = dirname14(at)) {
17934
- 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");
17935
18071
  if (at === dirname14(at))
17936
18072
  break;
17937
18073
  }
17938
18074
  }
17939
18075
  function readRegistry(path) {
17940
18076
  safe3(path);
17941
- const initial = lstatSync18(path);
18077
+ const initial = lstatSync19(path);
17942
18078
  pluginNeed(initial.isFile() && initial.size <= 1024 * 1024, "Plugin registry is not a bounded regular file");
17943
18079
  const fd = openSync10(path, constants9.O_RDONLY | constants9.O_NOFOLLOW | constants9.O_NONBLOCK);
17944
18080
  try {
@@ -17952,7 +18088,7 @@ function readRegistry(path) {
17952
18088
  break;
17953
18089
  length += count;
17954
18090
  }
17955
- const after = fstatSync10(fd), current = lstatSync18(path);
18091
+ const after = fstatSync10(fd), current = lstatSync19(path);
17956
18092
  safe3(path);
17957
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");
17958
18094
  let value;
@@ -17982,7 +18118,7 @@ function hashManagedPluginRegistry(path, managed) {
17982
18118
  seen.add(target.pluginId);
17983
18119
  const rows = plugins[target.pluginId];
17984
18120
  pluginNeed(Array.isArray(rows) && rows.length === target.registrations.length, "Managed plugin registration membership changed");
17985
- 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);
17986
18122
  const scopes = new Set, approvals = new Map, currentFiles = new Map;
17987
18123
  plugins[target.pluginId] = rows.map((row) => {
17988
18124
  pluginNeed(pluginObject(row), "Invalid managed plugin registration");
@@ -17995,7 +18131,7 @@ function hashManagedPluginRegistry(path, managed) {
17995
18131
  pluginText(row.sourceProducerPath);
17996
18132
  pluginText(row.lastUpdated, 64);
17997
18133
  pluginNeed(Number.isFinite(Date.parse(row.lastUpdated)), "Invalid managed plugin update timestamp");
17998
- 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");
17999
18135
  const approval = (producer) => {
18000
18136
  pluginText(producer);
18001
18137
  const digest = `sha256:${basename7(producer)}`;
@@ -18025,7 +18161,7 @@ function hashManagedPluginRegistry(path, managed) {
18025
18161
  return Object.fromEntries(Object.entries(row).filter(([key]) => !["version", "installPath", "sourceProducerPath", "previousProducerPaths", "lastUpdated"].includes(key)));
18026
18162
  });
18027
18163
  safe3(cacheRoot);
18028
- const before = lstatSync18(cacheRoot);
18164
+ const before = lstatSync19(cacheRoot);
18029
18165
  pluginNeed(before.isDirectory(), "Invalid managed plugin cache root");
18030
18166
  const directory = opendirSync5(cacheRoot), names = [];
18031
18167
  try {
@@ -18038,17 +18174,17 @@ function hashManagedPluginRegistry(path, managed) {
18038
18174
  directory.closeSync();
18039
18175
  }
18040
18176
  for (const version of names.sort()) {
18041
- 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}` }));
18042
18178
  verifiedBytes += files.reduce((total, file) => total + file.size, 0);
18043
18179
  pluginNeed(verifiedBytes <= 256 * 1024 * 1024, "Managed plugin verification exceeds its aggregate byte budget");
18044
18180
  const serialized = JSON.stringify(files);
18045
- const expected = currentFiles.get(join26(cacheRoot, version));
18181
+ const expected = currentFiles.get(join27(cacheRoot, version));
18046
18182
  pluginNeed(expected === undefined || serialized === expected, "Current native cache differs from its exact registered producer receipt");
18047
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");
18048
18184
  }
18049
18185
  for (const row of rows)
18050
18186
  pluginNeed(names.includes(row.version), "Managed native plugin installation is missing");
18051
- const after = lstatSync18(cacheRoot);
18187
+ const after = lstatSync19(cacheRoot);
18052
18188
  safe3(cacheRoot);
18053
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");
18054
18190
  }
@@ -18061,21 +18197,21 @@ function captureManagedPluginRegistry(path, managedPlugins) {
18061
18197
 
18062
18198
  // src/lib/claude-marketplace-registry.ts
18063
18199
  import { createHash as createHash15 } from "crypto";
18064
- import { closeSync as closeSync11, constants as constants10, fstatSync as fstatSync11, lstatSync as lstatSync19, openSync as openSync11, readSync as readSync9 } from "fs";
18065
- 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";
18066
18202
  var CLAUDE_MARKETPLACE_REGISTRY_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
18067
18203
  function need5(value, reason) {
18068
18204
  if (!value)
18069
18205
  throw new Error(`Claude marketplace registry ${reason}`);
18070
18206
  }
18071
18207
  function absolutePath3(value) {
18072
- 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");
18073
18209
  }
18074
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;
18075
18211
  function parents2(path) {
18076
18212
  const result = [];
18077
18213
  for (let at = dirname15(path);; at = dirname15(at)) {
18078
- const stat2 = lstatSync19(at, { bigint: true });
18214
+ const stat2 = lstatSync20(at, { bigint: true });
18079
18215
  need5(stat2.isDirectory() && !stat2.isSymbolicLink(), "path traverses a symlink or non-directory");
18080
18216
  result.push([at, stat2]);
18081
18217
  if (at === dirname15(at))
@@ -18086,7 +18222,7 @@ function readRegistry2(path, budget) {
18086
18222
  absolutePath3(path);
18087
18223
  need5(basename8(path) === "known_marketplaces.json", "requires known_marketplaces.json");
18088
18224
  need5(Number.isSafeInteger(budget.remaining) && budget.remaining >= 0, "has an invalid byte budget");
18089
- const ancestors2 = parents2(path), initial = lstatSync19(path, { bigint: true });
18225
+ const ancestors2 = parents2(path), initial = lstatSync20(path, { bigint: true });
18090
18226
  need5(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_MARKETPLACE_REGISTRY_LIMITS.bytes), "is not a bounded regular file");
18091
18227
  need5(initial.size <= BigInt(budget.remaining), "exceeds its aggregate byte limit");
18092
18228
  const fd = openSync11(path, constants10.O_RDONLY | constants10.O_NOFOLLOW | constants10.O_NONBLOCK);
@@ -18101,10 +18237,10 @@ function readRegistry2(path, budget) {
18101
18237
  break;
18102
18238
  length += count;
18103
18239
  }
18104
- const after = fstatSync11(fd, { bigint: true }), current = lstatSync19(path, { bigint: true });
18240
+ const after = fstatSync11(fd, { bigint: true }), current = lstatSync20(path, { bigint: true });
18105
18241
  need5(current.isFile() && sameFile2(opened, after) && sameFile2(after, current) && BigInt(length) === opened.size, "changed during read");
18106
18242
  for (const [ancestor, before] of ancestors2) {
18107
- const now = lstatSync19(ancestor, { bigint: true });
18243
+ const now = lstatSync20(ancestor, { bigint: true });
18108
18244
  need5(now.isDirectory() && before.dev === now.dev && before.ino === now.ino && before.mode === now.mode, "parent changed during read");
18109
18245
  }
18110
18246
  budget.remaining -= length;
@@ -18244,7 +18380,7 @@ function captureClaudeMarketplaceRegistryV2(path, budget = { remaining: 256 * 10
18244
18380
  import { createHash as createHash17 } from "crypto";
18245
18381
 
18246
18382
  // src/lib/codex-hook-trust-files.ts
18247
- 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";
18248
18384
  import { createHash as createHash16 } from "crypto";
18249
18385
  import { dirname as dirname16 } from "path";
18250
18386
  function need6(value, code) {
@@ -18254,7 +18390,7 @@ function need6(value, code) {
18254
18390
  var sameStat = (a, b) => ["dev", "ino", "uid", "nlink", "mode", "size", "mtimeNs", "ctimeNs"].every((key) => a[key] === b[key]);
18255
18391
  function safeParents(file) {
18256
18392
  for (let p = dirname16(file);; p = dirname16(p)) {
18257
- const s = lstatSync20(p);
18393
+ const s = lstatSync21(p);
18258
18394
  need6(s.isDirectory() && !s.isSymbolicLink(), "UNSAFE_PARENT");
18259
18395
  need6((s.mode & 18) === 0 || s.uid === 0 && (s.mode & 512) !== 0, "UNSAFE_PARENT");
18260
18396
  if (dirname16(p) === p)
@@ -18264,7 +18400,7 @@ function safeParents(file) {
18264
18400
  function snapshot(file, privateFile = false, options = {}) {
18265
18401
  const readOnlyPackage = options.readOnlyPackage === true && !privateFile;
18266
18402
  safeParents(file);
18267
- const initial = lstatSync20(file, { bigint: true });
18403
+ const initial = lstatSync21(file, { bigint: true });
18268
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");
18269
18405
  need6(initial.size <= 8n * 1024n * 1024n, "FILE_BOUND");
18270
18406
  const fd = openSync12(file, constants11.O_RDONLY | constants11.O_NOFOLLOW | constants11.O_NONBLOCK);
@@ -18279,7 +18415,7 @@ function snapshot(file, privateFile = false, options = {}) {
18279
18415
  break;
18280
18416
  length += n2;
18281
18417
  }
18282
- 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");
18283
18419
  const content = bytes.subarray(0, length), text2 = new TextDecoder("utf-8", { fatal: true }).decode(content);
18284
18420
  return { file, privateFile, readOnlyPackage, text: text2, bytes: content, stat: before, sha256: createHash16("sha256").update(content).digest("hex") };
18285
18421
  } finally {
@@ -18331,7 +18467,7 @@ function safe4(path) {
18331
18467
  if (!isAbsolute16(path) || path.includes("\x00"))
18332
18468
  throw new Error("Expected an absolute native discovery path");
18333
18469
  for (let cursor = path;; cursor = dirname17(cursor)) {
18334
- if (lstatSync21(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
18470
+ if (lstatSync22(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
18335
18471
  throw new Error(`Refusing symlink native discovery input: ${cursor}`);
18336
18472
  if (dirname17(cursor) === cursor)
18337
18473
  break;
@@ -18341,7 +18477,7 @@ function read2(path, changes) {
18341
18477
  safe4(path);
18342
18478
  if (changes?.has(path))
18343
18479
  return changes.get(path);
18344
- const stat2 = lstatSync21(path, { throwIfNoEntry: false });
18480
+ const stat2 = lstatSync22(path, { throwIfNoEntry: false });
18345
18481
  if (!stat2)
18346
18482
  return null;
18347
18483
  if (!stat2.isFile() || stat2.size > 16 * 1024 * 1024)
@@ -18452,12 +18588,12 @@ function codexInstallationRoots(binding) {
18452
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)
18453
18589
  throw new Error("Invalid native installation input proof");
18454
18590
  const cache = dirname17(dirname17(proof.plugins[0].pluginParent));
18455
- 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)));
18456
18592
  const configSource = binding.sources.find((source) => source.path === config && source.sha256 !== null && (!source.format || source.format === "toml" && source.fields?.includes("plugins")));
18457
- 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));
18458
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)))))
18459
18595
  throw new Error("Native installation input overlaps configuration or loading roots");
18460
- 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)))
18461
18597
  throw new Error("Missing native installation input source witness");
18462
18598
  return reviewedCodexPluginSourceRoots(cache, proof.plugins, (path) => {
18463
18599
  const text2 = read2(path);
@@ -18499,19 +18635,19 @@ function captureDiscoveryPathSources(paths) {
18499
18635
  const budget = discoveryByteBudget();
18500
18636
  return paths.map((path) => ({ path, hashMode: "path-bytes", sha256: hashDiscoveryPathFile(path, budget) }));
18501
18637
  }
18502
- function assertProjectDiscovery(agent, directories, home, canonical6 = resolve15, reviewed) {
18638
+ function assertProjectDiscovery(agent, directories, home, canonical6 = resolve16, reviewed) {
18503
18639
  const homeConfigPath = canonical6(agentDiscoveryConfigPath(home, agent));
18504
18640
  for (const directory of directories) {
18505
- const isHome = resolve15(directory) === resolve15(home);
18641
+ const isHome = resolve16(directory) === resolve16(home);
18506
18642
  if (agent === "hermes") {
18507
18643
  assertHermesEnvironment(home);
18508
18644
  continue;
18509
18645
  }
18510
- if (isHome && agent !== "claude")
18646
+ if (isHome && agent !== "claude" && agent !== "sumi")
18511
18647
  continue;
18512
- 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"];
18513
18649
  for (const suffix of paths) {
18514
- const path = canonical6(join27(directory, suffix)), raw = read2(path);
18650
+ const path = canonical6(join28(directory, suffix)), raw = read2(path);
18515
18651
  if (raw === null)
18516
18652
  continue;
18517
18653
  const isRootAliasedHomeConfig = agent === "claude" && suffix === ".claude/settings.json" || agent === "codex" && suffix === ".codex/config.toml";
@@ -18520,7 +18656,7 @@ function assertProjectDiscovery(agent, directories, home, canonical6 = resolve15
18520
18656
  if (suffix.endsWith(".jsonc"))
18521
18657
  throw new Error(`NATIVE_SKILL_DRIFT: project JSONC discovery configuration requires review: ${path}`);
18522
18658
  const config = parseConfig(raw, path, suffix.endsWith(".toml"));
18523
- 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"];
18524
18660
  let admittedPlugins = false;
18525
18661
  if (agent === "claude" && reviewed?.agent === agent && reviewed.method === "reviewed" && config.enabledPlugins && typeof config.enabledPlugins === "object" && !Array.isArray(config.enabledPlugins)) {
18526
18662
  const exact = reviewed.sources.find((source) => source.path === path && source.format === undefined && source.fields === undefined && (source.hashMode === undefined || source.hashMode === "bytes"));
@@ -18533,19 +18669,28 @@ function assertProjectDiscovery(agent, directories, home, canonical6 = resolve15
18533
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)))
18534
18670
  throw new Error(`NATIVE_SKILL_DRIFT: higher-precedence project skill or hook configuration requires review: ${path}`);
18535
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
+ }
18536
18679
  if (agent === "claude") {
18537
- const commands = canonical6(join27(directory, ".claude/commands"));
18680
+ const commands = canonical6(join28(directory, ".claude/commands"));
18538
18681
  safe4(commands);
18539
- if (lstatSync21(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(commands).length)
18682
+ if (lstatSync22(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(commands).length)
18540
18683
  throw new Error("NATIVE_SKILL_DRIFT: legacy project command discovery requires a dedicated format adapter");
18541
18684
  }
18542
18685
  }
18543
18686
  }
18544
18687
  function agentDiscoveryConfigPath(home, agent) {
18545
- 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"}`);
18546
18691
  }
18547
18692
  function resolveAgentDiscovery(options) {
18548
- 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;
18549
18694
  const sources = [], roots = new Set, builtinNames = [];
18550
18695
  const witness = (path, format, fields2) => {
18551
18696
  const source = { path: canonical6(path), sha256: null, ...format ? { format, fields: fields2 } : {} };
@@ -18556,13 +18701,49 @@ function resolveAgentDiscovery(options) {
18556
18701
  if (agent === "hermes")
18557
18702
  assertHermesEnvironment(home);
18558
18703
  const configPath = agentDiscoveryConfigPath(home, agent);
18559
- 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"]);
18560
18705
  const config = configText === null ? {} : agent === "hermes" ? parseHermesConfig(configText) : parseConfig(configText, configPath, agent === "codex");
18561
18706
  const unresolved = (detail) => {
18562
18707
  throw new Error(`Native discovery is unresolved (${agent}: ${detail}); provide a reviewed --discovery-inputs file`);
18563
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
+ }
18564
18745
  if (agent === "hermes") {
18565
- const profile = witness(join27(home, ".hermes/active_profile"));
18746
+ const profile = witness(join28(home, ".hermes/active_profile"));
18566
18747
  if (profile?.trim() && profile.trim() !== "default")
18567
18748
  unresolved("a non-default profile is active");
18568
18749
  const declared = config.skills?.external_dirs ?? [], paths = typeof declared === "string" ? [declared] : declared;
@@ -18576,7 +18757,7 @@ function resolveAgentDiscovery(options) {
18576
18757
  continue;
18577
18758
  if (normalized.startsWith("~") && normalized !== "~" && !normalized.startsWith("~/"))
18578
18759
  unresolved("user-specific tilde expansion requires a separate discovery adapter");
18579
- 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);
18580
18761
  safe4(path);
18581
18762
  roots.add(path);
18582
18763
  }
@@ -18585,8 +18766,8 @@ function resolveAgentDiscovery(options) {
18585
18766
  const executable = Bun.which("gemini");
18586
18767
  if (executable) {
18587
18768
  let packageRoot;
18588
- for (let directory = dirname17(realpathSync7(executable)), depth = 0;depth < 8; directory = dirname17(directory), depth++) {
18589
- 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);
18590
18771
  if (raw !== null && parseConfig(raw, path).name === "@google/gemini-cli") {
18591
18772
  packageRoot = directory;
18592
18773
  witness(path);
@@ -18597,12 +18778,12 @@ function resolveAgentDiscovery(options) {
18597
18778
  }
18598
18779
  if (!packageRoot)
18599
18780
  unresolved("installed Gemini package layout is not recognized");
18600
- const builtinRoot = join27(packageRoot, "bundle/builtin");
18781
+ const builtinRoot = join28(packageRoot, "bundle/builtin");
18601
18782
  safe4(builtinRoot);
18602
- if (!lstatSync21(builtinRoot, { throwIfNoEntry: false })?.isDirectory())
18783
+ if (!lstatSync22(builtinRoot, { throwIfNoEntry: false })?.isDirectory())
18603
18784
  unresolved("installed Gemini builtin root is missing");
18604
18785
  for (const name of readdirSync10(builtinRoot).sort()) {
18605
- const text2 = witness(join27(builtinRoot, name, "SKILL.md"));
18786
+ const text2 = witness(join28(builtinRoot, name, "SKILL.md"));
18606
18787
  const match = text2?.match(/^---\r?\n[\s\S]*?^name:\s*([a-z0-9][a-z0-9-]*)\s*$/m);
18607
18788
  if (!match?.[1] || builtinNames.includes(match[1]))
18608
18789
  unresolved("installed Gemini builtin name is missing or ambiguous");
@@ -18644,39 +18825,39 @@ function resolveAgentDiscovery(options) {
18644
18825
  function plugin(root2) {
18645
18826
  root2 = canonical6(root2);
18646
18827
  safe4(root2);
18647
- 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");
18648
18829
  const raw = witness(manifestPath);
18649
18830
  if (raw === null)
18650
18831
  unresolved("plugin manifest missing");
18651
18832
  const manifest = parseConfig(raw, manifestPath);
18652
- const hooks = witness(join27(root2, "hooks/hooks.json"));
18833
+ const hooks = witness(join28(root2, "hooks/hooks.json"));
18653
18834
  if (manifest.hooks !== undefined || hooks !== null)
18654
18835
  unresolved("plugin hooks require a separate no-skill-injection review");
18655
- const commands = join27(root2, "commands");
18836
+ const commands = join28(root2, "commands");
18656
18837
  safe4(commands);
18657
- 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))
18658
18839
  unresolved("legacy command discovery requires retirement or a dedicated format adapter");
18659
- const rootSkill = witness(join27(root2, "SKILL.md"));
18840
+ const rootSkill = witness(join28(root2, "SKILL.md"));
18660
18841
  if (rootSkill !== null)
18661
18842
  roots.add(root2);
18662
18843
  const declared = manifest.skills === undefined ? ["./skills"] : typeof manifest.skills === "string" ? [manifest.skills] : manifest.skills;
18663
18844
  if (!Array.isArray(declared) || declared.some((path) => typeof path !== "string"))
18664
18845
  unresolved("unsupported plugin skill paths");
18665
18846
  if (agent === "claude")
18666
- roots.add(join27(root2, "skills"));
18847
+ roots.add(join28(root2, "skills"));
18667
18848
  for (const value of declared) {
18668
- const path = resolve15(root2, value);
18849
+ const path = resolve16(root2, value);
18669
18850
  if (path !== root2 && !path.startsWith(root2 + sep6))
18670
18851
  unresolved("plugin skill path escapes its root");
18671
18852
  roots.add(path.endsWith(`${sep6}SKILL.md`) ? dirname17(path) : path);
18672
18853
  }
18673
18854
  }
18674
18855
  if (agent === "claude") {
18675
- const registrations = witness(join27(home, ".claude/plugins/installed_plugins.json"));
18676
- const installed = registrations === null ? {} : parseConfig(registrations, join27(home, ".claude/plugins/installed_plugins.json")).plugins;
18677
- 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"));
18678
18859
  safe4(commands);
18679
- if (lstatSync21(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(commands).length)
18860
+ if (lstatSync22(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(commands).length)
18680
18861
  unresolved("legacy home command discovery requires a dedicated format adapter");
18681
18862
  for (const [id, enabled] of Object.entries(config.enabledPlugins ?? {})) {
18682
18863
  if (enabled === false)
@@ -18698,16 +18879,16 @@ function resolveAgentDiscovery(options) {
18698
18879
  const market = config.marketplaces?.[marketplace] ?? (marketplace === "personal" ? { source_type: "local", source: home } : undefined);
18699
18880
  if (split < 1 || market?.source_type !== "local" || typeof market.source !== "string" || !isAbsolute16(market.source))
18700
18881
  unresolved("enabled plugin marketplace is not an explicit local source");
18701
- const catalogText = witness(join27(market.source, ".agents/plugins/marketplace.json"));
18882
+ const catalogText = witness(join28(market.source, ".agents/plugins/marketplace.json"));
18702
18883
  if (catalogText === null)
18703
18884
  unresolved("marketplace catalog missing");
18704
- 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) : [];
18705
18886
  if (matches.length !== 1 || matches[0]?.source?.source !== "local" || typeof matches[0]?.source?.path !== "string")
18706
18887
  unresolved("plugin catalog source is missing or ambiguous");
18707
- plugin(resolve15(market.source, matches[0].source.path));
18888
+ plugin(resolve16(market.source, matches[0].source.path));
18708
18889
  }
18709
18890
  } else if (agent === "opencode") {
18710
- 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)
18711
18892
  unresolved("additional config layers require review");
18712
18893
  if ((config.plugin?.length ?? 0) > 0 || config.skills?.urls?.length)
18713
18894
  unresolved("external plugin or remote skill source");
@@ -18716,14 +18897,17 @@ function resolveAgentDiscovery(options) {
18716
18897
  unresolved("relative or malformed added skill root");
18717
18898
  roots.add(path);
18718
18899
  }
18719
- const localPlugins = join27(home, ".config/opencode/plugins");
18900
+ const localPlugins = join28(home, ".config/opencode/plugins");
18720
18901
  safe4(localPlugins);
18721
- 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"))
18722
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");
18723
18907
  } else if (agent === "hermes") {
18724
- 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")]) {
18725
18909
  safe4(directory);
18726
- if (lstatSync21(directory, { throwIfNoEntry: false }))
18910
+ if (lstatSync22(directory, { throwIfNoEntry: false }))
18727
18911
  unresolved("installed runtime/plugin discovery requires reviewed source bindings");
18728
18912
  }
18729
18913
  if (Bun.which("hermes") || Object.keys(config.plugins ?? {}).length)
@@ -18731,30 +18915,30 @@ function resolveAgentDiscovery(options) {
18731
18915
  } else if (agent === "gemini") {
18732
18916
  if (Object.keys(config.extensions ?? {}).length || config.skills?.paths?.length)
18733
18917
  unresolved("custom extension or skill paths");
18734
- const directory = join27(home, ".gemini/extensions");
18918
+ const directory = join28(home, ".gemini/extensions");
18735
18919
  safe4(directory);
18736
- if (lstatSync21(directory, { throwIfNoEntry: false }))
18920
+ if (lstatSync22(directory, { throwIfNoEntry: false }))
18737
18921
  for (const name of readdirSync10(directory)) {
18738
- const root2 = join27(directory, name);
18922
+ const root2 = join28(directory, name);
18739
18923
  safe4(root2);
18740
- if (!lstatSync21(root2)?.isDirectory())
18924
+ if (!lstatSync22(root2)?.isDirectory())
18741
18925
  continue;
18742
- const raw = witness(join27(root2, "gemini-extension.json"));
18926
+ const raw = witness(join28(root2, "gemini-extension.json"));
18743
18927
  if (raw === null)
18744
18928
  unresolved("extension registration missing");
18745
- const manifest = parseConfig(raw, join27(root2, "gemini-extension.json"));
18746
- 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)
18747
18931
  unresolved("extension hooks or linked registration require review");
18748
- roots.add(join27(root2, "skills"));
18932
+ roots.add(join28(root2, "skills"));
18749
18933
  }
18750
18934
  }
18751
- 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))) } : {} };
18752
18936
  }
18753
18937
 
18754
18938
  // src/lib/authoring-path.ts
18755
18939
  var NATIVE_DISCOVERY_ROOTS = NATIVE_SKILL_ROOTS.map(([, path]) => path.split("/"));
18756
18940
  function pathParts(path) {
18757
- return resolve16(path).split(sep7).filter(Boolean);
18941
+ return resolve17(path).split(sep7).filter(Boolean);
18758
18942
  }
18759
18943
  function nativeRootMatch(path) {
18760
18944
  const parts = pathParts(path);
@@ -18768,24 +18952,24 @@ function nativeRootMatch(path) {
18768
18952
  return;
18769
18953
  }
18770
18954
  function physicalPath(path) {
18771
- let cursor = resolve16(path);
18955
+ let cursor = resolve17(path);
18772
18956
  const suffix = [];
18773
- while (!lstatSync22(cursor, { throwIfNoEntry: false })) {
18957
+ while (!lstatSync23(cursor, { throwIfNoEntry: false })) {
18774
18958
  suffix.unshift(basename10(cursor));
18775
18959
  const parent = dirname18(cursor);
18776
18960
  if (parent === cursor)
18777
- return resolve16(path);
18961
+ return resolve17(path);
18778
18962
  cursor = parent;
18779
18963
  }
18780
- return resolve16(realpathSync8(cursor), ...suffix);
18964
+ return resolve17(realpathSync9(cursor), ...suffix);
18781
18965
  }
18782
18966
  function contains(root2, path) {
18783
18967
  return path === root2 || path.startsWith(root2 + sep7);
18784
18968
  }
18785
18969
  function configuredRoots(home, explicitHome) {
18786
- const homeData = join28(home, ".hasna", "skills");
18970
+ const homeData = join29(home, ".hasna", "skills");
18787
18971
  const homePolicy = readManagedSkillPolicySnapshot(homeData);
18788
- const configuredPolicy = !explicitHome && resolve16(getDataDirReadOnly()) !== resolve16(homeData) ? readManagedSkillPolicySnapshot(getDataDirReadOnly()) : null;
18972
+ const configuredPolicy = !explicitHome && resolve17(getDataDirReadOnly()) !== resolve17(homeData) ? readManagedSkillPolicySnapshot(getDataDirReadOnly()) : null;
18789
18973
  const policy = homePolicy ?? configuredPolicy;
18790
18974
  const bridge = policy?.value.bridge;
18791
18975
  if (bridge && (typeof bridge.home !== "string" || physicalPath(bridge.home) !== physicalPath(home))) {
@@ -18814,8 +18998,8 @@ function configuredRoots(home, explicitHome) {
18814
18998
  admitBinding(binding);
18815
18999
  continue;
18816
19000
  }
18817
- const nativeHome = join28(home, agent === "opencode" ? ".config/opencode" : `.${agent}`);
18818
- if (!lstatSync22(nativeHome, { throwIfNoEntry: false }))
19001
+ const nativeHome = join29(home, agent === "opencode" ? ".config/opencode" : `.${agent}`);
19002
+ if (!lstatSync23(nativeHome, { throwIfNoEntry: false }))
18819
19003
  continue;
18820
19004
  roots.push(...resolveAgentDiscovery({ home, agent, canonical: physicalPath }).roots);
18821
19005
  }
@@ -18824,22 +19008,22 @@ function configuredRoots(home, explicitHome) {
18824
19008
  function assertPortableAuthoringPath(path, options = {}) {
18825
19009
  if (typeof path !== "string" || !path || path.includes("\x00"))
18826
19010
  throw new Error("Invalid portable authoring path");
18827
- const lexical = resolve16(path);
19011
+ const lexical = resolve17(path);
18828
19012
  const physical = physicalPath(lexical);
18829
19013
  const candidates = [lexical, physical];
18830
19014
  let native = candidates.map(nativeRootMatch).find(Boolean);
18831
- const home = resolve16(options.homeDir ?? homedir6());
18832
- const defaults = NATIVE_DISCOVERY_ROOTS.map((parts) => join28(home, ...parts));
18833
- 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"));
18834
19018
  for (const root2 of defaults) {
18835
- 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)))
18836
19020
  native ??= root2;
18837
19021
  }
18838
19022
  if (native) {
18839
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.`);
18840
19024
  }
18841
19025
  for (const root2 of configuredRoots(home, options.homeDir !== undefined)) {
18842
- 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))) {
18843
19027
  throw new Error(`Refusing portable Skills authoring below configured native discovery root: ${lexical}`);
18844
19028
  }
18845
19029
  }
@@ -18851,15 +19035,15 @@ function getPortableSkillsRoot(options = {}) {
18851
19035
  function resolvePortableSkillsRoot(options, migrate) {
18852
19036
  if (options.rootDir)
18853
19037
  return options.rootDir;
18854
- const appDir = options.homeDir ? join29(options.homeDir, ".hasna", "skills") : migrate ? getDataDir() : getDataDirReadOnly();
18855
- 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);
18856
19040
  if (isOwnerLayoutMigrated(appDir) && safeIsDirectory(cache))
18857
19041
  return cache;
18858
- const installed = join29(appDir, INSTALLED_SKILLS_DIRNAME);
19042
+ const installed = join30(appDir, INSTALLED_SKILLS_DIRNAME);
18859
19043
  return installed;
18860
19044
  }
18861
19045
  function getPortableSkillPath(name, options = {}) {
18862
- return join29(getPortableSkillsRoot(options), normalizePortableSkillName(name));
19046
+ return join30(getPortableSkillsRoot(options), normalizePortableSkillName(name));
18863
19047
  }
18864
19048
  function findPortableSkill(name, options = {}) {
18865
19049
  let normalized;
@@ -18869,7 +19053,7 @@ function findPortableSkill(name, options = {}) {
18869
19053
  return null;
18870
19054
  }
18871
19055
  const path = getPortableSkillPath(normalized, options);
18872
- if (!existsSync13(path) || !statSync10(path).isDirectory())
19056
+ if (!existsSync14(path) || !statSync10(path).isDirectory())
18873
19057
  return null;
18874
19058
  try {
18875
19059
  return summarizePortableSkill(path, normalized);
@@ -18885,7 +19069,7 @@ function listPortableSkills(options = {}) {
18885
19069
  for (const entry of readdirSync11(root2).sort()) {
18886
19070
  if (entry.startsWith("."))
18887
19071
  continue;
18888
- const path = join29(root2, entry);
19072
+ const path = join30(root2, entry);
18889
19073
  if (!safeIsDirectory(path))
18890
19074
  continue;
18891
19075
  try {
@@ -18919,11 +19103,11 @@ function isOfficialSkillName(name) {
18919
19103
  function scaffoldPortableSkill(name, options = {}) {
18920
19104
  const skillName = normalizeNewPortableSkillName(name);
18921
19105
  const readOnlyRoot = resolvePortableSkillsRoot(options, false);
18922
- assertPortableAuthoringPath(join29(readOnlyRoot, skillName), options);
19106
+ assertPortableAuthoringPath(join30(readOnlyRoot, skillName), options);
18923
19107
  const root2 = getPortableSkillsRoot(options);
18924
- const skillPath = join29(root2, skillName);
19108
+ const skillPath = join30(root2, skillName);
18925
19109
  assertPortableAuthoringPath(skillPath, options);
18926
- if (existsSync13(skillPath)) {
19110
+ if (existsSync14(skillPath)) {
18927
19111
  if (!options.overwrite)
18928
19112
  throw new Error(`Skill '${skillName}' already exists at ${skillPath}`);
18929
19113
  rmSync4(skillPath, { recursive: true, force: true });
@@ -18941,7 +19125,7 @@ function scaffoldPortableSkill(name, options = {}) {
18941
19125
  }
18942
19126
  function portPortableSkillDirectory(sourceDir, options = {}) {
18943
19127
  const absoluteSource = normalize4(sourceDir);
18944
- if (!existsSync13(absoluteSource) || !statSync10(absoluteSource).isDirectory()) {
19128
+ if (!existsSync14(absoluteSource) || !statSync10(absoluteSource).isDirectory()) {
18945
19129
  throw new Error(`Import directory not found: ${sourceDir}`);
18946
19130
  }
18947
19131
  const continueOnError = options.continueOnError ?? true;
@@ -18955,7 +19139,7 @@ function portPortableSkillDirectory(sourceDir, options = {}) {
18955
19139
  const skipped = [];
18956
19140
  const entries = readdirSync11(absoluteSource, { withFileTypes: true }).map((entry) => entry.name).filter((entryName) => !entryName.startsWith(".")).sort();
18957
19141
  for (const entryName of entries) {
18958
- const childPath = join29(absoluteSource, entryName);
19142
+ const childPath = join30(absoluteSource, entryName);
18959
19143
  if (!safeIsDirectory(childPath))
18960
19144
  continue;
18961
19145
  if (!isSkillCandidate(childPath)) {
@@ -18984,19 +19168,19 @@ function portPortableSkillDirectory(sourceDir, options = {}) {
18984
19168
  };
18985
19169
  }
18986
19170
  function isSkillCandidate(dir) {
18987
- 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"));
18988
19172
  }
18989
19173
  function physicalImportPath(path) {
18990
- let ancestor = resolve17(path);
19174
+ let ancestor = resolve18(path);
18991
19175
  const suffix = [];
18992
- while (!existsSync13(ancestor)) {
19176
+ while (!existsSync14(ancestor)) {
18993
19177
  suffix.unshift(basename11(ancestor));
18994
19178
  ancestor = dirname19(ancestor);
18995
19179
  }
18996
- return join29(realpathSync9(ancestor), ...suffix);
19180
+ return join30(realpathSync10(ancestor), ...suffix);
18997
19181
  }
18998
19182
  function assertDisjointImportPaths(source, destination) {
18999
- const sourcePath = realpathSync9(source);
19183
+ const sourcePath = realpathSync10(source);
19000
19184
  const destinationPath = physicalImportPath(destination);
19001
19185
  const contains2 = (parent, child) => {
19002
19186
  const path = relative6(parent, child);
@@ -19008,7 +19192,7 @@ function assertDisjointImportPaths(source, destination) {
19008
19192
  }
19009
19193
  function portPortableSkill(sourcePath, options = {}) {
19010
19194
  const absoluteSource = normalize4(sourcePath);
19011
- if (!existsSync13(absoluteSource) || !statSync10(absoluteSource).isDirectory()) {
19195
+ if (!existsSync14(absoluteSource) || !statSync10(absoluteSource).isDirectory()) {
19012
19196
  throw new Error(`Skill source directory not found: ${sourcePath}`);
19013
19197
  }
19014
19198
  const inferred = readPortableSkillManifestForImport(absoluteSource);
@@ -19019,14 +19203,14 @@ function portPortableSkill(sourcePath, options = {}) {
19019
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.`;
19020
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.`);
19021
19205
  }
19022
- const readOnlyDestination = join29(resolvePortableSkillsRoot(options, false), skillName);
19206
+ const readOnlyDestination = join30(resolvePortableSkillsRoot(options, false), skillName);
19023
19207
  assertPortableAuthoringPath(readOnlyDestination, options);
19024
19208
  assertDisjointImportPaths(absoluteSource, readOnlyDestination);
19025
19209
  const root2 = getPortableSkillsRoot(options);
19026
- const destination = join29(root2, skillName);
19210
+ const destination = join30(root2, skillName);
19027
19211
  assertPortableAuthoringPath(destination, options);
19028
19212
  assertDisjointImportPaths(absoluteSource, destination);
19029
- if (existsSync13(destination)) {
19213
+ if (existsSync14(destination)) {
19030
19214
  if (!options.overwrite)
19031
19215
  throw new Error(`Skill '${skillName}' already exists at ${destination}`);
19032
19216
  rmSync4(destination, { recursive: true, force: true });
@@ -19068,13 +19252,13 @@ function buildCorpusManifest(input, name) {
19068
19252
  function writeCorpusSkill(input, options = {}) {
19069
19253
  const name = normalizePortableSkillName(input.name);
19070
19254
  const readOnlyRoot = resolvePortableSkillsRoot(options, false);
19071
- assertPortableAuthoringPath(join29(readOnlyRoot, name), options);
19255
+ assertPortableAuthoringPath(join30(readOnlyRoot, name), options);
19072
19256
  const root2 = getPortableSkillsRoot(options);
19073
- const skillPath = join29(root2, name);
19257
+ const skillPath = join30(root2, name);
19074
19258
  assertPortableAuthoringPath(skillPath, options);
19075
- const created = !existsSync13(skillPath);
19259
+ const created = !existsSync14(skillPath);
19076
19260
  mkdirSync9(skillPath, { recursive: true });
19077
- writeFileSync8(join29(skillPath, "SKILL.md"), input.skillMd);
19261
+ writeFileSync8(join30(skillPath, "SKILL.md"), input.skillMd);
19078
19262
  const manifest = buildCorpusManifest(input, name);
19079
19263
  writeSkillJsonWithHash(skillPath, manifest);
19080
19264
  return { name, path: skillPath, manifest, created };
@@ -19082,22 +19266,22 @@ function writeCorpusSkill(input, options = {}) {
19082
19266
  function installCorpusSkillAtomically(input, options = {}) {
19083
19267
  const name = normalizePortableSkillName(input.name);
19084
19268
  const readOnlyRoot = resolvePortableSkillsRoot(options, false);
19085
- assertPortableAuthoringPath(join29(readOnlyRoot, name), options);
19269
+ assertPortableAuthoringPath(join30(readOnlyRoot, name), options);
19086
19270
  const root2 = getPortableSkillsRoot(options);
19087
- const target = join29(root2, name);
19271
+ const target = join30(root2, name);
19088
19272
  assertPortableAuthoringPath(target, options);
19089
- const created = !existsSync13(target);
19273
+ const created = !existsSync14(target);
19090
19274
  mkdirSync9(root2, { recursive: true });
19091
- const staging = mkdtempSync2(join29(root2, `.pull-${name}-`));
19275
+ const staging = mkdtempSync2(join30(root2, `.pull-${name}-`));
19092
19276
  let moved = false;
19093
19277
  let backup = null;
19094
19278
  try {
19095
- writeFileSync8(join29(staging, "SKILL.md"), input.skillMd);
19279
+ writeFileSync8(join30(staging, "SKILL.md"), input.skillMd);
19096
19280
  const manifest = buildCorpusManifest(input, name);
19097
19281
  writeSkillJsonWithHash(staging, manifest);
19098
- if (existsSync13(target)) {
19099
- backup = mkdtempSync2(join29(root2, `.pull-backup-${name}-`));
19100
- renameSync5(target, join29(backup, name));
19282
+ if (existsSync14(target)) {
19283
+ backup = mkdtempSync2(join30(root2, `.pull-backup-${name}-`));
19284
+ renameSync5(target, join30(backup, name));
19101
19285
  moved = true;
19102
19286
  }
19103
19287
  renameSync5(staging, target);
@@ -19106,9 +19290,9 @@ function installCorpusSkillAtomically(input, options = {}) {
19106
19290
  return { name, path: target, manifest, created };
19107
19291
  } catch (error) {
19108
19292
  rmSync4(staging, { recursive: true, force: true });
19109
- if (moved && backup && existsSync13(join29(backup, name))) {
19293
+ if (moved && backup && existsSync14(join30(backup, name))) {
19110
19294
  try {
19111
- renameSync5(join29(backup, name), target);
19295
+ renameSync5(join30(backup, name), target);
19112
19296
  } catch {}
19113
19297
  }
19114
19298
  throw error;
@@ -19120,10 +19304,10 @@ function validatePortableSkillDirectory(name, skillPath) {
19120
19304
  const issues = [...base.issues];
19121
19305
  const warnings = [...base.warnings];
19122
19306
  let manifest;
19123
- if (existsSync13(skillPath)) {
19124
- const skillJsonPath = join29(skillPath, "skill.json");
19125
- const skillMdPath = join29(skillPath, "SKILL.md");
19126
- 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)) {
19127
19311
  add3(issues, "portable.manifest_missing", "Missing portable manifest: expected SKILL.md frontmatter and/or skill.json");
19128
19312
  }
19129
19313
  try {
@@ -19142,7 +19326,7 @@ function validatePortableSkillDirectory(name, skillPath) {
19142
19326
  add3(issues, "portable.version_missing", "Portable manifest missing version");
19143
19327
  }
19144
19328
  const contractIssues = validatePortableManifestContract(manifest, {
19145
- strict: existsSync13(join29(skillPath, "skill.json")),
19329
+ strict: existsSync14(join30(skillPath, "skill.json")),
19146
19330
  skillPath
19147
19331
  });
19148
19332
  for (const issue of contractIssues)
@@ -19182,8 +19366,8 @@ function validatePortableSkillDirectory(name, skillPath) {
19182
19366
  add3(issues, "portable.command_entry_unsafe", `Command '${command.name}' entry '${command.entry}' must stay inside the skill directory`);
19183
19367
  continue;
19184
19368
  }
19185
- const entryPath = join29(skillPath, command.entry);
19186
- if (!existsSync13(entryPath))
19369
+ const entryPath = join30(skillPath, command.entry);
19370
+ if (!existsSync14(entryPath))
19187
19371
  add3(issues, "portable.command_entry_missing", `Command '${command.name}' entry '${command.entry}' is missing`);
19188
19372
  else if (statSync10(entryPath).isDirectory())
19189
19373
  add3(issues, "portable.command_entry_directory", `Command '${command.name}' entry '${command.entry}' must be a file`);
@@ -19193,7 +19377,7 @@ function validatePortableSkillDirectory(name, skillPath) {
19193
19377
  } catch (error) {
19194
19378
  add3(issues, "portable.manifest_invalid", error.message);
19195
19379
  }
19196
- if (manifest?.kind !== "instruction" && !existsSync13(join29(skillPath, "AGENTS.md"))) {
19380
+ if (manifest?.kind !== "instruction" && !existsSync14(join30(skillPath, "AGENTS.md"))) {
19197
19381
  add3(issues, "portable.agents_missing", "Missing AGENTS.md with build-out instructions for coding agents");
19198
19382
  }
19199
19383
  }
@@ -19229,13 +19413,13 @@ async function runPortableSkill(name, args, options = {}) {
19229
19413
  if (!isSafeRelativePath2(command.entry)) {
19230
19414
  return { exitCode: 1, error: `Portable skill '${name}' command entry is unsafe` };
19231
19415
  }
19232
- const entryPath = join29(skill.path, command.entry);
19233
- if (!existsSync13(entryPath)) {
19416
+ const entryPath = join30(skill.path, command.entry);
19417
+ if (!existsSync14(entryPath)) {
19234
19418
  return { exitCode: 1, error: `Entry point '${command.entry}' not found in portable skill '${name}'` };
19235
19419
  }
19236
- const pkgPath = join29(skill.path, "package.json");
19237
- const nodeModules = join29(skill.path, "node_modules");
19238
- 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)) {
19239
19423
  const install = Bun.spawn(["bun", "install", "--no-save"], {
19240
19424
  cwd: skill.path,
19241
19425
  stdout: "pipe",
@@ -19483,7 +19667,7 @@ function parseSkillMdFrontmatter(content) {
19483
19667
  return Object.keys(result).length > 0 ? result : null;
19484
19668
  }
19485
19669
  function discoverSkillsInDir(dir, source = "custom") {
19486
- if (!existsSync14(dir))
19670
+ if (!existsSync15(dir))
19487
19671
  return [];
19488
19672
  const result = [];
19489
19673
  try {
@@ -19491,8 +19675,8 @@ function discoverSkillsInDir(dir, source = "custom") {
19491
19675
  for (const entry of entries) {
19492
19676
  if (!entry.isDirectory())
19493
19677
  continue;
19494
- const skillMdPath = join30(dir, entry.name, "SKILL.md");
19495
- if (!existsSync14(skillMdPath))
19678
+ const skillMdPath = join31(dir, entry.name, "SKILL.md");
19679
+ if (!existsSync15(skillMdPath))
19496
19680
  continue;
19497
19681
  let content;
19498
19682
  try {
@@ -19511,7 +19695,7 @@ function discoverSkillsInDir(dir, source = "custom") {
19511
19695
  category: fm.category || "Development Tools",
19512
19696
  tags: fm.tags || [],
19513
19697
  ...fm.kind ? { kind: fm.kind } : {},
19514
- ...isHostedMetadataSkillDir(join30(dir, entry.name)) ? { serverOwned: true } : {},
19698
+ ...isHostedMetadataSkillDir(join31(dir, entry.name)) ? { serverOwned: true } : {},
19515
19699
  source
19516
19700
  });
19517
19701
  }
@@ -19520,16 +19704,16 @@ function discoverSkillsInDir(dir, source = "custom") {
19520
19704
  }
19521
19705
  function findExtensionSkillPath(name) {
19522
19706
  const config = loadConfig();
19523
- if (!config.extensionsDir || !existsSync14(config.extensionsDir))
19707
+ if (!config.extensionsDir || !existsSync15(config.extensionsDir))
19524
19708
  return null;
19525
19709
  try {
19526
19710
  const entries = readdirSync12(config.extensionsDir, { withFileTypes: true });
19527
19711
  for (const entry of entries) {
19528
19712
  if (!entry.isDirectory())
19529
19713
  continue;
19530
- const skillDir = join30(config.extensionsDir, entry.name);
19531
- const skillMdPath = join30(skillDir, "SKILL.md");
19532
- if (!existsSync14(skillMdPath))
19714
+ const skillDir = join31(config.extensionsDir, entry.name);
19715
+ const skillMdPath = join31(skillDir, "SKILL.md");
19716
+ if (!existsSync15(skillMdPath))
19533
19717
  continue;
19534
19718
  let content;
19535
19719
  try {
@@ -19576,21 +19760,21 @@ function clearRegistryCache() {
19576
19760
  registryCacheTime = 0;
19577
19761
  registryCacheKey = null;
19578
19762
  }
19579
- function getSkillsByCategory(category) {
19580
- return loadRegistry().filter((s) => s.category === category);
19763
+ function getSkillsByCategory(category, catalog) {
19764
+ return (catalog ?? loadRegistry()).filter((s) => s.category === category);
19581
19765
  }
19582
19766
  function getSkill(name) {
19583
19767
  const registry = loadRegistry();
19584
19768
  const slug = normalizeSkillSlug(name);
19585
19769
  return registry.find((s) => s.name === slug) ?? registry.find((s) => s.name === resolveSkillAlias(slug));
19586
19770
  }
19587
- function getSkillsByTag(tag) {
19771
+ function getSkillsByTag(tag, catalog) {
19588
19772
  const needle = tag.toLowerCase();
19589
- 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)));
19590
19774
  }
19591
- function getAllTags() {
19775
+ function getAllTags(catalog) {
19592
19776
  const tagSet = new Set;
19593
- for (const skill of loadRegistry()) {
19777
+ for (const skill of catalog ?? loadRegistry()) {
19594
19778
  for (const tag of skill.tags)
19595
19779
  tagSet.add(tag.toLowerCase());
19596
19780
  }
@@ -19598,20 +19782,20 @@ function getAllTags() {
19598
19782
  }
19599
19783
  // src/lib/run-state.ts
19600
19784
  import { createHash as createHash19, randomBytes } from "crypto";
19601
- import { existsSync as existsSync15, mkdirSync as mkdirSync10, readFileSync as readFileSync19, readdirSync as readdirSync13, statSync as statSync11, writeFileSync as writeFileSync9 } from "fs";
19602
- 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";
19603
19787
  function createSkillRun(params, targetDir = process.cwd()) {
19604
19788
  const now = new Date;
19605
19789
  const id = createRunId(now);
19606
19790
  const day = now.toISOString().slice(0, 10);
19607
19791
  const skillName = normalizeSkillName(params.skill);
19608
19792
  const root2 = getProjectStateDir(targetDir);
19609
- const runDir = join31(root2, "runs", day, id);
19610
- const logsDir = join31(runDir, "logs");
19611
- 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);
19612
19796
  mkdirSync10(logsDir, { recursive: true });
19613
19797
  mkdirSync10(exportDir, { recursive: true });
19614
- mkdirSync10(join31(root2, "tmp"), { recursive: true });
19798
+ mkdirSync10(join32(root2, "tmp"), { recursive: true });
19615
19799
  const record3 = {
19616
19800
  id,
19617
19801
  skill: skillName,
@@ -19663,27 +19847,27 @@ function updateSkillRun(context, patch) {
19663
19847
  return context.record;
19664
19848
  }
19665
19849
  function writeRunLogs(context, stdout = "", stderr = "") {
19666
- writeFileSync9(join31(context.logsDir, "stdout.log"), stdout);
19667
- writeFileSync9(join31(context.logsDir, "stderr.log"), stderr);
19850
+ writeFileSync9(join32(context.logsDir, "stdout.log"), stdout);
19851
+ writeFileSync9(join32(context.logsDir, "stderr.log"), stderr);
19668
19852
  }
19669
19853
  function appendRunEvent(context, event, data = {}) {
19670
19854
  const line = JSON.stringify({ ts: new Date().toISOString(), event, ...data }) + `
19671
19855
  `;
19672
- const path = join31(context.runDir, "events.ndjson");
19673
- 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") : "";
19674
19858
  writeFileSync9(path, previous + line);
19675
19859
  }
19676
19860
  function listSkillRuns(targetDir = process.cwd(), limit = 50) {
19677
- const runsRoot = join31(getProjectStateDir(targetDir), "runs");
19678
- if (!existsSync15(runsRoot))
19861
+ const runsRoot = join32(getProjectStateDir(targetDir), "runs");
19862
+ if (!existsSync16(runsRoot))
19679
19863
  return [];
19680
19864
  const records = [];
19681
19865
  for (const day of readdirSync13(runsRoot).sort().reverse()) {
19682
- const dayDir = join31(runsRoot, day);
19866
+ const dayDir = join32(runsRoot, day);
19683
19867
  if (!statSync11(dayDir).isDirectory())
19684
19868
  continue;
19685
19869
  for (const runId of readdirSync13(dayDir).sort().reverse()) {
19686
- const record3 = readRunRecord(join31(dayDir, runId));
19870
+ const record3 = readRunRecord(join32(dayDir, runId));
19687
19871
  if (record3)
19688
19872
  records.push(record3);
19689
19873
  if (records.length >= limit)
@@ -19693,29 +19877,29 @@ function listSkillRuns(targetDir = process.cwd(), limit = 50) {
19693
19877
  return records;
19694
19878
  }
19695
19879
  function findSkillRun(runId, targetDir = process.cwd()) {
19696
- const runsRoot = join31(getProjectStateDir(targetDir), "runs");
19697
- if (!existsSync15(runsRoot))
19880
+ const runsRoot = join32(getProjectStateDir(targetDir), "runs");
19881
+ if (!existsSync16(runsRoot))
19698
19882
  return null;
19699
19883
  for (const day of readdirSync13(runsRoot)) {
19700
- const record3 = readRunRecord(join31(runsRoot, day, runId));
19884
+ const record3 = readRunRecord(join32(runsRoot, day, runId));
19701
19885
  if (record3)
19702
19886
  return record3;
19703
19887
  }
19704
19888
  return null;
19705
19889
  }
19706
19890
  function getRunExportDir(runId, skill, targetDir = process.cwd()) {
19707
- return join31(getProjectStateDir(targetDir), "exports", normalizeSkillName(skill), runId);
19891
+ return join32(getProjectStateDir(targetDir), "exports", normalizeSkillName(skill), runId);
19708
19892
  }
19709
19893
  function writeRunRecord(context) {
19710
- 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) + `
19711
19895
  `);
19712
19896
  }
19713
19897
  function writeArtifactsManifest(context, artifacts) {
19714
- 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) + `
19715
19899
  `);
19716
19900
  }
19717
19901
  function collectRunArtifacts(context) {
19718
- if (!existsSync15(context.exportDir))
19902
+ if (!existsSync16(context.exportDir))
19719
19903
  return [];
19720
19904
  const artifacts = [];
19721
19905
  for (const path of walkFiles(context.exportDir)) {
@@ -19731,8 +19915,8 @@ function collectRunArtifacts(context) {
19731
19915
  return artifacts.sort((a, b) => a.path.localeCompare(b.path));
19732
19916
  }
19733
19917
  function readRunRecord(runDir) {
19734
- const path = join31(runDir, "run.json");
19735
- if (!existsSync15(path))
19918
+ const path = join32(runDir, "run.json");
19919
+ if (!existsSync16(path))
19736
19920
  return null;
19737
19921
  try {
19738
19922
  return JSON.parse(readFileSync19(path, "utf-8"));
@@ -19743,7 +19927,7 @@ function readRunRecord(runDir) {
19743
19927
  function walkFiles(dir) {
19744
19928
  const files = [];
19745
19929
  for (const entry of readdirSync13(dir)) {
19746
- const full = join31(dir, entry);
19930
+ const full = join32(dir, entry);
19747
19931
  if (statSync11(full).isDirectory())
19748
19932
  files.push(...walkFiles(full));
19749
19933
  else
@@ -40148,10 +40332,10 @@ async function readQuoteUnavailableCode(response) {
40148
40332
  let timer;
40149
40333
  let deadlineExceeded = false;
40150
40334
  const expired = Symbol("quote body deadline");
40151
- const deadline = new Promise((resolve18) => {
40335
+ const deadline = new Promise((resolve19) => {
40152
40336
  timer = setTimeout(() => {
40153
40337
  deadlineExceeded = true;
40154
- resolve18(expired);
40338
+ resolve19(expired);
40155
40339
  reader.cancel().catch(() => {});
40156
40340
  }, 1500);
40157
40341
  });
@@ -41250,14 +41434,14 @@ async function createRemoteSkillsClient(env = process.env) {
41250
41434
  return connection ? new RemoteSkillsClient(connection.apiKey, connection.apiOrigin) : null;
41251
41435
  }
41252
41436
  // src/lib/scheduler.ts
41253
- import { existsSync as existsSync16, readFileSync as readFileSync20, writeFileSync as writeFileSync10, mkdirSync as mkdirSync11 } from "fs";
41254
- 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";
41255
41439
  function getSchedulesPath(targetDir = process.cwd()) {
41256
- return join32(targetDir, ".skills", "schedules.json");
41440
+ return join33(targetDir, ".skills", "schedules.json");
41257
41441
  }
41258
41442
  function loadSchedules(targetDir = process.cwd()) {
41259
41443
  const path = getSchedulesPath(targetDir);
41260
- if (existsSync16(path)) {
41444
+ if (existsSync17(path)) {
41261
41445
  try {
41262
41446
  return JSON.parse(readFileSync20(path, "utf-8"));
41263
41447
  } catch {}
@@ -41266,8 +41450,8 @@ function loadSchedules(targetDir = process.cwd()) {
41266
41450
  }
41267
41451
  function saveSchedules(data, targetDir = process.cwd()) {
41268
41452
  const path = getSchedulesPath(targetDir);
41269
- const dir = join32(targetDir, ".skills");
41270
- if (!existsSync16(dir))
41453
+ const dir = join33(targetDir, ".skills");
41454
+ if (!existsSync17(dir))
41271
41455
  mkdirSync11(dir, { recursive: true });
41272
41456
  writeFileSync10(path, JSON.stringify(data, null, 2));
41273
41457
  }
@@ -41482,8 +41666,8 @@ function revisionIdOfRecord(record8) {
41482
41666
  return revisionIdOf(record8);
41483
41667
  }
41484
41668
  // src/lib/pull.ts
41485
- import { existsSync as existsSync17, mkdirSync as mkdirSync12, mkdtempSync as mkdtempSync3, readFileSync as readFileSync21, renameSync as renameSync6, rmSync as rmSync5, writeFileSync as writeFileSync11 } from "fs";
41486
- 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";
41487
41671
 
41488
41672
  // src/lib/skill-bundles.ts
41489
41673
  import { createHmac, timingSafeEqual } from "crypto";
@@ -41577,7 +41761,7 @@ async function pullOne(client2, rawName, corpusOptions, verify) {
41577
41761
  return reconcileTombstone(slug2, corpusOptions);
41578
41762
  }
41579
41763
  if (bundleResponse.status === 404) {
41580
- const marker = readPullMarker(join33(getPortableSkillsRoot(corpusOptions), slug2));
41764
+ const marker = readPullMarker(join34(getPortableSkillsRoot(corpusOptions), slug2));
41581
41765
  if (isPublishedInstallMarker(marker)) {
41582
41766
  return { name: slug2, success: true, purged: true, removed: false };
41583
41767
  }
@@ -41616,7 +41800,7 @@ async function pullOne(client2, rawName, corpusOptions, verify) {
41616
41800
  return { name: slug2, success: false, error: `Skill '${slug2}' was not found on the configured Skills instance.` };
41617
41801
  }
41618
41802
  if (!meta3?.revisionId) {
41619
- const marker = readPullMarker(join33(getPortableSkillsRoot(corpusOptions), slug2));
41803
+ const marker = readPullMarker(join34(getPortableSkillsRoot(corpusOptions), slug2));
41620
41804
  if (isPublishedInstallMarker(marker)) {
41621
41805
  return { name: slug2, success: true, purged: true, removed: false };
41622
41806
  }
@@ -41673,7 +41857,7 @@ function provenRevision(meta3, slug2, bundle2) {
41673
41857
  return declared;
41674
41858
  }
41675
41859
  function reconcileTombstone(slug2, corpusOptions) {
41676
- const target = join33(getPortableSkillsRoot(corpusOptions), slug2);
41860
+ const target = join34(getPortableSkillsRoot(corpusOptions), slug2);
41677
41861
  if (!hasSkillsOwnershipMarker(target)) {
41678
41862
  return { name: slug2, success: true, tombstoned: true, removed: false, leftInPlace: true };
41679
41863
  }
@@ -41682,7 +41866,7 @@ function reconcileTombstone(slug2, corpusOptions) {
41682
41866
  }
41683
41867
  function readPullMarker(dir) {
41684
41868
  try {
41685
- return JSON.parse(readFileSync21(join33(dir, PULL_MARKER_FILE), "utf-8"));
41869
+ return JSON.parse(readFileSync21(join34(dir, PULL_MARKER_FILE), "utf-8"));
41686
41870
  } catch {
41687
41871
  return null;
41688
41872
  }
@@ -41817,14 +42001,14 @@ function verifyBundleResponseBytes(buffer, response, verify = {}) {
41817
42001
  function installBundleAtomically(name, entries, options = {}, marker = {}) {
41818
42002
  const root2 = getPortableSkillsRoot(options);
41819
42003
  mkdirSync12(root2, { recursive: true });
41820
- const target = join33(root2, name);
41821
- const created = !existsSync17(target);
41822
- 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}-`));
41823
42007
  let moved = false;
41824
42008
  let backup = null;
41825
42009
  try {
41826
42010
  for (const entry of entries) {
41827
- const destination = join33(staging, entry.path);
42011
+ const destination = join34(staging, entry.path);
41828
42012
  mkdirSync12(dirname20(destination), { recursive: true });
41829
42013
  writeFileSync11(destination, entry.bytes, { mode: entry.mode });
41830
42014
  }
@@ -41836,9 +42020,9 @@ function installBundleAtomically(name, entries, options = {}, marker = {}) {
41836
42020
  ...marker.signature ? { signature: marker.signature } : {},
41837
42021
  ...marker.revisionId ? { revisionId: marker.revisionId } : {}
41838
42022
  });
41839
- if (existsSync17(target)) {
41840
- backup = mkdtempSync3(join33(root2, `.pull-backup-${name}-`));
41841
- renameSync6(target, join33(backup, name));
42023
+ if (existsSync18(target)) {
42024
+ backup = mkdtempSync3(join34(root2, `.pull-backup-${name}-`));
42025
+ renameSync6(target, join34(backup, name));
41842
42026
  moved = true;
41843
42027
  }
41844
42028
  renameSync6(staging, target);
@@ -41846,9 +42030,9 @@ function installBundleAtomically(name, entries, options = {}, marker = {}) {
41846
42030
  rmSync5(backup, { recursive: true, force: true });
41847
42031
  } catch (error61) {
41848
42032
  rmSync5(staging, { recursive: true, force: true });
41849
- if (moved && backup && existsSync17(join33(backup, name))) {
42033
+ if (moved && backup && existsSync18(join34(backup, name))) {
41850
42034
  try {
41851
- renameSync6(join33(backup, name), target);
42035
+ renameSync6(join34(backup, name), target);
41852
42036
  } catch {}
41853
42037
  }
41854
42038
  throw error61;
@@ -41867,7 +42051,7 @@ function writePullMarker(dir, record8) {
41867
42051
  ...record8.revisionId ? { revisionId: record8.revisionId } : {},
41868
42052
  syncedAt: new Date().toISOString()
41869
42053
  };
41870
- writeFileSync11(join33(dir, PULL_MARKER_FILE), `${JSON.stringify(marker, null, 2)}
42054
+ writeFileSync11(join34(dir, PULL_MARKER_FILE), `${JSON.stringify(marker, null, 2)}
41871
42055
  `);
41872
42056
  }
41873
42057
  async function safeMeta(client2, slug2) {
@@ -42309,12 +42493,12 @@ class RemotePrivatePublicationsClient {
42309
42493
  previous = view;
42310
42494
  if (!["queued", "verifying"].includes(view.state) || Date.now() >= until)
42311
42495
  return view;
42312
- await new Promise((resolve18) => {
42496
+ await new Promise((resolve19) => {
42313
42497
  const timer = setTimeout(done, Math.min(1000, until - Date.now()));
42314
42498
  function done() {
42315
42499
  clearTimeout(timer);
42316
42500
  options.signal?.removeEventListener("abort", done);
42317
- resolve18();
42501
+ resolve19();
42318
42502
  }
42319
42503
  options.signal?.addEventListener("abort", done, { once: true });
42320
42504
  });
@@ -43870,16 +44054,16 @@ function clone3(value) {
43870
44054
  return JSON.parse(JSON.stringify(value));
43871
44055
  }
43872
44056
  // src/lib/feedback.ts
43873
- import { appendFileSync, existsSync as existsSync18, mkdirSync as mkdirSync14 } from "fs";
43874
- 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";
43875
44059
  import { Database } from "bun:sqlite";
43876
44060
  function getFeedbackDbPath() {
43877
- return join34(getDataDir(), "skills.db");
44061
+ return join35(getDataDir(), "skills.db");
43878
44062
  }
43879
44063
  function getFeedbackDb() {
43880
44064
  const dbPath = getFeedbackDbPath();
43881
44065
  const dir = dirname22(dbPath);
43882
- if (!existsSync18(dir))
44066
+ if (!existsSync19(dir))
43883
44067
  mkdirSync14(dir, { recursive: true });
43884
44068
  const db = new Database(dbPath);
43885
44069
  db.exec("PRAGMA journal_mode = WAL");
@@ -43906,9 +44090,9 @@ function saveFeedback(input2) {
43906
44090
  throw new Error("Feedback message is required");
43907
44091
  const category = input2.category ?? "general";
43908
44092
  if (isApiMode()) {
43909
- const path = join34(getDataDir(), "feedback.jsonl");
44093
+ const path = join35(getDataDir(), "feedback.jsonl");
43910
44094
  const dir = dirname22(path);
43911
- if (!existsSync18(dir))
44095
+ if (!existsSync19(dir))
43912
44096
  mkdirSync14(dir, { recursive: true });
43913
44097
  appendFileSync(path, JSON.stringify({ message, category, email: input2.email ?? null, agent: input2.agent ?? null, version: input2.version ?? null, createdAt: new Date().toISOString() }) + `
43914
44098
  `);
@@ -43932,14 +44116,14 @@ function isApiMode(env = process.env) {
43932
44116
  // src/lib/native-storage.ts
43933
44117
  import { createHash as createHash23, createHmac as createHmac2 } from "crypto";
43934
44118
  import {
43935
- existsSync as existsSync19,
44119
+ existsSync as existsSync20,
43936
44120
  mkdirSync as mkdirSync15,
43937
44121
  readFileSync as readFileSync23,
43938
44122
  readdirSync as readdirSync14,
43939
44123
  statSync as statSync12,
43940
44124
  writeFileSync as writeFileSync13
43941
44125
  } from "fs";
43942
- 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";
43943
44127
  var SKILLS_STORAGE_TABLES = [
43944
44128
  "skills_sync_records",
43945
44129
  "skills_sync_cursors"
@@ -44076,7 +44260,7 @@ function getSkillsNativeStorageStatus(options = {}) {
44076
44260
  local: {
44077
44261
  dataDir: getDataDir(),
44078
44262
  projectStateDir: getProjectStateDir(targetDir),
44079
- feedbackDbPath: join35(getDataDir(), "skills.db")
44263
+ feedbackDbPath: join36(getDataDir(), "skills.db")
44080
44264
  },
44081
44265
  remote: {
44082
44266
  databaseConfigured: Boolean(config2.databaseUrl),
@@ -44099,7 +44283,7 @@ function getStorageStatus(options = {}) {
44099
44283
  function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
44100
44284
  const projectStateDir = getProjectStateDir(targetDir);
44101
44285
  const files = [];
44102
- if (existsSync19(projectStateDir)) {
44286
+ if (existsSync20(projectStateDir)) {
44103
44287
  for (const filePath of walkFiles2(projectStateDir)) {
44104
44288
  const bytes = readFileSync23(filePath);
44105
44289
  const relativePath = toPosix(relative9(targetDir, filePath));
@@ -44126,7 +44310,7 @@ function importSkillsLocalSnapshot(snapshot2, targetDir = process.cwd(), options
44126
44310
  continue;
44127
44311
  }
44128
44312
  const absolutePath4 = resolveSnapshotPath(targetDir, file2.path);
44129
- if (existsSync19(absolutePath4) && !options.overwrite) {
44313
+ if (existsSync20(absolutePath4) && !options.overwrite) {
44130
44314
  skipped += 1;
44131
44315
  continue;
44132
44316
  }
@@ -44433,7 +44617,7 @@ function parsePositiveInteger(value) {
44433
44617
  function walkFiles2(dir) {
44434
44618
  const files = [];
44435
44619
  for (const entry of readdirSync14(dir)) {
44436
- const full = join35(dir, entry);
44620
+ const full = join36(dir, entry);
44437
44621
  const stats = statSync12(full);
44438
44622
  if (stats.isDirectory())
44439
44623
  files.push(...walkFiles2(full));
@@ -44450,7 +44634,7 @@ function resolveSnapshotPath(targetDir, snapshotPath) {
44450
44634
  if (!toPosix(normalizedPath).startsWith(".skills/")) {
44451
44635
  throw new Error(`Snapshot path must stay inside .skills: ${snapshotPath}`);
44452
44636
  }
44453
- return join35(targetDir, normalizedPath);
44637
+ return join36(targetDir, normalizedPath);
44454
44638
  }
44455
44639
  function normalizeS3Prefix(prefix) {
44456
44640
  return (prefix ?? "").trim().replace(/^\/+|\/+$/g, "");
@@ -44523,12 +44707,12 @@ import {
44523
44707
  statSync as statSync14,
44524
44708
  writeFileSync as writeFileSync14
44525
44709
  } from "fs";
44526
- 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";
44527
44711
 
44528
44712
  // src/lib/portable-snapshot-filter.ts
44529
44713
  import { readdirSync as readdirSync15, statSync as statSync13 } from "fs";
44530
44714
  import { homedir as homedir7 } from "os";
44531
- import { join as join36, sep as sep10 } from "path";
44715
+ import { join as join37, sep as sep10 } from "path";
44532
44716
  var SYNC_HOMES = [
44533
44717
  { name: "skills", subClass: "skills", agent: null },
44534
44718
  { name: "custom", subClass: "custom", agent: null },
@@ -44621,16 +44805,16 @@ function isPortableWithinSkill(relativeParts) {
44621
44805
  function homePathFor(definition, homesRoot) {
44622
44806
  const home = homesRoot ?? homedir7();
44623
44807
  if (definition.subClass === "skills" || definition.subClass === "custom") {
44624
- return join36(skillsDataRootForHome(home), definition.name);
44808
+ return join37(skillsDataRootForHome(home), definition.name);
44625
44809
  }
44626
44810
  if (definition.agent === "opencode") {
44627
- return join36(home, ".config", "opencode", "skills");
44811
+ return join37(home, ".config", "opencode", "skills");
44628
44812
  }
44629
- return join36(home, `.${definition.agent}`, "skills");
44813
+ return join37(home, `.${definition.agent}`, "skills");
44630
44814
  }
44631
44815
  function destinationFor(definition, stationId, relativePath) {
44632
- const category = definition.subClass === "agent-homes" ? join36("agent-homes", definition.agent ?? "") : definition.name;
44633
- 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));
44634
44818
  }
44635
44819
  function walkEntries(absoluteRoot) {
44636
44820
  let entries;
@@ -44641,7 +44825,7 @@ function walkEntries(absoluteRoot) {
44641
44825
  }
44642
44826
  const output2 = [];
44643
44827
  for (const entry of entries) {
44644
- const childFull = join36(absoluteRoot, entry.name);
44828
+ const childFull = join37(absoluteRoot, entry.name);
44645
44829
  if (entry.isSymbolicLink()) {
44646
44830
  output2.push({ kind: "symlink", relativePath: entry.name, fullPath: childFull });
44647
44831
  continue;
@@ -44652,7 +44836,7 @@ function walkEntries(absoluteRoot) {
44652
44836
  }
44653
44837
  const nested = walkEntries(childFull);
44654
44838
  for (const item of nested) {
44655
- output2.push({ ...item, relativePath: join36(entry.name, item.relativePath) });
44839
+ output2.push({ ...item, relativePath: join37(entry.name, item.relativePath) });
44656
44840
  }
44657
44841
  continue;
44658
44842
  }
@@ -44761,7 +44945,7 @@ function humanHomes(scanned) {
44761
44945
  }));
44762
44946
  }
44763
44947
  function writeStationSnapshot(options) {
44764
- const repoRoot = resolve18(options.repoRoot ?? process.cwd());
44948
+ const repoRoot = resolve19(options.repoRoot ?? process.cwd());
44765
44949
  const { scanned, plans, totalBytes } = planStationSnapshot(options);
44766
44950
  const manifestFiles = plans.map((plan) => ({
44767
44951
  relativePath: plan.source.relativePath,
@@ -44787,7 +44971,7 @@ function writeStationSnapshot(options) {
44787
44971
  const conflicts = [];
44788
44972
  const untouched = [];
44789
44973
  for (const plan of plans) {
44790
- const destination = resolve18(repoRoot, plan.destination);
44974
+ const destination = resolve19(repoRoot, plan.destination);
44791
44975
  const destinationRelative = relative10(repoRoot, destination);
44792
44976
  if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep11) || isAbsolute19(destinationRelative)) {
44793
44977
  throw new StationSnapshotError("DESTINATION_ESCAPE", `destination escapes repo root: ${plan.destination}`);
@@ -44810,7 +44994,7 @@ function writeStationSnapshot(options) {
44810
44994
  }
44811
44995
  let written = 0;
44812
44996
  for (const plan of untouched) {
44813
- const destination = resolve18(repoRoot, plan.destination);
44997
+ const destination = resolve19(repoRoot, plan.destination);
44814
44998
  mkdirSync16(dirname24(destination), { recursive: true });
44815
44999
  copyFileSync(plan.source.fullPath, destination);
44816
45000
  written += 1;
@@ -44829,7 +45013,7 @@ function writeStationSnapshot(options) {
44829
45013
  },
44830
45014
  files: manifestFiles
44831
45015
  };
44832
- const manifestPath = resolve18(repoRoot, "resources", options.stationId, "skills", "sync-manifest.json");
45016
+ const manifestPath = resolve19(repoRoot, "resources", options.stationId, "skills", "sync-manifest.json");
44833
45017
  mkdirSync16(dirname24(manifestPath), { recursive: true });
44834
45018
  writeFileSync14(manifestPath, `${JSON.stringify(manifest, null, 2)}
44835
45019
  `);
@@ -44850,7 +45034,7 @@ import {
44850
45034
  statSync as statSync15,
44851
45035
  writeFileSync as writeFileSync15
44852
45036
  } from "fs";
44853
- 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";
44854
45038
  var STATION_HYDRATION_MANIFEST_SCHEMA = "hasna.fleet-resources.skills-hydration-manifest/v1";
44855
45039
  var STATION_HYDRATION_PRODUCER = { name: "@hasna/skills", version: package_default.version };
44856
45040
  var MANIFEST_HASH_KEY_SEP = String.fromCharCode(0);
@@ -44858,11 +45042,11 @@ function fail(code2, message, detail = []) {
44858
45042
  throw new StationSnapshotError(code2, message, detail);
44859
45043
  }
44860
45044
  function snapshotRootFor(repoRoot, stationId) {
44861
- return join37(repoRoot, "resources", stationId, "skills");
45045
+ return join38(repoRoot, "resources", stationId, "skills");
44862
45046
  }
44863
45047
  function readSnapshotManifest(repoRoot, stationId) {
44864
45048
  const snapshotRoot = snapshotRootFor(repoRoot, stationId);
44865
- const manifestPath = join37(snapshotRoot, "sync-manifest.json");
45049
+ const manifestPath = join38(snapshotRoot, "sync-manifest.json");
44866
45050
  let manifest;
44867
45051
  try {
44868
45052
  manifest = JSON.parse(readFileSync26(manifestPath, "utf8"));
@@ -44889,7 +45073,7 @@ function planStationHydration(stationId, repoRoot) {
44889
45073
  const hashMismatches = [];
44890
45074
  const skippedByRule = [];
44891
45075
  for (const agent of SYNC_AGENTS) {
44892
- const agentRoot = join37(snapshotRoot, "agent-homes", agent);
45076
+ const agentRoot = join38(snapshotRoot, "agent-homes", agent);
44893
45077
  let identEntries;
44894
45078
  try {
44895
45079
  identEntries = readdirSync16(agentRoot, { withFileTypes: true });
@@ -44900,7 +45084,7 @@ function planStationHydration(stationId, repoRoot) {
44900
45084
  if (!identEntry.isDirectory() || identEntry.name.startsWith(".")) {
44901
45085
  continue;
44902
45086
  }
44903
- const identRoot = join37(agentRoot, identEntry.name);
45087
+ const identRoot = join38(agentRoot, identEntry.name);
44904
45088
  const entries = walkEntries(identRoot);
44905
45089
  for (const entry of entries) {
44906
45090
  const relativeParts = [identEntry.name, ...entry.relativePath.split(sep12)];
@@ -45051,8 +45235,8 @@ function skillSha256(skill) {
45051
45235
  `)).digest("hex");
45052
45236
  }
45053
45237
  function writeStationHydration(options) {
45054
- const repoRoot = resolve19(options.repoRoot ?? process.cwd());
45055
- const cacheRoot = resolve19(options.cacheRoot ?? resolveCorpusRoot());
45238
+ const repoRoot = resolve20(options.repoRoot ?? process.cwd());
45239
+ const cacheRoot = resolve20(options.cacheRoot ?? resolveCorpusRoot());
45056
45240
  const plan = planStationHydration(options.stationId, repoRoot);
45057
45241
  const resultSkills = plan.winners.map((skill) => ({
45058
45242
  ident: skill.ident,
@@ -45085,7 +45269,7 @@ function writeStationHydration(options) {
45085
45269
  const toWrite = [];
45086
45270
  for (const skill of plan.winners) {
45087
45271
  for (const file2 of skill.files) {
45088
- const destination = join37(cacheRoot, skill.ident, file2.withinIdent);
45272
+ const destination = join38(cacheRoot, skill.ident, file2.withinIdent);
45089
45273
  const digest3 = sha256File(file2.winner.fullPath);
45090
45274
  let existingDigest = null;
45091
45275
  try {
@@ -45127,7 +45311,7 @@ function writeStationHydration(options) {
45127
45311
  },
45128
45312
  skills: resultSkills
45129
45313
  };
45130
- const hydrationManifestPath = join37(dirname25(cacheRoot), `hydration-${options.stationId}.json`);
45314
+ const hydrationManifestPath = join38(dirname25(cacheRoot), `hydration-${options.stationId}.json`);
45131
45315
  mkdirSync17(dirname25(hydrationManifestPath), { recursive: true });
45132
45316
  writeFileSync15(hydrationManifestPath, `${JSON.stringify(hydration, null, 2)}
45133
45317
  `);
@@ -45139,23 +45323,23 @@ function writeStationHydration(options) {
45139
45323
  };
45140
45324
  }
45141
45325
  // src/lib/private-publication-recovery.ts
45142
- 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";
45143
- 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";
45144
45328
  import { randomUUID as randomUUID4 } from "crypto";
45145
45329
  var fail2 = () => {
45146
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.");
45147
45331
  };
45148
45332
  function safeDirectory(directory2) {
45149
- if (!isAbsolute20(directory2) || directory2 !== resolve20(directory2) || realpathSync10(directory2) !== directory2)
45333
+ if (!isAbsolute20(directory2) || directory2 !== resolve21(directory2) || realpathSync11(directory2) !== directory2)
45150
45334
  return fail2();
45151
45335
  for (let path = directory2;; path = dirname26(path)) {
45152
- const stat2 = lstatSync23(path);
45336
+ const stat2 = lstatSync24(path);
45153
45337
  if (!stat2.isDirectory() || stat2.isSymbolicLink())
45154
45338
  return fail2();
45155
45339
  if (path === dirname26(path))
45156
45340
  break;
45157
45341
  }
45158
- const own2 = lstatSync23(directory2);
45342
+ const own2 = lstatSync24(directory2);
45159
45343
  if ((own2.mode & 63) !== 0 || process.getuid && own2.uid !== process.getuid())
45160
45344
  return fail2();
45161
45345
  return { dev: own2.dev, ino: own2.ino };
@@ -45166,7 +45350,7 @@ function unchangedDirectory(directory2, identity4) {
45166
45350
  return fail2();
45167
45351
  }
45168
45352
  function readOwned(directory2, name, max) {
45169
- const identity4 = safeDirectory(directory2), file2 = join38(directory2, name);
45353
+ const identity4 = safeDirectory(directory2), file2 = join39(directory2, name);
45170
45354
  const fd = openSync13(file2, constants12.O_RDONLY | constants12.O_NOFOLLOW);
45171
45355
  try {
45172
45356
  const stat2 = fstatSync13(fd);
@@ -45190,7 +45374,7 @@ function readOwned(directory2, name, max) {
45190
45374
  }
45191
45375
  }
45192
45376
  function writeOwned(directory2, name, bytes) {
45193
- 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);
45194
45378
  try {
45195
45379
  writeFileSync16(fd, bytes);
45196
45380
  fsyncSync5(fd);
@@ -45203,7 +45387,7 @@ function save(directory2, value) {
45203
45387
  writeOwned(directory2, temporary, JSON.stringify(value) + `
45204
45388
  `);
45205
45389
  unchangedDirectory(directory2, identity4);
45206
- renameSync7(join38(directory2, temporary), join38(directory2, "receipt.json"));
45390
+ renameSync7(join39(directory2, temporary), join39(directory2, "receipt.json"));
45207
45391
  const fd = openSync13(directory2, constants12.O_RDONLY | constants12.O_NOFOLLOW);
45208
45392
  try {
45209
45393
  fsyncSync5(fd);
@@ -45216,7 +45400,7 @@ function bind(client2, receipt) {
45216
45400
  throw new PrivatePublicationError("PUBLICATION_IDENTITY_CHANGED", "The fresh session does not match the recovery directory's server, account and membership.");
45217
45401
  }
45218
45402
  async function locked(directory2, action) {
45219
- const identity4 = safeDirectory(directory2), file2 = join38(directory2, "operation.lock");
45403
+ const identity4 = safeDirectory(directory2), file2 = join39(directory2, "operation.lock");
45220
45404
  let fd;
45221
45405
  try {
45222
45406
  fd = openSync13(file2, constants12.O_WRONLY | constants12.O_CREAT | constants12.O_EXCL | constants12.O_NOFOLLOW, 384);
@@ -45232,7 +45416,7 @@ async function locked(directory2, action) {
45232
45416
  } finally {
45233
45417
  closeSync13(fd);
45234
45418
  unchangedDirectory(directory2, identity4);
45235
- const now = lstatSync23(file2);
45419
+ const now = lstatSync24(file2);
45236
45420
  if (now.dev !== lock.dev || now.ino !== lock.ino || !now.isFile() || now.isSymbolicLink())
45237
45421
  fail2();
45238
45422
  unlinkSync5(file2);
@@ -45284,7 +45468,7 @@ async function preparePrivatePublication(client2, sourceDirectory, recoveryDirec
45284
45468
  phase: "prepared",
45285
45469
  intent: null
45286
45470
  };
45287
- if (!isAbsolute20(recoveryDirectory) || resolve20(recoveryDirectory) !== recoveryDirectory || realpathSync10(dirname26(recoveryDirectory)) !== dirname26(recoveryDirectory))
45471
+ if (!isAbsolute20(recoveryDirectory) || resolve21(recoveryDirectory) !== recoveryDirectory || realpathSync11(dirname26(recoveryDirectory)) !== dirname26(recoveryDirectory))
45288
45472
  return fail2();
45289
45473
  mkdirSync18(recoveryDirectory, { mode: 448 });
45290
45474
  safeDirectory(recoveryDirectory);
@@ -45376,7 +45560,7 @@ async function inspectLocked(client2, directory2, cancel) {
45376
45560
  }
45377
45561
  // src/lib/skill-context.ts
45378
45562
  import { createHash as createHash26 } from "crypto";
45379
- import { resolve as resolve21 } from "path";
45563
+ import { resolve as resolve22 } from "path";
45380
45564
  function words(value) {
45381
45565
  return new Set(value.toLowerCase().match(/[a-z0-9]+/g) ?? []);
45382
45566
  }
@@ -45398,7 +45582,11 @@ async function buildSkillContext(input2, options = {}) {
45398
45582
  }
45399
45583
  const profileId = input2.profileId ?? "default";
45400
45584
  const sessionId = input2.sessionId ? input2.agentId ? `${input2.sessionId}:${input2.agentId}` : input2.sessionId : undefined;
45401
- 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) };
45402
45590
  const resolved = await resolveSelectionContext(profileId, resolverOptions);
45403
45591
  const profile = resolved.receipt.profile;
45404
45592
  const explicit = new Set((input2.skills ?? []).map((spec) => selectionKey(exactProfileSelection(spec, profile))));
@@ -45411,7 +45599,7 @@ async function buildSkillContext(input2, options = {}) {
45411
45599
  const promptWords = words(prompt);
45412
45600
  const promptNames = new Set(prompt.toLowerCase().match(/[a-z0-9]+(?:-[a-z0-9]+)*/g) ?? []);
45413
45601
  const loaded = new Set(resolved.session?.loaded ?? []);
45414
- const paths = [...input2.paths ?? [], ...input2.cwd ? [input2.cwd, resolve21(input2.cwd)] : []];
45602
+ const paths = [...input2.paths ?? [], ...input2.cwd ? [input2.cwd, resolve22(input2.cwd)] : []];
45415
45603
  const candidates = profile.selections.map((selection) => {
45416
45604
  const key = selectionKey(selection);
45417
45605
  if (input2.restore && loaded.has(key))