@hasna/skills 0.10.10 → 0.10.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/index.js CHANGED
@@ -36868,7 +36868,7 @@ var package_default;
36868
36868
  var init_package = __esm(() => {
36869
36869
  package_default = {
36870
36870
  name: "@hasna/skills",
36871
- version: "0.10.10",
36871
+ version: "0.10.13",
36872
36872
  description: "Skills library for AI coding agents",
36873
36873
  type: "module",
36874
36874
  bin: {
@@ -50694,6 +50694,195 @@ var init_agent_bridge = __esm(() => {
50694
50694
 
50695
50695
  // src/lib/agent-codex.ts
50696
50696
  import { isDeepStrictEqual as isDeepStrictEqual3 } from "util";
50697
+ function splitTopLevel(value, delimiter) {
50698
+ const result2 = [];
50699
+ let start = 0, square = 0, curly = 0, quote = null, escaped = false;
50700
+ for (let index = 0;index < value.length; index++) {
50701
+ const char = value[index];
50702
+ if (quote === "double") {
50703
+ if (escaped)
50704
+ escaped = false;
50705
+ else if (char === "\\")
50706
+ escaped = true;
50707
+ else if (char === '"')
50708
+ quote = null;
50709
+ continue;
50710
+ }
50711
+ if (quote === "literal") {
50712
+ if (char === "'")
50713
+ quote = null;
50714
+ continue;
50715
+ }
50716
+ if (char === '"') {
50717
+ quote = "double";
50718
+ continue;
50719
+ }
50720
+ if (char === "'") {
50721
+ quote = "literal";
50722
+ continue;
50723
+ }
50724
+ if (char === "[")
50725
+ square++;
50726
+ else if (char === "]") {
50727
+ if (--square < 0)
50728
+ return null;
50729
+ } else if (char === "{")
50730
+ curly++;
50731
+ else if (char === "}") {
50732
+ if (--curly < 0)
50733
+ return null;
50734
+ } else if (char === delimiter && square === 0 && curly === 0) {
50735
+ result2.push(value.slice(start, index));
50736
+ start = index + 1;
50737
+ }
50738
+ }
50739
+ if (quote !== null || square !== 0 || curly !== 0)
50740
+ return null;
50741
+ result2.push(value.slice(start));
50742
+ return result2;
50743
+ }
50744
+ function inlineArrayEnd(value) {
50745
+ if (!value.startsWith("["))
50746
+ return null;
50747
+ let square = 0, curly = 0, quote = null, escaped = false;
50748
+ for (let index = 0;index < value.length; index++) {
50749
+ const char = value[index];
50750
+ if (quote === "double") {
50751
+ if (escaped)
50752
+ escaped = false;
50753
+ else if (char === "\\")
50754
+ escaped = true;
50755
+ else if (char === '"')
50756
+ quote = null;
50757
+ continue;
50758
+ }
50759
+ if (quote === "literal") {
50760
+ if (char === "'")
50761
+ quote = null;
50762
+ continue;
50763
+ }
50764
+ if (char === '"') {
50765
+ quote = "double";
50766
+ continue;
50767
+ }
50768
+ if (char === "'") {
50769
+ quote = "literal";
50770
+ continue;
50771
+ }
50772
+ if (char === "[")
50773
+ square++;
50774
+ else if (char === "]") {
50775
+ if (--square < 0)
50776
+ return null;
50777
+ if (square === 0 && curly === 0)
50778
+ return index;
50779
+ } else if (char === "{")
50780
+ curly++;
50781
+ else if (char === "}") {
50782
+ if (--curly < 0)
50783
+ return null;
50784
+ }
50785
+ }
50786
+ return null;
50787
+ }
50788
+ function inlineRowsAsTables(value, newline) {
50789
+ const end = inlineArrayEnd(value);
50790
+ if (end === null || value.slice(end + 1).trim() !== "" || value.includes(`
50791
+ `) || value.includes("\r"))
50792
+ return null;
50793
+ const elements = splitTopLevel(value.slice(1, end), ",");
50794
+ if (!elements)
50795
+ return null;
50796
+ const rows = elements.map((element) => element.trim()).filter(Boolean);
50797
+ if (rows.length === 0)
50798
+ return "";
50799
+ const sections = [];
50800
+ for (const row of rows) {
50801
+ if (!row.startsWith("{") || !row.endsWith("}"))
50802
+ return null;
50803
+ const fields = splitTopLevel(row.slice(1, -1), ",");
50804
+ if (!fields)
50805
+ return null;
50806
+ const lines = fields.map((field) => field.trim()).filter(Boolean);
50807
+ if (lines.some((line) => !/^(?:path|name|enabled)\s*=/.test(line)))
50808
+ return null;
50809
+ const keys2 = lines.map((line) => line.slice(0, line.indexOf("=")).trim());
50810
+ if (new Set(keys2).size !== keys2.length)
50811
+ return null;
50812
+ sections.push(`[[skills.config]]${newline}${lines.join(newline)}${newline}`);
50813
+ }
50814
+ return sections.join("");
50815
+ }
50816
+ function normalizeCodexInlinePathConfig(text2) {
50817
+ const before2 = Bun.TOML.parse(text2);
50818
+ if (!Array.isArray(before2.skills?.config))
50819
+ return text2;
50820
+ if (before2.skills.config.length === 0)
50821
+ throw codexPathConfigRefusal();
50822
+ const expectedWithoutConfig = structuredClone(before2);
50823
+ delete expectedWithoutConfig.skills.config;
50824
+ try {
50825
+ if (isDeepStrictEqual3(Bun.TOML.parse(text2.replace(CODEX_SKILL_CONFIG_SECTIONS, "")), expectedWithoutConfig))
50826
+ return text2;
50827
+ } catch {}
50828
+ const matches2 = [];
50829
+ for (const assignment of text2.matchAll(/^([ \t]*config[ \t]*=[ \t]*)(.*?)(\r?)$/gm)) {
50830
+ const rawValue = assignment[2].trimEnd();
50831
+ let quote = null, escaped = false, comment = "";
50832
+ for (let index = 0;index < rawValue.length; index++) {
50833
+ const char = rawValue[index];
50834
+ if (quote === "double") {
50835
+ if (escaped)
50836
+ escaped = false;
50837
+ else if (char === "\\")
50838
+ escaped = true;
50839
+ else if (char === '"')
50840
+ quote = null;
50841
+ continue;
50842
+ }
50843
+ if (quote === "literal") {
50844
+ if (char === "'")
50845
+ quote = null;
50846
+ continue;
50847
+ }
50848
+ if (char === '"')
50849
+ quote = "double";
50850
+ else if (char === "'")
50851
+ quote = "literal";
50852
+ else if (char === "#") {
50853
+ comment = rawValue.slice(index);
50854
+ break;
50855
+ }
50856
+ }
50857
+ const value = comment ? rawValue.slice(0, rawValue.length - comment.length).trimEnd() : rawValue;
50858
+ const tableSections = inlineRowsAsTables(value, text2.includes(`\r
50859
+ `) ? `\r
50860
+ ` : `
50861
+ `);
50862
+ if (tableSections === null)
50863
+ continue;
50864
+ const start = assignment.index;
50865
+ const end = start + assignment[0].length;
50866
+ const withoutAssignment = text2.slice(0, start) + (comment ? comment : "") + assignment[3] + text2.slice(end);
50867
+ const separator = withoutAssignment.endsWith(`
50868
+ `) ? withoutAssignment.endsWith(`
50869
+
50870
+ `) ? "" : `
50871
+ ` : `
50872
+
50873
+ `;
50874
+ const candidate = withoutAssignment + (tableSections ? separator + tableSections : "");
50875
+ try {
50876
+ const after2 = Bun.TOML.parse(candidate);
50877
+ const expected = structuredClone(before2);
50878
+ if (isDeepStrictEqual3(after2, expected))
50879
+ matches2.push(candidate);
50880
+ } catch {}
50881
+ }
50882
+ if (matches2.length !== 1)
50883
+ throw codexPathConfigRefusal();
50884
+ return matches2[0];
50885
+ }
50697
50886
  function assertCodexPathConfigEditable(text2) {
50698
50887
  const before2 = Bun.TOML.parse(text2);
50699
50888
  if (before2.skills?.config === undefined)
@@ -50704,8 +50893,12 @@ function assertCodexPathConfigEditable(text2) {
50704
50893
  const withoutSections = Bun.TOML.parse(text2.replace(CODEX_SKILL_CONFIG_SECTIONS, ""));
50705
50894
  if (isDeepStrictEqual3(withoutSections, expected))
50706
50895
  return;
50896
+ const normalized = normalizeCodexInlinePathConfig(text2);
50897
+ const withoutNormalizedSections = Bun.TOML.parse(normalized.replace(CODEX_SKILL_CONFIG_SECTIONS, ""));
50898
+ if (isDeepStrictEqual3(withoutNormalizedSections, expected))
50899
+ return;
50707
50900
  } catch {}
50708
- throw new Error("Codex skill path controls require ordinary [[skills.config]] TOML tables; preserve and convert inline or dotted config arrays before running skills hook install");
50901
+ throw codexPathConfigRefusal();
50709
50902
  }
50710
50903
  function disableCodexBundledSkills(text2) {
50711
50904
  const before2 = Bun.TOML.parse(text2);
@@ -50764,7 +50957,7 @@ function disableCodexBundledSkills(text2) {
50764
50957
  throw refuse();
50765
50958
  return [...candidates][0];
50766
50959
  }
50767
- var CODEX_SKILL_CONFIG_SECTIONS;
50960
+ var CODEX_SKILL_CONFIG_SECTIONS, codexPathConfigRefusal = () => new Error("Codex skill path controls require ordinary [[skills.config]] TOML tables or a native single-line inline array; preserve and review unsupported inline, dotted, or malformed definitions before running skills hook install");
50768
50961
  var init_agent_codex = __esm(() => {
50769
50962
  CODEX_SKILL_CONFIG_SECTIONS = /^\[\[skills\.config\]\][^\n]*(?:\n(?!\s*\[)[^\n]*)*/gm;
50770
50963
  });
@@ -51082,6 +51275,33 @@ function canonicalAgentPath(path, aliases) {
51082
51275
  const absolute = resolve10(path), binding = aliases.find((item) => absolute === item.alias || absolute.startsWith(item.alias + sep4));
51083
51276
  return binding ? join20(binding.target, relative4(binding.alias, absolute)) : absolute;
51084
51277
  }
51278
+ function disabledCodexSkillPaths(config, aliases) {
51279
+ const skills = config.skills;
51280
+ if (!skills || typeof skills !== "object" || Array.isArray(skills))
51281
+ return [];
51282
+ const rows = skills.config;
51283
+ if (!Array.isArray(rows))
51284
+ return [];
51285
+ if (rows.some((entry) => !entry || typeof entry !== "object" || Array.isArray(entry) || typeof entry.enabled !== "boolean" || entry.path !== undefined && (typeof entry.path !== "string" || !isAbsolute9(entry.path)) || entry.name !== undefined && typeof entry.name !== "string"))
51286
+ return [];
51287
+ if (rows.some((entry) => entry.name !== undefined))
51288
+ return [];
51289
+ const settings = new Map;
51290
+ for (const entry of rows) {
51291
+ if (typeof entry.path !== "string" || !isAbsolute9(entry.path))
51292
+ continue;
51293
+ let document3;
51294
+ try {
51295
+ document3 = realpathSync3(canonicalAgentPath(entry.path, aliases));
51296
+ } catch {
51297
+ continue;
51298
+ }
51299
+ if (!document3.endsWith(`${sep4}SKILL.md`))
51300
+ continue;
51301
+ settings.set(document3, [...settings.get(document3) ?? [], entry.enabled === false]);
51302
+ }
51303
+ return [...settings].filter(([, values2]) => values2.length === 1 && values2[0] === true).map(([document3]) => dirname10(document3));
51304
+ }
51085
51305
  function recheckRootAliases(aliases) {
51086
51306
  for (const binding of aliases) {
51087
51307
  if (!["claude", "codex"].includes(binding.agent) || binding.alias !== join20(binding.home, `.${binding.agent}`))
@@ -51238,6 +51458,9 @@ function projectAncestorDirectories(projects) {
51238
51458
  function inventoryNativeSkills(home = homedir3(), options = {}) {
51239
51459
  if (options.reviewedCacheAlias !== undefined && (!options.includeVendor || !isAbsolute9(options.reviewedCacheAlias) || resolve10(options.reviewedCacheAlias) !== options.reviewedCacheAlias))
51240
51460
  throw new Error("A reviewed cache alias requires vendor inventory and an exact absolute path");
51461
+ if (options.disabledVendorPaths?.some((path) => !isAbsolute9(path) || resolve10(path) !== path))
51462
+ throw new Error("Disabled vendor paths must be exact absolute paths");
51463
+ const disabledVendorPaths = new Set(options.disabledVendorPaths ?? []);
51241
51464
  const aliases = rootAliases(home, options.allowRootAliases);
51242
51465
  const selectedAgents = options.agents ? new Set(options.agents) : undefined;
51243
51466
  const includesAgent = (agent) => !selectedAgents || selectedAgents.has(agent);
@@ -51259,7 +51482,7 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
51259
51482
  throw new Error("Native skill discovery metadata limit exceeded");
51260
51483
  }
51261
51484
  let reviewedCacheAliasFound = false;
51262
- function verifiedSiblingCacheAlias(path, parent) {
51485
+ function verifiedSiblingCacheAlias(agent, path, parent) {
51263
51486
  const link2 = readlinkSync3(path), target = resolve10(parent, link2);
51264
51487
  if (dirname10(target) !== parent || (isAbsolute9(link2) ? link2 !== target : dirname10(link2) !== "."))
51265
51488
  return false;
@@ -51269,9 +51492,13 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
51269
51492
  if (!scan?.complete || scan.entries > maxAliasProofEntries)
51270
51493
  return false;
51271
51494
  if (scan.hasSkills) {
51495
+ const targetSkills = entries.filter((entry) => entry.agent === agent && entry.vendor && (entry.path === target || entry.path.startsWith(target + sep4)));
51496
+ if (path === options.reviewedCacheAlias && targetSkills.length > 0)
51497
+ reviewedCacheAliasFound = true;
51498
+ if (agent === "codex" && targetSkills.length > 0 && targetSkills.every((entry) => disabledVendorPaths.has(entry.path)))
51499
+ return true;
51272
51500
  if (path !== options.reviewedCacheAlias)
51273
51501
  return false;
51274
- reviewedCacheAliasFound = true;
51275
51502
  }
51276
51503
  return true;
51277
51504
  }
@@ -51330,7 +51557,7 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
51330
51557
  const child = join20(path, name);
51331
51558
  if (isVendor && lstatSync14(child, { throwIfNoEntry: false })?.isSymbolicLink()) {
51332
51559
  const target = statSync7(child, { throwIfNoEntry: false });
51333
- if (target?.isFile() || pluginCache && target?.isDirectory() && verifiedSiblingCacheAlias(child, path)) {
51560
+ if (target?.isFile() || pluginCache && target?.isDirectory() && verifiedSiblingCacheAlias(agent, child, path)) {
51334
51561
  scan.entries++;
51335
51562
  continue;
51336
51563
  }
@@ -51470,7 +51697,7 @@ function configureHooks(config, agent, command, profileId, nativeReady) {
51470
51697
  }
51471
51698
  function disableCodexSkills(text2, skills, aliases, bridgePath) {
51472
51699
  const bundled = disableCodexBundledSkills(text2);
51473
- let result2 = bundled;
51700
+ let result2 = normalizeCodexInlinePathConfig(bundled);
51474
51701
  const bridgeSelector = (entry) => typeof entry.name === "string" && entry.name.trim() === CLI_BRIDGE_NAME || typeof entry.path === "string" && [resolve10(bridgePath), resolve10(join20(bridgePath, "SKILL.md"))].includes(canonicalAgentPath(entry.path, aliases));
51475
51702
  const previousConfig = Bun.TOML.parse(bundled).skills?.config;
51476
51703
  if (previousConfig !== undefined && (!Array.isArray(previousConfig) || previousConfig.some((entry) => !entry || typeof entry !== "object" || entry.path !== undefined && entry.name !== undefined)))
@@ -51538,7 +51765,8 @@ function planAgentIntegration(options) {
51538
51765
  }
51539
51766
  return resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, retainedReview, canonical: (path) => canonicalAgentPath(path, aliases) });
51540
51767
  });
51541
- 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 });
51768
+ const codexConfig = options.agents.includes("codex") ? Bun.TOML.parse(readOptional(canonicalAgentPath(join20(home, ".codex", "config.toml"), aliases)) ?? "") : {};
51769
+ 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) });
51542
51770
  const changes = [];
51543
51771
  for (const agent of [...new Set(options.agents)]) {
51544
51772
  if (!INTEGRATION_AGENTS.includes(agent))
@@ -51979,7 +52207,7 @@ function assertManagedAgentBridge(agent, options = {}) {
51979
52207
  } catch (error) {
51980
52208
  throw new Error(`NATIVE_SKILL_DRIFT: ${error.message}`);
51981
52209
  }
51982
- const inventory = inventoryNativeSkills(home, { includeVendor: true, guardHermes: agent === "hermes", agents: [agent], projectDirs: [...roots], agentRoots: discovery.roots.map((path) => ({ agent, path })), allowRootAliases: aliases.length > 0 });
52210
+ const inventory = inventoryNativeSkills(home, { includeVendor: true, guardHermes: agent === "hermes", agents: [agent], projectDirs: [...roots], agentRoots: discovery.roots.map((path) => ({ agent, path })), allowRootAliases: aliases.length > 0, disabledVendorPaths: agent === "codex" ? disabledCodexSkillPaths(codexConfig, aliases) : [] });
51983
52211
  const unexpected = inventory.filter((entry) => visible(entry) && !entry.bridge && !disabledVendorSkill(entry));
51984
52212
  if (unexpected.length) {
51985
52213
  const paths = unexpected.slice(0, 3).map((entry) => {
@@ -110588,7 +110816,7 @@ async function connectCodexHookRpc(options) {
110588
110816
  }
110589
110817
  var SUPPORTED_CODEX_HOOK_VERSIONS;
110590
110818
  var init_codex_hook_rpc = __esm(() => {
110591
- SUPPORTED_CODEX_HOOK_VERSIONS = ["codex-cli 0.153.0", "codex-cli 0.154.0", "codex-cli 0.155.0", "codex-cli 0.155.1", "codex-cli 0.156.1", "codex-cli 0.157.0", "codex-cli 0.157.1"];
110819
+ SUPPORTED_CODEX_HOOK_VERSIONS = ["codex-cli 0.153.0", "codex-cli 0.154.0", "codex-cli 0.155.0", "codex-cli 0.155.1", "codex-cli 0.156.1", "codex-cli 0.157.0", "codex-cli 0.157.1", "codex-cli 0.158.0"];
110592
110820
  });
110593
110821
 
110594
110822
  // src/lib/codex-hook-trust-identity.ts
@@ -111179,10 +111407,10 @@ function registerAgentIntegration(parent) {
111179
111407
  hook.command("agents").option("--json", "Output the adapter capability inventory", false).description("Show maintained native adapters and explicit coverage limits").action(async () => {
111180
111408
  await writeCliOutput(JSON.stringify({ agents: INTEGRATION_AGENTS.map((agent) => ({ agent, bridge: true, ...AGENT_ADAPTERS[agent] })), inventoryOnly: ["codewith", "windsurf", "pi", "amp", "cline", "roo", "copilot"], limitations: ["Cursor prompt hooks gate submission; selected context is injected at session start only.", "Native discovery checks cover known home roots and current project ancestors. External plugin hook injection and arbitrary added directories require separate review.", "Hermes injects selected prompt context, but native pre_llm_call fails open. Exact native hook trust, bundled reseeding opt-out, native payload retirement and a supervised pre-tool guard are required. Child failures block explicitly; native host/supervisor death is not a universal fail-closed guarantee.", "Restart agents and use their normal hook trust controls after installation."] }, null, 2));
111181
111409
  });
111182
- hook.command("install").option("--agent <agent>", `Agent to configure: ${INTEGRATION_AGENTS.join(", ")}, all`, "all").option("--command <path>", "Skills executable used by the hook (preserves existing binding; new agents use skills)").option("--selection-profile <id>", "Selection profile (preserves existing binding; new agents use default)").option("--include-vendor", "Retained for compatibility; vendor system skills are always inventoried and disabled", false).option("--discovery-inputs <file>", "Advanced reviewed active plugin roots and source hashes for unsupported registrations").option("--allow-root-aliases", "Allow home .claude/.codex aliases to existing directories within this home", false).option("--apply", "Apply the plan, preserving prior configuration in private backups", false).option("--json", "Output a receipt as JSON", false).description("Plan or install one Skills CLI bridge plus native prompt hooks").action(async (options) => {
111410
+ hook.command("install").option("--agent <agent>", `Agent to configure: ${INTEGRATION_AGENTS.join(", ")}, all`, "all").option("--command <path>", "Skills executable used by the hook (preserves existing binding; new agents use skills)").option("--selection-profile <id>", "Selection profile (preserves existing binding; new agents use default)").option("--include-vendor", "Retained for compatibility; vendor system skills are always inventoried and disabled", false).option("--discovery-inputs <file>", "Advanced reviewed active plugin roots and source hashes for unsupported registrations").option("--reviewed-cache-alias <path>", "Exact skill-containing vendor cache alias reviewed for this hook plan").option("--allow-root-aliases", "Allow home .claude/.codex aliases to existing directories within this home", false).option("--apply", "Apply the plan, preserving prior configuration in private backups", false).option("--json", "Output a receipt as JSON", false).description("Plan or install one Skills CLI bridge plus native prompt hooks").action(async (options) => {
111183
111411
  try {
111184
111412
  const discoveryInputs = options.discoveryInputs ? JSON.parse(readFileSync41(options.discoveryInputs, "utf8")) : undefined;
111185
- const plan = planAgentIntegration({ agents: agents(options.agent), command: options.command, profileId: options.selectionProfile, includeVendor: options.includeVendor, discoveryInputs, allowRootAliases: options.allowRootAliases });
111413
+ const plan = planAgentIntegration({ agents: agents(options.agent), command: options.command, profileId: options.selectionProfile, includeVendor: options.includeVendor, discoveryInputs, allowRootAliases: options.allowRootAliases, reviewedCacheAlias: options.reviewedCacheAlias });
111186
111414
  const result2 = options.apply ? applyAgentIntegration(plan) : { changed: [], backups: [] };
111187
111415
  const receipt = { applied: options.apply, planned: plan.changes.map((change) => change.path), ...result2, rootAliases: plan.rootAliases ?? [], discovery: plan.discoveryAfter, nativeSkills: plan.nativeSkills.map((entry) => ({ agent: entry.agent, path: entry.path, managed: entry.managed, vendor: entry.vendor, system: entry.system === true, bridge: entry.bridge === true })), requiresNativeRetirement: plan.nativeSkills.some((entry) => !entry.bridge && !entry.system) };
111188
111416
  if (options.json)