@hasna/skills 0.1.71 → 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/README.md +4 -2
- package/bin/index.js +24657 -24681
- package/bin/mcp.js +372 -243
- package/bin/migrate.js +202 -41
- package/bin/server.js +3502 -2898
- package/bin/worker.js +2187 -1806
- package/dist/cli/commands/install.d.ts +16 -0
- package/dist/cli/commands/publish.d.ts +35 -0
- package/dist/cli/commands/registry.d.ts +5 -0
- package/dist/index.js +705 -363
- package/dist/lib/app-home.d.ts +111 -0
- package/dist/lib/config.d.ts +9 -10
- package/dist/lib/feedback.d.ts +6 -0
- package/dist/lib/installer.d.ts +2 -0
- package/dist/lib/pull.d.ts +18 -1
- package/dist/lib/remote-client.d.ts +15 -1
- package/dist/lib/skill-version.d.ts +11 -0
- package/dist/lib/station-hydrate.d.ts +2 -0
- package/dist/lib/station-snapshot.d.ts +1 -1
- package/dist/sdk/index.js +5723 -5340
- package/dist/server/app.d.ts +3 -0
- package/dist/server/artifact-storage.d.ts +27 -0
- package/dist/server/config.d.ts +2 -0
- package/dist/server/rows.d.ts +2 -1
- package/dist/server/seed-bundled.d.ts +20 -0
- package/dist/server/skills-api.d.ts +18 -1
- package/dist/server/sqlite-store.d.ts +3 -1
- package/dist/server/store.d.ts +6 -1
- package/dist/server/types.d.ts +43 -0
- package/dist/storage.js +156 -43
- package/migrations/postgres/0006_skill_versions.sql +30 -0
- package/migrations/sqlite/0006_skill_versions.sql +17 -0
- package/package.json +2 -2
package/bin/mcp.js
CHANGED
|
@@ -6679,10 +6679,114 @@ var init_retired_settings = __esm(() => {
|
|
|
6679
6679
|
};
|
|
6680
6680
|
});
|
|
6681
6681
|
|
|
6682
|
-
// src/lib/
|
|
6683
|
-
import { existsSync
|
|
6684
|
-
import { join, dirname } from "path";
|
|
6682
|
+
// src/lib/app-home.ts
|
|
6683
|
+
import { existsSync } from "fs";
|
|
6685
6684
|
import { homedir } from "os";
|
|
6685
|
+
import { join, resolve } from "path";
|
|
6686
|
+
import { homedir as pathsResolverHomedir } from "os";
|
|
6687
|
+
import { join as pathsResolverJoin } from "path";
|
|
6688
|
+
function pathsResolverAssertApp(app) {
|
|
6689
|
+
if (typeof app !== "string" || app.length === 0) {
|
|
6690
|
+
throw new TypeError("paths: app must be a non-empty string");
|
|
6691
|
+
}
|
|
6692
|
+
if (!PATHS_RESOLVER_APP_SLUG_RE.test(app)) {
|
|
6693
|
+
throw new TypeError(`paths: invalid app slug "${app}" \u2014 expected lowercase kebab-case ([a-z0-9]+(-[a-z0-9]+)*)`);
|
|
6694
|
+
}
|
|
6695
|
+
}
|
|
6696
|
+
function pathsResolverAssertKind(kind) {
|
|
6697
|
+
if (!Object.keys(PATHS_RESOLVER_KIND_ENV).includes(kind)) {
|
|
6698
|
+
throw new TypeError(`paths: invalid path kind "${kind}" \u2014 expected one of ${Object.keys(PATHS_RESOLVER_KIND_ENV).join(", ")}`);
|
|
6699
|
+
}
|
|
6700
|
+
}
|
|
6701
|
+
function pathsResolverBaseDir(kind, options) {
|
|
6702
|
+
pathsResolverAssertKind(kind);
|
|
6703
|
+
const env = options.env ?? process.env;
|
|
6704
|
+
const override = env[PATHS_RESOLVER_KIND_ENV[kind]];
|
|
6705
|
+
if (typeof override === "string" && override.length > 0)
|
|
6706
|
+
return override;
|
|
6707
|
+
const home = options.home ?? pathsResolverHomedir();
|
|
6708
|
+
const platform = options.platform ?? process.platform;
|
|
6709
|
+
if (platform === "darwin") {
|
|
6710
|
+
switch (kind) {
|
|
6711
|
+
case "config":
|
|
6712
|
+
case "data":
|
|
6713
|
+
return pathsResolverJoin(home, "Library", "Application Support", "Hasna");
|
|
6714
|
+
case "cache":
|
|
6715
|
+
return pathsResolverJoin(home, "Library", "Caches", "Hasna");
|
|
6716
|
+
case "state":
|
|
6717
|
+
return pathsResolverJoin(home, "Library", "Logs", "Hasna");
|
|
6718
|
+
}
|
|
6719
|
+
}
|
|
6720
|
+
switch (kind) {
|
|
6721
|
+
case "config":
|
|
6722
|
+
return pathsResolverJoin(home, ".config", "hasna");
|
|
6723
|
+
case "data":
|
|
6724
|
+
return pathsResolverJoin(home, ".local", "share", "hasna");
|
|
6725
|
+
case "state":
|
|
6726
|
+
return pathsResolverJoin(home, ".local", "state", "hasna");
|
|
6727
|
+
case "cache":
|
|
6728
|
+
return pathsResolverJoin(home, ".cache", "hasna");
|
|
6729
|
+
}
|
|
6730
|
+
}
|
|
6731
|
+
function pathsResolverResolve(kind, options) {
|
|
6732
|
+
pathsResolverAssertApp(options.app);
|
|
6733
|
+
const appSegment = options.internal === true ? pathsResolverJoin("internal", options.app) : options.app;
|
|
6734
|
+
return pathsResolverJoin(pathsResolverBaseDir(kind, options), appSegment);
|
|
6735
|
+
}
|
|
6736
|
+
function dataDir(options) {
|
|
6737
|
+
return pathsResolverResolve("data", options);
|
|
6738
|
+
}
|
|
6739
|
+
function effectiveHome() {
|
|
6740
|
+
return process.env["HOME"] || process.env["USERPROFILE"] || homedir() || "/tmp";
|
|
6741
|
+
}
|
|
6742
|
+
function legacyDataRoot() {
|
|
6743
|
+
return join(effectiveHome(), ".hasna", "skills");
|
|
6744
|
+
}
|
|
6745
|
+
function resolverDataRoot(home = effectiveHome(), env) {
|
|
6746
|
+
return dataDir({ app: "skills", home, env });
|
|
6747
|
+
}
|
|
6748
|
+
function adoptResolverDataRoot(resolved, env = process.env) {
|
|
6749
|
+
const dataOverride = env.HASNA_DATA_HOME;
|
|
6750
|
+
if (typeof dataOverride === "string" && dataOverride.trim().length > 0)
|
|
6751
|
+
return true;
|
|
6752
|
+
return existsSync(join(resolved, DEFAULT_SQLITE_FILENAME)) || existsSync(join(resolved, GLOBAL_CONFIG_FILENAME));
|
|
6753
|
+
}
|
|
6754
|
+
function exactDataRoot() {
|
|
6755
|
+
for (const key of [DATA_DIR_ENV, HASNA_SKILLS_HOME_ENV, SKILLS_HOME_ENV]) {
|
|
6756
|
+
const dir = process.env[key]?.trim();
|
|
6757
|
+
if (dir)
|
|
6758
|
+
return resolve(dir);
|
|
6759
|
+
}
|
|
6760
|
+
return;
|
|
6761
|
+
}
|
|
6762
|
+
function hasExactOverride(env = process.env) {
|
|
6763
|
+
return Boolean(env[DATA_DIR_ENV]?.trim()) || Boolean(env[HASNA_SKILLS_HOME_ENV]?.trim()) || Boolean(env[SKILLS_HOME_ENV]?.trim());
|
|
6764
|
+
}
|
|
6765
|
+
function hasOperatorOverride(env = process.env) {
|
|
6766
|
+
return hasExactOverride(env) || Boolean(env.HASNA_DATA_HOME?.trim());
|
|
6767
|
+
}
|
|
6768
|
+
function getDataRoot() {
|
|
6769
|
+
const exact = exactDataRoot();
|
|
6770
|
+
if (exact)
|
|
6771
|
+
return exact;
|
|
6772
|
+
const resolved = resolverDataRoot();
|
|
6773
|
+
return adoptResolverDataRoot(resolved) ? resolve(resolved) : resolve(legacyDataRoot());
|
|
6774
|
+
}
|
|
6775
|
+
var PATHS_RESOLVER_KIND_ENV, PATHS_RESOLVER_APP_SLUG_RE, DATA_DIR_ENV = "HASNA_SKILLS_DIR", HASNA_SKILLS_HOME_ENV = "HASNA_SKILLS_HOME", SKILLS_HOME_ENV = "SKILLS_HOME", DEFAULT_SQLITE_FILENAME = "server.db", GLOBAL_CONFIG_FILENAME = "config.json";
|
|
6776
|
+
var init_app_home = __esm(() => {
|
|
6777
|
+
PATHS_RESOLVER_KIND_ENV = {
|
|
6778
|
+
config: "HASNA_CONFIG_HOME",
|
|
6779
|
+
data: "HASNA_DATA_HOME",
|
|
6780
|
+
state: "HASNA_STATE_HOME",
|
|
6781
|
+
cache: "HASNA_CACHE_HOME"
|
|
6782
|
+
};
|
|
6783
|
+
PATHS_RESOLVER_APP_SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
6784
|
+
});
|
|
6785
|
+
|
|
6786
|
+
// src/lib/config.ts
|
|
6787
|
+
import { existsSync as existsSync2, readFileSync, writeFileSync, mkdirSync, copyFileSync, readdirSync, statSync } from "fs";
|
|
6788
|
+
import { join as join2, dirname } from "path";
|
|
6789
|
+
import { homedir as homedir2 } from "os";
|
|
6686
6790
|
function validKeys() {
|
|
6687
6791
|
return [...Object.keys(ENUM_KEYS), ...STRING_KEYS];
|
|
6688
6792
|
}
|
|
@@ -6690,19 +6794,19 @@ function allowedValues(key) {
|
|
|
6690
6794
|
return ENUM_KEYS[key];
|
|
6691
6795
|
}
|
|
6692
6796
|
function mergeDirectoryContents(sourceDir, targetDir) {
|
|
6693
|
-
if (!
|
|
6797
|
+
if (!existsSync2(sourceDir))
|
|
6694
6798
|
return;
|
|
6695
6799
|
mkdirSync(targetDir, { recursive: true });
|
|
6696
6800
|
for (const entry of readdirSync(sourceDir)) {
|
|
6697
|
-
const sourcePath =
|
|
6698
|
-
const targetPath =
|
|
6801
|
+
const sourcePath = join2(sourceDir, entry);
|
|
6802
|
+
const targetPath = join2(targetDir, entry);
|
|
6699
6803
|
try {
|
|
6700
6804
|
const sourceStat = statSync(sourcePath);
|
|
6701
6805
|
if (sourceStat.isDirectory()) {
|
|
6702
6806
|
mergeDirectoryContents(sourcePath, targetPath);
|
|
6703
6807
|
continue;
|
|
6704
6808
|
}
|
|
6705
|
-
if (!
|
|
6809
|
+
if (!existsSync2(targetPath))
|
|
6706
6810
|
copyFileSync(sourcePath, targetPath);
|
|
6707
6811
|
} catch {}
|
|
6708
6812
|
}
|
|
@@ -6728,48 +6832,42 @@ function normalizeConfigValue(key, value) {
|
|
|
6728
6832
|
return;
|
|
6729
6833
|
}
|
|
6730
6834
|
function isOwnerLayoutMigrated(appDir) {
|
|
6731
|
-
return
|
|
6835
|
+
return existsSync2(join2(appDir, SKILLS_CACHE_DIRNAME, LAYOUT_MIGRATION_RECORD));
|
|
6732
6836
|
}
|
|
6733
6837
|
function getDataDir() {
|
|
6734
|
-
const
|
|
6735
|
-
|
|
6736
|
-
|
|
6737
|
-
|
|
6738
|
-
|
|
6739
|
-
return
|
|
6740
|
-
|
|
6741
|
-
const
|
|
6742
|
-
const
|
|
6743
|
-
const oldDir = join(home, ".skills");
|
|
6744
|
-
const oldConfigFile = join(home, ".skillsrc");
|
|
6745
|
-
mkdirSync(newDir, { recursive: true });
|
|
6838
|
+
const root = getDataRoot();
|
|
6839
|
+
try {
|
|
6840
|
+
mkdirSync(root, { recursive: true });
|
|
6841
|
+
} catch {}
|
|
6842
|
+
if (hasOperatorOverride())
|
|
6843
|
+
return root;
|
|
6844
|
+
const home = effectiveHome();
|
|
6845
|
+
const oldDir = join2(home, ".skills");
|
|
6846
|
+
const oldConfigFile = join2(home, ".skillsrc");
|
|
6746
6847
|
try {
|
|
6747
|
-
mergeDirectoryContents(oldDir,
|
|
6848
|
+
mergeDirectoryContents(oldDir, root);
|
|
6748
6849
|
} catch {}
|
|
6749
|
-
if (
|
|
6850
|
+
if (existsSync2(oldConfigFile) && !existsSync2(join2(root, "config.json"))) {
|
|
6750
6851
|
try {
|
|
6751
|
-
copyFileSync(oldConfigFile,
|
|
6852
|
+
copyFileSync(oldConfigFile, join2(root, "config.json"));
|
|
6752
6853
|
} catch {}
|
|
6753
6854
|
}
|
|
6754
|
-
return
|
|
6855
|
+
return root;
|
|
6755
6856
|
}
|
|
6756
6857
|
function getDataDirReadOnly() {
|
|
6757
|
-
|
|
6758
|
-
if (override)
|
|
6759
|
-
return override;
|
|
6760
|
-
return join(process.env["HOME"] || process.env["USERPROFILE"] || homedir(), ".hasna", "skills");
|
|
6858
|
+
return getDataRoot();
|
|
6761
6859
|
}
|
|
6762
6860
|
function getConfigPathReadOnly(scope) {
|
|
6763
6861
|
if (scope === "global")
|
|
6764
|
-
return
|
|
6765
|
-
return
|
|
6862
|
+
return join2(getDataDirReadOnly(), "config.json");
|
|
6863
|
+
return join2(process.cwd(), "skills.config.json");
|
|
6766
6864
|
}
|
|
6767
6865
|
function loadConfigReadOnly() {
|
|
6768
6866
|
const canonicalConfigPath = getConfigPathReadOnly("global");
|
|
6769
6867
|
let globalConfig2;
|
|
6770
|
-
if (
|
|
6868
|
+
if (existsSync2(canonicalConfigPath)) {
|
|
6771
6869
|
globalConfig2 = readConfigFile(canonicalConfigPath);
|
|
6772
|
-
} else if (
|
|
6870
|
+
} else if (hasOperatorOverride()) {
|
|
6773
6871
|
globalConfig2 = {};
|
|
6774
6872
|
} else {
|
|
6775
6873
|
globalConfig2 = readConfigFile(legacyConfigFilePath());
|
|
@@ -6778,16 +6876,16 @@ function loadConfigReadOnly() {
|
|
|
6778
6876
|
return { ...globalConfig2, ...projectConfig };
|
|
6779
6877
|
}
|
|
6780
6878
|
function legacyConfigFilePath() {
|
|
6781
|
-
return
|
|
6879
|
+
return join2(process.env["HOME"] || process.env["USERPROFILE"] || homedir2(), ".skillsrc");
|
|
6782
6880
|
}
|
|
6783
6881
|
function getConfigPath(scope) {
|
|
6784
6882
|
if (scope === "global") {
|
|
6785
|
-
return
|
|
6883
|
+
return join2(getDataDir(), "config.json");
|
|
6786
6884
|
}
|
|
6787
|
-
return
|
|
6885
|
+
return join2(process.cwd(), "skills.config.json");
|
|
6788
6886
|
}
|
|
6789
6887
|
function readConfigFile(path) {
|
|
6790
|
-
if (!
|
|
6888
|
+
if (!existsSync2(path))
|
|
6791
6889
|
return {};
|
|
6792
6890
|
let parsed;
|
|
6793
6891
|
try {
|
|
@@ -6811,9 +6909,11 @@ function loadConfig() {
|
|
|
6811
6909
|
const projectConfig = readConfigFile(getConfigPath("project"));
|
|
6812
6910
|
return { ...globalConfig2, ...projectConfig };
|
|
6813
6911
|
}
|
|
6814
|
-
var ENUM_KEYS, STRING_KEYS,
|
|
6912
|
+
var ENUM_KEYS, STRING_KEYS, INSTALLED_SKILLS_DIRNAME = "installed", SKILLS_CACHE_DIRNAME = "skills", LAYOUT_MIGRATION_RECORD = ".layout-migration.json";
|
|
6815
6913
|
var init_config = __esm(() => {
|
|
6816
6914
|
init_retired_settings();
|
|
6915
|
+
init_app_home();
|
|
6916
|
+
init_app_home();
|
|
6817
6917
|
ENUM_KEYS = {
|
|
6818
6918
|
defaultAgent: ["claude", "codex", "gemini", "pi", "opencode", "all"],
|
|
6819
6919
|
defaultScope: ["global", "project"],
|
|
@@ -6905,23 +7005,23 @@ __export(exports_auth_store, {
|
|
|
6905
7005
|
getApiKey: () => getApiKey,
|
|
6906
7006
|
clearAuthConfig: () => clearAuthConfig
|
|
6907
7007
|
});
|
|
6908
|
-
import { existsSync as
|
|
6909
|
-
import { dirname as dirname5, join as
|
|
6910
|
-
import { homedir as
|
|
7008
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync6, readFileSync as readFileSync11, writeFileSync as writeFileSync6, unlinkSync } from "fs";
|
|
7009
|
+
import { dirname as dirname5, join as join13 } from "path";
|
|
7010
|
+
import { homedir as homedir4 } from "os";
|
|
6911
7011
|
function getAuthFilePath() {
|
|
6912
|
-
return
|
|
7012
|
+
return join13(getDataDir(), "auth.json");
|
|
6913
7013
|
}
|
|
6914
7014
|
function getAuthFilePathReadOnly() {
|
|
6915
|
-
return
|
|
7015
|
+
return join13(getDataDirReadOnly(), "auth.json");
|
|
6916
7016
|
}
|
|
6917
7017
|
function legacyAuthFilePath() {
|
|
6918
|
-
return
|
|
7018
|
+
return join13(process.env["HOME"] || process.env["USERPROFILE"] || homedir4(), ".skills", "auth.json");
|
|
6919
7019
|
}
|
|
6920
7020
|
function getAuthConfig() {
|
|
6921
7021
|
if (cachedConfig !== undefined)
|
|
6922
7022
|
return cachedConfig;
|
|
6923
7023
|
try {
|
|
6924
|
-
const file =
|
|
7024
|
+
const file = existsSync13(getAuthFilePath()) ? getAuthFilePath() : legacyAuthFilePath();
|
|
6925
7025
|
const raw = readFileSync11(file, "utf-8");
|
|
6926
7026
|
const config2 = JSON.parse(raw);
|
|
6927
7027
|
if (!config2.apiKey) {
|
|
@@ -6960,7 +7060,7 @@ function getApiKey() {
|
|
|
6960
7060
|
}
|
|
6961
7061
|
function getAuthConfigReadOnly() {
|
|
6962
7062
|
try {
|
|
6963
|
-
const file =
|
|
7063
|
+
const file = existsSync13(getAuthFilePathReadOnly()) ? getAuthFilePathReadOnly() : legacyAuthFilePath();
|
|
6964
7064
|
const raw = readFileSync11(file, "utf-8");
|
|
6965
7065
|
const config2 = JSON.parse(raw);
|
|
6966
7066
|
if (!config2.apiKey)
|
|
@@ -7247,12 +7347,30 @@ class RemoteSkillsClient {
|
|
|
7247
7347
|
async downloadSkillBundle(slug) {
|
|
7248
7348
|
return this.request(`/api/v1/skills/${encodeURIComponent(slug)}/bundle`, { method: "GET" });
|
|
7249
7349
|
}
|
|
7250
|
-
async getBundle(slug) {
|
|
7251
|
-
const
|
|
7350
|
+
async getBundle(slug, version2) {
|
|
7351
|
+
const path = version2 ? `/api/v1/skills/${encodeURIComponent(slug)}/versions/${encodeURIComponent(version2)}/bundle` : `/api/v1/skills/${encodeURIComponent(slug)}/bundle`;
|
|
7352
|
+
const response = await this.request(path, { method: "GET" });
|
|
7252
7353
|
if (response.status === 404)
|
|
7253
7354
|
return null;
|
|
7254
7355
|
return response;
|
|
7255
7356
|
}
|
|
7357
|
+
async listSkillVersions(slug) {
|
|
7358
|
+
const response = await this.requestNewRoute(`/api/v1/skills/${encodeURIComponent(slug)}/versions`, undefined, { domainNotFoundCodes: ["SKILL_NOT_FOUND"] });
|
|
7359
|
+
if (response.status === 404)
|
|
7360
|
+
return [];
|
|
7361
|
+
if (!response.ok)
|
|
7362
|
+
throw new Error(`versions request failed: ${response.status}`);
|
|
7363
|
+
const body = await response.json();
|
|
7364
|
+
return Array.isArray(body.versions) ? body.versions : [];
|
|
7365
|
+
}
|
|
7366
|
+
async getSkillVersion(slug, version2) {
|
|
7367
|
+
const response = await this.requestNewRoute(`/api/v1/skills/${encodeURIComponent(slug)}/versions/${encodeURIComponent(version2)}`, undefined, { domainNotFoundCodes: ["SKILL_NOT_FOUND", "SKILL_VERSION_NOT_FOUND"] });
|
|
7368
|
+
if (response.status === 404)
|
|
7369
|
+
return null;
|
|
7370
|
+
if (!response.ok)
|
|
7371
|
+
throw new Error(`version request failed: ${response.status}`);
|
|
7372
|
+
return await response.json();
|
|
7373
|
+
}
|
|
7256
7374
|
async listPins() {
|
|
7257
7375
|
const response = await this.requestNewRoute("/api/v1/pins");
|
|
7258
7376
|
return normalizePinList(await response.json());
|
|
@@ -12943,7 +13061,7 @@ class StdioServerTransport {
|
|
|
12943
13061
|
// package.json
|
|
12944
13062
|
var package_default = {
|
|
12945
13063
|
name: "@hasna/skills",
|
|
12946
|
-
version: "0.
|
|
13064
|
+
version: "0.2.0",
|
|
12947
13065
|
description: "Skills library for AI coding agents",
|
|
12948
13066
|
type: "module",
|
|
12949
13067
|
bin: {
|
|
@@ -13023,7 +13141,7 @@ var package_default = {
|
|
|
13023
13141
|
author: "Hasna",
|
|
13024
13142
|
license: "Apache-2.0",
|
|
13025
13143
|
devDependencies: {
|
|
13026
|
-
"@types/bun": "
|
|
13144
|
+
"@types/bun": "1.3.14",
|
|
13027
13145
|
"@types/node": "25.2.3",
|
|
13028
13146
|
"@types/react": "^18.2.0",
|
|
13029
13147
|
"bun-types": "1.3.14",
|
|
@@ -20887,14 +21005,14 @@ var EMPTY_COMPLETION_RESULT = {
|
|
|
20887
21005
|
|
|
20888
21006
|
// src/lib/registry.ts
|
|
20889
21007
|
init_config();
|
|
20890
|
-
import { existsSync as
|
|
20891
|
-
import { join as
|
|
21008
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6, readdirSync as readdirSync6 } from "fs";
|
|
21009
|
+
import { join as join8 } from "path";
|
|
20892
21010
|
|
|
20893
21011
|
// src/lib/portable-skills.ts
|
|
20894
21012
|
init_config();
|
|
20895
21013
|
import {
|
|
20896
21014
|
cpSync as cpSync2,
|
|
20897
|
-
existsSync as
|
|
21015
|
+
existsSync as existsSync7,
|
|
20898
21016
|
mkdirSync as mkdirSync3,
|
|
20899
21017
|
mkdtempSync,
|
|
20900
21018
|
readdirSync as readdirSync5,
|
|
@@ -20903,7 +21021,7 @@ import {
|
|
|
20903
21021
|
statSync as statSync6,
|
|
20904
21022
|
writeFileSync as writeFileSync3
|
|
20905
21023
|
} from "fs";
|
|
20906
|
-
import { basename as basename2, dirname as dirname3, isAbsolute as isAbsolute2, join as
|
|
21024
|
+
import { basename as basename2, dirname as dirname3, isAbsolute as isAbsolute2, join as join7, normalize as normalize2 } from "path";
|
|
20907
21025
|
|
|
20908
21026
|
// src/lib/registry-data/development-tools.ts
|
|
20909
21027
|
var DEVELOPMENT_TOOLS_SKILLS = [
|
|
@@ -21117,14 +21235,6 @@ var DEVELOPMENT_TOOLS_SKILLS = [
|
|
|
21117
21235
|
category: "Development Tools",
|
|
21118
21236
|
tags: ["storage", "backend", "postgresql", "sqlite", "two-backend", "oss-app"],
|
|
21119
21237
|
kind: "instruction"
|
|
21120
|
-
},
|
|
21121
|
-
{
|
|
21122
|
-
name: "session-inject-monitor",
|
|
21123
|
-
displayName: "Session Inject Monitor",
|
|
21124
|
-
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",
|
|
21125
|
-
category: "Development Tools",
|
|
21126
|
-
tags: ["monitor", "session", "injection", "automation", "wake"],
|
|
21127
|
-
kind: "instruction"
|
|
21128
21238
|
}
|
|
21129
21239
|
];
|
|
21130
21240
|
|
|
@@ -21623,8 +21733,8 @@ var SKILLS = [
|
|
|
21623
21733
|
];
|
|
21624
21734
|
|
|
21625
21735
|
// src/lib/hosted-skill-set.ts
|
|
21626
|
-
import { existsSync as
|
|
21627
|
-
import { join as
|
|
21736
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
21737
|
+
import { join as join3 } from "path";
|
|
21628
21738
|
var HOSTED_RUNTIMES = new Set(["hosted"]);
|
|
21629
21739
|
var HOSTED_SOURCES = new Set(["remote", "private-hosted"]);
|
|
21630
21740
|
function normalizeMarker(value) {
|
|
@@ -21637,8 +21747,8 @@ function isHostedMetadataPackage(pkg) {
|
|
|
21637
21747
|
return HOSTED_RUNTIMES.has(normalizeMarker(skills.runtime)) || HOSTED_SOURCES.has(normalizeMarker(skills.source));
|
|
21638
21748
|
}
|
|
21639
21749
|
function isHostedMetadataSkillDir(skillDir) {
|
|
21640
|
-
const pkgPath =
|
|
21641
|
-
if (!
|
|
21750
|
+
const pkgPath = join3(skillDir, "package.json");
|
|
21751
|
+
if (!existsSync3(pkgPath))
|
|
21642
21752
|
return false;
|
|
21643
21753
|
try {
|
|
21644
21754
|
return isHostedMetadataPackage(JSON.parse(readFileSync2(pkgPath, "utf8")));
|
|
@@ -21662,8 +21772,8 @@ var BRACE_SOURCE_EXCLUSION = new RegExp(`^!skills/\\{(${SLUG}(?:,${SLUG})+)\\}/s
|
|
|
21662
21772
|
var SINGLE_SOURCE_EXCLUSION = new RegExp(`^!skills/(${SLUG})/src$`);
|
|
21663
21773
|
|
|
21664
21774
|
// src/lib/skill-validation.ts
|
|
21665
|
-
import { existsSync as
|
|
21666
|
-
import { isAbsolute, join as
|
|
21775
|
+
import { existsSync as existsSync4, lstatSync, readFileSync as readFileSync3, readdirSync as readdirSync3, statSync as statSync3 } from "fs";
|
|
21776
|
+
import { isAbsolute, join as join4, normalize } from "path";
|
|
21667
21777
|
var VALID_SKILL_KINDS = ["executable", "instruction"];
|
|
21668
21778
|
var DOC_FILES = ["SKILL.md", "README.md", "CLAUDE.md"];
|
|
21669
21779
|
var RESERVED_SKILL_ENTRIES = new Set([
|
|
@@ -21801,7 +21911,7 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
21801
21911
|
binCommands: [],
|
|
21802
21912
|
docFiles: []
|
|
21803
21913
|
};
|
|
21804
|
-
if (!
|
|
21914
|
+
if (!existsSync4(skillPath)) {
|
|
21805
21915
|
add(issues, "skill.dir_missing", `Skill directory not found: ${skillPath}`);
|
|
21806
21916
|
return {
|
|
21807
21917
|
name: bareName,
|
|
@@ -21816,7 +21926,7 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
21816
21926
|
add(issues, "skill.name_invalid", `Skill name '${bareName}' must use lowercase letters, numbers, dots, underscores, or hyphens`);
|
|
21817
21927
|
}
|
|
21818
21928
|
for (const entry of readdirSync3(skillPath).sort()) {
|
|
21819
|
-
const entryPath =
|
|
21929
|
+
const entryPath = join4(skillPath, entry);
|
|
21820
21930
|
if (RESERVED_SKILL_ENTRIES.has(entry)) {
|
|
21821
21931
|
add(issues, "skill.reserved_file", `Reserved file '${entry}' is not allowed in skill packages`);
|
|
21822
21932
|
}
|
|
@@ -21828,14 +21938,14 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
21828
21938
|
}
|
|
21829
21939
|
}
|
|
21830
21940
|
for (const docFile of DOC_FILES) {
|
|
21831
|
-
if (
|
|
21941
|
+
if (existsSync4(join4(skillPath, docFile)))
|
|
21832
21942
|
metadata.docFiles.push(docFile);
|
|
21833
21943
|
}
|
|
21834
21944
|
if (metadata.docFiles.length === 0) {
|
|
21835
21945
|
add(issues, "skill.docs_missing", "Missing documentation file: expected SKILL.md, README.md, or CLAUDE.md");
|
|
21836
21946
|
}
|
|
21837
|
-
const skillMdPath =
|
|
21838
|
-
if (
|
|
21947
|
+
const skillMdPath = join4(skillPath, "SKILL.md");
|
|
21948
|
+
if (existsSync4(skillMdPath)) {
|
|
21839
21949
|
const frontmatter = parseSkillFrontmatter(readFileSync3(skillMdPath, "utf-8"));
|
|
21840
21950
|
if (!frontmatter) {
|
|
21841
21951
|
add(warnings, "skill.frontmatter_missing", "SKILL.md has no YAML frontmatter");
|
|
@@ -21878,8 +21988,8 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
21878
21988
|
}
|
|
21879
21989
|
metadata.kind = resolvedKind;
|
|
21880
21990
|
const isInstruction = resolvedKind === "instruction";
|
|
21881
|
-
const pkgPath =
|
|
21882
|
-
if (!
|
|
21991
|
+
const pkgPath = join4(skillPath, "package.json");
|
|
21992
|
+
if (!existsSync4(pkgPath)) {
|
|
21883
21993
|
if (!isInstruction)
|
|
21884
21994
|
add(issues, "package.missing", "Missing package.json");
|
|
21885
21995
|
} else {
|
|
@@ -21937,8 +22047,8 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
21937
22047
|
add(issues, "package.bin_target_unsafe", `package.json bin '${command}' target '${target}' must stay inside the skill directory`);
|
|
21938
22048
|
continue;
|
|
21939
22049
|
}
|
|
21940
|
-
const targetPath =
|
|
21941
|
-
if (!
|
|
22050
|
+
const targetPath = join4(skillPath, target);
|
|
22051
|
+
if (!existsSync4(targetPath)) {
|
|
21942
22052
|
add(warnings, "package.bin_target_missing", `package.json bin '${command}' target '${target}' is not present before build`);
|
|
21943
22053
|
} else if (statSync3(targetPath).isDirectory()) {
|
|
21944
22054
|
add(issues, "package.bin_target_directory", `package.json bin '${command}' target '${target}' must point to a file, not a directory`);
|
|
@@ -21955,17 +22065,17 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
21955
22065
|
metadata.runtime = "none";
|
|
21956
22066
|
} else {
|
|
21957
22067
|
metadata.runtime = hostedMetadata ? "hosted" : "local";
|
|
21958
|
-
const srcDir =
|
|
22068
|
+
const srcDir = join4(skillPath, "src");
|
|
21959
22069
|
if (hostedMetadata) {
|
|
21960
|
-
if (
|
|
22070
|
+
if (existsSync4(srcDir)) {
|
|
21961
22071
|
add(issues, "skill.hosted_source_forbidden", "Hosted metadata skills must not include local implementation source");
|
|
21962
22072
|
}
|
|
21963
|
-
} else if (!
|
|
22073
|
+
} else if (!existsSync4(srcDir)) {
|
|
21964
22074
|
add(issues, "skill.src_missing", "Missing src/ directory");
|
|
21965
|
-
} else if (!
|
|
22075
|
+
} else if (!existsSync4(join4(srcDir, "index.ts")) && !existsSync4(join4(srcDir, "index.js"))) {
|
|
21966
22076
|
add(issues, "skill.src_index_missing", "Missing src/index.ts or src/index.js");
|
|
21967
22077
|
} else {
|
|
21968
|
-
const indexPath =
|
|
22078
|
+
const indexPath = existsSync4(join4(srcDir, "index.ts")) ? join4(srcDir, "index.ts") : join4(srcDir, "index.js");
|
|
21969
22079
|
const size = statSync3(indexPath).size;
|
|
21970
22080
|
if (size < 50)
|
|
21971
22081
|
add(warnings, "skill.src_index_minimal", `Source entry point is very small (${size}B)`);
|
|
@@ -21983,8 +22093,8 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
21983
22093
|
|
|
21984
22094
|
// src/lib/skill-hash.ts
|
|
21985
22095
|
import { createHash } from "crypto";
|
|
21986
|
-
import { existsSync as
|
|
21987
|
-
import { join as
|
|
22096
|
+
import { existsSync as existsSync5, readdirSync as readdirSync4, readFileSync as readFileSync4, statSync as statSync4 } from "fs";
|
|
22097
|
+
import { join as join5, sep } from "path";
|
|
21988
22098
|
var CONTENT_HASH_ALGORITHM = "sha256";
|
|
21989
22099
|
var HASH_EXCLUDE_DIRS = new Set([".git", "node_modules", "dist", "build", ".turbo"]);
|
|
21990
22100
|
var HASH_COVERAGE = [
|
|
@@ -22043,8 +22153,8 @@ function collectBundleFiles(skillPath) {
|
|
|
22043
22153
|
if (seen.has(entry))
|
|
22044
22154
|
continue;
|
|
22045
22155
|
seen.add(entry);
|
|
22046
|
-
const absolute =
|
|
22047
|
-
if (!
|
|
22156
|
+
const absolute = join5(skillPath, entry);
|
|
22157
|
+
if (!existsSync5(absolute))
|
|
22048
22158
|
continue;
|
|
22049
22159
|
if (statSync4(absolute).isDirectory())
|
|
22050
22160
|
collectDirectory(files, absolute, entry);
|
|
@@ -22057,7 +22167,7 @@ function collectDirectory(files, dir, rel) {
|
|
|
22057
22167
|
for (const entry of readdirSync4(dir).sort()) {
|
|
22058
22168
|
if (entry.startsWith("."))
|
|
22059
22169
|
continue;
|
|
22060
|
-
const absolute =
|
|
22170
|
+
const absolute = join5(dir, entry);
|
|
22061
22171
|
const childRel = `${rel}/${entry}`;
|
|
22062
22172
|
let stats;
|
|
22063
22173
|
try {
|
|
@@ -22277,14 +22387,14 @@ function validateRuntimeContract(manifest, issues, strict) {
|
|
|
22277
22387
|
// src/lib/portable-skills-files.ts
|
|
22278
22388
|
import {
|
|
22279
22389
|
cpSync,
|
|
22280
|
-
existsSync as
|
|
22390
|
+
existsSync as existsSync6,
|
|
22281
22391
|
lstatSync as lstatSync2,
|
|
22282
22392
|
mkdirSync as mkdirSync2,
|
|
22283
22393
|
readFileSync as readFileSync5,
|
|
22284
22394
|
realpathSync,
|
|
22285
22395
|
writeFileSync as writeFileSync2
|
|
22286
22396
|
} from "fs";
|
|
22287
|
-
import { basename, dirname as dirname2, join as
|
|
22397
|
+
import { basename, dirname as dirname2, join as join6, relative } from "path";
|
|
22288
22398
|
var ANY_SEGMENT_COPY_EXCLUDES = new Set([
|
|
22289
22399
|
".git",
|
|
22290
22400
|
".DS_Store",
|
|
@@ -22327,12 +22437,12 @@ function normalizePortableSkillName(name) {
|
|
|
22327
22437
|
return normalized;
|
|
22328
22438
|
}
|
|
22329
22439
|
function readPortableSkillManifest(skillPath, fallbackName = basename(skillPath)) {
|
|
22330
|
-
const skillJsonPath =
|
|
22331
|
-
const skillMdPath =
|
|
22332
|
-
const pkgPath =
|
|
22333
|
-
const jsonManifest =
|
|
22334
|
-
const frontmatter =
|
|
22335
|
-
const pkg =
|
|
22440
|
+
const skillJsonPath = join6(skillPath, "skill.json");
|
|
22441
|
+
const skillMdPath = join6(skillPath, "SKILL.md");
|
|
22442
|
+
const pkgPath = join6(skillPath, "package.json");
|
|
22443
|
+
const jsonManifest = existsSync6(skillJsonPath) ? readJsonObject(skillJsonPath) : undefined;
|
|
22444
|
+
const frontmatter = existsSync6(skillMdPath) ? parseSkillFrontmatter(readFileSync5(skillMdPath, "utf-8")) ?? undefined : undefined;
|
|
22445
|
+
const pkg = existsSync6(pkgPath) ? readJsonObject(pkgPath) : undefined;
|
|
22336
22446
|
const name = normalizePortableSkillName(stringField(jsonManifest, "name") ?? frontmatter?.name ?? stringValue(pkg?.name) ?? fallbackName);
|
|
22337
22447
|
const description = stringField(jsonManifest, "description") ?? frontmatter?.description ?? stringValue(pkg?.description) ?? `${name} skill`;
|
|
22338
22448
|
const version2 = stringField(jsonManifest, "version") ?? frontmatter?.version ?? stringValue(pkg?.version) ?? PORTABLE_SKILL_DEFAULT_VERSION;
|
|
@@ -22378,7 +22488,7 @@ function createInstructionManifest(name, options) {
|
|
|
22378
22488
|
}
|
|
22379
22489
|
function writeInstructionSkillTemplate(skillPath, manifest) {
|
|
22380
22490
|
mkdirSync2(skillPath, { recursive: true });
|
|
22381
|
-
writeFileSync2(
|
|
22491
|
+
writeFileSync2(join6(skillPath, "SKILL.md"), renderInstructionSkillMd(manifest));
|
|
22382
22492
|
writeSkillJsonWithHash(skillPath, manifest);
|
|
22383
22493
|
}
|
|
22384
22494
|
function renderInstructionSkillMd(manifest) {
|
|
@@ -22427,12 +22537,12 @@ function createPortableManifest(name, options) {
|
|
|
22427
22537
|
};
|
|
22428
22538
|
}
|
|
22429
22539
|
function writePortableSkillTemplate(skillPath, manifest) {
|
|
22430
|
-
mkdirSync2(
|
|
22431
|
-
writeFileSync2(
|
|
22432
|
-
writeFileSync2(
|
|
22433
|
-
writeFileSync2(
|
|
22434
|
-
writeFileSync2(
|
|
22435
|
-
writeFileSync2(
|
|
22540
|
+
mkdirSync2(join6(skillPath, "src"), { recursive: true });
|
|
22541
|
+
writeFileSync2(join6(skillPath, "SKILL.md"), renderSkillMd(manifest));
|
|
22542
|
+
writeFileSync2(join6(skillPath, "AGENTS.md"), renderAgentsMd(manifest));
|
|
22543
|
+
writeFileSync2(join6(skillPath, "package.json"), renderPackageJson(manifest));
|
|
22544
|
+
writeFileSync2(join6(skillPath, "tsconfig.json"), renderTsconfig());
|
|
22545
|
+
writeFileSync2(join6(skillPath, "src", "index.ts"), renderEntrypoint(manifest));
|
|
22436
22546
|
writeSkillJsonWithHash(skillPath, manifest);
|
|
22437
22547
|
}
|
|
22438
22548
|
function fillContractDefaults(manifest, entrypoint) {
|
|
@@ -22457,7 +22567,7 @@ function writeSkillJsonWithHash(skillPath, manifest) {
|
|
|
22457
22567
|
content_hash: undefined
|
|
22458
22568
|
}
|
|
22459
22569
|
};
|
|
22460
|
-
writeFileSync2(
|
|
22570
|
+
writeFileSync2(join6(skillPath, "skill.json"), `${JSON.stringify({ ...existing, ...renderSkillJsonObject(withoutHash) }, null, 2)}
|
|
22461
22571
|
`);
|
|
22462
22572
|
const hash = computeContentHash(skillPath);
|
|
22463
22573
|
const withHash = {
|
|
@@ -22467,13 +22577,13 @@ function writeSkillJsonWithHash(skillPath, manifest) {
|
|
|
22467
22577
|
content_hash: hash
|
|
22468
22578
|
}
|
|
22469
22579
|
};
|
|
22470
|
-
writeFileSync2(
|
|
22580
|
+
writeFileSync2(join6(skillPath, "skill.json"), `${JSON.stringify({ ...existing, ...renderSkillJsonObject(withHash) }, null, 2)}
|
|
22471
22581
|
`);
|
|
22472
22582
|
return withHash;
|
|
22473
22583
|
}
|
|
22474
22584
|
function readExistingSkillJson(skillPath) {
|
|
22475
|
-
const path =
|
|
22476
|
-
if (!
|
|
22585
|
+
const path = join6(skillPath, "skill.json");
|
|
22586
|
+
if (!existsSync6(path))
|
|
22477
22587
|
return {};
|
|
22478
22588
|
try {
|
|
22479
22589
|
const parsed = JSON.parse(readFileSync5(path, "utf-8"));
|
|
@@ -22506,28 +22616,28 @@ function ensurePortableSkillFiles(skillPath, manifest) {
|
|
|
22506
22616
|
tags: next.tags?.length ? next.tags : ["custom", next.name]
|
|
22507
22617
|
};
|
|
22508
22618
|
const entry = next.commands[0]?.entry ?? "src/index.ts";
|
|
22509
|
-
if (entry && !
|
|
22510
|
-
mkdirSync2(dirname2(
|
|
22511
|
-
writeFileSync2(
|
|
22619
|
+
if (entry && !existsSync6(join6(skillPath, entry))) {
|
|
22620
|
+
mkdirSync2(dirname2(join6(skillPath, entry)), { recursive: true });
|
|
22621
|
+
writeFileSync2(join6(skillPath, entry), renderEntrypoint(next));
|
|
22512
22622
|
}
|
|
22513
|
-
if (!
|
|
22514
|
-
writeFileSync2(
|
|
22623
|
+
if (!existsSync6(join6(skillPath, "SKILL.md")))
|
|
22624
|
+
writeFileSync2(join6(skillPath, "SKILL.md"), renderSkillMd(next));
|
|
22515
22625
|
else
|
|
22516
|
-
writeFileSync2(
|
|
22517
|
-
if (!
|
|
22518
|
-
writeFileSync2(
|
|
22626
|
+
writeFileSync2(join6(skillPath, "SKILL.md"), ensureSkillMdFrontmatter(readFileSync5(join6(skillPath, "SKILL.md"), "utf-8"), next));
|
|
22627
|
+
if (!existsSync6(join6(skillPath, "AGENTS.md")))
|
|
22628
|
+
writeFileSync2(join6(skillPath, "AGENTS.md"), renderAgentsMd(next));
|
|
22519
22629
|
ensurePackageJson(skillPath, next);
|
|
22520
|
-
if (!
|
|
22521
|
-
writeFileSync2(
|
|
22630
|
+
if (!existsSync6(join6(skillPath, "tsconfig.json")))
|
|
22631
|
+
writeFileSync2(join6(skillPath, "tsconfig.json"), renderTsconfig());
|
|
22522
22632
|
writeSkillJsonWithHash(skillPath, next);
|
|
22523
22633
|
return readPortableSkillManifest(skillPath, next.name);
|
|
22524
22634
|
}
|
|
22525
22635
|
function ensurePackageJson(skillPath, manifest) {
|
|
22526
|
-
const pkgPath =
|
|
22636
|
+
const pkgPath = join6(skillPath, "package.json");
|
|
22527
22637
|
const first = manifest.commands[0] ?? { name: manifest.name, entry: "src/index.ts" };
|
|
22528
22638
|
const commandName = normalizePortableSkillName(first.name || manifest.name);
|
|
22529
22639
|
const entry = (first.entry ?? "src/index.ts").replace(/^\.\//, "");
|
|
22530
|
-
if (!
|
|
22640
|
+
if (!existsSync6(pkgPath)) {
|
|
22531
22641
|
writeFileSync2(pkgPath, renderPackageJson(manifest));
|
|
22532
22642
|
return;
|
|
22533
22643
|
}
|
|
@@ -22570,8 +22680,8 @@ function ensureInstructionSkillFiles(skillPath, manifest) {
|
|
|
22570
22680
|
inputs: [],
|
|
22571
22681
|
commands: []
|
|
22572
22682
|
};
|
|
22573
|
-
if (!
|
|
22574
|
-
writeFileSync2(
|
|
22683
|
+
if (!existsSync6(join6(skillPath, "SKILL.md"))) {
|
|
22684
|
+
writeFileSync2(join6(skillPath, "SKILL.md"), renderSkillMd(next));
|
|
22575
22685
|
}
|
|
22576
22686
|
writeSkillJsonWithHash(skillPath, next);
|
|
22577
22687
|
return readPortableSkillManifest(skillPath, next.name);
|
|
@@ -22854,18 +22964,18 @@ var LEGACY_CUSTOM_DIRNAME = "custom";
|
|
|
22854
22964
|
function getPortableSkillsRoot(options = {}) {
|
|
22855
22965
|
if (options.rootDir)
|
|
22856
22966
|
return options.rootDir;
|
|
22857
|
-
const appDir = options.homeDir ?
|
|
22858
|
-
const cache =
|
|
22967
|
+
const appDir = options.homeDir ? join7(options.homeDir, ".hasna", "skills") : getDataDir();
|
|
22968
|
+
const cache = join7(appDir, SKILLS_CACHE_DIRNAME);
|
|
22859
22969
|
if (isOwnerLayoutMigrated(appDir) && safeIsDirectory(cache))
|
|
22860
22970
|
return cache;
|
|
22861
|
-
const installed =
|
|
22971
|
+
const installed = join7(appDir, INSTALLED_SKILLS_DIRNAME);
|
|
22862
22972
|
migrateLegacySkillLayout(appDir, installed);
|
|
22863
22973
|
return installed;
|
|
22864
22974
|
}
|
|
22865
22975
|
function looksLikeSkillDirectory(path) {
|
|
22866
22976
|
if (!safeIsDirectory(path))
|
|
22867
22977
|
return false;
|
|
22868
|
-
return
|
|
22978
|
+
return existsSync7(join7(path, "SKILL.md")) || existsSync7(join7(path, "skill.json")) || existsSync7(join7(path, "package.json"));
|
|
22869
22979
|
}
|
|
22870
22980
|
function migrateLegacySkillLayout(appDir, installed) {
|
|
22871
22981
|
if (!safeIsDirectory(appDir))
|
|
@@ -22875,7 +22985,7 @@ function migrateLegacySkillLayout(appDir, installed) {
|
|
|
22875
22985
|
for (const entry of readdirSync5(appDir)) {
|
|
22876
22986
|
if (entry.startsWith(".") || entry === INSTALLED_SKILLS_DIRNAME)
|
|
22877
22987
|
continue;
|
|
22878
|
-
const path =
|
|
22988
|
+
const path = join7(appDir, entry);
|
|
22879
22989
|
if (entry === LEGACY_CUSTOM_DIRNAME) {
|
|
22880
22990
|
if (!safeIsDirectory(path))
|
|
22881
22991
|
continue;
|
|
@@ -22883,7 +22993,7 @@ function migrateLegacySkillLayout(appDir, installed) {
|
|
|
22883
22993
|
for (const nested of readdirSync5(path)) {
|
|
22884
22994
|
if (nested.startsWith("."))
|
|
22885
22995
|
continue;
|
|
22886
|
-
const nestedPath =
|
|
22996
|
+
const nestedPath = join7(path, nested);
|
|
22887
22997
|
if (looksLikeSkillDirectory(nestedPath))
|
|
22888
22998
|
candidates.push({ from: nestedPath, name: nested });
|
|
22889
22999
|
}
|
|
@@ -22897,10 +23007,10 @@ function migrateLegacySkillLayout(appDir, installed) {
|
|
|
22897
23007
|
return;
|
|
22898
23008
|
}
|
|
22899
23009
|
for (const { from, name } of candidates) {
|
|
22900
|
-
const target =
|
|
22901
|
-
if (
|
|
23010
|
+
const target = join7(installed, name);
|
|
23011
|
+
if (existsSync7(target))
|
|
22902
23012
|
continue;
|
|
22903
|
-
const staging =
|
|
23013
|
+
const staging = join7(installed, `.migrating-${name}-${process.pid}`);
|
|
22904
23014
|
try {
|
|
22905
23015
|
rmSync(staging, { recursive: true, force: true });
|
|
22906
23016
|
cpSync2(from, staging, { recursive: true, errorOnExist: false });
|
|
@@ -22913,7 +23023,7 @@ function migrateLegacySkillLayout(appDir, installed) {
|
|
|
22913
23023
|
}
|
|
22914
23024
|
}
|
|
22915
23025
|
function getPortableSkillPath(name, options = {}) {
|
|
22916
|
-
return
|
|
23026
|
+
return join7(getPortableSkillsRoot(options), normalizePortableSkillName(name));
|
|
22917
23027
|
}
|
|
22918
23028
|
function findPortableSkill(name, options = {}) {
|
|
22919
23029
|
let normalized;
|
|
@@ -22923,7 +23033,7 @@ function findPortableSkill(name, options = {}) {
|
|
|
22923
23033
|
return null;
|
|
22924
23034
|
}
|
|
22925
23035
|
const path = getPortableSkillPath(normalized, options);
|
|
22926
|
-
if (!
|
|
23036
|
+
if (!existsSync7(path) || !statSync6(path).isDirectory())
|
|
22927
23037
|
return null;
|
|
22928
23038
|
try {
|
|
22929
23039
|
return summarizePortableSkill(path, normalized);
|
|
@@ -22939,7 +23049,7 @@ function listPortableSkills(options = {}) {
|
|
|
22939
23049
|
for (const entry of readdirSync5(root).sort()) {
|
|
22940
23050
|
if (entry.startsWith("."))
|
|
22941
23051
|
continue;
|
|
22942
|
-
const path =
|
|
23052
|
+
const path = join7(root, entry);
|
|
22943
23053
|
if (!safeIsDirectory(path))
|
|
22944
23054
|
continue;
|
|
22945
23055
|
try {
|
|
@@ -22973,8 +23083,8 @@ function isOfficialSkillName(name) {
|
|
|
22973
23083
|
function scaffoldPortableSkill(name, options = {}) {
|
|
22974
23084
|
const skillName = normalizePortableSkillName(name);
|
|
22975
23085
|
const root = getPortableSkillsRoot(options);
|
|
22976
|
-
const skillPath =
|
|
22977
|
-
if (
|
|
23086
|
+
const skillPath = join7(root, skillName);
|
|
23087
|
+
if (existsSync7(skillPath)) {
|
|
22978
23088
|
if (!options.overwrite)
|
|
22979
23089
|
throw new Error(`Skill '${skillName}' already exists at ${skillPath}`);
|
|
22980
23090
|
rmSync(skillPath, { recursive: true, force: true });
|
|
@@ -22992,7 +23102,7 @@ function scaffoldPortableSkill(name, options = {}) {
|
|
|
22992
23102
|
}
|
|
22993
23103
|
function portPortableSkill(sourcePath, options = {}) {
|
|
22994
23104
|
const absoluteSource = normalize2(sourcePath);
|
|
22995
|
-
if (!
|
|
23105
|
+
if (!existsSync7(absoluteSource) || !statSync6(absoluteSource).isDirectory()) {
|
|
22996
23106
|
throw new Error(`Skill source directory not found: ${sourcePath}`);
|
|
22997
23107
|
}
|
|
22998
23108
|
const inferred = readPortableSkillManifest(absoluteSource, basename2(absoluteSource));
|
|
@@ -23004,8 +23114,8 @@ function portPortableSkill(sourcePath, options = {}) {
|
|
|
23004
23114
|
throw new Error(`${via} Importing it would shadow the official '${skillName}'. ` + `Pass --name to choose a different name, or --allow-shadow to override deliberately.`);
|
|
23005
23115
|
}
|
|
23006
23116
|
const root = getPortableSkillsRoot(options);
|
|
23007
|
-
const destination =
|
|
23008
|
-
if (
|
|
23117
|
+
const destination = join7(root, skillName);
|
|
23118
|
+
if (existsSync7(destination)) {
|
|
23009
23119
|
if (!options.overwrite)
|
|
23010
23120
|
throw new Error(`Skill '${skillName}' already exists at ${destination}`);
|
|
23011
23121
|
rmSync(destination, { recursive: true, force: true });
|
|
@@ -23033,10 +23143,10 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
23033
23143
|
const issues = [...base.issues];
|
|
23034
23144
|
const warnings = [...base.warnings];
|
|
23035
23145
|
let manifest;
|
|
23036
|
-
if (
|
|
23037
|
-
const skillJsonPath =
|
|
23038
|
-
const skillMdPath =
|
|
23039
|
-
if (!
|
|
23146
|
+
if (existsSync7(skillPath)) {
|
|
23147
|
+
const skillJsonPath = join7(skillPath, "skill.json");
|
|
23148
|
+
const skillMdPath = join7(skillPath, "SKILL.md");
|
|
23149
|
+
if (!existsSync7(skillJsonPath) && !existsSync7(skillMdPath)) {
|
|
23040
23150
|
add3(issues, "portable.manifest_missing", "Missing portable manifest: expected SKILL.md frontmatter and/or skill.json");
|
|
23041
23151
|
}
|
|
23042
23152
|
try {
|
|
@@ -23055,7 +23165,7 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
23055
23165
|
add3(issues, "portable.version_missing", "Portable manifest missing version");
|
|
23056
23166
|
}
|
|
23057
23167
|
const contractIssues = validatePortableManifestContract(manifest, {
|
|
23058
|
-
strict:
|
|
23168
|
+
strict: existsSync7(join7(skillPath, "skill.json")),
|
|
23059
23169
|
skillPath
|
|
23060
23170
|
});
|
|
23061
23171
|
for (const issue2 of contractIssues)
|
|
@@ -23091,8 +23201,8 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
23091
23201
|
add3(issues, "portable.command_entry_unsafe", `Command '${command.name}' entry '${command.entry}' must stay inside the skill directory`);
|
|
23092
23202
|
continue;
|
|
23093
23203
|
}
|
|
23094
|
-
const entryPath =
|
|
23095
|
-
if (!
|
|
23204
|
+
const entryPath = join7(skillPath, command.entry);
|
|
23205
|
+
if (!existsSync7(entryPath))
|
|
23096
23206
|
add3(issues, "portable.command_entry_missing", `Command '${command.name}' entry '${command.entry}' is missing`);
|
|
23097
23207
|
else if (statSync6(entryPath).isDirectory())
|
|
23098
23208
|
add3(issues, "portable.command_entry_directory", `Command '${command.name}' entry '${command.entry}' must be a file`);
|
|
@@ -23102,7 +23212,7 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
23102
23212
|
} catch (error2) {
|
|
23103
23213
|
add3(issues, "portable.manifest_invalid", error2.message);
|
|
23104
23214
|
}
|
|
23105
|
-
if (manifest?.kind !== "instruction" && !
|
|
23215
|
+
if (manifest?.kind !== "instruction" && !existsSync7(join7(skillPath, "AGENTS.md"))) {
|
|
23106
23216
|
add3(issues, "portable.agents_missing", "Missing AGENTS.md with build-out instructions for coding agents");
|
|
23107
23217
|
}
|
|
23108
23218
|
}
|
|
@@ -23360,7 +23470,7 @@ function parseSkillMdFrontmatter(content) {
|
|
|
23360
23470
|
return Object.keys(result).length > 0 ? result : null;
|
|
23361
23471
|
}
|
|
23362
23472
|
function discoverSkillsInDir(dir, source = "custom") {
|
|
23363
|
-
if (!
|
|
23473
|
+
if (!existsSync8(dir))
|
|
23364
23474
|
return [];
|
|
23365
23475
|
const result = [];
|
|
23366
23476
|
try {
|
|
@@ -23368,8 +23478,8 @@ function discoverSkillsInDir(dir, source = "custom") {
|
|
|
23368
23478
|
for (const entry of entries) {
|
|
23369
23479
|
if (!entry.isDirectory())
|
|
23370
23480
|
continue;
|
|
23371
|
-
const skillMdPath =
|
|
23372
|
-
if (!
|
|
23481
|
+
const skillMdPath = join8(dir, entry.name, "SKILL.md");
|
|
23482
|
+
if (!existsSync8(skillMdPath))
|
|
23373
23483
|
continue;
|
|
23374
23484
|
let content;
|
|
23375
23485
|
try {
|
|
@@ -23388,7 +23498,7 @@ function discoverSkillsInDir(dir, source = "custom") {
|
|
|
23388
23498
|
category: fm.category || "Development Tools",
|
|
23389
23499
|
tags: fm.tags || [],
|
|
23390
23500
|
...fm.kind ? { kind: fm.kind } : {},
|
|
23391
|
-
...isHostedMetadataSkillDir(
|
|
23501
|
+
...isHostedMetadataSkillDir(join8(dir, entry.name)) ? { serverOwned: true } : {},
|
|
23392
23502
|
source
|
|
23393
23503
|
});
|
|
23394
23504
|
}
|
|
@@ -23397,16 +23507,16 @@ function discoverSkillsInDir(dir, source = "custom") {
|
|
|
23397
23507
|
}
|
|
23398
23508
|
function findExtensionSkillPath(name) {
|
|
23399
23509
|
const config2 = loadConfig();
|
|
23400
|
-
if (!config2.extensionsDir || !
|
|
23510
|
+
if (!config2.extensionsDir || !existsSync8(config2.extensionsDir))
|
|
23401
23511
|
return null;
|
|
23402
23512
|
try {
|
|
23403
23513
|
const entries = readdirSync6(config2.extensionsDir, { withFileTypes: true });
|
|
23404
23514
|
for (const entry of entries) {
|
|
23405
23515
|
if (!entry.isDirectory())
|
|
23406
23516
|
continue;
|
|
23407
|
-
const skillDir =
|
|
23408
|
-
const skillMdPath =
|
|
23409
|
-
if (!
|
|
23517
|
+
const skillDir = join8(config2.extensionsDir, entry.name);
|
|
23518
|
+
const skillMdPath = join8(skillDir, "SKILL.md");
|
|
23519
|
+
if (!existsSync8(skillMdPath))
|
|
23410
23520
|
continue;
|
|
23411
23521
|
let content;
|
|
23412
23522
|
try {
|
|
@@ -23438,12 +23548,12 @@ function loadRegistry(cwd) {
|
|
|
23438
23548
|
if (registryCache && registryCacheKey === rootKey && now - registryCacheTime < REGISTRY_CACHE_TTL) {
|
|
23439
23549
|
return registryCache;
|
|
23440
23550
|
}
|
|
23441
|
-
const
|
|
23551
|
+
const dataDir2 = getDataDir();
|
|
23442
23552
|
const config2 = loadConfig();
|
|
23443
23553
|
const official = SKILLS.map((s) => ({ ...s, source: "official" }));
|
|
23444
23554
|
const extensions = config2.extensionsDir ? discoverSkillsInDir(config2.extensionsDir, "extension") : [];
|
|
23445
23555
|
const portableCustom = listPortableSkillMetas();
|
|
23446
|
-
const legacyCustom = discoverSkillsInDir(
|
|
23556
|
+
const legacyCustom = discoverSkillsInDir(join8(dataDir2, "custom"));
|
|
23447
23557
|
const globalCustom = mergeCustomSkills([...legacyCustom, ...portableCustom]);
|
|
23448
23558
|
registryCache = mergeSkillRegistryLists(official, extensions, globalCustom);
|
|
23449
23559
|
registryCacheTime = now;
|
|
@@ -23479,9 +23589,9 @@ function mergeCustomSkills(skills) {
|
|
|
23479
23589
|
}
|
|
23480
23590
|
|
|
23481
23591
|
// src/lib/installer.ts
|
|
23482
|
-
import { existsSync as
|
|
23483
|
-
import { dirname as dirname4, join as
|
|
23484
|
-
import { homedir as
|
|
23592
|
+
import { existsSync as existsSync10, readFileSync as readFileSync8, rmSync as rmSync2 } from "fs";
|
|
23593
|
+
import { dirname as dirname4, join as join10 } from "path";
|
|
23594
|
+
import { homedir as homedir3 } from "os";
|
|
23485
23595
|
import { fileURLToPath } from "url";
|
|
23486
23596
|
|
|
23487
23597
|
// src/lib/home-migration.ts
|
|
@@ -23497,8 +23607,8 @@ function normalizeSkillName(name) {
|
|
|
23497
23607
|
init_config();
|
|
23498
23608
|
|
|
23499
23609
|
// src/lib/project-state.ts
|
|
23500
|
-
import { existsSync as
|
|
23501
|
-
import { join as
|
|
23610
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
|
|
23611
|
+
import { join as join9 } from "path";
|
|
23502
23612
|
var VALID_PIN_SOURCES = [
|
|
23503
23613
|
"official",
|
|
23504
23614
|
"custom",
|
|
@@ -23513,14 +23623,14 @@ var SKILLS_PROJECT_DIR = ".skills";
|
|
|
23513
23623
|
var PROJECT_CONFIG_FILE = "project.json";
|
|
23514
23624
|
var DEFAULT_EXPORT_DIR = ".skills/exports";
|
|
23515
23625
|
function getProjectStateDir(targetDir = process.cwd()) {
|
|
23516
|
-
return
|
|
23626
|
+
return join9(targetDir, SKILLS_PROJECT_DIR);
|
|
23517
23627
|
}
|
|
23518
23628
|
function getProjectConfigPath(targetDir = process.cwd()) {
|
|
23519
|
-
return
|
|
23629
|
+
return join9(getProjectStateDir(targetDir), PROJECT_CONFIG_FILE);
|
|
23520
23630
|
}
|
|
23521
23631
|
function loadProjectConfig(targetDir = process.cwd()) {
|
|
23522
23632
|
const path = getProjectConfigPath(targetDir);
|
|
23523
|
-
if (!
|
|
23633
|
+
if (!existsSync9(path))
|
|
23524
23634
|
return null;
|
|
23525
23635
|
try {
|
|
23526
23636
|
return normalizeProjectConfig(JSON.parse(readFileSync7(path, "utf-8")));
|
|
@@ -23622,12 +23732,12 @@ var __dirname2 = dirname4(fileURLToPath(import.meta.url));
|
|
|
23622
23732
|
function findSkillsDir() {
|
|
23623
23733
|
let dir = __dirname2;
|
|
23624
23734
|
for (let i = 0;i < 5; i++) {
|
|
23625
|
-
const candidate =
|
|
23626
|
-
if (
|
|
23735
|
+
const candidate = join10(dir, "skills");
|
|
23736
|
+
if (existsSync10(candidate) && !dir.includes(".skills"))
|
|
23627
23737
|
return candidate;
|
|
23628
23738
|
dir = dirname4(dir);
|
|
23629
23739
|
}
|
|
23630
|
-
return
|
|
23740
|
+
return join10(__dirname2, "..", "skills");
|
|
23631
23741
|
}
|
|
23632
23742
|
var SKILLS_DIR = findSkillsDir();
|
|
23633
23743
|
function getSkillPath(name) {
|
|
@@ -23635,13 +23745,13 @@ function getSkillPath(name) {
|
|
|
23635
23745
|
const portable = findPortableSkill(skillName);
|
|
23636
23746
|
if (portable)
|
|
23637
23747
|
return portable.path;
|
|
23638
|
-
const legacyCustomPath =
|
|
23639
|
-
if (
|
|
23748
|
+
const legacyCustomPath = join10(getDataDir(), "custom", skillName);
|
|
23749
|
+
if (existsSync10(legacyCustomPath))
|
|
23640
23750
|
return legacyCustomPath;
|
|
23641
23751
|
const extensionPath = findExtensionSkillPath(skillName);
|
|
23642
23752
|
if (extensionPath)
|
|
23643
23753
|
return extensionPath;
|
|
23644
|
-
return
|
|
23754
|
+
return join10(SKILLS_DIR, skillName);
|
|
23645
23755
|
}
|
|
23646
23756
|
function getCanonicalSkillName(name) {
|
|
23647
23757
|
return getSkill(name)?.name ?? resolveSkillAlias(normalizeSkillSlug(name));
|
|
@@ -23650,7 +23760,7 @@ function installSkill(name, options = {}) {
|
|
|
23650
23760
|
const { targetDir = process.cwd(), overwrite = false } = options;
|
|
23651
23761
|
const canonicalName = getCanonicalSkillName(name);
|
|
23652
23762
|
const skillName = normalizeSkillName(canonicalName);
|
|
23653
|
-
if (!
|
|
23763
|
+
if (!existsSync10(getSkillPath(name))) {
|
|
23654
23764
|
const knownOfficial = Boolean(getSkill(name));
|
|
23655
23765
|
return {
|
|
23656
23766
|
skill: canonicalName,
|
|
@@ -23705,11 +23815,11 @@ function getAgentSkillsDir(agent, scope = "global", projectDir) {
|
|
|
23705
23815
|
const base = projectDir || process.cwd();
|
|
23706
23816
|
switch (agent) {
|
|
23707
23817
|
case "pi":
|
|
23708
|
-
return scope === "project" ?
|
|
23818
|
+
return scope === "project" ? join10(base, ".pi", "skills") : join10(homedir3(), ".pi", "agent", "skills");
|
|
23709
23819
|
case "opencode":
|
|
23710
|
-
return scope === "project" ?
|
|
23820
|
+
return scope === "project" ? join10(base, ".opencode", "skills") : join10(homedir3(), ".config", "opencode", "skills");
|
|
23711
23821
|
default:
|
|
23712
|
-
return scope === "project" ?
|
|
23822
|
+
return scope === "project" ? join10(base, `.${agent}`, "skills") : join10(homedir3(), `.${agent}`, "skills");
|
|
23713
23823
|
}
|
|
23714
23824
|
}
|
|
23715
23825
|
function warnMissingDependencies(name, targetDir) {
|
|
@@ -23724,8 +23834,8 @@ function warnMissingDependencies(name, targetDir) {
|
|
|
23724
23834
|
}
|
|
23725
23835
|
}
|
|
23726
23836
|
function readBundledSkillVersion(name) {
|
|
23727
|
-
const pkgPath =
|
|
23728
|
-
if (!
|
|
23837
|
+
const pkgPath = join10(getSkillPath(name), "package.json");
|
|
23838
|
+
if (!existsSync10(pkgPath))
|
|
23729
23839
|
return "unknown";
|
|
23730
23840
|
try {
|
|
23731
23841
|
const pkg = JSON.parse(readFileSync8(pkgPath, "utf-8"));
|
|
@@ -23736,13 +23846,13 @@ function readBundledSkillVersion(name) {
|
|
|
23736
23846
|
}
|
|
23737
23847
|
|
|
23738
23848
|
// src/lib/skillinfo.ts
|
|
23739
|
-
import { existsSync as
|
|
23740
|
-
import { join as
|
|
23849
|
+
import { existsSync as existsSync11, readFileSync as readFileSync9 } from "fs";
|
|
23850
|
+
import { join as join11 } from "path";
|
|
23741
23851
|
function isInstructionSkillDir(skillPath, meta) {
|
|
23742
23852
|
if (meta?.kind === "instruction")
|
|
23743
23853
|
return true;
|
|
23744
|
-
const skillMdPath =
|
|
23745
|
-
if (!
|
|
23854
|
+
const skillMdPath = join11(skillPath, "SKILL.md");
|
|
23855
|
+
if (!existsSync11(skillMdPath))
|
|
23746
23856
|
return false;
|
|
23747
23857
|
try {
|
|
23748
23858
|
return parseSkillFrontmatter(readFileSync9(skillMdPath, "utf-8"))?.kind === "instruction";
|
|
@@ -23768,12 +23878,12 @@ var HOSTED_PROVIDER_ENV_PREFIXES = [
|
|
|
23768
23878
|
];
|
|
23769
23879
|
function getSkillDocs(name) {
|
|
23770
23880
|
const skillPath = getSkillPath(name);
|
|
23771
|
-
if (!
|
|
23881
|
+
if (!existsSync11(skillPath))
|
|
23772
23882
|
return null;
|
|
23773
23883
|
return {
|
|
23774
|
-
skillMd: readIfExists(
|
|
23775
|
-
readme: readIfExists(
|
|
23776
|
-
claudeMd: readIfExists(
|
|
23884
|
+
skillMd: readIfExists(join11(skillPath, "SKILL.md")),
|
|
23885
|
+
readme: readIfExists(join11(skillPath, "README.md")),
|
|
23886
|
+
claudeMd: readIfExists(join11(skillPath, "CLAUDE.md"))
|
|
23777
23887
|
};
|
|
23778
23888
|
}
|
|
23779
23889
|
function getSkillBestDoc(name) {
|
|
@@ -23784,11 +23894,11 @@ function getSkillBestDoc(name) {
|
|
|
23784
23894
|
}
|
|
23785
23895
|
function getSkillRequirements(name) {
|
|
23786
23896
|
const skillPath = getSkillPath(name);
|
|
23787
|
-
if (!
|
|
23897
|
+
if (!existsSync11(skillPath))
|
|
23788
23898
|
return null;
|
|
23789
23899
|
const texts = [];
|
|
23790
23900
|
for (const file of ["SKILL.md", "README.md", "CLAUDE.md", ".env.example", ".env.local.example"]) {
|
|
23791
|
-
const content = readIfExists(
|
|
23901
|
+
const content = readIfExists(join11(skillPath, file));
|
|
23792
23902
|
if (content)
|
|
23793
23903
|
texts.push(content);
|
|
23794
23904
|
}
|
|
@@ -23827,8 +23937,8 @@ function getSkillRequirements(name) {
|
|
|
23827
23937
|
const skillName = normalizeSkillName(name);
|
|
23828
23938
|
let cliCommand = `skills run ${skillName}`;
|
|
23829
23939
|
let dependencies = {};
|
|
23830
|
-
const pkgPath =
|
|
23831
|
-
if (
|
|
23940
|
+
const pkgPath = join11(skillPath, "package.json");
|
|
23941
|
+
if (existsSync11(pkgPath)) {
|
|
23832
23942
|
try {
|
|
23833
23943
|
const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
|
|
23834
23944
|
dependencies = pkg.dependencies || {};
|
|
@@ -23848,7 +23958,7 @@ async function runSkill(name, args, options = {}) {
|
|
|
23848
23958
|
const meta = getSkill(name);
|
|
23849
23959
|
const canonicalName = meta?.name ?? name;
|
|
23850
23960
|
const skillPath = getSkillPath(canonicalName);
|
|
23851
|
-
if (!
|
|
23961
|
+
if (!existsSync11(skillPath)) {
|
|
23852
23962
|
return { exitCode: 1, error: `Skill '${name}' not found` };
|
|
23853
23963
|
}
|
|
23854
23964
|
if (isInstructionSkillDir(skillPath, meta)) {
|
|
@@ -23857,8 +23967,8 @@ async function runSkill(name, args, options = {}) {
|
|
|
23857
23967
|
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'.`
|
|
23858
23968
|
};
|
|
23859
23969
|
}
|
|
23860
|
-
const pkgPath =
|
|
23861
|
-
if (!
|
|
23970
|
+
const pkgPath = join11(skillPath, "package.json");
|
|
23971
|
+
if (!existsSync11(pkgPath)) {
|
|
23862
23972
|
return { exitCode: 1, error: `No package.json in skill '${name}'` };
|
|
23863
23973
|
}
|
|
23864
23974
|
let entryPoint;
|
|
@@ -23877,12 +23987,12 @@ async function runSkill(name, args, options = {}) {
|
|
|
23877
23987
|
} catch {
|
|
23878
23988
|
return { exitCode: 1, error: `Failed to parse package.json for skill '${name}'` };
|
|
23879
23989
|
}
|
|
23880
|
-
const entryPath =
|
|
23881
|
-
if (!
|
|
23990
|
+
const entryPath = join11(skillPath, entryPoint);
|
|
23991
|
+
if (!existsSync11(entryPath)) {
|
|
23882
23992
|
return { exitCode: 1, error: `Entry point '${entryPoint}' not found in skill '${name}'` };
|
|
23883
23993
|
}
|
|
23884
|
-
const nodeModules =
|
|
23885
|
-
if (!
|
|
23994
|
+
const nodeModules = join11(skillPath, "node_modules");
|
|
23995
|
+
if (!existsSync11(nodeModules)) {
|
|
23886
23996
|
const install = Bun.spawn(["bun", "install", "--no-save"], {
|
|
23887
23997
|
cwd: skillPath,
|
|
23888
23998
|
stdout: "pipe",
|
|
@@ -23909,8 +24019,8 @@ async function runSkill(name, args, options = {}) {
|
|
|
23909
24019
|
return { exitCode };
|
|
23910
24020
|
}
|
|
23911
24021
|
function detectProjectSkills(cwd = process.cwd()) {
|
|
23912
|
-
const pkgPath =
|
|
23913
|
-
if (!
|
|
24022
|
+
const pkgPath = join11(cwd, "package.json");
|
|
24023
|
+
if (!existsSync11(pkgPath)) {
|
|
23914
24024
|
const alwaysRecommend = ["market-research-report", "repo-onboarding-report", "blog-article"];
|
|
23915
24025
|
const recommended2 = alwaysRecommend.map((name) => loadRegistry().find((s) => s.name === name)).filter((s) => s !== undefined);
|
|
23916
24026
|
return { detected: [], recommended: recommended2 };
|
|
@@ -24006,7 +24116,7 @@ function extractEnvVars(text) {
|
|
|
24006
24116
|
}
|
|
24007
24117
|
function readIfExists(path) {
|
|
24008
24118
|
try {
|
|
24009
|
-
if (
|
|
24119
|
+
if (existsSync11(path)) {
|
|
24010
24120
|
return readFileSync9(path, "utf-8");
|
|
24011
24121
|
}
|
|
24012
24122
|
} catch {}
|
|
@@ -25589,25 +25699,25 @@ function registerDiscoveryTools(server) {
|
|
|
25589
25699
|
}
|
|
25590
25700
|
|
|
25591
25701
|
// src/mcp/operation-tools.ts
|
|
25592
|
-
import { existsSync as
|
|
25593
|
-
import { join as
|
|
25702
|
+
import { existsSync as existsSync14, readdirSync as readdirSync8, statSync as statSync8 } from "fs";
|
|
25703
|
+
import { join as join14 } from "path";
|
|
25594
25704
|
|
|
25595
25705
|
// src/lib/run-state.ts
|
|
25596
25706
|
import { createHash as createHash2, randomBytes } from "crypto";
|
|
25597
|
-
import { existsSync as
|
|
25598
|
-
import { extname, join as
|
|
25707
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync5, readFileSync as readFileSync10, readdirSync as readdirSync7, statSync as statSync7, writeFileSync as writeFileSync5 } from "fs";
|
|
25708
|
+
import { extname, join as join12, relative as relative2 } from "path";
|
|
25599
25709
|
function createSkillRun(params, targetDir = process.cwd()) {
|
|
25600
25710
|
const now = new Date;
|
|
25601
25711
|
const id = createRunId(now);
|
|
25602
25712
|
const day = now.toISOString().slice(0, 10);
|
|
25603
25713
|
const skillName = normalizeSkillName(params.skill);
|
|
25604
25714
|
const root = getProjectStateDir(targetDir);
|
|
25605
|
-
const runDir =
|
|
25606
|
-
const logsDir =
|
|
25607
|
-
const exportDir =
|
|
25715
|
+
const runDir = join12(root, "runs", day, id);
|
|
25716
|
+
const logsDir = join12(runDir, "logs");
|
|
25717
|
+
const exportDir = join12(root, "exports", skillName, id);
|
|
25608
25718
|
mkdirSync5(logsDir, { recursive: true });
|
|
25609
25719
|
mkdirSync5(exportDir, { recursive: true });
|
|
25610
|
-
mkdirSync5(
|
|
25720
|
+
mkdirSync5(join12(root, "tmp"), { recursive: true });
|
|
25611
25721
|
const record3 = {
|
|
25612
25722
|
id,
|
|
25613
25723
|
skill: skillName,
|
|
@@ -25658,22 +25768,22 @@ function updateSkillRun(context, patch) {
|
|
|
25658
25768
|
return context.record;
|
|
25659
25769
|
}
|
|
25660
25770
|
function writeRunLogs(context, stdout = "", stderr = "") {
|
|
25661
|
-
writeFileSync5(
|
|
25662
|
-
writeFileSync5(
|
|
25771
|
+
writeFileSync5(join12(context.logsDir, "stdout.log"), stdout);
|
|
25772
|
+
writeFileSync5(join12(context.logsDir, "stderr.log"), stderr);
|
|
25663
25773
|
}
|
|
25664
25774
|
function appendRunEvent(context, event, data = {}) {
|
|
25665
25775
|
const line = JSON.stringify({ ts: new Date().toISOString(), event, ...data }) + `
|
|
25666
25776
|
`;
|
|
25667
|
-
const path =
|
|
25668
|
-
const previous =
|
|
25777
|
+
const path = join12(context.runDir, "events.ndjson");
|
|
25778
|
+
const previous = existsSync12(path) ? readFileSync10(path, "utf-8") : "";
|
|
25669
25779
|
writeFileSync5(path, previous + line);
|
|
25670
25780
|
}
|
|
25671
25781
|
function findSkillRun(runId, targetDir = process.cwd()) {
|
|
25672
|
-
const runsRoot =
|
|
25673
|
-
if (!
|
|
25782
|
+
const runsRoot = join12(getProjectStateDir(targetDir), "runs");
|
|
25783
|
+
if (!existsSync12(runsRoot))
|
|
25674
25784
|
return null;
|
|
25675
25785
|
for (const day of readdirSync7(runsRoot)) {
|
|
25676
|
-
const record3 = readRunRecord(
|
|
25786
|
+
const record3 = readRunRecord(join12(runsRoot, day, runId));
|
|
25677
25787
|
if (record3)
|
|
25678
25788
|
return record3;
|
|
25679
25789
|
}
|
|
@@ -25690,15 +25800,15 @@ function skillRunEnv(context) {
|
|
|
25690
25800
|
};
|
|
25691
25801
|
}
|
|
25692
25802
|
function writeRunRecord(context) {
|
|
25693
|
-
writeFileSync5(
|
|
25803
|
+
writeFileSync5(join12(context.runDir, "run.json"), JSON.stringify(context.record, null, 2) + `
|
|
25694
25804
|
`);
|
|
25695
25805
|
}
|
|
25696
25806
|
function writeArtifactsManifest(context, artifacts) {
|
|
25697
|
-
writeFileSync5(
|
|
25807
|
+
writeFileSync5(join12(context.runDir, "artifacts.json"), JSON.stringify({ runId: context.record.id, artifacts }, null, 2) + `
|
|
25698
25808
|
`);
|
|
25699
25809
|
}
|
|
25700
25810
|
function collectRunArtifacts(context) {
|
|
25701
|
-
if (!
|
|
25811
|
+
if (!existsSync12(context.exportDir))
|
|
25702
25812
|
return [];
|
|
25703
25813
|
const artifacts = [];
|
|
25704
25814
|
for (const path of walkFiles(context.exportDir)) {
|
|
@@ -25714,8 +25824,8 @@ function collectRunArtifacts(context) {
|
|
|
25714
25824
|
return artifacts.sort((a, b) => a.path.localeCompare(b.path));
|
|
25715
25825
|
}
|
|
25716
25826
|
function readRunRecord(runDir) {
|
|
25717
|
-
const path =
|
|
25718
|
-
if (!
|
|
25827
|
+
const path = join12(runDir, "run.json");
|
|
25828
|
+
if (!existsSync12(path))
|
|
25719
25829
|
return null;
|
|
25720
25830
|
try {
|
|
25721
25831
|
return JSON.parse(readFileSync10(path, "utf-8"));
|
|
@@ -25726,7 +25836,7 @@ function readRunRecord(runDir) {
|
|
|
25726
25836
|
function walkFiles(dir) {
|
|
25727
25837
|
const files = [];
|
|
25728
25838
|
for (const entry of readdirSync7(dir)) {
|
|
25729
|
-
const full =
|
|
25839
|
+
const full = join12(dir, entry);
|
|
25730
25840
|
if (statSync7(full).isDirectory())
|
|
25731
25841
|
files.push(...walkFiles(full));
|
|
25732
25842
|
else
|
|
@@ -26197,12 +26307,12 @@ function registerOperationTools(server) {
|
|
|
26197
26307
|
const agents = [];
|
|
26198
26308
|
for (const agent of AGENT_TARGETS) {
|
|
26199
26309
|
const agentSkillsPath = getAgentSkillsDir(agent, "global");
|
|
26200
|
-
const exists =
|
|
26310
|
+
const exists = existsSync14(agentSkillsPath);
|
|
26201
26311
|
let skillCount = 0;
|
|
26202
26312
|
if (exists) {
|
|
26203
26313
|
try {
|
|
26204
26314
|
skillCount = readdirSync8(agentSkillsPath).filter((f) => {
|
|
26205
|
-
const full =
|
|
26315
|
+
const full = join14(agentSkillsPath, f);
|
|
26206
26316
|
return !f.startsWith(".") && statSync8(full).isDirectory();
|
|
26207
26317
|
}).length;
|
|
26208
26318
|
} catch {}
|
|
@@ -26248,17 +26358,18 @@ function compactRunToolPayload(payload, detailHint) {
|
|
|
26248
26358
|
}
|
|
26249
26359
|
|
|
26250
26360
|
// src/lib/feedback.ts
|
|
26361
|
+
init_api_url();
|
|
26251
26362
|
init_config();
|
|
26252
|
-
import { existsSync as
|
|
26253
|
-
import { dirname as dirname6, join as
|
|
26363
|
+
import { appendFileSync, existsSync as existsSync15, mkdirSync as mkdirSync7 } from "fs";
|
|
26364
|
+
import { dirname as dirname6, join as join15 } from "path";
|
|
26254
26365
|
import { Database } from "bun:sqlite";
|
|
26255
26366
|
function getFeedbackDbPath() {
|
|
26256
|
-
return
|
|
26367
|
+
return join15(getDataDir(), "skills.db");
|
|
26257
26368
|
}
|
|
26258
26369
|
function getFeedbackDb() {
|
|
26259
26370
|
const dbPath = getFeedbackDbPath();
|
|
26260
26371
|
const dir = dirname6(dbPath);
|
|
26261
|
-
if (!
|
|
26372
|
+
if (!existsSync15(dir))
|
|
26262
26373
|
mkdirSync7(dir, { recursive: true });
|
|
26263
26374
|
const db = new Database(dbPath);
|
|
26264
26375
|
db.exec("PRAGMA journal_mode = WAL");
|
|
@@ -26284,6 +26395,15 @@ function saveFeedback(input) {
|
|
|
26284
26395
|
if (!message)
|
|
26285
26396
|
throw new Error("Feedback message is required");
|
|
26286
26397
|
const category = input.category ?? "general";
|
|
26398
|
+
if (isApiMode()) {
|
|
26399
|
+
const path = join15(getDataDir(), "feedback.jsonl");
|
|
26400
|
+
const dir = dirname6(path);
|
|
26401
|
+
if (!existsSync15(dir))
|
|
26402
|
+
mkdirSync7(dir, { recursive: true });
|
|
26403
|
+
appendFileSync(path, JSON.stringify({ message, category, email: input.email ?? null, agent: input.agent ?? null, version: input.version ?? null, createdAt: new Date().toISOString() }) + `
|
|
26404
|
+
`);
|
|
26405
|
+
return { saved: true, category, path };
|
|
26406
|
+
}
|
|
26287
26407
|
const db = getFeedbackDb();
|
|
26288
26408
|
try {
|
|
26289
26409
|
db.run("INSERT INTO feedback (message, email, category, agent, version) VALUES (?, ?, ?, ?, ?)", [message, input.email || null, category, input.agent || null, input.version || null]);
|
|
@@ -26292,6 +26412,15 @@ function saveFeedback(input) {
|
|
|
26292
26412
|
}
|
|
26293
26413
|
return { saved: true, category, path: getFeedbackDbPath() };
|
|
26294
26414
|
}
|
|
26415
|
+
function isApiMode(env = process.env) {
|
|
26416
|
+
if (env.HASNA_SKILLS_API_URL?.trim())
|
|
26417
|
+
return true;
|
|
26418
|
+
try {
|
|
26419
|
+
return Boolean(resolveApiUrl(undefined, env));
|
|
26420
|
+
} catch {
|
|
26421
|
+
return Boolean(env.SKILLS_API_URL?.trim());
|
|
26422
|
+
}
|
|
26423
|
+
}
|
|
26295
26424
|
|
|
26296
26425
|
// src/mcp/resource-meta-tools.ts
|
|
26297
26426
|
function registerResourceMetaTools(server) {
|
|
@@ -26417,14 +26546,14 @@ function registerResourceMetaTools(server) {
|
|
|
26417
26546
|
}
|
|
26418
26547
|
|
|
26419
26548
|
// src/lib/scheduler.ts
|
|
26420
|
-
import { existsSync as
|
|
26421
|
-
import { join as
|
|
26549
|
+
import { existsSync as existsSync16, readFileSync as readFileSync12, writeFileSync as writeFileSync7, mkdirSync as mkdirSync8 } from "fs";
|
|
26550
|
+
import { join as join16 } from "path";
|
|
26422
26551
|
function getSchedulesPath(targetDir = process.cwd()) {
|
|
26423
|
-
return
|
|
26552
|
+
return join16(targetDir, ".skills", "schedules.json");
|
|
26424
26553
|
}
|
|
26425
26554
|
function loadSchedules(targetDir = process.cwd()) {
|
|
26426
26555
|
const path = getSchedulesPath(targetDir);
|
|
26427
|
-
if (
|
|
26556
|
+
if (existsSync16(path)) {
|
|
26428
26557
|
try {
|
|
26429
26558
|
return JSON.parse(readFileSync12(path, "utf-8"));
|
|
26430
26559
|
} catch {}
|
|
@@ -26433,8 +26562,8 @@ function loadSchedules(targetDir = process.cwd()) {
|
|
|
26433
26562
|
}
|
|
26434
26563
|
function saveSchedules(data, targetDir = process.cwd()) {
|
|
26435
26564
|
const path = getSchedulesPath(targetDir);
|
|
26436
|
-
const dir =
|
|
26437
|
-
if (!
|
|
26565
|
+
const dir = join16(targetDir, ".skills");
|
|
26566
|
+
if (!existsSync16(dir))
|
|
26438
26567
|
mkdirSync8(dir, { recursive: true });
|
|
26439
26568
|
writeFileSync7(path, JSON.stringify(data, null, 2));
|
|
26440
26569
|
}
|
|
@@ -26697,14 +26826,14 @@ function registerScheduleTools(server) {
|
|
|
26697
26826
|
init_config();
|
|
26698
26827
|
import { createHash as createHash3, createHmac } from "crypto";
|
|
26699
26828
|
import {
|
|
26700
|
-
existsSync as
|
|
26829
|
+
existsSync as existsSync17,
|
|
26701
26830
|
mkdirSync as mkdirSync9,
|
|
26702
26831
|
readFileSync as readFileSync13,
|
|
26703
26832
|
readdirSync as readdirSync9,
|
|
26704
26833
|
statSync as statSync9,
|
|
26705
26834
|
writeFileSync as writeFileSync8
|
|
26706
26835
|
} from "fs";
|
|
26707
|
-
import { dirname as dirname7, join as
|
|
26836
|
+
import { dirname as dirname7, join as join17, normalize as normalize3, relative as relative3, sep as sep2 } from "path";
|
|
26708
26837
|
init_retired_settings();
|
|
26709
26838
|
var SKILLS_STORAGE_TABLES = [
|
|
26710
26839
|
"skills_sync_records",
|
|
@@ -26781,7 +26910,7 @@ function getSkillsNativeStorageStatus(options = {}) {
|
|
|
26781
26910
|
local: {
|
|
26782
26911
|
dataDir: getDataDir(),
|
|
26783
26912
|
projectStateDir: getProjectStateDir(targetDir),
|
|
26784
|
-
feedbackDbPath:
|
|
26913
|
+
feedbackDbPath: join17(getDataDir(), "skills.db")
|
|
26785
26914
|
},
|
|
26786
26915
|
remote: {
|
|
26787
26916
|
databaseConfigured: Boolean(config2.databaseUrl),
|
|
@@ -26801,7 +26930,7 @@ function getStorageStatus(options = {}) {
|
|
|
26801
26930
|
function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
|
|
26802
26931
|
const projectStateDir = getProjectStateDir(targetDir);
|
|
26803
26932
|
const files = [];
|
|
26804
|
-
if (
|
|
26933
|
+
if (existsSync17(projectStateDir)) {
|
|
26805
26934
|
for (const filePath of walkFiles2(projectStateDir)) {
|
|
26806
26935
|
const bytes = readFileSync13(filePath);
|
|
26807
26936
|
const relativePath = toPosix(relative3(targetDir, filePath));
|
|
@@ -26885,7 +27014,7 @@ function parsePositiveInteger(value) {
|
|
|
26885
27014
|
function walkFiles2(dir) {
|
|
26886
27015
|
const files = [];
|
|
26887
27016
|
for (const entry of readdirSync9(dir)) {
|
|
26888
|
-
const full =
|
|
27017
|
+
const full = join17(dir, entry);
|
|
26889
27018
|
const stats = statSync9(full);
|
|
26890
27019
|
if (stats.isDirectory())
|
|
26891
27020
|
files.push(...walkFiles2(full));
|
|
@@ -27433,7 +27562,7 @@ var responseViaResponseObject = async (res, outgoing, options = {}) => {
|
|
|
27433
27562
|
});
|
|
27434
27563
|
if (!chunk) {
|
|
27435
27564
|
if (i === 1) {
|
|
27436
|
-
await new Promise((
|
|
27565
|
+
await new Promise((resolve2) => setTimeout(resolve2));
|
|
27437
27566
|
maxReadCount = 3;
|
|
27438
27567
|
continue;
|
|
27439
27568
|
}
|
|
@@ -27990,9 +28119,9 @@ data:
|
|
|
27990
28119
|
const initRequest = messages.find((m) => isInitializeRequest(m));
|
|
27991
28120
|
const clientProtocolVersion = initRequest ? initRequest.params.protocolVersion : req.headers.get("mcp-protocol-version") ?? DEFAULT_NEGOTIATED_PROTOCOL_VERSION;
|
|
27992
28121
|
if (this._enableJsonResponse) {
|
|
27993
|
-
return new Promise((
|
|
28122
|
+
return new Promise((resolve2) => {
|
|
27994
28123
|
this._streamMapping.set(streamId, {
|
|
27995
|
-
resolveJson:
|
|
28124
|
+
resolveJson: resolve2,
|
|
27996
28125
|
cleanup: () => {
|
|
27997
28126
|
this._streamMapping.delete(streamId);
|
|
27998
28127
|
}
|
|
@@ -28340,19 +28469,19 @@ async function connectMcpForNode() {
|
|
|
28340
28469
|
return { server: server2, transport };
|
|
28341
28470
|
}
|
|
28342
28471
|
function readBody(req) {
|
|
28343
|
-
return new Promise((
|
|
28472
|
+
return new Promise((resolve2, reject) => {
|
|
28344
28473
|
const chunks = [];
|
|
28345
28474
|
req.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
|
|
28346
28475
|
req.on("end", () => {
|
|
28347
28476
|
const raw = Buffer.concat(chunks).toString("utf-8");
|
|
28348
28477
|
if (!raw.trim()) {
|
|
28349
|
-
|
|
28478
|
+
resolve2(undefined);
|
|
28350
28479
|
return;
|
|
28351
28480
|
}
|
|
28352
28481
|
try {
|
|
28353
|
-
|
|
28482
|
+
resolve2(JSON.parse(raw));
|
|
28354
28483
|
} catch {
|
|
28355
|
-
|
|
28484
|
+
resolve2(undefined);
|
|
28356
28485
|
}
|
|
28357
28486
|
});
|
|
28358
28487
|
req.on("error", reject);
|
|
@@ -28394,9 +28523,9 @@ async function startSkillsMcpHttpServer(options = {}) {
|
|
|
28394
28523
|
}
|
|
28395
28524
|
}
|
|
28396
28525
|
});
|
|
28397
|
-
await new Promise((
|
|
28526
|
+
await new Promise((resolve2, reject) => {
|
|
28398
28527
|
httpServer.once("error", reject);
|
|
28399
|
-
httpServer.listen(port, hostname2, () =>
|
|
28528
|
+
httpServer.listen(port, hostname2, () => resolve2());
|
|
28400
28529
|
});
|
|
28401
28530
|
const address = httpServer.address();
|
|
28402
28531
|
const listenPort = typeof address === "object" && address ? address.port : port;
|