@hasna/skills 0.1.63 → 0.1.66
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 +61 -22
- package/bin/index.js +2120 -670
- package/bin/mcp.js +555 -249
- package/bin/migrate.js +229 -33
- package/bin/server.js +1542 -327
- package/bin/worker.js +716 -207
- package/dist/cli/commands/registry-reconcile.d.ts +2 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +682 -276
- package/dist/lib/agent-sync.d.ts +26 -6
- package/dist/lib/auth-store.d.ts +37 -0
- package/dist/lib/config.d.ts +51 -0
- package/dist/lib/home-census.d.ts +4 -0
- package/dist/lib/home-migration.d.ts +8 -9
- package/dist/lib/native-storage.d.ts +29 -1
- package/dist/lib/portable-skills.d.ts +49 -6
- package/dist/lib/pull.d.ts +31 -0
- package/dist/lib/registry-reconcile.d.ts +114 -0
- package/dist/lib/registry-types.d.ts +9 -0
- package/dist/lib/registry.d.ts +7 -4
- package/dist/lib/remote-client.d.ts +118 -1
- package/dist/lib/remote-registry.d.ts +26 -0
- package/dist/lib/revision.d.ts +29 -0
- package/dist/lib/run-routing.d.ts +60 -0
- package/dist/sdk/index.js +14412 -13338
- package/dist/server/app.d.ts +4 -1
- package/dist/server/config.d.ts +12 -2
- package/dist/server/rows.d.ts +2 -1
- package/dist/server/skills-api.d.ts +108 -6
- package/dist/server/sqlite-store.d.ts +20 -3
- package/dist/server/store-fixtures.d.ts +6 -0
- package/dist/server/store.d.ts +31 -5
- package/dist/server/types.d.ts +98 -3
- package/dist/storage.d.ts +1 -1
- package/dist/storage.js +57 -2
- package/migrations/postgres/0004_hosted_pins.sql +28 -0
- package/migrations/postgres/0005_revision_tombstone_registry.sql +37 -0
- package/migrations/postgres/0005_tag_projection.sql +36 -0
- package/migrations/sqlite/0004_hosted_pins.sql +21 -0
- package/migrations/sqlite/0005_revision_tombstone_registry.sql +18 -0
- package/migrations/sqlite/0005_tag_projection.sql +25 -0
- package/package.json +3 -2
package/bin/mcp.js
CHANGED
|
@@ -6727,6 +6727,9 @@ function normalizeConfigValue(key, value) {
|
|
|
6727
6727
|
return value.trim() ? value : undefined;
|
|
6728
6728
|
return;
|
|
6729
6729
|
}
|
|
6730
|
+
function isOwnerLayoutMigrated(appDir) {
|
|
6731
|
+
return existsSync(join(appDir, SKILLS_CACHE_DIRNAME, LAYOUT_MIGRATION_RECORD));
|
|
6732
|
+
}
|
|
6730
6733
|
function getDataDir() {
|
|
6731
6734
|
const override = process.env[DATA_DIR_ENV];
|
|
6732
6735
|
if (override) {
|
|
@@ -6750,6 +6753,33 @@ function getDataDir() {
|
|
|
6750
6753
|
}
|
|
6751
6754
|
return newDir;
|
|
6752
6755
|
}
|
|
6756
|
+
function getDataDirReadOnly() {
|
|
6757
|
+
const override = process.env[DATA_DIR_ENV];
|
|
6758
|
+
if (override)
|
|
6759
|
+
return override;
|
|
6760
|
+
return join(process.env["HOME"] || process.env["USERPROFILE"] || homedir(), ".hasna", "skills");
|
|
6761
|
+
}
|
|
6762
|
+
function getConfigPathReadOnly(scope) {
|
|
6763
|
+
if (scope === "global")
|
|
6764
|
+
return join(getDataDirReadOnly(), "config.json");
|
|
6765
|
+
return join(process.cwd(), "skills.config.json");
|
|
6766
|
+
}
|
|
6767
|
+
function loadConfigReadOnly() {
|
|
6768
|
+
const canonicalConfigPath = getConfigPathReadOnly("global");
|
|
6769
|
+
let globalConfig2;
|
|
6770
|
+
if (existsSync(canonicalConfigPath)) {
|
|
6771
|
+
globalConfig2 = readConfigFile(canonicalConfigPath);
|
|
6772
|
+
} else if (process.env[DATA_DIR_ENV] !== undefined) {
|
|
6773
|
+
globalConfig2 = {};
|
|
6774
|
+
} else {
|
|
6775
|
+
globalConfig2 = readConfigFile(legacyConfigFilePath());
|
|
6776
|
+
}
|
|
6777
|
+
const projectConfig = readConfigFile(getConfigPathReadOnly("project"));
|
|
6778
|
+
return { ...globalConfig2, ...projectConfig };
|
|
6779
|
+
}
|
|
6780
|
+
function legacyConfigFilePath() {
|
|
6781
|
+
return join(process.env["HOME"] || process.env["USERPROFILE"] || homedir(), ".skillsrc");
|
|
6782
|
+
}
|
|
6753
6783
|
function getConfigPath(scope) {
|
|
6754
6784
|
if (scope === "global") {
|
|
6755
6785
|
return join(getDataDir(), "config.json");
|
|
@@ -6781,7 +6811,7 @@ function loadConfig() {
|
|
|
6781
6811
|
const projectConfig = readConfigFile(getConfigPath("project"));
|
|
6782
6812
|
return { ...globalConfig2, ...projectConfig };
|
|
6783
6813
|
}
|
|
6784
|
-
var ENUM_KEYS, STRING_KEYS, DATA_DIR_ENV = "HASNA_SKILLS_DIR", INSTALLED_SKILLS_DIRNAME = "installed";
|
|
6814
|
+
var ENUM_KEYS, STRING_KEYS, DATA_DIR_ENV = "HASNA_SKILLS_DIR", INSTALLED_SKILLS_DIRNAME = "installed", SKILLS_CACHE_DIRNAME = "skills", LAYOUT_MIGRATION_RECORD = ".layout-migration.json";
|
|
6785
6815
|
var init_config = __esm(() => {
|
|
6786
6816
|
init_retired_settings();
|
|
6787
6817
|
ENUM_KEYS = {
|
|
@@ -6866,19 +6896,33 @@ var exports_auth_store = {};
|
|
|
6866
6896
|
__export(exports_auth_store, {
|
|
6867
6897
|
saveAuthConfig: () => saveAuthConfig,
|
|
6868
6898
|
normalizeSkillsApiOrigin: () => normalizeSkillsApiOrigin,
|
|
6899
|
+
getAuthFilePathReadOnly: () => getAuthFilePathReadOnly,
|
|
6900
|
+
getAuthFilePath: () => getAuthFilePath,
|
|
6901
|
+
getAuthConfigReadOnly: () => getAuthConfigReadOnly,
|
|
6869
6902
|
getAuthConfig: () => getAuthConfig,
|
|
6870
6903
|
getApiUrl: () => getApiUrl,
|
|
6904
|
+
getApiKeyReadOnly: () => getApiKeyReadOnly,
|
|
6871
6905
|
getApiKey: () => getApiKey,
|
|
6872
6906
|
clearAuthConfig: () => clearAuthConfig
|
|
6873
6907
|
});
|
|
6874
|
-
import { existsSync as
|
|
6875
|
-
import { join as
|
|
6908
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync6, readFileSync as readFileSync11, writeFileSync as writeFileSync6, unlinkSync } from "fs";
|
|
6909
|
+
import { dirname as dirname5, join as join12 } from "path";
|
|
6876
6910
|
import { homedir as homedir3 } from "os";
|
|
6911
|
+
function getAuthFilePath() {
|
|
6912
|
+
return join12(getDataDir(), "auth.json");
|
|
6913
|
+
}
|
|
6914
|
+
function getAuthFilePathReadOnly() {
|
|
6915
|
+
return join12(getDataDirReadOnly(), "auth.json");
|
|
6916
|
+
}
|
|
6917
|
+
function legacyAuthFilePath() {
|
|
6918
|
+
return join12(process.env["HOME"] || process.env["USERPROFILE"] || homedir3(), ".skills", "auth.json");
|
|
6919
|
+
}
|
|
6877
6920
|
function getAuthConfig() {
|
|
6878
6921
|
if (cachedConfig !== undefined)
|
|
6879
6922
|
return cachedConfig;
|
|
6880
6923
|
try {
|
|
6881
|
-
const
|
|
6924
|
+
const file = existsSync12(getAuthFilePath()) ? getAuthFilePath() : legacyAuthFilePath();
|
|
6925
|
+
const raw = readFileSync11(file, "utf-8");
|
|
6882
6926
|
const config2 = JSON.parse(raw);
|
|
6883
6927
|
if (!config2.apiKey) {
|
|
6884
6928
|
cachedConfig = null;
|
|
@@ -6892,19 +6936,20 @@ function getAuthConfig() {
|
|
|
6892
6936
|
}
|
|
6893
6937
|
}
|
|
6894
6938
|
function saveAuthConfig(config2) {
|
|
6895
|
-
|
|
6896
|
-
|
|
6939
|
+
const file = getAuthFilePath();
|
|
6940
|
+
mkdirSync6(dirname5(file), { recursive: true, mode: 448 });
|
|
6941
|
+
writeFileSync6(file, JSON.stringify(config2, null, 2) + `
|
|
6897
6942
|
`, { mode: 384 });
|
|
6898
6943
|
cachedConfig = config2;
|
|
6899
6944
|
}
|
|
6900
6945
|
function clearAuthConfig() {
|
|
6901
6946
|
try {
|
|
6902
|
-
unlinkSync(
|
|
6947
|
+
unlinkSync(getAuthFilePath());
|
|
6903
6948
|
} catch {}
|
|
6904
6949
|
try {
|
|
6905
|
-
unlinkSync(
|
|
6950
|
+
unlinkSync(legacyAuthFilePath());
|
|
6906
6951
|
} catch {}
|
|
6907
|
-
cachedConfig =
|
|
6952
|
+
cachedConfig = undefined;
|
|
6908
6953
|
}
|
|
6909
6954
|
function getApiKey() {
|
|
6910
6955
|
if (process.env.SKILLS_API_KEY)
|
|
@@ -6913,6 +6958,25 @@ function getApiKey() {
|
|
|
6913
6958
|
return process.env.SKILL_API_KEY;
|
|
6914
6959
|
return getAuthConfig()?.apiKey || null;
|
|
6915
6960
|
}
|
|
6961
|
+
function getAuthConfigReadOnly() {
|
|
6962
|
+
try {
|
|
6963
|
+
const file = existsSync12(getAuthFilePathReadOnly()) ? getAuthFilePathReadOnly() : legacyAuthFilePath();
|
|
6964
|
+
const raw = readFileSync11(file, "utf-8");
|
|
6965
|
+
const config2 = JSON.parse(raw);
|
|
6966
|
+
if (!config2.apiKey)
|
|
6967
|
+
return null;
|
|
6968
|
+
return config2;
|
|
6969
|
+
} catch {
|
|
6970
|
+
return null;
|
|
6971
|
+
}
|
|
6972
|
+
}
|
|
6973
|
+
function getApiKeyReadOnly() {
|
|
6974
|
+
if (process.env.SKILLS_API_KEY)
|
|
6975
|
+
return process.env.SKILLS_API_KEY;
|
|
6976
|
+
if (process.env.SKILL_API_KEY)
|
|
6977
|
+
return process.env.SKILL_API_KEY;
|
|
6978
|
+
return getAuthConfigReadOnly()?.apiKey || null;
|
|
6979
|
+
}
|
|
6916
6980
|
function normalizeSkillsApiOrigin(apiUrl) {
|
|
6917
6981
|
const url = new URL(apiUrl);
|
|
6918
6982
|
const pathname = url.pathname.replace(/\/+$/, "");
|
|
@@ -6928,12 +6992,10 @@ function normalizeSkillsApiOrigin(apiUrl) {
|
|
|
6928
6992
|
function getApiUrl(action) {
|
|
6929
6993
|
return normalizeSkillsApiOrigin(requireApiUrl(action));
|
|
6930
6994
|
}
|
|
6931
|
-
var
|
|
6995
|
+
var cachedConfig;
|
|
6932
6996
|
var init_auth_store = __esm(() => {
|
|
6933
6997
|
init_api_url();
|
|
6934
|
-
|
|
6935
|
-
AUTH_FILE = join11(AUTH_DIR, "auth.json");
|
|
6936
|
-
LEGACY_AUTH_FILE = join11(homedir3(), ".skills", "auth.json");
|
|
6998
|
+
init_config();
|
|
6937
6999
|
});
|
|
6938
7000
|
|
|
6939
7001
|
// src/lib/blog-article.ts
|
|
@@ -7069,8 +7131,11 @@ var init_blog_article = __esm(() => {
|
|
|
7069
7131
|
// src/lib/remote-client.ts
|
|
7070
7132
|
var exports_remote_client = {};
|
|
7071
7133
|
__export(exports_remote_client, {
|
|
7134
|
+
createRemoteSkillsClientReadOnly: () => createRemoteSkillsClientReadOnly,
|
|
7072
7135
|
createRemoteSkillsClient: () => createRemoteSkillsClient,
|
|
7073
|
-
RemoteSkillsClient: () => RemoteSkillsClient
|
|
7136
|
+
RemoteSkillsClient: () => RemoteSkillsClient,
|
|
7137
|
+
RemoteRouteUnsupportedError: () => RemoteRouteUnsupportedError,
|
|
7138
|
+
RemoteRequestError: () => RemoteRequestError
|
|
7074
7139
|
});
|
|
7075
7140
|
|
|
7076
7141
|
class RemoteSkillsClient {
|
|
@@ -7090,6 +7155,20 @@ class RemoteSkillsClient {
|
|
|
7090
7155
|
}
|
|
7091
7156
|
});
|
|
7092
7157
|
}
|
|
7158
|
+
async requestNewRoute(path, options, opts = {}) {
|
|
7159
|
+
const response = await this.request(path, options);
|
|
7160
|
+
const routePath = path.split("?")[0];
|
|
7161
|
+
if (response.status === 404 || response.status === 405) {
|
|
7162
|
+
if (response.status === 404 && opts.domainNotFoundCodes?.length && await responseBodyCarriesCode(response, opts.domainNotFoundCodes)) {
|
|
7163
|
+
return response;
|
|
7164
|
+
}
|
|
7165
|
+
throw new RemoteRouteUnsupportedError(routePath, response.status, this.apiUrl);
|
|
7166
|
+
}
|
|
7167
|
+
if (!response.ok) {
|
|
7168
|
+
throw new RemoteRequestError(routePath, response.status, response.statusText);
|
|
7169
|
+
}
|
|
7170
|
+
return response;
|
|
7171
|
+
}
|
|
7093
7172
|
async listSkills() {
|
|
7094
7173
|
const res = await this.request("/api/v1/skills");
|
|
7095
7174
|
return res.json();
|
|
@@ -7106,6 +7185,14 @@ class RemoteSkillsClient {
|
|
|
7106
7185
|
return null;
|
|
7107
7186
|
return res.json();
|
|
7108
7187
|
}
|
|
7188
|
+
async getSkillStatus(slug) {
|
|
7189
|
+
const res = await this.request(`/api/v1/skills/${encodeURIComponent(slug)}`, { method: "GET" });
|
|
7190
|
+
let body = null;
|
|
7191
|
+
try {
|
|
7192
|
+
body = await res.json();
|
|
7193
|
+
} catch {}
|
|
7194
|
+
return { status: res.status, body };
|
|
7195
|
+
}
|
|
7109
7196
|
async submitRun(slug, input, args) {
|
|
7110
7197
|
const res = await this.request(`/api/v1/runs/${slug}`, {
|
|
7111
7198
|
method: "POST",
|
|
@@ -7139,15 +7226,18 @@ class RemoteSkillsClient {
|
|
|
7139
7226
|
method: "GET"
|
|
7140
7227
|
});
|
|
7141
7228
|
}
|
|
7142
|
-
async publishSkill(manifest, bundle) {
|
|
7229
|
+
async publishSkill(manifest, bundle, ifMatch) {
|
|
7143
7230
|
const form = new FormData;
|
|
7144
7231
|
form.set("manifest", JSON.stringify(manifest));
|
|
7145
7232
|
if (bundle) {
|
|
7146
7233
|
form.set("bundle", new Blob([bundle], { type: "application/gzip" }), `${String(manifest.slug ?? "skill")}.tar.gz`);
|
|
7147
7234
|
}
|
|
7235
|
+
const headers = { Authorization: `Bearer ${this.apiKey}` };
|
|
7236
|
+
if (ifMatch)
|
|
7237
|
+
headers["If-Match"] = ifMatch;
|
|
7148
7238
|
return fetch(`${this.apiUrl}/api/v1/skills`, {
|
|
7149
7239
|
method: "POST",
|
|
7150
|
-
headers
|
|
7240
|
+
headers,
|
|
7151
7241
|
body: form
|
|
7152
7242
|
});
|
|
7153
7243
|
}
|
|
@@ -7163,6 +7253,135 @@ class RemoteSkillsClient {
|
|
|
7163
7253
|
return null;
|
|
7164
7254
|
return response;
|
|
7165
7255
|
}
|
|
7256
|
+
async listPins() {
|
|
7257
|
+
const response = await this.requestNewRoute("/api/v1/pins");
|
|
7258
|
+
return normalizePinList(await response.json());
|
|
7259
|
+
}
|
|
7260
|
+
async pin(slug, metadata) {
|
|
7261
|
+
const path = `/api/v1/pins/${encodeURIComponent(slug)}`;
|
|
7262
|
+
const response = await this.requestNewRoute(path, {
|
|
7263
|
+
method: "PUT",
|
|
7264
|
+
body: JSON.stringify({ ...metadata ? { metadata } : {} })
|
|
7265
|
+
});
|
|
7266
|
+
return normalizePin(await response.json());
|
|
7267
|
+
}
|
|
7268
|
+
async unpin(slug) {
|
|
7269
|
+
const path = `/api/v1/pins/${encodeURIComponent(slug)}`;
|
|
7270
|
+
const response = await this.requestNewRoute(path, { method: "DELETE" }, { domainNotFoundCodes: ["PIN_NOT_FOUND"] });
|
|
7271
|
+
return response.status !== 404;
|
|
7272
|
+
}
|
|
7273
|
+
async listTags() {
|
|
7274
|
+
const response = await this.requestNewRoute("/api/v1/tags");
|
|
7275
|
+
const payload = await response.json();
|
|
7276
|
+
if (!Array.isArray(payload)) {
|
|
7277
|
+
throw new Error("Remote tags payload did not match the expected contract (expected an array of tag names)");
|
|
7278
|
+
}
|
|
7279
|
+
for (const tag of payload) {
|
|
7280
|
+
if (typeof tag !== "string" || tag.trim().length === 0) {
|
|
7281
|
+
throw new Error("Remote tags payload did not match the expected contract (every element must be a non-empty tag name)");
|
|
7282
|
+
}
|
|
7283
|
+
}
|
|
7284
|
+
return payload;
|
|
7285
|
+
}
|
|
7286
|
+
async skillsByTag(tag) {
|
|
7287
|
+
const path = `/api/v1/tags/${encodeURIComponent(tag)}/skills`;
|
|
7288
|
+
const response = await this.requestNewRoute(path);
|
|
7289
|
+
return normalizeSkillSummaryList(await response.json());
|
|
7290
|
+
}
|
|
7291
|
+
async listUpdatedSince(since, options = {}) {
|
|
7292
|
+
const params = new URLSearchParams({ since });
|
|
7293
|
+
if (options.cursor)
|
|
7294
|
+
params.set("cursor", options.cursor);
|
|
7295
|
+
if (options.limit !== undefined)
|
|
7296
|
+
params.set("limit", String(options.limit));
|
|
7297
|
+
const response = await this.requestNewRoute(`/api/v1/skills/updated?${params.toString()}`);
|
|
7298
|
+
return normalizeUpdatedSincePage(await response.json());
|
|
7299
|
+
}
|
|
7300
|
+
}
|
|
7301
|
+
function requireOptionalString(record3, field) {
|
|
7302
|
+
if (record3[field] === undefined)
|
|
7303
|
+
return;
|
|
7304
|
+
if (typeof record3[field] !== "string") {
|
|
7305
|
+
throw new Error(`Remote payload did not match the expected contract (${field} must be a string when present)`);
|
|
7306
|
+
}
|
|
7307
|
+
return record3[field];
|
|
7308
|
+
}
|
|
7309
|
+
function normalizePin(entry) {
|
|
7310
|
+
if (!entry || typeof entry !== "object") {
|
|
7311
|
+
throw new Error("Remote pin payload did not match the expected contract (expected an object)");
|
|
7312
|
+
}
|
|
7313
|
+
const record3 = entry;
|
|
7314
|
+
const slug = typeof record3.slug === "string" && record3.slug.trim() ? record3.slug.trim() : undefined;
|
|
7315
|
+
if (!slug) {
|
|
7316
|
+
throw new Error("Remote pin payload did not match the expected contract (missing slug)");
|
|
7317
|
+
}
|
|
7318
|
+
let metadata;
|
|
7319
|
+
if (record3.metadata !== undefined) {
|
|
7320
|
+
if (!record3.metadata || typeof record3.metadata !== "object" || Array.isArray(record3.metadata)) {
|
|
7321
|
+
throw new Error("Remote pin payload did not match the expected contract (metadata must be a JSON object when present)");
|
|
7322
|
+
}
|
|
7323
|
+
metadata = record3.metadata;
|
|
7324
|
+
}
|
|
7325
|
+
const pinnedAt = requireOptionalString(record3, "pinnedAt");
|
|
7326
|
+
return {
|
|
7327
|
+
slug,
|
|
7328
|
+
...pinnedAt !== undefined ? { pinnedAt } : {},
|
|
7329
|
+
...metadata ? { metadata } : {}
|
|
7330
|
+
};
|
|
7331
|
+
}
|
|
7332
|
+
function normalizePinList(payload) {
|
|
7333
|
+
if (!Array.isArray(payload)) {
|
|
7334
|
+
throw new Error("Remote pins payload did not match the expected contract (expected an array of pins)");
|
|
7335
|
+
}
|
|
7336
|
+
return payload.map(normalizePin);
|
|
7337
|
+
}
|
|
7338
|
+
function normalizeSkillSummary(entry) {
|
|
7339
|
+
if (!entry || typeof entry !== "object") {
|
|
7340
|
+
throw new Error("Remote skill payload did not match the expected contract (expected an object)");
|
|
7341
|
+
}
|
|
7342
|
+
const record3 = entry;
|
|
7343
|
+
const slug = typeof record3.slug === "string" && record3.slug.trim() ? record3.slug.trim() : undefined;
|
|
7344
|
+
if (!slug) {
|
|
7345
|
+
throw new Error("Remote skill payload did not match the expected contract (missing slug)");
|
|
7346
|
+
}
|
|
7347
|
+
return {
|
|
7348
|
+
slug,
|
|
7349
|
+
...requireOptionalString(record3, "name") !== undefined ? { name: requireOptionalString(record3, "name") } : {},
|
|
7350
|
+
...requireOptionalString(record3, "version") !== undefined ? { version: requireOptionalString(record3, "version") } : {},
|
|
7351
|
+
...requireOptionalString(record3, "updatedAt") !== undefined ? { updatedAt: requireOptionalString(record3, "updatedAt") } : {}
|
|
7352
|
+
};
|
|
7353
|
+
}
|
|
7354
|
+
function normalizeSkillSummaryList(payload) {
|
|
7355
|
+
if (!Array.isArray(payload)) {
|
|
7356
|
+
throw new Error("Remote skills payload did not match the expected contract (expected an array of skills)");
|
|
7357
|
+
}
|
|
7358
|
+
return payload.map(normalizeSkillSummary);
|
|
7359
|
+
}
|
|
7360
|
+
async function responseBodyCarriesCode(response, codes) {
|
|
7361
|
+
try {
|
|
7362
|
+
const payload = await response.clone().json();
|
|
7363
|
+
if (!payload || typeof payload !== "object")
|
|
7364
|
+
return false;
|
|
7365
|
+
const code = payload.code;
|
|
7366
|
+
return typeof code === "string" && codes.includes(code);
|
|
7367
|
+
} catch {
|
|
7368
|
+
return false;
|
|
7369
|
+
}
|
|
7370
|
+
}
|
|
7371
|
+
function normalizeUpdatedSincePage(payload) {
|
|
7372
|
+
if (!payload || typeof payload !== "object") {
|
|
7373
|
+
throw new Error("Updated-since payload did not match the expected contract (expected an object)");
|
|
7374
|
+
}
|
|
7375
|
+
const record3 = payload;
|
|
7376
|
+
if (!Array.isArray(record3.skills)) {
|
|
7377
|
+
throw new Error("Updated-since payload did not match the expected contract (missing skills array)");
|
|
7378
|
+
}
|
|
7379
|
+
const skills = record3.skills.map(normalizeSkillSummary);
|
|
7380
|
+
const nextCursor = record3.nextCursor === undefined || record3.nextCursor === null ? null : record3.nextCursor;
|
|
7381
|
+
if (nextCursor !== null && typeof nextCursor !== "string") {
|
|
7382
|
+
throw new Error("Updated-since payload did not match the expected contract (nextCursor must be a string or absent)");
|
|
7383
|
+
}
|
|
7384
|
+
return { skills, nextCursor };
|
|
7166
7385
|
}
|
|
7167
7386
|
function createRemoteSkillsClient() {
|
|
7168
7387
|
const apiKey = getApiKey();
|
|
@@ -7170,8 +7389,42 @@ function createRemoteSkillsClient() {
|
|
|
7170
7389
|
return null;
|
|
7171
7390
|
return new RemoteSkillsClient(apiKey);
|
|
7172
7391
|
}
|
|
7392
|
+
function createRemoteSkillsClientReadOnly() {
|
|
7393
|
+
const apiKey = getApiKeyReadOnly();
|
|
7394
|
+
if (!apiKey)
|
|
7395
|
+
return null;
|
|
7396
|
+
const apiUrl = resolveApiUrl(loadConfigReadOnly(), process.env);
|
|
7397
|
+
if (!apiUrl)
|
|
7398
|
+
throw new MissingApiUrlError("the cloud group's sync verb (--dry-run)");
|
|
7399
|
+
return new RemoteSkillsClient(apiKey, apiUrl);
|
|
7400
|
+
}
|
|
7401
|
+
var RemoteRouteUnsupportedError, RemoteRequestError;
|
|
7173
7402
|
var init_remote_client = __esm(() => {
|
|
7174
7403
|
init_auth_store();
|
|
7404
|
+
init_api_url();
|
|
7405
|
+
init_config();
|
|
7406
|
+
RemoteRouteUnsupportedError = class RemoteRouteUnsupportedError extends Error {
|
|
7407
|
+
path;
|
|
7408
|
+
status;
|
|
7409
|
+
instance;
|
|
7410
|
+
constructor(path, status, instance) {
|
|
7411
|
+
super(`The configured Skills instance does not support ${path} (HTTP ${status}). ` + `The instance at ${instance} predates this client feature \u2014 upgrade the server, or ` + `use a client version that matches it.`);
|
|
7412
|
+
this.path = path;
|
|
7413
|
+
this.status = status;
|
|
7414
|
+
this.instance = instance;
|
|
7415
|
+
this.name = "RemoteRouteUnsupportedError";
|
|
7416
|
+
}
|
|
7417
|
+
};
|
|
7418
|
+
RemoteRequestError = class RemoteRequestError extends Error {
|
|
7419
|
+
path;
|
|
7420
|
+
status;
|
|
7421
|
+
constructor(path, status, statusText) {
|
|
7422
|
+
super(`Remote request to ${path} failed: HTTP ${status}${statusText ? ` ${statusText}` : ""}`);
|
|
7423
|
+
this.path = path;
|
|
7424
|
+
this.status = status;
|
|
7425
|
+
this.name = "RemoteRequestError";
|
|
7426
|
+
}
|
|
7427
|
+
};
|
|
7175
7428
|
});
|
|
7176
7429
|
|
|
7177
7430
|
// ../../node_modules/.bun/content-type@1.0.5/node_modules/content-type/index.js
|
|
@@ -12690,7 +12943,7 @@ class StdioServerTransport {
|
|
|
12690
12943
|
// package.json
|
|
12691
12944
|
var package_default = {
|
|
12692
12945
|
name: "@hasna/skills",
|
|
12693
|
-
version: "0.1.
|
|
12946
|
+
version: "0.1.66",
|
|
12694
12947
|
description: "Skills library for AI coding agents",
|
|
12695
12948
|
type: "module",
|
|
12696
12949
|
bin: {
|
|
@@ -12764,6 +13017,7 @@ var package_default = {
|
|
|
12764
13017
|
license: "Apache-2.0",
|
|
12765
13018
|
devDependencies: {
|
|
12766
13019
|
"@types/bun": "latest",
|
|
13020
|
+
"@types/node": "25.2.3",
|
|
12767
13021
|
"@types/react": "^18.2.0",
|
|
12768
13022
|
"bun-types": "1.3.14",
|
|
12769
13023
|
"react-devtools-core": "^7.0.1",
|
|
@@ -12772,7 +13026,7 @@ var package_default = {
|
|
|
12772
13026
|
dependencies: {
|
|
12773
13027
|
"@aws-sdk/client-ecs": "^3.1079.0",
|
|
12774
13028
|
"@aws-sdk/client-s3": "^3.1079.0",
|
|
12775
|
-
"@hasna/events": "0.1.
|
|
13029
|
+
"@hasna/events": "0.1.16",
|
|
12776
13030
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
12777
13031
|
chalk: "^5.3.0",
|
|
12778
13032
|
commander: "^12.1.0",
|
|
@@ -20625,22 +20879,23 @@ var EMPTY_COMPLETION_RESULT = {
|
|
|
20625
20879
|
|
|
20626
20880
|
// src/lib/registry.ts
|
|
20627
20881
|
init_config();
|
|
20628
|
-
import { existsSync as
|
|
20629
|
-
import { join as
|
|
20882
|
+
import { existsSync as existsSync7, readFileSync as readFileSync6, readdirSync as readdirSync6 } from "fs";
|
|
20883
|
+
import { join as join7 } from "path";
|
|
20630
20884
|
|
|
20631
20885
|
// src/lib/portable-skills.ts
|
|
20632
20886
|
init_config();
|
|
20633
20887
|
import {
|
|
20634
20888
|
cpSync as cpSync2,
|
|
20635
|
-
existsSync as
|
|
20889
|
+
existsSync as existsSync6,
|
|
20636
20890
|
mkdirSync as mkdirSync3,
|
|
20637
|
-
|
|
20891
|
+
mkdtempSync,
|
|
20892
|
+
readdirSync as readdirSync5,
|
|
20638
20893
|
renameSync,
|
|
20639
20894
|
rmSync,
|
|
20640
|
-
statSync as
|
|
20895
|
+
statSync as statSync6,
|
|
20641
20896
|
writeFileSync as writeFileSync3
|
|
20642
20897
|
} from "fs";
|
|
20643
|
-
import { basename as basename2, dirname as dirname3, isAbsolute as isAbsolute2, join as
|
|
20898
|
+
import { basename as basename2, dirname as dirname3, isAbsolute as isAbsolute2, join as join6, normalize as normalize2 } from "path";
|
|
20644
20899
|
|
|
20645
20900
|
// src/lib/registry-data/development-tools.ts
|
|
20646
20901
|
var DEVELOPMENT_TOOLS_SKILLS = [
|
|
@@ -20800,7 +21055,7 @@ var DEVELOPMENT_TOOLS_SKILLS = [
|
|
|
20800
21055
|
{
|
|
20801
21056
|
name: "monitor",
|
|
20802
21057
|
displayName: "Monitor",
|
|
20803
|
-
description: "Operate the
|
|
21058
|
+
description: "Operate the monitor MCP for machine health, processes, cron jobs, and cleanup workflows",
|
|
20804
21059
|
category: "Development Tools",
|
|
20805
21060
|
tags: ["monitoring", "mcp", "processes", "operations"]
|
|
20806
21061
|
},
|
|
@@ -20846,6 +21101,14 @@ var DEVELOPMENT_TOOLS_SKILLS = [
|
|
|
20846
21101
|
description: "Validate configuration files for syntax and schema compliance",
|
|
20847
21102
|
category: "Development Tools",
|
|
20848
21103
|
tags: ["config", "validation", "schema", "linting"]
|
|
21104
|
+
},
|
|
21105
|
+
{
|
|
21106
|
+
name: "session-inject-monitor",
|
|
21107
|
+
displayName: "Session Inject Monitor",
|
|
21108
|
+
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",
|
|
21109
|
+
category: "Development Tools",
|
|
21110
|
+
tags: ["monitor", "session", "injection", "automation", "wake"],
|
|
21111
|
+
kind: "instruction"
|
|
20849
21112
|
}
|
|
20850
21113
|
];
|
|
20851
21114
|
|
|
@@ -21233,7 +21496,7 @@ var DESIGN_BRANDING_SKILLS = [
|
|
|
21233
21496
|
displayName: "Site Analyze",
|
|
21234
21497
|
description: "Analyze any website's design system \u2014 detects shadcn/ui, Tailwind, extracts colors, typography, and components via Playwright + Claude Vision.",
|
|
21235
21498
|
category: "Design & Branding",
|
|
21236
|
-
tags: ["design", "shadcn", "tailwind", "colors", "typography", "playwright", "analysis", "
|
|
21499
|
+
tags: ["design", "shadcn", "tailwind", "colors", "typography", "playwright", "analysis", "styles"]
|
|
21237
21500
|
}
|
|
21238
21501
|
];
|
|
21239
21502
|
|
|
@@ -21343,11 +21606,9 @@ var SKILLS = [
|
|
|
21343
21606
|
...EVENT_MANAGEMENT_SKILLS
|
|
21344
21607
|
];
|
|
21345
21608
|
|
|
21346
|
-
// src/lib/skill-validation.ts
|
|
21347
|
-
import { existsSync as existsSync2, lstatSync, readFileSync as readFileSync2, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
21348
|
-
import { isAbsolute, join as join2, normalize } from "path";
|
|
21349
|
-
|
|
21350
21609
|
// src/lib/hosted-skill-set.ts
|
|
21610
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
21611
|
+
import { join as join2 } from "path";
|
|
21351
21612
|
var HOSTED_RUNTIMES = new Set(["hosted"]);
|
|
21352
21613
|
var HOSTED_SOURCES = new Set(["remote", "private-hosted"]);
|
|
21353
21614
|
function normalizeMarker(value) {
|
|
@@ -21359,6 +21620,16 @@ function isHostedMetadataPackage(pkg) {
|
|
|
21359
21620
|
return false;
|
|
21360
21621
|
return HOSTED_RUNTIMES.has(normalizeMarker(skills.runtime)) || HOSTED_SOURCES.has(normalizeMarker(skills.source));
|
|
21361
21622
|
}
|
|
21623
|
+
function isHostedMetadataSkillDir(skillDir) {
|
|
21624
|
+
const pkgPath = join2(skillDir, "package.json");
|
|
21625
|
+
if (!existsSync2(pkgPath))
|
|
21626
|
+
return false;
|
|
21627
|
+
try {
|
|
21628
|
+
return isHostedMetadataPackage(JSON.parse(readFileSync2(pkgPath, "utf8")));
|
|
21629
|
+
} catch {
|
|
21630
|
+
return false;
|
|
21631
|
+
}
|
|
21632
|
+
}
|
|
21362
21633
|
var HOSTED_METADATA_SET_EMPTY_ERROR = [
|
|
21363
21634
|
"The hosted metadata skill set is empty, but the packaging guards that depend on it",
|
|
21364
21635
|
"only mean anything while it is non-empty: an empty set makes every one of them pass",
|
|
@@ -21375,6 +21646,8 @@ var BRACE_SOURCE_EXCLUSION = new RegExp(`^!skills/\\{(${SLUG}(?:,${SLUG})+)\\}/s
|
|
|
21375
21646
|
var SINGLE_SOURCE_EXCLUSION = new RegExp(`^!skills/(${SLUG})/src$`);
|
|
21376
21647
|
|
|
21377
21648
|
// src/lib/skill-validation.ts
|
|
21649
|
+
import { existsSync as existsSync3, lstatSync, readFileSync as readFileSync3, readdirSync as readdirSync3, statSync as statSync3 } from "fs";
|
|
21650
|
+
import { isAbsolute, join as join3, normalize } from "path";
|
|
21378
21651
|
var VALID_SKILL_KINDS = ["executable", "instruction"];
|
|
21379
21652
|
var DOC_FILES = ["SKILL.md", "README.md", "CLAUDE.md"];
|
|
21380
21653
|
var RESERVED_SKILL_ENTRIES = new Set([
|
|
@@ -21428,7 +21701,7 @@ function add(target, code, message) {
|
|
|
21428
21701
|
target.push({ code, message });
|
|
21429
21702
|
}
|
|
21430
21703
|
function readJsonFile(path) {
|
|
21431
|
-
return JSON.parse(
|
|
21704
|
+
return JSON.parse(readFileSync3(path, "utf-8"));
|
|
21432
21705
|
}
|
|
21433
21706
|
function asRecord(value) {
|
|
21434
21707
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
|
|
@@ -21512,7 +21785,7 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
21512
21785
|
binCommands: [],
|
|
21513
21786
|
docFiles: []
|
|
21514
21787
|
};
|
|
21515
|
-
if (!
|
|
21788
|
+
if (!existsSync3(skillPath)) {
|
|
21516
21789
|
add(issues, "skill.dir_missing", `Skill directory not found: ${skillPath}`);
|
|
21517
21790
|
return {
|
|
21518
21791
|
name: bareName,
|
|
@@ -21526,8 +21799,8 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
21526
21799
|
if (!VALID_BIN_COMMAND.test(bareName)) {
|
|
21527
21800
|
add(issues, "skill.name_invalid", `Skill name '${bareName}' must use lowercase letters, numbers, dots, underscores, or hyphens`);
|
|
21528
21801
|
}
|
|
21529
|
-
for (const entry of
|
|
21530
|
-
const entryPath =
|
|
21802
|
+
for (const entry of readdirSync3(skillPath).sort()) {
|
|
21803
|
+
const entryPath = join3(skillPath, entry);
|
|
21531
21804
|
if (RESERVED_SKILL_ENTRIES.has(entry)) {
|
|
21532
21805
|
add(issues, "skill.reserved_file", `Reserved file '${entry}' is not allowed in skill packages`);
|
|
21533
21806
|
}
|
|
@@ -21539,15 +21812,15 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
21539
21812
|
}
|
|
21540
21813
|
}
|
|
21541
21814
|
for (const docFile of DOC_FILES) {
|
|
21542
|
-
if (
|
|
21815
|
+
if (existsSync3(join3(skillPath, docFile)))
|
|
21543
21816
|
metadata.docFiles.push(docFile);
|
|
21544
21817
|
}
|
|
21545
21818
|
if (metadata.docFiles.length === 0) {
|
|
21546
21819
|
add(issues, "skill.docs_missing", "Missing documentation file: expected SKILL.md, README.md, or CLAUDE.md");
|
|
21547
21820
|
}
|
|
21548
|
-
const skillMdPath =
|
|
21549
|
-
if (
|
|
21550
|
-
const frontmatter = parseSkillFrontmatter(
|
|
21821
|
+
const skillMdPath = join3(skillPath, "SKILL.md");
|
|
21822
|
+
if (existsSync3(skillMdPath)) {
|
|
21823
|
+
const frontmatter = parseSkillFrontmatter(readFileSync3(skillMdPath, "utf-8"));
|
|
21551
21824
|
if (!frontmatter) {
|
|
21552
21825
|
add(warnings, "skill.frontmatter_missing", "SKILL.md has no YAML frontmatter");
|
|
21553
21826
|
} else {
|
|
@@ -21589,8 +21862,8 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
21589
21862
|
}
|
|
21590
21863
|
metadata.kind = resolvedKind;
|
|
21591
21864
|
const isInstruction = resolvedKind === "instruction";
|
|
21592
|
-
const pkgPath =
|
|
21593
|
-
if (!
|
|
21865
|
+
const pkgPath = join3(skillPath, "package.json");
|
|
21866
|
+
if (!existsSync3(pkgPath)) {
|
|
21594
21867
|
if (!isInstruction)
|
|
21595
21868
|
add(issues, "package.missing", "Missing package.json");
|
|
21596
21869
|
} else {
|
|
@@ -21648,10 +21921,10 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
21648
21921
|
add(issues, "package.bin_target_unsafe", `package.json bin '${command}' target '${target}' must stay inside the skill directory`);
|
|
21649
21922
|
continue;
|
|
21650
21923
|
}
|
|
21651
|
-
const targetPath =
|
|
21652
|
-
if (!
|
|
21924
|
+
const targetPath = join3(skillPath, target);
|
|
21925
|
+
if (!existsSync3(targetPath)) {
|
|
21653
21926
|
add(warnings, "package.bin_target_missing", `package.json bin '${command}' target '${target}' is not present before build`);
|
|
21654
|
-
} else if (
|
|
21927
|
+
} else if (statSync3(targetPath).isDirectory()) {
|
|
21655
21928
|
add(issues, "package.bin_target_directory", `package.json bin '${command}' target '${target}' must point to a file, not a directory`);
|
|
21656
21929
|
}
|
|
21657
21930
|
}
|
|
@@ -21666,18 +21939,18 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
21666
21939
|
metadata.runtime = "none";
|
|
21667
21940
|
} else {
|
|
21668
21941
|
metadata.runtime = hostedMetadata ? "hosted" : "local";
|
|
21669
|
-
const srcDir =
|
|
21942
|
+
const srcDir = join3(skillPath, "src");
|
|
21670
21943
|
if (hostedMetadata) {
|
|
21671
|
-
if (
|
|
21944
|
+
if (existsSync3(srcDir)) {
|
|
21672
21945
|
add(issues, "skill.hosted_source_forbidden", "Hosted metadata skills must not include local implementation source");
|
|
21673
21946
|
}
|
|
21674
|
-
} else if (!
|
|
21947
|
+
} else if (!existsSync3(srcDir)) {
|
|
21675
21948
|
add(issues, "skill.src_missing", "Missing src/ directory");
|
|
21676
|
-
} else if (!
|
|
21949
|
+
} else if (!existsSync3(join3(srcDir, "index.ts")) && !existsSync3(join3(srcDir, "index.js"))) {
|
|
21677
21950
|
add(issues, "skill.src_index_missing", "Missing src/index.ts or src/index.js");
|
|
21678
21951
|
} else {
|
|
21679
|
-
const indexPath =
|
|
21680
|
-
const size =
|
|
21952
|
+
const indexPath = existsSync3(join3(srcDir, "index.ts")) ? join3(srcDir, "index.ts") : join3(srcDir, "index.js");
|
|
21953
|
+
const size = statSync3(indexPath).size;
|
|
21681
21954
|
if (size < 50)
|
|
21682
21955
|
add(warnings, "skill.src_index_minimal", `Source entry point is very small (${size}B)`);
|
|
21683
21956
|
}
|
|
@@ -21694,8 +21967,8 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
21694
21967
|
|
|
21695
21968
|
// src/lib/skill-hash.ts
|
|
21696
21969
|
import { createHash } from "crypto";
|
|
21697
|
-
import { existsSync as
|
|
21698
|
-
import { join as
|
|
21970
|
+
import { existsSync as existsSync4, readdirSync as readdirSync4, readFileSync as readFileSync4, statSync as statSync4 } from "fs";
|
|
21971
|
+
import { join as join4, sep } from "path";
|
|
21699
21972
|
var CONTENT_HASH_ALGORITHM = "sha256";
|
|
21700
21973
|
var HASH_EXCLUDE_DIRS = new Set([".git", "node_modules", "dist", "build", ".turbo"]);
|
|
21701
21974
|
var HASH_COVERAGE = [
|
|
@@ -21754,10 +22027,10 @@ function collectBundleFiles(skillPath) {
|
|
|
21754
22027
|
if (seen.has(entry))
|
|
21755
22028
|
continue;
|
|
21756
22029
|
seen.add(entry);
|
|
21757
|
-
const absolute =
|
|
21758
|
-
if (!
|
|
22030
|
+
const absolute = join4(skillPath, entry);
|
|
22031
|
+
if (!existsSync4(absolute))
|
|
21759
22032
|
continue;
|
|
21760
|
-
if (
|
|
22033
|
+
if (statSync4(absolute).isDirectory())
|
|
21761
22034
|
collectDirectory(files, absolute, entry);
|
|
21762
22035
|
else
|
|
21763
22036
|
collectFile(files, absolute, entry);
|
|
@@ -21765,14 +22038,14 @@ function collectBundleFiles(skillPath) {
|
|
|
21765
22038
|
return files.sort((a, b) => a.rel < b.rel ? -1 : a.rel > b.rel ? 1 : 0);
|
|
21766
22039
|
}
|
|
21767
22040
|
function collectDirectory(files, dir, rel) {
|
|
21768
|
-
for (const entry of
|
|
22041
|
+
for (const entry of readdirSync4(dir).sort()) {
|
|
21769
22042
|
if (entry.startsWith("."))
|
|
21770
22043
|
continue;
|
|
21771
|
-
const absolute =
|
|
22044
|
+
const absolute = join4(dir, entry);
|
|
21772
22045
|
const childRel = `${rel}/${entry}`;
|
|
21773
22046
|
let stats;
|
|
21774
22047
|
try {
|
|
21775
|
-
stats =
|
|
22048
|
+
stats = statSync4(absolute);
|
|
21776
22049
|
} catch {
|
|
21777
22050
|
continue;
|
|
21778
22051
|
}
|
|
@@ -21788,7 +22061,7 @@ function collectDirectory(files, dir, rel) {
|
|
|
21788
22061
|
}
|
|
21789
22062
|
}
|
|
21790
22063
|
function collectFile(files, absolute, rel) {
|
|
21791
|
-
const buffer =
|
|
22064
|
+
const buffer = readFileSync4(absolute);
|
|
21792
22065
|
if (rel === "skill.json") {
|
|
21793
22066
|
files.push({ rel: rel.split(sep).join("/"), content: new TextEncoder().encode(canonicalizeManifest(new TextDecoder().decode(buffer))) });
|
|
21794
22067
|
return;
|
|
@@ -21988,14 +22261,14 @@ function validateRuntimeContract(manifest, issues, strict) {
|
|
|
21988
22261
|
// src/lib/portable-skills-files.ts
|
|
21989
22262
|
import {
|
|
21990
22263
|
cpSync,
|
|
21991
|
-
existsSync as
|
|
22264
|
+
existsSync as existsSync5,
|
|
21992
22265
|
lstatSync as lstatSync2,
|
|
21993
22266
|
mkdirSync as mkdirSync2,
|
|
21994
|
-
readFileSync as
|
|
22267
|
+
readFileSync as readFileSync5,
|
|
21995
22268
|
realpathSync,
|
|
21996
22269
|
writeFileSync as writeFileSync2
|
|
21997
22270
|
} from "fs";
|
|
21998
|
-
import { basename, dirname as dirname2, join as
|
|
22271
|
+
import { basename, dirname as dirname2, join as join5, relative } from "path";
|
|
21999
22272
|
var ANY_SEGMENT_COPY_EXCLUDES = new Set([
|
|
22000
22273
|
".git",
|
|
22001
22274
|
".DS_Store",
|
|
@@ -22038,12 +22311,12 @@ function normalizePortableSkillName(name) {
|
|
|
22038
22311
|
return normalized;
|
|
22039
22312
|
}
|
|
22040
22313
|
function readPortableSkillManifest(skillPath, fallbackName = basename(skillPath)) {
|
|
22041
|
-
const skillJsonPath =
|
|
22042
|
-
const skillMdPath =
|
|
22043
|
-
const pkgPath =
|
|
22044
|
-
const jsonManifest =
|
|
22045
|
-
const frontmatter =
|
|
22046
|
-
const pkg =
|
|
22314
|
+
const skillJsonPath = join5(skillPath, "skill.json");
|
|
22315
|
+
const skillMdPath = join5(skillPath, "SKILL.md");
|
|
22316
|
+
const pkgPath = join5(skillPath, "package.json");
|
|
22317
|
+
const jsonManifest = existsSync5(skillJsonPath) ? readJsonObject(skillJsonPath) : undefined;
|
|
22318
|
+
const frontmatter = existsSync5(skillMdPath) ? parseSkillFrontmatter(readFileSync5(skillMdPath, "utf-8")) ?? undefined : undefined;
|
|
22319
|
+
const pkg = existsSync5(pkgPath) ? readJsonObject(pkgPath) : undefined;
|
|
22047
22320
|
const name = normalizePortableSkillName(stringField(jsonManifest, "name") ?? frontmatter?.name ?? stringValue(pkg?.name) ?? fallbackName);
|
|
22048
22321
|
const description = stringField(jsonManifest, "description") ?? frontmatter?.description ?? stringValue(pkg?.description) ?? `${name} skill`;
|
|
22049
22322
|
const version2 = stringField(jsonManifest, "version") ?? frontmatter?.version ?? stringValue(pkg?.version) ?? PORTABLE_SKILL_DEFAULT_VERSION;
|
|
@@ -22089,7 +22362,7 @@ function createInstructionManifest(name, options) {
|
|
|
22089
22362
|
}
|
|
22090
22363
|
function writeInstructionSkillTemplate(skillPath, manifest) {
|
|
22091
22364
|
mkdirSync2(skillPath, { recursive: true });
|
|
22092
|
-
writeFileSync2(
|
|
22365
|
+
writeFileSync2(join5(skillPath, "SKILL.md"), renderInstructionSkillMd(manifest));
|
|
22093
22366
|
writeSkillJsonWithHash(skillPath, manifest);
|
|
22094
22367
|
}
|
|
22095
22368
|
function renderInstructionSkillMd(manifest) {
|
|
@@ -22138,12 +22411,12 @@ function createPortableManifest(name, options) {
|
|
|
22138
22411
|
};
|
|
22139
22412
|
}
|
|
22140
22413
|
function writePortableSkillTemplate(skillPath, manifest) {
|
|
22141
|
-
mkdirSync2(
|
|
22142
|
-
writeFileSync2(
|
|
22143
|
-
writeFileSync2(
|
|
22144
|
-
writeFileSync2(
|
|
22145
|
-
writeFileSync2(
|
|
22146
|
-
writeFileSync2(
|
|
22414
|
+
mkdirSync2(join5(skillPath, "src"), { recursive: true });
|
|
22415
|
+
writeFileSync2(join5(skillPath, "SKILL.md"), renderSkillMd(manifest));
|
|
22416
|
+
writeFileSync2(join5(skillPath, "AGENTS.md"), renderAgentsMd(manifest));
|
|
22417
|
+
writeFileSync2(join5(skillPath, "package.json"), renderPackageJson(manifest));
|
|
22418
|
+
writeFileSync2(join5(skillPath, "tsconfig.json"), renderTsconfig());
|
|
22419
|
+
writeFileSync2(join5(skillPath, "src", "index.ts"), renderEntrypoint(manifest));
|
|
22147
22420
|
writeSkillJsonWithHash(skillPath, manifest);
|
|
22148
22421
|
}
|
|
22149
22422
|
function fillContractDefaults(manifest, entrypoint) {
|
|
@@ -22168,7 +22441,7 @@ function writeSkillJsonWithHash(skillPath, manifest) {
|
|
|
22168
22441
|
content_hash: undefined
|
|
22169
22442
|
}
|
|
22170
22443
|
};
|
|
22171
|
-
writeFileSync2(
|
|
22444
|
+
writeFileSync2(join5(skillPath, "skill.json"), `${JSON.stringify({ ...existing, ...renderSkillJsonObject(withoutHash) }, null, 2)}
|
|
22172
22445
|
`);
|
|
22173
22446
|
const hash = computeContentHash(skillPath);
|
|
22174
22447
|
const withHash = {
|
|
@@ -22178,16 +22451,16 @@ function writeSkillJsonWithHash(skillPath, manifest) {
|
|
|
22178
22451
|
content_hash: hash
|
|
22179
22452
|
}
|
|
22180
22453
|
};
|
|
22181
|
-
writeFileSync2(
|
|
22454
|
+
writeFileSync2(join5(skillPath, "skill.json"), `${JSON.stringify({ ...existing, ...renderSkillJsonObject(withHash) }, null, 2)}
|
|
22182
22455
|
`);
|
|
22183
22456
|
return withHash;
|
|
22184
22457
|
}
|
|
22185
22458
|
function readExistingSkillJson(skillPath) {
|
|
22186
|
-
const path =
|
|
22187
|
-
if (!
|
|
22459
|
+
const path = join5(skillPath, "skill.json");
|
|
22460
|
+
if (!existsSync5(path))
|
|
22188
22461
|
return {};
|
|
22189
22462
|
try {
|
|
22190
|
-
const parsed = JSON.parse(
|
|
22463
|
+
const parsed = JSON.parse(readFileSync5(path, "utf-8"));
|
|
22191
22464
|
return isRecord2(parsed) ? parsed : {};
|
|
22192
22465
|
} catch {
|
|
22193
22466
|
return {};
|
|
@@ -22217,28 +22490,28 @@ function ensurePortableSkillFiles(skillPath, manifest) {
|
|
|
22217
22490
|
tags: next.tags?.length ? next.tags : ["custom", next.name]
|
|
22218
22491
|
};
|
|
22219
22492
|
const entry = next.commands[0]?.entry ?? "src/index.ts";
|
|
22220
|
-
if (entry && !
|
|
22221
|
-
mkdirSync2(dirname2(
|
|
22222
|
-
writeFileSync2(
|
|
22493
|
+
if (entry && !existsSync5(join5(skillPath, entry))) {
|
|
22494
|
+
mkdirSync2(dirname2(join5(skillPath, entry)), { recursive: true });
|
|
22495
|
+
writeFileSync2(join5(skillPath, entry), renderEntrypoint(next));
|
|
22223
22496
|
}
|
|
22224
|
-
if (!
|
|
22225
|
-
writeFileSync2(
|
|
22497
|
+
if (!existsSync5(join5(skillPath, "SKILL.md")))
|
|
22498
|
+
writeFileSync2(join5(skillPath, "SKILL.md"), renderSkillMd(next));
|
|
22226
22499
|
else
|
|
22227
|
-
writeFileSync2(
|
|
22228
|
-
if (!
|
|
22229
|
-
writeFileSync2(
|
|
22500
|
+
writeFileSync2(join5(skillPath, "SKILL.md"), ensureSkillMdFrontmatter(readFileSync5(join5(skillPath, "SKILL.md"), "utf-8"), next));
|
|
22501
|
+
if (!existsSync5(join5(skillPath, "AGENTS.md")))
|
|
22502
|
+
writeFileSync2(join5(skillPath, "AGENTS.md"), renderAgentsMd(next));
|
|
22230
22503
|
ensurePackageJson(skillPath, next);
|
|
22231
|
-
if (!
|
|
22232
|
-
writeFileSync2(
|
|
22504
|
+
if (!existsSync5(join5(skillPath, "tsconfig.json")))
|
|
22505
|
+
writeFileSync2(join5(skillPath, "tsconfig.json"), renderTsconfig());
|
|
22233
22506
|
writeSkillJsonWithHash(skillPath, next);
|
|
22234
22507
|
return readPortableSkillManifest(skillPath, next.name);
|
|
22235
22508
|
}
|
|
22236
22509
|
function ensurePackageJson(skillPath, manifest) {
|
|
22237
|
-
const pkgPath =
|
|
22510
|
+
const pkgPath = join5(skillPath, "package.json");
|
|
22238
22511
|
const first = manifest.commands[0] ?? { name: manifest.name, entry: "src/index.ts" };
|
|
22239
22512
|
const commandName = normalizePortableSkillName(first.name || manifest.name);
|
|
22240
22513
|
const entry = (first.entry ?? "src/index.ts").replace(/^\.\//, "");
|
|
22241
|
-
if (!
|
|
22514
|
+
if (!existsSync5(pkgPath)) {
|
|
22242
22515
|
writeFileSync2(pkgPath, renderPackageJson(manifest));
|
|
22243
22516
|
return;
|
|
22244
22517
|
}
|
|
@@ -22281,8 +22554,8 @@ function ensureInstructionSkillFiles(skillPath, manifest) {
|
|
|
22281
22554
|
inputs: [],
|
|
22282
22555
|
commands: []
|
|
22283
22556
|
};
|
|
22284
|
-
if (!
|
|
22285
|
-
writeFileSync2(
|
|
22557
|
+
if (!existsSync5(join5(skillPath, "SKILL.md"))) {
|
|
22558
|
+
writeFileSync2(join5(skillPath, "SKILL.md"), renderSkillMd(next));
|
|
22286
22559
|
}
|
|
22287
22560
|
writeSkillJsonWithHash(skillPath, next);
|
|
22288
22561
|
return readPortableSkillManifest(skillPath, next.name);
|
|
@@ -22533,7 +22806,7 @@ function inferPackageCommands(pkg, fallbackName) {
|
|
|
22533
22806
|
return;
|
|
22534
22807
|
}
|
|
22535
22808
|
function readJsonObject(path) {
|
|
22536
|
-
const parsed = JSON.parse(
|
|
22809
|
+
const parsed = JSON.parse(readFileSync5(path, "utf-8"));
|
|
22537
22810
|
if (!isRecord2(parsed))
|
|
22538
22811
|
throw new Error(`${basename(path)} must contain a JSON object`);
|
|
22539
22812
|
return parsed;
|
|
@@ -22565,33 +22838,36 @@ var LEGACY_CUSTOM_DIRNAME = "custom";
|
|
|
22565
22838
|
function getPortableSkillsRoot(options = {}) {
|
|
22566
22839
|
if (options.rootDir)
|
|
22567
22840
|
return options.rootDir;
|
|
22568
|
-
const appDir = options.homeDir ?
|
|
22569
|
-
const
|
|
22841
|
+
const appDir = options.homeDir ? join6(options.homeDir, ".hasna", "skills") : getDataDir();
|
|
22842
|
+
const cache = join6(appDir, SKILLS_CACHE_DIRNAME);
|
|
22843
|
+
if (isOwnerLayoutMigrated(appDir) && safeIsDirectory(cache))
|
|
22844
|
+
return cache;
|
|
22845
|
+
const installed = join6(appDir, INSTALLED_SKILLS_DIRNAME);
|
|
22570
22846
|
migrateLegacySkillLayout(appDir, installed);
|
|
22571
22847
|
return installed;
|
|
22572
22848
|
}
|
|
22573
22849
|
function looksLikeSkillDirectory(path) {
|
|
22574
22850
|
if (!safeIsDirectory(path))
|
|
22575
22851
|
return false;
|
|
22576
|
-
return
|
|
22852
|
+
return existsSync6(join6(path, "SKILL.md")) || existsSync6(join6(path, "skill.json")) || existsSync6(join6(path, "package.json"));
|
|
22577
22853
|
}
|
|
22578
22854
|
function migrateLegacySkillLayout(appDir, installed) {
|
|
22579
22855
|
if (!safeIsDirectory(appDir))
|
|
22580
22856
|
return;
|
|
22581
22857
|
const candidates = [];
|
|
22582
22858
|
try {
|
|
22583
|
-
for (const entry of
|
|
22859
|
+
for (const entry of readdirSync5(appDir)) {
|
|
22584
22860
|
if (entry.startsWith(".") || entry === INSTALLED_SKILLS_DIRNAME)
|
|
22585
22861
|
continue;
|
|
22586
|
-
const path =
|
|
22862
|
+
const path = join6(appDir, entry);
|
|
22587
22863
|
if (entry === LEGACY_CUSTOM_DIRNAME) {
|
|
22588
22864
|
if (!safeIsDirectory(path))
|
|
22589
22865
|
continue;
|
|
22590
22866
|
try {
|
|
22591
|
-
for (const nested of
|
|
22867
|
+
for (const nested of readdirSync5(path)) {
|
|
22592
22868
|
if (nested.startsWith("."))
|
|
22593
22869
|
continue;
|
|
22594
|
-
const nestedPath =
|
|
22870
|
+
const nestedPath = join6(path, nested);
|
|
22595
22871
|
if (looksLikeSkillDirectory(nestedPath))
|
|
22596
22872
|
candidates.push({ from: nestedPath, name: nested });
|
|
22597
22873
|
}
|
|
@@ -22605,10 +22881,10 @@ function migrateLegacySkillLayout(appDir, installed) {
|
|
|
22605
22881
|
return;
|
|
22606
22882
|
}
|
|
22607
22883
|
for (const { from, name } of candidates) {
|
|
22608
|
-
const target =
|
|
22609
|
-
if (
|
|
22884
|
+
const target = join6(installed, name);
|
|
22885
|
+
if (existsSync6(target))
|
|
22610
22886
|
continue;
|
|
22611
|
-
const staging =
|
|
22887
|
+
const staging = join6(installed, `.migrating-${name}-${process.pid}`);
|
|
22612
22888
|
try {
|
|
22613
22889
|
rmSync(staging, { recursive: true, force: true });
|
|
22614
22890
|
cpSync2(from, staging, { recursive: true, errorOnExist: false });
|
|
@@ -22621,7 +22897,7 @@ function migrateLegacySkillLayout(appDir, installed) {
|
|
|
22621
22897
|
}
|
|
22622
22898
|
}
|
|
22623
22899
|
function getPortableSkillPath(name, options = {}) {
|
|
22624
|
-
return
|
|
22900
|
+
return join6(getPortableSkillsRoot(options), normalizePortableSkillName(name));
|
|
22625
22901
|
}
|
|
22626
22902
|
function findPortableSkill(name, options = {}) {
|
|
22627
22903
|
let normalized;
|
|
@@ -22631,7 +22907,7 @@ function findPortableSkill(name, options = {}) {
|
|
|
22631
22907
|
return null;
|
|
22632
22908
|
}
|
|
22633
22909
|
const path = getPortableSkillPath(normalized, options);
|
|
22634
|
-
if (!
|
|
22910
|
+
if (!existsSync6(path) || !statSync6(path).isDirectory())
|
|
22635
22911
|
return null;
|
|
22636
22912
|
try {
|
|
22637
22913
|
return summarizePortableSkill(path, normalized);
|
|
@@ -22644,10 +22920,10 @@ function listPortableSkills(options = {}) {
|
|
|
22644
22920
|
if (!safeIsDirectory(root))
|
|
22645
22921
|
return [];
|
|
22646
22922
|
const skills = [];
|
|
22647
|
-
for (const entry of
|
|
22923
|
+
for (const entry of readdirSync5(root).sort()) {
|
|
22648
22924
|
if (entry.startsWith("."))
|
|
22649
22925
|
continue;
|
|
22650
|
-
const path =
|
|
22926
|
+
const path = join6(root, entry);
|
|
22651
22927
|
if (!safeIsDirectory(path))
|
|
22652
22928
|
continue;
|
|
22653
22929
|
try {
|
|
@@ -22669,7 +22945,8 @@ function listPortableSkillMetas(options = {}) {
|
|
|
22669
22945
|
tags: manifest.tags || ["custom"],
|
|
22670
22946
|
version: skill.version,
|
|
22671
22947
|
...manifest.kind ? { kind: manifest.kind } : {},
|
|
22672
|
-
source: "custom"
|
|
22948
|
+
source: "custom",
|
|
22949
|
+
...isHostedMetadataSkillDir(skill.path) ? { serverOwned: true } : {}
|
|
22673
22950
|
};
|
|
22674
22951
|
});
|
|
22675
22952
|
}
|
|
@@ -22680,8 +22957,8 @@ function isOfficialSkillName(name) {
|
|
|
22680
22957
|
function scaffoldPortableSkill(name, options = {}) {
|
|
22681
22958
|
const skillName = normalizePortableSkillName(name);
|
|
22682
22959
|
const root = getPortableSkillsRoot(options);
|
|
22683
|
-
const skillPath =
|
|
22684
|
-
if (
|
|
22960
|
+
const skillPath = join6(root, skillName);
|
|
22961
|
+
if (existsSync6(skillPath)) {
|
|
22685
22962
|
if (!options.overwrite)
|
|
22686
22963
|
throw new Error(`Skill '${skillName}' already exists at ${skillPath}`);
|
|
22687
22964
|
rmSync(skillPath, { recursive: true, force: true });
|
|
@@ -22699,7 +22976,7 @@ function scaffoldPortableSkill(name, options = {}) {
|
|
|
22699
22976
|
}
|
|
22700
22977
|
function portPortableSkill(sourcePath, options = {}) {
|
|
22701
22978
|
const absoluteSource = normalize2(sourcePath);
|
|
22702
|
-
if (!
|
|
22979
|
+
if (!existsSync6(absoluteSource) || !statSync6(absoluteSource).isDirectory()) {
|
|
22703
22980
|
throw new Error(`Skill source directory not found: ${sourcePath}`);
|
|
22704
22981
|
}
|
|
22705
22982
|
const inferred = readPortableSkillManifest(absoluteSource, basename2(absoluteSource));
|
|
@@ -22711,8 +22988,8 @@ function portPortableSkill(sourcePath, options = {}) {
|
|
|
22711
22988
|
throw new Error(`${via} Importing it would shadow the official '${skillName}'. ` + `Pass --name to choose a different name, or --allow-shadow to override deliberately.`);
|
|
22712
22989
|
}
|
|
22713
22990
|
const root = getPortableSkillsRoot(options);
|
|
22714
|
-
const destination =
|
|
22715
|
-
if (
|
|
22991
|
+
const destination = join6(root, skillName);
|
|
22992
|
+
if (existsSync6(destination)) {
|
|
22716
22993
|
if (!options.overwrite)
|
|
22717
22994
|
throw new Error(`Skill '${skillName}' already exists at ${destination}`);
|
|
22718
22995
|
rmSync(destination, { recursive: true, force: true });
|
|
@@ -22740,10 +23017,10 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
22740
23017
|
const issues = [...base.issues];
|
|
22741
23018
|
const warnings = [...base.warnings];
|
|
22742
23019
|
let manifest;
|
|
22743
|
-
if (
|
|
22744
|
-
const skillJsonPath =
|
|
22745
|
-
const skillMdPath =
|
|
22746
|
-
if (!
|
|
23020
|
+
if (existsSync6(skillPath)) {
|
|
23021
|
+
const skillJsonPath = join6(skillPath, "skill.json");
|
|
23022
|
+
const skillMdPath = join6(skillPath, "SKILL.md");
|
|
23023
|
+
if (!existsSync6(skillJsonPath) && !existsSync6(skillMdPath)) {
|
|
22747
23024
|
add3(issues, "portable.manifest_missing", "Missing portable manifest: expected SKILL.md frontmatter and/or skill.json");
|
|
22748
23025
|
}
|
|
22749
23026
|
try {
|
|
@@ -22762,7 +23039,7 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
22762
23039
|
add3(issues, "portable.version_missing", "Portable manifest missing version");
|
|
22763
23040
|
}
|
|
22764
23041
|
const contractIssues = validatePortableManifestContract(manifest, {
|
|
22765
|
-
strict:
|
|
23042
|
+
strict: existsSync6(join6(skillPath, "skill.json")),
|
|
22766
23043
|
skillPath
|
|
22767
23044
|
});
|
|
22768
23045
|
for (const issue2 of contractIssues)
|
|
@@ -22798,10 +23075,10 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
22798
23075
|
add3(issues, "portable.command_entry_unsafe", `Command '${command.name}' entry '${command.entry}' must stay inside the skill directory`);
|
|
22799
23076
|
continue;
|
|
22800
23077
|
}
|
|
22801
|
-
const entryPath =
|
|
22802
|
-
if (!
|
|
23078
|
+
const entryPath = join6(skillPath, command.entry);
|
|
23079
|
+
if (!existsSync6(entryPath))
|
|
22803
23080
|
add3(issues, "portable.command_entry_missing", `Command '${command.name}' entry '${command.entry}' is missing`);
|
|
22804
|
-
else if (
|
|
23081
|
+
else if (statSync6(entryPath).isDirectory())
|
|
22805
23082
|
add3(issues, "portable.command_entry_directory", `Command '${command.name}' entry '${command.entry}' must be a file`);
|
|
22806
23083
|
}
|
|
22807
23084
|
}
|
|
@@ -22809,7 +23086,7 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
22809
23086
|
} catch (error2) {
|
|
22810
23087
|
add3(issues, "portable.manifest_invalid", error2.message);
|
|
22811
23088
|
}
|
|
22812
|
-
if (manifest?.kind !== "instruction" && !
|
|
23089
|
+
if (manifest?.kind !== "instruction" && !existsSync6(join6(skillPath, "AGENTS.md"))) {
|
|
22813
23090
|
add3(issues, "portable.agents_missing", "Missing AGENTS.md with build-out instructions for coding agents");
|
|
22814
23091
|
}
|
|
22815
23092
|
}
|
|
@@ -22841,7 +23118,7 @@ function summarizePortableSkill(skillPath, fallbackName) {
|
|
|
22841
23118
|
}
|
|
22842
23119
|
function safeIsDirectory(path) {
|
|
22843
23120
|
try {
|
|
22844
|
-
return
|
|
23121
|
+
return statSync6(path).isDirectory();
|
|
22845
23122
|
} catch {
|
|
22846
23123
|
return false;
|
|
22847
23124
|
}
|
|
@@ -23067,20 +23344,20 @@ function parseSkillMdFrontmatter(content) {
|
|
|
23067
23344
|
return Object.keys(result).length > 0 ? result : null;
|
|
23068
23345
|
}
|
|
23069
23346
|
function discoverSkillsInDir(dir, source = "custom") {
|
|
23070
|
-
if (!
|
|
23347
|
+
if (!existsSync7(dir))
|
|
23071
23348
|
return [];
|
|
23072
23349
|
const result = [];
|
|
23073
23350
|
try {
|
|
23074
|
-
const entries =
|
|
23351
|
+
const entries = readdirSync6(dir, { withFileTypes: true });
|
|
23075
23352
|
for (const entry of entries) {
|
|
23076
23353
|
if (!entry.isDirectory())
|
|
23077
23354
|
continue;
|
|
23078
|
-
const skillMdPath =
|
|
23079
|
-
if (!
|
|
23355
|
+
const skillMdPath = join7(dir, entry.name, "SKILL.md");
|
|
23356
|
+
if (!existsSync7(skillMdPath))
|
|
23080
23357
|
continue;
|
|
23081
23358
|
let content;
|
|
23082
23359
|
try {
|
|
23083
|
-
content =
|
|
23360
|
+
content = readFileSync6(skillMdPath, "utf-8");
|
|
23084
23361
|
} catch {
|
|
23085
23362
|
continue;
|
|
23086
23363
|
}
|
|
@@ -23095,6 +23372,7 @@ function discoverSkillsInDir(dir, source = "custom") {
|
|
|
23095
23372
|
category: fm.category || "Development Tools",
|
|
23096
23373
|
tags: fm.tags || [],
|
|
23097
23374
|
...fm.kind ? { kind: fm.kind } : {},
|
|
23375
|
+
...isHostedMetadataSkillDir(join7(dir, entry.name)) ? { serverOwned: true } : {},
|
|
23098
23376
|
source
|
|
23099
23377
|
});
|
|
23100
23378
|
}
|
|
@@ -23103,20 +23381,20 @@ function discoverSkillsInDir(dir, source = "custom") {
|
|
|
23103
23381
|
}
|
|
23104
23382
|
function findExtensionSkillPath(name) {
|
|
23105
23383
|
const config2 = loadConfig();
|
|
23106
|
-
if (!config2.extensionsDir || !
|
|
23384
|
+
if (!config2.extensionsDir || !existsSync7(config2.extensionsDir))
|
|
23107
23385
|
return null;
|
|
23108
23386
|
try {
|
|
23109
|
-
const entries =
|
|
23387
|
+
const entries = readdirSync6(config2.extensionsDir, { withFileTypes: true });
|
|
23110
23388
|
for (const entry of entries) {
|
|
23111
23389
|
if (!entry.isDirectory())
|
|
23112
23390
|
continue;
|
|
23113
|
-
const skillDir =
|
|
23114
|
-
const skillMdPath =
|
|
23115
|
-
if (!
|
|
23391
|
+
const skillDir = join7(config2.extensionsDir, entry.name);
|
|
23392
|
+
const skillMdPath = join7(skillDir, "SKILL.md");
|
|
23393
|
+
if (!existsSync7(skillMdPath))
|
|
23116
23394
|
continue;
|
|
23117
23395
|
let content;
|
|
23118
23396
|
try {
|
|
23119
|
-
content =
|
|
23397
|
+
content = readFileSync6(skillMdPath, "utf-8");
|
|
23120
23398
|
} catch {
|
|
23121
23399
|
continue;
|
|
23122
23400
|
}
|
|
@@ -23149,7 +23427,7 @@ function loadRegistry(cwd) {
|
|
|
23149
23427
|
const official = SKILLS.map((s) => ({ ...s, source: "official" }));
|
|
23150
23428
|
const extensions = config2.extensionsDir ? discoverSkillsInDir(config2.extensionsDir, "extension") : [];
|
|
23151
23429
|
const portableCustom = listPortableSkillMetas();
|
|
23152
|
-
const legacyCustom = discoverSkillsInDir(
|
|
23430
|
+
const legacyCustom = discoverSkillsInDir(join7(dataDir, "custom"));
|
|
23153
23431
|
const globalCustom = mergeCustomSkills([...legacyCustom, ...portableCustom]);
|
|
23154
23432
|
registryCache = mergeSkillRegistryLists(official, extensions, globalCustom);
|
|
23155
23433
|
registryCacheTime = now;
|
|
@@ -23185,13 +23463,14 @@ function mergeCustomSkills(skills) {
|
|
|
23185
23463
|
}
|
|
23186
23464
|
|
|
23187
23465
|
// src/lib/installer.ts
|
|
23188
|
-
import { existsSync as
|
|
23189
|
-
import { dirname as dirname4, join as
|
|
23466
|
+
import { existsSync as existsSync9, readFileSync as readFileSync8, rmSync as rmSync2 } from "fs";
|
|
23467
|
+
import { dirname as dirname4, join as join9 } from "path";
|
|
23190
23468
|
import { homedir as homedir2 } from "os";
|
|
23191
23469
|
import { fileURLToPath } from "url";
|
|
23192
23470
|
|
|
23193
23471
|
// src/lib/home-migration.ts
|
|
23194
23472
|
init_config();
|
|
23473
|
+
init_config();
|
|
23195
23474
|
|
|
23196
23475
|
// src/lib/utils.ts
|
|
23197
23476
|
function normalizeSkillName(name) {
|
|
@@ -23202,8 +23481,8 @@ function normalizeSkillName(name) {
|
|
|
23202
23481
|
init_config();
|
|
23203
23482
|
|
|
23204
23483
|
// src/lib/project-state.ts
|
|
23205
|
-
import { existsSync as
|
|
23206
|
-
import { join as
|
|
23484
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
|
|
23485
|
+
import { join as join8 } from "path";
|
|
23207
23486
|
var VALID_PIN_SOURCES = [
|
|
23208
23487
|
"official",
|
|
23209
23488
|
"custom",
|
|
@@ -23218,17 +23497,17 @@ var SKILLS_PROJECT_DIR = ".skills";
|
|
|
23218
23497
|
var PROJECT_CONFIG_FILE = "project.json";
|
|
23219
23498
|
var DEFAULT_EXPORT_DIR = ".skills/exports";
|
|
23220
23499
|
function getProjectStateDir(targetDir = process.cwd()) {
|
|
23221
|
-
return
|
|
23500
|
+
return join8(targetDir, SKILLS_PROJECT_DIR);
|
|
23222
23501
|
}
|
|
23223
23502
|
function getProjectConfigPath(targetDir = process.cwd()) {
|
|
23224
|
-
return
|
|
23503
|
+
return join8(getProjectStateDir(targetDir), PROJECT_CONFIG_FILE);
|
|
23225
23504
|
}
|
|
23226
23505
|
function loadProjectConfig(targetDir = process.cwd()) {
|
|
23227
23506
|
const path = getProjectConfigPath(targetDir);
|
|
23228
|
-
if (!
|
|
23507
|
+
if (!existsSync8(path))
|
|
23229
23508
|
return null;
|
|
23230
23509
|
try {
|
|
23231
|
-
return normalizeProjectConfig(JSON.parse(
|
|
23510
|
+
return normalizeProjectConfig(JSON.parse(readFileSync7(path, "utf-8")));
|
|
23232
23511
|
} catch {
|
|
23233
23512
|
return null;
|
|
23234
23513
|
}
|
|
@@ -23327,12 +23606,12 @@ var __dirname2 = dirname4(fileURLToPath(import.meta.url));
|
|
|
23327
23606
|
function findSkillsDir() {
|
|
23328
23607
|
let dir = __dirname2;
|
|
23329
23608
|
for (let i = 0;i < 5; i++) {
|
|
23330
|
-
const candidate =
|
|
23331
|
-
if (
|
|
23609
|
+
const candidate = join9(dir, "skills");
|
|
23610
|
+
if (existsSync9(candidate) && !dir.includes(".skills"))
|
|
23332
23611
|
return candidate;
|
|
23333
23612
|
dir = dirname4(dir);
|
|
23334
23613
|
}
|
|
23335
|
-
return
|
|
23614
|
+
return join9(__dirname2, "..", "skills");
|
|
23336
23615
|
}
|
|
23337
23616
|
var SKILLS_DIR = findSkillsDir();
|
|
23338
23617
|
function getSkillPath(name) {
|
|
@@ -23340,13 +23619,13 @@ function getSkillPath(name) {
|
|
|
23340
23619
|
const portable = findPortableSkill(skillName);
|
|
23341
23620
|
if (portable)
|
|
23342
23621
|
return portable.path;
|
|
23343
|
-
const legacyCustomPath =
|
|
23344
|
-
if (
|
|
23622
|
+
const legacyCustomPath = join9(getDataDir(), "custom", skillName);
|
|
23623
|
+
if (existsSync9(legacyCustomPath))
|
|
23345
23624
|
return legacyCustomPath;
|
|
23346
23625
|
const extensionPath = findExtensionSkillPath(skillName);
|
|
23347
23626
|
if (extensionPath)
|
|
23348
23627
|
return extensionPath;
|
|
23349
|
-
return
|
|
23628
|
+
return join9(SKILLS_DIR, skillName);
|
|
23350
23629
|
}
|
|
23351
23630
|
function getCanonicalSkillName(name) {
|
|
23352
23631
|
return getSkill(name)?.name ?? resolveSkillAlias(normalizeSkillSlug(name));
|
|
@@ -23355,7 +23634,7 @@ function installSkill(name, options = {}) {
|
|
|
23355
23634
|
const { targetDir = process.cwd(), overwrite = false } = options;
|
|
23356
23635
|
const canonicalName = getCanonicalSkillName(name);
|
|
23357
23636
|
const skillName = normalizeSkillName(canonicalName);
|
|
23358
|
-
if (!
|
|
23637
|
+
if (!existsSync9(getSkillPath(name))) {
|
|
23359
23638
|
const knownOfficial = Boolean(getSkill(name));
|
|
23360
23639
|
return {
|
|
23361
23640
|
skill: canonicalName,
|
|
@@ -23410,11 +23689,11 @@ function getAgentSkillsDir(agent, scope = "global", projectDir) {
|
|
|
23410
23689
|
const base = projectDir || process.cwd();
|
|
23411
23690
|
switch (agent) {
|
|
23412
23691
|
case "pi":
|
|
23413
|
-
return scope === "project" ?
|
|
23692
|
+
return scope === "project" ? join9(base, ".pi", "skills") : join9(homedir2(), ".pi", "agent", "skills");
|
|
23414
23693
|
case "opencode":
|
|
23415
|
-
return scope === "project" ?
|
|
23694
|
+
return scope === "project" ? join9(base, ".opencode", "skills") : join9(homedir2(), ".config", "opencode", "skills");
|
|
23416
23695
|
default:
|
|
23417
|
-
return scope === "project" ?
|
|
23696
|
+
return scope === "project" ? join9(base, `.${agent}`, "skills") : join9(homedir2(), `.${agent}`, "skills");
|
|
23418
23697
|
}
|
|
23419
23698
|
}
|
|
23420
23699
|
function warnMissingDependencies(name, targetDir) {
|
|
@@ -23429,11 +23708,11 @@ function warnMissingDependencies(name, targetDir) {
|
|
|
23429
23708
|
}
|
|
23430
23709
|
}
|
|
23431
23710
|
function readBundledSkillVersion(name) {
|
|
23432
|
-
const pkgPath =
|
|
23433
|
-
if (!
|
|
23711
|
+
const pkgPath = join9(getSkillPath(name), "package.json");
|
|
23712
|
+
if (!existsSync9(pkgPath))
|
|
23434
23713
|
return "unknown";
|
|
23435
23714
|
try {
|
|
23436
|
-
const pkg = JSON.parse(
|
|
23715
|
+
const pkg = JSON.parse(readFileSync8(pkgPath, "utf-8"));
|
|
23437
23716
|
return pkg.version || "unknown";
|
|
23438
23717
|
} catch {
|
|
23439
23718
|
return "unknown";
|
|
@@ -23441,16 +23720,16 @@ function readBundledSkillVersion(name) {
|
|
|
23441
23720
|
}
|
|
23442
23721
|
|
|
23443
23722
|
// src/lib/skillinfo.ts
|
|
23444
|
-
import { existsSync as
|
|
23445
|
-
import { join as
|
|
23723
|
+
import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
|
|
23724
|
+
import { join as join10 } from "path";
|
|
23446
23725
|
function isInstructionSkillDir(skillPath, meta) {
|
|
23447
23726
|
if (meta?.kind === "instruction")
|
|
23448
23727
|
return true;
|
|
23449
|
-
const skillMdPath =
|
|
23450
|
-
if (!
|
|
23728
|
+
const skillMdPath = join10(skillPath, "SKILL.md");
|
|
23729
|
+
if (!existsSync10(skillMdPath))
|
|
23451
23730
|
return false;
|
|
23452
23731
|
try {
|
|
23453
|
-
return parseSkillFrontmatter(
|
|
23732
|
+
return parseSkillFrontmatter(readFileSync9(skillMdPath, "utf-8"))?.kind === "instruction";
|
|
23454
23733
|
} catch {
|
|
23455
23734
|
return false;
|
|
23456
23735
|
}
|
|
@@ -23473,12 +23752,12 @@ var HOSTED_PROVIDER_ENV_PREFIXES = [
|
|
|
23473
23752
|
];
|
|
23474
23753
|
function getSkillDocs(name) {
|
|
23475
23754
|
const skillPath = getSkillPath(name);
|
|
23476
|
-
if (!
|
|
23755
|
+
if (!existsSync10(skillPath))
|
|
23477
23756
|
return null;
|
|
23478
23757
|
return {
|
|
23479
|
-
skillMd: readIfExists(
|
|
23480
|
-
readme: readIfExists(
|
|
23481
|
-
claudeMd: readIfExists(
|
|
23758
|
+
skillMd: readIfExists(join10(skillPath, "SKILL.md")),
|
|
23759
|
+
readme: readIfExists(join10(skillPath, "README.md")),
|
|
23760
|
+
claudeMd: readIfExists(join10(skillPath, "CLAUDE.md"))
|
|
23482
23761
|
};
|
|
23483
23762
|
}
|
|
23484
23763
|
function getSkillBestDoc(name) {
|
|
@@ -23489,11 +23768,11 @@ function getSkillBestDoc(name) {
|
|
|
23489
23768
|
}
|
|
23490
23769
|
function getSkillRequirements(name) {
|
|
23491
23770
|
const skillPath = getSkillPath(name);
|
|
23492
|
-
if (!
|
|
23771
|
+
if (!existsSync10(skillPath))
|
|
23493
23772
|
return null;
|
|
23494
23773
|
const texts = [];
|
|
23495
23774
|
for (const file of ["SKILL.md", "README.md", "CLAUDE.md", ".env.example", ".env.local.example"]) {
|
|
23496
|
-
const content = readIfExists(
|
|
23775
|
+
const content = readIfExists(join10(skillPath, file));
|
|
23497
23776
|
if (content)
|
|
23498
23777
|
texts.push(content);
|
|
23499
23778
|
}
|
|
@@ -23532,10 +23811,10 @@ function getSkillRequirements(name) {
|
|
|
23532
23811
|
const skillName = normalizeSkillName(name);
|
|
23533
23812
|
let cliCommand = `skills run ${skillName}`;
|
|
23534
23813
|
let dependencies = {};
|
|
23535
|
-
const pkgPath =
|
|
23536
|
-
if (
|
|
23814
|
+
const pkgPath = join10(skillPath, "package.json");
|
|
23815
|
+
if (existsSync10(pkgPath)) {
|
|
23537
23816
|
try {
|
|
23538
|
-
const pkg = JSON.parse(
|
|
23817
|
+
const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
|
|
23539
23818
|
dependencies = pkg.dependencies || {};
|
|
23540
23819
|
} catch {}
|
|
23541
23820
|
}
|
|
@@ -23553,7 +23832,7 @@ async function runSkill(name, args, options = {}) {
|
|
|
23553
23832
|
const meta = getSkill(name);
|
|
23554
23833
|
const canonicalName = meta?.name ?? name;
|
|
23555
23834
|
const skillPath = getSkillPath(canonicalName);
|
|
23556
|
-
if (!
|
|
23835
|
+
if (!existsSync10(skillPath)) {
|
|
23557
23836
|
return { exitCode: 1, error: `Skill '${name}' not found` };
|
|
23558
23837
|
}
|
|
23559
23838
|
if (isInstructionSkillDir(skillPath, meta)) {
|
|
@@ -23562,13 +23841,13 @@ async function runSkill(name, args, options = {}) {
|
|
|
23562
23841
|
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'.`
|
|
23563
23842
|
};
|
|
23564
23843
|
}
|
|
23565
|
-
const pkgPath =
|
|
23566
|
-
if (!
|
|
23844
|
+
const pkgPath = join10(skillPath, "package.json");
|
|
23845
|
+
if (!existsSync10(pkgPath)) {
|
|
23567
23846
|
return { exitCode: 1, error: `No package.json in skill '${name}'` };
|
|
23568
23847
|
}
|
|
23569
23848
|
let entryPoint;
|
|
23570
23849
|
try {
|
|
23571
|
-
const pkg = JSON.parse(
|
|
23850
|
+
const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
|
|
23572
23851
|
if (pkg.bin) {
|
|
23573
23852
|
const binValues = Object.values(pkg.bin);
|
|
23574
23853
|
entryPoint = binValues[0];
|
|
@@ -23582,12 +23861,12 @@ async function runSkill(name, args, options = {}) {
|
|
|
23582
23861
|
} catch {
|
|
23583
23862
|
return { exitCode: 1, error: `Failed to parse package.json for skill '${name}'` };
|
|
23584
23863
|
}
|
|
23585
|
-
const entryPath =
|
|
23586
|
-
if (!
|
|
23864
|
+
const entryPath = join10(skillPath, entryPoint);
|
|
23865
|
+
if (!existsSync10(entryPath)) {
|
|
23587
23866
|
return { exitCode: 1, error: `Entry point '${entryPoint}' not found in skill '${name}'` };
|
|
23588
23867
|
}
|
|
23589
|
-
const nodeModules =
|
|
23590
|
-
if (!
|
|
23868
|
+
const nodeModules = join10(skillPath, "node_modules");
|
|
23869
|
+
if (!existsSync10(nodeModules)) {
|
|
23591
23870
|
const install = Bun.spawn(["bun", "install", "--no-save"], {
|
|
23592
23871
|
cwd: skillPath,
|
|
23593
23872
|
stdout: "pipe",
|
|
@@ -23614,15 +23893,15 @@ async function runSkill(name, args, options = {}) {
|
|
|
23614
23893
|
return { exitCode };
|
|
23615
23894
|
}
|
|
23616
23895
|
function detectProjectSkills(cwd = process.cwd()) {
|
|
23617
|
-
const pkgPath =
|
|
23618
|
-
if (!
|
|
23896
|
+
const pkgPath = join10(cwd, "package.json");
|
|
23897
|
+
if (!existsSync10(pkgPath)) {
|
|
23619
23898
|
const alwaysRecommend = ["market-research-report", "repo-onboarding-report", "blog-article"];
|
|
23620
23899
|
const recommended2 = alwaysRecommend.map((name) => loadRegistry().find((s) => s.name === name)).filter((s) => s !== undefined);
|
|
23621
23900
|
return { detected: [], recommended: recommended2 };
|
|
23622
23901
|
}
|
|
23623
23902
|
let pkg;
|
|
23624
23903
|
try {
|
|
23625
|
-
pkg = JSON.parse(
|
|
23904
|
+
pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
|
|
23626
23905
|
} catch {
|
|
23627
23906
|
const alwaysRecommend = ["market-research-report", "repo-onboarding-report", "blog-article"];
|
|
23628
23907
|
const recommended2 = alwaysRecommend.map((name) => loadRegistry().find((s) => s.name === name)).filter((s) => s !== undefined);
|
|
@@ -23711,8 +23990,8 @@ function extractEnvVars(text) {
|
|
|
23711
23990
|
}
|
|
23712
23991
|
function readIfExists(path) {
|
|
23713
23992
|
try {
|
|
23714
|
-
if (
|
|
23715
|
-
return
|
|
23993
|
+
if (existsSync10(path)) {
|
|
23994
|
+
return readFileSync9(path, "utf-8");
|
|
23716
23995
|
}
|
|
23717
23996
|
} catch {}
|
|
23718
23997
|
return null;
|
|
@@ -25294,25 +25573,25 @@ function registerDiscoveryTools(server) {
|
|
|
25294
25573
|
}
|
|
25295
25574
|
|
|
25296
25575
|
// src/mcp/operation-tools.ts
|
|
25297
|
-
import { existsSync as
|
|
25298
|
-
import { join as
|
|
25576
|
+
import { existsSync as existsSync13, readdirSync as readdirSync8, statSync as statSync8 } from "fs";
|
|
25577
|
+
import { join as join13 } from "path";
|
|
25299
25578
|
|
|
25300
25579
|
// src/lib/run-state.ts
|
|
25301
25580
|
import { createHash as createHash2, randomBytes } from "crypto";
|
|
25302
|
-
import { existsSync as
|
|
25303
|
-
import { extname, join as
|
|
25581
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync5, readFileSync as readFileSync10, readdirSync as readdirSync7, statSync as statSync7, writeFileSync as writeFileSync5 } from "fs";
|
|
25582
|
+
import { extname, join as join11, relative as relative2 } from "path";
|
|
25304
25583
|
function createSkillRun(params, targetDir = process.cwd()) {
|
|
25305
25584
|
const now = new Date;
|
|
25306
25585
|
const id = createRunId(now);
|
|
25307
25586
|
const day = now.toISOString().slice(0, 10);
|
|
25308
25587
|
const skillName = normalizeSkillName(params.skill);
|
|
25309
25588
|
const root = getProjectStateDir(targetDir);
|
|
25310
|
-
const runDir =
|
|
25311
|
-
const logsDir =
|
|
25312
|
-
const exportDir =
|
|
25589
|
+
const runDir = join11(root, "runs", day, id);
|
|
25590
|
+
const logsDir = join11(runDir, "logs");
|
|
25591
|
+
const exportDir = join11(root, "exports", skillName, id);
|
|
25313
25592
|
mkdirSync5(logsDir, { recursive: true });
|
|
25314
25593
|
mkdirSync5(exportDir, { recursive: true });
|
|
25315
|
-
mkdirSync5(
|
|
25594
|
+
mkdirSync5(join11(root, "tmp"), { recursive: true });
|
|
25316
25595
|
const record3 = {
|
|
25317
25596
|
id,
|
|
25318
25597
|
skill: skillName,
|
|
@@ -25363,22 +25642,22 @@ function updateSkillRun(context, patch) {
|
|
|
25363
25642
|
return context.record;
|
|
25364
25643
|
}
|
|
25365
25644
|
function writeRunLogs(context, stdout = "", stderr = "") {
|
|
25366
|
-
writeFileSync5(
|
|
25367
|
-
writeFileSync5(
|
|
25645
|
+
writeFileSync5(join11(context.logsDir, "stdout.log"), stdout);
|
|
25646
|
+
writeFileSync5(join11(context.logsDir, "stderr.log"), stderr);
|
|
25368
25647
|
}
|
|
25369
25648
|
function appendRunEvent(context, event, data = {}) {
|
|
25370
25649
|
const line = JSON.stringify({ ts: new Date().toISOString(), event, ...data }) + `
|
|
25371
25650
|
`;
|
|
25372
|
-
const path =
|
|
25373
|
-
const previous =
|
|
25651
|
+
const path = join11(context.runDir, "events.ndjson");
|
|
25652
|
+
const previous = existsSync11(path) ? readFileSync10(path, "utf-8") : "";
|
|
25374
25653
|
writeFileSync5(path, previous + line);
|
|
25375
25654
|
}
|
|
25376
25655
|
function findSkillRun(runId, targetDir = process.cwd()) {
|
|
25377
|
-
const runsRoot =
|
|
25378
|
-
if (!
|
|
25656
|
+
const runsRoot = join11(getProjectStateDir(targetDir), "runs");
|
|
25657
|
+
if (!existsSync11(runsRoot))
|
|
25379
25658
|
return null;
|
|
25380
|
-
for (const day of
|
|
25381
|
-
const record3 = readRunRecord(
|
|
25659
|
+
for (const day of readdirSync7(runsRoot)) {
|
|
25660
|
+
const record3 = readRunRecord(join11(runsRoot, day, runId));
|
|
25382
25661
|
if (record3)
|
|
25383
25662
|
return record3;
|
|
25384
25663
|
}
|
|
@@ -25395,20 +25674,20 @@ function skillRunEnv(context) {
|
|
|
25395
25674
|
};
|
|
25396
25675
|
}
|
|
25397
25676
|
function writeRunRecord(context) {
|
|
25398
|
-
writeFileSync5(
|
|
25677
|
+
writeFileSync5(join11(context.runDir, "run.json"), JSON.stringify(context.record, null, 2) + `
|
|
25399
25678
|
`);
|
|
25400
25679
|
}
|
|
25401
25680
|
function writeArtifactsManifest(context, artifacts) {
|
|
25402
|
-
writeFileSync5(
|
|
25681
|
+
writeFileSync5(join11(context.runDir, "artifacts.json"), JSON.stringify({ runId: context.record.id, artifacts }, null, 2) + `
|
|
25403
25682
|
`);
|
|
25404
25683
|
}
|
|
25405
25684
|
function collectRunArtifacts(context) {
|
|
25406
|
-
if (!
|
|
25685
|
+
if (!existsSync11(context.exportDir))
|
|
25407
25686
|
return [];
|
|
25408
25687
|
const artifacts = [];
|
|
25409
25688
|
for (const path of walkFiles(context.exportDir)) {
|
|
25410
|
-
const stat =
|
|
25411
|
-
const bytes =
|
|
25689
|
+
const stat = statSync7(path);
|
|
25690
|
+
const bytes = readFileSync10(path);
|
|
25412
25691
|
artifacts.push({
|
|
25413
25692
|
path: toProjectRelative(context.targetDir, path),
|
|
25414
25693
|
mime: mimeForPath(path),
|
|
@@ -25419,20 +25698,20 @@ function collectRunArtifacts(context) {
|
|
|
25419
25698
|
return artifacts.sort((a, b) => a.path.localeCompare(b.path));
|
|
25420
25699
|
}
|
|
25421
25700
|
function readRunRecord(runDir) {
|
|
25422
|
-
const path =
|
|
25423
|
-
if (!
|
|
25701
|
+
const path = join11(runDir, "run.json");
|
|
25702
|
+
if (!existsSync11(path))
|
|
25424
25703
|
return null;
|
|
25425
25704
|
try {
|
|
25426
|
-
return JSON.parse(
|
|
25705
|
+
return JSON.parse(readFileSync10(path, "utf-8"));
|
|
25427
25706
|
} catch {
|
|
25428
25707
|
return null;
|
|
25429
25708
|
}
|
|
25430
25709
|
}
|
|
25431
25710
|
function walkFiles(dir) {
|
|
25432
25711
|
const files = [];
|
|
25433
|
-
for (const entry of
|
|
25434
|
-
const full =
|
|
25435
|
-
if (
|
|
25712
|
+
for (const entry of readdirSync7(dir)) {
|
|
25713
|
+
const full = join11(dir, entry);
|
|
25714
|
+
if (statSync7(full).isDirectory())
|
|
25436
25715
|
files.push(...walkFiles(full));
|
|
25437
25716
|
else
|
|
25438
25717
|
files.push(full);
|
|
@@ -25476,6 +25755,34 @@ function mimeForPath(path) {
|
|
|
25476
25755
|
return "application/octet-stream";
|
|
25477
25756
|
}
|
|
25478
25757
|
}
|
|
25758
|
+
// src/lib/run-routing.ts
|
|
25759
|
+
init_api_url();
|
|
25760
|
+
init_auth_store();
|
|
25761
|
+
function isServerOwnedSkill(skill) {
|
|
25762
|
+
return skill.serverOwned === true;
|
|
25763
|
+
}
|
|
25764
|
+
function resolveRunRouting(skill, apiKey, apiUrl) {
|
|
25765
|
+
if (!isServerOwnedSkill(skill))
|
|
25766
|
+
return { route: "local" };
|
|
25767
|
+
if (!apiUrl) {
|
|
25768
|
+
return {
|
|
25769
|
+
route: "error",
|
|
25770
|
+
code: "REMOTE_REQUIRES_ORIGIN",
|
|
25771
|
+
error: `${skill.name} is a server-owned skill. Point the CLI at a Skills API: ` + `skills setup --api-url <url> (or export SKILLS_API_URL)`
|
|
25772
|
+
};
|
|
25773
|
+
}
|
|
25774
|
+
if (!apiKey) {
|
|
25775
|
+
return {
|
|
25776
|
+
route: "error",
|
|
25777
|
+
code: "REMOTE_REQUIRES_CREDENTIAL",
|
|
25778
|
+
error: `${skill.name} is a server-owned skill. Run: skills auth login`
|
|
25779
|
+
};
|
|
25780
|
+
}
|
|
25781
|
+
return { route: "remote", apiKey };
|
|
25782
|
+
}
|
|
25783
|
+
function resolveConfiguredRunRouting(skill) {
|
|
25784
|
+
return resolveRunRouting(skill, getApiKey(), resolveApiUrl());
|
|
25785
|
+
}
|
|
25479
25786
|
|
|
25480
25787
|
// src/mcp/operation-tools.ts
|
|
25481
25788
|
function registerOperationTools(server) {
|
|
@@ -25685,7 +25992,6 @@ function registerOperationTools(server) {
|
|
|
25685
25992
|
if (!skill) {
|
|
25686
25993
|
return mcpError("SKILL_NOT_FOUND", `Skill '${name}' not found`, findSimilarSkills(name));
|
|
25687
25994
|
}
|
|
25688
|
-
const { getApiKey: getApiKey2 } = await Promise.resolve().then(() => (init_auth_store(), exports_auth_store));
|
|
25689
25995
|
const {
|
|
25690
25996
|
ARTICLE_GENERATION_SLUG: ARTICLE_GENERATION_SLUG2,
|
|
25691
25997
|
validateBlogArticleRunOptions: validateBlogArticleRunOptions2
|
|
@@ -25699,24 +26005,24 @@ function registerOperationTools(server) {
|
|
|
25699
26005
|
return mcpError("INVALID_BLOG_ARTICLE_OPTIONS", validation.errors.join(" "));
|
|
25700
26006
|
}
|
|
25701
26007
|
}
|
|
25702
|
-
const
|
|
25703
|
-
const hostedRuntime = false;
|
|
26008
|
+
const routing = resolveConfiguredRunRouting(skill);
|
|
25704
26009
|
const runContext = createSkillRun({
|
|
25705
26010
|
skill: skillName,
|
|
25706
26011
|
args: runArgs,
|
|
25707
|
-
remote:
|
|
26012
|
+
remote: routing.route === "remote"
|
|
25708
26013
|
});
|
|
25709
|
-
if (
|
|
25710
|
-
const error2 =
|
|
26014
|
+
if (routing.route === "error") {
|
|
26015
|
+
const error2 = routing.error;
|
|
25711
26016
|
writeRunLogs(runContext, "", error2 + `
|
|
25712
26017
|
`);
|
|
25713
26018
|
const run = completeSkillRun(runContext, { status: "failed", error: error2 });
|
|
25714
|
-
|
|
26019
|
+
const suggestions = routing.code === "REMOTE_REQUIRES_ORIGIN" ? ["skills setup --api-url <url>", "skills auth login"] : ["skills auth login"];
|
|
26020
|
+
return mcpError(routing.code, `${error2}. Local run metadata: ${run.paths.runDir}/run.json`, suggestions);
|
|
25715
26021
|
}
|
|
25716
|
-
if (
|
|
26022
|
+
if (routing.route === "remote") {
|
|
25717
26023
|
try {
|
|
25718
26024
|
const { RemoteSkillsClient: RemoteSkillsClient2 } = await Promise.resolve().then(() => (init_remote_client(), exports_remote_client));
|
|
25719
|
-
const client = new RemoteSkillsClient2(apiKey);
|
|
26025
|
+
const client = new RemoteSkillsClient2(routing.apiKey);
|
|
25720
26026
|
const run = await client.submitRun(skillName, runInput, runArgs);
|
|
25721
26027
|
if (run.error) {
|
|
25722
26028
|
writeRunLogs(runContext, "", String(run.error) + `
|
|
@@ -25875,13 +26181,13 @@ function registerOperationTools(server) {
|
|
|
25875
26181
|
const agents = [];
|
|
25876
26182
|
for (const agent of AGENT_TARGETS) {
|
|
25877
26183
|
const agentSkillsPath = getAgentSkillsDir(agent, "global");
|
|
25878
|
-
const exists =
|
|
26184
|
+
const exists = existsSync13(agentSkillsPath);
|
|
25879
26185
|
let skillCount = 0;
|
|
25880
26186
|
if (exists) {
|
|
25881
26187
|
try {
|
|
25882
|
-
skillCount =
|
|
25883
|
-
const full =
|
|
25884
|
-
return !f.startsWith(".") &&
|
|
26188
|
+
skillCount = readdirSync8(agentSkillsPath).filter((f) => {
|
|
26189
|
+
const full = join13(agentSkillsPath, f);
|
|
26190
|
+
return !f.startsWith(".") && statSync8(full).isDirectory();
|
|
25885
26191
|
}).length;
|
|
25886
26192
|
} catch {}
|
|
25887
26193
|
}
|
|
@@ -25927,16 +26233,16 @@ function compactRunToolPayload(payload, detailHint) {
|
|
|
25927
26233
|
|
|
25928
26234
|
// src/lib/feedback.ts
|
|
25929
26235
|
init_config();
|
|
25930
|
-
import { existsSync as
|
|
25931
|
-
import { dirname as
|
|
26236
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync7 } from "fs";
|
|
26237
|
+
import { dirname as dirname6, join as join14 } from "path";
|
|
25932
26238
|
import { Database } from "bun:sqlite";
|
|
25933
26239
|
function getFeedbackDbPath() {
|
|
25934
|
-
return
|
|
26240
|
+
return join14(getDataDir(), "skills.db");
|
|
25935
26241
|
}
|
|
25936
26242
|
function getFeedbackDb() {
|
|
25937
26243
|
const dbPath = getFeedbackDbPath();
|
|
25938
|
-
const dir =
|
|
25939
|
-
if (!
|
|
26244
|
+
const dir = dirname6(dbPath);
|
|
26245
|
+
if (!existsSync14(dir))
|
|
25940
26246
|
mkdirSync7(dir, { recursive: true });
|
|
25941
26247
|
const db = new Database(dbPath);
|
|
25942
26248
|
db.exec("PRAGMA journal_mode = WAL");
|
|
@@ -26095,24 +26401,24 @@ function registerResourceMetaTools(server) {
|
|
|
26095
26401
|
}
|
|
26096
26402
|
|
|
26097
26403
|
// src/lib/scheduler.ts
|
|
26098
|
-
import { existsSync as
|
|
26099
|
-
import { join as
|
|
26404
|
+
import { existsSync as existsSync15, readFileSync as readFileSync12, writeFileSync as writeFileSync7, mkdirSync as mkdirSync8 } from "fs";
|
|
26405
|
+
import { join as join15 } from "path";
|
|
26100
26406
|
function getSchedulesPath(targetDir = process.cwd()) {
|
|
26101
|
-
return
|
|
26407
|
+
return join15(targetDir, ".skills", "schedules.json");
|
|
26102
26408
|
}
|
|
26103
26409
|
function loadSchedules(targetDir = process.cwd()) {
|
|
26104
26410
|
const path = getSchedulesPath(targetDir);
|
|
26105
|
-
if (
|
|
26411
|
+
if (existsSync15(path)) {
|
|
26106
26412
|
try {
|
|
26107
|
-
return JSON.parse(
|
|
26413
|
+
return JSON.parse(readFileSync12(path, "utf-8"));
|
|
26108
26414
|
} catch {}
|
|
26109
26415
|
}
|
|
26110
26416
|
return { version: 1, schedules: [] };
|
|
26111
26417
|
}
|
|
26112
26418
|
function saveSchedules(data, targetDir = process.cwd()) {
|
|
26113
26419
|
const path = getSchedulesPath(targetDir);
|
|
26114
|
-
const dir =
|
|
26115
|
-
if (!
|
|
26420
|
+
const dir = join15(targetDir, ".skills");
|
|
26421
|
+
if (!existsSync15(dir))
|
|
26116
26422
|
mkdirSync8(dir, { recursive: true });
|
|
26117
26423
|
writeFileSync7(path, JSON.stringify(data, null, 2));
|
|
26118
26424
|
}
|
|
@@ -26375,14 +26681,14 @@ function registerScheduleTools(server) {
|
|
|
26375
26681
|
init_config();
|
|
26376
26682
|
import { createHash as createHash3, createHmac } from "crypto";
|
|
26377
26683
|
import {
|
|
26378
|
-
existsSync as
|
|
26684
|
+
existsSync as existsSync16,
|
|
26379
26685
|
mkdirSync as mkdirSync9,
|
|
26380
|
-
readFileSync as
|
|
26381
|
-
readdirSync as
|
|
26382
|
-
statSync as
|
|
26686
|
+
readFileSync as readFileSync13,
|
|
26687
|
+
readdirSync as readdirSync9,
|
|
26688
|
+
statSync as statSync9,
|
|
26383
26689
|
writeFileSync as writeFileSync8
|
|
26384
26690
|
} from "fs";
|
|
26385
|
-
import { dirname as
|
|
26691
|
+
import { dirname as dirname7, join as join16, normalize as normalize3, relative as relative3, sep as sep2 } from "path";
|
|
26386
26692
|
init_retired_settings();
|
|
26387
26693
|
var SKILLS_STORAGE_TABLES = [
|
|
26388
26694
|
"skills_sync_records",
|
|
@@ -26450,7 +26756,7 @@ function getSkillsNativeStorageStatus(options = {}) {
|
|
|
26450
26756
|
const s3BucketEnv = readStorageEnv(env, "s3Bucket");
|
|
26451
26757
|
const targetDir = options.targetDir ?? process.cwd();
|
|
26452
26758
|
return {
|
|
26453
|
-
package: "
|
|
26759
|
+
package: "skills",
|
|
26454
26760
|
tables: [...SKILLS_STORAGE_TABLES],
|
|
26455
26761
|
env: {
|
|
26456
26762
|
databaseUrl: SKILLS_NATIVE_STORAGE_ENV.databaseUrl,
|
|
@@ -26459,7 +26765,7 @@ function getSkillsNativeStorageStatus(options = {}) {
|
|
|
26459
26765
|
local: {
|
|
26460
26766
|
dataDir: getDataDir(),
|
|
26461
26767
|
projectStateDir: getProjectStateDir(targetDir),
|
|
26462
|
-
feedbackDbPath:
|
|
26768
|
+
feedbackDbPath: join16(getDataDir(), "skills.db")
|
|
26463
26769
|
},
|
|
26464
26770
|
remote: {
|
|
26465
26771
|
databaseConfigured: Boolean(config2.databaseUrl),
|
|
@@ -26479,9 +26785,9 @@ function getStorageStatus(options = {}) {
|
|
|
26479
26785
|
function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
|
|
26480
26786
|
const projectStateDir = getProjectStateDir(targetDir);
|
|
26481
26787
|
const files = [];
|
|
26482
|
-
if (
|
|
26788
|
+
if (existsSync16(projectStateDir)) {
|
|
26483
26789
|
for (const filePath of walkFiles2(projectStateDir)) {
|
|
26484
|
-
const bytes =
|
|
26790
|
+
const bytes = readFileSync13(filePath);
|
|
26485
26791
|
const relativePath = toPosix(relative3(targetDir, filePath));
|
|
26486
26792
|
files.push({
|
|
26487
26793
|
path: relativePath,
|
|
@@ -26562,9 +26868,9 @@ function parsePositiveInteger(value) {
|
|
|
26562
26868
|
}
|
|
26563
26869
|
function walkFiles2(dir) {
|
|
26564
26870
|
const files = [];
|
|
26565
|
-
for (const entry of
|
|
26566
|
-
const full =
|
|
26567
|
-
const stats =
|
|
26871
|
+
for (const entry of readdirSync9(dir)) {
|
|
26872
|
+
const full = join16(dir, entry);
|
|
26873
|
+
const stats = statSync9(full);
|
|
26568
26874
|
if (stats.isDirectory())
|
|
26569
26875
|
files.push(...walkFiles2(full));
|
|
26570
26876
|
else
|
|
@@ -26603,7 +26909,7 @@ function registerStorageTools(server) {
|
|
|
26603
26909
|
const snapshot = exportSkillsLocalSnapshot(targetDir, { includeFileContents: false });
|
|
26604
26910
|
const s3Plan = config2.s3Bucket ? planSkillsS3SnapshotUpload(snapshot, { prefix: config2.s3Prefix }) : [];
|
|
26605
26911
|
return mcpJson({
|
|
26606
|
-
package: "
|
|
26912
|
+
package: "skills",
|
|
26607
26913
|
noNetwork: true,
|
|
26608
26914
|
databaseConfigured: Boolean(config2.databaseUrl),
|
|
26609
26915
|
s3Configured: Boolean(config2.s3Bucket),
|