@hasna/skills 0.1.71 → 0.1.72

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/server.js CHANGED
@@ -22940,7 +22940,7 @@ var init_dist_es9 = __esm(() => {
22940
22940
  // package.json
22941
22941
  var package_default = {
22942
22942
  name: "@hasna/skills",
22943
- version: "0.1.71",
22943
+ version: "0.1.72",
22944
22944
  description: "Skills library for AI coding agents",
22945
22945
  type: "module",
22946
22946
  bin: {
@@ -23032,6 +23032,7 @@ var package_default = {
23032
23032
  "@aws-sdk/client-ecs": "^3.1079.0",
23033
23033
  "@aws-sdk/client-s3": "^3.1079.0",
23034
23034
  "@hasna/events": "0.1.16",
23035
+ "@hasna/paths": "0.1.0",
23035
23036
  "@modelcontextprotocol/sdk": "^1.26.0",
23036
23037
  chalk: "^5.3.0",
23037
23038
  commander: "^12.1.0",
@@ -33109,13 +33110,12 @@ import { mkdirSync as mkdirSync3 } from "fs";
33109
33110
  import { dirname as dirname5 } from "path";
33110
33111
 
33111
33112
  // src/server/database-url.ts
33112
- import { isAbsolute, join as join3 } from "path";
33113
+ import { isAbsolute, join as join5 } from "path";
33113
33114
  import { fileURLToPath } from "url";
33114
33115
 
33115
33116
  // src/lib/config.ts
33116
- import { existsSync, readFileSync as readFileSync2, writeFileSync, mkdirSync, copyFileSync, readdirSync, statSync } from "fs";
33117
- import { join as join2, dirname as dirname2 } from "path";
33118
- import { homedir as homedir2 } from "os";
33117
+ import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync, mkdirSync, copyFileSync, readdirSync, statSync } from "fs";
33118
+ import { join as join4, dirname as dirname2 } from "path";
33119
33119
 
33120
33120
  // src/lib/retired-settings.ts
33121
33121
  var RETIRED_ENV_SUFFIXES = ["_STORAGE_MODE", "_DEPLOYMENT_MODE", "_CLOUD_MODE"];
@@ -33163,6 +33163,119 @@ function assertNoRetiredConfigKeys(config, source) {
33163
33163
  }
33164
33164
  }
33165
33165
 
33166
+ // src/lib/app-home.ts
33167
+ import { existsSync } from "fs";
33168
+ import { homedir as homedir3 } from "os";
33169
+ import { join as join3, resolve } from "path";
33170
+
33171
+ // ../../node_modules/.bun/@hasna+paths@0.1.0/node_modules/@hasna/paths/dist/index.js
33172
+ import { homedir as homedir2 } from "os";
33173
+ import { join as join2 } from "path";
33174
+ var KIND_ENV = {
33175
+ config: "HASNA_CONFIG_HOME",
33176
+ data: "HASNA_DATA_HOME",
33177
+ state: "HASNA_STATE_HOME",
33178
+ cache: "HASNA_CACHE_HOME"
33179
+ };
33180
+ var APP_SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
33181
+ function assertApp(app) {
33182
+ if (typeof app !== "string" || app.length === 0) {
33183
+ throw new TypeError("paths: app must be a non-empty string");
33184
+ }
33185
+ if (!APP_SLUG_RE.test(app)) {
33186
+ throw new TypeError(`paths: invalid app slug "${app}" \u2014 expected lowercase kebab-case ([a-z0-9]+(-[a-z0-9]+)*)`);
33187
+ }
33188
+ }
33189
+ function envOf(options) {
33190
+ return options.env ?? process.env;
33191
+ }
33192
+ function envValue(options, kind) {
33193
+ const value = envOf(options)[KIND_ENV[kind]];
33194
+ return typeof value === "string" && value.length > 0 ? value : undefined;
33195
+ }
33196
+ function isMacOS(platform) {
33197
+ return platform === "darwin";
33198
+ }
33199
+ function baseDir(kind, options) {
33200
+ const override = envValue(options, kind);
33201
+ if (override)
33202
+ return override;
33203
+ const home = options.home ?? homedir2();
33204
+ const platform = options.platform ?? process.platform;
33205
+ if (isMacOS(platform)) {
33206
+ switch (kind) {
33207
+ case "config":
33208
+ case "data":
33209
+ return join2(home, "Library", "Application Support", "Hasna");
33210
+ case "cache":
33211
+ return join2(home, "Library", "Caches", "Hasna");
33212
+ case "state":
33213
+ return join2(home, "Library", "Logs", "Hasna");
33214
+ }
33215
+ }
33216
+ switch (kind) {
33217
+ case "config":
33218
+ return join2(home, ".config", "hasna");
33219
+ case "data":
33220
+ return join2(home, ".local", "share", "hasna");
33221
+ case "state":
33222
+ return join2(home, ".local", "state", "hasna");
33223
+ case "cache":
33224
+ return join2(home, ".cache", "hasna");
33225
+ }
33226
+ }
33227
+ function resolvePath(kind, options) {
33228
+ assertApp(options.app);
33229
+ const appSegment = options.internal === true ? join2("internal", options.app) : options.app;
33230
+ return join2(baseDir(kind, options), appSegment);
33231
+ }
33232
+ function dataDir(options) {
33233
+ return resolvePath("data", options);
33234
+ }
33235
+
33236
+ // src/lib/app-home.ts
33237
+ var DATA_DIR_ENV = "HASNA_SKILLS_DIR";
33238
+ var HASNA_SKILLS_HOME_ENV = "HASNA_SKILLS_HOME";
33239
+ var SKILLS_HOME_ENV = "SKILLS_HOME";
33240
+ var DEFAULT_SQLITE_FILENAME = "server.db";
33241
+ var GLOBAL_CONFIG_FILENAME = "config.json";
33242
+ function effectiveHome() {
33243
+ return process.env["HOME"] || process.env["USERPROFILE"] || homedir3() || "/tmp";
33244
+ }
33245
+ function legacyDataRoot() {
33246
+ return join3(effectiveHome(), ".hasna", "skills");
33247
+ }
33248
+ function resolverDataRoot(home = effectiveHome()) {
33249
+ return dataDir({ app: "skills", home });
33250
+ }
33251
+ function adoptResolverDataRoot(resolved, env = process.env) {
33252
+ const dataOverride = env.HASNA_DATA_HOME;
33253
+ if (typeof dataOverride === "string" && dataOverride.trim().length > 0)
33254
+ return true;
33255
+ return existsSync(join3(resolved, DEFAULT_SQLITE_FILENAME)) || existsSync(join3(resolved, GLOBAL_CONFIG_FILENAME));
33256
+ }
33257
+ function exactDataRoot() {
33258
+ for (const key of [DATA_DIR_ENV, HASNA_SKILLS_HOME_ENV, SKILLS_HOME_ENV]) {
33259
+ const dir = process.env[key]?.trim();
33260
+ if (dir)
33261
+ return resolve(dir);
33262
+ }
33263
+ return;
33264
+ }
33265
+ function hasExactOverride(env = process.env) {
33266
+ return Boolean(env[DATA_DIR_ENV]?.trim()) || Boolean(env[HASNA_SKILLS_HOME_ENV]?.trim()) || Boolean(env[SKILLS_HOME_ENV]?.trim());
33267
+ }
33268
+ function hasOperatorOverride(env = process.env) {
33269
+ return hasExactOverride(env) || Boolean(env.HASNA_DATA_HOME?.trim());
33270
+ }
33271
+ function getDataRoot() {
33272
+ const exact = exactDataRoot();
33273
+ if (exact)
33274
+ return exact;
33275
+ const resolved = resolverDataRoot();
33276
+ return adoptResolverDataRoot(resolved) ? resolve(resolved) : resolve(legacyDataRoot());
33277
+ }
33278
+
33166
33279
  // src/lib/config.ts
33167
33280
  var ENUM_KEYS = {
33168
33281
  defaultAgent: ["claude", "codex", "gemini", "pi", "opencode", "all"],
@@ -33177,19 +33290,19 @@ function allowedValues(key) {
33177
33290
  return ENUM_KEYS[key];
33178
33291
  }
33179
33292
  function mergeDirectoryContents(sourceDir, targetDir) {
33180
- if (!existsSync(sourceDir))
33293
+ if (!existsSync2(sourceDir))
33181
33294
  return;
33182
33295
  mkdirSync(targetDir, { recursive: true });
33183
33296
  for (const entry of readdirSync(sourceDir)) {
33184
- const sourcePath = join2(sourceDir, entry);
33185
- const targetPath = join2(targetDir, entry);
33297
+ const sourcePath = join4(sourceDir, entry);
33298
+ const targetPath = join4(targetDir, entry);
33186
33299
  try {
33187
33300
  const sourceStat = statSync(sourcePath);
33188
33301
  if (sourceStat.isDirectory()) {
33189
33302
  mergeDirectoryContents(sourcePath, targetPath);
33190
33303
  continue;
33191
33304
  }
33192
- if (!existsSync(targetPath))
33305
+ if (!existsSync2(targetPath))
33193
33306
  copyFileSync(sourcePath, targetPath);
33194
33307
  } catch {}
33195
33308
  }
@@ -33214,44 +33327,40 @@ function normalizeConfigValue(key, value) {
33214
33327
  return value.trim() ? value : undefined;
33215
33328
  return;
33216
33329
  }
33217
- var DATA_DIR_ENV = "HASNA_SKILLS_DIR";
33218
33330
  var INSTALLED_SKILLS_DIRNAME = "installed";
33219
33331
  var SKILLS_CACHE_DIRNAME = "skills";
33220
33332
  var LAYOUT_MIGRATION_RECORD = ".layout-migration.json";
33221
33333
  function isOwnerLayoutMigrated(appDir) {
33222
- return existsSync(join2(appDir, SKILLS_CACHE_DIRNAME, LAYOUT_MIGRATION_RECORD));
33334
+ return existsSync2(join4(appDir, SKILLS_CACHE_DIRNAME, LAYOUT_MIGRATION_RECORD));
33223
33335
  }
33224
33336
  function getDataDir() {
33225
- const override = process.env[DATA_DIR_ENV];
33226
- if (override) {
33227
- try {
33228
- mkdirSync(override, { recursive: true });
33229
- } catch {}
33230
- return override;
33231
- }
33232
- const home = process.env["HOME"] || process.env["USERPROFILE"] || homedir2();
33233
- const newDir = join2(home, ".hasna", "skills");
33234
- const oldDir = join2(home, ".skills");
33235
- const oldConfigFile = join2(home, ".skillsrc");
33236
- mkdirSync(newDir, { recursive: true });
33337
+ const root3 = getDataRoot();
33237
33338
  try {
33238
- mergeDirectoryContents(oldDir, newDir);
33339
+ mkdirSync(root3, { recursive: true });
33239
33340
  } catch {}
33240
- if (existsSync(oldConfigFile) && !existsSync(join2(newDir, "config.json"))) {
33341
+ if (hasOperatorOverride())
33342
+ return root3;
33343
+ const home = effectiveHome();
33344
+ const oldDir = join4(home, ".skills");
33345
+ const oldConfigFile = join4(home, ".skillsrc");
33346
+ try {
33347
+ mergeDirectoryContents(oldDir, root3);
33348
+ } catch {}
33349
+ if (existsSync2(oldConfigFile) && !existsSync2(join4(root3, "config.json"))) {
33241
33350
  try {
33242
- copyFileSync(oldConfigFile, join2(newDir, "config.json"));
33351
+ copyFileSync(oldConfigFile, join4(root3, "config.json"));
33243
33352
  } catch {}
33244
33353
  }
33245
- return newDir;
33354
+ return root3;
33246
33355
  }
33247
33356
  function getConfigPath(scope) {
33248
33357
  if (scope === "global") {
33249
- return join2(getDataDir(), "config.json");
33358
+ return join4(getDataDir(), "config.json");
33250
33359
  }
33251
- return join2(process.cwd(), "skills.config.json");
33360
+ return join4(process.cwd(), "skills.config.json");
33252
33361
  }
33253
33362
  function readConfigFile(path) {
33254
- if (!existsSync(path))
33363
+ if (!existsSync2(path))
33255
33364
  return {};
33256
33365
  let parsed;
33257
33366
  try {
@@ -33277,14 +33386,14 @@ function loadConfig4() {
33277
33386
  }
33278
33387
 
33279
33388
  // src/server/database-url.ts
33280
- var DEFAULT_SQLITE_FILENAME = "server.db";
33389
+ var DEFAULT_SQLITE_FILENAME2 = "server.db";
33281
33390
  var SQLITE_MEMORY_PATH = ":memory:";
33282
33391
  var POSTGRES_SCHEMES = new Set(["postgres", "postgresql"]);
33283
33392
  var SQLITE_SCHEMES = new Set(["sqlite", "sqlite3", "file"]);
33284
33393
  var MEMORY_SCHEMES = new Set(["memory"]);
33285
33394
  var SQLITE_EXTENSIONS = [".db", ".sqlite", ".sqlite3", ".db3"];
33286
33395
  function defaultSqlitePath() {
33287
- return join3(getDataDir(), DEFAULT_SQLITE_FILENAME);
33396
+ return join5(getDataDir(), DEFAULT_SQLITE_FILENAME2);
33288
33397
  }
33289
33398
  function resolveDatabaseTarget(raw) {
33290
33399
  const value = raw?.trim();
@@ -33311,7 +33420,7 @@ function resolveDatabaseTarget(raw) {
33311
33420
  throw new Error(`unsupported database scheme "${scheme}:". Supported: postgres://, postgresql://, sqlite:, file:, ` + `an absolute or relative path to a .db/.sqlite file, ":memory:", or "memory:" (non-durable, tests only). ` + `Leave the setting empty to use the default SQLite database at ${defaultSqlitePath()}.`);
33312
33421
  }
33313
33422
  if (looksLikeSqlitePath(value)) {
33314
- const path = isAbsolute(value) ? value : join3(process.cwd(), value);
33423
+ const path = isAbsolute(value) ? value : join5(process.cwd(), value);
33315
33424
  return { kind: "sqlite", path, durable: true, label: `sqlite (${path})` };
33316
33425
  }
33317
33426
  throw new Error(`could not resolve a database backend from "${value}". Use postgres://\u2026, sqlite:\u2026, an absolute or ` + `relative path ending in ${SQLITE_EXTENSIONS.join("/")}, ":memory:", or leave it empty for the ` + `default SQLite database at ${defaultSqlitePath()}.`);
@@ -33335,7 +33444,7 @@ function sqlitePathFromUrl(value, scheme) {
33335
33444
  }
33336
33445
  return rest.slice(2).replace(/^\/\/+/, "/");
33337
33446
  }
33338
- return isAbsolute(rest) ? rest : join3(process.cwd(), rest);
33447
+ return isAbsolute(rest) ? rest : join5(process.cwd(), rest);
33339
33448
  }
33340
33449
  function looksLikeSqlitePath(value) {
33341
33450
  if (value.includes("/"))
@@ -33347,7 +33456,7 @@ function looksLikeSqlitePath(value) {
33347
33456
  import { Database } from "bun:sqlite";
33348
33457
  import { randomUUID as randomUUID2 } from "crypto";
33349
33458
  import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, readdirSync as readdirSync2 } from "fs";
33350
- import { dirname as dirname4, join as join5 } from "path";
33459
+ import { dirname as dirname4, join as join7 } from "path";
33351
33460
 
33352
33461
  // src/server/auth.ts
33353
33462
  import { createHash as createHash3 } from "crypto";
@@ -33380,16 +33489,16 @@ function publicPrincipal(partial = {}) {
33380
33489
  }
33381
33490
 
33382
33491
  // src/server/migrations-dir.ts
33383
- import { existsSync as existsSync2 } from "fs";
33384
- import { dirname as dirname3, join as join4 } from "path";
33492
+ import { existsSync as existsSync3 } from "fs";
33493
+ import { dirname as dirname3, join as join6 } from "path";
33385
33494
  var MIGRATION_DIALECTS = ["postgres", "sqlite"];
33386
33495
  var MAX_WALK_UP = 6;
33387
33496
  function findMigrationsRoot(startDirs = defaultStartDirs()) {
33388
33497
  for (const start of startDirs) {
33389
33498
  let dir = start;
33390
33499
  for (let level = 0;level < MAX_WALK_UP; level += 1) {
33391
- const candidate = join4(dir, "migrations");
33392
- if (MIGRATION_DIALECTS.some((dialect) => existsSync2(join4(candidate, dialect))))
33500
+ const candidate = join6(dir, "migrations");
33501
+ if (MIGRATION_DIALECTS.some((dialect) => existsSync3(join6(candidate, dialect))))
33393
33502
  return candidate;
33394
33503
  const parent = dirname3(dir);
33395
33504
  if (parent === dir)
@@ -33403,8 +33512,8 @@ function resolveMigrationsDir(dialect, root3 = findMigrationsRoot()) {
33403
33512
  if (!root3) {
33404
33513
  throw new Error(`could not locate the migrations directory for dialect "${dialect}". ` + `Expected a migrations/${dialect}/ folder alongside the package root.`);
33405
33514
  }
33406
- const dir = join4(root3, dialect);
33407
- if (!existsSync2(dir)) {
33515
+ const dir = join6(root3, dialect);
33516
+ if (!existsSync3(dir)) {
33408
33517
  throw new Error(`migrations directory not found: ${dir}`);
33409
33518
  }
33410
33519
  return dir;
@@ -34149,7 +34258,7 @@ function applySqliteMigrations(db, migrationsDir = resolveMigrationsDir("sqlite"
34149
34258
  const appliedNow = [];
34150
34259
  for (const file of files) {
34151
34260
  const version2 = file.replace(/\.sql$/, "");
34152
- const text = readFileSync3(join5(migrationsDir, file), "utf8");
34261
+ const text = readFileSync3(join7(migrationsDir, file), "utf8");
34153
34262
  const apply = db.transaction(() => {
34154
34263
  const already = db.query("SELECT 1 AS present FROM schema_migrations WHERE version = ? LIMIT 1").get(version2);
34155
34264
  if (already)
@@ -35553,17 +35662,17 @@ function mergeSkillRegistryLists(...groups) {
35553
35662
  }
35554
35663
 
35555
35664
  // src/server/registry.ts
35556
- import { existsSync as existsSync9, readFileSync as readFileSync9 } from "fs";
35557
- import { resolve, sep } from "path";
35665
+ import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
35666
+ import { resolve as resolve2, sep } from "path";
35558
35667
 
35559
35668
  // src/lib/registry.ts
35560
- import { existsSync as existsSync6, readFileSync as readFileSync6, readdirSync as readdirSync5 } from "fs";
35561
- import { join as join9 } from "path";
35669
+ import { existsSync as existsSync7, readFileSync as readFileSync6, readdirSync as readdirSync5 } from "fs";
35670
+ import { join as join11 } from "path";
35562
35671
 
35563
35672
  // src/lib/portable-skills.ts
35564
35673
  import {
35565
35674
  cpSync as cpSync2,
35566
- existsSync as existsSync5,
35675
+ existsSync as existsSync6,
35567
35676
  mkdirSync as mkdirSync5,
35568
35677
  mkdtempSync,
35569
35678
  readdirSync as readdirSync4,
@@ -35572,7 +35681,7 @@ import {
35572
35681
  statSync as statSync4,
35573
35682
  writeFileSync as writeFileSync3
35574
35683
  } from "fs";
35575
- import { basename as basename2, dirname as dirname7, isAbsolute as isAbsolute2, join as join8, normalize } from "path";
35684
+ import { basename as basename2, dirname as dirname7, isAbsolute as isAbsolute2, join as join10, normalize } from "path";
35576
35685
 
35577
35686
  // src/lib/registry-data/development-tools.ts
35578
35687
  var DEVELOPMENT_TOOLS_SKILLS = [
@@ -36292,8 +36401,8 @@ var SKILLS = [
36292
36401
  ];
36293
36402
 
36294
36403
  // src/lib/hosted-skill-set.ts
36295
- import { existsSync as existsSync3, readFileSync as readFileSync4, readdirSync as readdirSync3, statSync as statSync2 } from "fs";
36296
- import { join as join6 } from "path";
36404
+ import { existsSync as existsSync4, readFileSync as readFileSync4, readdirSync as readdirSync3, statSync as statSync2 } from "fs";
36405
+ import { join as join8 } from "path";
36297
36406
  var HOSTED_RUNTIMES = new Set(["hosted"]);
36298
36407
  var HOSTED_SOURCES = new Set(["remote", "private-hosted"]);
36299
36408
  function normalizeMarker(value) {
@@ -36306,8 +36415,8 @@ function isHostedMetadataPackage(pkg) {
36306
36415
  return HOSTED_RUNTIMES.has(normalizeMarker(skills.runtime)) || HOSTED_SOURCES.has(normalizeMarker(skills.source));
36307
36416
  }
36308
36417
  function isHostedMetadataSkillDir(skillDir) {
36309
- const pkgPath = join6(skillDir, "package.json");
36310
- if (!existsSync3(pkgPath))
36418
+ const pkgPath = join8(skillDir, "package.json");
36419
+ if (!existsSync4(pkgPath))
36311
36420
  return false;
36312
36421
  try {
36313
36422
  return isHostedMetadataPackage(JSON.parse(readFileSync4(pkgPath, "utf8")));
@@ -36436,14 +36545,14 @@ var PORTABLE_SKILL_DEFAULT_VERSION = "0.1.0";
36436
36545
  // src/lib/portable-skills-files.ts
36437
36546
  import {
36438
36547
  cpSync,
36439
- existsSync as existsSync4,
36548
+ existsSync as existsSync5,
36440
36549
  lstatSync,
36441
36550
  mkdirSync as mkdirSync4,
36442
36551
  readFileSync as readFileSync5,
36443
36552
  realpathSync,
36444
36553
  writeFileSync as writeFileSync2
36445
36554
  } from "fs";
36446
- import { basename, dirname as dirname6, join as join7, relative } from "path";
36555
+ import { basename, dirname as dirname6, join as join9, relative } from "path";
36447
36556
  var ANY_SEGMENT_COPY_EXCLUDES = new Set([
36448
36557
  ".git",
36449
36558
  ".DS_Store",
@@ -36471,12 +36580,12 @@ function normalizePortableSkillName(name) {
36471
36580
  return normalized;
36472
36581
  }
36473
36582
  function readPortableSkillManifest(skillPath, fallbackName = basename(skillPath)) {
36474
- const skillJsonPath = join7(skillPath, "skill.json");
36475
- const skillMdPath = join7(skillPath, "SKILL.md");
36476
- const pkgPath = join7(skillPath, "package.json");
36477
- const jsonManifest = existsSync4(skillJsonPath) ? readJsonObject(skillJsonPath) : undefined;
36478
- const frontmatter = existsSync4(skillMdPath) ? parseSkillFrontmatter(readFileSync5(skillMdPath, "utf-8")) ?? undefined : undefined;
36479
- const pkg = existsSync4(pkgPath) ? readJsonObject(pkgPath) : undefined;
36583
+ const skillJsonPath = join9(skillPath, "skill.json");
36584
+ const skillMdPath = join9(skillPath, "SKILL.md");
36585
+ const pkgPath = join9(skillPath, "package.json");
36586
+ const jsonManifest = existsSync5(skillJsonPath) ? readJsonObject(skillJsonPath) : undefined;
36587
+ const frontmatter = existsSync5(skillMdPath) ? parseSkillFrontmatter(readFileSync5(skillMdPath, "utf-8")) ?? undefined : undefined;
36588
+ const pkg = existsSync5(pkgPath) ? readJsonObject(pkgPath) : undefined;
36480
36589
  const name = normalizePortableSkillName(stringField(jsonManifest, "name") ?? frontmatter?.name ?? stringValue(pkg?.name) ?? fallbackName);
36481
36590
  const description = stringField(jsonManifest, "description") ?? frontmatter?.description ?? stringValue(pkg?.description) ?? `${name} skill`;
36482
36591
  const version2 = stringField(jsonManifest, "version") ?? frontmatter?.version ?? stringValue(pkg?.version) ?? PORTABLE_SKILL_DEFAULT_VERSION;
@@ -36632,18 +36741,18 @@ var LEGACY_CUSTOM_DIRNAME = "custom";
36632
36741
  function getPortableSkillsRoot(options = {}) {
36633
36742
  if (options.rootDir)
36634
36743
  return options.rootDir;
36635
- const appDir = options.homeDir ? join8(options.homeDir, ".hasna", "skills") : getDataDir();
36636
- const cache3 = join8(appDir, SKILLS_CACHE_DIRNAME);
36744
+ const appDir = options.homeDir ? join10(options.homeDir, ".hasna", "skills") : getDataDir();
36745
+ const cache3 = join10(appDir, SKILLS_CACHE_DIRNAME);
36637
36746
  if (isOwnerLayoutMigrated(appDir) && safeIsDirectory(cache3))
36638
36747
  return cache3;
36639
- const installed = join8(appDir, INSTALLED_SKILLS_DIRNAME);
36748
+ const installed = join10(appDir, INSTALLED_SKILLS_DIRNAME);
36640
36749
  migrateLegacySkillLayout(appDir, installed);
36641
36750
  return installed;
36642
36751
  }
36643
36752
  function looksLikeSkillDirectory(path) {
36644
36753
  if (!safeIsDirectory(path))
36645
36754
  return false;
36646
- return existsSync5(join8(path, "SKILL.md")) || existsSync5(join8(path, "skill.json")) || existsSync5(join8(path, "package.json"));
36755
+ return existsSync6(join10(path, "SKILL.md")) || existsSync6(join10(path, "skill.json")) || existsSync6(join10(path, "package.json"));
36647
36756
  }
36648
36757
  function migrateLegacySkillLayout(appDir, installed) {
36649
36758
  if (!safeIsDirectory(appDir))
@@ -36653,7 +36762,7 @@ function migrateLegacySkillLayout(appDir, installed) {
36653
36762
  for (const entry of readdirSync4(appDir)) {
36654
36763
  if (entry.startsWith(".") || entry === INSTALLED_SKILLS_DIRNAME)
36655
36764
  continue;
36656
- const path = join8(appDir, entry);
36765
+ const path = join10(appDir, entry);
36657
36766
  if (entry === LEGACY_CUSTOM_DIRNAME) {
36658
36767
  if (!safeIsDirectory(path))
36659
36768
  continue;
@@ -36661,7 +36770,7 @@ function migrateLegacySkillLayout(appDir, installed) {
36661
36770
  for (const nested of readdirSync4(path)) {
36662
36771
  if (nested.startsWith("."))
36663
36772
  continue;
36664
- const nestedPath = join8(path, nested);
36773
+ const nestedPath = join10(path, nested);
36665
36774
  if (looksLikeSkillDirectory(nestedPath))
36666
36775
  candidates.push({ from: nestedPath, name: nested });
36667
36776
  }
@@ -36675,10 +36784,10 @@ function migrateLegacySkillLayout(appDir, installed) {
36675
36784
  return;
36676
36785
  }
36677
36786
  for (const { from, name } of candidates) {
36678
- const target = join8(installed, name);
36679
- if (existsSync5(target))
36787
+ const target = join10(installed, name);
36788
+ if (existsSync6(target))
36680
36789
  continue;
36681
- const staging = join8(installed, `.migrating-${name}-${process.pid}`);
36790
+ const staging = join10(installed, `.migrating-${name}-${process.pid}`);
36682
36791
  try {
36683
36792
  rmSync(staging, { recursive: true, force: true });
36684
36793
  cpSync2(from, staging, { recursive: true, errorOnExist: false });
@@ -36691,7 +36800,7 @@ function migrateLegacySkillLayout(appDir, installed) {
36691
36800
  }
36692
36801
  }
36693
36802
  function getPortableSkillPath(name, options = {}) {
36694
- return join8(getPortableSkillsRoot(options), normalizePortableSkillName(name));
36803
+ return join10(getPortableSkillsRoot(options), normalizePortableSkillName(name));
36695
36804
  }
36696
36805
  function findPortableSkill(name, options = {}) {
36697
36806
  let normalized;
@@ -36701,7 +36810,7 @@ function findPortableSkill(name, options = {}) {
36701
36810
  return null;
36702
36811
  }
36703
36812
  const path = getPortableSkillPath(normalized, options);
36704
- if (!existsSync5(path) || !statSync4(path).isDirectory())
36813
+ if (!existsSync6(path) || !statSync4(path).isDirectory())
36705
36814
  return null;
36706
36815
  try {
36707
36816
  return summarizePortableSkill(path, normalized);
@@ -36717,7 +36826,7 @@ function listPortableSkills(options = {}) {
36717
36826
  for (const entry of readdirSync4(root3).sort()) {
36718
36827
  if (entry.startsWith("."))
36719
36828
  continue;
36720
- const path = join8(root3, entry);
36829
+ const path = join10(root3, entry);
36721
36830
  if (!safeIsDirectory(path))
36722
36831
  continue;
36723
36832
  try {
@@ -36810,7 +36919,7 @@ function parseSkillMdFrontmatter(content) {
36810
36919
  return Object.keys(result).length > 0 ? result : null;
36811
36920
  }
36812
36921
  function discoverSkillsInDir(dir, source = "custom") {
36813
- if (!existsSync6(dir))
36922
+ if (!existsSync7(dir))
36814
36923
  return [];
36815
36924
  const result = [];
36816
36925
  try {
@@ -36818,8 +36927,8 @@ function discoverSkillsInDir(dir, source = "custom") {
36818
36927
  for (const entry of entries) {
36819
36928
  if (!entry.isDirectory())
36820
36929
  continue;
36821
- const skillMdPath = join9(dir, entry.name, "SKILL.md");
36822
- if (!existsSync6(skillMdPath))
36930
+ const skillMdPath = join11(dir, entry.name, "SKILL.md");
36931
+ if (!existsSync7(skillMdPath))
36823
36932
  continue;
36824
36933
  let content;
36825
36934
  try {
@@ -36838,7 +36947,7 @@ function discoverSkillsInDir(dir, source = "custom") {
36838
36947
  category: fm.category || "Development Tools",
36839
36948
  tags: fm.tags || [],
36840
36949
  ...fm.kind ? { kind: fm.kind } : {},
36841
- ...isHostedMetadataSkillDir(join9(dir, entry.name)) ? { serverOwned: true } : {},
36950
+ ...isHostedMetadataSkillDir(join11(dir, entry.name)) ? { serverOwned: true } : {},
36842
36951
  source
36843
36952
  });
36844
36953
  }
@@ -36847,16 +36956,16 @@ function discoverSkillsInDir(dir, source = "custom") {
36847
36956
  }
36848
36957
  function findExtensionSkillPath(name) {
36849
36958
  const config = loadConfig4();
36850
- if (!config.extensionsDir || !existsSync6(config.extensionsDir))
36959
+ if (!config.extensionsDir || !existsSync7(config.extensionsDir))
36851
36960
  return null;
36852
36961
  try {
36853
36962
  const entries = readdirSync5(config.extensionsDir, { withFileTypes: true });
36854
36963
  for (const entry of entries) {
36855
36964
  if (!entry.isDirectory())
36856
36965
  continue;
36857
- const skillDir = join9(config.extensionsDir, entry.name);
36858
- const skillMdPath = join9(skillDir, "SKILL.md");
36859
- if (!existsSync6(skillMdPath))
36966
+ const skillDir = join11(config.extensionsDir, entry.name);
36967
+ const skillMdPath = join11(skillDir, "SKILL.md");
36968
+ if (!existsSync7(skillMdPath))
36860
36969
  continue;
36861
36970
  let content;
36862
36971
  try {
@@ -36888,12 +36997,12 @@ function loadRegistry(cwd) {
36888
36997
  if (registryCache && registryCacheKey === rootKey && now - registryCacheTime < REGISTRY_CACHE_TTL) {
36889
36998
  return registryCache;
36890
36999
  }
36891
- const dataDir = getDataDir();
37000
+ const dataDir2 = getDataDir();
36892
37001
  const config = loadConfig4();
36893
37002
  const official = SKILLS.map((s2) => ({ ...s2, source: "official" }));
36894
37003
  const extensions = config.extensionsDir ? discoverSkillsInDir(config.extensionsDir, "extension") : [];
36895
37004
  const portableCustom = listPortableSkillMetas();
36896
- const legacyCustom = discoverSkillsInDir(join9(dataDir, "custom"));
37005
+ const legacyCustom = discoverSkillsInDir(join11(dataDir2, "custom"));
36897
37006
  const globalCustom = mergeCustomSkills([...legacyCustom, ...portableCustom]);
36898
37007
  registryCache = mergeSkillRegistryLists(official, extensions, globalCustom);
36899
37008
  registryCacheTime = now;
@@ -36913,12 +37022,12 @@ function mergeCustomSkills(skills) {
36913
37022
  }
36914
37023
 
36915
37024
  // src/lib/skillinfo.ts
36916
- import { existsSync as existsSync8, readFileSync as readFileSync8 } from "fs";
36917
- import { join as join11 } from "path";
37025
+ import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
37026
+ import { join as join13 } from "path";
36918
37027
 
36919
37028
  // src/lib/installer.ts
36920
- import { existsSync as existsSync7, readFileSync as readFileSync7, rmSync as rmSync2 } from "fs";
36921
- import { dirname as dirname8, join as join10 } from "path";
37029
+ import { existsSync as existsSync8, readFileSync as readFileSync7, rmSync as rmSync2 } from "fs";
37030
+ import { dirname as dirname8, join as join12 } from "path";
36922
37031
  import { fileURLToPath as fileURLToPath2 } from "url";
36923
37032
  // src/lib/utils.ts
36924
37033
  function normalizeSkillName(name) {
@@ -36930,12 +37039,12 @@ var __dirname2 = dirname8(fileURLToPath2(import.meta.url));
36930
37039
  function findSkillsDir() {
36931
37040
  let dir = __dirname2;
36932
37041
  for (let i3 = 0;i3 < 5; i3++) {
36933
- const candidate = join10(dir, "skills");
36934
- if (existsSync7(candidate) && !dir.includes(".skills"))
37042
+ const candidate = join12(dir, "skills");
37043
+ if (existsSync8(candidate) && !dir.includes(".skills"))
36935
37044
  return candidate;
36936
37045
  dir = dirname8(dir);
36937
37046
  }
36938
- return join10(__dirname2, "..", "skills");
37047
+ return join12(__dirname2, "..", "skills");
36939
37048
  }
36940
37049
  var SKILLS_DIR = findSkillsDir();
36941
37050
  function getSkillPath(name) {
@@ -36943,13 +37052,13 @@ function getSkillPath(name) {
36943
37052
  const portable = findPortableSkill(skillName);
36944
37053
  if (portable)
36945
37054
  return portable.path;
36946
- const legacyCustomPath = join10(getDataDir(), "custom", skillName);
36947
- if (existsSync7(legacyCustomPath))
37055
+ const legacyCustomPath = join12(getDataDir(), "custom", skillName);
37056
+ if (existsSync8(legacyCustomPath))
36948
37057
  return legacyCustomPath;
36949
37058
  const extensionPath = findExtensionSkillPath(skillName);
36950
37059
  if (extensionPath)
36951
37060
  return extensionPath;
36952
- return join10(SKILLS_DIR, skillName);
37061
+ return join12(SKILLS_DIR, skillName);
36953
37062
  }
36954
37063
  function getCanonicalSkillName(name) {
36955
37064
  return getSkill(name)?.name ?? resolveSkillAlias(normalizeSkillSlug(name));
@@ -36958,17 +37067,17 @@ function getCanonicalSkillName(name) {
36958
37067
  // src/lib/skillinfo.ts
36959
37068
  function getSkillDocs(name) {
36960
37069
  const skillPath = getSkillPath(name);
36961
- if (!existsSync8(skillPath))
37070
+ if (!existsSync9(skillPath))
36962
37071
  return null;
36963
37072
  return {
36964
- skillMd: readIfExists(join11(skillPath, "SKILL.md")),
36965
- readme: readIfExists(join11(skillPath, "README.md")),
36966
- claudeMd: readIfExists(join11(skillPath, "CLAUDE.md"))
37073
+ skillMd: readIfExists(join13(skillPath, "SKILL.md")),
37074
+ readme: readIfExists(join13(skillPath, "README.md")),
37075
+ claudeMd: readIfExists(join13(skillPath, "CLAUDE.md"))
36967
37076
  };
36968
37077
  }
36969
37078
  function readIfExists(path) {
36970
37079
  try {
36971
- if (existsSync8(path)) {
37080
+ if (existsSync9(path)) {
36972
37081
  return readFileSync8(path, "utf-8");
36973
37082
  }
36974
37083
  } catch {}
@@ -37006,11 +37115,11 @@ function getServerSkillMd(slug) {
37006
37115
  const docs = getSkillDocs(name);
37007
37116
  if (docs?.skillMd)
37008
37117
  return docs.skillMd;
37009
- const skillsDir = resolve(process.cwd(), "skills");
37010
- const path = resolve(skillsDir, name, "SKILL.md");
37118
+ const skillsDir = resolve2(process.cwd(), "skills");
37119
+ const path = resolve2(skillsDir, name, "SKILL.md");
37011
37120
  if (!isInsideDir(skillsDir, path))
37012
37121
  return null;
37013
- return existsSync9(path) ? readFileSync9(path, "utf8") : null;
37122
+ return existsSync10(path) ? readFileSync9(path, "utf8") : null;
37014
37123
  }
37015
37124
 
37016
37125
  // src/server/skills-api.ts
@@ -37420,6 +37529,9 @@ async function createSkillsFetchHandler(options = {}) {
37420
37529
  if (request.method === "GET" && url.pathname === "/ready") {
37421
37530
  return json({ ok: true, service: "skills" });
37422
37531
  }
37532
+ if (request.method === "GET" && url.pathname === "/version") {
37533
+ return json({ ok: true, service: "skills", version: package_default.version });
37534
+ }
37423
37535
  if (url.pathname.startsWith("/api/")) {
37424
37536
  const principal = await authenticateRequest(store, request);
37425
37537
  if (!principal)