@hasna/skills 0.1.72 → 0.2.0

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
@@ -48,11 +48,11 @@ var __require = import.meta.require;
48
48
 
49
49
  // src/lib/registry.ts
50
50
  import { existsSync as existsSync8, readFileSync as readFileSync6, readdirSync as readdirSync6 } from "fs";
51
- import { join as join9 } from "path";
51
+ import { join as join8 } from "path";
52
52
 
53
53
  // src/lib/config.ts
54
54
  import { existsSync as existsSync2, readFileSync, writeFileSync, mkdirSync, copyFileSync, readdirSync, statSync } from "fs";
55
- import { join as join3, dirname } from "path";
55
+ import { join as join2, dirname } from "path";
56
56
 
57
57
  // src/lib/retired-settings.ts
58
58
  var RETIRED_ENV_SUFFIXES = ["_STORAGE_MODE", "_DEPLOYMENT_MODE", "_CLOUD_MODE"];
@@ -102,94 +102,87 @@ function assertNoRetiredConfigKeys(config, source) {
102
102
 
103
103
  // src/lib/app-home.ts
104
104
  import { existsSync } from "fs";
105
- import { homedir as homedir2 } from "os";
106
- import { join as join2, resolve } from "path";
107
-
108
- // ../../node_modules/.bun/@hasna+paths@0.1.0/node_modules/@hasna/paths/dist/index.js
109
105
  import { homedir } from "os";
110
- import { join } from "path";
111
- var KIND_ENV = {
106
+ import { join, resolve } from "path";
107
+ import { homedir as pathsResolverHomedir } from "os";
108
+ import { join as pathsResolverJoin } from "path";
109
+ var PATHS_RESOLVER_KIND_ENV = {
112
110
  config: "HASNA_CONFIG_HOME",
113
111
  data: "HASNA_DATA_HOME",
114
112
  state: "HASNA_STATE_HOME",
115
113
  cache: "HASNA_CACHE_HOME"
116
114
  };
117
- var APP_SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
118
- function assertApp(app) {
115
+ var PATHS_RESOLVER_APP_SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
116
+ function pathsResolverAssertApp(app) {
119
117
  if (typeof app !== "string" || app.length === 0) {
120
118
  throw new TypeError("paths: app must be a non-empty string");
121
119
  }
122
- if (!APP_SLUG_RE.test(app)) {
120
+ if (!PATHS_RESOLVER_APP_SLUG_RE.test(app)) {
123
121
  throw new TypeError(`paths: invalid app slug "${app}" \u2014 expected lowercase kebab-case ([a-z0-9]+(-[a-z0-9]+)*)`);
124
122
  }
125
123
  }
126
- function envOf(options) {
127
- return options.env ?? process.env;
128
- }
129
- function envValue(options, kind) {
130
- const value = envOf(options)[KIND_ENV[kind]];
131
- return typeof value === "string" && value.length > 0 ? value : undefined;
132
- }
133
- function isMacOS(platform) {
134
- return platform === "darwin";
124
+ function pathsResolverAssertKind(kind) {
125
+ if (!Object.keys(PATHS_RESOLVER_KIND_ENV).includes(kind)) {
126
+ throw new TypeError(`paths: invalid path kind "${kind}" \u2014 expected one of ${Object.keys(PATHS_RESOLVER_KIND_ENV).join(", ")}`);
127
+ }
135
128
  }
136
- function baseDir(kind, options) {
137
- const override = envValue(options, kind);
138
- if (override)
129
+ function pathsResolverBaseDir(kind, options) {
130
+ pathsResolverAssertKind(kind);
131
+ const env = options.env ?? process.env;
132
+ const override = env[PATHS_RESOLVER_KIND_ENV[kind]];
133
+ if (typeof override === "string" && override.length > 0)
139
134
  return override;
140
- const home = options.home ?? homedir();
135
+ const home = options.home ?? pathsResolverHomedir();
141
136
  const platform = options.platform ?? process.platform;
142
- if (isMacOS(platform)) {
137
+ if (platform === "darwin") {
143
138
  switch (kind) {
144
139
  case "config":
145
140
  case "data":
146
- return join(home, "Library", "Application Support", "Hasna");
141
+ return pathsResolverJoin(home, "Library", "Application Support", "Hasna");
147
142
  case "cache":
148
- return join(home, "Library", "Caches", "Hasna");
143
+ return pathsResolverJoin(home, "Library", "Caches", "Hasna");
149
144
  case "state":
150
- return join(home, "Library", "Logs", "Hasna");
145
+ return pathsResolverJoin(home, "Library", "Logs", "Hasna");
151
146
  }
152
147
  }
153
148
  switch (kind) {
154
149
  case "config":
155
- return join(home, ".config", "hasna");
150
+ return pathsResolverJoin(home, ".config", "hasna");
156
151
  case "data":
157
- return join(home, ".local", "share", "hasna");
152
+ return pathsResolverJoin(home, ".local", "share", "hasna");
158
153
  case "state":
159
- return join(home, ".local", "state", "hasna");
154
+ return pathsResolverJoin(home, ".local", "state", "hasna");
160
155
  case "cache":
161
- return join(home, ".cache", "hasna");
156
+ return pathsResolverJoin(home, ".cache", "hasna");
162
157
  }
163
158
  }
164
- function resolvePath(kind, options) {
165
- assertApp(options.app);
166
- const appSegment = options.internal === true ? join("internal", options.app) : options.app;
167
- return join(baseDir(kind, options), appSegment);
159
+ function pathsResolverResolve(kind, options) {
160
+ pathsResolverAssertApp(options.app);
161
+ const appSegment = options.internal === true ? pathsResolverJoin("internal", options.app) : options.app;
162
+ return pathsResolverJoin(pathsResolverBaseDir(kind, options), appSegment);
168
163
  }
169
164
  function dataDir(options) {
170
- return resolvePath("data", options);
165
+ return pathsResolverResolve("data", options);
171
166
  }
172
-
173
- // src/lib/app-home.ts
174
167
  var DATA_DIR_ENV = "HASNA_SKILLS_DIR";
175
168
  var HASNA_SKILLS_HOME_ENV = "HASNA_SKILLS_HOME";
176
169
  var SKILLS_HOME_ENV = "SKILLS_HOME";
177
170
  var DEFAULT_SQLITE_FILENAME = "server.db";
178
171
  var GLOBAL_CONFIG_FILENAME = "config.json";
179
172
  function effectiveHome() {
180
- return process.env["HOME"] || process.env["USERPROFILE"] || homedir2() || "/tmp";
173
+ return process.env["HOME"] || process.env["USERPROFILE"] || homedir() || "/tmp";
181
174
  }
182
175
  function legacyDataRoot() {
183
- return join2(effectiveHome(), ".hasna", "skills");
176
+ return join(effectiveHome(), ".hasna", "skills");
184
177
  }
185
- function resolverDataRoot(home = effectiveHome()) {
186
- return dataDir({ app: "skills", home });
178
+ function resolverDataRoot(home = effectiveHome(), env) {
179
+ return dataDir({ app: "skills", home, env });
187
180
  }
188
181
  function adoptResolverDataRoot(resolved, env = process.env) {
189
182
  const dataOverride = env.HASNA_DATA_HOME;
190
183
  if (typeof dataOverride === "string" && dataOverride.trim().length > 0)
191
184
  return true;
192
- return existsSync(join2(resolved, DEFAULT_SQLITE_FILENAME)) || existsSync(join2(resolved, GLOBAL_CONFIG_FILENAME));
185
+ return existsSync(join(resolved, DEFAULT_SQLITE_FILENAME)) || existsSync(join(resolved, GLOBAL_CONFIG_FILENAME));
193
186
  }
194
187
  function exactDataRoot() {
195
188
  for (const key of [DATA_DIR_ENV, HASNA_SKILLS_HOME_ENV, SKILLS_HOME_ENV]) {
@@ -213,8 +206,16 @@ function getDataRoot() {
213
206
  return adoptResolverDataRoot(resolved) ? resolve(resolved) : resolve(legacyDataRoot());
214
207
  }
215
208
  function skillsDataRootForHome(home) {
216
- const resolved = resolverDataRoot(home);
217
- return adoptResolverDataRoot(resolved) ? resolve(resolved) : resolve(join2(home, ".hasna", "skills"));
209
+ const isOwnHome = resolve(home) === resolve(effectiveHome()) || resolve(home) === resolve(homedir());
210
+ if (isOwnHome) {
211
+ const exact = exactDataRoot();
212
+ if (exact)
213
+ return exact;
214
+ const resolved2 = resolverDataRoot(home);
215
+ return adoptResolverDataRoot(resolved2) ? resolve(resolved2) : resolve(join(home, ".hasna", "skills"));
216
+ }
217
+ const resolved = resolverDataRoot(home, {});
218
+ return adoptResolverDataRoot(resolved, {}) ? resolve(resolved) : resolve(join(home, ".hasna", "skills"));
218
219
  }
219
220
 
220
221
  // src/lib/config.ts
@@ -235,8 +236,8 @@ function mergeDirectoryContents(sourceDir, targetDir) {
235
236
  return;
236
237
  mkdirSync(targetDir, { recursive: true });
237
238
  for (const entry of readdirSync(sourceDir)) {
238
- const sourcePath = join3(sourceDir, entry);
239
- const targetPath = join3(targetDir, entry);
239
+ const sourcePath = join2(sourceDir, entry);
240
+ const targetPath = join2(targetDir, entry);
240
241
  try {
241
242
  const sourceStat = statSync(sourcePath);
242
243
  if (sourceStat.isDirectory()) {
@@ -272,7 +273,7 @@ var INSTALLED_SKILLS_DIRNAME = "installed";
272
273
  var SKILLS_CACHE_DIRNAME = "skills";
273
274
  var LAYOUT_MIGRATION_RECORD = ".layout-migration.json";
274
275
  function isOwnerLayoutMigrated(appDir) {
275
- return existsSync2(join3(appDir, SKILLS_CACHE_DIRNAME, LAYOUT_MIGRATION_RECORD));
276
+ return existsSync2(join2(appDir, SKILLS_CACHE_DIRNAME, LAYOUT_MIGRATION_RECORD));
276
277
  }
277
278
  function getDataDir() {
278
279
  const root = getDataRoot();
@@ -282,23 +283,23 @@ function getDataDir() {
282
283
  if (hasOperatorOverride())
283
284
  return root;
284
285
  const home = effectiveHome();
285
- const oldDir = join3(home, ".skills");
286
- const oldConfigFile = join3(home, ".skillsrc");
286
+ const oldDir = join2(home, ".skills");
287
+ const oldConfigFile = join2(home, ".skillsrc");
287
288
  try {
288
289
  mergeDirectoryContents(oldDir, root);
289
290
  } catch {}
290
- if (existsSync2(oldConfigFile) && !existsSync2(join3(root, "config.json"))) {
291
+ if (existsSync2(oldConfigFile) && !existsSync2(join2(root, "config.json"))) {
291
292
  try {
292
- copyFileSync(oldConfigFile, join3(root, "config.json"));
293
+ copyFileSync(oldConfigFile, join2(root, "config.json"));
293
294
  } catch {}
294
295
  }
295
296
  return root;
296
297
  }
297
298
  function getConfigPath(scope) {
298
299
  if (scope === "global") {
299
- return join3(getDataDir(), "config.json");
300
+ return join2(getDataDir(), "config.json");
300
301
  }
301
- return join3(process.cwd(), "skills.config.json");
302
+ return join2(process.cwd(), "skills.config.json");
302
303
  }
303
304
  function readConfigFile(path) {
304
305
  if (!existsSync2(path))
@@ -393,7 +394,7 @@ import {
393
394
  statSync as statSync6,
394
395
  writeFileSync as writeFileSync3
395
396
  } from "fs";
396
- import { basename as basename2, dirname as dirname3, isAbsolute as isAbsolute2, join as join8, normalize as normalize2 } from "path";
397
+ import { basename as basename2, dirname as dirname3, isAbsolute as isAbsolute2, join as join7, normalize as normalize2 } from "path";
397
398
 
398
399
  // src/lib/registry-data/development-tools.ts
399
400
  var DEVELOPMENT_TOOLS_SKILLS = [
@@ -607,14 +608,6 @@ var DEVELOPMENT_TOOLS_SKILLS = [
607
608
  category: "Development Tools",
608
609
  tags: ["storage", "backend", "postgresql", "sqlite", "two-backend", "oss-app"],
609
610
  kind: "instruction"
610
- },
611
- {
612
- name: "session-inject-monitor",
613
- displayName: "Session Inject Monitor",
614
- description: "Set up a declarative monitor that injects a prompt into a live coding-agent session when a watched source (conversations, email, todos, knowledge, command output) has new content",
615
- category: "Development Tools",
616
- tags: ["monitor", "session", "injection", "automation", "wake"],
617
- kind: "instruction"
618
611
  }
619
612
  ];
620
613
 
@@ -1114,7 +1107,7 @@ var SKILLS = [
1114
1107
 
1115
1108
  // src/lib/hosted-skill-set.ts
1116
1109
  import { existsSync as existsSync3, readFileSync as readFileSync2, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
1117
- import { join as join4 } from "path";
1110
+ import { join as join3 } from "path";
1118
1111
  var HOSTED_RUNTIMES = new Set(["hosted"]);
1119
1112
  var HOSTED_SOURCES = new Set(["remote", "private-hosted"]);
1120
1113
  function normalizeMarker(value) {
@@ -1127,7 +1120,7 @@ function isHostedMetadataPackage(pkg) {
1127
1120
  return HOSTED_RUNTIMES.has(normalizeMarker(skills.runtime)) || HOSTED_SOURCES.has(normalizeMarker(skills.source));
1128
1121
  }
1129
1122
  function isHostedMetadataSkillDir(skillDir) {
1130
- const pkgPath = join4(skillDir, "package.json");
1123
+ const pkgPath = join3(skillDir, "package.json");
1131
1124
  if (!existsSync3(pkgPath))
1132
1125
  return false;
1133
1126
  try {
@@ -1153,7 +1146,7 @@ var SINGLE_SOURCE_EXCLUSION = new RegExp(`^!skills/(${SLUG})/src$`);
1153
1146
 
1154
1147
  // src/lib/skill-validation.ts
1155
1148
  import { existsSync as existsSync4, lstatSync, readFileSync as readFileSync3, readdirSync as readdirSync3, statSync as statSync3 } from "fs";
1156
- import { isAbsolute, join as join5, normalize } from "path";
1149
+ import { isAbsolute, join as join4, normalize } from "path";
1157
1150
  var VALID_SKILL_KINDS = ["executable", "instruction"];
1158
1151
  var DOC_FILES = ["SKILL.md", "README.md", "CLAUDE.md"];
1159
1152
  var RESERVED_SKILL_ENTRIES = new Set([
@@ -1306,7 +1299,7 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
1306
1299
  add(issues, "skill.name_invalid", `Skill name '${bareName}' must use lowercase letters, numbers, dots, underscores, or hyphens`);
1307
1300
  }
1308
1301
  for (const entry of readdirSync3(skillPath).sort()) {
1309
- const entryPath = join5(skillPath, entry);
1302
+ const entryPath = join4(skillPath, entry);
1310
1303
  if (RESERVED_SKILL_ENTRIES.has(entry)) {
1311
1304
  add(issues, "skill.reserved_file", `Reserved file '${entry}' is not allowed in skill packages`);
1312
1305
  }
@@ -1318,13 +1311,13 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
1318
1311
  }
1319
1312
  }
1320
1313
  for (const docFile of DOC_FILES) {
1321
- if (existsSync4(join5(skillPath, docFile)))
1314
+ if (existsSync4(join4(skillPath, docFile)))
1322
1315
  metadata.docFiles.push(docFile);
1323
1316
  }
1324
1317
  if (metadata.docFiles.length === 0) {
1325
1318
  add(issues, "skill.docs_missing", "Missing documentation file: expected SKILL.md, README.md, or CLAUDE.md");
1326
1319
  }
1327
- const skillMdPath = join5(skillPath, "SKILL.md");
1320
+ const skillMdPath = join4(skillPath, "SKILL.md");
1328
1321
  if (existsSync4(skillMdPath)) {
1329
1322
  const frontmatter = parseSkillFrontmatter(readFileSync3(skillMdPath, "utf-8"));
1330
1323
  if (!frontmatter) {
@@ -1368,7 +1361,7 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
1368
1361
  }
1369
1362
  metadata.kind = resolvedKind;
1370
1363
  const isInstruction = resolvedKind === "instruction";
1371
- const pkgPath = join5(skillPath, "package.json");
1364
+ const pkgPath = join4(skillPath, "package.json");
1372
1365
  if (!existsSync4(pkgPath)) {
1373
1366
  if (!isInstruction)
1374
1367
  add(issues, "package.missing", "Missing package.json");
@@ -1427,7 +1420,7 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
1427
1420
  add(issues, "package.bin_target_unsafe", `package.json bin '${command}' target '${target}' must stay inside the skill directory`);
1428
1421
  continue;
1429
1422
  }
1430
- const targetPath = join5(skillPath, target);
1423
+ const targetPath = join4(skillPath, target);
1431
1424
  if (!existsSync4(targetPath)) {
1432
1425
  add(warnings, "package.bin_target_missing", `package.json bin '${command}' target '${target}' is not present before build`);
1433
1426
  } else if (statSync3(targetPath).isDirectory()) {
@@ -1445,17 +1438,17 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
1445
1438
  metadata.runtime = "none";
1446
1439
  } else {
1447
1440
  metadata.runtime = hostedMetadata ? "hosted" : "local";
1448
- const srcDir = join5(skillPath, "src");
1441
+ const srcDir = join4(skillPath, "src");
1449
1442
  if (hostedMetadata) {
1450
1443
  if (existsSync4(srcDir)) {
1451
1444
  add(issues, "skill.hosted_source_forbidden", "Hosted metadata skills must not include local implementation source");
1452
1445
  }
1453
1446
  } else if (!existsSync4(srcDir)) {
1454
1447
  add(issues, "skill.src_missing", "Missing src/ directory");
1455
- } else if (!existsSync4(join5(srcDir, "index.ts")) && !existsSync4(join5(srcDir, "index.js"))) {
1448
+ } else if (!existsSync4(join4(srcDir, "index.ts")) && !existsSync4(join4(srcDir, "index.js"))) {
1456
1449
  add(issues, "skill.src_index_missing", "Missing src/index.ts or src/index.js");
1457
1450
  } else {
1458
- const indexPath = existsSync4(join5(srcDir, "index.ts")) ? join5(srcDir, "index.ts") : join5(srcDir, "index.js");
1451
+ const indexPath = existsSync4(join4(srcDir, "index.ts")) ? join4(srcDir, "index.ts") : join4(srcDir, "index.js");
1459
1452
  const size = statSync3(indexPath).size;
1460
1453
  if (size < 50)
1461
1454
  add(warnings, "skill.src_index_minimal", `Source entry point is very small (${size}B)`);
@@ -1480,7 +1473,7 @@ function validateRegistryConsistency(registry, skillsDir) {
1480
1473
  return false;
1481
1474
  })));
1482
1475
  const skillDirs = existsSync4(skillsDir) ? readdirSync3(skillsDir).filter((entry) => {
1483
- const fullPath = join5(skillsDir, entry);
1476
+ const fullPath = join4(skillsDir, entry);
1484
1477
  return !entry.startsWith(".") && entry !== "_common" && statSync3(fullPath).isDirectory();
1485
1478
  }) : [];
1486
1479
  const directoryNames = new Set(skillDirs);
@@ -1498,7 +1491,7 @@ function validateRegistryConsistency(registry, skillsDir) {
1498
1491
  // src/lib/skill-hash.ts
1499
1492
  import { createHash } from "crypto";
1500
1493
  import { existsSync as existsSync5, readdirSync as readdirSync4, readFileSync as readFileSync4, statSync as statSync4 } from "fs";
1501
- import { join as join6, sep } from "path";
1494
+ import { join as join5, sep } from "path";
1502
1495
  var CONTENT_HASH_ALGORITHM = "sha256";
1503
1496
  var HASH_EXCLUDE_DIRS = new Set([".git", "node_modules", "dist", "build", ".turbo"]);
1504
1497
  var HASH_COVERAGE = [
@@ -1557,7 +1550,7 @@ function collectBundleFiles(skillPath) {
1557
1550
  if (seen.has(entry))
1558
1551
  continue;
1559
1552
  seen.add(entry);
1560
- const absolute = join6(skillPath, entry);
1553
+ const absolute = join5(skillPath, entry);
1561
1554
  if (!existsSync5(absolute))
1562
1555
  continue;
1563
1556
  if (statSync4(absolute).isDirectory())
@@ -1571,7 +1564,7 @@ function collectDirectory(files, dir, rel) {
1571
1564
  for (const entry of readdirSync4(dir).sort()) {
1572
1565
  if (entry.startsWith("."))
1573
1566
  continue;
1574
- const absolute = join6(dir, entry);
1567
+ const absolute = join5(dir, entry);
1575
1568
  const childRel = `${rel}/${entry}`;
1576
1569
  let stats;
1577
1570
  try {
@@ -1798,7 +1791,7 @@ import {
1798
1791
  realpathSync,
1799
1792
  writeFileSync as writeFileSync2
1800
1793
  } from "fs";
1801
- import { basename, dirname as dirname2, join as join7, relative } from "path";
1794
+ import { basename, dirname as dirname2, join as join6, relative } from "path";
1802
1795
  var ANY_SEGMENT_COPY_EXCLUDES = new Set([
1803
1796
  ".git",
1804
1797
  ".DS_Store",
@@ -1841,9 +1834,9 @@ function normalizePortableSkillName(name) {
1841
1834
  return normalized;
1842
1835
  }
1843
1836
  function readPortableSkillManifest(skillPath, fallbackName = basename(skillPath)) {
1844
- const skillJsonPath = join7(skillPath, "skill.json");
1845
- const skillMdPath = join7(skillPath, "SKILL.md");
1846
- const pkgPath = join7(skillPath, "package.json");
1837
+ const skillJsonPath = join6(skillPath, "skill.json");
1838
+ const skillMdPath = join6(skillPath, "SKILL.md");
1839
+ const pkgPath = join6(skillPath, "package.json");
1847
1840
  const jsonManifest = existsSync6(skillJsonPath) ? readJsonObject(skillJsonPath) : undefined;
1848
1841
  const frontmatter = existsSync6(skillMdPath) ? parseSkillFrontmatter(readFileSync5(skillMdPath, "utf-8")) ?? undefined : undefined;
1849
1842
  const pkg = existsSync6(pkgPath) ? readJsonObject(pkgPath) : undefined;
@@ -1892,7 +1885,7 @@ function createInstructionManifest(name, options) {
1892
1885
  }
1893
1886
  function writeInstructionSkillTemplate(skillPath, manifest) {
1894
1887
  mkdirSync2(skillPath, { recursive: true });
1895
- writeFileSync2(join7(skillPath, "SKILL.md"), renderInstructionSkillMd(manifest));
1888
+ writeFileSync2(join6(skillPath, "SKILL.md"), renderInstructionSkillMd(manifest));
1896
1889
  writeSkillJsonWithHash(skillPath, manifest);
1897
1890
  }
1898
1891
  function renderInstructionSkillMd(manifest) {
@@ -1941,12 +1934,12 @@ function createPortableManifest(name, options) {
1941
1934
  };
1942
1935
  }
1943
1936
  function writePortableSkillTemplate(skillPath, manifest) {
1944
- mkdirSync2(join7(skillPath, "src"), { recursive: true });
1945
- writeFileSync2(join7(skillPath, "SKILL.md"), renderSkillMd(manifest));
1946
- writeFileSync2(join7(skillPath, "AGENTS.md"), renderAgentsMd(manifest));
1947
- writeFileSync2(join7(skillPath, "package.json"), renderPackageJson(manifest));
1948
- writeFileSync2(join7(skillPath, "tsconfig.json"), renderTsconfig());
1949
- writeFileSync2(join7(skillPath, "src", "index.ts"), renderEntrypoint(manifest));
1937
+ mkdirSync2(join6(skillPath, "src"), { recursive: true });
1938
+ writeFileSync2(join6(skillPath, "SKILL.md"), renderSkillMd(manifest));
1939
+ writeFileSync2(join6(skillPath, "AGENTS.md"), renderAgentsMd(manifest));
1940
+ writeFileSync2(join6(skillPath, "package.json"), renderPackageJson(manifest));
1941
+ writeFileSync2(join6(skillPath, "tsconfig.json"), renderTsconfig());
1942
+ writeFileSync2(join6(skillPath, "src", "index.ts"), renderEntrypoint(manifest));
1950
1943
  writeSkillJsonWithHash(skillPath, manifest);
1951
1944
  }
1952
1945
  function fillContractDefaults(manifest, entrypoint) {
@@ -1971,7 +1964,7 @@ function writeSkillJsonWithHash(skillPath, manifest) {
1971
1964
  content_hash: undefined
1972
1965
  }
1973
1966
  };
1974
- writeFileSync2(join7(skillPath, "skill.json"), `${JSON.stringify({ ...existing, ...renderSkillJsonObject(withoutHash) }, null, 2)}
1967
+ writeFileSync2(join6(skillPath, "skill.json"), `${JSON.stringify({ ...existing, ...renderSkillJsonObject(withoutHash) }, null, 2)}
1975
1968
  `);
1976
1969
  const hash = computeContentHash(skillPath);
1977
1970
  const withHash = {
@@ -1981,12 +1974,12 @@ function writeSkillJsonWithHash(skillPath, manifest) {
1981
1974
  content_hash: hash
1982
1975
  }
1983
1976
  };
1984
- writeFileSync2(join7(skillPath, "skill.json"), `${JSON.stringify({ ...existing, ...renderSkillJsonObject(withHash) }, null, 2)}
1977
+ writeFileSync2(join6(skillPath, "skill.json"), `${JSON.stringify({ ...existing, ...renderSkillJsonObject(withHash) }, null, 2)}
1985
1978
  `);
1986
1979
  return withHash;
1987
1980
  }
1988
1981
  function readExistingSkillJson(skillPath) {
1989
- const path = join7(skillPath, "skill.json");
1982
+ const path = join6(skillPath, "skill.json");
1990
1983
  if (!existsSync6(path))
1991
1984
  return {};
1992
1985
  try {
@@ -2020,24 +2013,24 @@ function ensurePortableSkillFiles(skillPath, manifest) {
2020
2013
  tags: next.tags?.length ? next.tags : ["custom", next.name]
2021
2014
  };
2022
2015
  const entry = next.commands[0]?.entry ?? "src/index.ts";
2023
- if (entry && !existsSync6(join7(skillPath, entry))) {
2024
- mkdirSync2(dirname2(join7(skillPath, entry)), { recursive: true });
2025
- writeFileSync2(join7(skillPath, entry), renderEntrypoint(next));
2016
+ if (entry && !existsSync6(join6(skillPath, entry))) {
2017
+ mkdirSync2(dirname2(join6(skillPath, entry)), { recursive: true });
2018
+ writeFileSync2(join6(skillPath, entry), renderEntrypoint(next));
2026
2019
  }
2027
- if (!existsSync6(join7(skillPath, "SKILL.md")))
2028
- writeFileSync2(join7(skillPath, "SKILL.md"), renderSkillMd(next));
2020
+ if (!existsSync6(join6(skillPath, "SKILL.md")))
2021
+ writeFileSync2(join6(skillPath, "SKILL.md"), renderSkillMd(next));
2029
2022
  else
2030
- writeFileSync2(join7(skillPath, "SKILL.md"), ensureSkillMdFrontmatter(readFileSync5(join7(skillPath, "SKILL.md"), "utf-8"), next));
2031
- if (!existsSync6(join7(skillPath, "AGENTS.md")))
2032
- writeFileSync2(join7(skillPath, "AGENTS.md"), renderAgentsMd(next));
2023
+ writeFileSync2(join6(skillPath, "SKILL.md"), ensureSkillMdFrontmatter(readFileSync5(join6(skillPath, "SKILL.md"), "utf-8"), next));
2024
+ if (!existsSync6(join6(skillPath, "AGENTS.md")))
2025
+ writeFileSync2(join6(skillPath, "AGENTS.md"), renderAgentsMd(next));
2033
2026
  ensurePackageJson(skillPath, next);
2034
- if (!existsSync6(join7(skillPath, "tsconfig.json")))
2035
- writeFileSync2(join7(skillPath, "tsconfig.json"), renderTsconfig());
2027
+ if (!existsSync6(join6(skillPath, "tsconfig.json")))
2028
+ writeFileSync2(join6(skillPath, "tsconfig.json"), renderTsconfig());
2036
2029
  writeSkillJsonWithHash(skillPath, next);
2037
2030
  return readPortableSkillManifest(skillPath, next.name);
2038
2031
  }
2039
2032
  function ensurePackageJson(skillPath, manifest) {
2040
- const pkgPath = join7(skillPath, "package.json");
2033
+ const pkgPath = join6(skillPath, "package.json");
2041
2034
  const first = manifest.commands[0] ?? { name: manifest.name, entry: "src/index.ts" };
2042
2035
  const commandName = normalizePortableSkillName(first.name || manifest.name);
2043
2036
  const entry = (first.entry ?? "src/index.ts").replace(/^\.\//, "");
@@ -2084,8 +2077,8 @@ function ensureInstructionSkillFiles(skillPath, manifest) {
2084
2077
  inputs: [],
2085
2078
  commands: []
2086
2079
  };
2087
- if (!existsSync6(join7(skillPath, "SKILL.md"))) {
2088
- writeFileSync2(join7(skillPath, "SKILL.md"), renderSkillMd(next));
2080
+ if (!existsSync6(join6(skillPath, "SKILL.md"))) {
2081
+ writeFileSync2(join6(skillPath, "SKILL.md"), renderSkillMd(next));
2089
2082
  }
2090
2083
  writeSkillJsonWithHash(skillPath, next);
2091
2084
  return readPortableSkillManifest(skillPath, next.name);
@@ -2377,18 +2370,18 @@ var LEGACY_CUSTOM_DIRNAME = "custom";
2377
2370
  function getPortableSkillsRoot(options = {}) {
2378
2371
  if (options.rootDir)
2379
2372
  return options.rootDir;
2380
- const appDir = options.homeDir ? join8(options.homeDir, ".hasna", "skills") : getDataDir();
2381
- const cache = join8(appDir, SKILLS_CACHE_DIRNAME);
2373
+ const appDir = options.homeDir ? join7(options.homeDir, ".hasna", "skills") : getDataDir();
2374
+ const cache = join7(appDir, SKILLS_CACHE_DIRNAME);
2382
2375
  if (isOwnerLayoutMigrated(appDir) && safeIsDirectory(cache))
2383
2376
  return cache;
2384
- const installed = join8(appDir, INSTALLED_SKILLS_DIRNAME);
2377
+ const installed = join7(appDir, INSTALLED_SKILLS_DIRNAME);
2385
2378
  migrateLegacySkillLayout(appDir, installed);
2386
2379
  return installed;
2387
2380
  }
2388
2381
  function looksLikeSkillDirectory(path) {
2389
2382
  if (!safeIsDirectory(path))
2390
2383
  return false;
2391
- return existsSync7(join8(path, "SKILL.md")) || existsSync7(join8(path, "skill.json")) || existsSync7(join8(path, "package.json"));
2384
+ return existsSync7(join7(path, "SKILL.md")) || existsSync7(join7(path, "skill.json")) || existsSync7(join7(path, "package.json"));
2392
2385
  }
2393
2386
  function migrateLegacySkillLayout(appDir, installed) {
2394
2387
  if (!safeIsDirectory(appDir))
@@ -2398,7 +2391,7 @@ function migrateLegacySkillLayout(appDir, installed) {
2398
2391
  for (const entry of readdirSync5(appDir)) {
2399
2392
  if (entry.startsWith(".") || entry === INSTALLED_SKILLS_DIRNAME)
2400
2393
  continue;
2401
- const path = join8(appDir, entry);
2394
+ const path = join7(appDir, entry);
2402
2395
  if (entry === LEGACY_CUSTOM_DIRNAME) {
2403
2396
  if (!safeIsDirectory(path))
2404
2397
  continue;
@@ -2406,7 +2399,7 @@ function migrateLegacySkillLayout(appDir, installed) {
2406
2399
  for (const nested of readdirSync5(path)) {
2407
2400
  if (nested.startsWith("."))
2408
2401
  continue;
2409
- const nestedPath = join8(path, nested);
2402
+ const nestedPath = join7(path, nested);
2410
2403
  if (looksLikeSkillDirectory(nestedPath))
2411
2404
  candidates.push({ from: nestedPath, name: nested });
2412
2405
  }
@@ -2420,10 +2413,10 @@ function migrateLegacySkillLayout(appDir, installed) {
2420
2413
  return;
2421
2414
  }
2422
2415
  for (const { from, name } of candidates) {
2423
- const target = join8(installed, name);
2416
+ const target = join7(installed, name);
2424
2417
  if (existsSync7(target))
2425
2418
  continue;
2426
- const staging = join8(installed, `.migrating-${name}-${process.pid}`);
2419
+ const staging = join7(installed, `.migrating-${name}-${process.pid}`);
2427
2420
  try {
2428
2421
  rmSync(staging, { recursive: true, force: true });
2429
2422
  cpSync2(from, staging, { recursive: true, errorOnExist: false });
@@ -2436,7 +2429,7 @@ function migrateLegacySkillLayout(appDir, installed) {
2436
2429
  }
2437
2430
  }
2438
2431
  function getPortableSkillPath(name, options = {}) {
2439
- return join8(getPortableSkillsRoot(options), normalizePortableSkillName(name));
2432
+ return join7(getPortableSkillsRoot(options), normalizePortableSkillName(name));
2440
2433
  }
2441
2434
  function findPortableSkill(name, options = {}) {
2442
2435
  let normalized;
@@ -2462,7 +2455,7 @@ function listPortableSkills(options = {}) {
2462
2455
  for (const entry of readdirSync5(root).sort()) {
2463
2456
  if (entry.startsWith("."))
2464
2457
  continue;
2465
- const path = join8(root, entry);
2458
+ const path = join7(root, entry);
2466
2459
  if (!safeIsDirectory(path))
2467
2460
  continue;
2468
2461
  try {
@@ -2496,7 +2489,7 @@ function isOfficialSkillName(name) {
2496
2489
  function scaffoldPortableSkill(name, options = {}) {
2497
2490
  const skillName = normalizePortableSkillName(name);
2498
2491
  const root = getPortableSkillsRoot(options);
2499
- const skillPath = join8(root, skillName);
2492
+ const skillPath = join7(root, skillName);
2500
2493
  if (existsSync7(skillPath)) {
2501
2494
  if (!options.overwrite)
2502
2495
  throw new Error(`Skill '${skillName}' already exists at ${skillPath}`);
@@ -2528,7 +2521,7 @@ function portPortableSkillDirectory(sourceDir, options = {}) {
2528
2521
  const skipped = [];
2529
2522
  const entries = readdirSync5(absoluteSource, { withFileTypes: true }).map((entry) => entry.name).filter((entryName) => !entryName.startsWith(".")).sort();
2530
2523
  for (const entryName of entries) {
2531
- const childPath = join8(absoluteSource, entryName);
2524
+ const childPath = join7(absoluteSource, entryName);
2532
2525
  if (!safeIsDirectory(childPath))
2533
2526
  continue;
2534
2527
  if (!isSkillCandidate(childPath)) {
@@ -2557,7 +2550,7 @@ function portPortableSkillDirectory(sourceDir, options = {}) {
2557
2550
  };
2558
2551
  }
2559
2552
  function isSkillCandidate(dir) {
2560
- return existsSync7(join8(dir, "SKILL.md")) || existsSync7(join8(dir, "skill.json")) || existsSync7(join8(dir, "package.json"));
2553
+ return existsSync7(join7(dir, "SKILL.md")) || existsSync7(join7(dir, "skill.json")) || existsSync7(join7(dir, "package.json"));
2561
2554
  }
2562
2555
  function portPortableSkill(sourcePath, options = {}) {
2563
2556
  const absoluteSource = normalize2(sourcePath);
@@ -2573,7 +2566,7 @@ function portPortableSkill(sourcePath, options = {}) {
2573
2566
  throw new Error(`${via} Importing it would shadow the official '${skillName}'. ` + `Pass --name to choose a different name, or --allow-shadow to override deliberately.`);
2574
2567
  }
2575
2568
  const root = getPortableSkillsRoot(options);
2576
- const destination = join8(root, skillName);
2569
+ const destination = join7(root, skillName);
2577
2570
  if (existsSync7(destination)) {
2578
2571
  if (!options.overwrite)
2579
2572
  throw new Error(`Skill '${skillName}' already exists at ${destination}`);
@@ -2616,10 +2609,10 @@ function buildCorpusManifest(input, name) {
2616
2609
  function writeCorpusSkill(input, options = {}) {
2617
2610
  const name = normalizePortableSkillName(input.name);
2618
2611
  const root = getPortableSkillsRoot(options);
2619
- const skillPath = join8(root, name);
2612
+ const skillPath = join7(root, name);
2620
2613
  const created = !existsSync7(skillPath);
2621
2614
  mkdirSync3(skillPath, { recursive: true });
2622
- writeFileSync3(join8(skillPath, "SKILL.md"), input.skillMd);
2615
+ writeFileSync3(join7(skillPath, "SKILL.md"), input.skillMd);
2623
2616
  const manifest = buildCorpusManifest(input, name);
2624
2617
  writeSkillJsonWithHash(skillPath, manifest);
2625
2618
  return { name, path: skillPath, manifest, created };
@@ -2627,19 +2620,19 @@ function writeCorpusSkill(input, options = {}) {
2627
2620
  function installCorpusSkillAtomically(input, options = {}) {
2628
2621
  const name = normalizePortableSkillName(input.name);
2629
2622
  const root = getPortableSkillsRoot(options);
2630
- const target = join8(root, name);
2623
+ const target = join7(root, name);
2631
2624
  const created = !existsSync7(target);
2632
2625
  mkdirSync3(root, { recursive: true });
2633
- const staging = mkdtempSync(join8(root, `.pull-${name}-`));
2626
+ const staging = mkdtempSync(join7(root, `.pull-${name}-`));
2634
2627
  let moved = false;
2635
2628
  let backup = null;
2636
2629
  try {
2637
- writeFileSync3(join8(staging, "SKILL.md"), input.skillMd);
2630
+ writeFileSync3(join7(staging, "SKILL.md"), input.skillMd);
2638
2631
  const manifest = buildCorpusManifest(input, name);
2639
2632
  writeSkillJsonWithHash(staging, manifest);
2640
2633
  if (existsSync7(target)) {
2641
- backup = mkdtempSync(join8(root, `.pull-backup-${name}-`));
2642
- renameSync(target, join8(backup, name));
2634
+ backup = mkdtempSync(join7(root, `.pull-backup-${name}-`));
2635
+ renameSync(target, join7(backup, name));
2643
2636
  moved = true;
2644
2637
  }
2645
2638
  renameSync(staging, target);
@@ -2648,9 +2641,9 @@ function installCorpusSkillAtomically(input, options = {}) {
2648
2641
  return { name, path: target, manifest, created };
2649
2642
  } catch (error) {
2650
2643
  rmSync(staging, { recursive: true, force: true });
2651
- if (moved && backup && existsSync7(join8(backup, name))) {
2644
+ if (moved && backup && existsSync7(join7(backup, name))) {
2652
2645
  try {
2653
- renameSync(join8(backup, name), target);
2646
+ renameSync(join7(backup, name), target);
2654
2647
  } catch {}
2655
2648
  }
2656
2649
  throw error;
@@ -2663,8 +2656,8 @@ function validatePortableSkillDirectory(name, skillPath) {
2663
2656
  const warnings = [...base.warnings];
2664
2657
  let manifest;
2665
2658
  if (existsSync7(skillPath)) {
2666
- const skillJsonPath = join8(skillPath, "skill.json");
2667
- const skillMdPath = join8(skillPath, "SKILL.md");
2659
+ const skillJsonPath = join7(skillPath, "skill.json");
2660
+ const skillMdPath = join7(skillPath, "SKILL.md");
2668
2661
  if (!existsSync7(skillJsonPath) && !existsSync7(skillMdPath)) {
2669
2662
  add3(issues, "portable.manifest_missing", "Missing portable manifest: expected SKILL.md frontmatter and/or skill.json");
2670
2663
  }
@@ -2684,7 +2677,7 @@ function validatePortableSkillDirectory(name, skillPath) {
2684
2677
  add3(issues, "portable.version_missing", "Portable manifest missing version");
2685
2678
  }
2686
2679
  const contractIssues = validatePortableManifestContract(manifest, {
2687
- strict: existsSync7(join8(skillPath, "skill.json")),
2680
+ strict: existsSync7(join7(skillPath, "skill.json")),
2688
2681
  skillPath
2689
2682
  });
2690
2683
  for (const issue of contractIssues)
@@ -2720,7 +2713,7 @@ function validatePortableSkillDirectory(name, skillPath) {
2720
2713
  add3(issues, "portable.command_entry_unsafe", `Command '${command.name}' entry '${command.entry}' must stay inside the skill directory`);
2721
2714
  continue;
2722
2715
  }
2723
- const entryPath = join8(skillPath, command.entry);
2716
+ const entryPath = join7(skillPath, command.entry);
2724
2717
  if (!existsSync7(entryPath))
2725
2718
  add3(issues, "portable.command_entry_missing", `Command '${command.name}' entry '${command.entry}' is missing`);
2726
2719
  else if (statSync6(entryPath).isDirectory())
@@ -2731,7 +2724,7 @@ function validatePortableSkillDirectory(name, skillPath) {
2731
2724
  } catch (error) {
2732
2725
  add3(issues, "portable.manifest_invalid", error.message);
2733
2726
  }
2734
- if (manifest?.kind !== "instruction" && !existsSync7(join8(skillPath, "AGENTS.md"))) {
2727
+ if (manifest?.kind !== "instruction" && !existsSync7(join7(skillPath, "AGENTS.md"))) {
2735
2728
  add3(issues, "portable.agents_missing", "Missing AGENTS.md with build-out instructions for coding agents");
2736
2729
  }
2737
2730
  }
@@ -2767,12 +2760,12 @@ async function runPortableSkill(name, args, options = {}) {
2767
2760
  if (!isSafeRelativePath2(command.entry)) {
2768
2761
  return { exitCode: 1, error: `Portable skill '${name}' command entry is unsafe` };
2769
2762
  }
2770
- const entryPath = join8(skill.path, command.entry);
2763
+ const entryPath = join7(skill.path, command.entry);
2771
2764
  if (!existsSync7(entryPath)) {
2772
2765
  return { exitCode: 1, error: `Entry point '${command.entry}' not found in portable skill '${name}'` };
2773
2766
  }
2774
- const pkgPath = join8(skill.path, "package.json");
2775
- const nodeModules = join8(skill.path, "node_modules");
2767
+ const pkgPath = join7(skill.path, "package.json");
2768
+ const nodeModules = join7(skill.path, "node_modules");
2776
2769
  if (existsSync7(pkgPath) && !existsSync7(nodeModules) && hasPackageDependencies(pkgPath)) {
2777
2770
  const install = Bun.spawn(["bun", "install", "--no-save"], {
2778
2771
  cwd: skill.path,
@@ -3050,7 +3043,7 @@ function discoverSkillsInDir(dir, source = "custom") {
3050
3043
  for (const entry of entries) {
3051
3044
  if (!entry.isDirectory())
3052
3045
  continue;
3053
- const skillMdPath = join9(dir, entry.name, "SKILL.md");
3046
+ const skillMdPath = join8(dir, entry.name, "SKILL.md");
3054
3047
  if (!existsSync8(skillMdPath))
3055
3048
  continue;
3056
3049
  let content;
@@ -3070,7 +3063,7 @@ function discoverSkillsInDir(dir, source = "custom") {
3070
3063
  category: fm.category || "Development Tools",
3071
3064
  tags: fm.tags || [],
3072
3065
  ...fm.kind ? { kind: fm.kind } : {},
3073
- ...isHostedMetadataSkillDir(join9(dir, entry.name)) ? { serverOwned: true } : {},
3066
+ ...isHostedMetadataSkillDir(join8(dir, entry.name)) ? { serverOwned: true } : {},
3074
3067
  source
3075
3068
  });
3076
3069
  }
@@ -3086,8 +3079,8 @@ function findExtensionSkillPath(name) {
3086
3079
  for (const entry of entries) {
3087
3080
  if (!entry.isDirectory())
3088
3081
  continue;
3089
- const skillDir = join9(config.extensionsDir, entry.name);
3090
- const skillMdPath = join9(skillDir, "SKILL.md");
3082
+ const skillDir = join8(config.extensionsDir, entry.name);
3083
+ const skillMdPath = join8(skillDir, "SKILL.md");
3091
3084
  if (!existsSync8(skillMdPath))
3092
3085
  continue;
3093
3086
  let content;
@@ -3125,7 +3118,7 @@ function loadRegistry(cwd) {
3125
3118
  const official = SKILLS.map((s) => ({ ...s, source: "official" }));
3126
3119
  const extensions = config.extensionsDir ? discoverSkillsInDir(config.extensionsDir, "extension") : [];
3127
3120
  const portableCustom = listPortableSkillMetas();
3128
- const legacyCustom = discoverSkillsInDir(join9(dataDir2, "custom"));
3121
+ const legacyCustom = discoverSkillsInDir(join8(dataDir2, "custom"));
3129
3122
  const globalCustom = mergeCustomSkills([...legacyCustom, ...portableCustom]);
3130
3123
  registryCache = mergeSkillRegistryLists(official, extensions, globalCustom);
3131
3124
  registryCacheTime = now;
@@ -3173,8 +3166,8 @@ function getAllTags() {
3173
3166
  }
3174
3167
  // src/lib/installer.ts
3175
3168
  import { existsSync as existsSync11, readFileSync as readFileSync9, rmSync as rmSync3 } from "fs";
3176
- import { dirname as dirname5, join as join12 } from "path";
3177
- import { homedir as homedir4 } from "os";
3169
+ import { dirname as dirname5, join as join11 } from "path";
3170
+ import { homedir as homedir3 } from "os";
3178
3171
  import { fileURLToPath } from "url";
3179
3172
 
3180
3173
  // src/lib/agent-sync.ts
@@ -3190,8 +3183,8 @@ import {
3190
3183
  statSync as statSync7,
3191
3184
  writeFileSync as writeFileSync4
3192
3185
  } from "fs";
3193
- import { homedir as homedir3 } from "os";
3194
- import { basename as basename3, dirname as dirname4, join as join10 } from "path";
3186
+ import { homedir as homedir2 } from "os";
3187
+ import { basename as basename3, dirname as dirname4, join as join9 } from "path";
3195
3188
  // src/lib/home-migration.ts
3196
3189
  function resolveCorpusRoot(options = {}) {
3197
3190
  return getPortableSkillsRoot(options);
@@ -3213,12 +3206,12 @@ function resolveSyncAgents(arg) {
3213
3206
  }
3214
3207
  return [arg];
3215
3208
  }
3216
- function agentGlobalSkillsDir(agent, homeDir = homedir3()) {
3209
+ function agentGlobalSkillsDir(agent, homeDir = homedir2()) {
3217
3210
  switch (agent) {
3218
3211
  case "opencode":
3219
- return join10(homeDir, ".config", "opencode", "skills");
3212
+ return join9(homeDir, ".config", "opencode", "skills");
3220
3213
  default:
3221
- return join10(homeDir, `.${agent}`, "skills");
3214
+ return join9(homeDir, `.${agent}`, "skills");
3222
3215
  }
3223
3216
  }
3224
3217
  function adaptSkillMdForAgent(skillMd, agent) {
@@ -3277,7 +3270,7 @@ function resolveSyncCorpus(options = {}) {
3277
3270
  function packageSourceRoots(source) {
3278
3271
  const roots = [];
3279
3272
  for (const sub of ["skills"]) {
3280
- const candidate = join10(source, sub);
3273
+ const candidate = join9(source, sub);
3281
3274
  if (existsSync9(candidate) && isDirectory(candidate))
3282
3275
  roots.push(candidate);
3283
3276
  }
@@ -3293,10 +3286,10 @@ function containsSkillDirectories(path) {
3293
3286
  return false;
3294
3287
  }
3295
3288
  return entries.some((entry) => {
3296
- const candidate = join10(path, entry);
3289
+ const candidate = join9(path, entry);
3297
3290
  if (!isDirectory(candidate))
3298
3291
  return false;
3299
- return existsSync9(join10(candidate, "SKILL.md")) || existsSync9(join10(candidate, "skill.json")) || existsSync9(join10(candidate, "package.json"));
3292
+ return existsSync9(join9(candidate, "SKILL.md")) || existsSync9(join9(candidate, "skill.json")) || existsSync9(join9(candidate, "package.json"));
3300
3293
  });
3301
3294
  }
3302
3295
  function isDirectory(path) {
@@ -3309,7 +3302,7 @@ function isDirectory(path) {
3309
3302
  function syncSkillsToAgents(options = {}) {
3310
3303
  const requested = normalizeRequested(options.names);
3311
3304
  const agents = options.agents?.length ? options.agents : [...SYNC_AGENTS];
3312
- const homeDir = options.homeDir ?? homedir3();
3305
+ const homeDir = options.homeDir ?? homedir2();
3313
3306
  const { roots, source } = resolveSyncCorpus(options);
3314
3307
  const corpus = listPortableSkillsAcrossRoots(roots);
3315
3308
  const byName = new Map(corpus.map((skill) => [skill.name, skill]));
@@ -3335,7 +3328,7 @@ function syncSkillsToAgents(options = {}) {
3335
3328
  actions.push({
3336
3329
  skill: name,
3337
3330
  agent,
3338
- path: join10(agentGlobalSkillsDir(agent, homeDir), name, "SKILL.md"),
3331
+ path: join9(agentGlobalSkillsDir(agent, homeDir), name, "SKILL.md"),
3339
3332
  action: "skip",
3340
3333
  reason: "not found in this machine's corpus"
3341
3334
  });
@@ -3364,8 +3357,8 @@ function syncSkillsToAgents(options = {}) {
3364
3357
  return { actions };
3365
3358
  }
3366
3359
  function writeManagedAgentSkill(params) {
3367
- const homeDir = params.homeDir ?? homedir3();
3368
- const dir = join10(agentGlobalSkillsDir(params.agent, homeDir), params.skill);
3360
+ const homeDir = params.homeDir ?? homedir2();
3361
+ const dir = join9(agentGlobalSkillsDir(params.agent, homeDir), params.skill);
3369
3362
  const result = writeManagedSkillDir(dir, params.skillMd, {
3370
3363
  skill: params.skill,
3371
3364
  source: params.source,
@@ -3382,8 +3375,8 @@ function writeManagedAgentSkill(params) {
3382
3375
  };
3383
3376
  }
3384
3377
  function writeManagedSkillDir(dir, skillMd, options) {
3385
- const skillMdPath = join10(dir, "SKILL.md");
3386
- const markerPath = join10(dir, SYNC_MARKER_FILE);
3378
+ const skillMdPath = join9(dir, "SKILL.md");
3379
+ const markerPath = join9(dir, SYNC_MARKER_FILE);
3387
3380
  const dirExists = existsSync9(dir);
3388
3381
  const managed = existsSync9(markerPath);
3389
3382
  const hasSkillMd = existsSync9(skillMdPath);
@@ -3421,11 +3414,11 @@ function writeManagedSkillDir(dir, skillMd, options) {
3421
3414
  return { action, path: skillMdPath };
3422
3415
  const parentDir = dirname4(dir);
3423
3416
  mkdirSync4(parentDir, { recursive: true });
3424
- const transactionDir = mkdtempSync2(join10(parentDir, `.hasna-skills-write-${basename3(dir)}-`));
3425
- const candidateDir = join10(transactionDir, "candidate");
3426
- const backupDir = join10(transactionDir, "backup");
3427
- const candidateSkillMdPath = join10(candidateDir, "SKILL.md");
3428
- const candidateMarkerPath = join10(candidateDir, SYNC_MARKER_FILE);
3417
+ const transactionDir = mkdtempSync2(join9(parentDir, `.hasna-skills-write-${basename3(dir)}-`));
3418
+ const candidateDir = join9(transactionDir, "candidate");
3419
+ const backupDir = join9(transactionDir, "backup");
3420
+ const candidateSkillMdPath = join9(candidateDir, "SKILL.md");
3421
+ const candidateMarkerPath = join9(candidateDir, SYNC_MARKER_FILE);
3429
3422
  const marker = {
3430
3423
  managedBy: SYNC_MARKER_MANAGED_BY,
3431
3424
  skill: options.skill,
@@ -3473,16 +3466,16 @@ function writeManagedSkillDir(dir, skillMd, options) {
3473
3466
  }
3474
3467
  return { action, path: skillMdPath };
3475
3468
  }
3476
- function removeManagedAgentSkill(skill, agent, homeDir = homedir3()) {
3477
- const dir = join10(agentGlobalSkillsDir(agent, homeDir), skill);
3478
- if (!existsSync9(join10(dir, SYNC_MARKER_FILE)))
3469
+ function removeManagedAgentSkill(skill, agent, homeDir = homedir2()) {
3470
+ const dir = join9(agentGlobalSkillsDir(agent, homeDir), skill);
3471
+ if (!existsSync9(join9(dir, SYNC_MARKER_FILE)))
3479
3472
  return false;
3480
3473
  rmSync2(dir, { recursive: true, force: true });
3481
3474
  return true;
3482
3475
  }
3483
3476
  function sourceSkillMd(skillPath, name, description, kind, preferBundledDocs = false) {
3484
3477
  if (kind === undefined || kind === "instruction" || preferBundledDocs) {
3485
- const skillMdPath = join10(skillPath, "SKILL.md");
3478
+ const skillMdPath = join9(skillPath, "SKILL.md");
3486
3479
  if (existsSync9(skillMdPath))
3487
3480
  return readFileSync7(skillMdPath, "utf-8");
3488
3481
  }
@@ -3515,7 +3508,7 @@ function normalizeSkillName(name) {
3515
3508
 
3516
3509
  // src/lib/project-state.ts
3517
3510
  import { existsSync as existsSync10, mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
3518
- import { join as join11 } from "path";
3511
+ import { join as join10 } from "path";
3519
3512
  var VALID_PIN_SOURCES = [
3520
3513
  "official",
3521
3514
  "custom",
@@ -3530,10 +3523,10 @@ var SKILLS_PROJECT_DIR = ".skills";
3530
3523
  var PROJECT_CONFIG_FILE = "project.json";
3531
3524
  var DEFAULT_EXPORT_DIR = ".skills/exports";
3532
3525
  function getProjectStateDir(targetDir = process.cwd()) {
3533
- return join11(targetDir, SKILLS_PROJECT_DIR);
3526
+ return join10(targetDir, SKILLS_PROJECT_DIR);
3534
3527
  }
3535
3528
  function getProjectConfigPath(targetDir = process.cwd()) {
3536
- return join11(getProjectStateDir(targetDir), PROJECT_CONFIG_FILE);
3529
+ return join10(getProjectStateDir(targetDir), PROJECT_CONFIG_FILE);
3537
3530
  }
3538
3531
  function loadProjectConfig(targetDir = process.cwd()) {
3539
3532
  const path = getProjectConfigPath(targetDir);
@@ -3658,12 +3651,12 @@ var __dirname2 = dirname5(fileURLToPath(import.meta.url));
3658
3651
  function findSkillsDir() {
3659
3652
  let dir = __dirname2;
3660
3653
  for (let i = 0;i < 5; i++) {
3661
- const candidate = join12(dir, "skills");
3654
+ const candidate = join11(dir, "skills");
3662
3655
  if (existsSync11(candidate) && !dir.includes(".skills"))
3663
3656
  return candidate;
3664
3657
  dir = dirname5(dir);
3665
3658
  }
3666
- return join12(__dirname2, "..", "skills");
3659
+ return join11(__dirname2, "..", "skills");
3667
3660
  }
3668
3661
  var SKILLS_DIR = findSkillsDir();
3669
3662
  function getSkillPath(name) {
@@ -3671,13 +3664,13 @@ function getSkillPath(name) {
3671
3664
  const portable = findPortableSkill(skillName);
3672
3665
  if (portable)
3673
3666
  return portable.path;
3674
- const legacyCustomPath = join12(getDataDir(), "custom", skillName);
3667
+ const legacyCustomPath = join11(getDataDir(), "custom", skillName);
3675
3668
  if (existsSync11(legacyCustomPath))
3676
3669
  return legacyCustomPath;
3677
3670
  const extensionPath = findExtensionSkillPath(skillName);
3678
3671
  if (extensionPath)
3679
3672
  return extensionPath;
3680
- return join12(SKILLS_DIR, skillName);
3673
+ return join11(SKILLS_DIR, skillName);
3681
3674
  }
3682
3675
  function getCanonicalSkillName(name) {
3683
3676
  return getSkill(name)?.name ?? resolveSkillAlias(normalizeSkillSlug(name));
@@ -3740,7 +3733,7 @@ function createLocalSkillManifest(name, generateSkillMd) {
3740
3733
  if (!existsSync11(sourcePath))
3741
3734
  return null;
3742
3735
  let skillMd = "";
3743
- const skillMdPath = join12(sourcePath, "SKILL.md");
3736
+ const skillMdPath = join11(sourcePath, "SKILL.md");
3744
3737
  if (existsSync11(skillMdPath)) {
3745
3738
  skillMd = readFileSync9(skillMdPath, "utf-8");
3746
3739
  } else if (generateSkillMd) {
@@ -3814,16 +3807,16 @@ function getAgentSkillsDir(agent, scope = "global", projectDir) {
3814
3807
  const base = projectDir || process.cwd();
3815
3808
  switch (agent) {
3816
3809
  case "pi":
3817
- return scope === "project" ? join12(base, ".pi", "skills") : join12(homedir4(), ".pi", "agent", "skills");
3810
+ return scope === "project" ? join11(base, ".pi", "skills") : join11(homedir3(), ".pi", "agent", "skills");
3818
3811
  case "opencode":
3819
- return scope === "project" ? join12(base, ".opencode", "skills") : join12(homedir4(), ".config", "opencode", "skills");
3812
+ return scope === "project" ? join11(base, ".opencode", "skills") : join11(homedir3(), ".config", "opencode", "skills");
3820
3813
  default:
3821
- return scope === "project" ? join12(base, `.${agent}`, "skills") : join12(homedir4(), `.${agent}`, "skills");
3814
+ return scope === "project" ? join11(base, `.${agent}`, "skills") : join11(homedir3(), `.${agent}`, "skills");
3822
3815
  }
3823
3816
  }
3824
3817
  function getAgentSkillPath(name, agent, scope = "global", projectDir) {
3825
3818
  const skillName = normalizeSkillName(getCanonicalSkillName(name));
3826
- return join12(getAgentSkillsDir(agent, scope, projectDir), skillName);
3819
+ return join11(getAgentSkillsDir(agent, scope, projectDir), skillName);
3827
3820
  }
3828
3821
  function installSkillForAgent(name, options, generateSkillMd) {
3829
3822
  const canonicalName = getCanonicalSkillName(name);
@@ -3851,14 +3844,14 @@ function removeSkillForAgent(name, options) {
3851
3844
  const canonicalName = getCanonicalSkillName(name);
3852
3845
  const scope = options.scope ?? "global";
3853
3846
  const dir = getAgentSkillPath(canonicalName, options.agent, scope, options.projectDir);
3854
- if (!existsSync11(join12(dir, SYNC_MARKER_FILE)))
3847
+ if (!existsSync11(join11(dir, SYNC_MARKER_FILE)))
3855
3848
  return false;
3856
3849
  rmSync3(dir, { recursive: true, force: true });
3857
3850
  return true;
3858
3851
  }
3859
3852
  function resolveAgentSkillMd(name, generateSkillMd) {
3860
3853
  const sourcePath = getSkillPath(name);
3861
- const skillMdPath = join12(sourcePath, "SKILL.md");
3854
+ const skillMdPath = join11(sourcePath, "SKILL.md");
3862
3855
  if (existsSync11(skillMdPath))
3863
3856
  return readFileSync9(skillMdPath, "utf-8");
3864
3857
  if (generateSkillMd)
@@ -3893,7 +3886,7 @@ function generateMinimalSkillMd(name) {
3893
3886
  "---",
3894
3887
  ""
3895
3888
  ].filter(Boolean);
3896
- const fallbackDoc = readFileIfExists(join12(sourcePath, "README.md")) || readFileIfExists(join12(sourcePath, "CLAUDE.md"));
3889
+ const fallbackDoc = readFileIfExists(join11(sourcePath, "README.md")) || readFileIfExists(join11(sourcePath, "CLAUDE.md"));
3897
3890
  if (fallbackDoc)
3898
3891
  return `${frontmatter.join(`
3899
3892
  `)}${fallbackDoc.trim()}
@@ -3912,7 +3905,7 @@ skills run ${canonicalName}
3912
3905
  `;
3913
3906
  }
3914
3907
  function readBundledSkillVersion(name) {
3915
- const pkgPath = join12(getSkillPath(name), "package.json");
3908
+ const pkgPath = join11(getSkillPath(name), "package.json");
3916
3909
  if (!existsSync11(pkgPath))
3917
3910
  return "unknown";
3918
3911
  try {
@@ -3931,19 +3924,19 @@ function loadProjectConfigCompat(targetDir) {
3931
3924
  // src/lib/run-state.ts
3932
3925
  import { createHash as createHash2, randomBytes } from "crypto";
3933
3926
  import { existsSync as existsSync12, mkdirSync as mkdirSync6, readFileSync as readFileSync10, readdirSync as readdirSync8, statSync as statSync8, writeFileSync as writeFileSync6 } from "fs";
3934
- import { extname, join as join13, relative as relative2 } from "path";
3927
+ import { extname, join as join12, relative as relative2 } from "path";
3935
3928
  function createSkillRun(params, targetDir = process.cwd()) {
3936
3929
  const now = new Date;
3937
3930
  const id = createRunId(now);
3938
3931
  const day = now.toISOString().slice(0, 10);
3939
3932
  const skillName = normalizeSkillName(params.skill);
3940
3933
  const root = getProjectStateDir(targetDir);
3941
- const runDir = join13(root, "runs", day, id);
3942
- const logsDir = join13(runDir, "logs");
3943
- const exportDir = join13(root, "exports", skillName, id);
3934
+ const runDir = join12(root, "runs", day, id);
3935
+ const logsDir = join12(runDir, "logs");
3936
+ const exportDir = join12(root, "exports", skillName, id);
3944
3937
  mkdirSync6(logsDir, { recursive: true });
3945
3938
  mkdirSync6(exportDir, { recursive: true });
3946
- mkdirSync6(join13(root, "tmp"), { recursive: true });
3939
+ mkdirSync6(join12(root, "tmp"), { recursive: true });
3947
3940
  const record = {
3948
3941
  id,
3949
3942
  skill: skillName,
@@ -3994,27 +3987,27 @@ function updateSkillRun(context, patch) {
3994
3987
  return context.record;
3995
3988
  }
3996
3989
  function writeRunLogs(context, stdout = "", stderr = "") {
3997
- writeFileSync6(join13(context.logsDir, "stdout.log"), stdout);
3998
- writeFileSync6(join13(context.logsDir, "stderr.log"), stderr);
3990
+ writeFileSync6(join12(context.logsDir, "stdout.log"), stdout);
3991
+ writeFileSync6(join12(context.logsDir, "stderr.log"), stderr);
3999
3992
  }
4000
3993
  function appendRunEvent(context, event, data = {}) {
4001
3994
  const line = JSON.stringify({ ts: new Date().toISOString(), event, ...data }) + `
4002
3995
  `;
4003
- const path = join13(context.runDir, "events.ndjson");
3996
+ const path = join12(context.runDir, "events.ndjson");
4004
3997
  const previous = existsSync12(path) ? readFileSync10(path, "utf-8") : "";
4005
3998
  writeFileSync6(path, previous + line);
4006
3999
  }
4007
4000
  function listSkillRuns(targetDir = process.cwd(), limit = 50) {
4008
- const runsRoot = join13(getProjectStateDir(targetDir), "runs");
4001
+ const runsRoot = join12(getProjectStateDir(targetDir), "runs");
4009
4002
  if (!existsSync12(runsRoot))
4010
4003
  return [];
4011
4004
  const records = [];
4012
4005
  for (const day of readdirSync8(runsRoot).sort().reverse()) {
4013
- const dayDir = join13(runsRoot, day);
4006
+ const dayDir = join12(runsRoot, day);
4014
4007
  if (!statSync8(dayDir).isDirectory())
4015
4008
  continue;
4016
4009
  for (const runId of readdirSync8(dayDir).sort().reverse()) {
4017
- const record = readRunRecord(join13(dayDir, runId));
4010
+ const record = readRunRecord(join12(dayDir, runId));
4018
4011
  if (record)
4019
4012
  records.push(record);
4020
4013
  if (records.length >= limit)
@@ -4024,25 +4017,25 @@ function listSkillRuns(targetDir = process.cwd(), limit = 50) {
4024
4017
  return records;
4025
4018
  }
4026
4019
  function findSkillRun(runId, targetDir = process.cwd()) {
4027
- const runsRoot = join13(getProjectStateDir(targetDir), "runs");
4020
+ const runsRoot = join12(getProjectStateDir(targetDir), "runs");
4028
4021
  if (!existsSync12(runsRoot))
4029
4022
  return null;
4030
4023
  for (const day of readdirSync8(runsRoot)) {
4031
- const record = readRunRecord(join13(runsRoot, day, runId));
4024
+ const record = readRunRecord(join12(runsRoot, day, runId));
4032
4025
  if (record)
4033
4026
  return record;
4034
4027
  }
4035
4028
  return null;
4036
4029
  }
4037
4030
  function getRunExportDir(runId, skill, targetDir = process.cwd()) {
4038
- return join13(getProjectStateDir(targetDir), "exports", normalizeSkillName(skill), runId);
4031
+ return join12(getProjectStateDir(targetDir), "exports", normalizeSkillName(skill), runId);
4039
4032
  }
4040
4033
  function writeRunRecord(context) {
4041
- writeFileSync6(join13(context.runDir, "run.json"), JSON.stringify(context.record, null, 2) + `
4034
+ writeFileSync6(join12(context.runDir, "run.json"), JSON.stringify(context.record, null, 2) + `
4042
4035
  `);
4043
4036
  }
4044
4037
  function writeArtifactsManifest(context, artifacts) {
4045
- writeFileSync6(join13(context.runDir, "artifacts.json"), JSON.stringify({ runId: context.record.id, artifacts }, null, 2) + `
4038
+ writeFileSync6(join12(context.runDir, "artifacts.json"), JSON.stringify({ runId: context.record.id, artifacts }, null, 2) + `
4046
4039
  `);
4047
4040
  }
4048
4041
  function collectRunArtifacts(context) {
@@ -4062,7 +4055,7 @@ function collectRunArtifacts(context) {
4062
4055
  return artifacts.sort((a, b) => a.path.localeCompare(b.path));
4063
4056
  }
4064
4057
  function readRunRecord(runDir) {
4065
- const path = join13(runDir, "run.json");
4058
+ const path = join12(runDir, "run.json");
4066
4059
  if (!existsSync12(path))
4067
4060
  return null;
4068
4061
  try {
@@ -4074,7 +4067,7 @@ function readRunRecord(runDir) {
4074
4067
  function walkFiles(dir) {
4075
4068
  const files = [];
4076
4069
  for (const entry of readdirSync8(dir)) {
4077
- const full = join13(dir, entry);
4070
+ const full = join12(dir, entry);
4078
4071
  if (statSync8(full).isDirectory())
4079
4072
  files.push(...walkFiles(full));
4080
4073
  else
@@ -4121,11 +4114,11 @@ function mimeForPath(path) {
4121
4114
  }
4122
4115
  // src/lib/skillinfo.ts
4123
4116
  import { existsSync as existsSync13, readFileSync as readFileSync11 } from "fs";
4124
- import { join as join14 } from "path";
4117
+ import { join as join13 } from "path";
4125
4118
  function isInstructionSkillDir(skillPath, meta) {
4126
4119
  if (meta?.kind === "instruction")
4127
4120
  return true;
4128
- const skillMdPath = join14(skillPath, "SKILL.md");
4121
+ const skillMdPath = join13(skillPath, "SKILL.md");
4129
4122
  if (!existsSync13(skillMdPath))
4130
4123
  return false;
4131
4124
  try {
@@ -4155,9 +4148,9 @@ function getSkillDocs(name) {
4155
4148
  if (!existsSync13(skillPath))
4156
4149
  return null;
4157
4150
  return {
4158
- skillMd: readIfExists(join14(skillPath, "SKILL.md")),
4159
- readme: readIfExists(join14(skillPath, "README.md")),
4160
- claudeMd: readIfExists(join14(skillPath, "CLAUDE.md"))
4151
+ skillMd: readIfExists(join13(skillPath, "SKILL.md")),
4152
+ readme: readIfExists(join13(skillPath, "README.md")),
4153
+ claudeMd: readIfExists(join13(skillPath, "CLAUDE.md"))
4161
4154
  };
4162
4155
  }
4163
4156
  function getSkillBestDoc(name) {
@@ -4172,7 +4165,7 @@ function getSkillRequirements(name) {
4172
4165
  return null;
4173
4166
  const texts = [];
4174
4167
  for (const file of ["SKILL.md", "README.md", "CLAUDE.md", ".env.example", ".env.local.example"]) {
4175
- const content = readIfExists(join14(skillPath, file));
4168
+ const content = readIfExists(join13(skillPath, file));
4176
4169
  if (content)
4177
4170
  texts.push(content);
4178
4171
  }
@@ -4211,7 +4204,7 @@ function getSkillRequirements(name) {
4211
4204
  const skillName = normalizeSkillName(name);
4212
4205
  let cliCommand = `skills run ${skillName}`;
4213
4206
  let dependencies = {};
4214
- const pkgPath = join14(skillPath, "package.json");
4207
+ const pkgPath = join13(skillPath, "package.json");
4215
4208
  if (existsSync13(pkgPath)) {
4216
4209
  try {
4217
4210
  const pkg = JSON.parse(readFileSync11(pkgPath, "utf-8"));
@@ -4241,7 +4234,7 @@ async function runSkill(name, args, options = {}) {
4241
4234
  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'.`
4242
4235
  };
4243
4236
  }
4244
- const pkgPath = join14(skillPath, "package.json");
4237
+ const pkgPath = join13(skillPath, "package.json");
4245
4238
  if (!existsSync13(pkgPath)) {
4246
4239
  return { exitCode: 1, error: `No package.json in skill '${name}'` };
4247
4240
  }
@@ -4261,11 +4254,11 @@ async function runSkill(name, args, options = {}) {
4261
4254
  } catch {
4262
4255
  return { exitCode: 1, error: `Failed to parse package.json for skill '${name}'` };
4263
4256
  }
4264
- const entryPath = join14(skillPath, entryPoint);
4257
+ const entryPath = join13(skillPath, entryPoint);
4265
4258
  if (!existsSync13(entryPath)) {
4266
4259
  return { exitCode: 1, error: `Entry point '${entryPoint}' not found in skill '${name}'` };
4267
4260
  }
4268
- const nodeModules = join14(skillPath, "node_modules");
4261
+ const nodeModules = join13(skillPath, "node_modules");
4269
4262
  if (!existsSync13(nodeModules)) {
4270
4263
  const install = Bun.spawn(["bun", "install", "--no-save"], {
4271
4264
  cwd: skillPath,
@@ -4347,10 +4340,10 @@ function generateSkillMd(name) {
4347
4340
  "---"
4348
4341
  ].join(`
4349
4342
  `);
4350
- const readme = readIfExists(join14(skillPath, "README.md"));
4351
- const claudeMd = readIfExists(join14(skillPath, "CLAUDE.md"));
4343
+ const readme = readIfExists(join13(skillPath, "README.md"));
4344
+ const claudeMd = readIfExists(join13(skillPath, "CLAUDE.md"));
4352
4345
  let cliCommand = null;
4353
- const pkgPath = join14(skillPath, "package.json");
4346
+ const pkgPath = join13(skillPath, "package.json");
4354
4347
  if (existsSync13(pkgPath)) {
4355
4348
  try {
4356
4349
  const pkg = JSON.parse(readFileSync11(pkgPath, "utf-8"));
@@ -8433,13 +8426,13 @@ function requireApiUrl(action = "This command", config, env) {
8433
8426
 
8434
8427
  // src/lib/auth-store.ts
8435
8428
  import { existsSync as existsSync14, mkdirSync as mkdirSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync7, unlinkSync } from "fs";
8436
- import { dirname as dirname6, join as join15 } from "path";
8437
- import { homedir as homedir5 } from "os";
8429
+ import { dirname as dirname6, join as join14 } from "path";
8430
+ import { homedir as homedir4 } from "os";
8438
8431
  function getAuthFilePath() {
8439
- return join15(getDataDir(), "auth.json");
8432
+ return join14(getDataDir(), "auth.json");
8440
8433
  }
8441
8434
  function legacyAuthFilePath() {
8442
- return join15(process.env["HOME"] || process.env["USERPROFILE"] || homedir5(), ".skills", "auth.json");
8435
+ return join14(process.env["HOME"] || process.env["USERPROFILE"] || homedir4(), ".skills", "auth.json");
8443
8436
  }
8444
8437
  var cachedConfig;
8445
8438
  function getAuthConfig() {
@@ -9404,12 +9397,30 @@ class RemoteSkillsClient {
9404
9397
  async downloadSkillBundle(slug) {
9405
9398
  return this.request(`/api/v1/skills/${encodeURIComponent(slug)}/bundle`, { method: "GET" });
9406
9399
  }
9407
- async getBundle(slug) {
9408
- const response = await this.request(`/api/v1/skills/${encodeURIComponent(slug)}/bundle`, { method: "GET" });
9400
+ async getBundle(slug, version) {
9401
+ const path = version ? `/api/v1/skills/${encodeURIComponent(slug)}/versions/${encodeURIComponent(version)}/bundle` : `/api/v1/skills/${encodeURIComponent(slug)}/bundle`;
9402
+ const response = await this.request(path, { method: "GET" });
9409
9403
  if (response.status === 404)
9410
9404
  return null;
9411
9405
  return response;
9412
9406
  }
9407
+ async listSkillVersions(slug) {
9408
+ const response = await this.requestNewRoute(`/api/v1/skills/${encodeURIComponent(slug)}/versions`, undefined, { domainNotFoundCodes: ["SKILL_NOT_FOUND"] });
9409
+ if (response.status === 404)
9410
+ return [];
9411
+ if (!response.ok)
9412
+ throw new Error(`versions request failed: ${response.status}`);
9413
+ const body = await response.json();
9414
+ return Array.isArray(body.versions) ? body.versions : [];
9415
+ }
9416
+ async getSkillVersion(slug, version) {
9417
+ const response = await this.requestNewRoute(`/api/v1/skills/${encodeURIComponent(slug)}/versions/${encodeURIComponent(version)}`, undefined, { domainNotFoundCodes: ["SKILL_NOT_FOUND", "SKILL_VERSION_NOT_FOUND"] });
9418
+ if (response.status === 404)
9419
+ return null;
9420
+ if (!response.ok)
9421
+ throw new Error(`version request failed: ${response.status}`);
9422
+ return await response.json();
9423
+ }
9413
9424
  async listPins() {
9414
9425
  const response = await this.requestNewRoute("/api/v1/pins");
9415
9426
  return normalizePinList(await response.json());
@@ -9548,9 +9559,9 @@ function createRemoteSkillsClient() {
9548
9559
  }
9549
9560
  // src/lib/scheduler.ts
9550
9561
  import { existsSync as existsSync15, readFileSync as readFileSync13, writeFileSync as writeFileSync8, mkdirSync as mkdirSync8 } from "fs";
9551
- import { join as join16 } from "path";
9562
+ import { join as join15 } from "path";
9552
9563
  function getSchedulesPath(targetDir = process.cwd()) {
9553
- return join16(targetDir, ".skills", "schedules.json");
9564
+ return join15(targetDir, ".skills", "schedules.json");
9554
9565
  }
9555
9566
  function loadSchedules(targetDir = process.cwd()) {
9556
9567
  const path = getSchedulesPath(targetDir);
@@ -9563,7 +9574,7 @@ function loadSchedules(targetDir = process.cwd()) {
9563
9574
  }
9564
9575
  function saveSchedules(data, targetDir = process.cwd()) {
9565
9576
  const path = getSchedulesPath(targetDir);
9566
- const dir = join16(targetDir, ".skills");
9577
+ const dir = join15(targetDir, ".skills");
9567
9578
  if (!existsSync15(dir))
9568
9579
  mkdirSync8(dir, { recursive: true });
9569
9580
  writeFileSync8(path, JSON.stringify(data, null, 2));
@@ -9751,7 +9762,7 @@ function recordScheduleRun(id, status, targetDir) {
9751
9762
  saveSchedules(data, targetDir);
9752
9763
  }
9753
9764
  // src/lib/pull.ts
9754
- import { existsSync as existsSync16, mkdirSync as mkdirSync9, mkdtempSync as mkdtempSync3, readFileSync as readFileSync14, renameSync as renameSync3, rmSync as rmSync4, writeFileSync as writeFileSync9 } from "fs";
9765
+ import { existsSync as existsSync16, mkdirSync as mkdirSync9, mkdtempSync as mkdtempSync3, readFileSync as readFileSync15, renameSync as renameSync3, rmSync as rmSync4, writeFileSync as writeFileSync9 } from "fs";
9755
9766
  import { dirname as dirname7, join as join17 } from "path";
9756
9767
 
9757
9768
  // src/lib/revision.ts
@@ -9779,6 +9790,8 @@ function revisionIdOfRecord(record) {
9779
9790
 
9780
9791
  // src/lib/skill-bundle.ts
9781
9792
  import { createHash as createHash4 } from "crypto";
9793
+ import { readFileSync as readFileSync14, readdirSync as readdirSync9, statSync as statSync9 } from "fs";
9794
+ import { join as join16, relative as relative3 } from "path";
9782
9795
  var BLOCK = 512;
9783
9796
  var ANY_SEGMENT_EXCLUDES = new Set([
9784
9797
  ".git",
@@ -9805,6 +9818,7 @@ var CREDENTIAL_FILENAMES = new Set([
9805
9818
  "id_ecdsa",
9806
9819
  "id_ed25519"
9807
9820
  ]);
9821
+ var CREDENTIAL_EXTENSIONS = [".pem", ".key", ".p12", ".pfx", ".keystore", ".jks"];
9808
9822
  var ENV_TEMPLATE_NAMES = new Set([".env.example", ".env.sample", ".env.template", ".env.dist"]);
9809
9823
  var NON_DOTENV_EXTENSIONS = new Set([
9810
9824
  "ts",
@@ -9901,6 +9915,27 @@ var NON_DOTENV_EXTENSIONS = new Set([
9901
9915
  "tar",
9902
9916
  "wasm"
9903
9917
  ]);
9918
+ function isDotenvFile(lower) {
9919
+ if (lower === ".env" || lower.startsWith(".env."))
9920
+ return true;
9921
+ if (lower === "env")
9922
+ return true;
9923
+ if (lower.startsWith("env.")) {
9924
+ const extension = lower.slice(lower.lastIndexOf(".") + 1);
9925
+ return !NON_DOTENV_EXTENSIONS.has(extension);
9926
+ }
9927
+ return false;
9928
+ }
9929
+ function isCredentialFile(name) {
9930
+ const lower = name.toLowerCase();
9931
+ if (ENV_TEMPLATE_NAMES.has(lower))
9932
+ return false;
9933
+ if (isDotenvFile(lower))
9934
+ return true;
9935
+ if (CREDENTIAL_FILENAMES.has(lower))
9936
+ return true;
9937
+ return CREDENTIAL_EXTENSIONS.some((extension) => lower.endsWith(extension));
9938
+ }
9904
9939
  function ownBytes(view) {
9905
9940
  const source = view instanceof ArrayBuffer ? new Uint8Array(view) : view;
9906
9941
  const out = new Uint8Array(new ArrayBuffer(source.byteLength));
@@ -9910,9 +9945,117 @@ function ownBytes(view) {
9910
9945
  function sha256Hex(bytes) {
9911
9946
  return createHash4("sha256").update(bytes).digest("hex");
9912
9947
  }
9948
+ function collectSkillBundleEntries(dir) {
9949
+ const entries = [];
9950
+ walk(dir, dir, entries);
9951
+ return entries.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
9952
+ }
9953
+ function walk(root, current, out) {
9954
+ for (const entry of readdirSync9(current, { withFileTypes: true })) {
9955
+ const absolute = join16(current, entry.name);
9956
+ const rel = relative3(root, absolute).split("\\").join("/");
9957
+ const isRootLevel = !rel.includes("/");
9958
+ if (ANY_SEGMENT_EXCLUDES.has(entry.name.toLowerCase()))
9959
+ continue;
9960
+ if (isRootLevel && ROOT_EXCLUDES.has(entry.name.toLowerCase()))
9961
+ continue;
9962
+ if (isRootLevel && TOOL_SIDECAR_FILENAMES.has(entry.name.toLowerCase()))
9963
+ continue;
9964
+ if (entry.name.startsWith("._"))
9965
+ continue;
9966
+ if (entry.isSymbolicLink())
9967
+ continue;
9968
+ if (entry.isDirectory()) {
9969
+ walk(root, absolute, out);
9970
+ continue;
9971
+ }
9972
+ if (!entry.isFile())
9973
+ continue;
9974
+ if (isCredentialFile(entry.name))
9975
+ continue;
9976
+ const stats = statSync9(absolute);
9977
+ out.push({
9978
+ path: rel,
9979
+ bytes: ownBytes(readFileSync14(absolute)),
9980
+ mode: stats.mode & 64 ? 493 : 420
9981
+ });
9982
+ }
9983
+ }
9984
+ function packSkillBundle(dir, options = {}) {
9985
+ const entries = collectSkillBundleEntries(dir);
9986
+ if (entries.length === 0) {
9987
+ throw new Error(`Nothing to pack: ${dir} contains no files after exclusions (.git, node_modules, dist, .env)`);
9988
+ }
9989
+ const unpackedByteSize = entries.reduce((sum, entry) => sum + entry.bytes.byteLength, 0);
9990
+ const max = options.maxUnpackedBytes ?? 0;
9991
+ if (max > 0 && unpackedByteSize > max) {
9992
+ throw new Error(`Skill sources are ${unpackedByteSize} bytes, over the ${max} byte limit. Remove build output or large fixtures.`);
9993
+ }
9994
+ const tar = writeTar(entries);
9995
+ const bytes = canonicalGzip(tar);
9996
+ return {
9997
+ bytes,
9998
+ sha256: sha256Hex(bytes),
9999
+ fileCount: entries.length,
10000
+ unpackedByteSize,
10001
+ paths: entries.map((entry) => entry.path)
10002
+ };
10003
+ }
10004
+ function canonicalGzip(tar) {
10005
+ const bytes = ownBytes(Bun.gzipSync(tar, { level: 6 }));
10006
+ if (bytes.byteLength >= 10) {
10007
+ bytes[4] = 0;
10008
+ bytes[5] = 0;
10009
+ bytes[6] = 0;
10010
+ bytes[7] = 0;
10011
+ bytes[9] = 255;
10012
+ }
10013
+ return bytes;
10014
+ }
9913
10015
  function unpackSkillBundle(bundle) {
9914
10016
  return readTar(ownBytes(Bun.gunzipSync(ownBytes(bundle))));
9915
10017
  }
10018
+ function writeTar(entries) {
10019
+ const blocks = [];
10020
+ for (const entry of entries) {
10021
+ blocks.push(ustarHeader(entry));
10022
+ blocks.push(entry.bytes);
10023
+ const remainder = entry.bytes.byteLength % BLOCK;
10024
+ if (remainder !== 0)
10025
+ blocks.push(new Uint8Array(new ArrayBuffer(BLOCK - remainder)));
10026
+ }
10027
+ blocks.push(new Uint8Array(new ArrayBuffer(BLOCK * 2)));
10028
+ return concat(blocks);
10029
+ }
10030
+ function ustarHeader(entry) {
10031
+ const header = new Uint8Array(new ArrayBuffer(BLOCK));
10032
+ const encoder = new TextEncoder;
10033
+ const put = (offset, length, value) => {
10034
+ const encoded = encoder.encode(value);
10035
+ if (encoded.byteLength > length) {
10036
+ throw new Error(`Cannot pack '${entry.path}': field does not fit in a ustar header (${encoded.byteLength} > ${length})`);
10037
+ }
10038
+ header.set(encoded, offset);
10039
+ };
10040
+ if (encoder.encode(entry.path).byteLength > 100) {
10041
+ throw new Error(`Cannot pack '${entry.path}': path is longer than the 100 bytes a ustar header holds`);
10042
+ }
10043
+ put(0, 100, entry.path);
10044
+ put(100, 8, `${entry.mode.toString(8).padStart(7, "0")}\x00`);
10045
+ put(108, 8, "0000000\x00");
10046
+ put(116, 8, "0000000\x00");
10047
+ put(124, 12, `${entry.bytes.byteLength.toString(8).padStart(11, "0")}\x00`);
10048
+ put(136, 12, `${0 .toString(8).padStart(11, "0")}\x00`);
10049
+ put(148, 8, " ");
10050
+ put(156, 1, "0");
10051
+ put(257, 6, "ustar\x00");
10052
+ put(263, 2, "00");
10053
+ let checksum = 0;
10054
+ for (const byte of header)
10055
+ checksum += byte;
10056
+ put(148, 8, `${checksum.toString(8).padStart(6, "0")}\x00 `);
10057
+ return header;
10058
+ }
9916
10059
  function readTar(tar) {
9917
10060
  const decoder = new TextDecoder;
9918
10061
  const entries = [];
@@ -9961,6 +10104,23 @@ function trimNul(value) {
9961
10104
  const end = value.indexOf("\x00");
9962
10105
  return end === -1 ? value : value.slice(0, end);
9963
10106
  }
10107
+ function concat(chunks) {
10108
+ const total = chunks.reduce((sum, chunk) => sum + chunk.byteLength, 0);
10109
+ const merged = new Uint8Array(new ArrayBuffer(total));
10110
+ let offset = 0;
10111
+ for (const chunk of chunks) {
10112
+ merged.set(chunk, offset);
10113
+ offset += chunk.byteLength;
10114
+ }
10115
+ return merged;
10116
+ }
10117
+
10118
+ // src/lib/skill-version.ts
10119
+ var SKILL_VERSION_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/;
10120
+ function isValidSkillVersion(value) {
10121
+ return typeof value === "string" && SKILL_VERSION_PATTERN.test(value) && !/^\.+$/.test(value) && !value.includes("..");
10122
+ }
10123
+ var SKILL_VERSION_RULE = "1-128 characters: letters, digits, '.', '_', '+', '-'; must start with a letter or digit; no '..'";
9964
10124
 
9965
10125
  // src/lib/skill-bundles.ts
9966
10126
  import { createHmac, timingSafeEqual } from "crypto";
@@ -10025,19 +10185,30 @@ async function resolveTargetSlugs(client, options) {
10025
10185
  return explicit;
10026
10186
  }
10027
10187
  async function pullOne(client, rawName, corpusOptions, verify) {
10188
+ const { name: bareName, version: requestedVersion } = splitNameVersion(rawName);
10028
10189
  let slug;
10029
10190
  try {
10030
- slug = normalizePortableSkillName(rawName);
10191
+ slug = normalizePortableSkillName(bareName);
10031
10192
  } catch (error) {
10032
10193
  return { name: rawName, success: false, error: error.message };
10033
10194
  }
10195
+ if (requestedVersion !== undefined && !isValidSkillVersion(requestedVersion)) {
10196
+ return { name: rawName, success: false, error: `'${requestedVersion}' is not a valid skill version (${SKILL_VERSION_RULE}).` };
10197
+ }
10034
10198
  const meta = await safeMeta(client, slug);
10035
10199
  let bundleResponse;
10036
10200
  try {
10037
- bundleResponse = await client.getBundle(slug);
10201
+ bundleResponse = await client.getBundle(slug, requestedVersion);
10038
10202
  } catch (error) {
10039
10203
  return { name: slug, success: false, error: `Failed to fetch '${slug}': ${error.message}` };
10040
10204
  }
10205
+ if (requestedVersion && (!bundleResponse || bundleResponse.status === 404)) {
10206
+ return {
10207
+ name: rawName,
10208
+ success: false,
10209
+ error: `Version '${requestedVersion}' of '${slug}' is not published on the configured instance (run 'skills versions ${slug}' to list what exists).`
10210
+ };
10211
+ }
10041
10212
  if (bundleResponse && !bundleResponse.ok) {
10042
10213
  if (bundleResponse.status === 410) {
10043
10214
  return reconcileTombstone(slug, corpusOptions);
@@ -10056,7 +10227,15 @@ async function pullOne(client, rawName, corpusOptions, verify) {
10056
10227
  }
10057
10228
  if (bundleResponse) {
10058
10229
  try {
10059
- return await installVerifiedBundle(slug, bundleResponse, meta, corpusOptions, verify);
10230
+ let expectedSha256;
10231
+ if (requestedVersion && client.getSkillVersion) {
10232
+ const recorded = await client.getSkillVersion(slug, requestedVersion);
10233
+ if (!recorded) {
10234
+ return { name: rawName, success: false, error: `Version '${requestedVersion}' of '${slug}' is not published on the configured instance (run 'skills versions ${slug}' to list what exists).` };
10235
+ }
10236
+ expectedSha256 = typeof recorded.bundleSha256 === "string" ? recorded.bundleSha256 : undefined;
10237
+ }
10238
+ return await installVerifiedBundle(slug, bundleResponse, meta, corpusOptions, verify, requestedVersion ? { version: requestedVersion, expectedSha256 } : undefined);
10060
10239
  } catch (error) {
10061
10240
  if (error instanceof PullSkillError) {
10062
10241
  return { name: slug, success: false, error: error.message };
@@ -10140,23 +10319,30 @@ function reconcileTombstone(slug, corpusOptions) {
10140
10319
  }
10141
10320
  function readPullMarker(dir) {
10142
10321
  try {
10143
- return JSON.parse(readFileSync14(join17(dir, PULL_MARKER_FILE), "utf-8"));
10322
+ return JSON.parse(readFileSync15(join17(dir, PULL_MARKER_FILE), "utf-8"));
10144
10323
  } catch {
10145
10324
  return null;
10146
10325
  }
10147
10326
  }
10148
- function installVerifiedBundle(slug, response, meta, corpusOptions, verify) {
10327
+ function installVerifiedBundle(slug, response, meta, corpusOptions, verify, exact) {
10149
10328
  return response.arrayBuffer().then((buffer) => {
10150
10329
  const verified = verifyBundleResponseBytes(buffer, response, verify);
10330
+ if (exact?.expectedSha256 && exact.expectedSha256 !== verified.contentHash) {
10331
+ throw new PullSkillError(`Digest proof failed for '${slug}@${exact.version}': the registry records bundle ${exact.expectedSha256.slice(0, 12)}\u2026 but the received bytes hash to ${verified.contentHash.slice(0, 12)}\u2026. Nothing was installed.`);
10332
+ }
10333
+ const served = str(response.headers.get("X-Skill-Version"));
10334
+ if (exact && served && served !== exact.version) {
10335
+ throw new PullSkillError(`Version proof failed for '${slug}': asked for '${exact.version}', the instance served '${served}'. Nothing was installed.`);
10336
+ }
10151
10337
  let entries;
10152
10338
  try {
10153
10339
  entries = unpackSkillBundle(verified.bytes);
10154
10340
  } catch (error) {
10155
10341
  throw new PullSkillError(`Bundle for '${slug}' could not be unpacked: ${error.message}`);
10156
10342
  }
10157
- const version = str(meta?.version) ?? versionFromEntries(entries) ?? "unknown";
10343
+ const version = exact?.version ?? served ?? str(meta?.version) ?? versionFromEntries(entries) ?? "unknown";
10158
10344
  const sourceCommit = sourceCommitFromEntries(entries);
10159
- const declaredRevision = verified.revisionId ?? meta?.revisionId;
10345
+ const declaredRevision = exact ? undefined : verified.revisionId ?? meta?.revisionId;
10160
10346
  if (declaredRevision && meta?.revisionId && verified.revisionId && declaredRevision !== meta.revisionId) {
10161
10347
  throw new PullSkillError(`Revision proof failed for '${slug}': the bundle header declares '${verified.revisionId.slice(0, 12)}\u2026' but the metadata declares '${meta.revisionId.slice(0, 12)}\u2026'. The instance is inconsistent. Nothing was installed.`);
10162
10348
  }
@@ -10353,6 +10539,15 @@ function dedupe(values) {
10353
10539
  function str(value) {
10354
10540
  return typeof value === "string" && value.trim() ? value.trim() : undefined;
10355
10541
  }
10542
+ function splitNameVersion(raw) {
10543
+ const trimmed = raw.trim();
10544
+ const at = trimmed.indexOf("@");
10545
+ if (at <= 0)
10546
+ return { name: trimmed };
10547
+ const name = trimmed.slice(0, at);
10548
+ const version = trimmed.slice(at + 1);
10549
+ return version ? { name, version } : { name };
10550
+ }
10356
10551
  // src/lib/cli-mcp-parity.ts
10357
10552
  var SKILLS_CLI_MCP_PARITY = [
10358
10553
  {
@@ -10458,11 +10653,11 @@ function findSkillsParityForMcpTool(tool) {
10458
10653
  }
10459
10654
  // src/lib/registry-sync.ts
10460
10655
  import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync10 } from "fs";
10461
- import { dirname as dirname8, relative as relative3 } from "path";
10656
+ import { dirname as dirname8, relative as relative4 } from "path";
10462
10657
  // package.json
10463
10658
  var package_default = {
10464
10659
  name: "@hasna/skills",
10465
- version: "0.1.72",
10660
+ version: "0.2.0",
10466
10661
  description: "Skills library for AI coding agents",
10467
10662
  type: "module",
10468
10663
  bin: {
@@ -10542,7 +10737,7 @@ var package_default = {
10542
10737
  author: "Hasna",
10543
10738
  license: "Apache-2.0",
10544
10739
  devDependencies: {
10545
- "@types/bun": "latest",
10740
+ "@types/bun": "1.3.14",
10546
10741
  "@types/node": "25.2.3",
10547
10742
  "@types/react": "^18.2.0",
10548
10743
  "bun-types": "1.3.14",
@@ -10554,7 +10749,6 @@ var package_default = {
10554
10749
  "@aws-sdk/client-ecs": "^3.1079.0",
10555
10750
  "@aws-sdk/client-s3": "^3.1079.0",
10556
10751
  "@hasna/events": "0.1.16",
10557
- "@hasna/paths": "0.1.0",
10558
10752
  "@modelcontextprotocol/sdk": "^1.26.0",
10559
10753
  chalk: "^5.3.0",
10560
10754
  commander: "^12.1.0",
@@ -10594,7 +10788,7 @@ function createRegistrySyncArtifact(options = {}) {
10594
10788
  const registry = [...loadRegistryProfile(profile)].sort((a, b) => a.name.localeCompare(b.name));
10595
10789
  const skills = registry.map((skill) => {
10596
10790
  const skillPath = getSkillPath(skill.name);
10597
- const directory = relative3(process.cwd(), skillPath) || skillPath;
10791
+ const directory = relative4(process.cwd(), skillPath) || skillPath;
10598
10792
  const validation = includeValidation ? validateSkillDirectory(skill.name, skillPath, skill) : undefined;
10599
10793
  const docs = includeDocs ? buildDocs(skill.name) : undefined;
10600
10794
  return {
@@ -11479,7 +11673,7 @@ function clone2(value) {
11479
11673
  return JSON.parse(JSON.stringify(value));
11480
11674
  }
11481
11675
  // src/lib/feedback.ts
11482
- import { existsSync as existsSync17, mkdirSync as mkdirSync11 } from "fs";
11676
+ import { appendFileSync, existsSync as existsSync17, mkdirSync as mkdirSync11 } from "fs";
11483
11677
  import { dirname as dirname9, join as join18 } from "path";
11484
11678
  import { Database } from "bun:sqlite";
11485
11679
  function getFeedbackDbPath() {
@@ -11514,6 +11708,15 @@ function saveFeedback(input) {
11514
11708
  if (!message)
11515
11709
  throw new Error("Feedback message is required");
11516
11710
  const category = input.category ?? "general";
11711
+ if (isApiMode()) {
11712
+ const path = join18(getDataDir(), "feedback.jsonl");
11713
+ const dir = dirname9(path);
11714
+ if (!existsSync17(dir))
11715
+ mkdirSync11(dir, { recursive: true });
11716
+ appendFileSync(path, JSON.stringify({ message, category, email: input.email ?? null, agent: input.agent ?? null, version: input.version ?? null, createdAt: new Date().toISOString() }) + `
11717
+ `);
11718
+ return { saved: true, category, path };
11719
+ }
11517
11720
  const db = getFeedbackDb();
11518
11721
  try {
11519
11722
  db.run("INSERT INTO feedback (message, email, category, agent, version) VALUES (?, ?, ?, ?, ?)", [message, input.email || null, category, input.agent || null, input.version || null]);
@@ -11522,17 +11725,26 @@ function saveFeedback(input) {
11522
11725
  }
11523
11726
  return { saved: true, category, path: getFeedbackDbPath() };
11524
11727
  }
11728
+ function isApiMode(env = process.env) {
11729
+ if (env.HASNA_SKILLS_API_URL?.trim())
11730
+ return true;
11731
+ try {
11732
+ return Boolean(resolveApiUrl(undefined, env));
11733
+ } catch {
11734
+ return Boolean(env.SKILLS_API_URL?.trim());
11735
+ }
11736
+ }
11525
11737
  // src/lib/native-storage.ts
11526
11738
  import { createHash as createHash5, createHmac as createHmac2 } from "crypto";
11527
11739
  import {
11528
11740
  existsSync as existsSync18,
11529
11741
  mkdirSync as mkdirSync12,
11530
- readFileSync as readFileSync15,
11531
- readdirSync as readdirSync9,
11532
- statSync as statSync9,
11742
+ readFileSync as readFileSync16,
11743
+ readdirSync as readdirSync10,
11744
+ statSync as statSync10,
11533
11745
  writeFileSync as writeFileSync11
11534
11746
  } from "fs";
11535
- import { dirname as dirname10, join as join19, normalize as normalize3, relative as relative4, sep as sep2 } from "path";
11747
+ import { dirname as dirname10, join as join19, normalize as normalize3, relative as relative5, sep as sep2 } from "path";
11536
11748
  var SKILLS_STORAGE_TABLES = [
11537
11749
  "skills_sync_records",
11538
11750
  "skills_sync_cursors"
@@ -11694,8 +11906,8 @@ function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
11694
11906
  const files = [];
11695
11907
  if (existsSync18(projectStateDir)) {
11696
11908
  for (const filePath of walkFiles2(projectStateDir)) {
11697
- const bytes = readFileSync15(filePath);
11698
- const relativePath = toPosix(relative4(targetDir, filePath));
11909
+ const bytes = readFileSync16(filePath);
11910
+ const relativePath = toPosix(relative5(targetDir, filePath));
11699
11911
  files.push({
11700
11912
  path: relativePath,
11701
11913
  sizeBytes: bytes.byteLength,
@@ -12025,9 +12237,9 @@ function parsePositiveInteger(value) {
12025
12237
  }
12026
12238
  function walkFiles2(dir) {
12027
12239
  const files = [];
12028
- for (const entry of readdirSync9(dir)) {
12240
+ for (const entry of readdirSync10(dir)) {
12029
12241
  const full = join19(dir, entry);
12030
- const stats = statSync9(full);
12242
+ const stats = statSync10(full);
12031
12243
  if (stats.isDirectory())
12032
12244
  files.push(...walkFiles2(full));
12033
12245
  else
@@ -12108,15 +12320,15 @@ import { createHash as createHash6 } from "crypto";
12108
12320
  import {
12109
12321
  copyFileSync as copyFileSync2,
12110
12322
  mkdirSync as mkdirSync13,
12111
- readFileSync as readFileSync16,
12112
- statSync as statSync11,
12323
+ readFileSync as readFileSync17,
12324
+ statSync as statSync12,
12113
12325
  writeFileSync as writeFileSync12
12114
12326
  } from "fs";
12115
- import { dirname as dirname11, isAbsolute as isAbsolute3, relative as relative5, resolve as resolve2, sep as sep4 } from "path";
12327
+ import { dirname as dirname11, isAbsolute as isAbsolute3, relative as relative6, resolve as resolve2, sep as sep4 } from "path";
12116
12328
 
12117
12329
  // src/lib/portable-snapshot-filter.ts
12118
- import { readdirSync as readdirSync10, statSync as statSync10 } from "fs";
12119
- import { homedir as homedir6 } from "os";
12330
+ import { readdirSync as readdirSync11, statSync as statSync11 } from "fs";
12331
+ import { homedir as homedir5 } from "os";
12120
12332
  import { join as join20, sep as sep3 } from "path";
12121
12333
  var SYNC_HOMES = [
12122
12334
  { name: "skills", subClass: "skills", agent: null },
@@ -12208,7 +12420,7 @@ function isPortableWithinSkill(relativeParts) {
12208
12420
  return PORTABLE_SUBDIRS.has(second);
12209
12421
  }
12210
12422
  function homePathFor(definition, homesRoot) {
12211
- const home = homesRoot ?? homedir6();
12423
+ const home = homesRoot ?? homedir5();
12212
12424
  if (definition.subClass === "skills" || definition.subClass === "custom") {
12213
12425
  return join20(skillsDataRootForHome(home), definition.name);
12214
12426
  }
@@ -12224,7 +12436,7 @@ function destinationFor(definition, stationId, relativePath) {
12224
12436
  function walkEntries(absoluteRoot) {
12225
12437
  let entries;
12226
12438
  try {
12227
- entries = readdirSync10(absoluteRoot, { withFileTypes: true });
12439
+ entries = readdirSync11(absoluteRoot, { withFileTypes: true });
12228
12440
  } catch {
12229
12441
  return [];
12230
12442
  }
@@ -12253,7 +12465,7 @@ function walkEntries(absoluteRoot) {
12253
12465
  }
12254
12466
  function isRegularFile(filePath) {
12255
12467
  try {
12256
- return statSync10(filePath).isFile();
12468
+ return statSync11(filePath).isFile();
12257
12469
  } catch {
12258
12470
  return false;
12259
12471
  }
@@ -12279,7 +12491,7 @@ function validateStationId(stationId) {
12279
12491
  }
12280
12492
  }
12281
12493
  function sha256File(filePath) {
12282
- return createHash6("sha256").update(readFileSync16(filePath)).digest("hex");
12494
+ return createHash6("sha256").update(readFileSync17(filePath)).digest("hex");
12283
12495
  }
12284
12496
  function scanHome(definition, homesRoot) {
12285
12497
  const homePath = homePathFor(definition, homesRoot);
@@ -12309,7 +12521,7 @@ function scanHome(definition, homesRoot) {
12309
12521
  skipped.push({ relativePath: entry.relativePath, reason: "not-regular-file" });
12310
12522
  continue;
12311
12523
  }
12312
- const info = statSync11(entry.fullPath);
12524
+ const info = statSync12(entry.fullPath);
12313
12525
  portable.push({
12314
12526
  relativePath: entry.relativePath,
12315
12527
  fullPath: entry.fullPath,
@@ -12377,7 +12589,7 @@ function writeStationSnapshot(options) {
12377
12589
  const untouched = [];
12378
12590
  for (const plan of plans) {
12379
12591
  const destination = resolve2(repoRoot, plan.destination);
12380
- const destinationRelative = relative5(repoRoot, destination);
12592
+ const destinationRelative = relative6(repoRoot, destination);
12381
12593
  if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep4) || isAbsolute3(destinationRelative)) {
12382
12594
  throw new StationSnapshotError("DESTINATION_ESCAPE", `destination escapes repo root: ${plan.destination}`);
12383
12595
  }
@@ -12434,9 +12646,9 @@ import { createHash as createHash7 } from "crypto";
12434
12646
  import {
12435
12647
  copyFileSync as copyFileSync3,
12436
12648
  mkdirSync as mkdirSync14,
12437
- readdirSync as readdirSync11,
12438
- readFileSync as readFileSync17,
12439
- statSync as statSync12,
12649
+ readdirSync as readdirSync12,
12650
+ readFileSync as readFileSync18,
12651
+ statSync as statSync13,
12440
12652
  writeFileSync as writeFileSync13
12441
12653
  } from "fs";
12442
12654
  import { dirname as dirname12, join as join21, resolve as resolve3, sep as sep5 } from "path";
@@ -12454,7 +12666,7 @@ function readSnapshotManifest(repoRoot, stationId) {
12454
12666
  const manifestPath = join21(snapshotRoot, "sync-manifest.json");
12455
12667
  let manifest;
12456
12668
  try {
12457
- manifest = JSON.parse(readFileSync17(manifestPath, "utf8"));
12669
+ manifest = JSON.parse(readFileSync18(manifestPath, "utf8"));
12458
12670
  } catch (error) {
12459
12671
  fail("MANIFEST_UNREADABLE", `cannot read snapshot manifest: ${manifestPath}: ${error.message}`);
12460
12672
  }
@@ -12481,7 +12693,7 @@ function planStationHydration(stationId, repoRoot) {
12481
12693
  const agentRoot = join21(snapshotRoot, "agent-homes", agent);
12482
12694
  let identEntries;
12483
12695
  try {
12484
- identEntries = readdirSync11(agentRoot, { withFileTypes: true });
12696
+ identEntries = readdirSync12(agentRoot, { withFileTypes: true });
12485
12697
  } catch {
12486
12698
  continue;
12487
12699
  }
@@ -12536,7 +12748,7 @@ function planStationHydration(stationId, repoRoot) {
12536
12748
  });
12537
12749
  continue;
12538
12750
  }
12539
- const info = statSync12(entry.fullPath);
12751
+ const info = statSync13(entry.fullPath);
12540
12752
  const manifestHash = manifestHashes.get(`${agent}${MANIFEST_HASH_KEY_SEP}${homeRelative}`) ?? null;
12541
12753
  let verified = false;
12542
12754
  if (manifestHash !== null) {
@@ -12590,7 +12802,7 @@ function planStationHydration(stationId, repoRoot) {
12590
12802
  for (const copy of copies) {
12591
12803
  let isStub = false;
12592
12804
  try {
12593
- isStub = isPointerSkillMd(readFileSync17(copy.fullPath, "utf8"));
12805
+ isStub = isPointerSkillMd(readFileSync18(copy.fullPath, "utf8"));
12594
12806
  } catch {
12595
12807
  isStub = false;
12596
12808
  }