@hasna/skills 0.1.71 → 0.1.72
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/index.js +639 -858
- package/bin/mcp.js +342 -232
- package/bin/migrate.js +149 -40
- package/bin/server.js +212 -100
- package/bin/worker.js +151 -45
- package/dist/index.js +461 -331
- package/dist/lib/app-home.d.ts +85 -0
- package/dist/lib/config.d.ts +9 -10
- package/dist/lib/station-hydrate.d.ts +2 -0
- package/dist/lib/station-snapshot.d.ts +1 -1
- package/dist/sdk/index.js +518 -630
- package/dist/storage.js +155 -43
- package/package.json +2 -1
package/bin/index.js
CHANGED
|
@@ -36860,7 +36860,7 @@ var package_default;
|
|
|
36860
36860
|
var init_package = __esm(() => {
|
|
36861
36861
|
package_default = {
|
|
36862
36862
|
name: "@hasna/skills",
|
|
36863
|
-
version: "0.1.
|
|
36863
|
+
version: "0.1.72",
|
|
36864
36864
|
description: "Skills library for AI coding agents",
|
|
36865
36865
|
type: "module",
|
|
36866
36866
|
bin: {
|
|
@@ -36952,6 +36952,7 @@ var init_package = __esm(() => {
|
|
|
36952
36952
|
"@aws-sdk/client-ecs": "^3.1079.0",
|
|
36953
36953
|
"@aws-sdk/client-s3": "^3.1079.0",
|
|
36954
36954
|
"@hasna/events": "0.1.16",
|
|
36955
|
+
"@hasna/paths": "0.1.0",
|
|
36955
36956
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
36956
36957
|
chalk: "^5.3.0",
|
|
36957
36958
|
commander: "^12.1.0",
|
|
@@ -37917,10 +37918,127 @@ var init_retired_settings = __esm(() => {
|
|
|
37917
37918
|
};
|
|
37918
37919
|
});
|
|
37919
37920
|
|
|
37920
|
-
//
|
|
37921
|
-
import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync, mkdirSync, copyFileSync, readdirSync, statSync } from "fs";
|
|
37922
|
-
import { join as join3, dirname } from "path";
|
|
37921
|
+
// ../../node_modules/.bun/@hasna+paths@0.1.0/node_modules/@hasna/paths/dist/index.js
|
|
37923
37922
|
import { homedir as homedir2 } from "os";
|
|
37923
|
+
import { join as join3 } from "path";
|
|
37924
|
+
function assertApp(app) {
|
|
37925
|
+
if (typeof app !== "string" || app.length === 0) {
|
|
37926
|
+
throw new TypeError("paths: app must be a non-empty string");
|
|
37927
|
+
}
|
|
37928
|
+
if (!APP_SLUG_RE.test(app)) {
|
|
37929
|
+
throw new TypeError(`paths: invalid app slug "${app}" \u2014 expected lowercase kebab-case ([a-z0-9]+(-[a-z0-9]+)*)`);
|
|
37930
|
+
}
|
|
37931
|
+
}
|
|
37932
|
+
function envOf(options) {
|
|
37933
|
+
return options.env ?? process.env;
|
|
37934
|
+
}
|
|
37935
|
+
function envValue(options, kind) {
|
|
37936
|
+
const value = envOf(options)[KIND_ENV[kind]];
|
|
37937
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
37938
|
+
}
|
|
37939
|
+
function isMacOS(platform2) {
|
|
37940
|
+
return platform2 === "darwin";
|
|
37941
|
+
}
|
|
37942
|
+
function baseDir(kind, options) {
|
|
37943
|
+
const override = envValue(options, kind);
|
|
37944
|
+
if (override)
|
|
37945
|
+
return override;
|
|
37946
|
+
const home = options.home ?? homedir2();
|
|
37947
|
+
const platform2 = options.platform ?? process.platform;
|
|
37948
|
+
if (isMacOS(platform2)) {
|
|
37949
|
+
switch (kind) {
|
|
37950
|
+
case "config":
|
|
37951
|
+
case "data":
|
|
37952
|
+
return join3(home, "Library", "Application Support", "Hasna");
|
|
37953
|
+
case "cache":
|
|
37954
|
+
return join3(home, "Library", "Caches", "Hasna");
|
|
37955
|
+
case "state":
|
|
37956
|
+
return join3(home, "Library", "Logs", "Hasna");
|
|
37957
|
+
}
|
|
37958
|
+
}
|
|
37959
|
+
switch (kind) {
|
|
37960
|
+
case "config":
|
|
37961
|
+
return join3(home, ".config", "hasna");
|
|
37962
|
+
case "data":
|
|
37963
|
+
return join3(home, ".local", "share", "hasna");
|
|
37964
|
+
case "state":
|
|
37965
|
+
return join3(home, ".local", "state", "hasna");
|
|
37966
|
+
case "cache":
|
|
37967
|
+
return join3(home, ".cache", "hasna");
|
|
37968
|
+
}
|
|
37969
|
+
}
|
|
37970
|
+
function resolvePath(kind, options) {
|
|
37971
|
+
assertApp(options.app);
|
|
37972
|
+
const appSegment = options.internal === true ? join3("internal", options.app) : options.app;
|
|
37973
|
+
return join3(baseDir(kind, options), appSegment);
|
|
37974
|
+
}
|
|
37975
|
+
function dataDir(options) {
|
|
37976
|
+
return resolvePath("data", options);
|
|
37977
|
+
}
|
|
37978
|
+
var KIND_ENV, APP_SLUG_RE;
|
|
37979
|
+
var init_dist = __esm(() => {
|
|
37980
|
+
KIND_ENV = {
|
|
37981
|
+
config: "HASNA_CONFIG_HOME",
|
|
37982
|
+
data: "HASNA_DATA_HOME",
|
|
37983
|
+
state: "HASNA_STATE_HOME",
|
|
37984
|
+
cache: "HASNA_CACHE_HOME"
|
|
37985
|
+
};
|
|
37986
|
+
APP_SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
37987
|
+
});
|
|
37988
|
+
|
|
37989
|
+
// src/lib/app-home.ts
|
|
37990
|
+
import { existsSync as existsSync3 } from "fs";
|
|
37991
|
+
import { homedir as homedir3 } from "os";
|
|
37992
|
+
import { join as join4, resolve } from "path";
|
|
37993
|
+
function effectiveHome() {
|
|
37994
|
+
return process.env["HOME"] || process.env["USERPROFILE"] || homedir3() || "/tmp";
|
|
37995
|
+
}
|
|
37996
|
+
function legacyDataRoot() {
|
|
37997
|
+
return join4(effectiveHome(), ".hasna", "skills");
|
|
37998
|
+
}
|
|
37999
|
+
function resolverDataRoot(home = effectiveHome()) {
|
|
38000
|
+
return dataDir({ app: "skills", home });
|
|
38001
|
+
}
|
|
38002
|
+
function adoptResolverDataRoot(resolved, env3 = process.env) {
|
|
38003
|
+
const dataOverride = env3.HASNA_DATA_HOME;
|
|
38004
|
+
if (typeof dataOverride === "string" && dataOverride.trim().length > 0)
|
|
38005
|
+
return true;
|
|
38006
|
+
return existsSync3(join4(resolved, DEFAULT_SQLITE_FILENAME)) || existsSync3(join4(resolved, GLOBAL_CONFIG_FILENAME));
|
|
38007
|
+
}
|
|
38008
|
+
function exactDataRoot() {
|
|
38009
|
+
for (const key of [DATA_DIR_ENV, HASNA_SKILLS_HOME_ENV, SKILLS_HOME_ENV]) {
|
|
38010
|
+
const dir = process.env[key]?.trim();
|
|
38011
|
+
if (dir)
|
|
38012
|
+
return resolve(dir);
|
|
38013
|
+
}
|
|
38014
|
+
return;
|
|
38015
|
+
}
|
|
38016
|
+
function hasExactOverride(env3 = process.env) {
|
|
38017
|
+
return Boolean(env3[DATA_DIR_ENV]?.trim()) || Boolean(env3[HASNA_SKILLS_HOME_ENV]?.trim()) || Boolean(env3[SKILLS_HOME_ENV]?.trim());
|
|
38018
|
+
}
|
|
38019
|
+
function hasOperatorOverride(env3 = process.env) {
|
|
38020
|
+
return hasExactOverride(env3) || Boolean(env3.HASNA_DATA_HOME?.trim());
|
|
38021
|
+
}
|
|
38022
|
+
function getDataRoot() {
|
|
38023
|
+
const exact = exactDataRoot();
|
|
38024
|
+
if (exact)
|
|
38025
|
+
return exact;
|
|
38026
|
+
const resolved = resolverDataRoot();
|
|
38027
|
+
return adoptResolverDataRoot(resolved) ? resolve(resolved) : resolve(legacyDataRoot());
|
|
38028
|
+
}
|
|
38029
|
+
function skillsDataRootForHome(home) {
|
|
38030
|
+
const resolved = resolverDataRoot(home);
|
|
38031
|
+
return adoptResolverDataRoot(resolved) ? resolve(resolved) : resolve(join4(home, ".hasna", "skills"));
|
|
38032
|
+
}
|
|
38033
|
+
var 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";
|
|
38034
|
+
var init_app_home = __esm(() => {
|
|
38035
|
+
init_dist();
|
|
38036
|
+
});
|
|
38037
|
+
|
|
38038
|
+
// src/lib/config.ts
|
|
38039
|
+
import { existsSync as existsSync4, readFileSync as readFileSync2, writeFileSync, mkdirSync, copyFileSync, readdirSync, statSync } from "fs";
|
|
38040
|
+
import { join as join5, dirname } from "path";
|
|
38041
|
+
import { homedir as homedir4 } from "os";
|
|
37924
38042
|
function validKeys() {
|
|
37925
38043
|
return [...Object.keys(ENUM_KEYS), ...STRING_KEYS];
|
|
37926
38044
|
}
|
|
@@ -37928,19 +38046,19 @@ function allowedValues(key) {
|
|
|
37928
38046
|
return ENUM_KEYS[key];
|
|
37929
38047
|
}
|
|
37930
38048
|
function mergeDirectoryContents(sourceDir, targetDir) {
|
|
37931
|
-
if (!
|
|
38049
|
+
if (!existsSync4(sourceDir))
|
|
37932
38050
|
return;
|
|
37933
38051
|
mkdirSync(targetDir, { recursive: true });
|
|
37934
38052
|
for (const entry of readdirSync(sourceDir)) {
|
|
37935
|
-
const sourcePath =
|
|
37936
|
-
const targetPath =
|
|
38053
|
+
const sourcePath = join5(sourceDir, entry);
|
|
38054
|
+
const targetPath = join5(targetDir, entry);
|
|
37937
38055
|
try {
|
|
37938
38056
|
const sourceStat = statSync(sourcePath);
|
|
37939
38057
|
if (sourceStat.isDirectory()) {
|
|
37940
38058
|
mergeDirectoryContents(sourcePath, targetPath);
|
|
37941
38059
|
continue;
|
|
37942
38060
|
}
|
|
37943
|
-
if (!
|
|
38061
|
+
if (!existsSync4(targetPath))
|
|
37944
38062
|
copyFileSync(sourcePath, targetPath);
|
|
37945
38063
|
} catch {}
|
|
37946
38064
|
}
|
|
@@ -37966,48 +38084,42 @@ function normalizeConfigValue(key, value) {
|
|
|
37966
38084
|
return;
|
|
37967
38085
|
}
|
|
37968
38086
|
function isOwnerLayoutMigrated(appDir) {
|
|
37969
|
-
return
|
|
38087
|
+
return existsSync4(join5(appDir, SKILLS_CACHE_DIRNAME, LAYOUT_MIGRATION_RECORD));
|
|
37970
38088
|
}
|
|
37971
38089
|
function getDataDir() {
|
|
37972
|
-
const
|
|
37973
|
-
if (override) {
|
|
37974
|
-
try {
|
|
37975
|
-
mkdirSync(override, { recursive: true });
|
|
37976
|
-
} catch {}
|
|
37977
|
-
return override;
|
|
37978
|
-
}
|
|
37979
|
-
const home = process.env["HOME"] || process.env["USERPROFILE"] || homedir2();
|
|
37980
|
-
const newDir = join3(home, ".hasna", "skills");
|
|
37981
|
-
const oldDir = join3(home, ".skills");
|
|
37982
|
-
const oldConfigFile = join3(home, ".skillsrc");
|
|
37983
|
-
mkdirSync(newDir, { recursive: true });
|
|
38090
|
+
const root = getDataRoot();
|
|
37984
38091
|
try {
|
|
37985
|
-
|
|
38092
|
+
mkdirSync(root, { recursive: true });
|
|
37986
38093
|
} catch {}
|
|
37987
|
-
if (
|
|
38094
|
+
if (hasOperatorOverride())
|
|
38095
|
+
return root;
|
|
38096
|
+
const home = effectiveHome();
|
|
38097
|
+
const oldDir = join5(home, ".skills");
|
|
38098
|
+
const oldConfigFile = join5(home, ".skillsrc");
|
|
38099
|
+
try {
|
|
38100
|
+
mergeDirectoryContents(oldDir, root);
|
|
38101
|
+
} catch {}
|
|
38102
|
+
if (existsSync4(oldConfigFile) && !existsSync4(join5(root, "config.json"))) {
|
|
37988
38103
|
try {
|
|
37989
|
-
copyFileSync(oldConfigFile,
|
|
38104
|
+
copyFileSync(oldConfigFile, join5(root, "config.json"));
|
|
37990
38105
|
} catch {}
|
|
37991
38106
|
}
|
|
37992
|
-
return
|
|
38107
|
+
return root;
|
|
37993
38108
|
}
|
|
37994
38109
|
function getDataDirReadOnly() {
|
|
37995
|
-
|
|
37996
|
-
if (override)
|
|
37997
|
-
return override;
|
|
37998
|
-
return join3(process.env["HOME"] || process.env["USERPROFILE"] || homedir2(), ".hasna", "skills");
|
|
38110
|
+
return getDataRoot();
|
|
37999
38111
|
}
|
|
38000
38112
|
function getConfigPathReadOnly(scope) {
|
|
38001
38113
|
if (scope === "global")
|
|
38002
|
-
return
|
|
38003
|
-
return
|
|
38114
|
+
return join5(getDataDirReadOnly(), "config.json");
|
|
38115
|
+
return join5(process.cwd(), "skills.config.json");
|
|
38004
38116
|
}
|
|
38005
38117
|
function loadConfigReadOnly() {
|
|
38006
38118
|
const canonicalConfigPath = getConfigPathReadOnly("global");
|
|
38007
38119
|
let globalConfig;
|
|
38008
|
-
if (
|
|
38120
|
+
if (existsSync4(canonicalConfigPath)) {
|
|
38009
38121
|
globalConfig = readConfigFile(canonicalConfigPath);
|
|
38010
|
-
} else if (
|
|
38122
|
+
} else if (hasOperatorOverride()) {
|
|
38011
38123
|
globalConfig = {};
|
|
38012
38124
|
} else {
|
|
38013
38125
|
globalConfig = readConfigFile(legacyConfigFilePath());
|
|
@@ -38016,16 +38128,16 @@ function loadConfigReadOnly() {
|
|
|
38016
38128
|
return { ...globalConfig, ...projectConfig };
|
|
38017
38129
|
}
|
|
38018
38130
|
function legacyConfigFilePath() {
|
|
38019
|
-
return
|
|
38131
|
+
return join5(process.env["HOME"] || process.env["USERPROFILE"] || homedir4(), ".skillsrc");
|
|
38020
38132
|
}
|
|
38021
38133
|
function getConfigPath(scope) {
|
|
38022
38134
|
if (scope === "global") {
|
|
38023
|
-
return
|
|
38135
|
+
return join5(getDataDir(), "config.json");
|
|
38024
38136
|
}
|
|
38025
|
-
return
|
|
38137
|
+
return join5(process.cwd(), "skills.config.json");
|
|
38026
38138
|
}
|
|
38027
38139
|
function readConfigFile(path) {
|
|
38028
|
-
if (!
|
|
38140
|
+
if (!existsSync4(path))
|
|
38029
38141
|
return {};
|
|
38030
38142
|
let parsed;
|
|
38031
38143
|
try {
|
|
@@ -38061,7 +38173,7 @@ function saveConfig(key, value, scope = "project") {
|
|
|
38061
38173
|
}
|
|
38062
38174
|
const filePath = getConfigPath(scope);
|
|
38063
38175
|
let existing = {};
|
|
38064
|
-
if (
|
|
38176
|
+
if (existsSync4(filePath)) {
|
|
38065
38177
|
try {
|
|
38066
38178
|
existing = JSON.parse(readFileSync2(filePath, "utf-8"));
|
|
38067
38179
|
if (typeof existing !== "object" || existing === null || Array.isArray(existing)) {
|
|
@@ -38072,7 +38184,7 @@ function saveConfig(key, value, scope = "project") {
|
|
|
38072
38184
|
}
|
|
38073
38185
|
} else {
|
|
38074
38186
|
const dir = dirname(filePath);
|
|
38075
|
-
if (!
|
|
38187
|
+
if (!existsSync4(dir)) {
|
|
38076
38188
|
mkdirSync(dir, { recursive: true });
|
|
38077
38189
|
}
|
|
38078
38190
|
}
|
|
@@ -38086,7 +38198,7 @@ function unsetConfig(key, scope = "project") {
|
|
|
38086
38198
|
throw new Error(`Unknown config key: ${key}. Valid keys: ${validKeys().join(", ")}`);
|
|
38087
38199
|
}
|
|
38088
38200
|
const filePath = getConfigPath(scope);
|
|
38089
|
-
if (!
|
|
38201
|
+
if (!existsSync4(filePath))
|
|
38090
38202
|
return false;
|
|
38091
38203
|
let existing;
|
|
38092
38204
|
try {
|
|
@@ -38104,9 +38216,11 @@ function unsetConfig(key, scope = "project") {
|
|
|
38104
38216
|
`);
|
|
38105
38217
|
return true;
|
|
38106
38218
|
}
|
|
38107
|
-
var ENUM_KEYS, STRING_KEYS,
|
|
38219
|
+
var ENUM_KEYS, STRING_KEYS, INSTALLED_SKILLS_DIRNAME = "installed", SKILLS_CACHE_DIRNAME = "skills", LAYOUT_MIGRATION_RECORD = ".layout-migration.json";
|
|
38108
38220
|
var init_config = __esm(() => {
|
|
38109
38221
|
init_retired_settings();
|
|
38222
|
+
init_app_home();
|
|
38223
|
+
init_app_home();
|
|
38110
38224
|
ENUM_KEYS = {
|
|
38111
38225
|
defaultAgent: ["claude", "codex", "gemini", "pi", "opencode", "all"],
|
|
38112
38226
|
defaultScope: ["global", "project"],
|
|
@@ -38904,8 +39018,8 @@ var init_registry_data = __esm(() => {
|
|
|
38904
39018
|
});
|
|
38905
39019
|
|
|
38906
39020
|
// src/lib/hosted-skill-set.ts
|
|
38907
|
-
import { existsSync as
|
|
38908
|
-
import { join as
|
|
39021
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
39022
|
+
import { join as join6 } from "path";
|
|
38909
39023
|
function normalizeMarker(value) {
|
|
38910
39024
|
return typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
38911
39025
|
}
|
|
@@ -38916,8 +39030,8 @@ function isHostedMetadataPackage(pkg) {
|
|
|
38916
39030
|
return HOSTED_RUNTIMES.has(normalizeMarker(skills.runtime)) || HOSTED_SOURCES.has(normalizeMarker(skills.source));
|
|
38917
39031
|
}
|
|
38918
39032
|
function isHostedMetadataSkillDir(skillDir) {
|
|
38919
|
-
const pkgPath =
|
|
38920
|
-
if (!
|
|
39033
|
+
const pkgPath = join6(skillDir, "package.json");
|
|
39034
|
+
if (!existsSync5(pkgPath))
|
|
38921
39035
|
return false;
|
|
38922
39036
|
try {
|
|
38923
39037
|
return isHostedMetadataPackage(JSON.parse(readFileSync3(pkgPath, "utf8")));
|
|
@@ -38945,8 +39059,8 @@ var init_hosted_skill_set = __esm(() => {
|
|
|
38945
39059
|
});
|
|
38946
39060
|
|
|
38947
39061
|
// src/lib/skill-validation.ts
|
|
38948
|
-
import { existsSync as
|
|
38949
|
-
import { isAbsolute, join as
|
|
39062
|
+
import { existsSync as existsSync6, lstatSync, readFileSync as readFileSync4, readdirSync as readdirSync3, statSync as statSync3 } from "fs";
|
|
39063
|
+
import { isAbsolute, join as join7, normalize } from "path";
|
|
38950
39064
|
function add2(target, code, message) {
|
|
38951
39065
|
target.push({ code, message });
|
|
38952
39066
|
}
|
|
@@ -39035,7 +39149,7 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
39035
39149
|
binCommands: [],
|
|
39036
39150
|
docFiles: []
|
|
39037
39151
|
};
|
|
39038
|
-
if (!
|
|
39152
|
+
if (!existsSync6(skillPath)) {
|
|
39039
39153
|
add2(issues, "skill.dir_missing", `Skill directory not found: ${skillPath}`);
|
|
39040
39154
|
return {
|
|
39041
39155
|
name: bareName,
|
|
@@ -39050,7 +39164,7 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
39050
39164
|
add2(issues, "skill.name_invalid", `Skill name '${bareName}' must use lowercase letters, numbers, dots, underscores, or hyphens`);
|
|
39051
39165
|
}
|
|
39052
39166
|
for (const entry of readdirSync3(skillPath).sort()) {
|
|
39053
|
-
const entryPath =
|
|
39167
|
+
const entryPath = join7(skillPath, entry);
|
|
39054
39168
|
if (RESERVED_SKILL_ENTRIES.has(entry)) {
|
|
39055
39169
|
add2(issues, "skill.reserved_file", `Reserved file '${entry}' is not allowed in skill packages`);
|
|
39056
39170
|
}
|
|
@@ -39062,14 +39176,14 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
39062
39176
|
}
|
|
39063
39177
|
}
|
|
39064
39178
|
for (const docFile of DOC_FILES) {
|
|
39065
|
-
if (
|
|
39179
|
+
if (existsSync6(join7(skillPath, docFile)))
|
|
39066
39180
|
metadata.docFiles.push(docFile);
|
|
39067
39181
|
}
|
|
39068
39182
|
if (metadata.docFiles.length === 0) {
|
|
39069
39183
|
add2(issues, "skill.docs_missing", "Missing documentation file: expected SKILL.md, README.md, or CLAUDE.md");
|
|
39070
39184
|
}
|
|
39071
|
-
const skillMdPath =
|
|
39072
|
-
if (
|
|
39185
|
+
const skillMdPath = join7(skillPath, "SKILL.md");
|
|
39186
|
+
if (existsSync6(skillMdPath)) {
|
|
39073
39187
|
const frontmatter = parseSkillFrontmatter(readFileSync4(skillMdPath, "utf-8"));
|
|
39074
39188
|
if (!frontmatter) {
|
|
39075
39189
|
add2(warnings, "skill.frontmatter_missing", "SKILL.md has no YAML frontmatter");
|
|
@@ -39112,8 +39226,8 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
39112
39226
|
}
|
|
39113
39227
|
metadata.kind = resolvedKind;
|
|
39114
39228
|
const isInstruction = resolvedKind === "instruction";
|
|
39115
|
-
const pkgPath =
|
|
39116
|
-
if (!
|
|
39229
|
+
const pkgPath = join7(skillPath, "package.json");
|
|
39230
|
+
if (!existsSync6(pkgPath)) {
|
|
39117
39231
|
if (!isInstruction)
|
|
39118
39232
|
add2(issues, "package.missing", "Missing package.json");
|
|
39119
39233
|
} else {
|
|
@@ -39171,8 +39285,8 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
39171
39285
|
add2(issues, "package.bin_target_unsafe", `package.json bin '${command}' target '${target}' must stay inside the skill directory`);
|
|
39172
39286
|
continue;
|
|
39173
39287
|
}
|
|
39174
|
-
const targetPath =
|
|
39175
|
-
if (!
|
|
39288
|
+
const targetPath = join7(skillPath, target);
|
|
39289
|
+
if (!existsSync6(targetPath)) {
|
|
39176
39290
|
add2(warnings, "package.bin_target_missing", `package.json bin '${command}' target '${target}' is not present before build`);
|
|
39177
39291
|
} else if (statSync3(targetPath).isDirectory()) {
|
|
39178
39292
|
add2(issues, "package.bin_target_directory", `package.json bin '${command}' target '${target}' must point to a file, not a directory`);
|
|
@@ -39189,17 +39303,17 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
39189
39303
|
metadata.runtime = "none";
|
|
39190
39304
|
} else {
|
|
39191
39305
|
metadata.runtime = hostedMetadata ? "hosted" : "local";
|
|
39192
|
-
const srcDir =
|
|
39306
|
+
const srcDir = join7(skillPath, "src");
|
|
39193
39307
|
if (hostedMetadata) {
|
|
39194
|
-
if (
|
|
39308
|
+
if (existsSync6(srcDir)) {
|
|
39195
39309
|
add2(issues, "skill.hosted_source_forbidden", "Hosted metadata skills must not include local implementation source");
|
|
39196
39310
|
}
|
|
39197
|
-
} else if (!
|
|
39311
|
+
} else if (!existsSync6(srcDir)) {
|
|
39198
39312
|
add2(issues, "skill.src_missing", "Missing src/ directory");
|
|
39199
|
-
} else if (!
|
|
39313
|
+
} else if (!existsSync6(join7(srcDir, "index.ts")) && !existsSync6(join7(srcDir, "index.js"))) {
|
|
39200
39314
|
add2(issues, "skill.src_index_missing", "Missing src/index.ts or src/index.js");
|
|
39201
39315
|
} else {
|
|
39202
|
-
const indexPath =
|
|
39316
|
+
const indexPath = existsSync6(join7(srcDir, "index.ts")) ? join7(srcDir, "index.ts") : join7(srcDir, "index.js");
|
|
39203
39317
|
const size2 = statSync3(indexPath).size;
|
|
39204
39318
|
if (size2 < 50)
|
|
39205
39319
|
add2(warnings, "skill.src_index_minimal", `Source entry point is very small (${size2}B)`);
|
|
@@ -39270,8 +39384,8 @@ var init_skill_validation = __esm(() => {
|
|
|
39270
39384
|
|
|
39271
39385
|
// src/lib/skill-hash.ts
|
|
39272
39386
|
import { createHash } from "crypto";
|
|
39273
|
-
import { existsSync as
|
|
39274
|
-
import { join as
|
|
39387
|
+
import { existsSync as existsSync7, readdirSync as readdirSync4, readFileSync as readFileSync5, statSync as statSync4 } from "fs";
|
|
39388
|
+
import { join as join8, sep } from "path";
|
|
39275
39389
|
function normalizeLineEndings(content) {
|
|
39276
39390
|
return content.replace(/\r\n/g, `
|
|
39277
39391
|
`).replace(/\r/g, `
|
|
@@ -39317,8 +39431,8 @@ function collectBundleFiles(skillPath) {
|
|
|
39317
39431
|
if (seen.has(entry))
|
|
39318
39432
|
continue;
|
|
39319
39433
|
seen.add(entry);
|
|
39320
|
-
const absolute =
|
|
39321
|
-
if (!
|
|
39434
|
+
const absolute = join8(skillPath, entry);
|
|
39435
|
+
if (!existsSync7(absolute))
|
|
39322
39436
|
continue;
|
|
39323
39437
|
if (statSync4(absolute).isDirectory())
|
|
39324
39438
|
collectDirectory(files, absolute, entry);
|
|
@@ -39331,7 +39445,7 @@ function collectDirectory(files, dir, rel) {
|
|
|
39331
39445
|
for (const entry of readdirSync4(dir).sort()) {
|
|
39332
39446
|
if (entry.startsWith("."))
|
|
39333
39447
|
continue;
|
|
39334
|
-
const absolute =
|
|
39448
|
+
const absolute = join8(dir, entry);
|
|
39335
39449
|
const childRel = `${rel}/${entry}`;
|
|
39336
39450
|
let stats;
|
|
39337
39451
|
try {
|
|
@@ -39586,14 +39700,14 @@ var init_skill_contract = __esm(() => {
|
|
|
39586
39700
|
// src/lib/portable-skills-files.ts
|
|
39587
39701
|
import {
|
|
39588
39702
|
cpSync,
|
|
39589
|
-
existsSync as
|
|
39703
|
+
existsSync as existsSync8,
|
|
39590
39704
|
lstatSync as lstatSync2,
|
|
39591
39705
|
mkdirSync as mkdirSync2,
|
|
39592
39706
|
readFileSync as readFileSync6,
|
|
39593
39707
|
realpathSync,
|
|
39594
39708
|
writeFileSync as writeFileSync2
|
|
39595
39709
|
} from "fs";
|
|
39596
|
-
import { basename, dirname as dirname2, join as
|
|
39710
|
+
import { basename, dirname as dirname2, join as join9, relative } from "path";
|
|
39597
39711
|
function defaultRuntimeContract(entrypoint = "src/index.ts") {
|
|
39598
39712
|
return {
|
|
39599
39713
|
runtime: "bun",
|
|
@@ -39617,12 +39731,12 @@ function normalizePortableSkillName(name) {
|
|
|
39617
39731
|
return normalized;
|
|
39618
39732
|
}
|
|
39619
39733
|
function readPortableSkillManifest(skillPath, fallbackName = basename(skillPath)) {
|
|
39620
|
-
const skillJsonPath =
|
|
39621
|
-
const skillMdPath =
|
|
39622
|
-
const pkgPath =
|
|
39623
|
-
const jsonManifest =
|
|
39624
|
-
const frontmatter =
|
|
39625
|
-
const pkg =
|
|
39734
|
+
const skillJsonPath = join9(skillPath, "skill.json");
|
|
39735
|
+
const skillMdPath = join9(skillPath, "SKILL.md");
|
|
39736
|
+
const pkgPath = join9(skillPath, "package.json");
|
|
39737
|
+
const jsonManifest = existsSync8(skillJsonPath) ? readJsonObject(skillJsonPath) : undefined;
|
|
39738
|
+
const frontmatter = existsSync8(skillMdPath) ? parseSkillFrontmatter(readFileSync6(skillMdPath, "utf-8")) ?? undefined : undefined;
|
|
39739
|
+
const pkg = existsSync8(pkgPath) ? readJsonObject(pkgPath) : undefined;
|
|
39626
39740
|
const name = normalizePortableSkillName(stringField(jsonManifest, "name") ?? frontmatter?.name ?? stringValue(pkg?.name) ?? fallbackName);
|
|
39627
39741
|
const description = stringField(jsonManifest, "description") ?? frontmatter?.description ?? stringValue(pkg?.description) ?? `${name} skill`;
|
|
39628
39742
|
const version = stringField(jsonManifest, "version") ?? frontmatter?.version ?? stringValue(pkg?.version) ?? PORTABLE_SKILL_DEFAULT_VERSION;
|
|
@@ -39668,7 +39782,7 @@ function createInstructionManifest(name, options) {
|
|
|
39668
39782
|
}
|
|
39669
39783
|
function writeInstructionSkillTemplate(skillPath, manifest) {
|
|
39670
39784
|
mkdirSync2(skillPath, { recursive: true });
|
|
39671
|
-
writeFileSync2(
|
|
39785
|
+
writeFileSync2(join9(skillPath, "SKILL.md"), renderInstructionSkillMd(manifest));
|
|
39672
39786
|
writeSkillJsonWithHash(skillPath, manifest);
|
|
39673
39787
|
}
|
|
39674
39788
|
function renderInstructionSkillMd(manifest) {
|
|
@@ -39717,12 +39831,12 @@ function createPortableManifest(name, options) {
|
|
|
39717
39831
|
};
|
|
39718
39832
|
}
|
|
39719
39833
|
function writePortableSkillTemplate(skillPath, manifest) {
|
|
39720
|
-
mkdirSync2(
|
|
39721
|
-
writeFileSync2(
|
|
39722
|
-
writeFileSync2(
|
|
39723
|
-
writeFileSync2(
|
|
39724
|
-
writeFileSync2(
|
|
39725
|
-
writeFileSync2(
|
|
39834
|
+
mkdirSync2(join9(skillPath, "src"), { recursive: true });
|
|
39835
|
+
writeFileSync2(join9(skillPath, "SKILL.md"), renderSkillMd(manifest));
|
|
39836
|
+
writeFileSync2(join9(skillPath, "AGENTS.md"), renderAgentsMd(manifest));
|
|
39837
|
+
writeFileSync2(join9(skillPath, "package.json"), renderPackageJson(manifest));
|
|
39838
|
+
writeFileSync2(join9(skillPath, "tsconfig.json"), renderTsconfig());
|
|
39839
|
+
writeFileSync2(join9(skillPath, "src", "index.ts"), renderEntrypoint(manifest));
|
|
39726
39840
|
writeSkillJsonWithHash(skillPath, manifest);
|
|
39727
39841
|
}
|
|
39728
39842
|
function fillContractDefaults(manifest, entrypoint) {
|
|
@@ -39747,7 +39861,7 @@ function writeSkillJsonWithHash(skillPath, manifest) {
|
|
|
39747
39861
|
content_hash: undefined
|
|
39748
39862
|
}
|
|
39749
39863
|
};
|
|
39750
|
-
writeFileSync2(
|
|
39864
|
+
writeFileSync2(join9(skillPath, "skill.json"), `${JSON.stringify({ ...existing, ...renderSkillJsonObject(withoutHash) }, null, 2)}
|
|
39751
39865
|
`);
|
|
39752
39866
|
const hash = computeContentHash(skillPath);
|
|
39753
39867
|
const withHash = {
|
|
@@ -39757,13 +39871,13 @@ function writeSkillJsonWithHash(skillPath, manifest) {
|
|
|
39757
39871
|
content_hash: hash
|
|
39758
39872
|
}
|
|
39759
39873
|
};
|
|
39760
|
-
writeFileSync2(
|
|
39874
|
+
writeFileSync2(join9(skillPath, "skill.json"), `${JSON.stringify({ ...existing, ...renderSkillJsonObject(withHash) }, null, 2)}
|
|
39761
39875
|
`);
|
|
39762
39876
|
return withHash;
|
|
39763
39877
|
}
|
|
39764
39878
|
function readExistingSkillJson(skillPath) {
|
|
39765
|
-
const path =
|
|
39766
|
-
if (!
|
|
39879
|
+
const path = join9(skillPath, "skill.json");
|
|
39880
|
+
if (!existsSync8(path))
|
|
39767
39881
|
return {};
|
|
39768
39882
|
try {
|
|
39769
39883
|
const parsed = JSON.parse(readFileSync6(path, "utf-8"));
|
|
@@ -39796,28 +39910,28 @@ function ensurePortableSkillFiles(skillPath, manifest) {
|
|
|
39796
39910
|
tags: next.tags?.length ? next.tags : ["custom", next.name]
|
|
39797
39911
|
};
|
|
39798
39912
|
const entry = next.commands[0]?.entry ?? "src/index.ts";
|
|
39799
|
-
if (entry && !
|
|
39800
|
-
mkdirSync2(dirname2(
|
|
39801
|
-
writeFileSync2(
|
|
39913
|
+
if (entry && !existsSync8(join9(skillPath, entry))) {
|
|
39914
|
+
mkdirSync2(dirname2(join9(skillPath, entry)), { recursive: true });
|
|
39915
|
+
writeFileSync2(join9(skillPath, entry), renderEntrypoint(next));
|
|
39802
39916
|
}
|
|
39803
|
-
if (!
|
|
39804
|
-
writeFileSync2(
|
|
39917
|
+
if (!existsSync8(join9(skillPath, "SKILL.md")))
|
|
39918
|
+
writeFileSync2(join9(skillPath, "SKILL.md"), renderSkillMd(next));
|
|
39805
39919
|
else
|
|
39806
|
-
writeFileSync2(
|
|
39807
|
-
if (!
|
|
39808
|
-
writeFileSync2(
|
|
39920
|
+
writeFileSync2(join9(skillPath, "SKILL.md"), ensureSkillMdFrontmatter(readFileSync6(join9(skillPath, "SKILL.md"), "utf-8"), next));
|
|
39921
|
+
if (!existsSync8(join9(skillPath, "AGENTS.md")))
|
|
39922
|
+
writeFileSync2(join9(skillPath, "AGENTS.md"), renderAgentsMd(next));
|
|
39809
39923
|
ensurePackageJson(skillPath, next);
|
|
39810
|
-
if (!
|
|
39811
|
-
writeFileSync2(
|
|
39924
|
+
if (!existsSync8(join9(skillPath, "tsconfig.json")))
|
|
39925
|
+
writeFileSync2(join9(skillPath, "tsconfig.json"), renderTsconfig());
|
|
39812
39926
|
writeSkillJsonWithHash(skillPath, next);
|
|
39813
39927
|
return readPortableSkillManifest(skillPath, next.name);
|
|
39814
39928
|
}
|
|
39815
39929
|
function ensurePackageJson(skillPath, manifest) {
|
|
39816
|
-
const pkgPath =
|
|
39930
|
+
const pkgPath = join9(skillPath, "package.json");
|
|
39817
39931
|
const first = manifest.commands[0] ?? { name: manifest.name, entry: "src/index.ts" };
|
|
39818
39932
|
const commandName = normalizePortableSkillName(first.name || manifest.name);
|
|
39819
39933
|
const entry = (first.entry ?? "src/index.ts").replace(/^\.\//, "");
|
|
39820
|
-
if (!
|
|
39934
|
+
if (!existsSync8(pkgPath)) {
|
|
39821
39935
|
writeFileSync2(pkgPath, renderPackageJson(manifest));
|
|
39822
39936
|
return;
|
|
39823
39937
|
}
|
|
@@ -39860,8 +39974,8 @@ function ensureInstructionSkillFiles(skillPath, manifest) {
|
|
|
39860
39974
|
inputs: [],
|
|
39861
39975
|
commands: []
|
|
39862
39976
|
};
|
|
39863
|
-
if (!
|
|
39864
|
-
writeFileSync2(
|
|
39977
|
+
if (!existsSync8(join9(skillPath, "SKILL.md"))) {
|
|
39978
|
+
writeFileSync2(join9(skillPath, "SKILL.md"), renderSkillMd(next));
|
|
39865
39979
|
}
|
|
39866
39980
|
writeSkillJsonWithHash(skillPath, next);
|
|
39867
39981
|
return readPortableSkillManifest(skillPath, next.name);
|
|
@@ -40168,7 +40282,7 @@ var init_portable_skills_files = __esm(() => {
|
|
|
40168
40282
|
// src/lib/portable-skills.ts
|
|
40169
40283
|
import {
|
|
40170
40284
|
cpSync as cpSync2,
|
|
40171
|
-
existsSync as
|
|
40285
|
+
existsSync as existsSync9,
|
|
40172
40286
|
mkdirSync as mkdirSync3,
|
|
40173
40287
|
mkdtempSync,
|
|
40174
40288
|
readdirSync as readdirSync5,
|
|
@@ -40177,22 +40291,22 @@ import {
|
|
|
40177
40291
|
statSync as statSync6,
|
|
40178
40292
|
writeFileSync as writeFileSync3
|
|
40179
40293
|
} from "fs";
|
|
40180
|
-
import { basename as basename2, dirname as dirname3, isAbsolute as isAbsolute2, join as
|
|
40294
|
+
import { basename as basename2, dirname as dirname3, isAbsolute as isAbsolute2, join as join10, normalize as normalize2 } from "path";
|
|
40181
40295
|
function getPortableSkillsRoot(options = {}) {
|
|
40182
40296
|
if (options.rootDir)
|
|
40183
40297
|
return options.rootDir;
|
|
40184
|
-
const appDir = options.homeDir ?
|
|
40185
|
-
const cache3 =
|
|
40298
|
+
const appDir = options.homeDir ? join10(options.homeDir, ".hasna", "skills") : getDataDir();
|
|
40299
|
+
const cache3 = join10(appDir, SKILLS_CACHE_DIRNAME);
|
|
40186
40300
|
if (isOwnerLayoutMigrated(appDir) && safeIsDirectory(cache3))
|
|
40187
40301
|
return cache3;
|
|
40188
|
-
const installed =
|
|
40302
|
+
const installed = join10(appDir, INSTALLED_SKILLS_DIRNAME);
|
|
40189
40303
|
migrateLegacySkillLayout(appDir, installed);
|
|
40190
40304
|
return installed;
|
|
40191
40305
|
}
|
|
40192
40306
|
function looksLikeSkillDirectory(path) {
|
|
40193
40307
|
if (!safeIsDirectory(path))
|
|
40194
40308
|
return false;
|
|
40195
|
-
return
|
|
40309
|
+
return existsSync9(join10(path, "SKILL.md")) || existsSync9(join10(path, "skill.json")) || existsSync9(join10(path, "package.json"));
|
|
40196
40310
|
}
|
|
40197
40311
|
function migrateLegacySkillLayout(appDir, installed) {
|
|
40198
40312
|
if (!safeIsDirectory(appDir))
|
|
@@ -40202,7 +40316,7 @@ function migrateLegacySkillLayout(appDir, installed) {
|
|
|
40202
40316
|
for (const entry of readdirSync5(appDir)) {
|
|
40203
40317
|
if (entry.startsWith(".") || entry === INSTALLED_SKILLS_DIRNAME)
|
|
40204
40318
|
continue;
|
|
40205
|
-
const path =
|
|
40319
|
+
const path = join10(appDir, entry);
|
|
40206
40320
|
if (entry === LEGACY_CUSTOM_DIRNAME) {
|
|
40207
40321
|
if (!safeIsDirectory(path))
|
|
40208
40322
|
continue;
|
|
@@ -40210,7 +40324,7 @@ function migrateLegacySkillLayout(appDir, installed) {
|
|
|
40210
40324
|
for (const nested of readdirSync5(path)) {
|
|
40211
40325
|
if (nested.startsWith("."))
|
|
40212
40326
|
continue;
|
|
40213
|
-
const nestedPath =
|
|
40327
|
+
const nestedPath = join10(path, nested);
|
|
40214
40328
|
if (looksLikeSkillDirectory(nestedPath))
|
|
40215
40329
|
candidates.push({ from: nestedPath, name: nested });
|
|
40216
40330
|
}
|
|
@@ -40224,10 +40338,10 @@ function migrateLegacySkillLayout(appDir, installed) {
|
|
|
40224
40338
|
return;
|
|
40225
40339
|
}
|
|
40226
40340
|
for (const { from, name } of candidates) {
|
|
40227
|
-
const target =
|
|
40228
|
-
if (
|
|
40341
|
+
const target = join10(installed, name);
|
|
40342
|
+
if (existsSync9(target))
|
|
40229
40343
|
continue;
|
|
40230
|
-
const staging =
|
|
40344
|
+
const staging = join10(installed, `.migrating-${name}-${process.pid}`);
|
|
40231
40345
|
try {
|
|
40232
40346
|
rmSync(staging, { recursive: true, force: true });
|
|
40233
40347
|
cpSync2(from, staging, { recursive: true, errorOnExist: false });
|
|
@@ -40240,7 +40354,7 @@ function migrateLegacySkillLayout(appDir, installed) {
|
|
|
40240
40354
|
}
|
|
40241
40355
|
}
|
|
40242
40356
|
function getPortableSkillPath(name, options = {}) {
|
|
40243
|
-
return
|
|
40357
|
+
return join10(getPortableSkillsRoot(options), normalizePortableSkillName(name));
|
|
40244
40358
|
}
|
|
40245
40359
|
function findPortableSkill(name, options = {}) {
|
|
40246
40360
|
let normalized;
|
|
@@ -40250,7 +40364,7 @@ function findPortableSkill(name, options = {}) {
|
|
|
40250
40364
|
return null;
|
|
40251
40365
|
}
|
|
40252
40366
|
const path = getPortableSkillPath(normalized, options);
|
|
40253
|
-
if (!
|
|
40367
|
+
if (!existsSync9(path) || !statSync6(path).isDirectory())
|
|
40254
40368
|
return null;
|
|
40255
40369
|
try {
|
|
40256
40370
|
return summarizePortableSkill(path, normalized);
|
|
@@ -40266,7 +40380,7 @@ function listPortableSkills(options = {}) {
|
|
|
40266
40380
|
for (const entry of readdirSync5(root).sort()) {
|
|
40267
40381
|
if (entry.startsWith("."))
|
|
40268
40382
|
continue;
|
|
40269
|
-
const path =
|
|
40383
|
+
const path = join10(root, entry);
|
|
40270
40384
|
if (!safeIsDirectory(path))
|
|
40271
40385
|
continue;
|
|
40272
40386
|
try {
|
|
@@ -40299,8 +40413,8 @@ function isOfficialSkillName(name) {
|
|
|
40299
40413
|
function scaffoldPortableSkill(name, options = {}) {
|
|
40300
40414
|
const skillName = normalizePortableSkillName(name);
|
|
40301
40415
|
const root = getPortableSkillsRoot(options);
|
|
40302
|
-
const skillPath =
|
|
40303
|
-
if (
|
|
40416
|
+
const skillPath = join10(root, skillName);
|
|
40417
|
+
if (existsSync9(skillPath)) {
|
|
40304
40418
|
if (!options.overwrite)
|
|
40305
40419
|
throw new Error(`Skill '${skillName}' already exists at ${skillPath}`);
|
|
40306
40420
|
rmSync(skillPath, { recursive: true, force: true });
|
|
@@ -40318,7 +40432,7 @@ function scaffoldPortableSkill(name, options = {}) {
|
|
|
40318
40432
|
}
|
|
40319
40433
|
function portPortableSkillDirectory(sourceDir, options = {}) {
|
|
40320
40434
|
const absoluteSource = normalize2(sourceDir);
|
|
40321
|
-
if (!
|
|
40435
|
+
if (!existsSync9(absoluteSource) || !statSync6(absoluteSource).isDirectory()) {
|
|
40322
40436
|
throw new Error(`Import directory not found: ${sourceDir}`);
|
|
40323
40437
|
}
|
|
40324
40438
|
const continueOnError = options.continueOnError ?? true;
|
|
@@ -40331,7 +40445,7 @@ function portPortableSkillDirectory(sourceDir, options = {}) {
|
|
|
40331
40445
|
const skipped = [];
|
|
40332
40446
|
const entries = readdirSync5(absoluteSource, { withFileTypes: true }).map((entry) => entry.name).filter((entryName) => !entryName.startsWith(".")).sort();
|
|
40333
40447
|
for (const entryName of entries) {
|
|
40334
|
-
const childPath =
|
|
40448
|
+
const childPath = join10(absoluteSource, entryName);
|
|
40335
40449
|
if (!safeIsDirectory(childPath))
|
|
40336
40450
|
continue;
|
|
40337
40451
|
if (!isSkillCandidate(childPath)) {
|
|
@@ -40360,11 +40474,11 @@ function portPortableSkillDirectory(sourceDir, options = {}) {
|
|
|
40360
40474
|
};
|
|
40361
40475
|
}
|
|
40362
40476
|
function isSkillCandidate(dir) {
|
|
40363
|
-
return
|
|
40477
|
+
return existsSync9(join10(dir, "SKILL.md")) || existsSync9(join10(dir, "skill.json")) || existsSync9(join10(dir, "package.json"));
|
|
40364
40478
|
}
|
|
40365
40479
|
function portPortableSkill(sourcePath, options = {}) {
|
|
40366
40480
|
const absoluteSource = normalize2(sourcePath);
|
|
40367
|
-
if (!
|
|
40481
|
+
if (!existsSync9(absoluteSource) || !statSync6(absoluteSource).isDirectory()) {
|
|
40368
40482
|
throw new Error(`Skill source directory not found: ${sourcePath}`);
|
|
40369
40483
|
}
|
|
40370
40484
|
const inferred = readPortableSkillManifest(absoluteSource, basename2(absoluteSource));
|
|
@@ -40376,8 +40490,8 @@ function portPortableSkill(sourcePath, options = {}) {
|
|
|
40376
40490
|
throw new Error(`${via} Importing it would shadow the official '${skillName}'. ` + `Pass --name to choose a different name, or --allow-shadow to override deliberately.`);
|
|
40377
40491
|
}
|
|
40378
40492
|
const root = getPortableSkillsRoot(options);
|
|
40379
|
-
const destination =
|
|
40380
|
-
if (
|
|
40493
|
+
const destination = join10(root, skillName);
|
|
40494
|
+
if (existsSync9(destination)) {
|
|
40381
40495
|
if (!options.overwrite)
|
|
40382
40496
|
throw new Error(`Skill '${skillName}' already exists at ${destination}`);
|
|
40383
40497
|
rmSync(destination, { recursive: true, force: true });
|
|
@@ -40419,19 +40533,19 @@ function buildCorpusManifest(input, name) {
|
|
|
40419
40533
|
function installCorpusSkillAtomically(input, options = {}) {
|
|
40420
40534
|
const name = normalizePortableSkillName(input.name);
|
|
40421
40535
|
const root = getPortableSkillsRoot(options);
|
|
40422
|
-
const target =
|
|
40423
|
-
const created = !
|
|
40536
|
+
const target = join10(root, name);
|
|
40537
|
+
const created = !existsSync9(target);
|
|
40424
40538
|
mkdirSync3(root, { recursive: true });
|
|
40425
|
-
const staging = mkdtempSync(
|
|
40539
|
+
const staging = mkdtempSync(join10(root, `.pull-${name}-`));
|
|
40426
40540
|
let moved = false;
|
|
40427
40541
|
let backup = null;
|
|
40428
40542
|
try {
|
|
40429
|
-
writeFileSync3(
|
|
40543
|
+
writeFileSync3(join10(staging, "SKILL.md"), input.skillMd);
|
|
40430
40544
|
const manifest = buildCorpusManifest(input, name);
|
|
40431
40545
|
writeSkillJsonWithHash(staging, manifest);
|
|
40432
|
-
if (
|
|
40433
|
-
backup = mkdtempSync(
|
|
40434
|
-
renameSync(target,
|
|
40546
|
+
if (existsSync9(target)) {
|
|
40547
|
+
backup = mkdtempSync(join10(root, `.pull-backup-${name}-`));
|
|
40548
|
+
renameSync(target, join10(backup, name));
|
|
40435
40549
|
moved = true;
|
|
40436
40550
|
}
|
|
40437
40551
|
renameSync(staging, target);
|
|
@@ -40440,9 +40554,9 @@ function installCorpusSkillAtomically(input, options = {}) {
|
|
|
40440
40554
|
return { name, path: target, manifest, created };
|
|
40441
40555
|
} catch (error) {
|
|
40442
40556
|
rmSync(staging, { recursive: true, force: true });
|
|
40443
|
-
if (moved && backup &&
|
|
40557
|
+
if (moved && backup && existsSync9(join10(backup, name))) {
|
|
40444
40558
|
try {
|
|
40445
|
-
renameSync(
|
|
40559
|
+
renameSync(join10(backup, name), target);
|
|
40446
40560
|
} catch {}
|
|
40447
40561
|
}
|
|
40448
40562
|
throw error;
|
|
@@ -40454,10 +40568,10 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
40454
40568
|
const issues = [...base2.issues];
|
|
40455
40569
|
const warnings = [...base2.warnings];
|
|
40456
40570
|
let manifest;
|
|
40457
|
-
if (
|
|
40458
|
-
const skillJsonPath =
|
|
40459
|
-
const skillMdPath =
|
|
40460
|
-
if (!
|
|
40571
|
+
if (existsSync9(skillPath)) {
|
|
40572
|
+
const skillJsonPath = join10(skillPath, "skill.json");
|
|
40573
|
+
const skillMdPath = join10(skillPath, "SKILL.md");
|
|
40574
|
+
if (!existsSync9(skillJsonPath) && !existsSync9(skillMdPath)) {
|
|
40461
40575
|
add4(issues, "portable.manifest_missing", "Missing portable manifest: expected SKILL.md frontmatter and/or skill.json");
|
|
40462
40576
|
}
|
|
40463
40577
|
try {
|
|
@@ -40476,7 +40590,7 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
40476
40590
|
add4(issues, "portable.version_missing", "Portable manifest missing version");
|
|
40477
40591
|
}
|
|
40478
40592
|
const contractIssues = validatePortableManifestContract(manifest, {
|
|
40479
|
-
strict:
|
|
40593
|
+
strict: existsSync9(join10(skillPath, "skill.json")),
|
|
40480
40594
|
skillPath
|
|
40481
40595
|
});
|
|
40482
40596
|
for (const issue of contractIssues)
|
|
@@ -40512,8 +40626,8 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
40512
40626
|
add4(issues, "portable.command_entry_unsafe", `Command '${command.name}' entry '${command.entry}' must stay inside the skill directory`);
|
|
40513
40627
|
continue;
|
|
40514
40628
|
}
|
|
40515
|
-
const entryPath =
|
|
40516
|
-
if (!
|
|
40629
|
+
const entryPath = join10(skillPath, command.entry);
|
|
40630
|
+
if (!existsSync9(entryPath))
|
|
40517
40631
|
add4(issues, "portable.command_entry_missing", `Command '${command.name}' entry '${command.entry}' is missing`);
|
|
40518
40632
|
else if (statSync6(entryPath).isDirectory())
|
|
40519
40633
|
add4(issues, "portable.command_entry_directory", `Command '${command.name}' entry '${command.entry}' must be a file`);
|
|
@@ -40523,7 +40637,7 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
40523
40637
|
} catch (error) {
|
|
40524
40638
|
add4(issues, "portable.manifest_invalid", error.message);
|
|
40525
40639
|
}
|
|
40526
|
-
if (manifest?.kind !== "instruction" && !
|
|
40640
|
+
if (manifest?.kind !== "instruction" && !existsSync9(join10(skillPath, "AGENTS.md"))) {
|
|
40527
40641
|
add4(issues, "portable.agents_missing", "Missing AGENTS.md with build-out instructions for coding agents");
|
|
40528
40642
|
}
|
|
40529
40643
|
}
|
|
@@ -40792,8 +40906,8 @@ __export(exports_registry, {
|
|
|
40792
40906
|
CATEGORIES: () => CATEGORIES,
|
|
40793
40907
|
BASIC_SKILL_NAMES: () => BASIC_SKILL_NAMES
|
|
40794
40908
|
});
|
|
40795
|
-
import { existsSync as
|
|
40796
|
-
import { join as
|
|
40909
|
+
import { existsSync as existsSync10, readFileSync as readFileSync7, readdirSync as readdirSync6 } from "fs";
|
|
40910
|
+
import { join as join11 } from "path";
|
|
40797
40911
|
function isBasicSkillName(name) {
|
|
40798
40912
|
return BASIC_SKILL_NAMES.includes(name);
|
|
40799
40913
|
}
|
|
@@ -40829,7 +40943,7 @@ function parseSkillMdFrontmatter(content) {
|
|
|
40829
40943
|
return Object.keys(result2).length > 0 ? result2 : null;
|
|
40830
40944
|
}
|
|
40831
40945
|
function discoverSkillsInDir(dir, source = "custom") {
|
|
40832
|
-
if (!
|
|
40946
|
+
if (!existsSync10(dir))
|
|
40833
40947
|
return [];
|
|
40834
40948
|
const result2 = [];
|
|
40835
40949
|
try {
|
|
@@ -40837,8 +40951,8 @@ function discoverSkillsInDir(dir, source = "custom") {
|
|
|
40837
40951
|
for (const entry of entries) {
|
|
40838
40952
|
if (!entry.isDirectory())
|
|
40839
40953
|
continue;
|
|
40840
|
-
const skillMdPath =
|
|
40841
|
-
if (!
|
|
40954
|
+
const skillMdPath = join11(dir, entry.name, "SKILL.md");
|
|
40955
|
+
if (!existsSync10(skillMdPath))
|
|
40842
40956
|
continue;
|
|
40843
40957
|
let content;
|
|
40844
40958
|
try {
|
|
@@ -40857,7 +40971,7 @@ function discoverSkillsInDir(dir, source = "custom") {
|
|
|
40857
40971
|
category: fm.category || "Development Tools",
|
|
40858
40972
|
tags: fm.tags || [],
|
|
40859
40973
|
...fm.kind ? { kind: fm.kind } : {},
|
|
40860
|
-
...isHostedMetadataSkillDir(
|
|
40974
|
+
...isHostedMetadataSkillDir(join11(dir, entry.name)) ? { serverOwned: true } : {},
|
|
40861
40975
|
source
|
|
40862
40976
|
});
|
|
40863
40977
|
}
|
|
@@ -40866,16 +40980,16 @@ function discoverSkillsInDir(dir, source = "custom") {
|
|
|
40866
40980
|
}
|
|
40867
40981
|
function findExtensionSkillPath(name) {
|
|
40868
40982
|
const config = loadConfig();
|
|
40869
|
-
if (!config.extensionsDir || !
|
|
40983
|
+
if (!config.extensionsDir || !existsSync10(config.extensionsDir))
|
|
40870
40984
|
return null;
|
|
40871
40985
|
try {
|
|
40872
40986
|
const entries = readdirSync6(config.extensionsDir, { withFileTypes: true });
|
|
40873
40987
|
for (const entry of entries) {
|
|
40874
40988
|
if (!entry.isDirectory())
|
|
40875
40989
|
continue;
|
|
40876
|
-
const skillDir =
|
|
40877
|
-
const skillMdPath =
|
|
40878
|
-
if (!
|
|
40990
|
+
const skillDir = join11(config.extensionsDir, entry.name);
|
|
40991
|
+
const skillMdPath = join11(skillDir, "SKILL.md");
|
|
40992
|
+
if (!existsSync10(skillMdPath))
|
|
40879
40993
|
continue;
|
|
40880
40994
|
let content;
|
|
40881
40995
|
try {
|
|
@@ -40903,12 +41017,12 @@ function loadRegistry(cwd2) {
|
|
|
40903
41017
|
if (registryCache && registryCacheKey === rootKey && now3 - registryCacheTime < REGISTRY_CACHE_TTL) {
|
|
40904
41018
|
return registryCache;
|
|
40905
41019
|
}
|
|
40906
|
-
const
|
|
41020
|
+
const dataDir2 = getDataDir();
|
|
40907
41021
|
const config = loadConfig();
|
|
40908
41022
|
const official = SKILLS.map((s) => ({ ...s, source: "official" }));
|
|
40909
41023
|
const extensions = config.extensionsDir ? discoverSkillsInDir(config.extensionsDir, "extension") : [];
|
|
40910
41024
|
const portableCustom = listPortableSkillMetas();
|
|
40911
|
-
const legacyCustom = discoverSkillsInDir(
|
|
41025
|
+
const legacyCustom = discoverSkillsInDir(join11(dataDir2, "custom"));
|
|
40912
41026
|
const globalCustom = mergeCustomSkills([...legacyCustom, ...portableCustom]);
|
|
40913
41027
|
registryCache = mergeSkillRegistryLists(official, extensions, globalCustom);
|
|
40914
41028
|
registryCacheTime = now3;
|
|
@@ -42607,16 +42721,16 @@ var require_cli_spinners = __commonJS((exports, module) => {
|
|
|
42607
42721
|
});
|
|
42608
42722
|
|
|
42609
42723
|
// src/lib/home-migration.ts
|
|
42610
|
-
import { existsSync as
|
|
42611
|
-
import { join as
|
|
42724
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync4, readdirSync as readdirSync7, renameSync as renameSync2, rmSync as rmSync2, statSync as statSync7, writeFileSync as writeFileSync4 } from "fs";
|
|
42725
|
+
import { join as join12 } from "path";
|
|
42612
42726
|
function layoutMigrationRecordPath(appDir) {
|
|
42613
|
-
return
|
|
42727
|
+
return join12(appDir, SKILLS_CACHE_DIRNAME, LAYOUT_MIGRATION_RECORD);
|
|
42614
42728
|
}
|
|
42615
42729
|
function resolveCorpusRoot(options = {}) {
|
|
42616
42730
|
return getPortableSkillsRoot(options);
|
|
42617
42731
|
}
|
|
42618
42732
|
function migrationAppDir(homeDir) {
|
|
42619
|
-
return homeDir ?
|
|
42733
|
+
return homeDir ? join12(homeDir, ".hasna", "skills") : getDataDir();
|
|
42620
42734
|
}
|
|
42621
42735
|
function looksLikeSkillDirectory2(path) {
|
|
42622
42736
|
try {
|
|
@@ -42625,11 +42739,11 @@ function looksLikeSkillDirectory2(path) {
|
|
|
42625
42739
|
} catch {
|
|
42626
42740
|
return false;
|
|
42627
42741
|
}
|
|
42628
|
-
return
|
|
42742
|
+
return existsSync11(join12(path, "SKILL.md")) || existsSync11(join12(path, "skill.json")) || existsSync11(join12(path, "package.json"));
|
|
42629
42743
|
}
|
|
42630
42744
|
function listLegacyFlatSkillDirs(appDir) {
|
|
42631
42745
|
const legacy = [];
|
|
42632
|
-
if (!
|
|
42746
|
+
if (!existsSync11(appDir))
|
|
42633
42747
|
return legacy;
|
|
42634
42748
|
let entries = [];
|
|
42635
42749
|
try {
|
|
@@ -42643,15 +42757,15 @@ function listLegacyFlatSkillDirs(appDir) {
|
|
|
42643
42757
|
if (entry === SKILLS_CACHE_DIRNAME || entry === INSTALLED_SKILLS_DIRNAME || entry === LEGACY_CUSTOM_DIRNAME2 || entry === LOGS_DIRNAME || entry === OUTPUTS_DIRNAME) {
|
|
42644
42758
|
continue;
|
|
42645
42759
|
}
|
|
42646
|
-
if (looksLikeSkillDirectory2(
|
|
42760
|
+
if (looksLikeSkillDirectory2(join12(appDir, entry)))
|
|
42647
42761
|
legacy.push(entry);
|
|
42648
42762
|
}
|
|
42649
42763
|
return legacy;
|
|
42650
42764
|
}
|
|
42651
42765
|
function createLazyDirs(appDir, created) {
|
|
42652
42766
|
for (const name of [LOGS_DIRNAME, OUTPUTS_DIRNAME]) {
|
|
42653
|
-
const dir =
|
|
42654
|
-
if (!
|
|
42767
|
+
const dir = join12(appDir, name);
|
|
42768
|
+
if (!existsSync11(dir)) {
|
|
42655
42769
|
mkdirSync4(dir, { recursive: true });
|
|
42656
42770
|
created.push(dir);
|
|
42657
42771
|
}
|
|
@@ -42659,14 +42773,14 @@ function createLazyDirs(appDir, created) {
|
|
|
42659
42773
|
}
|
|
42660
42774
|
function migrateOwnerLayout(options = {}) {
|
|
42661
42775
|
const appDir = migrationAppDir(options.homeDir);
|
|
42662
|
-
const cache3 =
|
|
42663
|
-
const installed =
|
|
42776
|
+
const cache3 = join12(appDir, SKILLS_CACHE_DIRNAME);
|
|
42777
|
+
const installed = join12(appDir, INSTALLED_SKILLS_DIRNAME);
|
|
42664
42778
|
const moved = [];
|
|
42665
42779
|
const created = [];
|
|
42666
42780
|
if (isOwnerLayoutMigrated(appDir)) {
|
|
42667
42781
|
return { status: "already-migrated", moved, created };
|
|
42668
42782
|
}
|
|
42669
|
-
if (
|
|
42783
|
+
if (existsSync11(cache3)) {
|
|
42670
42784
|
let entries = [];
|
|
42671
42785
|
try {
|
|
42672
42786
|
entries = readdirSync7(cache3);
|
|
@@ -42680,7 +42794,7 @@ function migrateOwnerLayout(options = {}) {
|
|
|
42680
42794
|
};
|
|
42681
42795
|
}
|
|
42682
42796
|
}
|
|
42683
|
-
const installedPresent =
|
|
42797
|
+
const installedPresent = existsSync11(installed);
|
|
42684
42798
|
const legacy = listLegacyFlatSkillDirs(appDir);
|
|
42685
42799
|
const planned = [...installedPresent ? [INSTALLED_SKILLS_DIRNAME] : [], ...legacy];
|
|
42686
42800
|
if (!installedPresent && legacy.length === 0) {
|
|
@@ -42693,7 +42807,7 @@ function migrateOwnerLayout(options = {}) {
|
|
|
42693
42807
|
}
|
|
42694
42808
|
const collisionNames = new Set;
|
|
42695
42809
|
for (const dir of [cache3, ...installedPresent ? [installed] : []]) {
|
|
42696
|
-
if (!
|
|
42810
|
+
if (!existsSync11(dir))
|
|
42697
42811
|
continue;
|
|
42698
42812
|
try {
|
|
42699
42813
|
for (const entry of readdirSync7(dir))
|
|
@@ -42710,7 +42824,7 @@ function migrateOwnerLayout(options = {}) {
|
|
|
42710
42824
|
};
|
|
42711
42825
|
}
|
|
42712
42826
|
}
|
|
42713
|
-
if (
|
|
42827
|
+
if (existsSync11(cache3)) {
|
|
42714
42828
|
rmSync2(cache3, { recursive: true, force: true });
|
|
42715
42829
|
}
|
|
42716
42830
|
if (installedPresent) {
|
|
@@ -42720,7 +42834,7 @@ function migrateOwnerLayout(options = {}) {
|
|
|
42720
42834
|
mkdirSync4(cache3, { recursive: true });
|
|
42721
42835
|
}
|
|
42722
42836
|
for (const name of legacy) {
|
|
42723
|
-
renameSync2(
|
|
42837
|
+
renameSync2(join12(appDir, name), join12(cache3, name));
|
|
42724
42838
|
moved.push(name);
|
|
42725
42839
|
}
|
|
42726
42840
|
createLazyDirs(appDir, created);
|
|
@@ -42744,7 +42858,7 @@ var init_home_migration = __esm(() => {
|
|
|
42744
42858
|
// src/lib/agent-sync.ts
|
|
42745
42859
|
import {
|
|
42746
42860
|
cpSync as cpSync3,
|
|
42747
|
-
existsSync as
|
|
42861
|
+
existsSync as existsSync12,
|
|
42748
42862
|
mkdirSync as mkdirSync5,
|
|
42749
42863
|
mkdtempSync as mkdtempSync2,
|
|
42750
42864
|
readFileSync as readFileSync8,
|
|
@@ -42754,8 +42868,8 @@ import {
|
|
|
42754
42868
|
statSync as statSync8,
|
|
42755
42869
|
writeFileSync as writeFileSync5
|
|
42756
42870
|
} from "fs";
|
|
42757
|
-
import { homedir as
|
|
42758
|
-
import { basename as basename3, dirname as dirname4, join as
|
|
42871
|
+
import { homedir as homedir5 } from "os";
|
|
42872
|
+
import { basename as basename3, dirname as dirname4, join as join13 } from "path";
|
|
42759
42873
|
function isSyncAgent(value) {
|
|
42760
42874
|
return SYNC_AGENTS.includes(value);
|
|
42761
42875
|
}
|
|
@@ -42767,12 +42881,12 @@ function resolveSyncAgents(arg) {
|
|
|
42767
42881
|
}
|
|
42768
42882
|
return [arg];
|
|
42769
42883
|
}
|
|
42770
|
-
function agentGlobalSkillsDir(agent, homeDir =
|
|
42884
|
+
function agentGlobalSkillsDir(agent, homeDir = homedir5()) {
|
|
42771
42885
|
switch (agent) {
|
|
42772
42886
|
case "opencode":
|
|
42773
|
-
return
|
|
42887
|
+
return join13(homeDir, ".config", "opencode", "skills");
|
|
42774
42888
|
default:
|
|
42775
|
-
return
|
|
42889
|
+
return join13(homeDir, `.${agent}`, "skills");
|
|
42776
42890
|
}
|
|
42777
42891
|
}
|
|
42778
42892
|
function adaptSkillMdForAgent(skillMd, agent) {
|
|
@@ -42830,8 +42944,8 @@ function resolveSyncCorpus(options = {}) {
|
|
|
42830
42944
|
function packageSourceRoots(source) {
|
|
42831
42945
|
const roots = [];
|
|
42832
42946
|
for (const sub of ["skills"]) {
|
|
42833
|
-
const candidate =
|
|
42834
|
-
if (
|
|
42947
|
+
const candidate = join13(source, sub);
|
|
42948
|
+
if (existsSync12(candidate) && isDirectory(candidate))
|
|
42835
42949
|
roots.push(candidate);
|
|
42836
42950
|
}
|
|
42837
42951
|
if (roots.length > 0)
|
|
@@ -42846,10 +42960,10 @@ function containsSkillDirectories(path) {
|
|
|
42846
42960
|
return false;
|
|
42847
42961
|
}
|
|
42848
42962
|
return entries.some((entry) => {
|
|
42849
|
-
const candidate =
|
|
42963
|
+
const candidate = join13(path, entry);
|
|
42850
42964
|
if (!isDirectory(candidate))
|
|
42851
42965
|
return false;
|
|
42852
|
-
return
|
|
42966
|
+
return existsSync12(join13(candidate, "SKILL.md")) || existsSync12(join13(candidate, "skill.json")) || existsSync12(join13(candidate, "package.json"));
|
|
42853
42967
|
});
|
|
42854
42968
|
}
|
|
42855
42969
|
function isDirectory(path) {
|
|
@@ -42862,7 +42976,7 @@ function isDirectory(path) {
|
|
|
42862
42976
|
function syncSkillsToAgents(options = {}) {
|
|
42863
42977
|
const requested = normalizeRequested(options.names);
|
|
42864
42978
|
const agents = options.agents?.length ? options.agents : [...SYNC_AGENTS];
|
|
42865
|
-
const homeDir = options.homeDir ??
|
|
42979
|
+
const homeDir = options.homeDir ?? homedir5();
|
|
42866
42980
|
const { roots, source } = resolveSyncCorpus(options);
|
|
42867
42981
|
const corpus = listPortableSkillsAcrossRoots(roots);
|
|
42868
42982
|
const byName = new Map(corpus.map((skill) => [skill.name, skill]));
|
|
@@ -42888,7 +43002,7 @@ function syncSkillsToAgents(options = {}) {
|
|
|
42888
43002
|
actions.push({
|
|
42889
43003
|
skill: name,
|
|
42890
43004
|
agent,
|
|
42891
|
-
path:
|
|
43005
|
+
path: join13(agentGlobalSkillsDir(agent, homeDir), name, "SKILL.md"),
|
|
42892
43006
|
action: "skip",
|
|
42893
43007
|
reason: "not found in this machine's corpus"
|
|
42894
43008
|
});
|
|
@@ -42917,8 +43031,8 @@ function syncSkillsToAgents(options = {}) {
|
|
|
42917
43031
|
return { actions };
|
|
42918
43032
|
}
|
|
42919
43033
|
function writeManagedAgentSkill(params) {
|
|
42920
|
-
const homeDir = params.homeDir ??
|
|
42921
|
-
const dir =
|
|
43034
|
+
const homeDir = params.homeDir ?? homedir5();
|
|
43035
|
+
const dir = join13(agentGlobalSkillsDir(params.agent, homeDir), params.skill);
|
|
42922
43036
|
const result2 = writeManagedSkillDir(dir, params.skillMd, {
|
|
42923
43037
|
skill: params.skill,
|
|
42924
43038
|
source: params.source,
|
|
@@ -42935,11 +43049,11 @@ function writeManagedAgentSkill(params) {
|
|
|
42935
43049
|
};
|
|
42936
43050
|
}
|
|
42937
43051
|
function writeManagedSkillDir(dir, skillMd, options) {
|
|
42938
|
-
const skillMdPath =
|
|
42939
|
-
const markerPath =
|
|
42940
|
-
const dirExists =
|
|
42941
|
-
const managed =
|
|
42942
|
-
const hasSkillMd =
|
|
43052
|
+
const skillMdPath = join13(dir, "SKILL.md");
|
|
43053
|
+
const markerPath = join13(dir, SYNC_MARKER_FILE);
|
|
43054
|
+
const dirExists = existsSync12(dir);
|
|
43055
|
+
const managed = existsSync12(markerPath);
|
|
43056
|
+
const hasSkillMd = existsSync12(skillMdPath);
|
|
42943
43057
|
if (dirExists && !managed && !hasSkillMd) {
|
|
42944
43058
|
return {
|
|
42945
43059
|
action: "skip",
|
|
@@ -42974,11 +43088,11 @@ function writeManagedSkillDir(dir, skillMd, options) {
|
|
|
42974
43088
|
return { action, path: skillMdPath };
|
|
42975
43089
|
const parentDir = dirname4(dir);
|
|
42976
43090
|
mkdirSync5(parentDir, { recursive: true });
|
|
42977
|
-
const transactionDir = mkdtempSync2(
|
|
42978
|
-
const candidateDir =
|
|
42979
|
-
const backupDir =
|
|
42980
|
-
const candidateSkillMdPath =
|
|
42981
|
-
const candidateMarkerPath =
|
|
43091
|
+
const transactionDir = mkdtempSync2(join13(parentDir, `.hasna-skills-write-${basename3(dir)}-`));
|
|
43092
|
+
const candidateDir = join13(transactionDir, "candidate");
|
|
43093
|
+
const backupDir = join13(transactionDir, "backup");
|
|
43094
|
+
const candidateSkillMdPath = join13(candidateDir, "SKILL.md");
|
|
43095
|
+
const candidateMarkerPath = join13(candidateDir, SYNC_MARKER_FILE);
|
|
42982
43096
|
const marker = {
|
|
42983
43097
|
managedBy: SYNC_MARKER_MANAGED_BY,
|
|
42984
43098
|
skill: options.skill,
|
|
@@ -43005,9 +43119,9 @@ function writeManagedSkillDir(dir, skillMd, options) {
|
|
|
43005
43119
|
}
|
|
43006
43120
|
renameDirectory(candidateDir, dir);
|
|
43007
43121
|
} catch (error) {
|
|
43008
|
-
if (originalMoved &&
|
|
43122
|
+
if (originalMoved && existsSync12(backupDir)) {
|
|
43009
43123
|
try {
|
|
43010
|
-
if (
|
|
43124
|
+
if (existsSync12(dir))
|
|
43011
43125
|
rmSync3(dir, { recursive: true, force: true });
|
|
43012
43126
|
renameDirectory(backupDir, dir);
|
|
43013
43127
|
originalMoved = false;
|
|
@@ -43028,8 +43142,8 @@ function writeManagedSkillDir(dir, skillMd, options) {
|
|
|
43028
43142
|
}
|
|
43029
43143
|
function sourceSkillMd(skillPath, name, description, kind, preferBundledDocs = false) {
|
|
43030
43144
|
if (kind === undefined || kind === "instruction" || preferBundledDocs) {
|
|
43031
|
-
const skillMdPath =
|
|
43032
|
-
if (
|
|
43145
|
+
const skillMdPath = join13(skillPath, "SKILL.md");
|
|
43146
|
+
if (existsSync12(skillMdPath))
|
|
43033
43147
|
return readFileSync8(skillMdPath, "utf-8");
|
|
43034
43148
|
}
|
|
43035
43149
|
return pointerSkillMd(name, description);
|
|
@@ -43066,17 +43180,17 @@ function normalizeSkillName(name) {
|
|
|
43066
43180
|
}
|
|
43067
43181
|
|
|
43068
43182
|
// src/lib/project-state.ts
|
|
43069
|
-
import { existsSync as
|
|
43070
|
-
import { join as
|
|
43183
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync6, readFileSync as readFileSync9, writeFileSync as writeFileSync6 } from "fs";
|
|
43184
|
+
import { join as join14 } from "path";
|
|
43071
43185
|
function getProjectStateDir(targetDir = process.cwd()) {
|
|
43072
|
-
return
|
|
43186
|
+
return join14(targetDir, SKILLS_PROJECT_DIR);
|
|
43073
43187
|
}
|
|
43074
43188
|
function getProjectConfigPath(targetDir = process.cwd()) {
|
|
43075
|
-
return
|
|
43189
|
+
return join14(getProjectStateDir(targetDir), PROJECT_CONFIG_FILE);
|
|
43076
43190
|
}
|
|
43077
43191
|
function loadProjectConfig(targetDir = process.cwd()) {
|
|
43078
43192
|
const path = getProjectConfigPath(targetDir);
|
|
43079
|
-
if (!
|
|
43193
|
+
if (!existsSync13(path))
|
|
43080
43194
|
return null;
|
|
43081
43195
|
try {
|
|
43082
43196
|
return normalizeProjectConfig(JSON.parse(readFileSync9(path, "utf-8")));
|
|
@@ -43233,44 +43347,44 @@ __export(exports_installer, {
|
|
|
43233
43347
|
AGENT_TARGETS: () => AGENT_TARGETS,
|
|
43234
43348
|
AGENT_LABELS: () => AGENT_LABELS
|
|
43235
43349
|
});
|
|
43236
|
-
import { existsSync as
|
|
43237
|
-
import { dirname as dirname5, join as
|
|
43238
|
-
import { homedir as
|
|
43350
|
+
import { existsSync as existsSync14, readFileSync as readFileSync10, rmSync as rmSync4 } from "fs";
|
|
43351
|
+
import { dirname as dirname5, join as join15 } from "path";
|
|
43352
|
+
import { homedir as homedir6 } from "os";
|
|
43239
43353
|
import { fileURLToPath } from "url";
|
|
43240
43354
|
function findSkillsDir() {
|
|
43241
43355
|
let dir = __dirname2;
|
|
43242
43356
|
for (let i = 0;i < 5; i++) {
|
|
43243
|
-
const candidate =
|
|
43244
|
-
if (
|
|
43357
|
+
const candidate = join15(dir, "skills");
|
|
43358
|
+
if (existsSync14(candidate) && !dir.includes(".skills"))
|
|
43245
43359
|
return candidate;
|
|
43246
43360
|
dir = dirname5(dir);
|
|
43247
43361
|
}
|
|
43248
|
-
return
|
|
43362
|
+
return join15(__dirname2, "..", "skills");
|
|
43249
43363
|
}
|
|
43250
43364
|
function getSkillPath(name) {
|
|
43251
43365
|
const skillName = normalizeSkillName(getCanonicalSkillName(name));
|
|
43252
43366
|
const portable = findPortableSkill(skillName);
|
|
43253
43367
|
if (portable)
|
|
43254
43368
|
return portable.path;
|
|
43255
|
-
const legacyCustomPath =
|
|
43256
|
-
if (
|
|
43369
|
+
const legacyCustomPath = join15(getDataDir(), "custom", skillName);
|
|
43370
|
+
if (existsSync14(legacyCustomPath))
|
|
43257
43371
|
return legacyCustomPath;
|
|
43258
43372
|
const extensionPath = findExtensionSkillPath(skillName);
|
|
43259
43373
|
if (extensionPath)
|
|
43260
43374
|
return extensionPath;
|
|
43261
|
-
return
|
|
43375
|
+
return join15(SKILLS_DIR, skillName);
|
|
43262
43376
|
}
|
|
43263
43377
|
function getCanonicalSkillName(name) {
|
|
43264
43378
|
return getSkill(name)?.name ?? resolveSkillAlias(normalizeSkillSlug(name));
|
|
43265
43379
|
}
|
|
43266
43380
|
function skillExists(name) {
|
|
43267
|
-
return
|
|
43381
|
+
return existsSync14(getSkillPath(name));
|
|
43268
43382
|
}
|
|
43269
43383
|
function installSkill(name, options = {}) {
|
|
43270
43384
|
const { targetDir = process.cwd(), overwrite = false } = options;
|
|
43271
43385
|
const canonicalName = getCanonicalSkillName(name);
|
|
43272
43386
|
const skillName = normalizeSkillName(canonicalName);
|
|
43273
|
-
if (!
|
|
43387
|
+
if (!existsSync14(getSkillPath(name))) {
|
|
43274
43388
|
const knownOfficial = Boolean(getSkill(name));
|
|
43275
43389
|
return {
|
|
43276
43390
|
skill: canonicalName,
|
|
@@ -43320,7 +43434,7 @@ function installRemoteSkill(skill, options = {}) {
|
|
|
43320
43434
|
}
|
|
43321
43435
|
function installSkillSource(name, _options = {}) {
|
|
43322
43436
|
const canonicalName = getCanonicalSkillName(name);
|
|
43323
|
-
if (!
|
|
43437
|
+
if (!existsSync14(getSkillPath(name))) {
|
|
43324
43438
|
return { skill: canonicalName, success: false, error: `Skill '${name}' not found`, mode: "source" };
|
|
43325
43439
|
}
|
|
43326
43440
|
return {
|
|
@@ -43341,11 +43455,11 @@ function installSkillManifest(manifest, _options = {}) {
|
|
|
43341
43455
|
}
|
|
43342
43456
|
function createLocalSkillManifest(name, generateSkillMd) {
|
|
43343
43457
|
const sourcePath = getSkillPath(name);
|
|
43344
|
-
if (!
|
|
43458
|
+
if (!existsSync14(sourcePath))
|
|
43345
43459
|
return null;
|
|
43346
43460
|
let skillMd = "";
|
|
43347
|
-
const skillMdPath =
|
|
43348
|
-
if (
|
|
43461
|
+
const skillMdPath = join15(sourcePath, "SKILL.md");
|
|
43462
|
+
if (existsSync14(skillMdPath)) {
|
|
43349
43463
|
skillMd = readFileSync10(skillMdPath, "utf-8");
|
|
43350
43464
|
} else if (generateSkillMd) {
|
|
43351
43465
|
skillMd = generateSkillMd(name) ?? "";
|
|
@@ -43426,20 +43540,20 @@ function getAgentSkillsDir(agent, scope = "global", projectDir) {
|
|
|
43426
43540
|
const base2 = projectDir || process.cwd();
|
|
43427
43541
|
switch (agent) {
|
|
43428
43542
|
case "pi":
|
|
43429
|
-
return scope === "project" ?
|
|
43543
|
+
return scope === "project" ? join15(base2, ".pi", "skills") : join15(homedir6(), ".pi", "agent", "skills");
|
|
43430
43544
|
case "opencode":
|
|
43431
|
-
return scope === "project" ?
|
|
43545
|
+
return scope === "project" ? join15(base2, ".opencode", "skills") : join15(homedir6(), ".config", "opencode", "skills");
|
|
43432
43546
|
default:
|
|
43433
|
-
return scope === "project" ?
|
|
43547
|
+
return scope === "project" ? join15(base2, `.${agent}`, "skills") : join15(homedir6(), `.${agent}`, "skills");
|
|
43434
43548
|
}
|
|
43435
43549
|
}
|
|
43436
43550
|
function getAgentSkillPath(name, agent, scope = "global", projectDir) {
|
|
43437
43551
|
const skillName = normalizeSkillName(getCanonicalSkillName(name));
|
|
43438
|
-
return
|
|
43552
|
+
return join15(getAgentSkillsDir(agent, scope, projectDir), skillName);
|
|
43439
43553
|
}
|
|
43440
43554
|
function installSkillForAgent(name, options, generateSkillMd) {
|
|
43441
43555
|
const canonicalName = getCanonicalSkillName(name);
|
|
43442
|
-
if (!
|
|
43556
|
+
if (!existsSync14(getSkillPath(name))) {
|
|
43443
43557
|
return { skill: canonicalName, success: false, error: `Skill '${name}' not found` };
|
|
43444
43558
|
}
|
|
43445
43559
|
const scope = options.scope ?? "global";
|
|
@@ -43463,15 +43577,15 @@ function removeSkillForAgent(name, options) {
|
|
|
43463
43577
|
const canonicalName = getCanonicalSkillName(name);
|
|
43464
43578
|
const scope = options.scope ?? "global";
|
|
43465
43579
|
const dir = getAgentSkillPath(canonicalName, options.agent, scope, options.projectDir);
|
|
43466
|
-
if (!
|
|
43580
|
+
if (!existsSync14(join15(dir, SYNC_MARKER_FILE)))
|
|
43467
43581
|
return false;
|
|
43468
43582
|
rmSync4(dir, { recursive: true, force: true });
|
|
43469
43583
|
return true;
|
|
43470
43584
|
}
|
|
43471
43585
|
function resolveAgentSkillMd(name, generateSkillMd) {
|
|
43472
43586
|
const sourcePath = getSkillPath(name);
|
|
43473
|
-
const skillMdPath =
|
|
43474
|
-
if (
|
|
43587
|
+
const skillMdPath = join15(sourcePath, "SKILL.md");
|
|
43588
|
+
if (existsSync14(skillMdPath))
|
|
43475
43589
|
return readFileSync10(skillMdPath, "utf-8");
|
|
43476
43590
|
if (generateSkillMd)
|
|
43477
43591
|
return generateSkillMd(name);
|
|
@@ -43490,7 +43604,7 @@ function warnMissingDependencies(name, targetDir) {
|
|
|
43490
43604
|
}
|
|
43491
43605
|
function generateMinimalSkillMd(name) {
|
|
43492
43606
|
const sourcePath = getSkillPath(name);
|
|
43493
|
-
if (!
|
|
43607
|
+
if (!existsSync14(sourcePath))
|
|
43494
43608
|
return null;
|
|
43495
43609
|
const canonicalName = getCanonicalSkillName(name);
|
|
43496
43610
|
const meta = getSkill(canonicalName);
|
|
@@ -43505,7 +43619,7 @@ function generateMinimalSkillMd(name) {
|
|
|
43505
43619
|
"---",
|
|
43506
43620
|
""
|
|
43507
43621
|
].filter(Boolean);
|
|
43508
|
-
const fallbackDoc = readFileIfExists(
|
|
43622
|
+
const fallbackDoc = readFileIfExists(join15(sourcePath, "README.md")) || readFileIfExists(join15(sourcePath, "CLAUDE.md"));
|
|
43509
43623
|
if (fallbackDoc)
|
|
43510
43624
|
return `${frontmatter.join(`
|
|
43511
43625
|
`)}${fallbackDoc.trim()}
|
|
@@ -43524,8 +43638,8 @@ skills run ${canonicalName}
|
|
|
43524
43638
|
`;
|
|
43525
43639
|
}
|
|
43526
43640
|
function readBundledSkillVersion(name) {
|
|
43527
|
-
const pkgPath =
|
|
43528
|
-
if (!
|
|
43641
|
+
const pkgPath = join15(getSkillPath(name), "package.json");
|
|
43642
|
+
if (!existsSync14(pkgPath))
|
|
43529
43643
|
return "unknown";
|
|
43530
43644
|
try {
|
|
43531
43645
|
const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
|
|
@@ -43535,7 +43649,7 @@ function readBundledSkillVersion(name) {
|
|
|
43535
43649
|
}
|
|
43536
43650
|
}
|
|
43537
43651
|
function readFileIfExists(path) {
|
|
43538
|
-
return
|
|
43652
|
+
return existsSync14(path) ? readFileSync10(path, "utf-8") : null;
|
|
43539
43653
|
}
|
|
43540
43654
|
function loadProjectConfigCompat(targetDir) {
|
|
43541
43655
|
return loadProjectConfig(targetDir);
|
|
@@ -47654,23 +47768,23 @@ __export(exports_auth_store, {
|
|
|
47654
47768
|
getApiKey: () => getApiKey,
|
|
47655
47769
|
clearAuthConfig: () => clearAuthConfig
|
|
47656
47770
|
});
|
|
47657
|
-
import { existsSync as
|
|
47658
|
-
import { dirname as dirname6, join as
|
|
47659
|
-
import { homedir as
|
|
47771
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync7, readFileSync as readFileSync11, writeFileSync as writeFileSync7, unlinkSync } from "fs";
|
|
47772
|
+
import { dirname as dirname6, join as join16 } from "path";
|
|
47773
|
+
import { homedir as homedir7 } from "os";
|
|
47660
47774
|
function getAuthFilePath() {
|
|
47661
|
-
return
|
|
47775
|
+
return join16(getDataDir(), "auth.json");
|
|
47662
47776
|
}
|
|
47663
47777
|
function getAuthFilePathReadOnly() {
|
|
47664
|
-
return
|
|
47778
|
+
return join16(getDataDirReadOnly(), "auth.json");
|
|
47665
47779
|
}
|
|
47666
47780
|
function legacyAuthFilePath() {
|
|
47667
|
-
return
|
|
47781
|
+
return join16(process.env["HOME"] || process.env["USERPROFILE"] || homedir7(), ".skills", "auth.json");
|
|
47668
47782
|
}
|
|
47669
47783
|
function getAuthConfig() {
|
|
47670
47784
|
if (cachedConfig !== undefined)
|
|
47671
47785
|
return cachedConfig;
|
|
47672
47786
|
try {
|
|
47673
|
-
const file =
|
|
47787
|
+
const file = existsSync15(getAuthFilePath()) ? getAuthFilePath() : legacyAuthFilePath();
|
|
47674
47788
|
const raw = readFileSync11(file, "utf-8");
|
|
47675
47789
|
const config = JSON.parse(raw);
|
|
47676
47790
|
if (!config.apiKey) {
|
|
@@ -47709,7 +47823,7 @@ function getApiKey() {
|
|
|
47709
47823
|
}
|
|
47710
47824
|
function getAuthConfigReadOnly() {
|
|
47711
47825
|
try {
|
|
47712
|
-
const file =
|
|
47826
|
+
const file = existsSync15(getAuthFilePathReadOnly()) ? getAuthFilePathReadOnly() : legacyAuthFilePath();
|
|
47713
47827
|
const raw = readFileSync11(file, "utf-8");
|
|
47714
47828
|
const config = JSON.parse(raw);
|
|
47715
47829
|
if (!config.apiKey)
|
|
@@ -48266,12 +48380,12 @@ function handleBrowseError(error) {
|
|
|
48266
48380
|
async function writeJson(value, space) {
|
|
48267
48381
|
const text = `${JSON.stringify(value, null, space)}
|
|
48268
48382
|
`;
|
|
48269
|
-
await new Promise((
|
|
48383
|
+
await new Promise((resolve2, reject2) => {
|
|
48270
48384
|
process.stdout.write(text, (error) => {
|
|
48271
48385
|
if (error)
|
|
48272
48386
|
reject2(error);
|
|
48273
48387
|
else
|
|
48274
|
-
|
|
48388
|
+
resolve2();
|
|
48275
48389
|
});
|
|
48276
48390
|
});
|
|
48277
48391
|
}
|
|
@@ -48602,13 +48716,13 @@ __export(exports_skillinfo, {
|
|
|
48602
48716
|
generateEnvExample: () => generateEnvExample,
|
|
48603
48717
|
detectProjectSkills: () => detectProjectSkills
|
|
48604
48718
|
});
|
|
48605
|
-
import { existsSync as
|
|
48606
|
-
import { join as
|
|
48719
|
+
import { existsSync as existsSync16, readFileSync as readFileSync12 } from "fs";
|
|
48720
|
+
import { join as join17 } from "path";
|
|
48607
48721
|
function isInstructionSkillDir(skillPath, meta) {
|
|
48608
48722
|
if (meta?.kind === "instruction")
|
|
48609
48723
|
return true;
|
|
48610
|
-
const skillMdPath =
|
|
48611
|
-
if (!
|
|
48724
|
+
const skillMdPath = join17(skillPath, "SKILL.md");
|
|
48725
|
+
if (!existsSync16(skillMdPath))
|
|
48612
48726
|
return false;
|
|
48613
48727
|
try {
|
|
48614
48728
|
return parseSkillFrontmatter(readFileSync12(skillMdPath, "utf-8"))?.kind === "instruction";
|
|
@@ -48618,12 +48732,12 @@ function isInstructionSkillDir(skillPath, meta) {
|
|
|
48618
48732
|
}
|
|
48619
48733
|
function getSkillDocs(name) {
|
|
48620
48734
|
const skillPath = getSkillPath(name);
|
|
48621
|
-
if (!
|
|
48735
|
+
if (!existsSync16(skillPath))
|
|
48622
48736
|
return null;
|
|
48623
48737
|
return {
|
|
48624
|
-
skillMd: readIfExists(
|
|
48625
|
-
readme: readIfExists(
|
|
48626
|
-
claudeMd: readIfExists(
|
|
48738
|
+
skillMd: readIfExists(join17(skillPath, "SKILL.md")),
|
|
48739
|
+
readme: readIfExists(join17(skillPath, "README.md")),
|
|
48740
|
+
claudeMd: readIfExists(join17(skillPath, "CLAUDE.md"))
|
|
48627
48741
|
};
|
|
48628
48742
|
}
|
|
48629
48743
|
function getSkillBestDoc(name) {
|
|
@@ -48634,11 +48748,11 @@ function getSkillBestDoc(name) {
|
|
|
48634
48748
|
}
|
|
48635
48749
|
function getSkillRequirements(name) {
|
|
48636
48750
|
const skillPath = getSkillPath(name);
|
|
48637
|
-
if (!
|
|
48751
|
+
if (!existsSync16(skillPath))
|
|
48638
48752
|
return null;
|
|
48639
48753
|
const texts = [];
|
|
48640
48754
|
for (const file of ["SKILL.md", "README.md", "CLAUDE.md", ".env.example", ".env.local.example"]) {
|
|
48641
|
-
const content = readIfExists(
|
|
48755
|
+
const content = readIfExists(join17(skillPath, file));
|
|
48642
48756
|
if (content)
|
|
48643
48757
|
texts.push(content);
|
|
48644
48758
|
}
|
|
@@ -48677,8 +48791,8 @@ function getSkillRequirements(name) {
|
|
|
48677
48791
|
const skillName = normalizeSkillName(name);
|
|
48678
48792
|
let cliCommand = `skills run ${skillName}`;
|
|
48679
48793
|
let dependencies = {};
|
|
48680
|
-
const pkgPath =
|
|
48681
|
-
if (
|
|
48794
|
+
const pkgPath = join17(skillPath, "package.json");
|
|
48795
|
+
if (existsSync16(pkgPath)) {
|
|
48682
48796
|
try {
|
|
48683
48797
|
const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
|
|
48684
48798
|
dependencies = pkg.dependencies || {};
|
|
@@ -48697,9 +48811,9 @@ function isHostedPremiumSkill(skillName, meta) {
|
|
|
48697
48811
|
function isPackageResolvable(pkgName, fromDir) {
|
|
48698
48812
|
let dir = fromDir;
|
|
48699
48813
|
while (true) {
|
|
48700
|
-
if (
|
|
48814
|
+
if (existsSync16(join17(dir, "node_modules", pkgName, "package.json")))
|
|
48701
48815
|
return true;
|
|
48702
|
-
const parent =
|
|
48816
|
+
const parent = join17(dir, "..");
|
|
48703
48817
|
if (parent === dir)
|
|
48704
48818
|
return false;
|
|
48705
48819
|
dir = parent;
|
|
@@ -48719,7 +48833,7 @@ async function runSkill(name, args, options = {}) {
|
|
|
48719
48833
|
const meta = getSkill(name);
|
|
48720
48834
|
const canonicalName = meta?.name ?? name;
|
|
48721
48835
|
const skillPath = getSkillPath(canonicalName);
|
|
48722
|
-
if (!
|
|
48836
|
+
if (!existsSync16(skillPath)) {
|
|
48723
48837
|
return { exitCode: 1, error: `Skill '${name}' not found` };
|
|
48724
48838
|
}
|
|
48725
48839
|
if (isInstructionSkillDir(skillPath, meta)) {
|
|
@@ -48728,8 +48842,8 @@ async function runSkill(name, args, options = {}) {
|
|
|
48728
48842
|
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'.`
|
|
48729
48843
|
};
|
|
48730
48844
|
}
|
|
48731
|
-
const pkgPath =
|
|
48732
|
-
if (!
|
|
48845
|
+
const pkgPath = join17(skillPath, "package.json");
|
|
48846
|
+
if (!existsSync16(pkgPath)) {
|
|
48733
48847
|
return { exitCode: 1, error: `No package.json in skill '${name}'` };
|
|
48734
48848
|
}
|
|
48735
48849
|
let entryPoint;
|
|
@@ -48748,12 +48862,12 @@ async function runSkill(name, args, options = {}) {
|
|
|
48748
48862
|
} catch {
|
|
48749
48863
|
return { exitCode: 1, error: `Failed to parse package.json for skill '${name}'` };
|
|
48750
48864
|
}
|
|
48751
|
-
const entryPath =
|
|
48752
|
-
if (!
|
|
48865
|
+
const entryPath = join17(skillPath, entryPoint);
|
|
48866
|
+
if (!existsSync16(entryPath)) {
|
|
48753
48867
|
return { exitCode: 1, error: `Entry point '${entryPoint}' not found in skill '${name}'` };
|
|
48754
48868
|
}
|
|
48755
|
-
const nodeModules =
|
|
48756
|
-
if (!
|
|
48869
|
+
const nodeModules = join17(skillPath, "node_modules");
|
|
48870
|
+
if (!existsSync16(nodeModules)) {
|
|
48757
48871
|
const install = Bun.spawn(["bun", "install", "--no-save"], {
|
|
48758
48872
|
cwd: skillPath,
|
|
48759
48873
|
stdout: "pipe",
|
|
@@ -48780,8 +48894,8 @@ async function runSkill(name, args, options = {}) {
|
|
|
48780
48894
|
return { exitCode };
|
|
48781
48895
|
}
|
|
48782
48896
|
function detectProjectSkills(cwd2 = process.cwd()) {
|
|
48783
|
-
const pkgPath =
|
|
48784
|
-
if (!
|
|
48897
|
+
const pkgPath = join17(cwd2, "package.json");
|
|
48898
|
+
if (!existsSync16(pkgPath)) {
|
|
48785
48899
|
const alwaysRecommend = ["market-research-report", "repo-onboarding-report", "blog-article"];
|
|
48786
48900
|
const recommended2 = alwaysRecommend.map((name) => loadRegistry().find((s) => s.name === name)).filter((s) => s !== undefined);
|
|
48787
48901
|
return { detected: [], recommended: recommended2 };
|
|
@@ -48908,7 +49022,7 @@ function generateSkillMd(name) {
|
|
|
48908
49022
|
if (!meta)
|
|
48909
49023
|
return null;
|
|
48910
49024
|
const skillPath = getSkillPath(name);
|
|
48911
|
-
if (!
|
|
49025
|
+
if (!existsSync16(skillPath))
|
|
48912
49026
|
return null;
|
|
48913
49027
|
const frontmatter = [
|
|
48914
49028
|
"---",
|
|
@@ -48917,11 +49031,11 @@ function generateSkillMd(name) {
|
|
|
48917
49031
|
"---"
|
|
48918
49032
|
].join(`
|
|
48919
49033
|
`);
|
|
48920
|
-
const readme = readIfExists(
|
|
48921
|
-
const claudeMd = readIfExists(
|
|
49034
|
+
const readme = readIfExists(join17(skillPath, "README.md"));
|
|
49035
|
+
const claudeMd = readIfExists(join17(skillPath, "CLAUDE.md"));
|
|
48922
49036
|
let cliCommand = null;
|
|
48923
|
-
const pkgPath =
|
|
48924
|
-
if (
|
|
49037
|
+
const pkgPath = join17(skillPath, "package.json");
|
|
49038
|
+
if (existsSync16(pkgPath)) {
|
|
48925
49039
|
try {
|
|
48926
49040
|
const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
|
|
48927
49041
|
if (pkg.bin) {
|
|
@@ -48996,7 +49110,7 @@ function extractEnvVars(text) {
|
|
|
48996
49110
|
}
|
|
48997
49111
|
function readIfExists(path) {
|
|
48998
49112
|
try {
|
|
48999
|
-
if (
|
|
49113
|
+
if (existsSync16(path)) {
|
|
49000
49114
|
return readFileSync12(path, "utf-8");
|
|
49001
49115
|
}
|
|
49002
49116
|
} catch {}
|
|
@@ -49032,8 +49146,8 @@ var exports_introspect = {};
|
|
|
49032
49146
|
__export(exports_introspect, {
|
|
49033
49147
|
registerIntrospect: () => registerIntrospect
|
|
49034
49148
|
});
|
|
49035
|
-
import { existsSync as
|
|
49036
|
-
import { join as
|
|
49149
|
+
import { existsSync as existsSync17, readFileSync as readFileSync13 } from "fs";
|
|
49150
|
+
import { join as join18 } from "path";
|
|
49037
49151
|
import { execSync } from "child_process";
|
|
49038
49152
|
function registerIntrospect(parent) {
|
|
49039
49153
|
parent.command("info").argument("<skill>", "Skill name").option("--json", "Output as JSON", false).option("--brief", "Single line: name \u2014 description [category] (tags: ...)", false).option("--remote", "Use remote registry from SKILLS_API_URL or config apiUrl", false).description("Show details about a specific skill").action((name, options) => {
|
|
@@ -49254,20 +49368,20 @@ function handleDiff(name, options) {
|
|
|
49254
49368
|
const bare = name;
|
|
49255
49369
|
const normalized = normalizePortableSkillName(bare);
|
|
49256
49370
|
const sourcePath = getSkillPath(bare);
|
|
49257
|
-
const canonicalDir =
|
|
49258
|
-
const canonicalSkillMd =
|
|
49371
|
+
const canonicalDir = join18(resolveCorpusRoot(), normalized);
|
|
49372
|
+
const canonicalSkillMd = join18(canonicalDir, "SKILL.md");
|
|
49259
49373
|
const canonical = {
|
|
49260
|
-
present:
|
|
49374
|
+
present: existsSync17(canonicalSkillMd),
|
|
49261
49375
|
path: canonicalDir,
|
|
49262
|
-
...
|
|
49263
|
-
...
|
|
49376
|
+
...existsSync17(canonicalSkillMd) ? { hash: hashSkillMarkdownFile(canonicalSkillMd) } : {},
|
|
49377
|
+
...existsSync17(canonicalSkillMd) ? { stub: isPointerSkillMd(readFileSync13(canonicalSkillMd, "utf-8")) } : {}
|
|
49264
49378
|
};
|
|
49265
49379
|
const pinned = getInstalledSkills().includes(bare);
|
|
49266
49380
|
const installMeta = getInstallMeta();
|
|
49267
49381
|
const installedVersion = installMeta.skills[bare]?.version ?? "unknown";
|
|
49268
|
-
const registryPkgPath =
|
|
49382
|
+
const registryPkgPath = join18(sourcePath, "package.json");
|
|
49269
49383
|
let registryVersion = "unknown";
|
|
49270
|
-
if (
|
|
49384
|
+
if (existsSync17(registryPkgPath)) {
|
|
49271
49385
|
try {
|
|
49272
49386
|
registryVersion = JSON.parse(readFileSync13(registryPkgPath, "utf-8")).version || "unknown";
|
|
49273
49387
|
} catch {}
|
|
@@ -49275,13 +49389,13 @@ function handleDiff(name, options) {
|
|
|
49275
49389
|
const upToDate = installedVersion === registryVersion;
|
|
49276
49390
|
const homes = [];
|
|
49277
49391
|
for (const agent of SYNC_AGENTS) {
|
|
49278
|
-
const dir =
|
|
49279
|
-
const present =
|
|
49280
|
-
const managed =
|
|
49281
|
-
const skillMdPath =
|
|
49282
|
-
const hash = present &&
|
|
49392
|
+
const dir = join18(agentGlobalSkillsDir(agent), normalized);
|
|
49393
|
+
const present = existsSync17(dir);
|
|
49394
|
+
const managed = existsSync17(join18(dir, SYNC_MARKER_FILE));
|
|
49395
|
+
const skillMdPath = join18(dir, "SKILL.md");
|
|
49396
|
+
const hash = present && existsSync17(skillMdPath) ? hashSkillMarkdownFile(skillMdPath) : undefined;
|
|
49283
49397
|
let stub;
|
|
49284
|
-
if (present &&
|
|
49398
|
+
if (present && existsSync17(skillMdPath)) {
|
|
49285
49399
|
try {
|
|
49286
49400
|
stub = isPointerSkillMd(readFileSync13(skillMdPath, "utf-8"));
|
|
49287
49401
|
} catch {
|
|
@@ -49816,8 +49930,8 @@ var exports_init = {};
|
|
|
49816
49930
|
__export(exports_init, {
|
|
49817
49931
|
registerSetup: () => registerSetup
|
|
49818
49932
|
});
|
|
49819
|
-
import { existsSync as
|
|
49820
|
-
import { join as
|
|
49933
|
+
import { existsSync as existsSync18, readFileSync as readFileSync14, writeFileSync as writeFileSync8, appendFileSync } from "fs";
|
|
49934
|
+
import { join as join19 } from "path";
|
|
49821
49935
|
function registerSetup(parent) {
|
|
49822
49936
|
parent.command("init").option("--json", "Output as JSON", false).option("--for <agent>", "Detect project type and show MCP registration guidance for agent").option("--scope <scope>", "Deprecated; agent skill-folder installs are disabled", "global").description("Initialize project for pinned skills (.env.example, .gitignore)").action((options) => handleInit(options));
|
|
49823
49937
|
parent.command("export").option("--json", "Output as JSON (default behavior)", false).description("Export pinned skills to JSON for sharing or backup").action((_options) => handleExport());
|
|
@@ -49901,7 +50015,7 @@ Use: skills render`));
|
|
|
49901
50015
|
lines.push(`# Used by: ${skills.join(", ")}`);
|
|
49902
50016
|
lines.push(`${envVar}=`);
|
|
49903
50017
|
}
|
|
49904
|
-
writeFileSync8(
|
|
50018
|
+
writeFileSync8(join19(cwd2, ".env.example"), lines.join(`
|
|
49905
50019
|
`) + `
|
|
49906
50020
|
`);
|
|
49907
50021
|
envVarCount = envMap.size;
|
|
@@ -49909,9 +50023,9 @@ Use: skills render`));
|
|
|
49909
50023
|
console.log(source_default.green(`\u2713 Generated .env.example (${envVarCount} variables from ${installed.length} skills)`));
|
|
49910
50024
|
} else if (!options.json)
|
|
49911
50025
|
console.log(source_default.dim(" No environment variables detected across pinned skills"));
|
|
49912
|
-
const gitignorePath =
|
|
50026
|
+
const gitignorePath = join19(cwd2, ".gitignore");
|
|
49913
50027
|
const gitignoreEntries = [".skills/runs/", ".skills/exports/", ".skills/tmp/"];
|
|
49914
|
-
let gitignoreContent =
|
|
50028
|
+
let gitignoreContent = existsSync18(gitignorePath) ? readFileSync14(gitignorePath, "utf-8") : "";
|
|
49915
50029
|
let gitignoreUpdated = false;
|
|
49916
50030
|
const missingEntries = gitignoreEntries.filter((entry) => !gitignoreContent.includes(entry));
|
|
49917
50031
|
if (missingEntries.length > 0) {
|
|
@@ -49957,7 +50071,7 @@ async function handleImport(file, options) {
|
|
|
49957
50071
|
if (file === "-")
|
|
49958
50072
|
raw = await new Response(process.stdin).text();
|
|
49959
50073
|
else {
|
|
49960
|
-
if (!
|
|
50074
|
+
if (!existsSync18(file)) {
|
|
49961
50075
|
const error = `File not found: ${file}`;
|
|
49962
50076
|
if (options.json)
|
|
49963
50077
|
console.log(JSON.stringify({ imported: 0, error }));
|
|
@@ -50063,12 +50177,12 @@ var init_init = __esm(() => {
|
|
|
50063
50177
|
});
|
|
50064
50178
|
|
|
50065
50179
|
// src/lib/home-adoption.ts
|
|
50066
|
-
import { existsSync as
|
|
50067
|
-
import { homedir as
|
|
50068
|
-
import { join as
|
|
50180
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync8, readdirSync as readdirSync9, readFileSync as readFileSync15, rmSync as rmSync5, statSync as statSync9, writeFileSync as writeFileSync9 } from "fs";
|
|
50181
|
+
import { homedir as homedir8 } from "os";
|
|
50182
|
+
import { join as join20 } from "path";
|
|
50069
50183
|
function indexCanonicalCorpus(corpusRoot) {
|
|
50070
50184
|
const byName = new Map;
|
|
50071
|
-
if (!
|
|
50185
|
+
if (!existsSync19(corpusRoot))
|
|
50072
50186
|
return byName;
|
|
50073
50187
|
let entries = [];
|
|
50074
50188
|
try {
|
|
@@ -50079,8 +50193,8 @@ function indexCanonicalCorpus(corpusRoot) {
|
|
|
50079
50193
|
for (const entry of entries.sort()) {
|
|
50080
50194
|
if (entry.startsWith("."))
|
|
50081
50195
|
continue;
|
|
50082
|
-
const skillMd =
|
|
50083
|
-
if (!
|
|
50196
|
+
const skillMd = join20(corpusRoot, entry, "SKILL.md");
|
|
50197
|
+
if (!existsSync19(skillMd))
|
|
50084
50198
|
continue;
|
|
50085
50199
|
try {
|
|
50086
50200
|
byName.set(entry, hashSkillMarkdownFile(skillMd));
|
|
@@ -50089,14 +50203,14 @@ function indexCanonicalCorpus(corpusRoot) {
|
|
|
50089
50203
|
return byName;
|
|
50090
50204
|
}
|
|
50091
50205
|
function scanUnmarkedHomes(options = {}) {
|
|
50092
|
-
const homeDir = options.homeDir ??
|
|
50206
|
+
const homeDir = options.homeDir ?? homedir8();
|
|
50093
50207
|
const corpusRoot = resolveCorpusRoot(options);
|
|
50094
50208
|
const index = indexCanonicalCorpus(corpusRoot);
|
|
50095
50209
|
const agents = options.agents?.length ? options.agents : [...SYNC_AGENTS];
|
|
50096
50210
|
const scan = { adoptable: [], conflicts: [], unknown: [], managed: 0 };
|
|
50097
50211
|
for (const agent of agents) {
|
|
50098
50212
|
const home = agentGlobalSkillsDir(agent, homeDir);
|
|
50099
|
-
if (!
|
|
50213
|
+
if (!existsSync19(home))
|
|
50100
50214
|
continue;
|
|
50101
50215
|
let entries = [];
|
|
50102
50216
|
try {
|
|
@@ -50107,19 +50221,19 @@ function scanUnmarkedHomes(options = {}) {
|
|
|
50107
50221
|
for (const skill of entries.sort()) {
|
|
50108
50222
|
if (skill.startsWith("."))
|
|
50109
50223
|
continue;
|
|
50110
|
-
const dir =
|
|
50224
|
+
const dir = join20(home, skill);
|
|
50111
50225
|
try {
|
|
50112
50226
|
if (!statSync9(dir).isDirectory())
|
|
50113
50227
|
continue;
|
|
50114
50228
|
} catch {
|
|
50115
50229
|
continue;
|
|
50116
50230
|
}
|
|
50117
|
-
if (
|
|
50231
|
+
if (existsSync19(join20(dir, SYNC_MARKER_FILE))) {
|
|
50118
50232
|
scan.managed += 1;
|
|
50119
50233
|
continue;
|
|
50120
50234
|
}
|
|
50121
|
-
const skillMdPath =
|
|
50122
|
-
if (!
|
|
50235
|
+
const skillMdPath = join20(dir, "SKILL.md");
|
|
50236
|
+
if (!existsSync19(skillMdPath))
|
|
50123
50237
|
continue;
|
|
50124
50238
|
const hash = hashSkillMarkdownFile(skillMdPath);
|
|
50125
50239
|
const mtime = statSync9(skillMdPath).mtime.toISOString();
|
|
@@ -50139,9 +50253,9 @@ function scanUnmarkedHomes(options = {}) {
|
|
|
50139
50253
|
function appendConflictsLedger(appDir, conflicts) {
|
|
50140
50254
|
if (conflicts.length === 0)
|
|
50141
50255
|
return;
|
|
50142
|
-
const ledgerPath =
|
|
50256
|
+
const ledgerPath = join20(appDir, CONFLICTS_LEDGER_FILE);
|
|
50143
50257
|
let ledger = { version: 1, entries: [] };
|
|
50144
|
-
if (
|
|
50258
|
+
if (existsSync19(ledgerPath)) {
|
|
50145
50259
|
try {
|
|
50146
50260
|
const parsed = JSON.parse(readFileSync15(ledgerPath, "utf-8"));
|
|
50147
50261
|
if (parsed && typeof parsed === "object" && Array.isArray(parsed.entries)) {
|
|
@@ -50159,9 +50273,9 @@ function appendConflictsLedger(appDir, conflicts) {
|
|
|
50159
50273
|
`);
|
|
50160
50274
|
}
|
|
50161
50275
|
function writeRollbackRecord(mode, entries, appDir = getDataDir()) {
|
|
50162
|
-
const dir =
|
|
50276
|
+
const dir = join20(appDir, ROLLBACK_DIRNAME);
|
|
50163
50277
|
mkdirSync8(dir, { recursive: true });
|
|
50164
|
-
const file =
|
|
50278
|
+
const file = join20(dir, `${mode}-${Date.now()}.json`);
|
|
50165
50279
|
const record = { version: 1, mode, timestamp: new Date().toISOString(), entries };
|
|
50166
50280
|
writeFileSync9(file, `${JSON.stringify(record, null, 2)}
|
|
50167
50281
|
`);
|
|
@@ -50172,7 +50286,7 @@ function adoptUnmarkedHomes(options = {}) {
|
|
|
50172
50286
|
if (!options.apply) {
|
|
50173
50287
|
return { ...scan, applied: false };
|
|
50174
50288
|
}
|
|
50175
|
-
const appDir = options.homeDir ?
|
|
50289
|
+
const appDir = options.homeDir ? join20(options.homeDir, ".hasna", "skills") : getDataDir();
|
|
50176
50290
|
const markers = scan.adoptable.map((entry) => {
|
|
50177
50291
|
const marker = {
|
|
50178
50292
|
managedBy: SYNC_MARKER_MANAGED_BY,
|
|
@@ -50180,7 +50294,7 @@ function adoptUnmarkedHomes(options = {}) {
|
|
|
50180
50294
|
source: "adopted",
|
|
50181
50295
|
syncedAt: new Date().toISOString()
|
|
50182
50296
|
};
|
|
50183
|
-
writeFileSync9(
|
|
50297
|
+
writeFileSync9(join20(entry.path, SYNC_MARKER_FILE), `${JSON.stringify(marker, null, 2)}
|
|
50184
50298
|
`);
|
|
50185
50299
|
return { agent: entry.agent, skill: entry.skill, path: entry.path, hash: entry.hash, marker };
|
|
50186
50300
|
});
|
|
@@ -50192,14 +50306,14 @@ function adoptUnmarkedHomes(options = {}) {
|
|
|
50192
50306
|
return { ...scan, applied: true, rollbackFile };
|
|
50193
50307
|
}
|
|
50194
50308
|
function pruneStrayHomes(options = {}) {
|
|
50195
|
-
const homeDir = options.homeDir ??
|
|
50309
|
+
const homeDir = options.homeDir ?? homedir8();
|
|
50196
50310
|
const corpusRoot = resolveCorpusRoot(options);
|
|
50197
50311
|
const index = indexCanonicalCorpus(corpusRoot);
|
|
50198
50312
|
const agents = options.agents?.length ? options.agents : [...SYNC_AGENTS];
|
|
50199
50313
|
const candidates = [];
|
|
50200
50314
|
for (const agent of agents) {
|
|
50201
50315
|
const home = agentGlobalSkillsDir(agent, homeDir);
|
|
50202
|
-
if (!
|
|
50316
|
+
if (!existsSync19(home))
|
|
50203
50317
|
continue;
|
|
50204
50318
|
let entries = [];
|
|
50205
50319
|
try {
|
|
@@ -50210,15 +50324,15 @@ function pruneStrayHomes(options = {}) {
|
|
|
50210
50324
|
for (const skill of entries.sort()) {
|
|
50211
50325
|
if (skill.startsWith("."))
|
|
50212
50326
|
continue;
|
|
50213
|
-
const dir =
|
|
50327
|
+
const dir = join20(home, skill);
|
|
50214
50328
|
try {
|
|
50215
50329
|
if (!statSync9(dir).isDirectory())
|
|
50216
50330
|
continue;
|
|
50217
50331
|
} catch {
|
|
50218
50332
|
continue;
|
|
50219
50333
|
}
|
|
50220
|
-
const markerPath =
|
|
50221
|
-
if (!
|
|
50334
|
+
const markerPath = join20(dir, SYNC_MARKER_FILE);
|
|
50335
|
+
if (!existsSync19(markerPath))
|
|
50222
50336
|
continue;
|
|
50223
50337
|
if (index.has(skill))
|
|
50224
50338
|
continue;
|
|
@@ -50231,15 +50345,15 @@ function pruneStrayHomes(options = {}) {
|
|
|
50231
50345
|
} catch {
|
|
50232
50346
|
continue;
|
|
50233
50347
|
}
|
|
50234
|
-
const skillMdPath =
|
|
50235
|
-
const hash =
|
|
50348
|
+
const skillMdPath = join20(dir, "SKILL.md");
|
|
50349
|
+
const hash = existsSync19(skillMdPath) ? hashSkillMarkdownFile(skillMdPath) : "";
|
|
50236
50350
|
candidates.push({ agent, skill, home, path: dir, hash, marker });
|
|
50237
50351
|
}
|
|
50238
50352
|
}
|
|
50239
50353
|
if (!options.apply) {
|
|
50240
50354
|
return { candidates, pruned: 0, dryRun: true };
|
|
50241
50355
|
}
|
|
50242
|
-
const appDir = options.homeDir ?
|
|
50356
|
+
const appDir = options.homeDir ? join20(options.homeDir, ".hasna", "skills") : getDataDir();
|
|
50243
50357
|
const rollbackFile = writeRollbackRecord("prune", candidates.map(({ agent, skill, path, hash, marker }) => ({ agent, skill, path, hash, marker })), appDir);
|
|
50244
50358
|
for (const candidate of candidates) {
|
|
50245
50359
|
rmSync5(candidate.path, { recursive: true, force: true });
|
|
@@ -50255,9 +50369,9 @@ var init_home_adoption = __esm(() => {
|
|
|
50255
50369
|
});
|
|
50256
50370
|
|
|
50257
50371
|
// src/lib/home-census.ts
|
|
50258
|
-
import { existsSync as
|
|
50259
|
-
import { homedir as
|
|
50260
|
-
import { join as
|
|
50372
|
+
import { existsSync as existsSync20, readFileSync as readFileSync16, readdirSync as readdirSync10, statSync as statSync10 } from "fs";
|
|
50373
|
+
import { homedir as homedir9 } from "os";
|
|
50374
|
+
import { join as join21 } from "path";
|
|
50261
50375
|
function sortEntries(entries) {
|
|
50262
50376
|
return entries.sort((a, b) => {
|
|
50263
50377
|
const left = `${a.agent}/${a.skill}/${a.kind}`;
|
|
@@ -50266,7 +50380,7 @@ function sortEntries(entries) {
|
|
|
50266
50380
|
});
|
|
50267
50381
|
}
|
|
50268
50382
|
function censusHomeDrift(options = {}) {
|
|
50269
|
-
const homeDir = options.homeDir ??
|
|
50383
|
+
const homeDir = options.homeDir ?? homedir9();
|
|
50270
50384
|
const corpusRoot = resolveCorpusRoot(options);
|
|
50271
50385
|
const index = indexCanonicalCorpus(corpusRoot);
|
|
50272
50386
|
const agents = options.agents?.length ? options.agents : [...SYNC_AGENTS];
|
|
@@ -50276,7 +50390,7 @@ function censusHomeDrift(options = {}) {
|
|
|
50276
50390
|
let homesChecked = 0;
|
|
50277
50391
|
for (const agent of agents) {
|
|
50278
50392
|
const home = agentGlobalSkillsDir(agent, homeDir);
|
|
50279
|
-
if (!
|
|
50393
|
+
if (!existsSync20(home))
|
|
50280
50394
|
continue;
|
|
50281
50395
|
homesChecked += 1;
|
|
50282
50396
|
const present = new Set;
|
|
@@ -50289,7 +50403,7 @@ function censusHomeDrift(options = {}) {
|
|
|
50289
50403
|
for (const skill of dirEntries.sort()) {
|
|
50290
50404
|
if (skill.startsWith("."))
|
|
50291
50405
|
continue;
|
|
50292
|
-
const dir =
|
|
50406
|
+
const dir = join21(home, skill);
|
|
50293
50407
|
try {
|
|
50294
50408
|
if (!statSync10(dir).isDirectory())
|
|
50295
50409
|
continue;
|
|
@@ -50297,8 +50411,8 @@ function censusHomeDrift(options = {}) {
|
|
|
50297
50411
|
continue;
|
|
50298
50412
|
}
|
|
50299
50413
|
present.add(skill);
|
|
50300
|
-
const markerPath =
|
|
50301
|
-
if (!
|
|
50414
|
+
const markerPath = join21(dir, SYNC_MARKER_FILE);
|
|
50415
|
+
if (!existsSync20(markerPath)) {
|
|
50302
50416
|
unmarked += 1;
|
|
50303
50417
|
continue;
|
|
50304
50418
|
}
|
|
@@ -50308,8 +50422,8 @@ function censusHomeDrift(options = {}) {
|
|
|
50308
50422
|
entries.push({ agent, skill, kind: "stray-in-home", path: dir });
|
|
50309
50423
|
continue;
|
|
50310
50424
|
}
|
|
50311
|
-
const skillMdPath =
|
|
50312
|
-
if (!
|
|
50425
|
+
const skillMdPath = join21(dir, "SKILL.md");
|
|
50426
|
+
if (!existsSync20(skillMdPath)) {
|
|
50313
50427
|
entries.push({ agent, skill, kind: "diverged", path: dir, canonicalHash });
|
|
50314
50428
|
continue;
|
|
50315
50429
|
}
|
|
@@ -50322,7 +50436,7 @@ function censusHomeDrift(options = {}) {
|
|
|
50322
50436
|
homeStub = undefined;
|
|
50323
50437
|
}
|
|
50324
50438
|
let canonicalStub;
|
|
50325
|
-
const canonicalSkillMd =
|
|
50439
|
+
const canonicalSkillMd = join21(corpusRoot, skill, "SKILL.md");
|
|
50326
50440
|
try {
|
|
50327
50441
|
canonicalStub = isPointerSkillMd(readFileSync16(canonicalSkillMd, "utf-8"));
|
|
50328
50442
|
} catch {
|
|
@@ -50337,7 +50451,7 @@ function censusHomeDrift(options = {}) {
|
|
|
50337
50451
|
agent,
|
|
50338
50452
|
skill: name,
|
|
50339
50453
|
kind: "missing-from-home",
|
|
50340
|
-
path:
|
|
50454
|
+
path: join21(home, name),
|
|
50341
50455
|
canonicalHash
|
|
50342
50456
|
});
|
|
50343
50457
|
}
|
|
@@ -50363,8 +50477,8 @@ var exports_diagnostic = {};
|
|
|
50363
50477
|
__export(exports_diagnostic, {
|
|
50364
50478
|
registerDiagnostic: () => registerDiagnostic
|
|
50365
50479
|
});
|
|
50366
|
-
import { existsSync as
|
|
50367
|
-
import { join as
|
|
50480
|
+
import { existsSync as existsSync21, readFileSync as readFileSync17, readdirSync as readdirSync11, statSync as statSync11, writeFileSync as writeFileSync10 } from "fs";
|
|
50481
|
+
import { join as join22 } from "path";
|
|
50368
50482
|
import { execSync as execSync2 } from "child_process";
|
|
50369
50483
|
function registerDiagnostic(parent) {
|
|
50370
50484
|
parent.command("doctor").option("--json", "Output as JSON", false).description("Check env vars, system deps, and readiness for pinned skills").action((options) => handleDoctor(options));
|
|
@@ -50480,7 +50594,7 @@ Skills Test (${results.length} skill${results.length === 1 ? "" : "s"}):
|
|
|
50480
50594
|
}
|
|
50481
50595
|
function handleAuth(name, options) {
|
|
50482
50596
|
const cwd2 = process.cwd();
|
|
50483
|
-
const envFilePath =
|
|
50597
|
+
const envFilePath = join22(cwd2, ".env");
|
|
50484
50598
|
if (options.set) {
|
|
50485
50599
|
const eqIdx = options.set.indexOf("=");
|
|
50486
50600
|
if (eqIdx === -1) {
|
|
@@ -50502,7 +50616,7 @@ function handleAuth(name, options) {
|
|
|
50502
50616
|
process.exitCode = 1;
|
|
50503
50617
|
return;
|
|
50504
50618
|
}
|
|
50505
|
-
let existing =
|
|
50619
|
+
let existing = existsSync21(envFilePath) ? readFileSync17(envFilePath, "utf-8") : "";
|
|
50506
50620
|
const keyPattern = new RegExp(`^${key}=.*$`, "m");
|
|
50507
50621
|
const updated = keyPattern.test(existing) ? existing.replace(keyPattern, `${key}=${value}`) : existing.endsWith(`
|
|
50508
50622
|
`) || existing === "" ? existing + `${key}=${value}
|
|
@@ -50568,11 +50682,11 @@ function handleWhoami(options) {
|
|
|
50568
50682
|
const agentConfigs = [];
|
|
50569
50683
|
for (const agent of AGENT_TARGETS) {
|
|
50570
50684
|
const agentSkillsPath = getAgentSkillsDir(agent, "global");
|
|
50571
|
-
const exists =
|
|
50685
|
+
const exists = existsSync21(agentSkillsPath);
|
|
50572
50686
|
let skillCount = 0;
|
|
50573
50687
|
if (exists)
|
|
50574
50688
|
try {
|
|
50575
|
-
skillCount = readdirSync11(agentSkillsPath).filter((f) => !f.startsWith(".") && statSync11(
|
|
50689
|
+
skillCount = readdirSync11(agentSkillsPath).filter((f) => !f.startsWith(".") && statSync11(join22(agentSkillsPath, f)).isDirectory()).length;
|
|
50576
50690
|
} catch {}
|
|
50577
50691
|
agentConfigs.push({ agent, label: AGENT_LABELS[agent], path: agentSkillsPath, exists, skillCount });
|
|
50578
50692
|
}
|
|
@@ -50606,9 +50720,9 @@ function handleOutdated(options) {
|
|
|
50606
50720
|
for (const name of installed) {
|
|
50607
50721
|
const installedVersion = meta.skills[name]?.version ?? "unknown";
|
|
50608
50722
|
const registryPath = getSkillPath(name);
|
|
50609
|
-
const registryPkgPath =
|
|
50723
|
+
const registryPkgPath = join22(registryPath, "package.json");
|
|
50610
50724
|
let registryVersion = "unknown";
|
|
50611
|
-
if (
|
|
50725
|
+
if (existsSync21(registryPkgPath))
|
|
50612
50726
|
try {
|
|
50613
50727
|
registryVersion = JSON.parse(readFileSync17(registryPkgPath, "utf-8")).version || "unknown";
|
|
50614
50728
|
} catch {}
|
|
@@ -50874,20 +50988,20 @@ var init_runs = __esm(() => {
|
|
|
50874
50988
|
|
|
50875
50989
|
// src/lib/run-state.ts
|
|
50876
50990
|
import { createHash as createHash2, randomBytes } from "crypto";
|
|
50877
|
-
import { existsSync as
|
|
50878
|
-
import { extname, join as
|
|
50991
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync9, readFileSync as readFileSync18, readdirSync as readdirSync12, statSync as statSync12, writeFileSync as writeFileSync11 } from "fs";
|
|
50992
|
+
import { extname, join as join23, relative as relative2 } from "path";
|
|
50879
50993
|
function createSkillRun(params, targetDir = process.cwd()) {
|
|
50880
50994
|
const now3 = new Date;
|
|
50881
50995
|
const id = createRunId(now3);
|
|
50882
50996
|
const day = now3.toISOString().slice(0, 10);
|
|
50883
50997
|
const skillName = normalizeSkillName(params.skill);
|
|
50884
50998
|
const root = getProjectStateDir(targetDir);
|
|
50885
|
-
const runDir =
|
|
50886
|
-
const logsDir =
|
|
50887
|
-
const exportDir =
|
|
50999
|
+
const runDir = join23(root, "runs", day, id);
|
|
51000
|
+
const logsDir = join23(runDir, "logs");
|
|
51001
|
+
const exportDir = join23(root, "exports", skillName, id);
|
|
50888
51002
|
mkdirSync9(logsDir, { recursive: true });
|
|
50889
51003
|
mkdirSync9(exportDir, { recursive: true });
|
|
50890
|
-
mkdirSync9(
|
|
51004
|
+
mkdirSync9(join23(root, "tmp"), { recursive: true });
|
|
50891
51005
|
const record = {
|
|
50892
51006
|
id,
|
|
50893
51007
|
skill: skillName,
|
|
@@ -50938,27 +51052,27 @@ function updateSkillRun(context, patch) {
|
|
|
50938
51052
|
return context.record;
|
|
50939
51053
|
}
|
|
50940
51054
|
function writeRunLogs(context, stdout = "", stderr = "") {
|
|
50941
|
-
writeFileSync11(
|
|
50942
|
-
writeFileSync11(
|
|
51055
|
+
writeFileSync11(join23(context.logsDir, "stdout.log"), stdout);
|
|
51056
|
+
writeFileSync11(join23(context.logsDir, "stderr.log"), stderr);
|
|
50943
51057
|
}
|
|
50944
51058
|
function appendRunEvent(context, event, data = {}) {
|
|
50945
51059
|
const line = JSON.stringify({ ts: new Date().toISOString(), event, ...data }) + `
|
|
50946
51060
|
`;
|
|
50947
|
-
const path =
|
|
50948
|
-
const previous =
|
|
51061
|
+
const path = join23(context.runDir, "events.ndjson");
|
|
51062
|
+
const previous = existsSync22(path) ? readFileSync18(path, "utf-8") : "";
|
|
50949
51063
|
writeFileSync11(path, previous + line);
|
|
50950
51064
|
}
|
|
50951
51065
|
function listSkillRuns(targetDir = process.cwd(), limit = 50) {
|
|
50952
|
-
const runsRoot =
|
|
50953
|
-
if (!
|
|
51066
|
+
const runsRoot = join23(getProjectStateDir(targetDir), "runs");
|
|
51067
|
+
if (!existsSync22(runsRoot))
|
|
50954
51068
|
return [];
|
|
50955
51069
|
const records = [];
|
|
50956
51070
|
for (const day of readdirSync12(runsRoot).sort().reverse()) {
|
|
50957
|
-
const dayDir =
|
|
51071
|
+
const dayDir = join23(runsRoot, day);
|
|
50958
51072
|
if (!statSync12(dayDir).isDirectory())
|
|
50959
51073
|
continue;
|
|
50960
51074
|
for (const runId of readdirSync12(dayDir).sort().reverse()) {
|
|
50961
|
-
const record = readRunRecord(
|
|
51075
|
+
const record = readRunRecord(join23(dayDir, runId));
|
|
50962
51076
|
if (record)
|
|
50963
51077
|
records.push(record);
|
|
50964
51078
|
if (records.length >= limit)
|
|
@@ -50968,11 +51082,11 @@ function listSkillRuns(targetDir = process.cwd(), limit = 50) {
|
|
|
50968
51082
|
return records;
|
|
50969
51083
|
}
|
|
50970
51084
|
function findSkillRun(runId, targetDir = process.cwd()) {
|
|
50971
|
-
const runsRoot =
|
|
50972
|
-
if (!
|
|
51085
|
+
const runsRoot = join23(getProjectStateDir(targetDir), "runs");
|
|
51086
|
+
if (!existsSync22(runsRoot))
|
|
50973
51087
|
return null;
|
|
50974
51088
|
for (const day of readdirSync12(runsRoot)) {
|
|
50975
|
-
const record = readRunRecord(
|
|
51089
|
+
const record = readRunRecord(join23(runsRoot, day, runId));
|
|
50976
51090
|
if (record)
|
|
50977
51091
|
return record;
|
|
50978
51092
|
}
|
|
@@ -50989,18 +51103,18 @@ function skillRunEnv(context) {
|
|
|
50989
51103
|
};
|
|
50990
51104
|
}
|
|
50991
51105
|
function getRunExportDir(runId, skill, targetDir = process.cwd()) {
|
|
50992
|
-
return
|
|
51106
|
+
return join23(getProjectStateDir(targetDir), "exports", normalizeSkillName(skill), runId);
|
|
50993
51107
|
}
|
|
50994
51108
|
function writeRunRecord(context) {
|
|
50995
|
-
writeFileSync11(
|
|
51109
|
+
writeFileSync11(join23(context.runDir, "run.json"), JSON.stringify(context.record, null, 2) + `
|
|
50996
51110
|
`);
|
|
50997
51111
|
}
|
|
50998
51112
|
function writeArtifactsManifest(context, artifacts) {
|
|
50999
|
-
writeFileSync11(
|
|
51113
|
+
writeFileSync11(join23(context.runDir, "artifacts.json"), JSON.stringify({ runId: context.record.id, artifacts }, null, 2) + `
|
|
51000
51114
|
`);
|
|
51001
51115
|
}
|
|
51002
51116
|
function collectRunArtifacts(context) {
|
|
51003
|
-
if (!
|
|
51117
|
+
if (!existsSync22(context.exportDir))
|
|
51004
51118
|
return [];
|
|
51005
51119
|
const artifacts = [];
|
|
51006
51120
|
for (const path of walkFiles(context.exportDir)) {
|
|
@@ -51016,8 +51130,8 @@ function collectRunArtifacts(context) {
|
|
|
51016
51130
|
return artifacts.sort((a, b) => a.path.localeCompare(b.path));
|
|
51017
51131
|
}
|
|
51018
51132
|
function readRunRecord(runDir) {
|
|
51019
|
-
const path =
|
|
51020
|
-
if (!
|
|
51133
|
+
const path = join23(runDir, "run.json");
|
|
51134
|
+
if (!existsSync22(path))
|
|
51021
51135
|
return null;
|
|
51022
51136
|
try {
|
|
51023
51137
|
return JSON.parse(readFileSync18(path, "utf-8"));
|
|
@@ -51028,7 +51142,7 @@ function readRunRecord(runDir) {
|
|
|
51028
51142
|
function walkFiles(dir) {
|
|
51029
51143
|
const files = [];
|
|
51030
51144
|
for (const entry of readdirSync12(dir)) {
|
|
51031
|
-
const full =
|
|
51145
|
+
const full = join23(dir, entry);
|
|
51032
51146
|
if (statSync12(full).isDirectory())
|
|
51033
51147
|
files.push(...walkFiles(full));
|
|
51034
51148
|
else
|
|
@@ -56598,12 +56712,12 @@ class StdioServerTransport {
|
|
|
56598
56712
|
this.onclose?.();
|
|
56599
56713
|
}
|
|
56600
56714
|
send(message) {
|
|
56601
|
-
return new Promise((
|
|
56715
|
+
return new Promise((resolve2) => {
|
|
56602
56716
|
const json = serializeMessage(message);
|
|
56603
56717
|
if (this._stdout.write(json)) {
|
|
56604
|
-
|
|
56718
|
+
resolve2();
|
|
56605
56719
|
} else {
|
|
56606
|
-
this._stdout.once("drain",
|
|
56720
|
+
this._stdout.once("drain", resolve2);
|
|
56607
56721
|
}
|
|
56608
56722
|
});
|
|
56609
56723
|
}
|
|
@@ -58778,7 +58892,7 @@ class Protocol {
|
|
|
58778
58892
|
return;
|
|
58779
58893
|
}
|
|
58780
58894
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1000;
|
|
58781
|
-
await new Promise((
|
|
58895
|
+
await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
|
|
58782
58896
|
options?.signal?.throwIfAborted();
|
|
58783
58897
|
}
|
|
58784
58898
|
} catch (error2) {
|
|
@@ -58790,7 +58904,7 @@ class Protocol {
|
|
|
58790
58904
|
}
|
|
58791
58905
|
request(request, resultSchema, options) {
|
|
58792
58906
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
58793
|
-
return new Promise((
|
|
58907
|
+
return new Promise((resolve2, reject2) => {
|
|
58794
58908
|
const earlyReject = (error2) => {
|
|
58795
58909
|
reject2(error2);
|
|
58796
58910
|
};
|
|
@@ -58868,7 +58982,7 @@ class Protocol {
|
|
|
58868
58982
|
if (!parseResult.success) {
|
|
58869
58983
|
reject2(parseResult.error);
|
|
58870
58984
|
} else {
|
|
58871
|
-
|
|
58985
|
+
resolve2(parseResult.data);
|
|
58872
58986
|
}
|
|
58873
58987
|
} catch (error2) {
|
|
58874
58988
|
reject2(error2);
|
|
@@ -59059,12 +59173,12 @@ class Protocol {
|
|
|
59059
59173
|
interval = task.pollInterval;
|
|
59060
59174
|
}
|
|
59061
59175
|
} catch {}
|
|
59062
|
-
return new Promise((
|
|
59176
|
+
return new Promise((resolve2, reject2) => {
|
|
59063
59177
|
if (signal.aborted) {
|
|
59064
59178
|
reject2(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
59065
59179
|
return;
|
|
59066
59180
|
}
|
|
59067
|
-
const timeoutId = setTimeout(
|
|
59181
|
+
const timeoutId = setTimeout(resolve2, interval);
|
|
59068
59182
|
signal.addEventListener("abort", () => {
|
|
59069
59183
|
clearTimeout(timeoutId);
|
|
59070
59184
|
reject2(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -62049,7 +62163,7 @@ var require_compile = __commonJS((exports) => {
|
|
|
62049
62163
|
const schOrFunc = root.refs[ref];
|
|
62050
62164
|
if (schOrFunc)
|
|
62051
62165
|
return schOrFunc;
|
|
62052
|
-
let _sch =
|
|
62166
|
+
let _sch = resolve2.call(this, root, ref);
|
|
62053
62167
|
if (_sch === undefined) {
|
|
62054
62168
|
const schema = (_a = root.localRefs) === null || _a === undefined ? undefined : _a[ref];
|
|
62055
62169
|
const { schemaId } = this.opts;
|
|
@@ -62076,7 +62190,7 @@ var require_compile = __commonJS((exports) => {
|
|
|
62076
62190
|
function sameSchemaEnv(s1, s2) {
|
|
62077
62191
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
62078
62192
|
}
|
|
62079
|
-
function
|
|
62193
|
+
function resolve2(root, ref) {
|
|
62080
62194
|
let sch;
|
|
62081
62195
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
62082
62196
|
ref = sch;
|
|
@@ -62368,8 +62482,8 @@ var require_utils = __commonJS((exports, module) => {
|
|
|
62368
62482
|
var HOST_DELIMS = { "@": "%40", "/": "%2F", "?": "%3F", "#": "%23", ":": "%3A" };
|
|
62369
62483
|
var HOST_DELIM_RE = /[@/?#:]/g;
|
|
62370
62484
|
var HOST_DELIM_NO_COLON_RE = /[@/?#]/g;
|
|
62371
|
-
function reescapeHostDelimiters(host,
|
|
62372
|
-
const re =
|
|
62485
|
+
function reescapeHostDelimiters(host, isIP) {
|
|
62486
|
+
const re = isIP ? HOST_DELIM_NO_COLON_RE : HOST_DELIM_RE;
|
|
62373
62487
|
re.lastIndex = 0;
|
|
62374
62488
|
return host.replace(re, (ch) => HOST_DELIMS[ch]);
|
|
62375
62489
|
}
|
|
@@ -62662,7 +62776,7 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
62662
62776
|
}
|
|
62663
62777
|
return uri;
|
|
62664
62778
|
}
|
|
62665
|
-
function
|
|
62779
|
+
function resolve2(baseURI, relativeURI, options) {
|
|
62666
62780
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
62667
62781
|
const { parsed: baseParsed, malformedAuthorityOrPort: baseMalformed } = parseWithStatus(baseURI, schemelessOptions);
|
|
62668
62782
|
const { parsed: relativeParsed, malformedAuthorityOrPort: relativeMalformed } = parseWithStatus(relativeURI, schemelessOptions);
|
|
@@ -62818,7 +62932,7 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
62818
62932
|
fragment: undefined
|
|
62819
62933
|
};
|
|
62820
62934
|
let malformedAuthorityOrPort = false;
|
|
62821
|
-
let
|
|
62935
|
+
let isIP = false;
|
|
62822
62936
|
if (options.reference === "suffix") {
|
|
62823
62937
|
if (options.scheme) {
|
|
62824
62938
|
uri = options.scheme + ":" + uri;
|
|
@@ -62867,9 +62981,9 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
62867
62981
|
if (ipv4result === false) {
|
|
62868
62982
|
const ipv6result = normalizeIPv6(parsed.host);
|
|
62869
62983
|
parsed.host = ipv6result.host.toLowerCase();
|
|
62870
|
-
|
|
62984
|
+
isIP = ipv6result.isIPV6;
|
|
62871
62985
|
} else {
|
|
62872
|
-
|
|
62986
|
+
isIP = true;
|
|
62873
62987
|
}
|
|
62874
62988
|
}
|
|
62875
62989
|
if (parsed.scheme === undefined && parsed.userinfo === undefined && parsed.host === undefined && parsed.port === undefined && parsed.query === undefined && !parsed.path) {
|
|
@@ -62886,7 +63000,7 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
62886
63000
|
}
|
|
62887
63001
|
const schemeHandler = getSchemeHandler(options.scheme || parsed.scheme);
|
|
62888
63002
|
if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport)) {
|
|
62889
|
-
if (parsed.host && (options.domainHost || schemeHandler && schemeHandler.domainHost) &&
|
|
63003
|
+
if (parsed.host && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) {
|
|
62890
63004
|
try {
|
|
62891
63005
|
parsed.host = new URL("http://" + parsed.host).hostname;
|
|
62892
63006
|
} catch (e) {
|
|
@@ -62900,7 +63014,7 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
62900
63014
|
parsed.scheme = unescape(parsed.scheme);
|
|
62901
63015
|
}
|
|
62902
63016
|
if (parsed.host !== undefined) {
|
|
62903
|
-
parsed.host = reescapeHostDelimiters(unescape(parsed.host),
|
|
63017
|
+
parsed.host = reescapeHostDelimiters(unescape(parsed.host), isIP);
|
|
62904
63018
|
}
|
|
62905
63019
|
}
|
|
62906
63020
|
if (parsed.path) {
|
|
@@ -62947,7 +63061,7 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
62947
63061
|
var fastUri = {
|
|
62948
63062
|
SCHEMES,
|
|
62949
63063
|
normalize: normalize3,
|
|
62950
|
-
resolve,
|
|
63064
|
+
resolve: resolve2,
|
|
62951
63065
|
resolveComponent,
|
|
62952
63066
|
equal,
|
|
62953
63067
|
serialize,
|
|
@@ -66733,7 +66847,7 @@ class McpServer {
|
|
|
66733
66847
|
let task = createTaskResult.task;
|
|
66734
66848
|
const pollInterval = task.pollInterval ?? 5000;
|
|
66735
66849
|
while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
|
|
66736
|
-
await new Promise((
|
|
66850
|
+
await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
|
|
66737
66851
|
const updatedTask = await extra.taskStore.getTask(taskId);
|
|
66738
66852
|
if (!updatedTask) {
|
|
66739
66853
|
throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
|
|
@@ -68679,8 +68793,8 @@ var init_remote_client = __esm(() => {
|
|
|
68679
68793
|
});
|
|
68680
68794
|
|
|
68681
68795
|
// src/mcp/operation-tools.ts
|
|
68682
|
-
import { existsSync as
|
|
68683
|
-
import { join as
|
|
68796
|
+
import { existsSync as existsSync23, readdirSync as readdirSync13, statSync as statSync13 } from "fs";
|
|
68797
|
+
import { join as join24 } from "path";
|
|
68684
68798
|
function registerOperationTools(server) {
|
|
68685
68799
|
server.registerTool("scaffold_skill", {
|
|
68686
68800
|
title: "Scaffold Skill",
|
|
@@ -69077,12 +69191,12 @@ function registerOperationTools(server) {
|
|
|
69077
69191
|
const agents = [];
|
|
69078
69192
|
for (const agent of AGENT_TARGETS) {
|
|
69079
69193
|
const agentSkillsPath = getAgentSkillsDir(agent, "global");
|
|
69080
|
-
const exists =
|
|
69194
|
+
const exists = existsSync23(agentSkillsPath);
|
|
69081
69195
|
let skillCount = 0;
|
|
69082
69196
|
if (exists) {
|
|
69083
69197
|
try {
|
|
69084
69198
|
skillCount = readdirSync13(agentSkillsPath).filter((f) => {
|
|
69085
|
-
const full =
|
|
69199
|
+
const full = join24(agentSkillsPath, f);
|
|
69086
69200
|
return !f.startsWith(".") && statSync13(full).isDirectory();
|
|
69087
69201
|
}).length;
|
|
69088
69202
|
} catch {}
|
|
@@ -69139,16 +69253,16 @@ var init_operation_tools = __esm(() => {
|
|
|
69139
69253
|
});
|
|
69140
69254
|
|
|
69141
69255
|
// src/lib/feedback.ts
|
|
69142
|
-
import { existsSync as
|
|
69143
|
-
import { dirname as dirname7, join as
|
|
69256
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync10 } from "fs";
|
|
69257
|
+
import { dirname as dirname7, join as join25 } from "path";
|
|
69144
69258
|
import { Database } from "bun:sqlite";
|
|
69145
69259
|
function getFeedbackDbPath() {
|
|
69146
|
-
return
|
|
69260
|
+
return join25(getDataDir(), "skills.db");
|
|
69147
69261
|
}
|
|
69148
69262
|
function getFeedbackDb() {
|
|
69149
69263
|
const dbPath = getFeedbackDbPath();
|
|
69150
69264
|
const dir = dirname7(dbPath);
|
|
69151
|
-
if (!
|
|
69265
|
+
if (!existsSync24(dir))
|
|
69152
69266
|
mkdirSync10(dir, { recursive: true });
|
|
69153
69267
|
const db = new Database(dbPath);
|
|
69154
69268
|
db.exec("PRAGMA journal_mode = WAL");
|
|
@@ -69322,14 +69436,14 @@ var init_resource_meta_tools = __esm(() => {
|
|
|
69322
69436
|
});
|
|
69323
69437
|
|
|
69324
69438
|
// src/lib/scheduler.ts
|
|
69325
|
-
import { existsSync as
|
|
69326
|
-
import { join as
|
|
69439
|
+
import { existsSync as existsSync25, readFileSync as readFileSync19, writeFileSync as writeFileSync12, mkdirSync as mkdirSync11 } from "fs";
|
|
69440
|
+
import { join as join26 } from "path";
|
|
69327
69441
|
function getSchedulesPath(targetDir = process.cwd()) {
|
|
69328
|
-
return
|
|
69442
|
+
return join26(targetDir, ".skills", "schedules.json");
|
|
69329
69443
|
}
|
|
69330
69444
|
function loadSchedules(targetDir = process.cwd()) {
|
|
69331
69445
|
const path = getSchedulesPath(targetDir);
|
|
69332
|
-
if (
|
|
69446
|
+
if (existsSync25(path)) {
|
|
69333
69447
|
try {
|
|
69334
69448
|
return JSON.parse(readFileSync19(path, "utf-8"));
|
|
69335
69449
|
} catch {}
|
|
@@ -69338,8 +69452,8 @@ function loadSchedules(targetDir = process.cwd()) {
|
|
|
69338
69452
|
}
|
|
69339
69453
|
function saveSchedules(data, targetDir = process.cwd()) {
|
|
69340
69454
|
const path = getSchedulesPath(targetDir);
|
|
69341
|
-
const dir =
|
|
69342
|
-
if (!
|
|
69455
|
+
const dir = join26(targetDir, ".skills");
|
|
69456
|
+
if (!existsSync25(dir))
|
|
69343
69457
|
mkdirSync11(dir, { recursive: true });
|
|
69344
69458
|
writeFileSync12(path, JSON.stringify(data, null, 2));
|
|
69345
69459
|
}
|
|
@@ -69639,14 +69753,14 @@ var init_schedule_tools = __esm(() => {
|
|
|
69639
69753
|
// src/lib/native-storage.ts
|
|
69640
69754
|
import { createHash as createHash3, createHmac as createHmac2 } from "crypto";
|
|
69641
69755
|
import {
|
|
69642
|
-
existsSync as
|
|
69756
|
+
existsSync as existsSync26,
|
|
69643
69757
|
mkdirSync as mkdirSync12,
|
|
69644
69758
|
readFileSync as readFileSync20,
|
|
69645
69759
|
readdirSync as readdirSync14,
|
|
69646
69760
|
statSync as statSync14,
|
|
69647
69761
|
writeFileSync as writeFileSync13
|
|
69648
69762
|
} from "fs";
|
|
69649
|
-
import { dirname as dirname8, join as
|
|
69763
|
+
import { dirname as dirname8, join as join27, normalize as normalize3, relative as relative3, sep as sep2 } from "path";
|
|
69650
69764
|
function resolveSkillsNativeStorageConfig(env3 = process.env) {
|
|
69651
69765
|
assertNoRetiredModeEnvVars(env3, {
|
|
69652
69766
|
app: SKILLS_ENV_NAMESPACE,
|
|
@@ -69687,7 +69801,7 @@ function getSkillsNativeStorageStatus(options = {}) {
|
|
|
69687
69801
|
local: {
|
|
69688
69802
|
dataDir: getDataDir(),
|
|
69689
69803
|
projectStateDir: getProjectStateDir(targetDir),
|
|
69690
|
-
feedbackDbPath:
|
|
69804
|
+
feedbackDbPath: join27(getDataDir(), "skills.db")
|
|
69691
69805
|
},
|
|
69692
69806
|
remote: {
|
|
69693
69807
|
databaseConfigured: Boolean(config2.databaseUrl),
|
|
@@ -69707,7 +69821,7 @@ function getStorageStatus(options = {}) {
|
|
|
69707
69821
|
function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
|
|
69708
69822
|
const projectStateDir = getProjectStateDir(targetDir);
|
|
69709
69823
|
const files = [];
|
|
69710
|
-
if (
|
|
69824
|
+
if (existsSync26(projectStateDir)) {
|
|
69711
69825
|
for (const filePath of walkFiles2(projectStateDir)) {
|
|
69712
69826
|
const bytes = readFileSync20(filePath);
|
|
69713
69827
|
const relativePath = toPosix(relative3(targetDir, filePath));
|
|
@@ -69768,7 +69882,7 @@ function parsePositiveInteger(value) {
|
|
|
69768
69882
|
function walkFiles2(dir) {
|
|
69769
69883
|
const files = [];
|
|
69770
69884
|
for (const entry of readdirSync14(dir)) {
|
|
69771
|
-
const full =
|
|
69885
|
+
const full = join27(dir, entry);
|
|
69772
69886
|
const stats = statSync14(full);
|
|
69773
69887
|
if (stats.isDirectory())
|
|
69774
69888
|
files.push(...walkFiles2(full));
|
|
@@ -69976,7 +70090,7 @@ var GlobalRequest, Request, newHeadersFromIncoming = (incoming) => {
|
|
|
69976
70090
|
}
|
|
69977
70091
|
return new Request(url, init);
|
|
69978
70092
|
}, getRequestCache, requestCache, incomingKey, urlKey, headersKey, abortControllerKey, getAbortController, requestPrototype, responseCache, getResponseCache, cacheKey, GlobalResponse, Response2, outgoingEnded, incomingDraining, MAX_DRAIN_BYTES;
|
|
69979
|
-
var
|
|
70093
|
+
var init_dist2 = __esm(() => {
|
|
69980
70094
|
GlobalRequest = global.Request;
|
|
69981
70095
|
Request = class extends GlobalRequest {
|
|
69982
70096
|
constructor(input, options) {
|
|
@@ -70266,7 +70380,7 @@ var init_webStandardStreamableHttp = __esm(() => {
|
|
|
70266
70380
|
|
|
70267
70381
|
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0/node_modules/@modelcontextprotocol/sdk/dist/esm/server/streamableHttp.js
|
|
70268
70382
|
var init_streamableHttp = __esm(() => {
|
|
70269
|
-
|
|
70383
|
+
init_dist2();
|
|
70270
70384
|
init_webStandardStreamableHttp();
|
|
70271
70385
|
});
|
|
70272
70386
|
|
|
@@ -70317,9 +70431,9 @@ var init_mcp2 = __esm(() => {
|
|
|
70317
70431
|
});
|
|
70318
70432
|
|
|
70319
70433
|
// src/cli/commands/runtime-mcp.ts
|
|
70320
|
-
import { existsSync as
|
|
70321
|
-
import { homedir as
|
|
70322
|
-
import { dirname as dirname9, join as
|
|
70434
|
+
import { existsSync as existsSync27, mkdirSync as mkdirSync13, readFileSync as readFileSync21, writeFileSync as writeFileSync14 } from "fs";
|
|
70435
|
+
import { homedir as homedir10 } from "os";
|
|
70436
|
+
import { dirname as dirname9, join as join28 } from "path";
|
|
70323
70437
|
async function handleMcp(options) {
|
|
70324
70438
|
if (options.register) {
|
|
70325
70439
|
let agents;
|
|
@@ -70362,24 +70476,24 @@ async function registerMcpForAgent(agent, command) {
|
|
|
70362
70476
|
case "codex":
|
|
70363
70477
|
return registerCodexMcp(command);
|
|
70364
70478
|
case "gemini":
|
|
70365
|
-
return registerJsonMcpServer(agent,
|
|
70479
|
+
return registerJsonMcpServer(agent, join28(homedir10(), ".gemini", "settings.json"), "mcpServers", {
|
|
70366
70480
|
command,
|
|
70367
70481
|
args: []
|
|
70368
70482
|
});
|
|
70369
70483
|
case "pi":
|
|
70370
|
-
return registerJsonMcpServer(agent,
|
|
70484
|
+
return registerJsonMcpServer(agent, join28(homedir10(), ".pi", "agent", "mcp.json"), "mcpServers", {
|
|
70371
70485
|
command,
|
|
70372
70486
|
args: []
|
|
70373
70487
|
});
|
|
70374
70488
|
case "opencode":
|
|
70375
70489
|
return registerOpenCodeMcp(command);
|
|
70376
70490
|
case "cursor":
|
|
70377
|
-
return registerJsonMcpServer(agent,
|
|
70491
|
+
return registerJsonMcpServer(agent, join28(homedir10(), ".cursor", "mcp.json"), "mcpServers", {
|
|
70378
70492
|
command,
|
|
70379
70493
|
args: []
|
|
70380
70494
|
});
|
|
70381
70495
|
case "windsurf":
|
|
70382
|
-
return registerJsonMcpServer(agent,
|
|
70496
|
+
return registerJsonMcpServer(agent, join28(homedir10(), ".windsurf", "mcp.json"), "mcpServers", {
|
|
70383
70497
|
command,
|
|
70384
70498
|
args: []
|
|
70385
70499
|
});
|
|
@@ -70402,7 +70516,7 @@ async function registerClaudeMcp(command) {
|
|
|
70402
70516
|
if (exitCode === 0) {
|
|
70403
70517
|
return { agent: "claude", success: true, command: cliCommand };
|
|
70404
70518
|
}
|
|
70405
|
-
const fallback = registerJsonMcpServer("claude",
|
|
70519
|
+
const fallback = registerJsonMcpServer("claude", join28(homedir10(), ".claude", ".mcp.json"), "mcpServers", {
|
|
70406
70520
|
command,
|
|
70407
70521
|
args: []
|
|
70408
70522
|
});
|
|
@@ -70412,7 +70526,7 @@ async function registerClaudeMcp(command) {
|
|
|
70412
70526
|
error: fallback.success ? undefined : `claude exited with ${exitCode}: ${(stderr || stdout).trim()}`
|
|
70413
70527
|
};
|
|
70414
70528
|
} catch (err) {
|
|
70415
|
-
const fallback = registerJsonMcpServer("claude",
|
|
70529
|
+
const fallback = registerJsonMcpServer("claude", join28(homedir10(), ".claude", ".mcp.json"), "mcpServers", {
|
|
70416
70530
|
command,
|
|
70417
70531
|
args: []
|
|
70418
70532
|
});
|
|
@@ -70424,11 +70538,11 @@ async function registerClaudeMcp(command) {
|
|
|
70424
70538
|
}
|
|
70425
70539
|
}
|
|
70426
70540
|
function registerCodexMcp(command) {
|
|
70427
|
-
const path =
|
|
70541
|
+
const path = join28(homedir10(), ".codex", "config.toml");
|
|
70428
70542
|
const config2 = `[mcp_servers.${MCP_SERVER_NAME}]
|
|
70429
70543
|
command = ${JSON.stringify(command)}`;
|
|
70430
70544
|
try {
|
|
70431
|
-
const current =
|
|
70545
|
+
const current = existsSync27(path) ? readFileSync21(path, "utf-8") : "";
|
|
70432
70546
|
writeTextFile(path, upsertTomlSection(current, `[mcp_servers.${MCP_SERVER_NAME}]`, `command = ${JSON.stringify(command)}`));
|
|
70433
70547
|
return { agent: "codex", success: true, path, config: config2 };
|
|
70434
70548
|
} catch (err) {
|
|
@@ -70436,7 +70550,7 @@ command = ${JSON.stringify(command)}`;
|
|
|
70436
70550
|
}
|
|
70437
70551
|
}
|
|
70438
70552
|
function registerOpenCodeMcp(command) {
|
|
70439
|
-
const path =
|
|
70553
|
+
const path = join28(homedir10(), ".config", "opencode", "opencode.json");
|
|
70440
70554
|
const config2 = JSON.stringify({
|
|
70441
70555
|
$schema: "https://opencode.ai/config.json",
|
|
70442
70556
|
mcp: {
|
|
@@ -70478,7 +70592,7 @@ function registerJsonMcpServer(agent, path, containerKey, server2) {
|
|
|
70478
70592
|
}
|
|
70479
70593
|
}
|
|
70480
70594
|
function readJsonObject2(path) {
|
|
70481
|
-
if (!
|
|
70595
|
+
if (!existsSync27(path))
|
|
70482
70596
|
return {};
|
|
70483
70597
|
const raw = readFileSync21(path, "utf-8").trim();
|
|
70484
70598
|
if (!raw)
|
|
@@ -70524,8 +70638,8 @@ function findCommandOnPath(command) {
|
|
|
70524
70638
|
for (const dir of pathValue.split(":")) {
|
|
70525
70639
|
if (!dir)
|
|
70526
70640
|
continue;
|
|
70527
|
-
const candidate =
|
|
70528
|
-
if (
|
|
70641
|
+
const candidate = join28(dir, command);
|
|
70642
|
+
if (existsSync27(candidate))
|
|
70529
70643
|
return candidate;
|
|
70530
70644
|
}
|
|
70531
70645
|
return command;
|
|
@@ -70545,7 +70659,7 @@ __export(exports_runtime, {
|
|
|
70545
70659
|
registerRuntime: () => registerRuntime
|
|
70546
70660
|
});
|
|
70547
70661
|
import { mkdirSync as mkdirSync14, writeFileSync as writeFileSync15 } from "fs";
|
|
70548
|
-
import { dirname as dirname10, join as
|
|
70662
|
+
import { dirname as dirname10, join as join29 } from "path";
|
|
70549
70663
|
import { createInterface } from "readline";
|
|
70550
70664
|
function registerRuntime(parent) {
|
|
70551
70665
|
parent.command("run").argument("<skill>", "Skill name").argument("[args...]", "Arguments to pass to the skill").allowUnknownOption(true).passThroughOptions(true).option("--json", "Output result as JSON", false).option("--wait", "Poll remote runs until a terminal status", false).option("--poll-interval-ms <ms>", "Remote polling interval in milliseconds", "1000").option("--poll-timeout-ms <ms>", "Maximum time to wait for a remote run", "300000").description("Run a skill directly").action(async (name, args2, options) => handleRun(name, args2, options));
|
|
@@ -70670,10 +70784,10 @@ async function handleSetup(options) {
|
|
|
70670
70784
|
}
|
|
70671
70785
|
function promptLine(question) {
|
|
70672
70786
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
70673
|
-
return new Promise((
|
|
70787
|
+
return new Promise((resolve2) => {
|
|
70674
70788
|
rl.question(source_default.bold(question), (answer) => {
|
|
70675
70789
|
rl.close();
|
|
70676
|
-
|
|
70790
|
+
resolve2(answer.trim());
|
|
70677
70791
|
});
|
|
70678
70792
|
});
|
|
70679
70793
|
}
|
|
@@ -71034,7 +71148,7 @@ async function handleExportsDownload(runId, options) {
|
|
|
71034
71148
|
if (!response.ok)
|
|
71035
71149
|
throw new Error(`download failed for artifact ${artifactId}: ${response.status}`);
|
|
71036
71150
|
const relativePath = safeArtifactRelativePath(typeof artifact.relativePath === "string" ? artifact.relativePath : artifact.fileName, String(artifact.fileName || artifactId));
|
|
71037
|
-
const outputPath =
|
|
71151
|
+
const outputPath = join29(exportDir, relativePath);
|
|
71038
71152
|
mkdirSync14(dirname10(outputPath), { recursive: true });
|
|
71039
71153
|
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
71040
71154
|
writeFileSync15(outputPath, bytes);
|
|
@@ -71094,7 +71208,7 @@ async function pollRemoteRun(client, runId, options) {
|
|
|
71094
71208
|
const remaining = deadline - Date.now();
|
|
71095
71209
|
if (remaining <= 0)
|
|
71096
71210
|
break;
|
|
71097
|
-
await new Promise((
|
|
71211
|
+
await new Promise((resolve2) => setTimeout(resolve2, Math.min(options.intervalMs, remaining)));
|
|
71098
71212
|
}
|
|
71099
71213
|
return {
|
|
71100
71214
|
run: current ?? { id: runId, status: "queued" },
|
|
@@ -71397,8 +71511,8 @@ var init_completion = __esm(() => {
|
|
|
71397
71511
|
|
|
71398
71512
|
// src/lib/portable-snapshot-filter.ts
|
|
71399
71513
|
import { readdirSync as readdirSync15, statSync as statSync15 } from "fs";
|
|
71400
|
-
import { homedir as
|
|
71401
|
-
import { join as
|
|
71514
|
+
import { homedir as homedir11 } from "os";
|
|
71515
|
+
import { join as join30, sep as sep3 } from "path";
|
|
71402
71516
|
function isExcludedSkillFileName(fileName) {
|
|
71403
71517
|
if (EXCLUDE_FILE_NAMES.has(fileName)) {
|
|
71404
71518
|
return true;
|
|
@@ -71419,18 +71533,18 @@ function isPortableWithinSkill(relativeParts) {
|
|
|
71419
71533
|
return PORTABLE_SUBDIRS.has(second);
|
|
71420
71534
|
}
|
|
71421
71535
|
function homePathFor(definition, homesRoot) {
|
|
71422
|
-
const home = homesRoot ??
|
|
71536
|
+
const home = homesRoot ?? homedir11();
|
|
71423
71537
|
if (definition.subClass === "skills" || definition.subClass === "custom") {
|
|
71424
|
-
return
|
|
71538
|
+
return join30(skillsDataRootForHome(home), definition.name);
|
|
71425
71539
|
}
|
|
71426
71540
|
if (definition.agent === "opencode") {
|
|
71427
|
-
return
|
|
71541
|
+
return join30(home, ".config", "opencode", "skills");
|
|
71428
71542
|
}
|
|
71429
|
-
return
|
|
71543
|
+
return join30(home, `.${definition.agent}`, "skills");
|
|
71430
71544
|
}
|
|
71431
71545
|
function destinationFor(definition, stationId, relativePath) {
|
|
71432
|
-
const category = definition.subClass === "agent-homes" ?
|
|
71433
|
-
return
|
|
71546
|
+
const category = definition.subClass === "agent-homes" ? join30("agent-homes", definition.agent ?? "") : definition.name;
|
|
71547
|
+
return join30("resources", stationId, "skills", category, ...relativePath.split(sep3));
|
|
71434
71548
|
}
|
|
71435
71549
|
function walkEntries(absoluteRoot) {
|
|
71436
71550
|
let entries;
|
|
@@ -71441,7 +71555,7 @@ function walkEntries(absoluteRoot) {
|
|
|
71441
71555
|
}
|
|
71442
71556
|
const output = [];
|
|
71443
71557
|
for (const entry of entries) {
|
|
71444
|
-
const childFull =
|
|
71558
|
+
const childFull = join30(absoluteRoot, entry.name);
|
|
71445
71559
|
if (entry.isSymbolicLink()) {
|
|
71446
71560
|
output.push({ kind: "symlink", relativePath: entry.name, fullPath: childFull });
|
|
71447
71561
|
continue;
|
|
@@ -71452,7 +71566,7 @@ function walkEntries(absoluteRoot) {
|
|
|
71452
71566
|
}
|
|
71453
71567
|
const nested = walkEntries(childFull);
|
|
71454
71568
|
for (const item of nested) {
|
|
71455
|
-
output.push({ ...item, relativePath:
|
|
71569
|
+
output.push({ ...item, relativePath: join30(entry.name, item.relativePath) });
|
|
71456
71570
|
}
|
|
71457
71571
|
continue;
|
|
71458
71572
|
}
|
|
@@ -71471,6 +71585,7 @@ function isRegularFile(filePath) {
|
|
|
71471
71585
|
}
|
|
71472
71586
|
var SYNC_HOMES, EXCLUDE_DIR_NAMES, EXCLUDE_DIR_PATTERNS, EXCLUDE_FILE_NAMES, EXCLUDE_FILE_PATTERNS, PORTABLE_TOP_LEVEL, PORTABLE_SUBDIRS, REFUSED_SCANNER_FLAGGED;
|
|
71473
71587
|
var init_portable_snapshot_filter = __esm(() => {
|
|
71588
|
+
init_app_home();
|
|
71474
71589
|
SYNC_HOMES = [
|
|
71475
71590
|
{ name: "skills", subClass: "skills", agent: null },
|
|
71476
71591
|
{ name: "custom", subClass: "custom", agent: null },
|
|
@@ -71552,7 +71667,7 @@ import {
|
|
|
71552
71667
|
statSync as statSync16,
|
|
71553
71668
|
writeFileSync as writeFileSync16
|
|
71554
71669
|
} from "fs";
|
|
71555
|
-
import { dirname as dirname11, isAbsolute as isAbsolute3, relative as relative4, resolve, sep as sep4 } from "path";
|
|
71670
|
+
import { dirname as dirname11, isAbsolute as isAbsolute3, relative as relative4, resolve as resolve2, sep as sep4 } from "path";
|
|
71556
71671
|
function validateStationId(stationId) {
|
|
71557
71672
|
if (!/^[a-z0-9-]+$/.test(stationId)) {
|
|
71558
71673
|
throw new StationSnapshotError("INVALID_STATION", `station id must be a slug, got: ${stationId}`);
|
|
@@ -71630,7 +71745,7 @@ function humanHomes(scanned) {
|
|
|
71630
71745
|
}));
|
|
71631
71746
|
}
|
|
71632
71747
|
function writeStationSnapshot(options) {
|
|
71633
|
-
const repoRoot =
|
|
71748
|
+
const repoRoot = resolve2(options.repoRoot ?? process.cwd());
|
|
71634
71749
|
const { scanned, plans, totalBytes } = planStationSnapshot(options);
|
|
71635
71750
|
const manifestFiles = plans.map((plan) => ({
|
|
71636
71751
|
relativePath: plan.source.relativePath,
|
|
@@ -71656,7 +71771,7 @@ function writeStationSnapshot(options) {
|
|
|
71656
71771
|
const conflicts = [];
|
|
71657
71772
|
const untouched = [];
|
|
71658
71773
|
for (const plan of plans) {
|
|
71659
|
-
const destination =
|
|
71774
|
+
const destination = resolve2(repoRoot, plan.destination);
|
|
71660
71775
|
const destinationRelative = relative4(repoRoot, destination);
|
|
71661
71776
|
if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep4) || isAbsolute3(destinationRelative)) {
|
|
71662
71777
|
throw new StationSnapshotError("DESTINATION_ESCAPE", `destination escapes repo root: ${plan.destination}`);
|
|
@@ -71679,7 +71794,7 @@ function writeStationSnapshot(options) {
|
|
|
71679
71794
|
}
|
|
71680
71795
|
let written = 0;
|
|
71681
71796
|
for (const plan of untouched) {
|
|
71682
|
-
const destination =
|
|
71797
|
+
const destination = resolve2(repoRoot, plan.destination);
|
|
71683
71798
|
mkdirSync15(dirname11(destination), { recursive: true });
|
|
71684
71799
|
copyFileSync2(plan.source.fullPath, destination);
|
|
71685
71800
|
written += 1;
|
|
@@ -71698,7 +71813,7 @@ function writeStationSnapshot(options) {
|
|
|
71698
71813
|
},
|
|
71699
71814
|
files: manifestFiles
|
|
71700
71815
|
};
|
|
71701
|
-
const manifestPath =
|
|
71816
|
+
const manifestPath = resolve2(repoRoot, "resources", options.stationId, "skills", "sync-manifest.json");
|
|
71702
71817
|
mkdirSync15(dirname11(manifestPath), { recursive: true });
|
|
71703
71818
|
writeFileSync16(manifestPath, `${JSON.stringify(manifest, null, 2)}
|
|
71704
71819
|
`);
|
|
@@ -71731,8 +71846,8 @@ var exports_create_sync_config = {};
|
|
|
71731
71846
|
__export(exports_create_sync_config, {
|
|
71732
71847
|
registerCreateSync: () => registerCreateSync
|
|
71733
71848
|
});
|
|
71734
|
-
import { existsSync as
|
|
71735
|
-
import { join as
|
|
71849
|
+
import { existsSync as existsSync28, writeFileSync as writeFileSync17, mkdirSync as mkdirSync16 } from "fs";
|
|
71850
|
+
import { join as join31 } from "path";
|
|
71736
71851
|
function registerCreateSync(parent) {
|
|
71737
71852
|
const configCmd = parent.command("config").description("Manage skills configuration");
|
|
71738
71853
|
configCmd.command("show", { isDefault: true }).option("--json", "Output as JSON", false).description("Show current merged configuration").action((options) => {
|
|
@@ -71798,13 +71913,13 @@ function registerCreateSync(parent) {
|
|
|
71798
71913
|
const pp = getConfigPath("project");
|
|
71799
71914
|
if (options.json) {
|
|
71800
71915
|
console.log(JSON.stringify({
|
|
71801
|
-
global: { path: gp, exists:
|
|
71802
|
-
project: { path: pp, exists:
|
|
71916
|
+
global: { path: gp, exists: existsSync28(gp) },
|
|
71917
|
+
project: { path: pp, exists: existsSync28(pp) }
|
|
71803
71918
|
}, null, 2));
|
|
71804
71919
|
return;
|
|
71805
71920
|
}
|
|
71806
|
-
console.log(`${source_default.cyan("global")}: ${gp}${
|
|
71807
|
-
console.log(`${source_default.cyan("project")}: ${pp}${
|
|
71921
|
+
console.log(`${source_default.cyan("global")}: ${gp}${existsSync28(gp) ? source_default.green(" (exists)") : source_default.dim(" (not found)")}`);
|
|
71922
|
+
console.log(`${source_default.cyan("project")}: ${pp}${existsSync28(pp) ? source_default.green(" (exists)") : source_default.dim(" (not found)")}`);
|
|
71808
71923
|
});
|
|
71809
71924
|
parent.command("create").argument("<name>", "Skill name (e.g. my-tool)").option("--category <category>", "Skill category", "Development Tools").option("--description <description>", "Short description of what the skill does").option("--tags <tags>", "Comma-separated tags (e.g. api,testing,automation)").option("--global", "Deprecated; custom skills are always global", false).option("--json", "Output result as JSON", false).description("Scaffold a new custom skill directory").action((name, options) => handleCreate(name, options));
|
|
71810
71925
|
parent.command("sync").alias("render").argument("[names...]", "Skills to sync (default: every skill in this machine's corpus)").option("--for <agent>", `Target one agent (${SYNC_AGENTS.join(", ")}, or all)`, "all").option("--all", "Sync every corpus skill (the default)", false).option("--source <path>", "Canonical corpus source: a directory of skill folders, or a package root with skills/ (overrides $SKILLS_SOURCE)").option("--dry-run", "Show what would be written without touching any agent folder", false).option("--force", "Adopt an unmanaged skill that already has SKILL.md; other unmarked directories are never overwritten", false).option("--check", "Home drift census (missing-from-home / stray-in-home / diverged); exits non-zero on drift, writes nothing", false).option("--adopt", "Unmarked-home adoption mode: hash unmarked home skills against the corpus; exact matches are marked (dry-run by default)", false).option("--prune", "Prune mode: list (or with --apply, remove) marked home skill dirs that have no canonical corpus entry", false).option("--apply", "Write adoption markers / conflicts ledger, or perform prune removals", false).option("--json", "Output as JSON", false).option("--station <id>", "Per-station snapshot mode: snapshot the installed skill homes into resources/<station>/skills with a v3 sync-manifest (dry-run by default; --populate writes)").option("--populate", "Write the per-station snapshot (station mode; the default is dry-run)", false).option("--repo-root <path>", "Station snapshot destination repo root (default: cwd)").option("--homes-root <dir>", "Build the station snapshot from a staged mirror of the skill homes instead of this machine's $HOME").description("Write corpus skills into each coding agent's global skills folder, per-tool adapted; with --station, snapshot the homes into a reviewed snapshot repo instead").action((names, options) => handleSync(names, options));
|
|
@@ -71812,9 +71927,9 @@ function registerCreateSync(parent) {
|
|
|
71812
71927
|
function handleCreate(name, options) {
|
|
71813
71928
|
const bare = name.trim();
|
|
71814
71929
|
const dirName = bare;
|
|
71815
|
-
const
|
|
71816
|
-
const skillDir =
|
|
71817
|
-
if (
|
|
71930
|
+
const baseDir2 = getPortableSkillsRoot();
|
|
71931
|
+
const skillDir = join31(baseDir2, dirName);
|
|
71932
|
+
if (existsSync28(skillDir)) {
|
|
71818
71933
|
console.log(options.json ? JSON.stringify({ error: `Skill '${bare}' already exists at ${skillDir}` }) : source_default.red(`Skill '${bare}' already exists at ${skillDir}`));
|
|
71819
71934
|
process.exitCode = 1;
|
|
71820
71935
|
return;
|
|
@@ -71822,8 +71937,8 @@ function handleCreate(name, options) {
|
|
|
71822
71937
|
const description = options.description || `${bare} skill`;
|
|
71823
71938
|
const tags = options.tags ? options.tags.split(",").map((t) => t.trim()).filter(Boolean) : [bare];
|
|
71824
71939
|
const displayName2 = bare.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
71825
|
-
mkdirSync16(
|
|
71826
|
-
writeFileSync17(
|
|
71940
|
+
mkdirSync16(join31(skillDir, "src"), { recursive: true });
|
|
71941
|
+
writeFileSync17(join31(skillDir, "SKILL.md"), [
|
|
71827
71942
|
"---",
|
|
71828
71943
|
`name: ${bare}`,
|
|
71829
71944
|
`description: ${description}`,
|
|
@@ -71843,11 +71958,11 @@ function handleCreate(name, options) {
|
|
|
71843
71958
|
""
|
|
71844
71959
|
].join(`
|
|
71845
71960
|
`));
|
|
71846
|
-
writeFileSync17(
|
|
71961
|
+
writeFileSync17(join31(skillDir, "src", "index.ts"), [`#!/usr/bin/env bun`, `/**`, ` * ${displayName2} \u2014 ${description}`, ` */`, "", `console.log("${displayName2}");`, ""].join(`
|
|
71847
71962
|
`));
|
|
71848
|
-
writeFileSync17(
|
|
71963
|
+
writeFileSync17(join31(skillDir, "package.json"), JSON.stringify({ name: bare, version: "0.1.0", description, bin: { [bare]: "./src/index.ts" }, scripts: { dev: `bun src/index.ts` }, dependencies: {} }, null, 2) + `
|
|
71849
71964
|
`);
|
|
71850
|
-
writeFileSync17(
|
|
71965
|
+
writeFileSync17(join31(skillDir, "tsconfig.json"), JSON.stringify({ compilerOptions: { target: "ES2022", module: "ESNext", moduleResolution: "bundler", strict: true, outDir: "dist" }, include: ["src/**/*.ts"] }, null, 2) + `
|
|
71851
71966
|
`);
|
|
71852
71967
|
clearRegistryCache();
|
|
71853
71968
|
if (options.json)
|
|
@@ -71856,8 +71971,8 @@ function handleCreate(name, options) {
|
|
|
71856
71971
|
console.log(source_default.green(`\u2713 Created custom skill '${bare}' at ${skillDir}`));
|
|
71857
71972
|
console.log(source_default.dim(` Category: ${options.category}`));
|
|
71858
71973
|
console.log(source_default.dim(` Tags: ${tags.join(", ")}`));
|
|
71859
|
-
console.log(` ${source_default.cyan("Edit:")} ${
|
|
71860
|
-
console.log(` ${source_default.cyan("Run:")} bun ${
|
|
71974
|
+
console.log(` ${source_default.cyan("Edit:")} ${join31(skillDir, "src", "index.ts")}`);
|
|
71975
|
+
console.log(` ${source_default.cyan("Run:")} bun ${join31(skillDir, "src", "index.ts")}`);
|
|
71861
71976
|
}
|
|
71862
71977
|
}
|
|
71863
71978
|
function handleSync(names, options) {
|
|
@@ -72111,16 +72226,16 @@ import {
|
|
|
72111
72226
|
statSync as statSync17,
|
|
72112
72227
|
writeFileSync as writeFileSync18
|
|
72113
72228
|
} from "fs";
|
|
72114
|
-
import { dirname as dirname12, join as
|
|
72229
|
+
import { dirname as dirname12, join as join32, resolve as resolve3, sep as sep5 } from "path";
|
|
72115
72230
|
function fail2(code, message, detail = []) {
|
|
72116
72231
|
throw new StationSnapshotError(code, message, detail);
|
|
72117
72232
|
}
|
|
72118
72233
|
function snapshotRootFor(repoRoot, stationId) {
|
|
72119
|
-
return
|
|
72234
|
+
return join32(repoRoot, "resources", stationId, "skills");
|
|
72120
72235
|
}
|
|
72121
72236
|
function readSnapshotManifest(repoRoot, stationId) {
|
|
72122
72237
|
const snapshotRoot = snapshotRootFor(repoRoot, stationId);
|
|
72123
|
-
const manifestPath =
|
|
72238
|
+
const manifestPath = join32(snapshotRoot, "sync-manifest.json");
|
|
72124
72239
|
let manifest;
|
|
72125
72240
|
try {
|
|
72126
72241
|
manifest = JSON.parse(readFileSync23(manifestPath, "utf8"));
|
|
@@ -72144,9 +72259,10 @@ function planStationHydration(stationId, repoRoot) {
|
|
|
72144
72259
|
}
|
|
72145
72260
|
const candidates = [];
|
|
72146
72261
|
const symlinks = [];
|
|
72262
|
+
const hashMismatches = [];
|
|
72147
72263
|
const skippedByRule = [];
|
|
72148
72264
|
for (const agent of SYNC_AGENTS) {
|
|
72149
|
-
const agentRoot =
|
|
72265
|
+
const agentRoot = join32(snapshotRoot, "agent-homes", agent);
|
|
72150
72266
|
let identEntries;
|
|
72151
72267
|
try {
|
|
72152
72268
|
identEntries = readdirSync16(agentRoot, { withFileTypes: true });
|
|
@@ -72157,7 +72273,7 @@ function planStationHydration(stationId, repoRoot) {
|
|
|
72157
72273
|
if (!identEntry.isDirectory() || identEntry.name.startsWith(".")) {
|
|
72158
72274
|
continue;
|
|
72159
72275
|
}
|
|
72160
|
-
const identRoot =
|
|
72276
|
+
const identRoot = join32(agentRoot, identEntry.name);
|
|
72161
72277
|
const entries = walkEntries(identRoot);
|
|
72162
72278
|
for (const entry of entries) {
|
|
72163
72279
|
const relativeParts = [identEntry.name, ...entry.relativePath.split(sep5)];
|
|
@@ -72205,6 +72321,18 @@ function planStationHydration(stationId, repoRoot) {
|
|
|
72205
72321
|
continue;
|
|
72206
72322
|
}
|
|
72207
72323
|
const info = statSync17(entry.fullPath);
|
|
72324
|
+
const manifestHash = manifestHashes.get(`${agent}${MANIFEST_HASH_KEY_SEP}${homeRelative}`) ?? null;
|
|
72325
|
+
let verified = false;
|
|
72326
|
+
if (manifestHash !== null) {
|
|
72327
|
+
verified = sha256File(entry.fullPath) === manifestHash;
|
|
72328
|
+
if (!verified) {
|
|
72329
|
+
hashMismatches.push({
|
|
72330
|
+
ident: identEntry.name,
|
|
72331
|
+
agent,
|
|
72332
|
+
relativePath: entry.relativePath
|
|
72333
|
+
});
|
|
72334
|
+
}
|
|
72335
|
+
}
|
|
72208
72336
|
candidates.push({
|
|
72209
72337
|
ident: identEntry.name,
|
|
72210
72338
|
agent,
|
|
@@ -72212,7 +72340,8 @@ function planStationHydration(stationId, repoRoot) {
|
|
|
72212
72340
|
fullPath: entry.fullPath,
|
|
72213
72341
|
size: info.size,
|
|
72214
72342
|
mtimeMs: info.mtimeMs,
|
|
72215
|
-
manifestHash
|
|
72343
|
+
manifestHash,
|
|
72344
|
+
verified
|
|
72216
72345
|
});
|
|
72217
72346
|
}
|
|
72218
72347
|
}
|
|
@@ -72220,6 +72349,9 @@ function planStationHydration(stationId, repoRoot) {
|
|
|
72220
72349
|
if (symlinks.length > 0) {
|
|
72221
72350
|
fail2("SYMLINKS_REFUSED", `${symlinks.length} symlink(s) inside the snapshot; symlinks are refused (fail closed)`);
|
|
72222
72351
|
}
|
|
72352
|
+
if (hashMismatches.length > 0) {
|
|
72353
|
+
fail2("MANIFEST_HASH_MISMATCH", `${hashMismatches.length} snapshot file(s) no longer match their sync-manifest sha256; ` + "stale or tampered content is refused (fail closed), nothing written \u2014 re-run sync to refresh the manifest", hashMismatches.map((mismatch) => `agent-homes/${mismatch.agent}/${mismatch.ident}/${mismatch.relativePath}`));
|
|
72354
|
+
}
|
|
72223
72355
|
const byIdent = new Map;
|
|
72224
72356
|
for (const candidate of candidates) {
|
|
72225
72357
|
const group = byIdent.get(candidate.ident) ?? [];
|
|
@@ -72254,8 +72386,8 @@ function planStationHydration(stationId, repoRoot) {
|
|
|
72254
72386
|
}
|
|
72255
72387
|
}
|
|
72256
72388
|
eligible.sort((left, right) => {
|
|
72257
|
-
const leftHash = left.
|
|
72258
|
-
const rightHash = right.
|
|
72389
|
+
const leftHash = left.verified;
|
|
72390
|
+
const rightHash = right.verified;
|
|
72259
72391
|
if (leftHash !== rightHash) {
|
|
72260
72392
|
return leftHash ? -1 : 1;
|
|
72261
72393
|
}
|
|
@@ -72292,8 +72424,8 @@ function skillSha256(skill) {
|
|
|
72292
72424
|
`)).digest("hex");
|
|
72293
72425
|
}
|
|
72294
72426
|
function writeStationHydration(options) {
|
|
72295
|
-
const repoRoot =
|
|
72296
|
-
const cacheRoot =
|
|
72427
|
+
const repoRoot = resolve3(options.repoRoot ?? process.cwd());
|
|
72428
|
+
const cacheRoot = resolve3(options.cacheRoot ?? resolveCorpusRoot());
|
|
72297
72429
|
const plan = planStationHydration(options.stationId, repoRoot);
|
|
72298
72430
|
const resultSkills = plan.winners.map((skill) => ({
|
|
72299
72431
|
ident: skill.ident,
|
|
@@ -72326,7 +72458,7 @@ function writeStationHydration(options) {
|
|
|
72326
72458
|
const toWrite = [];
|
|
72327
72459
|
for (const skill of plan.winners) {
|
|
72328
72460
|
for (const file of skill.files) {
|
|
72329
|
-
const destination =
|
|
72461
|
+
const destination = join32(cacheRoot, skill.ident, file.withinIdent);
|
|
72330
72462
|
const digest = sha256File(file.winner.fullPath);
|
|
72331
72463
|
let existingDigest = null;
|
|
72332
72464
|
try {
|
|
@@ -72368,7 +72500,7 @@ function writeStationHydration(options) {
|
|
|
72368
72500
|
},
|
|
72369
72501
|
skills: resultSkills
|
|
72370
72502
|
};
|
|
72371
|
-
const hydrationManifestPath =
|
|
72503
|
+
const hydrationManifestPath = join32(dirname12(cacheRoot), `hydration-${options.stationId}.json`);
|
|
72372
72504
|
mkdirSync17(dirname12(hydrationManifestPath), { recursive: true });
|
|
72373
72505
|
writeFileSync18(hydrationManifestPath, `${JSON.stringify(hydration, null, 2)}
|
|
72374
72506
|
`);
|
|
@@ -72875,7 +73007,7 @@ var init_revision = __esm(() => {
|
|
|
72875
73007
|
// src/lib/skill-bundle.ts
|
|
72876
73008
|
import { createHash as createHash7 } from "crypto";
|
|
72877
73009
|
import { readFileSync as readFileSync24, readdirSync as readdirSync17, statSync as statSync18 } from "fs";
|
|
72878
|
-
import { join as
|
|
73010
|
+
import { join as join33, relative as relative6 } from "path";
|
|
72879
73011
|
function isDotenvFile(lower) {
|
|
72880
73012
|
if (lower === ".env" || lower.startsWith(".env."))
|
|
72881
73013
|
return true;
|
|
@@ -72913,7 +73045,7 @@ function collectSkillBundleEntries(dir) {
|
|
|
72913
73045
|
}
|
|
72914
73046
|
function walk(root, current, out) {
|
|
72915
73047
|
for (const entry of readdirSync17(current, { withFileTypes: true })) {
|
|
72916
|
-
const absolute =
|
|
73048
|
+
const absolute = join33(current, entry.name);
|
|
72917
73049
|
const rel = relative6(root, absolute).split("\\").join("/");
|
|
72918
73050
|
const isRootLevel = !rel.includes("/");
|
|
72919
73051
|
if (ANY_SEGMENT_EXCLUDES.has(entry.name.toLowerCase()))
|
|
@@ -73223,8 +73355,8 @@ var init_skill_bundles = __esm(() => {
|
|
|
73223
73355
|
});
|
|
73224
73356
|
|
|
73225
73357
|
// src/lib/pull.ts
|
|
73226
|
-
import { existsSync as
|
|
73227
|
-
import { dirname as dirname14, join as
|
|
73358
|
+
import { existsSync as existsSync29, mkdirSync as mkdirSync19, mkdtempSync as mkdtempSync3, readFileSync as readFileSync25, renameSync as renameSync4, rmSync as rmSync6, writeFileSync as writeFileSync20 } from "fs";
|
|
73359
|
+
import { dirname as dirname14, join as join34 } from "path";
|
|
73228
73360
|
async function pullSkills(options = {}) {
|
|
73229
73361
|
const client = options.client !== undefined ? options.client : createRemoteSkillsClient();
|
|
73230
73362
|
if (!client) {
|
|
@@ -73269,7 +73401,7 @@ async function pullOne(client, rawName, corpusOptions, verify) {
|
|
|
73269
73401
|
return reconcileTombstone(slug, corpusOptions);
|
|
73270
73402
|
}
|
|
73271
73403
|
if (bundleResponse.status === 404) {
|
|
73272
|
-
const marker = readPullMarker(
|
|
73404
|
+
const marker = readPullMarker(join34(getPortableSkillsRoot(corpusOptions), slug));
|
|
73273
73405
|
if (marker && typeof marker.revisionId === "string" && marker.revisionId) {
|
|
73274
73406
|
return { name: slug, success: true, purged: true, removed: false };
|
|
73275
73407
|
}
|
|
@@ -73300,7 +73432,7 @@ async function pullOne(client, rawName, corpusOptions, verify) {
|
|
|
73300
73432
|
return { name: slug, success: false, error: `Skill '${slug}' was not found on the configured Skills instance.` };
|
|
73301
73433
|
}
|
|
73302
73434
|
if (!meta?.revisionId) {
|
|
73303
|
-
const marker = readPullMarker(
|
|
73435
|
+
const marker = readPullMarker(join34(getPortableSkillsRoot(corpusOptions), slug));
|
|
73304
73436
|
if (marker && typeof marker.revisionId === "string" && marker.revisionId) {
|
|
73305
73437
|
return { name: slug, success: true, purged: true, removed: false };
|
|
73306
73438
|
}
|
|
@@ -73357,8 +73489,8 @@ function provenRevision(meta, slug, bundle) {
|
|
|
73357
73489
|
return declared;
|
|
73358
73490
|
}
|
|
73359
73491
|
function reconcileTombstone(slug, corpusOptions) {
|
|
73360
|
-
const target =
|
|
73361
|
-
if (!
|
|
73492
|
+
const target = join34(getPortableSkillsRoot(corpusOptions), slug);
|
|
73493
|
+
if (!existsSync29(join34(target, PULL_MARKER_FILE))) {
|
|
73362
73494
|
return { name: slug, success: true, tombstoned: true, removed: false, leftInPlace: true };
|
|
73363
73495
|
}
|
|
73364
73496
|
rmSync6(target, { recursive: true, force: true });
|
|
@@ -73366,7 +73498,7 @@ function reconcileTombstone(slug, corpusOptions) {
|
|
|
73366
73498
|
}
|
|
73367
73499
|
function readPullMarker(dir) {
|
|
73368
73500
|
try {
|
|
73369
|
-
return JSON.parse(readFileSync25(
|
|
73501
|
+
return JSON.parse(readFileSync25(join34(dir, PULL_MARKER_FILE), "utf-8"));
|
|
73370
73502
|
} catch {
|
|
73371
73503
|
return null;
|
|
73372
73504
|
}
|
|
@@ -73487,14 +73619,14 @@ function verifyBundleResponseBytes(buffer, response, verify = {}) {
|
|
|
73487
73619
|
function installBundleAtomically(name, entries, options = {}, marker = {}) {
|
|
73488
73620
|
const root = getPortableSkillsRoot(options);
|
|
73489
73621
|
mkdirSync19(root, { recursive: true });
|
|
73490
|
-
const target =
|
|
73491
|
-
const created = !
|
|
73492
|
-
const staging = mkdtempSync3(
|
|
73622
|
+
const target = join34(root, name);
|
|
73623
|
+
const created = !existsSync29(target);
|
|
73624
|
+
const staging = mkdtempSync3(join34(root, `.pull-${name}-`));
|
|
73493
73625
|
let moved = false;
|
|
73494
73626
|
let backup = null;
|
|
73495
73627
|
try {
|
|
73496
73628
|
for (const entry of entries) {
|
|
73497
|
-
const destination =
|
|
73629
|
+
const destination = join34(staging, entry.path);
|
|
73498
73630
|
mkdirSync19(dirname14(destination), { recursive: true });
|
|
73499
73631
|
writeFileSync20(destination, entry.bytes, { mode: entry.mode });
|
|
73500
73632
|
}
|
|
@@ -73506,9 +73638,9 @@ function installBundleAtomically(name, entries, options = {}, marker = {}) {
|
|
|
73506
73638
|
...marker.signature ? { signature: marker.signature } : {},
|
|
73507
73639
|
...marker.revisionId ? { revisionId: marker.revisionId } : {}
|
|
73508
73640
|
});
|
|
73509
|
-
if (
|
|
73510
|
-
backup = mkdtempSync3(
|
|
73511
|
-
renameSync4(target,
|
|
73641
|
+
if (existsSync29(target)) {
|
|
73642
|
+
backup = mkdtempSync3(join34(root, `.pull-backup-${name}-`));
|
|
73643
|
+
renameSync4(target, join34(backup, name));
|
|
73512
73644
|
moved = true;
|
|
73513
73645
|
}
|
|
73514
73646
|
renameSync4(staging, target);
|
|
@@ -73516,9 +73648,9 @@ function installBundleAtomically(name, entries, options = {}, marker = {}) {
|
|
|
73516
73648
|
rmSync6(backup, { recursive: true, force: true });
|
|
73517
73649
|
} catch (error2) {
|
|
73518
73650
|
rmSync6(staging, { recursive: true, force: true });
|
|
73519
|
-
if (moved && backup &&
|
|
73651
|
+
if (moved && backup && existsSync29(join34(backup, name))) {
|
|
73520
73652
|
try {
|
|
73521
|
-
renameSync4(
|
|
73653
|
+
renameSync4(join34(backup, name), target);
|
|
73522
73654
|
} catch {}
|
|
73523
73655
|
}
|
|
73524
73656
|
throw error2;
|
|
@@ -73537,7 +73669,7 @@ function writePullMarker(dir, record3) {
|
|
|
73537
73669
|
...record3.revisionId ? { revisionId: record3.revisionId } : {},
|
|
73538
73670
|
syncedAt: new Date().toISOString()
|
|
73539
73671
|
};
|
|
73540
|
-
writeFileSync20(
|
|
73672
|
+
writeFileSync20(join34(dir, PULL_MARKER_FILE), `${JSON.stringify(marker, null, 2)}
|
|
73541
73673
|
`);
|
|
73542
73674
|
}
|
|
73543
73675
|
async function safeMeta(client, slug) {
|
|
@@ -73611,12 +73743,12 @@ function registerRegistry(parent) {
|
|
|
73611
73743
|
async function writeJson2(value, space) {
|
|
73612
73744
|
const text = `${JSON.stringify(value, null, space)}
|
|
73613
73745
|
`;
|
|
73614
|
-
await new Promise((
|
|
73746
|
+
await new Promise((resolve4, reject2) => {
|
|
73615
73747
|
process.stdout.write(text, (error2) => {
|
|
73616
73748
|
if (error2)
|
|
73617
73749
|
reject2(error2);
|
|
73618
73750
|
else
|
|
73619
|
-
|
|
73751
|
+
resolve4();
|
|
73620
73752
|
});
|
|
73621
73753
|
});
|
|
73622
73754
|
}
|
|
@@ -73708,8 +73840,8 @@ __export(exports_publish, {
|
|
|
73708
73840
|
pushSkill: () => pushSkill,
|
|
73709
73841
|
PushSkillError: () => PushSkillError
|
|
73710
73842
|
});
|
|
73711
|
-
import { existsSync as
|
|
73712
|
-
import { join as
|
|
73843
|
+
import { existsSync as existsSync30, readFileSync as readFileSync26 } from "fs";
|
|
73844
|
+
import { join as join35 } from "path";
|
|
73713
73845
|
function registerPublish(parent) {
|
|
73714
73846
|
parent.command("push").argument("<name>", "Name of a skill in the local corpus (~/.hasna/skills/installed or the migrated ~/.hasna/skills/skills)").option("--version <version>", "Override the version recorded on the instance").option("--dry-run", "Pack and validate without uploading", false).option("--json", "Output result as JSON", false).description("Publish a local skill to the configured skills instance").action(async (name, options) => {
|
|
73715
73847
|
try {
|
|
@@ -73750,8 +73882,8 @@ async function pushSkill(name, options = {}) {
|
|
|
73750
73882
|
}
|
|
73751
73883
|
const manifest = readPortableSkillManifest(skill.path, skill.name);
|
|
73752
73884
|
const packed = packSkillBundle(skill.path, { maxUnpackedBytes: MAX_UNPACKED_BYTES });
|
|
73753
|
-
const skillMdPath =
|
|
73754
|
-
const skillMd =
|
|
73885
|
+
const skillMdPath = join35(skill.path, "SKILL.md");
|
|
73886
|
+
const skillMd = existsSync30(skillMdPath) ? readFileSync26(skillMdPath, "utf-8") : undefined;
|
|
73755
73887
|
const base2 = {
|
|
73756
73888
|
slug: skill.name,
|
|
73757
73889
|
path: skill.path,
|
|
@@ -73868,10 +74000,10 @@ __export(exports_auth, {
|
|
|
73868
74000
|
import { createInterface as createInterface2 } from "readline";
|
|
73869
74001
|
function prompt(question) {
|
|
73870
74002
|
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
73871
|
-
return new Promise((
|
|
74003
|
+
return new Promise((resolve4) => {
|
|
73872
74004
|
rl.question(question, (answer) => {
|
|
73873
74005
|
rl.close();
|
|
73874
|
-
|
|
74006
|
+
resolve4(answer.trim());
|
|
73875
74007
|
});
|
|
73876
74008
|
});
|
|
73877
74009
|
}
|
|
@@ -74021,7 +74153,7 @@ function printWhoami(payload) {
|
|
|
74021
74153
|
console.log(source_default.dim("(offline \u2014 showing cached info)"));
|
|
74022
74154
|
}
|
|
74023
74155
|
function sleep(ms) {
|
|
74024
|
-
return new Promise((
|
|
74156
|
+
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
74025
74157
|
}
|
|
74026
74158
|
function browserCommand(url) {
|
|
74027
74159
|
if (process.platform === "darwin")
|
|
@@ -74522,8 +74654,8 @@ var init_storage = __esm(() => {
|
|
|
74522
74654
|
});
|
|
74523
74655
|
|
|
74524
74656
|
// src/lib/registry-reconcile.ts
|
|
74525
|
-
import { existsSync as
|
|
74526
|
-
import { join as
|
|
74657
|
+
import { existsSync as existsSync31, readFileSync as readFileSync27, statSync as statSync19, writeFileSync as writeFileSync21 } from "fs";
|
|
74658
|
+
import { join as join36 } from "path";
|
|
74527
74659
|
function isDirectory2(path) {
|
|
74528
74660
|
try {
|
|
74529
74661
|
return statSync19(path).isDirectory();
|
|
@@ -74534,12 +74666,12 @@ function isDirectory2(path) {
|
|
|
74534
74666
|
function migrationNeeded(options) {
|
|
74535
74667
|
if (options.rootDir)
|
|
74536
74668
|
return false;
|
|
74537
|
-
const appDir = options.homeDir ?
|
|
74538
|
-
return !(isOwnerLayoutMigrated(appDir) && isDirectory2(
|
|
74669
|
+
const appDir = options.homeDir ? join36(options.homeDir, ".hasna", "skills") : getDataDir();
|
|
74670
|
+
return !(isOwnerLayoutMigrated(appDir) && isDirectory2(join36(appDir, SKILLS_CACHE_DIRNAME)));
|
|
74539
74671
|
}
|
|
74540
74672
|
function readBaseline(skillDir) {
|
|
74541
|
-
const markerPath =
|
|
74542
|
-
if (!
|
|
74673
|
+
const markerPath = join36(skillDir, PULL_MARKER_FILE);
|
|
74674
|
+
if (!existsSync31(markerPath))
|
|
74543
74675
|
return;
|
|
74544
74676
|
try {
|
|
74545
74677
|
const marker = JSON.parse(readFileSync27(markerPath, "utf-8"));
|
|
@@ -74552,8 +74684,8 @@ function readBaseline(skillDir) {
|
|
|
74552
74684
|
}
|
|
74553
74685
|
}
|
|
74554
74686
|
function readCursor(root) {
|
|
74555
|
-
const path =
|
|
74556
|
-
if (!
|
|
74687
|
+
const path = join36(root, SYNC_CURSOR_FILE);
|
|
74688
|
+
if (!existsSync31(path))
|
|
74557
74689
|
return { runCount: 0 };
|
|
74558
74690
|
try {
|
|
74559
74691
|
const cursor = JSON.parse(readFileSync27(path, "utf-8"));
|
|
@@ -74565,12 +74697,12 @@ function readCursor(root) {
|
|
|
74565
74697
|
function resolveCorpusRootReadOnly(options) {
|
|
74566
74698
|
if (options.rootDir)
|
|
74567
74699
|
return { root: options.rootDir, migrationPending: false };
|
|
74568
|
-
const appDir = options.homeDir ?
|
|
74569
|
-
const cache3 =
|
|
74700
|
+
const appDir = options.homeDir ? join36(options.homeDir, ".hasna", "skills") : getDataDirReadOnly();
|
|
74701
|
+
const cache3 = join36(appDir, SKILLS_CACHE_DIRNAME);
|
|
74570
74702
|
if (isOwnerLayoutMigrated(appDir) && isDirectory2(cache3)) {
|
|
74571
74703
|
return { root: cache3, migrationPending: false };
|
|
74572
74704
|
}
|
|
74573
|
-
return { root:
|
|
74705
|
+
return { root: join36(appDir, INSTALLED_SKILLS_DIRNAME), migrationPending: true };
|
|
74574
74706
|
}
|
|
74575
74707
|
function remoteRowToSkill(record3) {
|
|
74576
74708
|
const slug = typeof record3.slug === "string" ? record3.slug : typeof record3.name === "string" ? record3.name : undefined;
|
|
@@ -74584,7 +74716,7 @@ function remoteRowToSkill(record3) {
|
|
|
74584
74716
|
}
|
|
74585
74717
|
function recheckLocalSide(plannedLocal, localDir, ops = {
|
|
74586
74718
|
pack: (dir) => packSkillBundle(dir).sha256,
|
|
74587
|
-
exists:
|
|
74719
|
+
exists: existsSync31
|
|
74588
74720
|
}) {
|
|
74589
74721
|
let localNow;
|
|
74590
74722
|
try {
|
|
@@ -74702,7 +74834,7 @@ async function reconcileRegistry(options = {}) {
|
|
|
74702
74834
|
for (const slug of allSlugs) {
|
|
74703
74835
|
const local = locals.get(slug);
|
|
74704
74836
|
const remote = remotes.get(slug);
|
|
74705
|
-
const baseline = local ? readBaseline(
|
|
74837
|
+
const baseline = local ? readBaseline(join36(root, slug)) : undefined;
|
|
74706
74838
|
const { state, reason } = classifySkill(local, remote, baseline);
|
|
74707
74839
|
let { action, reason: actionReason } = resolveAction(state, direction, conflict);
|
|
74708
74840
|
if (state === "remote-only" && isDigestless(remote)) {
|
|
@@ -74761,7 +74893,7 @@ async function reconcileRegistry(options = {}) {
|
|
|
74761
74893
|
try {
|
|
74762
74894
|
await pushSkill(slug, { rootDir: root, client });
|
|
74763
74895
|
const pushed = locals.get(slug);
|
|
74764
|
-
writePullMarker(
|
|
74896
|
+
writePullMarker(join36(root, slug), {
|
|
74765
74897
|
skill: slug,
|
|
74766
74898
|
...pushed?.version ? { version: pushed.version } : {},
|
|
74767
74899
|
...pushed?.sha256 ? { contentHash: pushed.sha256 } : {},
|
|
@@ -74833,7 +74965,7 @@ async function reconcileRegistry(options = {}) {
|
|
|
74833
74965
|
runCount: readCursor(root).runCount + 1,
|
|
74834
74966
|
summary
|
|
74835
74967
|
};
|
|
74836
|
-
writeFileSync21(
|
|
74968
|
+
writeFileSync21(join36(root, SYNC_CURSOR_FILE), `${JSON.stringify(cursor, null, 2)}
|
|
74837
74969
|
`);
|
|
74838
74970
|
return {
|
|
74839
74971
|
corpusRoot: root,
|
|
@@ -79963,19 +80095,15 @@ var import_react21 = __toESM(require_react(), 1);
|
|
|
79963
80095
|
// src/cli/index.tsx
|
|
79964
80096
|
init_esm();
|
|
79965
80097
|
|
|
79966
|
-
//
|
|
80098
|
+
// ../../node_modules/.bun/@hasna+events@0.1.16/node_modules/@hasna/events/dist/commander.js
|
|
79967
80099
|
import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
79968
80100
|
import { Buffer as Buffer22 } from "buffer";
|
|
79969
80101
|
import { existsSync as existsSync2 } from "fs";
|
|
79970
80102
|
import { homedir } from "os";
|
|
79971
80103
|
import { join as join2 } from "path";
|
|
79972
80104
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
79973
|
-
import { lookup as dnsLookup } from "dns/promises";
|
|
79974
|
-
import { isIP } from "net";
|
|
79975
80105
|
import { randomUUID } from "crypto";
|
|
79976
80106
|
import { spawn } from "child_process";
|
|
79977
|
-
import { request as nodeHttpRequest } from "http";
|
|
79978
|
-
import { request as nodeHttpsRequest } from "https";
|
|
79979
80107
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
79980
80108
|
function getPathValue(input, path) {
|
|
79981
80109
|
return path.split(".").reduce((value, part) => {
|
|
@@ -80378,214 +80506,6 @@ function signPayload(secret, timestamp, body) {
|
|
|
80378
80506
|
const digest = createHmac("sha256", secret).update(buildSignatureBase(timestamp, body)).digest("hex");
|
|
80379
80507
|
return `sha256=${digest}`;
|
|
80380
80508
|
}
|
|
80381
|
-
var DEFAULT_MAX_REDIRECTS = 5;
|
|
80382
|
-
var IPV4_PRIVATE_RANGES = [
|
|
80383
|
-
[0, 16777215],
|
|
80384
|
-
[167772160, 184549375],
|
|
80385
|
-
[1681915904, 1686110207],
|
|
80386
|
-
[2130706432, 2147483647],
|
|
80387
|
-
[2851995648, 2852061183],
|
|
80388
|
-
[2886729728, 2887778303],
|
|
80389
|
-
[3221225472, 3221225727],
|
|
80390
|
-
[3221225984, 3221226239],
|
|
80391
|
-
[3227017984, 3227018239],
|
|
80392
|
-
[3232235520, 3232301055],
|
|
80393
|
-
[3323068416, 3323199487],
|
|
80394
|
-
[3325256704, 3325256959],
|
|
80395
|
-
[3405803776, 3405804031],
|
|
80396
|
-
[3758096384, 4294967295]
|
|
80397
|
-
];
|
|
80398
|
-
var IPV6_SPECIAL_PREFIXES = [
|
|
80399
|
-
{ groups: [0, 0, 0, 0, 0, 0, 0, 0], bits: 128 },
|
|
80400
|
-
{ groups: [0, 0, 0, 0, 0, 0, 0, 1], bits: 128 },
|
|
80401
|
-
{ groups: [0, 0, 0, 0, 0, 65535, 0, 0], bits: 96 },
|
|
80402
|
-
{ groups: [100, 65435, 0, 0, 0, 0, 0, 0], bits: 96 },
|
|
80403
|
-
{ groups: [256, 0, 0, 0, 0, 0, 0, 0], bits: 64 },
|
|
80404
|
-
{ groups: [8193, 0, 0, 0, 0, 0, 0, 0], bits: 32 },
|
|
80405
|
-
{ groups: [8193, 2, 0, 0, 0, 0, 0, 0], bits: 48 },
|
|
80406
|
-
{ groups: [8193, 16, 0, 0, 0, 0, 0, 0], bits: 28 },
|
|
80407
|
-
{ groups: [8193, 3512, 0, 0, 0, 0, 0, 0], bits: 32 },
|
|
80408
|
-
{ groups: [8194, 0, 0, 0, 0, 0, 0, 0], bits: 16 },
|
|
80409
|
-
{ groups: [16383, 0, 0, 0, 0, 0, 0, 0], bits: 20 },
|
|
80410
|
-
{ groups: [64512, 0, 0, 0, 0, 0, 0, 0], bits: 7 },
|
|
80411
|
-
{ groups: [65152, 0, 0, 0, 0, 0, 0, 0], bits: 10 },
|
|
80412
|
-
{ groups: [65216, 0, 0, 0, 0, 0, 0, 0], bits: 10 },
|
|
80413
|
-
{ groups: [65280, 0, 0, 0, 0, 0, 0, 0], bits: 8 }
|
|
80414
|
-
];
|
|
80415
|
-
function isPrivateAddress(address) {
|
|
80416
|
-
const normalized = stripZoneId(address);
|
|
80417
|
-
const version = isIP(normalized);
|
|
80418
|
-
if (version === 4) {
|
|
80419
|
-
const integer = ipv4ToInt(normalized);
|
|
80420
|
-
if (integer === undefined)
|
|
80421
|
-
return true;
|
|
80422
|
-
return IPV4_PRIVATE_RANGES.some(([low, high]) => integer >= low && integer <= high);
|
|
80423
|
-
}
|
|
80424
|
-
if (version === 6) {
|
|
80425
|
-
const groups = ipv6Groups(normalized);
|
|
80426
|
-
if (!groups)
|
|
80427
|
-
return true;
|
|
80428
|
-
for (const prefix of IPV6_SPECIAL_PREFIXES) {
|
|
80429
|
-
if (!ipv6MatchesPrefix(groups, prefix.groups, prefix.bits))
|
|
80430
|
-
continue;
|
|
80431
|
-
if (prefix.bits === 96 && groups[5] === 65535) {
|
|
80432
|
-
return isPrivateAddress(ipv4IntToString(groups[6] << 16 | groups[7]));
|
|
80433
|
-
}
|
|
80434
|
-
if (prefix.bits === 16 && groups[0] === 8194) {
|
|
80435
|
-
return isPrivateAddress(ipv4IntToString(groups[1] << 16 | groups[2]));
|
|
80436
|
-
}
|
|
80437
|
-
return true;
|
|
80438
|
-
}
|
|
80439
|
-
return false;
|
|
80440
|
-
}
|
|
80441
|
-
return true;
|
|
80442
|
-
}
|
|
80443
|
-
async function resolveWebhookTarget(url, policy = {}) {
|
|
80444
|
-
const hostname = normalizeHostname(url.hostname);
|
|
80445
|
-
const allowlist = (policy.allowPrivateHosts ?? []).map((entry) => normalizeHostname(entry.toLowerCase()));
|
|
80446
|
-
if (allowlist.includes(hostname)) {
|
|
80447
|
-
const version2 = isIP(hostname);
|
|
80448
|
-
if (version2 === 4 || version2 === 6) {
|
|
80449
|
-
return { hostname, addresses: [hostname] };
|
|
80450
|
-
}
|
|
80451
|
-
const lookup2 = policy.lookup ?? defaultTargetLookup;
|
|
80452
|
-
let resolved2;
|
|
80453
|
-
try {
|
|
80454
|
-
resolved2 = await lookup2(hostname);
|
|
80455
|
-
} catch {
|
|
80456
|
-
throw new Error(`Webhook target ${hostname} could not be resolved`);
|
|
80457
|
-
}
|
|
80458
|
-
if (!Array.isArray(resolved2) || resolved2.length === 0) {
|
|
80459
|
-
throw new Error(`Webhook target ${hostname} resolved to no addresses`);
|
|
80460
|
-
}
|
|
80461
|
-
const addresses = resolved2.map((entry) => normalizeHostname(entry.address));
|
|
80462
|
-
return { hostname, addresses };
|
|
80463
|
-
}
|
|
80464
|
-
const version = isIP(hostname);
|
|
80465
|
-
if (version === 4 || version === 6) {
|
|
80466
|
-
if (isPrivateAddress(hostname)) {
|
|
80467
|
-
throw new Error(`Webhook target ${hostname} is a private or special-use address`);
|
|
80468
|
-
}
|
|
80469
|
-
return { hostname, addresses: [hostname] };
|
|
80470
|
-
}
|
|
80471
|
-
const lookup = policy.lookup ?? defaultTargetLookup;
|
|
80472
|
-
let resolved;
|
|
80473
|
-
try {
|
|
80474
|
-
resolved = await lookup(hostname);
|
|
80475
|
-
} catch {
|
|
80476
|
-
throw new Error(`Webhook target ${hostname} could not be resolved`);
|
|
80477
|
-
}
|
|
80478
|
-
if (!Array.isArray(resolved) || resolved.length === 0) {
|
|
80479
|
-
throw new Error(`Webhook target ${hostname} resolved to no addresses`);
|
|
80480
|
-
}
|
|
80481
|
-
const allowed = [];
|
|
80482
|
-
for (const entry of resolved) {
|
|
80483
|
-
const address = normalizeHostname(entry.address);
|
|
80484
|
-
if (isPrivateAddress(address)) {
|
|
80485
|
-
if (allowlist.includes(address)) {
|
|
80486
|
-
allowed.push(address);
|
|
80487
|
-
continue;
|
|
80488
|
-
}
|
|
80489
|
-
throw new Error(`Webhook target ${hostname} resolves to private or special-use address ${address}`);
|
|
80490
|
-
}
|
|
80491
|
-
allowed.push(address);
|
|
80492
|
-
}
|
|
80493
|
-
if (allowed.length === 0) {
|
|
80494
|
-
throw new Error(`Webhook target ${hostname} resolved to no public addresses`);
|
|
80495
|
-
}
|
|
80496
|
-
return { hostname, addresses: allowed };
|
|
80497
|
-
}
|
|
80498
|
-
function normalizeMaxRedirects(value) {
|
|
80499
|
-
if (value === undefined)
|
|
80500
|
-
return DEFAULT_MAX_REDIRECTS;
|
|
80501
|
-
if (!Number.isInteger(value) || value < 0)
|
|
80502
|
-
throw new Error("webhookTargetPolicy.maxRedirects must be a non-negative integer");
|
|
80503
|
-
return value;
|
|
80504
|
-
}
|
|
80505
|
-
var defaultTargetLookup = async (hostname) => {
|
|
80506
|
-
return dnsLookup(hostname, { all: true, verbatim: false });
|
|
80507
|
-
};
|
|
80508
|
-
function normalizeHostname(hostname) {
|
|
80509
|
-
const lower = hostname.toLowerCase();
|
|
80510
|
-
if (lower.startsWith("[") && lower.endsWith("]"))
|
|
80511
|
-
return lower.slice(1, -1);
|
|
80512
|
-
return lower;
|
|
80513
|
-
}
|
|
80514
|
-
function stripZoneId(address) {
|
|
80515
|
-
const percent = address.indexOf("%");
|
|
80516
|
-
return percent === -1 ? address : address.slice(0, percent);
|
|
80517
|
-
}
|
|
80518
|
-
function ipv4ToInt(address) {
|
|
80519
|
-
const parts = address.split(".");
|
|
80520
|
-
if (parts.length !== 4)
|
|
80521
|
-
return;
|
|
80522
|
-
let value = 0;
|
|
80523
|
-
for (const part of parts) {
|
|
80524
|
-
if (!/^\d{1,3}$/.test(part))
|
|
80525
|
-
return;
|
|
80526
|
-
const octet = Number(part);
|
|
80527
|
-
if (octet > 255)
|
|
80528
|
-
return;
|
|
80529
|
-
value = value << 8 | octet;
|
|
80530
|
-
}
|
|
80531
|
-
return value >>> 0;
|
|
80532
|
-
}
|
|
80533
|
-
function ipv4IntToString(integer) {
|
|
80534
|
-
return [
|
|
80535
|
-
integer >>> 24 & 255,
|
|
80536
|
-
integer >>> 16 & 255,
|
|
80537
|
-
integer >>> 8 & 255,
|
|
80538
|
-
integer & 255
|
|
80539
|
-
].join(".");
|
|
80540
|
-
}
|
|
80541
|
-
function ipv6Groups(address) {
|
|
80542
|
-
const raw = stripZoneId(address);
|
|
80543
|
-
const doubleColon = raw.indexOf("::");
|
|
80544
|
-
const headText = doubleColon === -1 ? raw : raw.slice(0, doubleColon);
|
|
80545
|
-
const tailText = doubleColon === -1 ? "" : raw.slice(doubleColon + 2);
|
|
80546
|
-
const parseGroups = (text) => {
|
|
80547
|
-
if (text === "")
|
|
80548
|
-
return [];
|
|
80549
|
-
const out = [];
|
|
80550
|
-
for (const part of text.split(":")) {
|
|
80551
|
-
if (part.includes(".")) {
|
|
80552
|
-
const v4 = ipv4ToInt(part);
|
|
80553
|
-
if (v4 === undefined)
|
|
80554
|
-
return;
|
|
80555
|
-
out.push(v4 >>> 16 & 65535, v4 & 65535);
|
|
80556
|
-
} else {
|
|
80557
|
-
if (!/^[0-9a-fA-F]{1,4}$/.test(part))
|
|
80558
|
-
return;
|
|
80559
|
-
out.push(parseInt(part, 16));
|
|
80560
|
-
}
|
|
80561
|
-
}
|
|
80562
|
-
return out;
|
|
80563
|
-
};
|
|
80564
|
-
const head2 = parseGroups(headText);
|
|
80565
|
-
if (!head2)
|
|
80566
|
-
return;
|
|
80567
|
-
const tail2 = parseGroups(tailText);
|
|
80568
|
-
if (!tail2)
|
|
80569
|
-
return;
|
|
80570
|
-
const total = head2.length + tail2.length;
|
|
80571
|
-
if (doubleColon === -1) {
|
|
80572
|
-
return total === 8 ? head2 : undefined;
|
|
80573
|
-
}
|
|
80574
|
-
if (total >= 8)
|
|
80575
|
-
return;
|
|
80576
|
-
return [...head2, ...new Array(8 - total).fill(0), ...tail2];
|
|
80577
|
-
}
|
|
80578
|
-
function ipv6MatchesPrefix(groups, prefixGroups, prefixBits) {
|
|
80579
|
-
let remaining = prefixBits;
|
|
80580
|
-
for (let index = 0;index < prefixGroups.length && remaining > 0; index += 1) {
|
|
80581
|
-
const take2 = Math.min(16, remaining);
|
|
80582
|
-
const mask = 65535 << 16 - take2 & 65535;
|
|
80583
|
-
if ((groups[index] & mask) !== (prefixGroups[index] & mask))
|
|
80584
|
-
return false;
|
|
80585
|
-
remaining -= take2;
|
|
80586
|
-
}
|
|
80587
|
-
return true;
|
|
80588
|
-
}
|
|
80589
80509
|
function now2() {
|
|
80590
80510
|
return new Date().toISOString();
|
|
80591
80511
|
}
|
|
@@ -80616,18 +80536,9 @@ function buildWebhookRequest(event, channel, options = {}) {
|
|
|
80616
80536
|
}
|
|
80617
80537
|
return { body, headers };
|
|
80618
80538
|
}
|
|
80619
|
-
function normalizeWebhookUrl(raw) {
|
|
80620
|
-
const url = new URL(raw);
|
|
80621
|
-
if (url.username !== "" || url.password !== "") {
|
|
80622
|
-
url.username = "";
|
|
80623
|
-
url.password = "";
|
|
80624
|
-
}
|
|
80625
|
-
return url.toString();
|
|
80626
|
-
}
|
|
80627
80539
|
async function dispatchWebhook(event, channel, options = {}) {
|
|
80628
80540
|
if (!channel.webhook)
|
|
80629
80541
|
throw new Error(`Channel ${channel.id} has no webhook config`);
|
|
80630
|
-
const webhookUrl = normalizeWebhookUrl(channel.webhook.url);
|
|
80631
80542
|
const startedAt = now2();
|
|
80632
80543
|
let secret = channel.webhook.secret;
|
|
80633
80544
|
if (channel.webhook.secretRef) {
|
|
@@ -80644,14 +80555,10 @@ async function dispatchWebhook(event, channel, options = {}) {
|
|
|
80644
80555
|
}
|
|
80645
80556
|
const timestamp = (options.now?.() ?? new Date).toISOString();
|
|
80646
80557
|
const { body, headers } = buildWebhookRequest(event, channel, { secret, timestamp });
|
|
80647
|
-
const validateTargets = options.webhookTargetPolicy !== undefined || options.fetchImpl === undefined;
|
|
80648
|
-
if (validateTargets) {
|
|
80649
|
-
return dispatchValidatedWebhook(event, channel, { body, headers, startedAt, options });
|
|
80650
|
-
}
|
|
80651
80558
|
const controller = new AbortController;
|
|
80652
80559
|
const timeout = setTimeout(() => controller.abort(), channel.webhook.timeoutMs ?? 15000);
|
|
80653
80560
|
try {
|
|
80654
|
-
const response = await (options.fetchImpl ?? fetch)(
|
|
80561
|
+
const response = await (options.fetchImpl ?? fetch)(channel.webhook.url, {
|
|
80655
80562
|
method: "POST",
|
|
80656
80563
|
headers,
|
|
80657
80564
|
body,
|
|
@@ -80679,130 +80586,6 @@ async function dispatchWebhook(event, channel, options = {}) {
|
|
|
80679
80586
|
clearTimeout(timeout);
|
|
80680
80587
|
}
|
|
80681
80588
|
}
|
|
80682
|
-
function isRedirectStatus(status) {
|
|
80683
|
-
return status === 301 || status === 302 || status === 303 || status === 307 || status === 308;
|
|
80684
|
-
}
|
|
80685
|
-
function redirectKeepsBody(status) {
|
|
80686
|
-
return status === 307 || status === 308;
|
|
80687
|
-
}
|
|
80688
|
-
async function pinnedNativeRequest(target, addresses, method2, headers, body, signal, tls) {
|
|
80689
|
-
const isHttps = target.protocol === "https:";
|
|
80690
|
-
if (!isHttps && target.protocol !== "http:") {
|
|
80691
|
-
throw new Error(`Webhook target uses unsupported protocol ${target.protocol}`);
|
|
80692
|
-
}
|
|
80693
|
-
const defaultPort = isHttps ? 443 : 80;
|
|
80694
|
-
const port = target.port ? Number(target.port) : defaultPort;
|
|
80695
|
-
const requestOptions = {
|
|
80696
|
-
hostname: target.hostname,
|
|
80697
|
-
port,
|
|
80698
|
-
path: `${target.pathname}${target.search}`,
|
|
80699
|
-
method: method2,
|
|
80700
|
-
headers,
|
|
80701
|
-
...tls?.ca ? { ca: tls.ca } : {},
|
|
80702
|
-
lookup: (hostname, _options, callback) => {
|
|
80703
|
-
const entries = addresses.map((address) => ({
|
|
80704
|
-
address,
|
|
80705
|
-
family: address.includes(":") ? 6 : 4
|
|
80706
|
-
}));
|
|
80707
|
-
callback(null, entries);
|
|
80708
|
-
}
|
|
80709
|
-
};
|
|
80710
|
-
return new Promise((resolve, reject2) => {
|
|
80711
|
-
const request = isHttps ? nodeHttpsRequest(requestOptions, onResponse) : nodeHttpRequest(requestOptions, onResponse);
|
|
80712
|
-
const onAbort = () => {
|
|
80713
|
-
const error = new Error("The operation was aborted.");
|
|
80714
|
-
error.name = "AbortError";
|
|
80715
|
-
request.destroy(error);
|
|
80716
|
-
};
|
|
80717
|
-
if (signal.aborted)
|
|
80718
|
-
onAbort();
|
|
80719
|
-
else
|
|
80720
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
80721
|
-
request.on("error", reject2);
|
|
80722
|
-
if (body !== undefined)
|
|
80723
|
-
request.write(body);
|
|
80724
|
-
request.end();
|
|
80725
|
-
function onResponse(response) {
|
|
80726
|
-
const chunks = [];
|
|
80727
|
-
response.on("data", (chunk2) => chunks.push(Buffer.from(chunk2)));
|
|
80728
|
-
response.on("error", reject2);
|
|
80729
|
-
response.on("end", () => {
|
|
80730
|
-
const headersRecord = {};
|
|
80731
|
-
for (const [name, value] of Object.entries(response.headers)) {
|
|
80732
|
-
if (typeof value === "string")
|
|
80733
|
-
headersRecord[name] = value;
|
|
80734
|
-
else if (Array.isArray(value))
|
|
80735
|
-
headersRecord[name] = value.join(", ");
|
|
80736
|
-
}
|
|
80737
|
-
resolve(new Response(Buffer.concat(chunks), { status: response.statusCode ?? 200, headers: headersRecord }));
|
|
80738
|
-
});
|
|
80739
|
-
}
|
|
80740
|
-
});
|
|
80741
|
-
}
|
|
80742
|
-
async function dispatchValidatedWebhook(event, channel, input) {
|
|
80743
|
-
const { body, headers, startedAt, options } = input;
|
|
80744
|
-
const webhook = channel.webhook;
|
|
80745
|
-
if (!webhook)
|
|
80746
|
-
throw new Error(`Channel ${channel.id} has no webhook config`);
|
|
80747
|
-
const policy = options.webhookTargetPolicy ?? {};
|
|
80748
|
-
const maxRedirects = normalizeMaxRedirects(policy.maxRedirects);
|
|
80749
|
-
const controller = new AbortController;
|
|
80750
|
-
const timeout = setTimeout(() => controller.abort(), webhook.timeoutMs ?? 15000);
|
|
80751
|
-
try {
|
|
80752
|
-
let target = new URL(normalizeWebhookUrl(webhook.url));
|
|
80753
|
-
let requestHeaders = headers;
|
|
80754
|
-
let method2 = "POST";
|
|
80755
|
-
let requestBody = body;
|
|
80756
|
-
let redirectsFollowed = 0;
|
|
80757
|
-
for (;; ) {
|
|
80758
|
-
const resolved = await resolveWebhookTarget(target, policy).catch((error) => {
|
|
80759
|
-
throw new Error(`Webhook target rejected by SSRF guard: ${error.message}`);
|
|
80760
|
-
});
|
|
80761
|
-
const response = options.fetchImpl ? await options.fetchImpl(target, {
|
|
80762
|
-
method: method2,
|
|
80763
|
-
headers: requestHeaders,
|
|
80764
|
-
body: requestBody,
|
|
80765
|
-
signal: controller.signal,
|
|
80766
|
-
redirect: "manual"
|
|
80767
|
-
}) : await pinnedNativeRequest(target, resolved.addresses, method2, requestHeaders, requestBody, controller.signal, options.tls);
|
|
80768
|
-
const location = response.headers.get("location");
|
|
80769
|
-
if (isRedirectStatus(response.status) && location) {
|
|
80770
|
-
if (redirectsFollowed >= maxRedirects) {
|
|
80771
|
-
return failedAttempt(startedAt, `Webhook target exceeded ${maxRedirects} redirects`);
|
|
80772
|
-
}
|
|
80773
|
-
redirectsFollowed += 1;
|
|
80774
|
-
const next = new URL(location, target);
|
|
80775
|
-
target = next;
|
|
80776
|
-
if (!redirectKeepsBody(response.status)) {
|
|
80777
|
-
method2 = "GET";
|
|
80778
|
-
requestBody = undefined;
|
|
80779
|
-
requestHeaders = Object.fromEntries(Object.entries(requestHeaders).filter(([name]) => name.toLowerCase() !== "content-type" && name.toLowerCase() !== "content-length"));
|
|
80780
|
-
}
|
|
80781
|
-
continue;
|
|
80782
|
-
}
|
|
80783
|
-
const responseBody = truncate2(await response.text());
|
|
80784
|
-
return {
|
|
80785
|
-
attempt: 1,
|
|
80786
|
-
status: response.ok ? "success" : "failed",
|
|
80787
|
-
startedAt,
|
|
80788
|
-
completedAt: now2(),
|
|
80789
|
-
responseStatus: response.status,
|
|
80790
|
-
responseBody,
|
|
80791
|
-
error: response.ok ? undefined : `Webhook returned HTTP ${response.status}`
|
|
80792
|
-
};
|
|
80793
|
-
}
|
|
80794
|
-
} catch (error) {
|
|
80795
|
-
return {
|
|
80796
|
-
attempt: 1,
|
|
80797
|
-
status: "failed",
|
|
80798
|
-
startedAt,
|
|
80799
|
-
completedAt: now2(),
|
|
80800
|
-
error: error instanceof Error ? error.message : String(error)
|
|
80801
|
-
};
|
|
80802
|
-
} finally {
|
|
80803
|
-
clearTimeout(timeout);
|
|
80804
|
-
}
|
|
80805
|
-
}
|
|
80806
80589
|
function failedAttempt(startedAt, error) {
|
|
80807
80590
|
return {
|
|
80808
80591
|
attempt: 1,
|
|
@@ -81012,9 +80795,7 @@ class EventsClient {
|
|
|
81012
80795
|
this.transportOptions = {
|
|
81013
80796
|
fetchImpl: options.fetchImpl,
|
|
81014
80797
|
secretResolver: options.secretResolver,
|
|
81015
|
-
now: options.now
|
|
81016
|
-
tls: options.tls,
|
|
81017
|
-
webhookTargetPolicy: options.webhookTargetPolicy
|
|
80798
|
+
now: options.now
|
|
81018
80799
|
};
|
|
81019
80800
|
this.catalog = options.catalog ?? defaultEventTypeCatalog;
|
|
81020
80801
|
this.validateCatalogTypes = options.validateCatalogTypes ?? false;
|