@stefafafan/skm 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.initManifest = initManifest;
7
+ exports.initLockfile = initLockfile;
8
+ exports.readManifest = readManifest;
9
+ exports.writeManifest = writeManifest;
10
+ exports.readLockfile = readLockfile;
11
+ exports.writeLockfile = writeLockfile;
12
+ exports.mergeSkillState = mergeSkillState;
13
+ const promises_1 = require("node:fs/promises");
14
+ const node_path_1 = __importDefault(require("node:path"));
15
+ const errors_1 = require("./errors");
16
+ const fs_1 = require("./fs");
17
+ async function initManifest(manifestPath, force) {
18
+ if (!force && (await (0, fs_1.pathExists)(manifestPath))) {
19
+ throw new errors_1.SkmError(`Manifest already exists at ${manifestPath}`, 5);
20
+ }
21
+ await writeManifest(manifestPath, {
22
+ skills: {},
23
+ });
24
+ }
25
+ async function initLockfile(lockfilePath, force) {
26
+ if (!force && (await (0, fs_1.pathExists)(lockfilePath))) {
27
+ throw new errors_1.SkmError(`Lockfile already exists at ${lockfilePath}`, 5);
28
+ }
29
+ await writeLockfile(lockfilePath, {
30
+ skills: {},
31
+ });
32
+ }
33
+ async function readManifest(manifestPath) {
34
+ if (!(await (0, fs_1.pathExists)(manifestPath))) {
35
+ throw new errors_1.SkmError(`Manifest not found at ${manifestPath}`, 2);
36
+ }
37
+ const raw = await (0, promises_1.readFile)(manifestPath, "utf8");
38
+ let parsed;
39
+ try {
40
+ parsed = JSON.parse(raw);
41
+ }
42
+ catch (error) {
43
+ throw new errors_1.SkmError(`Invalid manifest JSON at ${manifestPath}: ${error.message}`, 2);
44
+ }
45
+ if (!parsed ||
46
+ typeof parsed !== "object" ||
47
+ !("skills" in parsed) ||
48
+ typeof parsed.skills !== "object") {
49
+ throw new errors_1.SkmError(`Invalid manifest shape at ${manifestPath}`, 2);
50
+ }
51
+ return {
52
+ skills: parsed.skills,
53
+ };
54
+ }
55
+ async function writeManifest(manifestPath, manifest) {
56
+ await (0, fs_1.ensureDir)(node_path_1.default.dirname(manifestPath));
57
+ await (0, promises_1.writeFile)(manifestPath, `${JSON.stringify({ skills: manifest.skills }, null, 2)}\n`);
58
+ }
59
+ async function readLockfile(lockfilePath) {
60
+ if (!(await (0, fs_1.pathExists)(lockfilePath))) {
61
+ throw new errors_1.SkmError(`Lockfile not found at ${lockfilePath}`, 2);
62
+ }
63
+ const raw = await (0, promises_1.readFile)(lockfilePath, "utf8");
64
+ let parsed;
65
+ try {
66
+ parsed = JSON.parse(raw);
67
+ }
68
+ catch (error) {
69
+ throw new errors_1.SkmError(`Invalid lockfile JSON at ${lockfilePath}: ${error.message}`, 2);
70
+ }
71
+ if (!parsed ||
72
+ typeof parsed !== "object" ||
73
+ !("skills" in parsed) ||
74
+ typeof parsed.skills !== "object") {
75
+ throw new errors_1.SkmError(`Invalid lockfile shape at ${lockfilePath}`, 2);
76
+ }
77
+ return parsed;
78
+ }
79
+ async function writeLockfile(lockfilePath, lockfile) {
80
+ await (0, fs_1.ensureDir)(node_path_1.default.dirname(lockfilePath));
81
+ await (0, promises_1.writeFile)(lockfilePath, `${JSON.stringify(lockfile, null, 2)}\n`);
82
+ }
83
+ function mergeSkillState(manifest, lockfile) {
84
+ const merged = {};
85
+ for (const [name, entry] of Object.entries(manifest.skills)) {
86
+ merged[name] = {
87
+ ...entry,
88
+ resolved: lockfile.skills[name]?.resolved,
89
+ integrity: lockfile.skills[name]?.integrity,
90
+ };
91
+ }
92
+ return merged;
93
+ }
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.materializeSkill = materializeSkill;
7
+ const promises_1 = require("node:fs/promises");
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const errors_1 = require("./errors");
10
+ const fs_1 = require("./fs");
11
+ async function materializeSkill(options) {
12
+ const outputDir = node_path_1.default.join(options.generatedSkillsDir, options.canonicalName);
13
+ await (0, fs_1.ensureDir)(options.generatedSkillsDir);
14
+ await (0, fs_1.removeIfExists)(outputDir);
15
+ if (options.strategy === "link") {
16
+ await (0, promises_1.symlink)(options.sourceDir, outputDir, process.platform === "win32" ? "junction" : "dir");
17
+ return outputDir;
18
+ }
19
+ await (0, fs_1.copyDirectory)(options.sourceDir, outputDir);
20
+ if (options.strategy === "wrap") {
21
+ const wrapped = await wrapSkillMarkdown(node_path_1.default.join(outputDir, "SKILL.md"), options.canonicalName, options.manifestSource, options.resolved);
22
+ await (0, promises_1.writeFile)(node_path_1.default.join(outputDir, "SKILL.md"), wrapped);
23
+ }
24
+ else if (!(await (0, fs_1.pathExists)(node_path_1.default.join(outputDir, "SKILL.md")))) {
25
+ throw new errors_1.SkmError(`Materialized skill at ${outputDir} is missing SKILL.md`, 4);
26
+ }
27
+ await (0, promises_1.writeFile)(node_path_1.default.join(outputDir, ".skm-meta.json"), `${JSON.stringify({
28
+ source: options.manifestSource,
29
+ resolved: options.resolved,
30
+ strategy: options.strategy,
31
+ }, null, 2)}\n`);
32
+ return outputDir;
33
+ }
34
+ async function wrapSkillMarkdown(skillMdPath, canonicalName, manifestSource, resolved) {
35
+ const raw = await (0, promises_1.readFile)(skillMdPath, "utf8");
36
+ const provenanceComment = `\n\n<!-- Generated by skm from ${manifestSource} @ ${resolved} -->\n`;
37
+ if (!raw.startsWith("---\n")) {
38
+ return [
39
+ "---",
40
+ `name: ${canonicalName}`,
41
+ "description: Materialized by skm",
42
+ "---",
43
+ "",
44
+ raw.trimEnd(),
45
+ provenanceComment.trimEnd(),
46
+ "",
47
+ ].join("\n");
48
+ }
49
+ const endIndex = raw.indexOf("\n---", 4);
50
+ if (endIndex === -1) {
51
+ return `${raw.trimEnd()}${provenanceComment}`;
52
+ }
53
+ const frontmatter = raw.slice(4, endIndex).split("\n");
54
+ const body = raw.slice(endIndex + 5).replace(/^\n/, "");
55
+ let replacedName = false;
56
+ const nextFrontmatter = frontmatter.map((line) => {
57
+ if (line.startsWith("name:")) {
58
+ replacedName = true;
59
+ return `name: ${canonicalName}`;
60
+ }
61
+ return line;
62
+ });
63
+ if (!replacedName) {
64
+ nextFrontmatter.unshift(`name: ${canonicalName}`);
65
+ }
66
+ return [
67
+ "---",
68
+ ...nextFrontmatter,
69
+ "---",
70
+ "",
71
+ body.trimEnd(),
72
+ provenanceComment.trimEnd(),
73
+ "",
74
+ ].join("\n");
75
+ }
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderCliResultAsText = renderCliResultAsText;
4
+ function renderCliResultAsText(result) {
5
+ switch (result.kind) {
6
+ case "summary":
7
+ return `${result.summary}\n`;
8
+ case "list": {
9
+ const lines = ["name\tscope\tsource\trequested\tresolved\teffective"];
10
+ for (const row of result.rows) {
11
+ lines.push([
12
+ row.name,
13
+ row.scope,
14
+ row.source,
15
+ row.requested ?? "",
16
+ row.resolved ?? "",
17
+ row.effective,
18
+ ].join("\t"));
19
+ }
20
+ return `${lines.join("\n")}\n`;
21
+ }
22
+ case "inspect":
23
+ return `${result.details.map((detail) => `${detail.label}: ${detail.value}`).join("\n")}\n\n`;
24
+ case "help": {
25
+ const lines = [result.title, "", `Usage: ${result.usage}`];
26
+ for (const section of result.sections) {
27
+ lines.push("", `${section.title}:`, ...section.lines);
28
+ }
29
+ return `${lines.join("\n")}\n`;
30
+ }
31
+ }
32
+ }
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.resolveScope = resolveScope;
7
+ exports.findProjectRoot = findProjectRoot;
8
+ exports.globalScope = globalScope;
9
+ exports.projectScope = projectScope;
10
+ const node_path_1 = __importDefault(require("node:path"));
11
+ const errors_1 = require("./errors");
12
+ const fs_1 = require("./fs");
13
+ async function resolveScope(options) {
14
+ const homeDir = options.homeDir ?? process.env.HOME;
15
+ if (!homeDir) {
16
+ throw new errors_1.SkmError("HOME is required to resolve scope", 2);
17
+ }
18
+ if (options.explicitScope === "project") {
19
+ const projectRoot = (await findProjectRoot(options.cwd)) ??
20
+ (options.allowCreateProject ? options.cwd : undefined);
21
+ if (!projectRoot) {
22
+ throw new errors_1.SkmError("Project scope requested but no skills.json was found", 2);
23
+ }
24
+ return projectScope(projectRoot);
25
+ }
26
+ if (options.explicitScope === "global") {
27
+ return globalScope(homeDir, options.xdgConfigHome);
28
+ }
29
+ const discoveredProjectRoot = await findProjectRoot(options.cwd);
30
+ if (discoveredProjectRoot) {
31
+ return projectScope(discoveredProjectRoot);
32
+ }
33
+ return globalScope(homeDir, options.xdgConfigHome);
34
+ }
35
+ async function findProjectRoot(startDir) {
36
+ let currentDir = node_path_1.default.resolve(startDir);
37
+ while (true) {
38
+ if (await (0, fs_1.pathExists)(node_path_1.default.join(currentDir, "skills.json"))) {
39
+ return currentDir;
40
+ }
41
+ const parentDir = node_path_1.default.dirname(currentDir);
42
+ if (parentDir === currentDir) {
43
+ return undefined;
44
+ }
45
+ currentDir = parentDir;
46
+ }
47
+ }
48
+ function globalScope(homeDir, xdgConfigHome) {
49
+ const configRoot = xdgConfigHome
50
+ ? node_path_1.default.join(xdgConfigHome, "skm")
51
+ : node_path_1.default.join(homeDir, ".config", "skm");
52
+ return {
53
+ kind: "global",
54
+ rootDir: configRoot,
55
+ manifestPath: node_path_1.default.join(configRoot, "skills.json"),
56
+ lockfilePath: node_path_1.default.join(configRoot, "skills.lock.json"),
57
+ stateDir: configRoot,
58
+ storeDir: node_path_1.default.join(configRoot, "store"),
59
+ generatedSkillsDir: node_path_1.default.join(homeDir, ".agents", "skills"),
60
+ };
61
+ }
62
+ function projectScope(projectRoot) {
63
+ return {
64
+ kind: "project",
65
+ rootDir: projectRoot,
66
+ manifestPath: node_path_1.default.join(projectRoot, "skills.json"),
67
+ lockfilePath: node_path_1.default.join(projectRoot, "skills.lock.json"),
68
+ stateDir: node_path_1.default.join(projectRoot, ".skm"),
69
+ storeDir: node_path_1.default.join(projectRoot, ".skm", "store"),
70
+ generatedSkillsDir: node_path_1.default.join(projectRoot, ".agents", "skills"),
71
+ };
72
+ }
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.parseSource = parseSource;
7
+ exports.fetchSkillToTempDir = fetchSkillToTempDir;
8
+ exports.isFixedRef = isFixedRef;
9
+ exports.defaultRequestedRef = defaultRequestedRef;
10
+ exports.canonicalTreeUrl = canonicalTreeUrl;
11
+ exports.checkoutSourceRepo = checkoutSourceRepo;
12
+ exports.discoverSkillsInRepo = discoverSkillsInRepo;
13
+ const promises_1 = require("node:fs/promises");
14
+ const node_os_1 = __importDefault(require("node:os"));
15
+ const node_path_1 = __importDefault(require("node:path"));
16
+ const node_crypto_1 = require("node:crypto");
17
+ const errors_1 = require("./errors");
18
+ const fs_1 = require("./fs");
19
+ const git_1 = require("./git");
20
+ function parseSource(input) {
21
+ const parsedUrlSource = parseHttpsGitHubSource(input);
22
+ if (parsedUrlSource) {
23
+ return parsedUrlSource;
24
+ }
25
+ const githubRepoShorthandMatch = /^([^/\s]+)\/([^/\s]+)$/.exec(input);
26
+ if (githubRepoShorthandMatch) {
27
+ const owner = githubRepoShorthandMatch[1];
28
+ const repo = githubRepoShorthandMatch[2];
29
+ if (!owner || !repo) {
30
+ throw new errors_1.SkmError(`Invalid GitHub repository shorthand: ${input}`, 2);
31
+ }
32
+ return {
33
+ kind: "github-repo",
34
+ raw: input,
35
+ owner,
36
+ repo,
37
+ };
38
+ }
39
+ throw new errors_1.SkmError(`Unsupported source: ${input}`, 2);
40
+ }
41
+ async function fetchSkillToTempDir(options, tempRoot) {
42
+ if (options.source.kind !== "github-tree") {
43
+ throw new errors_1.SkmError(`Source ${options.source.raw} does not point to a single skill`, 2);
44
+ }
45
+ const workingRoot = tempRoot ?? (await (0, promises_1.mkdtemp)(node_path_1.default.join(node_os_1.default.tmpdir(), "skm-fetch-")));
46
+ const outputDir = node_path_1.default.join(workingRoot, options.source.defaultName);
47
+ const checkedOut = await checkoutSourceRepo(options, workingRoot);
48
+ const upstreamSkillDir = node_path_1.default.join(checkedOut.checkoutDir, options.source.subpath);
49
+ const skillMdPath = node_path_1.default.join(upstreamSkillDir, "SKILL.md");
50
+ if (!(await (0, fs_1.pathExists)(skillMdPath))) {
51
+ throw new errors_1.SkmError(`Skill source ${options.source.raw} is missing SKILL.md`, 4);
52
+ }
53
+ await (0, fs_1.copyDirectory)(upstreamSkillDir, outputDir);
54
+ await (0, fs_1.removeIfExists)(checkedOut.checkoutDir);
55
+ return {
56
+ skillDir: outputDir,
57
+ resolved: checkedOut.resolved,
58
+ };
59
+ }
60
+ function isFixedRef(ref) {
61
+ return /^[0-9a-f]{7,40}$/i.test(ref);
62
+ }
63
+ function defaultRequestedRef(source) {
64
+ return source.kind === "github-tree" ? source.ref : "main";
65
+ }
66
+ function canonicalTreeUrl(source, ref, subpath) {
67
+ return `${resolveCanonicalGithubBaseUrl(source)}/${source.owner}/${source.repo}/tree/${ref}/${subpath}`;
68
+ }
69
+ async function checkoutSourceRepo(options, tempRoot) {
70
+ const workingRoot = tempRoot ?? (await (0, promises_1.mkdtemp)(node_path_1.default.join(node_os_1.default.tmpdir(), "skm-fetch-")));
71
+ const checkoutDir = node_path_1.default.join(workingRoot, `checkout-${(0, node_crypto_1.randomUUID)()}`);
72
+ const repoUrl = resolveRepoUrl(options.source, options.githubBaseUrl);
73
+ await (0, fs_1.removeIfExists)(checkoutDir);
74
+ await (0, git_1.cloneAndCheckout)(repoUrl, options.requestedRef, checkoutDir);
75
+ return {
76
+ checkoutDir,
77
+ resolved: await (0, git_1.readHeadCommit)(checkoutDir),
78
+ };
79
+ }
80
+ async function discoverSkillsInRepo(repoDir) {
81
+ const discovered = new Map();
82
+ await walk(repoDir);
83
+ return [...discovered.values()].sort((left, right) => left.relativeDir.localeCompare(right.relativeDir));
84
+ async function walk(currentDir) {
85
+ const entries = await (0, promises_1.readdir)(currentDir, { withFileTypes: true });
86
+ const hasSkill = entries.some((entry) => entry.isFile() && entry.name === "SKILL.md");
87
+ if (hasSkill) {
88
+ const relativeDir = node_path_1.default.relative(repoDir, currentDir) || ".";
89
+ discovered.set(relativeDir, {
90
+ relativeDir,
91
+ canonicalName: node_path_1.default.basename(currentDir),
92
+ absoluteDir: currentDir,
93
+ });
94
+ }
95
+ for (const entry of entries) {
96
+ if (!entry.isDirectory()) {
97
+ continue;
98
+ }
99
+ await walk(node_path_1.default.join(currentDir, entry.name));
100
+ }
101
+ }
102
+ }
103
+ function resolveRepoUrl(source, githubBaseUrl = process.env.SKM_GITHUB_BASE_URL ?? "https://github.com") {
104
+ const trimmedBase = githubBaseUrl.replace(/\/$/, "");
105
+ if (trimmedBase.includes("://")) {
106
+ return `${trimmedBase}/${source.owner}/${source.repo}.git`;
107
+ }
108
+ return node_path_1.default.join(trimmedBase, source.owner, `${source.repo}.git`);
109
+ }
110
+ function parseHttpsGitHubSource(input) {
111
+ let url;
112
+ try {
113
+ url = new URL(input);
114
+ }
115
+ catch {
116
+ return undefined;
117
+ }
118
+ if (url.protocol !== "https:" || url.search || url.hash) {
119
+ return undefined;
120
+ }
121
+ const segments = url.pathname.split("/").filter(Boolean);
122
+ if (segments.length === 2) {
123
+ const [owner, repo] = segments;
124
+ if (!owner || !repo) {
125
+ throw new errors_1.SkmError(`Invalid GitHub repository URL: ${input}`, 2);
126
+ }
127
+ return {
128
+ kind: "github-repo",
129
+ raw: input,
130
+ owner,
131
+ repo,
132
+ urlBase: url.origin,
133
+ };
134
+ }
135
+ if (segments.length >= 5 && segments[2] === "tree") {
136
+ const [owner, repo, , ref, ...subpathSegments] = segments;
137
+ const subpath = subpathSegments.join("/");
138
+ if (!owner || !repo || !ref || !subpath) {
139
+ throw new errors_1.SkmError(`Invalid GitHub source: ${input}`, 2);
140
+ }
141
+ return {
142
+ kind: "github-tree",
143
+ raw: input,
144
+ owner,
145
+ repo,
146
+ urlBase: url.origin,
147
+ ref,
148
+ subpath,
149
+ defaultName: node_path_1.default.basename(subpath),
150
+ };
151
+ }
152
+ return undefined;
153
+ }
154
+ function resolveCanonicalGithubBaseUrl(source) {
155
+ return (source.urlBase ?? process.env.SKM_GITHUB_URL_BASE ?? "https://github.com").replace(/\/$/, "");
156
+ }
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.storeSkill = storeSkill;
7
+ exports.storePath = storePath;
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const fs_1 = require("./fs");
10
+ async function storeSkill(storeDir, sourceDir, integrity) {
11
+ await (0, fs_1.ensureDir)(storeDir);
12
+ const destination = storePath(storeDir, integrity);
13
+ if (!(await (0, fs_1.pathExists)(destination))) {
14
+ await (0, fs_1.copyDirectory)(sourceDir, destination);
15
+ }
16
+ return destination;
17
+ }
18
+ function storePath(storeDir, integrity) {
19
+ return node_path_1.default.join(storeDir, integrity);
20
+ }
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.renderCliResultWithInk = renderCliResultWithInk;
7
+ const react_1 = __importDefault(require("react"));
8
+ const nativeImport = new Function("specifier", "return import(specifier);");
9
+ async function loadInk() {
10
+ return nativeImport("ink");
11
+ }
12
+ async function renderCliResultWithInk(result, options) {
13
+ const ink = await loadInk();
14
+ return ink.renderToString(createResultView(ink, result), {
15
+ columns: options?.columns,
16
+ });
17
+ }
18
+ function createResultView(ink, result) {
19
+ switch (result.kind) {
20
+ case "summary":
21
+ return createSummaryView(ink, result);
22
+ case "list":
23
+ return createListView(ink, result);
24
+ case "inspect":
25
+ return createInspectView(ink, result);
26
+ case "help":
27
+ return createHelpView(ink, result);
28
+ }
29
+ }
30
+ function createSummaryView(ink, result) {
31
+ const { Box, Text } = ink;
32
+ return react_1.default.createElement(Box, { flexDirection: "column" }, react_1.default.createElement(Text, { color: "green", bold: true }, `[ok] ${result.command}`), react_1.default.createElement(Text, { dimColor: true }, `scope: ${result.scope}`), react_1.default.createElement(Text, null, result.summary), ...(result.skills && result.skills.length > 0
33
+ ? [
34
+ react_1.default.createElement(Text, { key: "skills-heading", bold: true }, "skills"),
35
+ ...result.skills.map((skill, index) => createSkillSummary(ink, skill, index)),
36
+ ]
37
+ : []), ...(result.details && result.details.length > 0
38
+ ? [
39
+ react_1.default.createElement(Text, { key: "details-heading", bold: true }, "details"),
40
+ ...result.details.map((detail, index) => createDetailRow(ink, detail, `detail-${index}`)),
41
+ ]
42
+ : []));
43
+ }
44
+ function createListView(ink, result) {
45
+ const { Box, Text } = ink;
46
+ return react_1.default.createElement(Box, { flexDirection: "column" }, react_1.default.createElement(Text, { color: "green", bold: true }, "Installed skills"), react_1.default.createElement(Text, { dimColor: true }, result.all ? "showing project and global scopes" : "showing active scope"), ...result.rows.flatMap((row, index) => [
47
+ react_1.default.createElement(Text, { key: `name-${index}`, bold: true }, `${row.name} [${row.effective}]`),
48
+ react_1.default.createElement(Box, { key: `row-${index}`, flexDirection: "column", marginLeft: 2 }, createDetailRow(ink, { label: "scope", value: row.scope }, `scope-${index}`), createDetailRow(ink, { label: "source", value: row.source }, `source-${index}`), createDetailRow(ink, { label: "requested", value: row.requested ?? "" }, `requested-${index}`), createDetailRow(ink, { label: "resolved", value: row.resolved ?? "" }, `resolved-${index}`)),
49
+ ]));
50
+ }
51
+ function createInspectView(ink, result) {
52
+ const { Box, Text } = ink;
53
+ return react_1.default.createElement(Box, { flexDirection: "column" }, react_1.default.createElement(Text, { color: "green", bold: true }, `[ok] inspect`), react_1.default.createElement(Text, { dimColor: true }, `scope: ${result.scope}`), react_1.default.createElement(Text, { bold: true }, result.name), ...result.details.map((detail, index) => createDetailRow(ink, detail, `inspect-${index}`)));
54
+ }
55
+ function createHelpView(ink, result) {
56
+ const { Box, Text } = ink;
57
+ return react_1.default.createElement(Box, { flexDirection: "column" }, react_1.default.createElement(Text, { color: "green", bold: true }, result.title), react_1.default.createElement(Text, null, `Usage: ${result.usage}`), ...result.sections.flatMap((section, index) => [
58
+ react_1.default.createElement(Text, { key: `section-title-${index}`, bold: true }, section.title),
59
+ ...section.lines.map((line, lineIndex) => react_1.default.createElement(Text, { key: `section-line-${index}-${lineIndex}` }, line)),
60
+ ]));
61
+ }
62
+ function createSkillSummary(ink, skill, index) {
63
+ const { Box, Text } = ink;
64
+ return react_1.default.createElement(Box, { key: `skill-${index}`, flexDirection: "column", marginLeft: 2 }, react_1.default.createElement(Text, { bold: true }, `${skill.name}${skill.status ? ` (${skill.status})` : ""}`), ...(skill.previousName
65
+ ? [
66
+ createDetailRow(ink, { label: "previous", value: skill.previousName }, `previous-${index}`),
67
+ ]
68
+ : []), ...(skill.source
69
+ ? [createDetailRow(ink, { label: "source", value: skill.source }, `skill-source-${index}`)]
70
+ : []), ...(skill.requested
71
+ ? [
72
+ createDetailRow(ink, { label: "requested", value: skill.requested }, `skill-requested-${index}`),
73
+ ]
74
+ : []), ...(skill.resolved
75
+ ? [
76
+ createDetailRow(ink, { label: "resolved", value: skill.resolved }, `skill-resolved-${index}`),
77
+ ]
78
+ : []));
79
+ }
80
+ function createDetailRow(ink, detail, key) {
81
+ const { Box, Text } = ink;
82
+ return react_1.default.createElement(Box, { key }, react_1.default.createElement(Text, { dimColor: true }, `${detail.label}: `), react_1.default.createElement(Text, null, detail.value));
83
+ }
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@stefafafan/skm",
3
+ "version": "0.1.0",
4
+ "author": "stefafafan",
5
+ "private": false,
6
+ "description": "A package manager for AI Agent Skills. Supports installation, version management, and renaming of skills. Can be used for both global and project specific skill management.",
7
+ "keywords": [
8
+ "agents",
9
+ "cli",
10
+ "developer-tools",
11
+ "skills"
12
+ ],
13
+ "homepage": "https://github.com/stefafafan/skm",
14
+ "bugs": {
15
+ "url": "https://github.com/stefafafan/skm/issues"
16
+ },
17
+ "license": "MIT",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/stefafafan/skm.git"
21
+ },
22
+ "bin": {
23
+ "skm": "dist/src/cli.js"
24
+ },
25
+ "files": [
26
+ "dist/src/**/*",
27
+ "LICENSE",
28
+ "README.md"
29
+ ],
30
+ "publishConfig": {
31
+ "access": "public"
32
+ },
33
+ "dependencies": {
34
+ "ink": "^6.8.0",
35
+ "react": "^19.2.4"
36
+ },
37
+ "devDependencies": {
38
+ "@types/node": "^24.5.2",
39
+ "@types/react": "^19.2.14",
40
+ "oxfmt": "^0.41.0",
41
+ "oxlint": "^1.56.0",
42
+ "typescript": "^5.9.2"
43
+ },
44
+ "scripts": {
45
+ "build": "tsc -p tsconfig.json",
46
+ "lint": "oxlint src test",
47
+ "lint:fix": "oxlint --fix src test",
48
+ "format": "oxfmt --write .",
49
+ "format:check": "oxfmt --check .",
50
+ "test": "pnpm run build && node --test \"dist/test/**/*.test.js\""
51
+ }
52
+ }