@hasna/skills 0.1.31 → 0.1.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -15
- package/bin/index.js +524 -272
- package/bin/mcp.js +49 -30
- package/dist/cli/onboarding.d.ts +13 -0
- package/dist/index.js +39 -23
- package/dist/lib/config.d.ts +1 -1
- package/package.json +1 -1
package/bin/index.js
CHANGED
|
@@ -1910,7 +1910,7 @@ var package_default;
|
|
|
1910
1910
|
var init_package = __esm(() => {
|
|
1911
1911
|
package_default = {
|
|
1912
1912
|
name: "@hasna/skills",
|
|
1913
|
-
version: "0.1.
|
|
1913
|
+
version: "0.1.32",
|
|
1914
1914
|
description: "Skills library for AI coding agents",
|
|
1915
1915
|
type: "module",
|
|
1916
1916
|
bin: {
|
|
@@ -6863,6 +6863,130 @@ var init_discovery = __esm(() => {
|
|
|
6863
6863
|
vendorPattern = new RegExp(`\\b(${VENDOR_TERMS.map(escapeRegExp).join("|")})\\b`, "gi");
|
|
6864
6864
|
});
|
|
6865
6865
|
|
|
6866
|
+
// src/lib/config.ts
|
|
6867
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, copyFileSync } from "fs";
|
|
6868
|
+
import { join as join4, dirname as dirname2 } from "path";
|
|
6869
|
+
import { homedir as homedir3 } from "os";
|
|
6870
|
+
function validKeys() {
|
|
6871
|
+
return ["mode", ...Object.keys(ENUM_KEYS), ...STRING_KEYS];
|
|
6872
|
+
}
|
|
6873
|
+
function allowedValues(key) {
|
|
6874
|
+
if (key === "mode")
|
|
6875
|
+
return MODE_VALUES;
|
|
6876
|
+
return ENUM_KEYS[key];
|
|
6877
|
+
}
|
|
6878
|
+
function normalizeConfigValue(key, value) {
|
|
6879
|
+
if (typeof value !== "string")
|
|
6880
|
+
return;
|
|
6881
|
+
if (key === "mode")
|
|
6882
|
+
return MODE_ALIASES[value.trim().toLowerCase()];
|
|
6883
|
+
const allowed = allowedValues(key);
|
|
6884
|
+
if (allowed)
|
|
6885
|
+
return allowed.includes(value) ? value : undefined;
|
|
6886
|
+
if (key === "apiUrl") {
|
|
6887
|
+
try {
|
|
6888
|
+
const url = new URL(value);
|
|
6889
|
+
if (url.protocol !== "http:" && url.protocol !== "https:")
|
|
6890
|
+
return;
|
|
6891
|
+
return value.replace(/\/+$/, "");
|
|
6892
|
+
} catch {
|
|
6893
|
+
return;
|
|
6894
|
+
}
|
|
6895
|
+
}
|
|
6896
|
+
return;
|
|
6897
|
+
}
|
|
6898
|
+
function getDataDir() {
|
|
6899
|
+
const home = process.env["HOME"] || process.env["USERPROFILE"] || homedir3();
|
|
6900
|
+
const newDir = join4(home, ".hasna", "skills");
|
|
6901
|
+
const oldConfigFile = join4(home, ".skillsrc");
|
|
6902
|
+
if (existsSync4(oldConfigFile) && !existsSync4(join4(newDir, "config.json"))) {
|
|
6903
|
+
mkdirSync2(newDir, { recursive: true });
|
|
6904
|
+
try {
|
|
6905
|
+
copyFileSync(oldConfigFile, join4(newDir, "config.json"));
|
|
6906
|
+
} catch {}
|
|
6907
|
+
}
|
|
6908
|
+
mkdirSync2(newDir, { recursive: true });
|
|
6909
|
+
return newDir;
|
|
6910
|
+
}
|
|
6911
|
+
function getConfigPath(scope) {
|
|
6912
|
+
if (scope === "global") {
|
|
6913
|
+
return join4(getDataDir(), "config.json");
|
|
6914
|
+
}
|
|
6915
|
+
return join4(process.cwd(), "skills.config.json");
|
|
6916
|
+
}
|
|
6917
|
+
function readConfigFile(path) {
|
|
6918
|
+
if (!existsSync4(path))
|
|
6919
|
+
return {};
|
|
6920
|
+
try {
|
|
6921
|
+
const raw = readFileSync4(path, "utf-8");
|
|
6922
|
+
const parsed = JSON.parse(raw);
|
|
6923
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
|
|
6924
|
+
return {};
|
|
6925
|
+
const config = {};
|
|
6926
|
+
for (const key of validKeys()) {
|
|
6927
|
+
const value = normalizeConfigValue(key, parsed[key]);
|
|
6928
|
+
if (value !== undefined)
|
|
6929
|
+
config[key] = value;
|
|
6930
|
+
}
|
|
6931
|
+
return config;
|
|
6932
|
+
} catch {
|
|
6933
|
+
return {};
|
|
6934
|
+
}
|
|
6935
|
+
}
|
|
6936
|
+
function loadConfig() {
|
|
6937
|
+
const globalConfig = readConfigFile(getConfigPath("global"));
|
|
6938
|
+
const projectConfig = readConfigFile(getConfigPath("project"));
|
|
6939
|
+
return { ...globalConfig, ...projectConfig };
|
|
6940
|
+
}
|
|
6941
|
+
function saveConfig(key, value, scope = "project") {
|
|
6942
|
+
if (!validKeys().includes(key)) {
|
|
6943
|
+
throw new Error(`Unknown config key: ${key}. Valid keys: ${validKeys().join(", ")}`);
|
|
6944
|
+
}
|
|
6945
|
+
const normalized = normalizeConfigValue(key, value);
|
|
6946
|
+
if (normalized === undefined) {
|
|
6947
|
+
const allowed = allowedValues(key);
|
|
6948
|
+
throw new Error(allowed ? `Invalid value '${value}' for ${key}. Allowed: ${allowed.join(", ")}` : `Invalid value '${value}' for ${key}. Expected an http(s) URL`);
|
|
6949
|
+
}
|
|
6950
|
+
const filePath = getConfigPath(scope);
|
|
6951
|
+
let existing = {};
|
|
6952
|
+
if (existsSync4(filePath)) {
|
|
6953
|
+
try {
|
|
6954
|
+
existing = JSON.parse(readFileSync4(filePath, "utf-8"));
|
|
6955
|
+
if (typeof existing !== "object" || existing === null || Array.isArray(existing)) {
|
|
6956
|
+
existing = {};
|
|
6957
|
+
}
|
|
6958
|
+
} catch {
|
|
6959
|
+
existing = {};
|
|
6960
|
+
}
|
|
6961
|
+
} else {
|
|
6962
|
+
const dir = dirname2(filePath);
|
|
6963
|
+
if (!existsSync4(dir)) {
|
|
6964
|
+
mkdirSync2(dir, { recursive: true });
|
|
6965
|
+
}
|
|
6966
|
+
}
|
|
6967
|
+
existing[key] = normalized;
|
|
6968
|
+
writeFileSync2(filePath, JSON.stringify(existing, null, 2) + `
|
|
6969
|
+
`);
|
|
6970
|
+
}
|
|
6971
|
+
var ENUM_KEYS, STRING_KEYS, MODE_VALUES, MODE_ALIASES;
|
|
6972
|
+
var init_config = __esm(() => {
|
|
6973
|
+
ENUM_KEYS = {
|
|
6974
|
+
defaultAgent: ["claude", "codex", "gemini", "pi", "opencode", "all"],
|
|
6975
|
+
defaultScope: ["global", "project"],
|
|
6976
|
+
format: ["compact", "json", "csv"]
|
|
6977
|
+
};
|
|
6978
|
+
STRING_KEYS = ["apiUrl"];
|
|
6979
|
+
MODE_VALUES = ["local", "hosted"];
|
|
6980
|
+
MODE_ALIASES = {
|
|
6981
|
+
local: "local",
|
|
6982
|
+
offline: "local",
|
|
6983
|
+
hosted: "hosted",
|
|
6984
|
+
remote: "hosted",
|
|
6985
|
+
"skills.md": "hosted",
|
|
6986
|
+
skillsmd: "hosted"
|
|
6987
|
+
};
|
|
6988
|
+
});
|
|
6989
|
+
|
|
6866
6990
|
// node_modules/zod/v4/core/core.js
|
|
6867
6991
|
function $constructor(name, initializer, params) {
|
|
6868
6992
|
function init(inst, def) {
|
|
@@ -20729,115 +20853,6 @@ var init_zod = __esm(() => {
|
|
|
20729
20853
|
init_external();
|
|
20730
20854
|
});
|
|
20731
20855
|
|
|
20732
|
-
// src/lib/config.ts
|
|
20733
|
-
import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, copyFileSync } from "fs";
|
|
20734
|
-
import { join as join4, dirname as dirname2 } from "path";
|
|
20735
|
-
import { homedir as homedir3 } from "os";
|
|
20736
|
-
function validKeys() {
|
|
20737
|
-
return [...Object.keys(ENUM_KEYS), ...STRING_KEYS];
|
|
20738
|
-
}
|
|
20739
|
-
function normalizeConfigValue(key, value) {
|
|
20740
|
-
if (typeof value !== "string")
|
|
20741
|
-
return;
|
|
20742
|
-
const allowed = ENUM_KEYS[key];
|
|
20743
|
-
if (allowed)
|
|
20744
|
-
return allowed.includes(value) ? value : undefined;
|
|
20745
|
-
if (key === "apiUrl") {
|
|
20746
|
-
try {
|
|
20747
|
-
const url2 = new URL(value);
|
|
20748
|
-
if (url2.protocol !== "http:" && url2.protocol !== "https:")
|
|
20749
|
-
return;
|
|
20750
|
-
return value.replace(/\/+$/, "");
|
|
20751
|
-
} catch {
|
|
20752
|
-
return;
|
|
20753
|
-
}
|
|
20754
|
-
}
|
|
20755
|
-
return;
|
|
20756
|
-
}
|
|
20757
|
-
function getDataDir() {
|
|
20758
|
-
const home = process.env["HOME"] || process.env["USERPROFILE"] || homedir3();
|
|
20759
|
-
const newDir = join4(home, ".hasna", "skills");
|
|
20760
|
-
const oldConfigFile = join4(home, ".skillsrc");
|
|
20761
|
-
if (existsSync4(oldConfigFile) && !existsSync4(join4(newDir, "config.json"))) {
|
|
20762
|
-
mkdirSync2(newDir, { recursive: true });
|
|
20763
|
-
try {
|
|
20764
|
-
copyFileSync(oldConfigFile, join4(newDir, "config.json"));
|
|
20765
|
-
} catch {}
|
|
20766
|
-
}
|
|
20767
|
-
mkdirSync2(newDir, { recursive: true });
|
|
20768
|
-
return newDir;
|
|
20769
|
-
}
|
|
20770
|
-
function getConfigPath(scope) {
|
|
20771
|
-
if (scope === "global") {
|
|
20772
|
-
return join4(getDataDir(), "config.json");
|
|
20773
|
-
}
|
|
20774
|
-
return join4(process.cwd(), "skills.config.json");
|
|
20775
|
-
}
|
|
20776
|
-
function readConfigFile(path) {
|
|
20777
|
-
if (!existsSync4(path))
|
|
20778
|
-
return {};
|
|
20779
|
-
try {
|
|
20780
|
-
const raw = readFileSync4(path, "utf-8");
|
|
20781
|
-
const parsed = JSON.parse(raw);
|
|
20782
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
|
|
20783
|
-
return {};
|
|
20784
|
-
const config2 = {};
|
|
20785
|
-
for (const key of validKeys()) {
|
|
20786
|
-
const value = normalizeConfigValue(key, parsed[key]);
|
|
20787
|
-
if (value !== undefined)
|
|
20788
|
-
config2[key] = value;
|
|
20789
|
-
}
|
|
20790
|
-
return config2;
|
|
20791
|
-
} catch {
|
|
20792
|
-
return {};
|
|
20793
|
-
}
|
|
20794
|
-
}
|
|
20795
|
-
function loadConfig() {
|
|
20796
|
-
const globalConfig2 = readConfigFile(getConfigPath("global"));
|
|
20797
|
-
const projectConfig = readConfigFile(getConfigPath("project"));
|
|
20798
|
-
return { ...globalConfig2, ...projectConfig };
|
|
20799
|
-
}
|
|
20800
|
-
function saveConfig(key, value, scope = "project") {
|
|
20801
|
-
if (!validKeys().includes(key)) {
|
|
20802
|
-
throw new Error(`Unknown config key: ${key}. Valid keys: ${validKeys().join(", ")}`);
|
|
20803
|
-
}
|
|
20804
|
-
const normalized = normalizeConfigValue(key, value);
|
|
20805
|
-
if (normalized === undefined) {
|
|
20806
|
-
const allowed = ENUM_KEYS[key];
|
|
20807
|
-
throw new Error(allowed ? `Invalid value '${value}' for ${key}. Allowed: ${allowed.join(", ")}` : `Invalid value '${value}' for ${key}. Expected an http(s) URL`);
|
|
20808
|
-
}
|
|
20809
|
-
const filePath = getConfigPath(scope);
|
|
20810
|
-
let existing = {};
|
|
20811
|
-
if (existsSync4(filePath)) {
|
|
20812
|
-
try {
|
|
20813
|
-
existing = JSON.parse(readFileSync4(filePath, "utf-8"));
|
|
20814
|
-
if (typeof existing !== "object" || existing === null || Array.isArray(existing)) {
|
|
20815
|
-
existing = {};
|
|
20816
|
-
}
|
|
20817
|
-
} catch {
|
|
20818
|
-
existing = {};
|
|
20819
|
-
}
|
|
20820
|
-
} else {
|
|
20821
|
-
const dir = dirname2(filePath);
|
|
20822
|
-
if (!existsSync4(dir)) {
|
|
20823
|
-
mkdirSync2(dir, { recursive: true });
|
|
20824
|
-
}
|
|
20825
|
-
}
|
|
20826
|
-
existing[key] = normalized;
|
|
20827
|
-
writeFileSync2(filePath, JSON.stringify(existing, null, 2) + `
|
|
20828
|
-
`);
|
|
20829
|
-
}
|
|
20830
|
-
var ENUM_KEYS, STRING_KEYS;
|
|
20831
|
-
var init_config = __esm(() => {
|
|
20832
|
-
ENUM_KEYS = {
|
|
20833
|
-
mode: ["local", "skills.md"],
|
|
20834
|
-
defaultAgent: ["claude", "codex", "gemini", "pi", "opencode", "all"],
|
|
20835
|
-
defaultScope: ["global", "project"],
|
|
20836
|
-
format: ["compact", "json", "csv"]
|
|
20837
|
-
};
|
|
20838
|
-
STRING_KEYS = ["apiUrl"];
|
|
20839
|
-
});
|
|
20840
|
-
|
|
20841
20856
|
// src/lib/auth-store.ts
|
|
20842
20857
|
var exports_auth_store = {};
|
|
20843
20858
|
__export(exports_auth_store, {
|
|
@@ -20848,14 +20863,14 @@ __export(exports_auth_store, {
|
|
|
20848
20863
|
getApiKey: () => getApiKey,
|
|
20849
20864
|
clearAuthConfig: () => clearAuthConfig
|
|
20850
20865
|
});
|
|
20851
|
-
import { mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3, unlinkSync } from "fs";
|
|
20866
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3, unlinkSync } from "fs";
|
|
20852
20867
|
import { join as join5 } from "path";
|
|
20853
20868
|
import { homedir as homedir4 } from "os";
|
|
20854
20869
|
function getAuthConfig() {
|
|
20855
20870
|
if (cachedConfig !== undefined)
|
|
20856
20871
|
return cachedConfig;
|
|
20857
20872
|
try {
|
|
20858
|
-
const raw = readFileSync5(AUTH_FILE, "utf-8");
|
|
20873
|
+
const raw = readFileSync5(existsSync5(AUTH_FILE) ? AUTH_FILE : LEGACY_AUTH_FILE, "utf-8");
|
|
20859
20874
|
const config2 = JSON.parse(raw);
|
|
20860
20875
|
if (!config2.apiKey || !config2.email) {
|
|
20861
20876
|
cachedConfig = null;
|
|
@@ -20869,7 +20884,7 @@ function getAuthConfig() {
|
|
|
20869
20884
|
}
|
|
20870
20885
|
}
|
|
20871
20886
|
function saveAuthConfig(config2) {
|
|
20872
|
-
mkdirSync3(AUTH_DIR, { recursive: true });
|
|
20887
|
+
mkdirSync3(AUTH_DIR, { recursive: true, mode: 448 });
|
|
20873
20888
|
writeFileSync3(AUTH_FILE, JSON.stringify(config2, null, 2) + `
|
|
20874
20889
|
`, { mode: 384 });
|
|
20875
20890
|
cachedConfig = config2;
|
|
@@ -20878,6 +20893,9 @@ function clearAuthConfig() {
|
|
|
20878
20893
|
try {
|
|
20879
20894
|
unlinkSync(AUTH_FILE);
|
|
20880
20895
|
} catch {}
|
|
20896
|
+
try {
|
|
20897
|
+
unlinkSync(LEGACY_AUTH_FILE);
|
|
20898
|
+
} catch {}
|
|
20881
20899
|
cachedConfig = null;
|
|
20882
20900
|
}
|
|
20883
20901
|
function getApiKey() {
|
|
@@ -20900,11 +20918,12 @@ function normalizeSkillsApiOrigin(apiUrl) {
|
|
|
20900
20918
|
function getApiUrl() {
|
|
20901
20919
|
return normalizeSkillsApiOrigin(process.env.SKILLS_API_URL || loadConfig().apiUrl || "https://skills.md");
|
|
20902
20920
|
}
|
|
20903
|
-
var AUTH_DIR, AUTH_FILE, cachedConfig;
|
|
20921
|
+
var AUTH_DIR, AUTH_FILE, LEGACY_AUTH_FILE, cachedConfig;
|
|
20904
20922
|
var init_auth_store = __esm(() => {
|
|
20905
20923
|
init_config();
|
|
20906
|
-
AUTH_DIR = join5(homedir4(), ".skills");
|
|
20924
|
+
AUTH_DIR = join5(homedir4(), ".hasna", "skills");
|
|
20907
20925
|
AUTH_FILE = join5(AUTH_DIR, "auth.json");
|
|
20926
|
+
LEGACY_AUTH_FILE = join5(homedir4(), ".skills", "auth.json");
|
|
20908
20927
|
});
|
|
20909
20928
|
|
|
20910
20929
|
// src/lib/remote-registry.ts
|
|
@@ -21543,11 +21562,11 @@ __export(exports_skillinfo, {
|
|
|
21543
21562
|
generateEnvExample: () => generateEnvExample,
|
|
21544
21563
|
detectProjectSkills: () => detectProjectSkills
|
|
21545
21564
|
});
|
|
21546
|
-
import { existsSync as
|
|
21565
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
|
|
21547
21566
|
import { join as join6 } from "path";
|
|
21548
21567
|
function getSkillDocs(name) {
|
|
21549
21568
|
const skillPath = getSkillPath(name);
|
|
21550
|
-
if (!
|
|
21569
|
+
if (!existsSync6(skillPath))
|
|
21551
21570
|
return null;
|
|
21552
21571
|
return {
|
|
21553
21572
|
skillMd: readIfExists(join6(skillPath, "SKILL.md")),
|
|
@@ -21563,7 +21582,7 @@ function getSkillBestDoc(name) {
|
|
|
21563
21582
|
}
|
|
21564
21583
|
function getSkillRequirements(name) {
|
|
21565
21584
|
const skillPath = getSkillPath(name);
|
|
21566
|
-
if (!
|
|
21585
|
+
if (!existsSync6(skillPath))
|
|
21567
21586
|
return null;
|
|
21568
21587
|
const texts = [];
|
|
21569
21588
|
for (const file2 of ["SKILL.md", "README.md", "CLAUDE.md", ".env.example", ".env.local.example"]) {
|
|
@@ -21606,7 +21625,7 @@ function getSkillRequirements(name) {
|
|
|
21606
21625
|
let cliCommand = `skills run ${skillName}`;
|
|
21607
21626
|
let dependencies = {};
|
|
21608
21627
|
const pkgPath = join6(skillPath, "package.json");
|
|
21609
|
-
if (
|
|
21628
|
+
if (existsSync6(pkgPath)) {
|
|
21610
21629
|
try {
|
|
21611
21630
|
const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
|
|
21612
21631
|
dependencies = pkg.dependencies || {};
|
|
@@ -21625,11 +21644,11 @@ function isHostedPremiumSkill(skillName, meta3) {
|
|
|
21625
21644
|
async function runSkill(name, args, options = {}) {
|
|
21626
21645
|
const canonicalName = getSkill(name)?.name ?? name;
|
|
21627
21646
|
const skillPath = getSkillPath(canonicalName);
|
|
21628
|
-
if (!
|
|
21647
|
+
if (!existsSync6(skillPath)) {
|
|
21629
21648
|
return { exitCode: 1, error: `Skill '${name}' not found` };
|
|
21630
21649
|
}
|
|
21631
21650
|
const pkgPath = join6(skillPath, "package.json");
|
|
21632
|
-
if (!
|
|
21651
|
+
if (!existsSync6(pkgPath)) {
|
|
21633
21652
|
return { exitCode: 1, error: `No package.json in skill '${name}'` };
|
|
21634
21653
|
}
|
|
21635
21654
|
let entryPoint;
|
|
@@ -21649,11 +21668,11 @@ async function runSkill(name, args, options = {}) {
|
|
|
21649
21668
|
return { exitCode: 1, error: `Failed to parse package.json for skill '${name}'` };
|
|
21650
21669
|
}
|
|
21651
21670
|
const entryPath = join6(skillPath, entryPoint);
|
|
21652
|
-
if (!
|
|
21671
|
+
if (!existsSync6(entryPath)) {
|
|
21653
21672
|
return { exitCode: 1, error: `Entry point '${entryPoint}' not found in skill '${name}'` };
|
|
21654
21673
|
}
|
|
21655
21674
|
const nodeModules = join6(skillPath, "node_modules");
|
|
21656
|
-
if (!
|
|
21675
|
+
if (!existsSync6(nodeModules)) {
|
|
21657
21676
|
const install = Bun.spawn(["bun", "install", "--no-save"], {
|
|
21658
21677
|
cwd: skillPath,
|
|
21659
21678
|
stdout: "pipe",
|
|
@@ -21681,7 +21700,7 @@ async function runSkill(name, args, options = {}) {
|
|
|
21681
21700
|
}
|
|
21682
21701
|
function detectProjectSkills(cwd = process.cwd()) {
|
|
21683
21702
|
const pkgPath = join6(cwd, "package.json");
|
|
21684
|
-
if (!
|
|
21703
|
+
if (!existsSync6(pkgPath)) {
|
|
21685
21704
|
const alwaysRecommend = ["implementation-plan", "write", "deepresearch"];
|
|
21686
21705
|
const recommended2 = alwaysRecommend.map((name) => loadRegistry().find((s) => s.name === name)).filter((s) => s !== undefined);
|
|
21687
21706
|
return { detected: [], recommended: recommended2 };
|
|
@@ -21812,7 +21831,7 @@ function generateSkillMd(name) {
|
|
|
21812
21831
|
if (!meta3)
|
|
21813
21832
|
return null;
|
|
21814
21833
|
const skillPath = getSkillPath(name);
|
|
21815
|
-
if (!
|
|
21834
|
+
if (!existsSync6(skillPath))
|
|
21816
21835
|
return null;
|
|
21817
21836
|
const frontmatter = [
|
|
21818
21837
|
"---",
|
|
@@ -21825,7 +21844,7 @@ function generateSkillMd(name) {
|
|
|
21825
21844
|
const claudeMd = readIfExists(join6(skillPath, "CLAUDE.md"));
|
|
21826
21845
|
let cliCommand = null;
|
|
21827
21846
|
const pkgPath = join6(skillPath, "package.json");
|
|
21828
|
-
if (
|
|
21847
|
+
if (existsSync6(pkgPath)) {
|
|
21829
21848
|
try {
|
|
21830
21849
|
const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
|
|
21831
21850
|
if (pkg.bin) {
|
|
@@ -21900,7 +21919,7 @@ function extractEnvVars(text) {
|
|
|
21900
21919
|
}
|
|
21901
21920
|
function readIfExists(path) {
|
|
21902
21921
|
try {
|
|
21903
|
-
if (
|
|
21922
|
+
if (existsSync6(path)) {
|
|
21904
21923
|
return readFileSync6(path, "utf-8");
|
|
21905
21924
|
}
|
|
21906
21925
|
} catch {}
|
|
@@ -21932,7 +21951,7 @@ var init_skillinfo = __esm(() => {
|
|
|
21932
21951
|
});
|
|
21933
21952
|
|
|
21934
21953
|
// src/lib/skill-validation.ts
|
|
21935
|
-
import { existsSync as
|
|
21954
|
+
import { existsSync as existsSync7, lstatSync, readFileSync as readFileSync7, readdirSync as readdirSync2, statSync } from "fs";
|
|
21936
21955
|
import { isAbsolute, join as join7, normalize } from "path";
|
|
21937
21956
|
function add(target, code, message) {
|
|
21938
21957
|
target.push({ code, message });
|
|
@@ -22005,7 +22024,7 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
22005
22024
|
binCommands: [],
|
|
22006
22025
|
docFiles: []
|
|
22007
22026
|
};
|
|
22008
|
-
if (!
|
|
22027
|
+
if (!existsSync7(skillPath)) {
|
|
22009
22028
|
add(issues, "skill.dir_missing", `Skill directory not found: ${skillPath}`);
|
|
22010
22029
|
return {
|
|
22011
22030
|
name: bareName,
|
|
@@ -22032,14 +22051,14 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
22032
22051
|
}
|
|
22033
22052
|
}
|
|
22034
22053
|
for (const docFile of DOC_FILES) {
|
|
22035
|
-
if (
|
|
22054
|
+
if (existsSync7(join7(skillPath, docFile)))
|
|
22036
22055
|
metadata.docFiles.push(docFile);
|
|
22037
22056
|
}
|
|
22038
22057
|
if (metadata.docFiles.length === 0) {
|
|
22039
22058
|
add(issues, "skill.docs_missing", "Missing documentation file: expected SKILL.md, README.md, or CLAUDE.md");
|
|
22040
22059
|
}
|
|
22041
22060
|
const skillMdPath = join7(skillPath, "SKILL.md");
|
|
22042
|
-
if (
|
|
22061
|
+
if (existsSync7(skillMdPath)) {
|
|
22043
22062
|
const frontmatter = parseSkillFrontmatter(readFileSync7(skillMdPath, "utf-8"));
|
|
22044
22063
|
if (!frontmatter) {
|
|
22045
22064
|
add(warnings, "skill.frontmatter_missing", "SKILL.md has no YAML frontmatter");
|
|
@@ -22074,7 +22093,7 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
22074
22093
|
add(warnings, "skill.skill_md_missing", "Missing SKILL.md; registry docs may need generated agent-facing instructions");
|
|
22075
22094
|
}
|
|
22076
22095
|
const pkgPath = join7(skillPath, "package.json");
|
|
22077
|
-
if (!
|
|
22096
|
+
if (!existsSync7(pkgPath)) {
|
|
22078
22097
|
add(issues, "package.missing", "Missing package.json");
|
|
22079
22098
|
} else {
|
|
22080
22099
|
try {
|
|
@@ -22119,7 +22138,7 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
22119
22138
|
continue;
|
|
22120
22139
|
}
|
|
22121
22140
|
const targetPath = join7(skillPath, target);
|
|
22122
|
-
if (!
|
|
22141
|
+
if (!existsSync7(targetPath)) {
|
|
22123
22142
|
add(warnings, "package.bin_target_missing", `package.json bin '${command}' target '${target}' is not present before build`);
|
|
22124
22143
|
} else if (statSync(targetPath).isDirectory()) {
|
|
22125
22144
|
add(issues, "package.bin_target_directory", `package.json bin '${command}' target '${target}' must point to a file, not a directory`);
|
|
@@ -22132,12 +22151,12 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
22132
22151
|
}
|
|
22133
22152
|
}
|
|
22134
22153
|
const srcDir = join7(skillPath, "src");
|
|
22135
|
-
if (!
|
|
22154
|
+
if (!existsSync7(srcDir)) {
|
|
22136
22155
|
add(issues, "skill.src_missing", "Missing src/ directory");
|
|
22137
|
-
} else if (!
|
|
22156
|
+
} else if (!existsSync7(join7(srcDir, "index.ts")) && !existsSync7(join7(srcDir, "index.js"))) {
|
|
22138
22157
|
add(issues, "skill.src_index_missing", "Missing src/index.ts or src/index.js");
|
|
22139
22158
|
} else {
|
|
22140
|
-
const indexPath =
|
|
22159
|
+
const indexPath = existsSync7(join7(srcDir, "index.ts")) ? join7(srcDir, "index.ts") : join7(srcDir, "index.js");
|
|
22141
22160
|
const size = statSync(indexPath).size;
|
|
22142
22161
|
if (size < 50)
|
|
22143
22162
|
add(warnings, "skill.src_index_minimal", `Source entry point is very small (${size}B)`);
|
|
@@ -22205,7 +22224,7 @@ __export(exports_introspect, {
|
|
|
22205
22224
|
registerIntrospect: () => registerIntrospect
|
|
22206
22225
|
});
|
|
22207
22226
|
import chalk4 from "chalk";
|
|
22208
|
-
import { existsSync as
|
|
22227
|
+
import { existsSync as existsSync8, readFileSync as readFileSync8 } from "fs";
|
|
22209
22228
|
import { join as join8 } from "path";
|
|
22210
22229
|
import { execSync } from "child_process";
|
|
22211
22230
|
function registerIntrospect(parent) {
|
|
@@ -22417,7 +22436,7 @@ function handleValidate(name, options) {
|
|
|
22417
22436
|
function handleDiff(name, options) {
|
|
22418
22437
|
const bare = name;
|
|
22419
22438
|
const sourcePath = getSkillPath(bare);
|
|
22420
|
-
if (!
|
|
22439
|
+
if (!existsSync8(sourcePath)) {
|
|
22421
22440
|
if (options.json)
|
|
22422
22441
|
console.log(JSON.stringify({ error: `Skill '${bare}' not found in registry` }));
|
|
22423
22442
|
else
|
|
@@ -22436,7 +22455,7 @@ function handleDiff(name, options) {
|
|
|
22436
22455
|
const installedVersion = installMeta.skills[bare]?.version ?? "unknown";
|
|
22437
22456
|
const registryPkgPath = join8(sourcePath, "package.json");
|
|
22438
22457
|
let registryVersion = "unknown";
|
|
22439
|
-
if (
|
|
22458
|
+
if (existsSync8(registryPkgPath)) {
|
|
22440
22459
|
try {
|
|
22441
22460
|
registryVersion = JSON.parse(readFileSync8(registryPkgPath, "utf-8")).version || "unknown";
|
|
22442
22461
|
} catch {}
|
|
@@ -22469,7 +22488,7 @@ __export(exports_init, {
|
|
|
22469
22488
|
registerSetup: () => registerSetup
|
|
22470
22489
|
});
|
|
22471
22490
|
import chalk5 from "chalk";
|
|
22472
|
-
import { existsSync as
|
|
22491
|
+
import { existsSync as existsSync9, readFileSync as readFileSync9, writeFileSync as writeFileSync4, appendFileSync } from "fs";
|
|
22473
22492
|
import { join as join9 } from "path";
|
|
22474
22493
|
function registerSetup(parent) {
|
|
22475
22494
|
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));
|
|
@@ -22564,7 +22583,7 @@ Use: skills mcp --register ${options.for}`));
|
|
|
22564
22583
|
console.log(chalk5.dim(" No environment variables detected across pinned skills"));
|
|
22565
22584
|
const gitignorePath = join9(cwd, ".gitignore");
|
|
22566
22585
|
const gitignoreEntries = [".skills/runs/", ".skills/exports/", ".skills/tmp/"];
|
|
22567
|
-
let gitignoreContent =
|
|
22586
|
+
let gitignoreContent = existsSync9(gitignorePath) ? readFileSync9(gitignorePath, "utf-8") : "";
|
|
22568
22587
|
let gitignoreUpdated = false;
|
|
22569
22588
|
const missingEntries = gitignoreEntries.filter((entry) => !gitignoreContent.includes(entry));
|
|
22570
22589
|
if (missingEntries.length > 0) {
|
|
@@ -22610,7 +22629,7 @@ async function handleImport(file2, options) {
|
|
|
22610
22629
|
if (file2 === "-")
|
|
22611
22630
|
raw = await new Response(process.stdin).text();
|
|
22612
22631
|
else {
|
|
22613
|
-
if (!
|
|
22632
|
+
if (!existsSync9(file2)) {
|
|
22614
22633
|
const error48 = `File not found: ${file2}`;
|
|
22615
22634
|
if (options.json)
|
|
22616
22635
|
console.log(JSON.stringify({ imported: 0, error: error48 }));
|
|
@@ -22720,7 +22739,7 @@ __export(exports_diagnostic, {
|
|
|
22720
22739
|
registerDiagnostic: () => registerDiagnostic
|
|
22721
22740
|
});
|
|
22722
22741
|
import chalk6 from "chalk";
|
|
22723
|
-
import { existsSync as
|
|
22742
|
+
import { existsSync as existsSync10, readFileSync as readFileSync10, readdirSync as readdirSync3, statSync as statSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
22724
22743
|
import { join as join10 } from "path";
|
|
22725
22744
|
import { execSync as execSync2 } from "child_process";
|
|
22726
22745
|
function registerDiagnostic(parent) {
|
|
@@ -22856,7 +22875,7 @@ function handleAuth(name, options) {
|
|
|
22856
22875
|
process.exitCode = 1;
|
|
22857
22876
|
return;
|
|
22858
22877
|
}
|
|
22859
|
-
let existing =
|
|
22878
|
+
let existing = existsSync10(envFilePath) ? readFileSync10(envFilePath, "utf-8") : "";
|
|
22860
22879
|
const keyPattern = new RegExp(`^${key}=.*$`, "m");
|
|
22861
22880
|
const updated = keyPattern.test(existing) ? existing.replace(keyPattern, `${key}=${value}`) : existing.endsWith(`
|
|
22862
22881
|
`) || existing === "" ? existing + `${key}=${value}
|
|
@@ -22922,7 +22941,7 @@ function handleWhoami(options) {
|
|
|
22922
22941
|
const agentConfigs = [];
|
|
22923
22942
|
for (const agent of AGENT_TARGETS) {
|
|
22924
22943
|
const agentSkillsPath = getAgentSkillsDir(agent, "global");
|
|
22925
|
-
const exists =
|
|
22944
|
+
const exists = existsSync10(agentSkillsPath);
|
|
22926
22945
|
let skillCount = 0;
|
|
22927
22946
|
if (exists)
|
|
22928
22947
|
try {
|
|
@@ -22967,7 +22986,7 @@ function handleOutdated(options) {
|
|
|
22967
22986
|
const registryPath = getSkillPath(name);
|
|
22968
22987
|
const registryPkgPath = join10(registryPath, "package.json");
|
|
22969
22988
|
let registryVersion = "unknown";
|
|
22970
|
-
if (
|
|
22989
|
+
if (existsSync10(registryPkgPath))
|
|
22971
22990
|
try {
|
|
22972
22991
|
registryVersion = JSON.parse(readFileSync10(registryPkgPath, "utf-8")).version || "unknown";
|
|
22973
22992
|
} catch {}
|
|
@@ -23058,7 +23077,7 @@ var REMOTE_SKILL_RUN_CONTRACT_VERSION = 1;
|
|
|
23058
23077
|
|
|
23059
23078
|
// src/lib/run-state.ts
|
|
23060
23079
|
import { createHash, randomBytes } from "crypto";
|
|
23061
|
-
import { existsSync as
|
|
23080
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync4, readFileSync as readFileSync11, readdirSync as readdirSync4, statSync as statSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
23062
23081
|
import { extname, join as join11, relative } from "path";
|
|
23063
23082
|
function createSkillRun(params, targetDir = process.cwd()) {
|
|
23064
23083
|
const now = new Date;
|
|
@@ -23125,12 +23144,12 @@ function appendRunEvent(context, event, data = {}) {
|
|
|
23125
23144
|
const line = JSON.stringify({ ts: new Date().toISOString(), event, ...data }) + `
|
|
23126
23145
|
`;
|
|
23127
23146
|
const path = join11(context.runDir, "events.ndjson");
|
|
23128
|
-
const previous =
|
|
23147
|
+
const previous = existsSync11(path) ? readFileSync11(path, "utf-8") : "";
|
|
23129
23148
|
writeFileSync6(path, previous + line);
|
|
23130
23149
|
}
|
|
23131
23150
|
function listSkillRuns(targetDir = process.cwd(), limit = 50) {
|
|
23132
23151
|
const runsRoot = join11(getProjectStateDir(targetDir), "runs");
|
|
23133
|
-
if (!
|
|
23152
|
+
if (!existsSync11(runsRoot))
|
|
23134
23153
|
return [];
|
|
23135
23154
|
const records = [];
|
|
23136
23155
|
for (const day of readdirSync4(runsRoot).sort().reverse()) {
|
|
@@ -23149,7 +23168,7 @@ function listSkillRuns(targetDir = process.cwd(), limit = 50) {
|
|
|
23149
23168
|
}
|
|
23150
23169
|
function findSkillRun(runId, targetDir = process.cwd()) {
|
|
23151
23170
|
const runsRoot = join11(getProjectStateDir(targetDir), "runs");
|
|
23152
|
-
if (!
|
|
23171
|
+
if (!existsSync11(runsRoot))
|
|
23153
23172
|
return null;
|
|
23154
23173
|
for (const day of readdirSync4(runsRoot)) {
|
|
23155
23174
|
const record2 = readRunRecord(join11(runsRoot, day, runId));
|
|
@@ -23170,7 +23189,7 @@ function writeArtifactsManifest(context, artifacts) {
|
|
|
23170
23189
|
`);
|
|
23171
23190
|
}
|
|
23172
23191
|
function collectRunArtifacts(context) {
|
|
23173
|
-
if (!
|
|
23192
|
+
if (!existsSync11(context.exportDir))
|
|
23174
23193
|
return [];
|
|
23175
23194
|
const artifacts = [];
|
|
23176
23195
|
for (const path of walkFiles(context.exportDir)) {
|
|
@@ -23187,7 +23206,7 @@ function collectRunArtifacts(context) {
|
|
|
23187
23206
|
}
|
|
23188
23207
|
function readRunRecord(runDir) {
|
|
23189
23208
|
const path = join11(runDir, "run.json");
|
|
23190
|
-
if (!
|
|
23209
|
+
if (!existsSync11(path))
|
|
23191
23210
|
return null;
|
|
23192
23211
|
try {
|
|
23193
23212
|
return JSON.parse(readFileSync11(path, "utf-8"));
|
|
@@ -39276,7 +39295,7 @@ var init_remote_client = __esm(() => {
|
|
|
39276
39295
|
});
|
|
39277
39296
|
|
|
39278
39297
|
// src/mcp/operation-tools.ts
|
|
39279
|
-
import { existsSync as
|
|
39298
|
+
import { existsSync as existsSync12, readdirSync as readdirSync5, statSync as statSync4 } from "fs";
|
|
39280
39299
|
import { join as join12 } from "path";
|
|
39281
39300
|
function registerOperationTools(server) {
|
|
39282
39301
|
server.registerTool("pin_skill", {
|
|
@@ -39497,7 +39516,7 @@ function registerOperationTools(server) {
|
|
|
39497
39516
|
});
|
|
39498
39517
|
if (isPremiumSkill2(skillName) && !apiKey) {
|
|
39499
39518
|
const cost = formatCost2(costCents ?? 0);
|
|
39500
|
-
const error48 = `${skillName} is a hosted skill (${cost}). Run: skills setup --mode
|
|
39519
|
+
const error48 = `${skillName} is a hosted skill (${cost}). Run: skills setup --mode hosted && skills auth login`;
|
|
39501
39520
|
writeRunLogs(runContext, "", error48 + `
|
|
39502
39521
|
`);
|
|
39503
39522
|
const run = completeSkillRun(runContext, { status: "failed", error: error48, costCents });
|
|
@@ -39533,9 +39552,9 @@ function registerOperationTools(server) {
|
|
|
39533
39552
|
nextActions: remoteRunNextActions(remoteRunId)
|
|
39534
39553
|
});
|
|
39535
39554
|
} catch (err) {
|
|
39536
|
-
console.error(`[skills]
|
|
39555
|
+
console.error(`[skills] hosted API failed for ${skillName}, falling back to local:`, err.message);
|
|
39537
39556
|
if (isPremiumSkill2(skillName)) {
|
|
39538
|
-
const error48 = `Hosted skill ${skillName} requires
|
|
39557
|
+
const error48 = `Hosted skill ${skillName} requires hosted access: ${err.message}`;
|
|
39539
39558
|
writeRunLogs(runContext, "", error48 + `
|
|
39540
39559
|
`);
|
|
39541
39560
|
const localRun2 = completeSkillRun(runContext, { status: "failed", error: error48 });
|
|
@@ -39576,7 +39595,7 @@ function registerOperationTools(server) {
|
|
|
39576
39595
|
const { getApiKey: getApiKey2 } = await Promise.resolve().then(() => (init_auth_store(), exports_auth_store));
|
|
39577
39596
|
const apiKey = getApiKey2();
|
|
39578
39597
|
if (!apiKey) {
|
|
39579
|
-
return mcpError("AUTH_REQUIRED", "Remote run status requires
|
|
39598
|
+
return mcpError("AUTH_REQUIRED", "Remote run status requires hosted access. Run: skills auth login", ["skills auth login"]);
|
|
39580
39599
|
}
|
|
39581
39600
|
const localRun = findSkillRun(run_id);
|
|
39582
39601
|
const remoteRunId = localRun?.remoteRunId || run_id;
|
|
@@ -39662,7 +39681,7 @@ function registerOperationTools(server) {
|
|
|
39662
39681
|
const agents = [];
|
|
39663
39682
|
for (const agent of AGENT_TARGETS) {
|
|
39664
39683
|
const agentSkillsPath = getAgentSkillsDir(agent, "global");
|
|
39665
|
-
const exists =
|
|
39684
|
+
const exists = existsSync12(agentSkillsPath);
|
|
39666
39685
|
let skillCount = 0;
|
|
39667
39686
|
if (exists) {
|
|
39668
39687
|
try {
|
|
@@ -39697,7 +39716,7 @@ var init_operation_tools = __esm(() => {
|
|
|
39697
39716
|
});
|
|
39698
39717
|
|
|
39699
39718
|
// src/lib/feedback.ts
|
|
39700
|
-
import { existsSync as
|
|
39719
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync5 } from "fs";
|
|
39701
39720
|
import { homedir as homedir5 } from "os";
|
|
39702
39721
|
import { dirname as dirname3, join as join13 } from "path";
|
|
39703
39722
|
import { Database } from "bun:sqlite";
|
|
@@ -39707,7 +39726,7 @@ function getFeedbackDbPath() {
|
|
|
39707
39726
|
function getFeedbackDb() {
|
|
39708
39727
|
const dbPath = getFeedbackDbPath();
|
|
39709
39728
|
const dir = dirname3(dbPath);
|
|
39710
|
-
if (!
|
|
39729
|
+
if (!existsSync13(dir))
|
|
39711
39730
|
mkdirSync5(dir, { recursive: true });
|
|
39712
39731
|
const db = new Database(dbPath);
|
|
39713
39732
|
db.exec("PRAGMA journal_mode = WAL");
|
|
@@ -39868,14 +39887,14 @@ var init_resource_meta_tools = __esm(() => {
|
|
|
39868
39887
|
});
|
|
39869
39888
|
|
|
39870
39889
|
// src/lib/scheduler.ts
|
|
39871
|
-
import { existsSync as
|
|
39890
|
+
import { existsSync as existsSync14, readFileSync as readFileSync12, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6 } from "fs";
|
|
39872
39891
|
import { join as join14 } from "path";
|
|
39873
39892
|
function getSchedulesPath(targetDir = process.cwd()) {
|
|
39874
39893
|
return join14(targetDir, ".skills", "schedules.json");
|
|
39875
39894
|
}
|
|
39876
39895
|
function loadSchedules(targetDir = process.cwd()) {
|
|
39877
39896
|
const path = getSchedulesPath(targetDir);
|
|
39878
|
-
if (
|
|
39897
|
+
if (existsSync14(path)) {
|
|
39879
39898
|
try {
|
|
39880
39899
|
return JSON.parse(readFileSync12(path, "utf-8"));
|
|
39881
39900
|
} catch {}
|
|
@@ -39885,7 +39904,7 @@ function loadSchedules(targetDir = process.cwd()) {
|
|
|
39885
39904
|
function saveSchedules(data, targetDir = process.cwd()) {
|
|
39886
39905
|
const path = getSchedulesPath(targetDir);
|
|
39887
39906
|
const dir = join14(targetDir, ".skills");
|
|
39888
|
-
if (!
|
|
39907
|
+
if (!existsSync14(dir))
|
|
39889
39908
|
mkdirSync6(dir, { recursive: true });
|
|
39890
39909
|
writeFileSync7(path, JSON.stringify(data, null, 2));
|
|
39891
39910
|
}
|
|
@@ -40430,7 +40449,7 @@ var init_mcp2 = __esm(() => {
|
|
|
40430
40449
|
|
|
40431
40450
|
// src/cli/commands/runtime-mcp.ts
|
|
40432
40451
|
import chalk7 from "chalk";
|
|
40433
|
-
import { existsSync as
|
|
40452
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync7, readFileSync as readFileSync13, writeFileSync as writeFileSync8 } from "fs";
|
|
40434
40453
|
import { homedir as homedir6 } from "os";
|
|
40435
40454
|
import { dirname as dirname4, join as join15 } from "path";
|
|
40436
40455
|
async function handleMcp(options) {
|
|
@@ -40540,7 +40559,7 @@ function registerCodexMcp(command) {
|
|
|
40540
40559
|
const config2 = `[mcp_servers.${MCP_SERVER_NAME}]
|
|
40541
40560
|
command = ${JSON.stringify(command)}`;
|
|
40542
40561
|
try {
|
|
40543
|
-
const current =
|
|
40562
|
+
const current = existsSync15(path) ? readFileSync13(path, "utf-8") : "";
|
|
40544
40563
|
writeTextFile(path, upsertTomlSection(current, `[mcp_servers.${MCP_SERVER_NAME}]`, `command = ${JSON.stringify(command)}`));
|
|
40545
40564
|
return { agent: "codex", success: true, path, config: config2 };
|
|
40546
40565
|
} catch (err) {
|
|
@@ -40590,7 +40609,7 @@ function registerJsonMcpServer(agent, path, containerKey, server2) {
|
|
|
40590
40609
|
}
|
|
40591
40610
|
}
|
|
40592
40611
|
function readJsonObject(path) {
|
|
40593
|
-
if (!
|
|
40612
|
+
if (!existsSync15(path))
|
|
40594
40613
|
return {};
|
|
40595
40614
|
const raw = readFileSync13(path, "utf-8").trim();
|
|
40596
40615
|
if (!raw)
|
|
@@ -40637,7 +40656,7 @@ function findCommandOnPath(command) {
|
|
|
40637
40656
|
if (!dir)
|
|
40638
40657
|
continue;
|
|
40639
40658
|
const candidate = join15(dir, command);
|
|
40640
|
-
if (
|
|
40659
|
+
if (existsSync15(candidate))
|
|
40641
40660
|
return candidate;
|
|
40642
40661
|
}
|
|
40643
40662
|
return command;
|
|
@@ -40670,7 +40689,7 @@ function registerRuntime(parent) {
|
|
|
40670
40689
|
exportsCommand.command("open").argument("<run-id>", "Run id").option("--json", "Output as JSON", false).description("Open the export directory for a run").action((runId, options) => handleExportsOpen(runId, options));
|
|
40671
40690
|
exportsCommand.command("download").argument("<run-id>", "Remote run id").option("--json", "Output as JSON", false).description("Download remote run artifacts into .skills/exports").action((runId, options) => handleExportsDownload(runId, options));
|
|
40672
40691
|
parent.command("mcp").option("--register <agent>", "Register MCP server with agent").option("--json", "Output registration result as JSON", false).description("Start MCP server (stdio) or register with an agent").action(async (options) => handleMcp(options));
|
|
40673
|
-
const setup = parent.command("setup").description("Choose
|
|
40692
|
+
const setup = parent.command("setup").description("Choose hosted mode, local-only mode, or agent integrations").option("--mode <mode>", "Runtime mode: hosted or local").option("--api-url <url>", "Hosted API origin").option("--global", "Save setup choice globally", false).option("--json", "Output setup result as JSON", false).action(async (options) => handleSetup(options));
|
|
40674
40693
|
setup.command("agents").option("--json", "Output registration result as JSON", false).description("Register the Skills MCP server with all supported agents").action(async (options) => handleMcp({ register: "all", json: options.json }));
|
|
40675
40694
|
parent.command("self-update").description("Update @hasna/skills to the latest version").option("--json", "Output result as JSON", false).action(async (options) => {
|
|
40676
40695
|
if (process.env.SKILLS_TEST_MODE === "1") {
|
|
@@ -40719,15 +40738,15 @@ async function handleSetup(options) {
|
|
|
40719
40738
|
const scope = options.global ? "global" : "project";
|
|
40720
40739
|
let mode = normalizeSetupMode(options.mode);
|
|
40721
40740
|
if (!mode && process.stdin.isTTY && process.stdout.isTTY) {
|
|
40722
|
-
mode = normalizeSetupMode(await promptLine("Use Skills
|
|
40741
|
+
mode = normalizeSetupMode(await promptLine("Use hosted Skills or local-only mode? [hosted/local] ")) ?? "hosted";
|
|
40723
40742
|
}
|
|
40724
40743
|
mode = mode ?? "local";
|
|
40725
40744
|
saveConfig("mode", mode, scope);
|
|
40726
|
-
if (mode === "
|
|
40745
|
+
if (mode === "hosted") {
|
|
40727
40746
|
saveConfig("apiUrl", options.apiUrl || "https://skills.md", scope);
|
|
40728
40747
|
}
|
|
40729
40748
|
const config2 = loadConfig();
|
|
40730
|
-
const next = mode === "
|
|
40749
|
+
const next = mode === "hosted" ? ["skills auth login", "skills list --remote"] : ["skills list", "skills run <skill>"];
|
|
40731
40750
|
const payload = {
|
|
40732
40751
|
mode,
|
|
40733
40752
|
scope,
|
|
@@ -40740,7 +40759,7 @@ async function handleSetup(options) {
|
|
|
40740
40759
|
}
|
|
40741
40760
|
console.log(chalk8.green(`Set Skills mode to ${mode}`));
|
|
40742
40761
|
console.log(chalk8.dim(` Scope: ${scope}`));
|
|
40743
|
-
if (mode === "
|
|
40762
|
+
if (mode === "hosted") {
|
|
40744
40763
|
console.log(chalk8.dim(` API: ${config2.apiUrl || "https://skills.md"}`));
|
|
40745
40764
|
console.log(chalk8.dim(" Next: skills auth login"));
|
|
40746
40765
|
} else {
|
|
@@ -40755,8 +40774,8 @@ function normalizeSetupMode(value) {
|
|
|
40755
40774
|
if (normalized === "local" || normalized === "offline")
|
|
40756
40775
|
return "local";
|
|
40757
40776
|
if (normalized === "skills.md" || normalized === "skillsmd" || normalized === "remote" || normalized === "hosted")
|
|
40758
|
-
return "
|
|
40759
|
-
throw new Error("Invalid setup mode. Use
|
|
40777
|
+
return "hosted";
|
|
40778
|
+
throw new Error("Invalid setup mode. Use hosted or local.");
|
|
40760
40779
|
}
|
|
40761
40780
|
function promptLine(question) {
|
|
40762
40781
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -40845,7 +40864,7 @@ async function handleRun(name, args2, options) {
|
|
|
40845
40864
|
const { getApiKey: getApiKey2 } = await Promise.resolve().then(() => (init_auth_store(), exports_auth_store));
|
|
40846
40865
|
const apiKey = getApiKey2();
|
|
40847
40866
|
if (!apiKey) {
|
|
40848
|
-
const error48 = `${skill.name} is a hosted skill (${pricing.formatCost(costCents ?? 0)}). Run: skills setup --mode
|
|
40867
|
+
const error48 = `${skill.name} is a hosted skill (${pricing.formatCost(costCents ?? 0)}). Run: skills setup --mode hosted && skills auth login`;
|
|
40849
40868
|
writeRunLogs(runContext, "", error48 + `
|
|
40850
40869
|
`);
|
|
40851
40870
|
const run = completeSkillRun(runContext, { status: "failed", error: error48, costCents });
|
|
@@ -40927,7 +40946,7 @@ async function handleRun(name, args2, options) {
|
|
|
40927
40946
|
process.exitCode = exitCode;
|
|
40928
40947
|
return;
|
|
40929
40948
|
} catch (err) {
|
|
40930
|
-
const error48 = `Hosted skill ${skill.name} requires
|
|
40949
|
+
const error48 = `Hosted skill ${skill.name} requires hosted access: ${err.message}`;
|
|
40931
40950
|
writeRunLogs(runContext, "", error48 + `
|
|
40932
40951
|
`);
|
|
40933
40952
|
const run = completeSkillRun(runContext, { status: "failed", error: error48, costCents });
|
|
@@ -41033,7 +41052,7 @@ async function handleRunsStatus(runId, options) {
|
|
|
41033
41052
|
const { getApiKey: getApiKey2 } = await Promise.resolve().then(() => (init_auth_store(), exports_auth_store));
|
|
41034
41053
|
const apiKey = getApiKey2();
|
|
41035
41054
|
if (!apiKey) {
|
|
41036
|
-
const error48 = "Remote run status requires
|
|
41055
|
+
const error48 = "Remote run status requires hosted access. Run: skills auth login";
|
|
41037
41056
|
if (options.json)
|
|
41038
41057
|
console.log(JSON.stringify({ contractVersion: REMOTE_SKILL_RUN_CONTRACT_VERSION, error: error48 }, null, 2));
|
|
41039
41058
|
else
|
|
@@ -41134,7 +41153,7 @@ async function handleExportsDownload(runId, options) {
|
|
|
41134
41153
|
const { getApiKey: getApiKey2 } = await Promise.resolve().then(() => (init_auth_store(), exports_auth_store));
|
|
41135
41154
|
const apiKey = getApiKey2();
|
|
41136
41155
|
if (!apiKey) {
|
|
41137
|
-
const error48 = "Remote artifact downloads require
|
|
41156
|
+
const error48 = "Remote artifact downloads require hosted access. Run: skills auth login";
|
|
41138
41157
|
if (options.json)
|
|
41139
41158
|
console.log(JSON.stringify({ error: error48 }, null, 2));
|
|
41140
41159
|
else
|
|
@@ -41537,6 +41556,8 @@ var init_completion = __esm(() => {
|
|
|
41537
41556
|
"import",
|
|
41538
41557
|
"doctor",
|
|
41539
41558
|
"auth",
|
|
41559
|
+
"billing",
|
|
41560
|
+
"credits",
|
|
41540
41561
|
"env-check",
|
|
41541
41562
|
"setup-info",
|
|
41542
41563
|
"test",
|
|
@@ -41562,7 +41583,7 @@ __export(exports_create_sync_config, {
|
|
|
41562
41583
|
registerCreateSync: () => registerCreateSync
|
|
41563
41584
|
});
|
|
41564
41585
|
import chalk9 from "chalk";
|
|
41565
|
-
import { existsSync as
|
|
41586
|
+
import { existsSync as existsSync16, writeFileSync as writeFileSync10, mkdirSync as mkdirSync9 } from "fs";
|
|
41566
41587
|
import { join as join17 } from "path";
|
|
41567
41588
|
import { homedir as homedir7 } from "os";
|
|
41568
41589
|
function registerCreateSync(parent) {
|
|
@@ -41612,13 +41633,13 @@ function registerCreateSync(parent) {
|
|
|
41612
41633
|
const pp = getConfigPath("project");
|
|
41613
41634
|
if (options.json) {
|
|
41614
41635
|
console.log(JSON.stringify({
|
|
41615
|
-
global: { path: gp, exists:
|
|
41616
|
-
project: { path: pp, exists:
|
|
41636
|
+
global: { path: gp, exists: existsSync16(gp) },
|
|
41637
|
+
project: { path: pp, exists: existsSync16(pp) }
|
|
41617
41638
|
}, null, 2));
|
|
41618
41639
|
return;
|
|
41619
41640
|
}
|
|
41620
|
-
console.log(`${chalk9.cyan("global")}: ${gp}${
|
|
41621
|
-
console.log(`${chalk9.cyan("project")}: ${pp}${
|
|
41641
|
+
console.log(`${chalk9.cyan("global")}: ${gp}${existsSync16(gp) ? chalk9.green(" (exists)") : chalk9.dim(" (not found)")}`);
|
|
41642
|
+
console.log(`${chalk9.cyan("project")}: ${pp}${existsSync16(pp) ? chalk9.green(" (exists)") : chalk9.dim(" (not found)")}`);
|
|
41622
41643
|
});
|
|
41623
41644
|
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));
|
|
41624
41645
|
parent.command("sync").option("--to <agent>", "Deprecated; use skills mcp --register <agent|all>").option("--from <agent>", "Deprecated; agent skill-folder sync is disabled").option("--register", "Deprecated; agent skill-folder imports are disabled", false).option("--scope <scope>", "Deprecated; ignored", "global").option("--json", "Output as JSON", false).description("Disabled legacy agent skill-folder sync").action((options) => handleSync(options));
|
|
@@ -41628,7 +41649,7 @@ function handleCreate(name, options) {
|
|
|
41628
41649
|
const dirName = bare;
|
|
41629
41650
|
const baseDir = join17(homedir7(), ".hasna", "skills", "custom");
|
|
41630
41651
|
const skillDir = join17(baseDir, dirName);
|
|
41631
|
-
if (
|
|
41652
|
+
if (existsSync16(skillDir)) {
|
|
41632
41653
|
console.log(options.json ? JSON.stringify({ error: `Skill '${bare}' already exists at ${skillDir}` }) : chalk9.red(`Skill '${bare}' already exists at ${skillDir}`));
|
|
41633
41654
|
process.exitCode = 1;
|
|
41634
41655
|
return;
|
|
@@ -41840,7 +41861,7 @@ async function executeScheduledSkill(skillName, args2) {
|
|
|
41840
41861
|
const { getApiKey: getApiKey2 } = await Promise.resolve().then(() => (init_auth_store(), exports_auth_store));
|
|
41841
41862
|
const apiKey = getApiKey2();
|
|
41842
41863
|
if (!apiKey) {
|
|
41843
|
-
throw new Error(`${skill.name} is a hosted skill. Run: skills setup --mode
|
|
41864
|
+
throw new Error(`${skill.name} is a hosted skill. Run: skills setup --mode hosted && skills auth login`);
|
|
41844
41865
|
}
|
|
41845
41866
|
const { RemoteSkillsClient: RemoteSkillsClient2 } = await Promise.resolve().then(() => (init_remote_client(), exports_remote_client));
|
|
41846
41867
|
const client = new RemoteSkillsClient2(apiKey);
|
|
@@ -42004,6 +42025,66 @@ async function apiRequest(path, options) {
|
|
|
42004
42025
|
});
|
|
42005
42026
|
return res.json();
|
|
42006
42027
|
}
|
|
42028
|
+
function sleep(ms) {
|
|
42029
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
42030
|
+
}
|
|
42031
|
+
function browserCommand(url2) {
|
|
42032
|
+
if (process.platform === "darwin")
|
|
42033
|
+
return ["open", url2];
|
|
42034
|
+
if (process.platform === "win32")
|
|
42035
|
+
return ["cmd", "/c", "start", "", url2];
|
|
42036
|
+
return ["xdg-open", url2];
|
|
42037
|
+
}
|
|
42038
|
+
function openBrowser(url2) {
|
|
42039
|
+
const command = browserCommand(url2);
|
|
42040
|
+
if (!command)
|
|
42041
|
+
return;
|
|
42042
|
+
try {
|
|
42043
|
+
Bun.spawn(command, { stdout: "ignore", stderr: "ignore" });
|
|
42044
|
+
} catch {}
|
|
42045
|
+
}
|
|
42046
|
+
async function ensureApiKey(loginResult) {
|
|
42047
|
+
if (loginResult.apiKey)
|
|
42048
|
+
return loginResult.apiKey;
|
|
42049
|
+
if (!loginResult.token)
|
|
42050
|
+
return;
|
|
42051
|
+
const keyRes = await apiRequest("/api/auth/keys", {
|
|
42052
|
+
method: "POST",
|
|
42053
|
+
headers: { Authorization: `Bearer ${loginResult.token}` },
|
|
42054
|
+
body: JSON.stringify({ name: "cli" })
|
|
42055
|
+
});
|
|
42056
|
+
return keyRes.key;
|
|
42057
|
+
}
|
|
42058
|
+
async function persistLoginResult(loginResult) {
|
|
42059
|
+
const storedKey = await ensureApiKey(loginResult);
|
|
42060
|
+
if (!storedKey)
|
|
42061
|
+
return;
|
|
42062
|
+
saveAuthConfig({
|
|
42063
|
+
apiKey: storedKey,
|
|
42064
|
+
email: loginResult.user.email,
|
|
42065
|
+
orgId: loginResult.organization.id,
|
|
42066
|
+
orgSlug: loginResult.organization.slug,
|
|
42067
|
+
userId: loginResult.user.id
|
|
42068
|
+
});
|
|
42069
|
+
return storedKey;
|
|
42070
|
+
}
|
|
42071
|
+
function printLoginSuccess(loginResult, json2) {
|
|
42072
|
+
if (json2 || !isTTY) {
|
|
42073
|
+
console.log(JSON.stringify({
|
|
42074
|
+
status: "authenticated",
|
|
42075
|
+
email: loginResult.user.email,
|
|
42076
|
+
organization: loginResult.organization.slug,
|
|
42077
|
+
firstLogin: loginResult.firstLogin
|
|
42078
|
+
}));
|
|
42079
|
+
return;
|
|
42080
|
+
}
|
|
42081
|
+
console.log(chalk12.green(`
|
|
42082
|
+
\u2713 Signed in as ${loginResult.user.email}`));
|
|
42083
|
+
console.log(chalk12.dim(` Organization: ${loginResult.organization.name}`));
|
|
42084
|
+
if (loginResult.firstLogin) {
|
|
42085
|
+
console.log(chalk12.dim(` API key saved to ~/.hasna/skills/auth.json`));
|
|
42086
|
+
}
|
|
42087
|
+
}
|
|
42007
42088
|
async function doLogin(email3, code) {
|
|
42008
42089
|
if (!email3 || !email3.includes("@")) {
|
|
42009
42090
|
console.error(chalk12.red("Invalid email"));
|
|
@@ -42037,41 +42118,99 @@ async function doLogin(email3, code) {
|
|
|
42037
42118
|
process.exitCode = 1;
|
|
42038
42119
|
return;
|
|
42039
42120
|
}
|
|
42040
|
-
|
|
42121
|
+
const storedKey = await persistLoginResult(verifyRes);
|
|
42041
42122
|
if (!storedKey) {
|
|
42042
|
-
|
|
42123
|
+
console.error(chalk12.red("Login succeeded but API key creation failed"));
|
|
42124
|
+
process.exitCode = 1;
|
|
42125
|
+
return;
|
|
42126
|
+
}
|
|
42127
|
+
printLoginSuccess(verifyRes, false);
|
|
42128
|
+
}
|
|
42129
|
+
async function doDeviceLogin(options) {
|
|
42130
|
+
const start = await apiRequest("/api/auth/device/start", {
|
|
42131
|
+
method: "POST",
|
|
42132
|
+
body: JSON.stringify({ client: "skills-cli" })
|
|
42133
|
+
});
|
|
42134
|
+
if (start.error) {
|
|
42135
|
+
console.error(chalk12.red(start.error));
|
|
42136
|
+
process.exitCode = 1;
|
|
42137
|
+
return;
|
|
42138
|
+
}
|
|
42139
|
+
const verificationUrl = start.verificationUriComplete || start.verificationUri;
|
|
42140
|
+
const shouldPoll = Boolean(options.poll || isTTY && !options.json);
|
|
42141
|
+
if (options.open !== false && isTTY && verificationUrl) {
|
|
42142
|
+
openBrowser(verificationUrl);
|
|
42143
|
+
}
|
|
42144
|
+
if (!shouldPoll) {
|
|
42145
|
+
const payload = {
|
|
42146
|
+
status: "pending",
|
|
42147
|
+
userCode: start.userCode,
|
|
42148
|
+
verificationUri: start.verificationUri,
|
|
42149
|
+
verificationUriComplete: start.verificationUriComplete,
|
|
42150
|
+
expiresIn: start.expiresIn,
|
|
42151
|
+
interval: start.interval,
|
|
42152
|
+
poll: "skills auth login --device --poll"
|
|
42153
|
+
};
|
|
42154
|
+
if (options.json || !isTTY)
|
|
42155
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
42156
|
+
else {
|
|
42157
|
+
console.log(chalk12.bold(`
|
|
42158
|
+
Sign in in your browser
|
|
42159
|
+
`));
|
|
42160
|
+
console.log(`${chalk12.dim("Code:")} ${start.userCode}`);
|
|
42161
|
+
console.log(`${chalk12.dim("URL:")} ${verificationUrl}`);
|
|
42162
|
+
}
|
|
42163
|
+
return;
|
|
42164
|
+
}
|
|
42165
|
+
if (!options.json) {
|
|
42166
|
+
console.log(chalk12.bold(`
|
|
42167
|
+
Sign in in your browser
|
|
42168
|
+
`));
|
|
42169
|
+
console.log(`${chalk12.dim("Code:")} ${start.userCode}`);
|
|
42170
|
+
console.log(`${chalk12.dim("URL:")} ${verificationUrl}`);
|
|
42171
|
+
console.log(chalk12.dim(`
|
|
42172
|
+
Waiting for authentication...`));
|
|
42173
|
+
}
|
|
42174
|
+
const intervalMs = Math.max(1000, Number(start.interval || 5) * 1000);
|
|
42175
|
+
const timeoutMs = Number(options.pollTimeoutMs || DEFAULT_DEVICE_POLL_TIMEOUT_MS);
|
|
42176
|
+
const deadline = Date.now() + timeoutMs;
|
|
42177
|
+
while (Date.now() < deadline) {
|
|
42178
|
+
const tokenRes = await apiRequest("/api/auth/device/token", {
|
|
42043
42179
|
method: "POST",
|
|
42044
|
-
|
|
42045
|
-
body: JSON.stringify({ name: "cli" })
|
|
42180
|
+
body: JSON.stringify({ deviceCode: start.deviceCode })
|
|
42046
42181
|
});
|
|
42047
|
-
|
|
42048
|
-
|
|
42049
|
-
|
|
42050
|
-
apiKey: storedKey,
|
|
42051
|
-
email: verifyRes.user.email,
|
|
42052
|
-
orgId: verifyRes.organization.id,
|
|
42053
|
-
orgSlug: verifyRes.organization.slug,
|
|
42054
|
-
userId: verifyRes.user.id
|
|
42055
|
-
});
|
|
42056
|
-
if (isTTY) {
|
|
42057
|
-
console.log(chalk12.green(`
|
|
42058
|
-
\u2713 Signed in as ${verifyRes.user.email}`));
|
|
42059
|
-
console.log(chalk12.dim(` Organization: ${verifyRes.organization.name}`));
|
|
42060
|
-
if (verifyRes.firstLogin) {
|
|
42061
|
-
console.log(chalk12.dim(` API key saved to ~/.skills/auth.json`));
|
|
42182
|
+
if (tokenRes.error === "authorization_pending" || tokenRes.status === "pending") {
|
|
42183
|
+
await sleep(intervalMs);
|
|
42184
|
+
continue;
|
|
42062
42185
|
}
|
|
42063
|
-
|
|
42064
|
-
|
|
42065
|
-
|
|
42066
|
-
|
|
42067
|
-
|
|
42068
|
-
|
|
42069
|
-
|
|
42186
|
+
if (tokenRes.error) {
|
|
42187
|
+
console.error(chalk12.red(tokenRes.detail || tokenRes.error));
|
|
42188
|
+
process.exitCode = 1;
|
|
42189
|
+
return;
|
|
42190
|
+
}
|
|
42191
|
+
const storedKey = await persistLoginResult(tokenRes);
|
|
42192
|
+
if (!storedKey) {
|
|
42193
|
+
console.error(chalk12.red("Login succeeded but API key creation failed"));
|
|
42194
|
+
process.exitCode = 1;
|
|
42195
|
+
return;
|
|
42196
|
+
}
|
|
42197
|
+
printLoginSuccess(tokenRes, Boolean(options.json));
|
|
42198
|
+
return;
|
|
42070
42199
|
}
|
|
42200
|
+
const error48 = "Device login timed out before browser authentication completed";
|
|
42201
|
+
if (options.json || !isTTY)
|
|
42202
|
+
console.log(JSON.stringify({ status: "expired", error: error48 }));
|
|
42203
|
+
else
|
|
42204
|
+
console.error(chalk12.red(error48));
|
|
42205
|
+
process.exitCode = 1;
|
|
42071
42206
|
}
|
|
42072
42207
|
function registerAuth(parent) {
|
|
42073
|
-
const auth = parent.command("auth").description("Manage
|
|
42074
|
-
auth.command("login").description("Sign in with
|
|
42208
|
+
const auth = parent.command("auth").description("Manage hosted account authentication");
|
|
42209
|
+
auth.command("login").description("Sign in with browser/device code or email code").option("--email <email>", "Email address (non-interactive)").option("--code <code>", "Verification code (non-interactive)").option("--device", "Use browser/device-code login", false).option("--no-open", "Do not open a browser for device-code login").option("--poll", "Poll until browser authentication completes in non-interactive mode", false).option("--poll-timeout-ms <ms>", "Maximum time to wait for device-code login").option("--json", "Output result as JSON", false).action(async (options) => {
|
|
42210
|
+
if (options.device || !options.email && !options.code) {
|
|
42211
|
+
await doDeviceLogin(options);
|
|
42212
|
+
return;
|
|
42213
|
+
}
|
|
42075
42214
|
let email3 = options.email;
|
|
42076
42215
|
if (!email3 && isTTY) {
|
|
42077
42216
|
const existing = getAuthConfig();
|
|
@@ -42140,79 +42279,137 @@ function registerAuth(parent) {
|
|
|
42140
42279
|
console.log(chalk12.dim("(offline \u2014 showing cached info)"));
|
|
42141
42280
|
}
|
|
42142
42281
|
});
|
|
42143
|
-
auth.command("status").description("Show billing status
|
|
42144
|
-
|
|
42145
|
-
|
|
42146
|
-
|
|
42147
|
-
|
|
42148
|
-
|
|
42149
|
-
|
|
42150
|
-
|
|
42151
|
-
|
|
42152
|
-
|
|
42153
|
-
|
|
42154
|
-
|
|
42155
|
-
|
|
42156
|
-
|
|
42157
|
-
|
|
42158
|
-
|
|
42159
|
-
|
|
42160
|
-
|
|
42161
|
-
|
|
42162
|
-
|
|
42163
|
-
|
|
42164
|
-
|
|
42165
|
-
|
|
42166
|
-
|
|
42282
|
+
auth.command("status").description("Show hosted billing status").option("--json", "Output as JSON", false).action(handleBillingStatus);
|
|
42283
|
+
auth.command("checkout").description("Create a Pro checkout session").option("--json", "Output as JSON", false).action(handleCheckout);
|
|
42284
|
+
auth.command("portal").description("Create a customer portal session").option("--json", "Output as JSON", false).action(handlePortal);
|
|
42285
|
+
auth.command("buy-credits").description("Create a credit pack checkout session").argument("<amount>", "Credit pack amount: 1, 5, 20, 50, or 100").option("--json", "Output as JSON", false).action(handleBuyCredits);
|
|
42286
|
+
registerBilling(parent);
|
|
42287
|
+
registerCredits(parent);
|
|
42288
|
+
}
|
|
42289
|
+
function requireHostedAuth(json2) {
|
|
42290
|
+
const config2 = getAuthConfig();
|
|
42291
|
+
if (config2)
|
|
42292
|
+
return config2;
|
|
42293
|
+
const message = "Not signed in. Run: skills auth login";
|
|
42294
|
+
if (json2)
|
|
42295
|
+
console.log(JSON.stringify({ error: message }));
|
|
42296
|
+
else
|
|
42297
|
+
console.log(chalk12.dim(message));
|
|
42298
|
+
process.exitCode = 1;
|
|
42299
|
+
return null;
|
|
42300
|
+
}
|
|
42301
|
+
async function handleBillingStatus(options = {}) {
|
|
42302
|
+
const config2 = requireHostedAuth(options.json);
|
|
42303
|
+
if (!config2)
|
|
42304
|
+
return;
|
|
42305
|
+
try {
|
|
42306
|
+
const res = await apiRequest("/api/v1/billing/status", {
|
|
42167
42307
|
headers: { Authorization: `Bearer ${config2.apiKey}` }
|
|
42168
42308
|
});
|
|
42169
|
-
if (
|
|
42170
|
-
console.
|
|
42171
|
-
process.exitCode = 1;
|
|
42309
|
+
if (options.json) {
|
|
42310
|
+
console.log(JSON.stringify(res, null, 2));
|
|
42172
42311
|
return;
|
|
42173
42312
|
}
|
|
42313
|
+
console.log(chalk12.bold("Plan: ") + res.plan);
|
|
42314
|
+
console.log(chalk12.bold("Balance: ") + res.balance);
|
|
42315
|
+
} catch {
|
|
42316
|
+
console.error(chalk12.red("Failed to fetch billing status"));
|
|
42317
|
+
process.exitCode = 1;
|
|
42318
|
+
}
|
|
42319
|
+
}
|
|
42320
|
+
async function handleCheckout(options = {}) {
|
|
42321
|
+
const config2 = requireHostedAuth(options.json);
|
|
42322
|
+
if (!config2)
|
|
42323
|
+
return;
|
|
42324
|
+
const res = await apiRequest("/api/v1/billing/checkout", {
|
|
42325
|
+
method: "POST",
|
|
42326
|
+
headers: { Authorization: `Bearer ${config2.apiKey}` }
|
|
42327
|
+
});
|
|
42328
|
+
if (res.error || !res.url) {
|
|
42329
|
+
console.error(chalk12.red(res.detail || res.error || "Failed to create checkout session"));
|
|
42330
|
+
process.exitCode = 1;
|
|
42331
|
+
return;
|
|
42332
|
+
}
|
|
42333
|
+
if (options.json)
|
|
42334
|
+
console.log(JSON.stringify(res, null, 2));
|
|
42335
|
+
else
|
|
42174
42336
|
console.log(res.url);
|
|
42337
|
+
}
|
|
42338
|
+
async function handlePortal(options = {}) {
|
|
42339
|
+
const config2 = requireHostedAuth(options.json);
|
|
42340
|
+
if (!config2)
|
|
42341
|
+
return;
|
|
42342
|
+
const res = await apiRequest("/api/v1/billing/portal", {
|
|
42343
|
+
method: "POST",
|
|
42344
|
+
headers: { Authorization: `Bearer ${config2.apiKey}` }
|
|
42175
42345
|
});
|
|
42176
|
-
|
|
42177
|
-
|
|
42178
|
-
|
|
42179
|
-
|
|
42180
|
-
|
|
42181
|
-
|
|
42182
|
-
|
|
42183
|
-
|
|
42184
|
-
headers: { Authorization: `Bearer ${config2.apiKey}` }
|
|
42185
|
-
});
|
|
42186
|
-
if (res.error || !res.url) {
|
|
42187
|
-
console.error(chalk12.red(res.detail || res.error || "Failed to create customer portal session"));
|
|
42188
|
-
process.exitCode = 1;
|
|
42189
|
-
return;
|
|
42190
|
-
}
|
|
42346
|
+
if (res.error || !res.url) {
|
|
42347
|
+
console.error(chalk12.red(res.detail || res.error || "Failed to create customer portal session"));
|
|
42348
|
+
process.exitCode = 1;
|
|
42349
|
+
return;
|
|
42350
|
+
}
|
|
42351
|
+
if (options.json)
|
|
42352
|
+
console.log(JSON.stringify(res, null, 2));
|
|
42353
|
+
else
|
|
42191
42354
|
console.log(res.url);
|
|
42355
|
+
}
|
|
42356
|
+
async function handleBuyCredits(amount, options = {}) {
|
|
42357
|
+
const config2 = requireHostedAuth(options.json);
|
|
42358
|
+
if (!config2)
|
|
42359
|
+
return;
|
|
42360
|
+
const res = await apiRequest("/api/v1/billing/credits", {
|
|
42361
|
+
method: "POST",
|
|
42362
|
+
headers: { Authorization: `Bearer ${config2.apiKey}` },
|
|
42363
|
+
body: JSON.stringify({ amount })
|
|
42192
42364
|
});
|
|
42193
|
-
|
|
42194
|
-
|
|
42195
|
-
|
|
42196
|
-
|
|
42197
|
-
|
|
42198
|
-
|
|
42199
|
-
|
|
42200
|
-
|
|
42201
|
-
headers: { Authorization: `Bearer ${config2.apiKey}` },
|
|
42202
|
-
body: JSON.stringify({ amount })
|
|
42203
|
-
});
|
|
42204
|
-
if (res.error || !res.url) {
|
|
42205
|
-
console.error(chalk12.red(res.detail || res.error || "Failed to create credit checkout session"));
|
|
42206
|
-
process.exitCode = 1;
|
|
42207
|
-
return;
|
|
42208
|
-
}
|
|
42365
|
+
if (res.error || !res.url) {
|
|
42366
|
+
console.error(chalk12.red(res.detail || res.error || "Failed to create credit checkout session"));
|
|
42367
|
+
process.exitCode = 1;
|
|
42368
|
+
return;
|
|
42369
|
+
}
|
|
42370
|
+
if (options.json)
|
|
42371
|
+
console.log(JSON.stringify(res, null, 2));
|
|
42372
|
+
else
|
|
42209
42373
|
console.log(res.url);
|
|
42374
|
+
}
|
|
42375
|
+
async function handleListCreditPacks(options = {}) {
|
|
42376
|
+
const config2 = requireHostedAuth(options.json);
|
|
42377
|
+
if (!config2)
|
|
42378
|
+
return;
|
|
42379
|
+
const res = await apiRequest("/api/v1/billing/credits", {
|
|
42380
|
+
headers: { Authorization: `Bearer ${config2.apiKey}` }
|
|
42210
42381
|
});
|
|
42382
|
+
if (res.error) {
|
|
42383
|
+
console.error(chalk12.red(res.detail || res.error || "Failed to list credit packs"));
|
|
42384
|
+
process.exitCode = 1;
|
|
42385
|
+
return;
|
|
42386
|
+
}
|
|
42387
|
+
if (options.json) {
|
|
42388
|
+
console.log(JSON.stringify(res, null, 2));
|
|
42389
|
+
return;
|
|
42390
|
+
}
|
|
42391
|
+
const packs = Array.isArray(res) ? res : res.packs;
|
|
42392
|
+
for (const pack of packs ?? []) {
|
|
42393
|
+
console.log(`${pack.amount}: ${pack.amountCents ? `$${(pack.amountCents / 100).toFixed(2)}` : pack.label || ""}`);
|
|
42394
|
+
}
|
|
42395
|
+
}
|
|
42396
|
+
function registerBilling(parent) {
|
|
42397
|
+
const billing = parent.command("billing").description("Manage hosted billing");
|
|
42398
|
+
billing.command("status").description("Show billing status").option("--json", "Output as JSON", false).action(handleBillingStatus);
|
|
42399
|
+
billing.command("checkout").description("Create a Pro checkout session").option("--json", "Output as JSON", false).action(handleCheckout);
|
|
42400
|
+
billing.command("portal").description("Create a customer portal session").option("--json", "Output as JSON", false).action(handlePortal);
|
|
42401
|
+
billing.command("buy-credits").description("Create a credit pack checkout session").argument("<amount>", "Credit pack amount: 1, 5, 20, 50, or 100").option("--json", "Output as JSON", false).action(handleBuyCredits);
|
|
42211
42402
|
}
|
|
42212
|
-
|
|
42403
|
+
function registerCredits(parent) {
|
|
42404
|
+
const credits = parent.command("credits").description("Manage hosted credit packs");
|
|
42405
|
+
credits.command("buy").description("Create a credit pack checkout session").argument("<amount>", "Credit pack amount: 1, 5, 20, 50, or 100").option("--json", "Output as JSON", false).action(handleBuyCredits);
|
|
42406
|
+
credits.command("packs").description("List available credit packs").option("--json", "Output as JSON", false).action(handleListCreditPacks);
|
|
42407
|
+
}
|
|
42408
|
+
var isTTY, DEFAULT_DEVICE_POLL_TIMEOUT_MS;
|
|
42213
42409
|
var init_auth = __esm(() => {
|
|
42214
42410
|
init_auth_store();
|
|
42215
42411
|
isTTY = process.stdin.isTTY && process.stdout.isTTY;
|
|
42412
|
+
DEFAULT_DEVICE_POLL_TIMEOUT_MS = 10 * 60 * 1000;
|
|
42216
42413
|
});
|
|
42217
42414
|
|
|
42218
42415
|
// src/cli/commands/feedback.ts
|
|
@@ -43382,6 +43579,58 @@ function App({ initialSkills, overwrite = false }) {
|
|
|
43382
43579
|
// src/cli/index.tsx
|
|
43383
43580
|
init_registry();
|
|
43384
43581
|
init_discovery();
|
|
43582
|
+
|
|
43583
|
+
// src/cli/onboarding.ts
|
|
43584
|
+
init_config();
|
|
43585
|
+
var SKIP_ONBOARDING_COMMANDS = new Set([
|
|
43586
|
+
"interactive",
|
|
43587
|
+
"setup",
|
|
43588
|
+
"config",
|
|
43589
|
+
"auth",
|
|
43590
|
+
"billing",
|
|
43591
|
+
"credits",
|
|
43592
|
+
"completion",
|
|
43593
|
+
"self-update"
|
|
43594
|
+
]);
|
|
43595
|
+
function getRootCommandName(actionCommand) {
|
|
43596
|
+
let current = actionCommand;
|
|
43597
|
+
while (current.parent && current.parent.parent)
|
|
43598
|
+
current = current.parent;
|
|
43599
|
+
return current.name();
|
|
43600
|
+
}
|
|
43601
|
+
function shouldShowFirstRunOnboarding(input) {
|
|
43602
|
+
if (!input.isInteractive || input.testMode)
|
|
43603
|
+
return false;
|
|
43604
|
+
if (input.config.mode)
|
|
43605
|
+
return false;
|
|
43606
|
+
if (input.argv.some((arg) => arg === "--json" || arg === "--help" || arg === "-h" || arg === "--version" || arg === "-V")) {
|
|
43607
|
+
return false;
|
|
43608
|
+
}
|
|
43609
|
+
if (!input.commandName || SKIP_ONBOARDING_COMMANDS.has(input.commandName))
|
|
43610
|
+
return false;
|
|
43611
|
+
return input.argv.length > 0;
|
|
43612
|
+
}
|
|
43613
|
+
function getFirstRunOnboardingMessage() {
|
|
43614
|
+
return [
|
|
43615
|
+
"No Skills setup found.",
|
|
43616
|
+
" Hosted: skills setup --mode hosted && skills auth login",
|
|
43617
|
+
" Local: skills setup --mode local"
|
|
43618
|
+
].join(`
|
|
43619
|
+
`);
|
|
43620
|
+
}
|
|
43621
|
+
function maybePrintFirstRunOnboarding(actionCommand, argv, isInteractive) {
|
|
43622
|
+
if (shouldShowFirstRunOnboarding({
|
|
43623
|
+
argv,
|
|
43624
|
+
commandName: getRootCommandName(actionCommand),
|
|
43625
|
+
config: loadConfig(),
|
|
43626
|
+
isInteractive,
|
|
43627
|
+
testMode: process.env.SKILLS_TEST_MODE === "1"
|
|
43628
|
+
})) {
|
|
43629
|
+
console.error(getFirstRunOnboardingMessage());
|
|
43630
|
+
}
|
|
43631
|
+
}
|
|
43632
|
+
|
|
43633
|
+
// src/cli/index.tsx
|
|
43385
43634
|
import { jsxDEV as jsxDEV7 } from "react/jsx-dev-runtime";
|
|
43386
43635
|
var isTTY2 = (process.stdout.isTTY ?? false) && (process.stdin.isTTY ?? false);
|
|
43387
43636
|
if (process.argv.includes("--no-color")) {
|
|
@@ -43422,4 +43671,7 @@ var { registerAuth: registerAuth2 } = await Promise.resolve().then(() => (init_a
|
|
|
43422
43671
|
registerAuth2(program2);
|
|
43423
43672
|
var { registerFeedback: registerFeedback2 } = await Promise.resolve().then(() => (init_feedback2(), exports_feedback));
|
|
43424
43673
|
registerFeedback2(program2);
|
|
43674
|
+
program2.hook("preAction", (_thisCommand, actionCommand) => {
|
|
43675
|
+
maybePrintFirstRunOnboarding(actionCommand, process.argv.slice(2), isTTY2);
|
|
43676
|
+
});
|
|
43425
43677
|
await program2.parseAsync();
|