@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/index.js +639 -858
- package/bin/mcp.js +342 -232
- package/bin/migrate.js +149 -40
- package/bin/server.js +212 -100
- package/bin/worker.js +151 -45
- package/dist/index.js +461 -331
- package/dist/lib/app-home.d.ts +85 -0
- package/dist/lib/config.d.ts +9 -10
- package/dist/lib/station-hydrate.d.ts +2 -0
- package/dist/lib/station-snapshot.d.ts +1 -1
- package/dist/sdk/index.js +518 -630
- package/dist/storage.js +155 -43
- package/package.json +2 -1
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.
|
|
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
|
|
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
|
|
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 (!
|
|
33293
|
+
if (!existsSync2(sourceDir))
|
|
33181
33294
|
return;
|
|
33182
33295
|
mkdirSync(targetDir, { recursive: true });
|
|
33183
33296
|
for (const entry of readdirSync(sourceDir)) {
|
|
33184
|
-
const sourcePath =
|
|
33185
|
-
const targetPath =
|
|
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 (!
|
|
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
|
|
33334
|
+
return existsSync2(join4(appDir, SKILLS_CACHE_DIRNAME, LAYOUT_MIGRATION_RECORD));
|
|
33223
33335
|
}
|
|
33224
33336
|
function getDataDir() {
|
|
33225
|
-
const
|
|
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
|
-
|
|
33339
|
+
mkdirSync(root3, { recursive: true });
|
|
33239
33340
|
} catch {}
|
|
33240
|
-
if (
|
|
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,
|
|
33351
|
+
copyFileSync(oldConfigFile, join4(root3, "config.json"));
|
|
33243
33352
|
} catch {}
|
|
33244
33353
|
}
|
|
33245
|
-
return
|
|
33354
|
+
return root3;
|
|
33246
33355
|
}
|
|
33247
33356
|
function getConfigPath(scope) {
|
|
33248
33357
|
if (scope === "global") {
|
|
33249
|
-
return
|
|
33358
|
+
return join4(getDataDir(), "config.json");
|
|
33250
33359
|
}
|
|
33251
|
-
return
|
|
33360
|
+
return join4(process.cwd(), "skills.config.json");
|
|
33252
33361
|
}
|
|
33253
33362
|
function readConfigFile(path) {
|
|
33254
|
-
if (!
|
|
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
|
|
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
|
|
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 :
|
|
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 :
|
|
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
|
|
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
|
|
33384
|
-
import { dirname as dirname3, join as
|
|
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 =
|
|
33392
|
-
if (MIGRATION_DIALECTS.some((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 =
|
|
33407
|
-
if (!
|
|
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(
|
|
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
|
|
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
|
|
35561
|
-
import { join as
|
|
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
|
|
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
|
|
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
|
|
36296
|
-
import { join as
|
|
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 =
|
|
36310
|
-
if (!
|
|
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
|
|
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
|
|
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 =
|
|
36475
|
-
const skillMdPath =
|
|
36476
|
-
const pkgPath =
|
|
36477
|
-
const jsonManifest =
|
|
36478
|
-
const frontmatter =
|
|
36479
|
-
const pkg =
|
|
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 ?
|
|
36636
|
-
const cache3 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
36679
|
-
if (
|
|
36787
|
+
const target = join10(installed, name);
|
|
36788
|
+
if (existsSync6(target))
|
|
36680
36789
|
continue;
|
|
36681
|
-
const staging =
|
|
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
|
|
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 (!
|
|
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 =
|
|
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 (!
|
|
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 =
|
|
36822
|
-
if (!
|
|
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(
|
|
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 || !
|
|
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 =
|
|
36858
|
-
const skillMdPath =
|
|
36859
|
-
if (!
|
|
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
|
|
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(
|
|
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
|
|
36917
|
-
import { join as
|
|
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
|
|
36921
|
-
import { dirname as dirname8, join as
|
|
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 =
|
|
36934
|
-
if (
|
|
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
|
|
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 =
|
|
36947
|
-
if (
|
|
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
|
|
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 (!
|
|
37070
|
+
if (!existsSync9(skillPath))
|
|
36962
37071
|
return null;
|
|
36963
37072
|
return {
|
|
36964
|
-
skillMd: readIfExists(
|
|
36965
|
-
readme: readIfExists(
|
|
36966
|
-
claudeMd: readIfExists(
|
|
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 (
|
|
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 =
|
|
37010
|
-
const path =
|
|
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
|
|
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)
|