@hasna/skills 0.10.48 → 0.10.50

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.
@@ -1872,6 +1872,24 @@ var require_commander = __commonJS((exports) => {
1872
1872
  exports.InvalidOptionArgumentError = InvalidArgumentError;
1873
1873
  });
1874
1874
 
1875
+ // src/lib/launch-cwd.ts
1876
+ var LAUNCH_CWD_VARIABLE = "HASNA_SKILLS_LAUNCH_CWD";
1877
+ function restoreLaunchCwd(env = process.env) {
1878
+ const requested = env[LAUNCH_CWD_VARIABLE];
1879
+ delete env[LAUNCH_CWD_VARIABLE];
1880
+ if (!requested || !requested.startsWith("/"))
1881
+ return { restored: false, cwd: process.cwd() };
1882
+ try {
1883
+ process.chdir(requested);
1884
+ return { restored: true, cwd: process.cwd() };
1885
+ } catch {
1886
+ return { restored: false, cwd: process.cwd() };
1887
+ }
1888
+ }
1889
+
1890
+ // src/lib/launch-cwd-apply.ts
1891
+ restoreLaunchCwd();
1892
+
1875
1893
  // node_modules/commander/esm.mjs
1876
1894
  var import__ = __toESM(require_commander(), 1);
1877
1895
  var {
package/bin/mcp.js CHANGED
@@ -20019,7 +20019,7 @@ var package_default;
20019
20019
  var init_package = __esm(() => {
20020
20020
  package_default = {
20021
20021
  name: "@hasna/skills",
20022
- version: "0.10.48",
20022
+ version: "0.10.50",
20023
20023
  description: "Skills library for AI coding agents",
20024
20024
  type: "module",
20025
20025
  bin: {
@@ -38370,7 +38370,9 @@ function inferencePreference(key, child, version2) {
38370
38370
  function settingsDigest(text, version2 = 1) {
38371
38371
  const value = parse6(text);
38372
38372
  value.entries = value.entries.filter(([key, child]) => {
38373
- if (version2 !== 1 && inferencePreference(key, child, version2))
38373
+ if (version2 === 4 && key === "theme" && child.kind === "string" && BUILTIN_THEMES.has(child.value))
38374
+ return false;
38375
+ if (version2 !== 1 && inferencePreference(key, child, version2 === 4 ? 3 : version2))
38374
38376
  return false;
38375
38377
  if (key === "model") {
38376
38378
  need(child.kind === "string", "contains an invalid model selection");
@@ -38418,6 +38420,16 @@ function hashClaudeSettingsReplacementV3(text, budget) {
38418
38420
  budget.remaining -= bytes;
38419
38421
  return settingsDigest(text, 3);
38420
38422
  }
38423
+ function captureClaudeSettingsV4(path, budget = { remaining: 256 * 1024 * 1024 }) {
38424
+ return { path, hashMode: "claude-settings-v4", sha256: settingsDigest(readNativeSettingsWitnessFile(path, budget), 4) };
38425
+ }
38426
+ function hashClaudeSettingsReplacementV4(text, budget) {
38427
+ need(typeof text === "string", "requires settings text");
38428
+ const bytes = Buffer.byteLength(text);
38429
+ need(bytes <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes && Number.isSafeInteger(budget.remaining) && budget.remaining >= bytes, "exceeds its byte limit");
38430
+ budget.remaining -= bytes;
38431
+ return settingsDigest(text, 4);
38432
+ }
38421
38433
  function hashNativeJsonControls(text, excludedMetadata) {
38422
38434
  need(Buffer.byteLength(text) <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes, "exceeds its byte limit");
38423
38435
  const value = parse6(text);
@@ -38450,7 +38462,7 @@ function hashSumiNativeJsonControls(text) {
38450
38462
  });
38451
38463
  return createHash2("sha256").update("hasna.skills.sumi-settings.v1\x00").update(JSON.stringify(value)).digest("hex");
38452
38464
  }
38453
- var CLAUDE_SETTINGS_WITNESS_LIMITS, sameFile = (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, BOOLEAN_PREFERENCES, ENUM_PREFERENCES, BUILTIN_MODEL_SELECTIONS, PERSISTED_EFFORT_LEVELS;
38465
+ var CLAUDE_SETTINGS_WITNESS_LIMITS, sameFile = (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, BOOLEAN_PREFERENCES, ENUM_PREFERENCES, BUILTIN_MODEL_SELECTIONS, CLAUDE_BUILTIN_THEMES, BUILTIN_THEMES, PERSISTED_EFFORT_LEVELS;
38454
38466
  var init_claude_settings_witness = __esm(() => {
38455
38467
  CLAUDE_SETTINGS_WITNESS_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
38456
38468
  BOOLEAN_PREFERENCES = new Set([
@@ -38504,6 +38516,8 @@ var init_claude_settings_witness = __esm(() => {
38504
38516
  "claude-opus-4-6[1m]",
38505
38517
  "claude-sonnet-4-6[1m]"
38506
38518
  ]);
38519
+ CLAUDE_BUILTIN_THEMES = Object.freeze(["auto", "dark", "light", "dark-daltonized", "light-daltonized", "dark-ansi", "light-ansi"]);
38520
+ BUILTIN_THEMES = new Set(CLAUDE_BUILTIN_THEMES);
38507
38521
  PERSISTED_EFFORT_LEVELS = new Set(["low", "medium", "high", "xhigh"]);
38508
38522
  });
38509
38523
 
@@ -38666,14 +38680,14 @@ function assertAgentPolicyCollections(policy) {
38666
38680
  text(source.path);
38667
38681
  requireBound(source.sha256 === null || typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256));
38668
38682
  if (source.hashMode !== undefined)
38669
- requireBound(["bytes", "path-bytes", "claude-plugin-manifest-v1", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-marketplace-registry-v2", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3", "codex-settings-v4", "sumi-settings-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
38683
+ requireBound(["bytes", "path-bytes", "claude-plugin-manifest-v1", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-settings-v4", "claude-marketplace-registry-v2", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3", "codex-settings-v4", "sumi-settings-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
38670
38684
  if (source.hashMode === "claude-plugin-manifest-v1")
38671
38685
  requireBound(agent === "claude" && value.agent === "claude" && ["automatic", "reviewed"].includes(value.method) && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute4(source.path) && resolve3(source.path) === source.path && source.path.endsWith(join10(sep3, ".claude-plugin", "plugin.json")) && source.managedPlugins === undefined);
38672
38686
  if (source.hashMode === "sumi-settings-v1")
38673
38687
  requireBound(agent === "sumi" && value.agent === "sumi" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute4(source.path) && resolve3(source.path) === source.path && basename4(source.path) === "sumi.json");
38674
38688
  if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4"))
38675
38689
  requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute4(source.path) && resolve3(source.path) === source.path && basename4(source.path) === "config.toml");
38676
- if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3")
38690
+ if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4"))
38677
38691
  requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute4(source.path) && resolve3(source.path) === source.path && basename4(source.path) === "settings.json");
38678
38692
  if (source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2")
38679
38693
  requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute4(source.path) && resolve3(source.path) === source.path && basename4(source.path) === "known_marketplaces.json");
@@ -48869,6 +48883,50 @@ function unchanged2(before) {
48869
48883
  var sameStat = (a, b) => ["dev", "ino", "uid", "nlink", "mode", "size", "mtimeNs", "ctimeNs"].every((key) => a[key] === b[key]);
48870
48884
  var init_codex_hook_trust_files = () => {};
48871
48885
 
48886
+ // src/cli/commands/runtime-launcher.ts
48887
+ var PINNED_LAUNCHER_BUN_FLAGS, PINNED_LAUNCHER_ENV_NAMES, PINNED_LAUNCHER_ENV_PREFIXES, ENV_PREFIX, ENV_EXACT, COMMAND_LINE, PINNED_LAUNCHER_MAX_BYTES;
48888
+ var init_runtime_launcher = __esm(() => {
48889
+ PINNED_LAUNCHER_BUN_FLAGS = ["--config=/dev/null", "--no-env-file", "--no-macros", "--no-install"];
48890
+ PINNED_LAUNCHER_ENV_NAMES = [
48891
+ "HOME",
48892
+ "PATH",
48893
+ "TMPDIR",
48894
+ "TERM",
48895
+ "TERM_PROGRAM",
48896
+ "TERM_PROGRAM_VERSION",
48897
+ "COLORTERM",
48898
+ "LANG",
48899
+ "USER",
48900
+ "LOGNAME",
48901
+ "SHELL",
48902
+ "TZ",
48903
+ "NO_COLOR",
48904
+ "FORCE_COLOR",
48905
+ "CI",
48906
+ "COLUMNS",
48907
+ "LINES",
48908
+ "EDITOR",
48909
+ "VISUAL",
48910
+ "PAGER",
48911
+ "SSH_AUTH_SOCK",
48912
+ "DATABASE_URL",
48913
+ "TERMINAL_CWD",
48914
+ "CODEX_HOME",
48915
+ "HERMES_HOME",
48916
+ "HERMES_ENABLE_PROJECT_PLUGINS",
48917
+ "HOST",
48918
+ "PORT",
48919
+ "NODE_ENV",
48920
+ "AGENT_ID",
48921
+ "ECS_CONTAINER_METADATA_URI_V4"
48922
+ ];
48923
+ PINNED_LAUNCHER_ENV_PREFIXES = ["HASNA", "SKILLS", "SKILL", "MCP", "XDG", "LC", "AWS"];
48924
+ ENV_PREFIX = new RegExp(`^(${PINNED_LAUNCHER_ENV_PREFIXES.join("|")})_`);
48925
+ ENV_EXACT = new Set(PINNED_LAUNCHER_ENV_NAMES);
48926
+ COMMAND_LINE = new RegExp(`^set -- '([^']+)' ${PINNED_LAUNCHER_BUN_FLAGS.join(" ")} '--cwd=([^']+)' '([^']+)' "\\$@"$`, "m");
48927
+ PINNED_LAUNCHER_MAX_BYTES = 16 * 1024;
48928
+ });
48929
+
48872
48930
  // src/lib/agent-integration.ts
48873
48931
  var init_agent_integration = __esm(() => {
48874
48932
  init_sumi_settings_witness();
@@ -48889,6 +48947,7 @@ var init_agent_integration = __esm(() => {
48889
48947
  init_agent_hermes();
48890
48948
  init_claude_settings_witness();
48891
48949
  init_codex_hook_trust_files();
48950
+ init_runtime_launcher();
48892
48951
  });
48893
48952
 
48894
48953
  // src/lib/home-migration.ts
@@ -50535,13 +50594,13 @@ function projected(source, changes, budget2 = discoveryByteBudget()) {
50535
50594
  throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
50536
50595
  return changes?.has(source.path) ? (source.hashMode === "codex-settings-v4" ? hashCodexSettingsReplacementV4 : source.hashMode === "codex-settings-v3" ? hashCodexSettingsReplacementV3 : source.hashMode === "codex-settings-v2" ? hashCodexSettingsReplacementV2 : hashCodexSettingsReplacement)(changes.get(source.path), budget2) : current;
50537
50596
  }
50538
- if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") {
50597
+ if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4")) {
50539
50598
  if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
50540
50599
  throw new Error("Claude settings witnesses require exact typed metadata");
50541
- const current = (source.hashMode === "claude-settings-v3" ? captureClaudeSettingsV3 : source.hashMode === "claude-settings-v2" ? captureClaudeSettingsV2 : captureClaudeSettings)(source.path, budget2).sha256;
50600
+ const current = (source.hashMode === "claude-settings-v4" ? captureClaudeSettingsV4 : source.hashMode === "claude-settings-v3" ? captureClaudeSettingsV3 : source.hashMode === "claude-settings-v2" ? captureClaudeSettingsV2 : captureClaudeSettings)(source.path, budget2).sha256;
50542
50601
  if (current !== source.sha256)
50543
50602
  throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
50544
- return changes?.has(source.path) ? (source.hashMode === "claude-settings-v3" ? hashClaudeSettingsReplacementV3 : source.hashMode === "claude-settings-v2" ? hashClaudeSettingsReplacementV2 : hashClaudeSettingsReplacement)(changes.get(source.path), budget2) : current;
50603
+ return changes?.has(source.path) ? (source.hashMode === "claude-settings-v4" ? hashClaudeSettingsReplacementV4 : source.hashMode === "claude-settings-v3" ? hashClaudeSettingsReplacementV3 : source.hashMode === "claude-settings-v2" ? hashClaudeSettingsReplacementV2 : hashClaudeSettingsReplacement)(changes.get(source.path), budget2) : current;
50545
50604
  }
50546
50605
  if (source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2") {
50547
50606
  if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256) || changes?.has(source.path))
@@ -50760,7 +50819,7 @@ function assertMarketplaceBinding(binding, source) {
50760
50819
  throw new Error("Sumi settings witnesses require explicit reviewed Sumi configuration");
50761
50820
  if ((source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")) && (binding.agent !== "codex" || binding.method !== "reviewed" || basename8(source.path) !== "config.toml"))
50762
50821
  throw new Error("Codex settings witnesses require explicit reviewed Codex configuration");
50763
- if ((source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") && (binding.agent !== "claude" || binding.method !== "reviewed" || basename8(source.path) !== "settings.json"))
50822
+ if ((source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4")) && (binding.agent !== "claude" || binding.method !== "reviewed" || basename8(source.path) !== "settings.json"))
50764
50823
  throw new Error("Claude settings witnesses require explicit reviewed Claude configuration");
50765
50824
  if ((source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2") && (binding.agent !== "claude" || binding.method !== "reviewed"))
50766
50825
  throw new Error("Claude marketplace witnesses require explicit reviewed Claude discovery");
@@ -50882,7 +50941,7 @@ function resolveAgentDiscovery(options) {
50882
50941
  verifyAgentDiscovery(retained);
50883
50942
  if (!retained.sources.some((source) => source.path === canonical7(configPath) && source.format === undefined && source.fields === undefined))
50884
50943
  unresolved("retained review is missing the full agent configuration source");
50885
- if (retained.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical7(configPath)))
50944
+ if (retained.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical7(configPath)))
50886
50945
  unresolved("retained settings witness names another configuration source");
50887
50946
  const covered = supersedesSettingsProjection(agent, retained.sources, canonical7(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical7(configPath), source)) : sources;
50888
50947
  if (covered.some((source) => !retained.sources.some((saved) => isDeepStrictEqual4(saved, source))) || [...roots].some((root) => !retained.roots.includes(root)) || agent === "gemini" && JSON.stringify(retained.builtinNames) !== JSON.stringify(builtinNames))
@@ -50896,7 +50955,7 @@ function resolveAgentDiscovery(options) {
50896
50955
  if (review.sources.some((source) => source.format !== undefined || source.fields !== undefined))
50897
50956
  throw new Error("Explicit discovery reviews require full source-file hashes");
50898
50957
  verifyAgentDiscovery(supplied);
50899
- if (review.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical7(configPath)))
50958
+ if (review.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical7(configPath)))
50900
50959
  throw new Error("Semantic settings witnesses must name the configured agent configuration source");
50901
50960
  if (!review.sources.some((source) => source.path === canonical7(configPath)))
50902
50961
  throw new Error("Discovery review must include the agent configuration source");
@@ -63996,6 +64055,24 @@ var init_http = __esm(() => {
63996
64055
  init_server3();
63997
64056
  });
63998
64057
 
64058
+ // src/lib/launch-cwd.ts
64059
+ var LAUNCH_CWD_VARIABLE = "HASNA_SKILLS_LAUNCH_CWD";
64060
+ function restoreLaunchCwd(env = process.env) {
64061
+ const requested = env[LAUNCH_CWD_VARIABLE];
64062
+ delete env[LAUNCH_CWD_VARIABLE];
64063
+ if (!requested || !requested.startsWith("/"))
64064
+ return { restored: false, cwd: process.cwd() };
64065
+ try {
64066
+ process.chdir(requested);
64067
+ return { restored: true, cwd: process.cwd() };
64068
+ } catch {
64069
+ return { restored: false, cwd: process.cwd() };
64070
+ }
64071
+ }
64072
+
64073
+ // src/lib/launch-cwd-apply.ts
64074
+ restoreLaunchCwd();
64075
+
63999
64076
  // src/mcp/index.ts
64000
64077
  init_stdio2();
64001
64078
  init_package();
package/bin/migrate.js CHANGED
@@ -1,13 +1,31 @@
1
1
  #!/usr/bin/env bun
2
2
  // @bun
3
3
 
4
+ // src/lib/launch-cwd.ts
5
+ var LAUNCH_CWD_VARIABLE = "HASNA_SKILLS_LAUNCH_CWD";
6
+ function restoreLaunchCwd(env = process.env) {
7
+ const requested = env[LAUNCH_CWD_VARIABLE];
8
+ delete env[LAUNCH_CWD_VARIABLE];
9
+ if (!requested || !requested.startsWith("/"))
10
+ return { restored: false, cwd: process.cwd() };
11
+ try {
12
+ process.chdir(requested);
13
+ return { restored: true, cwd: process.cwd() };
14
+ } catch {
15
+ return { restored: false, cwd: process.cwd() };
16
+ }
17
+ }
18
+
19
+ // src/lib/launch-cwd-apply.ts
20
+ restoreLaunchCwd();
21
+
4
22
  // src/server/migrate.ts
5
23
  import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync3 } from "fs";
6
24
  import { join as join5 } from "path";
7
25
  // package.json
8
26
  var package_default = {
9
27
  name: "@hasna/skills",
10
- version: "0.10.48",
28
+ version: "0.10.50",
11
29
  description: "Skills library for AI coding agents",
12
30
  type: "module",
13
31
  bin: {
package/bin/server.js CHANGED
@@ -24119,10 +24119,28 @@ __export(exports_dist_es8, {
24119
24119
  var init_dist_es9 = __esm(() => {
24120
24120
  init_fromIni();
24121
24121
  });
24122
+
24123
+ // src/lib/launch-cwd.ts
24124
+ var LAUNCH_CWD_VARIABLE = "HASNA_SKILLS_LAUNCH_CWD";
24125
+ function restoreLaunchCwd(env = process.env) {
24126
+ const requested = env[LAUNCH_CWD_VARIABLE];
24127
+ delete env[LAUNCH_CWD_VARIABLE];
24128
+ if (!requested || !requested.startsWith("/"))
24129
+ return { restored: false, cwd: process.cwd() };
24130
+ try {
24131
+ process.chdir(requested);
24132
+ return { restored: true, cwd: process.cwd() };
24133
+ } catch {
24134
+ return { restored: false, cwd: process.cwd() };
24135
+ }
24136
+ }
24137
+
24138
+ // src/lib/launch-cwd-apply.ts
24139
+ restoreLaunchCwd();
24122
24140
  // package.json
24123
24141
  var package_default = {
24124
24142
  name: "@hasna/skills",
24125
- version: "0.10.48",
24143
+ version: "0.10.50",
24126
24144
  description: "Skills library for AI coding agents",
24127
24145
  type: "module",
24128
24146
  bin: {
package/bin/worker.js CHANGED
@@ -24120,12 +24120,30 @@ var init_dist_es9 = __esm(() => {
24120
24120
  init_fromIni();
24121
24121
  });
24122
24122
 
24123
+ // src/lib/launch-cwd.ts
24124
+ var LAUNCH_CWD_VARIABLE = "HASNA_SKILLS_LAUNCH_CWD";
24125
+ function restoreLaunchCwd(env = process.env) {
24126
+ const requested = env[LAUNCH_CWD_VARIABLE];
24127
+ delete env[LAUNCH_CWD_VARIABLE];
24128
+ if (!requested || !requested.startsWith("/"))
24129
+ return { restored: false, cwd: process.cwd() };
24130
+ try {
24131
+ process.chdir(requested);
24132
+ return { restored: true, cwd: process.cwd() };
24133
+ } catch {
24134
+ return { restored: false, cwd: process.cwd() };
24135
+ }
24136
+ }
24137
+
24138
+ // src/lib/launch-cwd-apply.ts
24139
+ restoreLaunchCwd();
24140
+
24123
24141
  // src/server/worker.ts
24124
24142
  import { randomUUID as randomUUID6 } from "crypto";
24125
24143
  // package.json
24126
24144
  var package_default = {
24127
24145
  name: "@hasna/skills",
24128
- version: "0.10.48",
24146
+ version: "0.10.50",
24129
24147
  description: "Skills library for AI coding agents",
24130
24148
  type: "module",
24131
24149
  bin: {
@@ -0,0 +1,102 @@
1
+ export declare const PINNED_LAUNCHER_SCHEMA = "hasna-skills-pinned-launcher-v1";
2
+ /** The variable the launcher uses to hand the caller's directory to the CLI. */
3
+ export declare const LAUNCH_CWD_VARIABLE = "HASNA_SKILLS_LAUNCH_CWD";
4
+ /** Bun runtime flags that remove every working-directory configuration source. */
5
+ export declare const PINNED_LAUNCHER_BUN_FLAGS: readonly ["--config=/dev/null", "--no-env-file", "--no-macros", "--no-install"];
6
+ /**
7
+ * Environment names passed through `env -i`, by exact name. Everything else,
8
+ * including `BUN_*`, `NODE_OPTIONS`, `DYLD_*`, `LD_*` and proxy settings, is
9
+ * dropped. The handling is total on purpose: a name is either listed here,
10
+ * matches a prefix below, or never reaches the runtime.
11
+ */
12
+ export declare const PINNED_LAUNCHER_ENV_NAMES: readonly ["HOME", "PATH", "TMPDIR", "TERM", "TERM_PROGRAM", "TERM_PROGRAM_VERSION", "COLORTERM", "LANG", "USER", "LOGNAME", "SHELL", "TZ", "NO_COLOR", "FORCE_COLOR", "CI", "COLUMNS", "LINES", "EDITOR", "VISUAL", "PAGER", "SSH_AUTH_SOCK", "DATABASE_URL", "TERMINAL_CWD", "CODEX_HOME", "HERMES_HOME", "HERMES_ENABLE_PROJECT_PLUGINS", "HOST", "PORT", "NODE_ENV", "AGENT_ID", "ECS_CONTAINER_METADATA_URI_V4"];
13
+ /** Environment name prefixes passed through, for the app's own documented overrides. */
14
+ export declare const PINNED_LAUNCHER_ENV_PREFIXES: readonly ["HASNA", "SKILLS", "SKILL", "MCP", "XDG", "LC", "AWS"];
15
+ /** The allowlisted subset of an environment, by the same rule the launcher applies. */
16
+ export declare function pinnedLauncherEnvironment(env: Record<string, string | undefined>): Record<string, string>;
17
+ export type LauncherShape = "symlink" | "pinned";
18
+ export interface PinnedLauncherBinding {
19
+ runtime: string;
20
+ cwd: string;
21
+ entry: string;
22
+ }
23
+ export type LauncherState = {
24
+ shape: "symlink";
25
+ linkTarget: string;
26
+ target: string;
27
+ } | {
28
+ shape: "pinned";
29
+ runtime: string;
30
+ cwd: string;
31
+ target: string;
32
+ sha256: string;
33
+ };
34
+ export type InspectedLauncher = {
35
+ kind: "symlink";
36
+ linkTarget: string;
37
+ target: string;
38
+ } | {
39
+ kind: "pinned";
40
+ runtime: string;
41
+ cwd: string;
42
+ target: string;
43
+ sha256: string;
44
+ text: string;
45
+ } | {
46
+ kind: "foreign";
47
+ };
48
+ /** The exact launcher text for one Bun binary, trusted directory and entry. Deterministic. */
49
+ export declare function renderPinnedLauncher(binding: PinnedLauncherBinding): string;
50
+ /** Parse launcher text; only the exact rendered shape is accepted. */
51
+ export declare function parsePinnedLauncher(text: string): PinnedLauncherBinding | null;
52
+ /**
53
+ * Resolve a command path the way the symlink checks used to: the physical file
54
+ * it reaches and, when that file is a managed pinned launcher (its exact
55
+ * rendered text), the entry the launcher runs. `entry` equals `physical` for a
56
+ * symlink or a plain executable. A pinned entry must itself be a physical path.
57
+ */
58
+ export declare function resolveLauncherCommand(path: string): {
59
+ physical: string;
60
+ entry: string;
61
+ pinned: (PinnedLauncherBinding & {
62
+ sha256: string;
63
+ }) | null;
64
+ };
65
+ /** Classify the launcher at `path` without following anything but its own symlink. */
66
+ export declare function inspectLauncher(path: string): InspectedLauncher;
67
+ /** The entry a managed launcher runs, whichever shape it has. */
68
+ export declare function launcherTarget(path: string): string;
69
+ /** The "link text" of a managed launcher: the symlink text, or the pinned entry path. */
70
+ export declare function launcherLinkText(path: string): string;
71
+ /** Whether the launcher at `path` is exactly `state`: same shape, text and target. */
72
+ export declare function launcherIs(path: string, state: LauncherState): boolean;
73
+ /** The pinned launcher text for a state, verified against the recorded digest. */
74
+ export declare function pinnedLauncherText(state: {
75
+ runtime: string;
76
+ cwd: string;
77
+ target: string;
78
+ sha256: string;
79
+ }): string;
80
+ /** Build the pinned state for an entry under the given Bun binary and trusted directory. */
81
+ export declare function pinnedLauncherState(runtime: string, cwd: string, target: string): Extract<LauncherState, {
82
+ shape: "pinned";
83
+ }>;
84
+ /**
85
+ * Create a launcher in `state` at a path that must not exist yet, and read it
86
+ * back. Symlinks are created as-is; pinned launchers are regular 0755 files.
87
+ */
88
+ export declare function materializeLauncher(path: string, state: LauncherState): void;
89
+ /**
90
+ * The Bun binary launchers are pinned to: the exact runtime running the
91
+ * updater, resolved physically and required to be owned by root or the
92
+ * current user, executable, and not writable by group or others.
93
+ */
94
+ export declare function pinnedLauncherRuntime(): string;
95
+ /** A pinned runtime is a physical, executable regular file owned by root or the current user and not group/world-writable. */
96
+ export declare function assertPinnedLauncherRuntimeSafe(runtime: string): void;
97
+ /**
98
+ * The launcher depends on `/bin/sh -p`, `/usr/bin/awk` and `/usr/bin/env`.
99
+ * Refuse before any launcher is written when this station's shell rejects
100
+ * `-p` (for example dash before 0.5.11), so no launcher is left unusable.
101
+ */
102
+ export declare function assertPinnedLauncherShell(): void;
@@ -1,2 +1,9 @@
1
1
  import type { Command } from "commander";
2
2
  export declare function registerRuntime(parent: Command): void;
3
+ /**
4
+ * The command to run for the Skills selected on PATH, after checking that it
5
+ * runs the installed entry. A symlink is compared by its physical target, as
6
+ * before; a managed pinned launcher by the exact entry it runs, and it is run
7
+ * through the launcher so the caller's directory configures nothing.
8
+ */
9
+ export declare function selectedSkillsCommand(selected: string, installed: string): string;
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env bun
2
- export {};
2
+ import "../lib/launch-cwd-apply.js";
package/dist/index.d.ts CHANGED
@@ -64,7 +64,7 @@ export { planAgentIntegration, planClaudeStopHookUpdate, planClaudePreToolUseHoo
64
64
  export type { ClaudeManagedHookProjection, ClaudeManagedHookProjectionOptions } from "./lib/agent-integration.js";
65
65
  export { captureDiscoveryDirectories, captureDiscoveryByteSources, captureDiscoveryPathSources, type DiscoveryDirectory, type DiscoverySource } from "./lib/agent-discovery.js";
66
66
  export { captureClaudeMarketplaceRegistry, captureClaudeMarketplaceRegistryV2 } from "./lib/claude-marketplace-registry.js";
67
- export { captureClaudeSettings, captureClaudeSettingsV2, captureClaudeSettingsV3, upgradeClaudeSettingsWitness } from "./lib/claude-settings-witness.js";
67
+ export { captureClaudeSettings, captureClaudeSettingsV2, captureClaudeSettingsV3, captureClaudeSettingsV4, upgradeClaudeSettingsWitness, upgradeClaudeSettingsWitnessV4 } from "./lib/claude-settings-witness.js";
68
68
  export { buildPluginProjection, pluginTreeDigest, type PluginProjectionManifest, type PluginPayloadMapping, type PluginFileWitness } from "./lib/plugin-projection.js";
69
69
  export { planPluginAdmission, admitPlugin, resolveAdmittedPlugin, pluginResolverCommand, type PluginAdmissionTarget, type PluginAdmissionBinding, type PluginAdmissionPrincipal, type PluginAdmissionPlan, type PluginAdmissionIdentity, type PluginBundleIdentity, type PluginRoutingIdentity, type PluginAdmissionReceipt } from "./lib/plugin-admission.js";
70
70
  export { captureManagedPluginRegistry, type ManagedPluginRegistrationWitness } from "./lib/plugin-discovery.js";