@its-thepoe/skills 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.mjs +4 -0
- package/lib/cli-main.mjs +249 -0
- package/lib/link-engine.mjs +218 -0
- package/package.json +31 -0
- package/skills.manifest.json +10 -0
package/bin/cli.mjs
ADDED
package/lib/cli-main.mjs
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { spawnSync } from "node:child_process";
|
|
5
|
+
import {
|
|
6
|
+
AGENT_TARGETS,
|
|
7
|
+
agentBaseDir,
|
|
8
|
+
linkSkillToAgents,
|
|
9
|
+
readSkillVersion,
|
|
10
|
+
removeSkillFromAgents,
|
|
11
|
+
resolveSkillRoot,
|
|
12
|
+
} from "./link-engine.mjs";
|
|
13
|
+
|
|
14
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
const PKG_DIR = path.resolve(__dirname, "..");
|
|
16
|
+
const MANIFEST_PATH = path.join(PKG_DIR, "skills.manifest.json");
|
|
17
|
+
|
|
18
|
+
function logLine(msg) {
|
|
19
|
+
process.stdout.write(msg + "\n");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function loadManifest() {
|
|
23
|
+
const raw = fs.readFileSync(MANIFEST_PATH, "utf8");
|
|
24
|
+
return JSON.parse(raw);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function parseGlobalFlags(argv) {
|
|
28
|
+
const flags = {
|
|
29
|
+
dryRun: false,
|
|
30
|
+
only: [],
|
|
31
|
+
strategy: "symlink",
|
|
32
|
+
online: false,
|
|
33
|
+
};
|
|
34
|
+
const rest = [];
|
|
35
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
36
|
+
const a = argv[i];
|
|
37
|
+
if (a === "--dry-run") flags.dryRun = true;
|
|
38
|
+
else if (a === "--online") flags.online = true;
|
|
39
|
+
else if (a === "--strategy" && argv[i + 1]) {
|
|
40
|
+
flags.strategy = String(argv[++i]);
|
|
41
|
+
if (flags.strategy !== "symlink" && flags.strategy !== "copy") {
|
|
42
|
+
throw new Error('--strategy must be "symlink" or "copy"');
|
|
43
|
+
}
|
|
44
|
+
} else if (a.startsWith("--only=")) {
|
|
45
|
+
flags.only.push(
|
|
46
|
+
...a
|
|
47
|
+
.slice("--only=".length)
|
|
48
|
+
.split(",")
|
|
49
|
+
.map((s) => s.trim())
|
|
50
|
+
.filter(Boolean)
|
|
51
|
+
);
|
|
52
|
+
} else if (a === "--only" && argv[i + 1]) {
|
|
53
|
+
flags.only.push(
|
|
54
|
+
...String(argv[++i])
|
|
55
|
+
.split(",")
|
|
56
|
+
.map((s) => s.trim())
|
|
57
|
+
.filter(Boolean)
|
|
58
|
+
);
|
|
59
|
+
} else rest.push(a);
|
|
60
|
+
}
|
|
61
|
+
return { flags, rest };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function printHelp() {
|
|
65
|
+
logLine(`@its-thepoe/skills — install Agent Skills into local agent directories
|
|
66
|
+
|
|
67
|
+
Usage:
|
|
68
|
+
npx @its-thepoe/skills@latest <command> [options]
|
|
69
|
+
|
|
70
|
+
Commands:
|
|
71
|
+
install --all Link or copy every skill from the manifest
|
|
72
|
+
install <skill-name> [...] Install specific skills (folder names)
|
|
73
|
+
sync --all Idempotent re-install (same as install --all)
|
|
74
|
+
sync <skill-name> [...] Re-install specific skills
|
|
75
|
+
check Verify installs under agent dirs
|
|
76
|
+
remove --all Remove managed installs for all manifest skills
|
|
77
|
+
remove <skill-name> [...] Remove specific skills
|
|
78
|
+
|
|
79
|
+
Options:
|
|
80
|
+
--dry-run Print actions without writing
|
|
81
|
+
--only=<a>[,<b>...] Limit targets: cursor, claude, opencode, windsurf, all
|
|
82
|
+
--strategy symlink|copy Default symlink; use copy if symlinks fail (e.g. Windows)
|
|
83
|
+
--online With check: compare to npm registry (requires network)
|
|
84
|
+
|
|
85
|
+
Agents (default: all):
|
|
86
|
+
${Object.keys(AGENT_TARGETS)
|
|
87
|
+
.map((k) => ` - ${k}`)
|
|
88
|
+
.join("\n")}
|
|
89
|
+
`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function getManifestEntries(manifest, names) {
|
|
93
|
+
const byName = new Map(manifest.skills.map((s) => [s.name, s]));
|
|
94
|
+
const out = [];
|
|
95
|
+
for (const n of names) {
|
|
96
|
+
const row = byName.get(n);
|
|
97
|
+
if (!row) throw new Error(`Unknown skill "${n}" (not in skills.manifest.json)`);
|
|
98
|
+
out.push(row);
|
|
99
|
+
}
|
|
100
|
+
return out;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function npmViewVersion(pkg) {
|
|
104
|
+
const r = spawnSync("npm", ["view", pkg, "version"], {
|
|
105
|
+
encoding: "utf8",
|
|
106
|
+
shell: process.platform === "win32",
|
|
107
|
+
});
|
|
108
|
+
if (r.status !== 0) return null;
|
|
109
|
+
return String(r.stdout || "").trim() || null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* @param {{ online: boolean }} opts
|
|
114
|
+
*/
|
|
115
|
+
function runCheck(opts) {
|
|
116
|
+
const manifest = loadManifest();
|
|
117
|
+
const { online } = opts;
|
|
118
|
+
let exit = 0;
|
|
119
|
+
for (const row of manifest.skills) {
|
|
120
|
+
let root = null;
|
|
121
|
+
try {
|
|
122
|
+
root = resolveSkillRoot(row.package, import.meta.url);
|
|
123
|
+
} catch {
|
|
124
|
+
logLine(`BUNDLE ${row.package}: not resolvable via node_modules (install orchestrator deps)`);
|
|
125
|
+
exit = 1;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
const bundledVer = readSkillVersion(root);
|
|
129
|
+
let latest = null;
|
|
130
|
+
if (online) {
|
|
131
|
+
latest = npmViewVersion(row.package);
|
|
132
|
+
}
|
|
133
|
+
logLine(`\n${row.name} (${row.package}) bundled@${bundledVer}${latest ? ` registry_latest@${latest}` : ""}`);
|
|
134
|
+
|
|
135
|
+
for (const agent of Object.keys(AGENT_TARGETS)) {
|
|
136
|
+
const dest = path.join(agentBaseDir(agent), row.name);
|
|
137
|
+
if (!fs.existsSync(dest)) {
|
|
138
|
+
logLine(` [${agent}] MISSING ${dest}`);
|
|
139
|
+
exit = 1;
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
const st = fs.lstatSync(dest);
|
|
143
|
+
if (st.isSymbolicLink()) {
|
|
144
|
+
const t = fs.readlinkSync(dest);
|
|
145
|
+
logLine(` [${agent}] OK symlink -> ${t}`);
|
|
146
|
+
} else if (st.isDirectory()) {
|
|
147
|
+
const skillMd = path.join(dest, "SKILL.md");
|
|
148
|
+
if (!fs.existsSync(skillMd)) {
|
|
149
|
+
logLine(` [${agent}] BAD copy dir (no SKILL.md) ${dest}`);
|
|
150
|
+
exit = 1;
|
|
151
|
+
} else {
|
|
152
|
+
logLine(` [${agent}] OK directory copy ${dest}`);
|
|
153
|
+
}
|
|
154
|
+
} else {
|
|
155
|
+
logLine(` [${agent}] UNEXPECTED ${dest}`);
|
|
156
|
+
exit = 1;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return exit;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* @param {string[]} argv
|
|
165
|
+
*/
|
|
166
|
+
export function run(argv) {
|
|
167
|
+
if (argv.length === 0 || argv[0] === "-h" || argv[0] === "--help") {
|
|
168
|
+
printHelp();
|
|
169
|
+
return 0;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const { flags, rest } = parseGlobalFlags(argv);
|
|
173
|
+
const cmd = rest[0];
|
|
174
|
+
const args = rest.slice(1);
|
|
175
|
+
|
|
176
|
+
if (!cmd || cmd === "help") {
|
|
177
|
+
printHelp();
|
|
178
|
+
return 0;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const log = logLine;
|
|
182
|
+
const only = flags.only.length ? flags.only.join(",").split(",").map((s) => s.trim()).filter(Boolean) : ["all"];
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
if (cmd === "check") {
|
|
186
|
+
return runCheck({ online: flags.online });
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (cmd === "install" || cmd === "sync") {
|
|
190
|
+
const all = args.includes("--all");
|
|
191
|
+
const names = args.filter((a) => a !== "--all");
|
|
192
|
+
const manifest = loadManifest();
|
|
193
|
+
let entries = manifest.skills;
|
|
194
|
+
if (!all) {
|
|
195
|
+
if (names.length === 0) {
|
|
196
|
+
logLine("Error: specify skill names or --all");
|
|
197
|
+
printHelp();
|
|
198
|
+
return 1;
|
|
199
|
+
}
|
|
200
|
+
entries = getManifestEntries(manifest, names);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
for (const row of entries) {
|
|
204
|
+
const root = resolveSkillRoot(row.package, import.meta.url);
|
|
205
|
+
const version = readSkillVersion(root);
|
|
206
|
+
logLine(`\n${cmd} ${row.name} @ ${root}`);
|
|
207
|
+
linkSkillToAgents({
|
|
208
|
+
skillRoot: root,
|
|
209
|
+
skillName: row.name,
|
|
210
|
+
skillPackage: row.package,
|
|
211
|
+
version,
|
|
212
|
+
only,
|
|
213
|
+
strategy: flags.strategy,
|
|
214
|
+
dryRun: flags.dryRun,
|
|
215
|
+
log,
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
logLine(`\nDone. Reload your agents (Cursor, Claude Code, OpenCode, Windsurf).`);
|
|
219
|
+
return 0;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (cmd === "remove") {
|
|
223
|
+
const all = args.includes("--all");
|
|
224
|
+
const names = args.filter((a) => a !== "--all");
|
|
225
|
+
const manifest = loadManifest();
|
|
226
|
+
let targets = manifest.skills.map((s) => s.name);
|
|
227
|
+
if (!all) {
|
|
228
|
+
if (names.length === 0) {
|
|
229
|
+
logLine("Error: specify skill names or --all");
|
|
230
|
+
return 1;
|
|
231
|
+
}
|
|
232
|
+
targets = names;
|
|
233
|
+
for (const n of targets) getManifestEntries(manifest, [n]);
|
|
234
|
+
}
|
|
235
|
+
for (const name of targets) {
|
|
236
|
+
logLine(`\nremove ${name}`);
|
|
237
|
+
removeSkillFromAgents({ skillName: name, only, dryRun: flags.dryRun, log });
|
|
238
|
+
}
|
|
239
|
+
return 0;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
logLine(`Unknown command: ${cmd}`);
|
|
243
|
+
printHelp();
|
|
244
|
+
return 1;
|
|
245
|
+
} catch (e) {
|
|
246
|
+
logLine(`Error: ${e instanceof Error ? e.message : e}`);
|
|
247
|
+
return 1;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { createRequire } from "node:module";
|
|
5
|
+
|
|
6
|
+
const INSTALL_MARKER = ".its-thepoe-skills-install.json";
|
|
7
|
+
|
|
8
|
+
/** @type {Record<string, { env?: string, segments: string[] }>} */
|
|
9
|
+
export const AGENT_TARGETS = {
|
|
10
|
+
cursor: { segments: [".cursor", "skills"] },
|
|
11
|
+
claude: { segments: [".claude", "skills"] },
|
|
12
|
+
opencode: { segments: [".config", "opencode", "skills"] },
|
|
13
|
+
windsurf: { segments: [".codeium", "windsurf", "skills"] },
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @param {string} key
|
|
18
|
+
* @returns {string}
|
|
19
|
+
*/
|
|
20
|
+
export function agentBaseDir(key) {
|
|
21
|
+
const home = os.homedir();
|
|
22
|
+
const spec = AGENT_TARGETS[key];
|
|
23
|
+
if (!spec) throw new Error(`Unknown agent target: ${key}`);
|
|
24
|
+
return path.join(home, ...spec.segments);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {string[]} onlyKeys
|
|
29
|
+
*/
|
|
30
|
+
export function normalizeOnly(onlyKeys) {
|
|
31
|
+
const allowed = new Set(["cursor", "claude", "opencode", "windsurf"]);
|
|
32
|
+
if (onlyKeys.length === 0 || onlyKeys.includes("all")) {
|
|
33
|
+
return [...allowed];
|
|
34
|
+
}
|
|
35
|
+
for (const k of onlyKeys) {
|
|
36
|
+
if (!allowed.has(k)) throw new Error(`Invalid --only value: ${k}`);
|
|
37
|
+
}
|
|
38
|
+
return onlyKeys;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function exists(p) {
|
|
42
|
+
try {
|
|
43
|
+
fs.accessSync(p);
|
|
44
|
+
return true;
|
|
45
|
+
} catch {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @param {string} skillPackage
|
|
52
|
+
* @param {string} fromFile - use import.meta.url of caller for resolution context
|
|
53
|
+
* @returns {string} absolute path to package root
|
|
54
|
+
*/
|
|
55
|
+
export function resolveSkillRoot(skillPackage, fromFile) {
|
|
56
|
+
const require = createRequire(fromFile);
|
|
57
|
+
// Do not use `${pkg}/package.json` — many packages omit it from "exports".
|
|
58
|
+
// Resolve the package main (e.g. SKILL.md); its directory is the package root.
|
|
59
|
+
const entry = require.resolve(skillPackage);
|
|
60
|
+
return path.dirname(entry);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function readJson(p) {
|
|
64
|
+
return JSON.parse(fs.readFileSync(p, "utf8"));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function writeMarker(destDir, meta) {
|
|
68
|
+
const p = path.join(destDir, INSTALL_MARKER);
|
|
69
|
+
fs.writeFileSync(p, JSON.stringify(meta, null, 2) + "\n", "utf8");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @param {{ dest: string, source: string, dryRun: boolean, strategy: 'symlink'|'copy', skillName: string, skillPackage: string, version: string, log: (m:string)=>void }} opts
|
|
74
|
+
*/
|
|
75
|
+
function linkOneDest(opts) {
|
|
76
|
+
const { dest, source, dryRun, strategy, skillName, skillPackage, version, log } = opts;
|
|
77
|
+
const absSource = path.resolve(source);
|
|
78
|
+
|
|
79
|
+
if (strategy === "symlink") {
|
|
80
|
+
if (exists(dest)) {
|
|
81
|
+
const st = fs.lstatSync(dest);
|
|
82
|
+
if (st.isSymbolicLink()) {
|
|
83
|
+
let target = fs.readlinkSync(dest);
|
|
84
|
+
if (!path.isAbsolute(target)) {
|
|
85
|
+
target = path.resolve(path.dirname(dest), target);
|
|
86
|
+
}
|
|
87
|
+
if (path.resolve(target) === absSource) {
|
|
88
|
+
log(`SKIP ${dest} -> ${absSource}`);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
log(`${dryRun ? "DRY-RUN " : ""}REPLACE ${dest} (symlink -> new target)`);
|
|
92
|
+
if (!dryRun) fs.unlinkSync(dest);
|
|
93
|
+
} else if (st.isDirectory()) {
|
|
94
|
+
// copy-mode directory or manual folder — don't destroy
|
|
95
|
+
const marker = path.join(dest, INSTALL_MARKER);
|
|
96
|
+
if (exists(marker)) {
|
|
97
|
+
log(`${dryRun ? "DRY-RUN " : ""}REPLACE ${dest} (prior copy install)`);
|
|
98
|
+
if (!dryRun) fs.rmSync(dest, { recursive: true, force: true });
|
|
99
|
+
} else {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`Refusing to replace ${dest}: exists and is not a symlink managed by this tool`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
throw new Error(`Refusing to replace ${dest}: not a directory or symlink`);
|
|
106
|
+
}
|
|
107
|
+
} else {
|
|
108
|
+
log(`${dryRun ? "DRY-RUN " : ""}CREATE ${dest} -> ${absSource}`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (dryRun) return;
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
fs.symlinkSync(absSource, dest, "dir");
|
|
115
|
+
} catch (err) {
|
|
116
|
+
if (err && err.code === "EPERM" && process.platform === "win32") {
|
|
117
|
+
throw new Error(
|
|
118
|
+
`Symlink failed (EPERM). Re-run with --strategy copy, or enable Developer Mode / run as admin for symlinks.`
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
throw err;
|
|
122
|
+
}
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// copy
|
|
127
|
+
if (exists(dest)) {
|
|
128
|
+
log(`${dryRun ? "DRY-RUN " : ""}REPLACE ${dest} (copy)`);
|
|
129
|
+
if (!dryRun) fs.rmSync(dest, { recursive: true, force: true });
|
|
130
|
+
} else {
|
|
131
|
+
log(`${dryRun ? "DRY-RUN " : ""}CREATE ${dest} (copy)`);
|
|
132
|
+
}
|
|
133
|
+
if (dryRun) return;
|
|
134
|
+
fs.cpSync(absSource, dest, { recursive: true });
|
|
135
|
+
writeMarker(dest, {
|
|
136
|
+
managedBy: "@its-thepoe/skills",
|
|
137
|
+
skillName,
|
|
138
|
+
package: skillPackage,
|
|
139
|
+
version,
|
|
140
|
+
strategy: "copy",
|
|
141
|
+
installedAt: new Date().toISOString(),
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* @param {{ skillRoot: string, skillName: string, skillPackage: string, version: string, only: string[], strategy: 'symlink'|'copy', dryRun: boolean, log: (m:string)=>void }} p
|
|
147
|
+
*/
|
|
148
|
+
export function linkSkillToAgents(p) {
|
|
149
|
+
const { skillRoot, skillName, skillPackage, version, only, strategy, dryRun, log } = p;
|
|
150
|
+
if (!exists(path.join(skillRoot, "SKILL.md"))) {
|
|
151
|
+
throw new Error(`Missing SKILL.md under ${skillRoot}`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
for (const key of normalizeOnly(only)) {
|
|
155
|
+
const base = agentBaseDir(key);
|
|
156
|
+
const dest = path.join(base, skillName);
|
|
157
|
+
if (!dryRun) fs.mkdirSync(base, { recursive: true });
|
|
158
|
+
linkOneDest({
|
|
159
|
+
dest,
|
|
160
|
+
source: skillRoot,
|
|
161
|
+
dryRun,
|
|
162
|
+
strategy,
|
|
163
|
+
skillName,
|
|
164
|
+
skillPackage,
|
|
165
|
+
version,
|
|
166
|
+
log,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* @param {{ skillName: string, only: string[], dryRun: boolean, log: (m:string)=>void }} p
|
|
173
|
+
*/
|
|
174
|
+
export function removeSkillFromAgents(p) {
|
|
175
|
+
const { skillName, only, dryRun, log } = p;
|
|
176
|
+
for (const key of normalizeOnly(only)) {
|
|
177
|
+
const dest = path.join(agentBaseDir(key), skillName);
|
|
178
|
+
if (!exists(dest)) {
|
|
179
|
+
log(`SKIP (missing) ${dest}`);
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
const st = fs.lstatSync(dest);
|
|
183
|
+
if (st.isSymbolicLink()) {
|
|
184
|
+
log(`REMOVE ${dest}`);
|
|
185
|
+
if (!dryRun) fs.unlinkSync(dest);
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
if (st.isDirectory()) {
|
|
189
|
+
const marker = path.join(dest, INSTALL_MARKER);
|
|
190
|
+
if (exists(marker)) {
|
|
191
|
+
try {
|
|
192
|
+
const meta = readJson(marker);
|
|
193
|
+
if (meta.managedBy === "@its-thepoe/skills") {
|
|
194
|
+
log(`REMOVE ${dest} (copy install)`);
|
|
195
|
+
if (!dryRun) fs.rmSync(dest, { recursive: true, force: true });
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
} catch {
|
|
199
|
+
/* fall through */
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
log(`SKIP ${dest} (directory not managed; no marker)`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* @param {string} skillRoot
|
|
209
|
+
*/
|
|
210
|
+
export function readSkillVersion(skillRoot) {
|
|
211
|
+
const pj = path.join(skillRoot, "package.json");
|
|
212
|
+
if (!exists(pj)) return "0.0.0";
|
|
213
|
+
try {
|
|
214
|
+
return readJson(String(pj)).version ?? "0.0.0";
|
|
215
|
+
} catch {
|
|
216
|
+
return "0.0.0";
|
|
217
|
+
}
|
|
218
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@its-thepoe/skills",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Orchestrator CLI to install, check, sync, and remove @its-thepoe Agent Skills (Cursor, Claude Code, OpenCode, Windsurf).",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"agent-skills",
|
|
9
|
+
"cursor",
|
|
10
|
+
"claude-code",
|
|
11
|
+
"skills",
|
|
12
|
+
"its-thepoe"
|
|
13
|
+
],
|
|
14
|
+
"bin": {
|
|
15
|
+
"skills": "./bin/cli.mjs"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"bin",
|
|
19
|
+
"lib",
|
|
20
|
+
"skills.manifest.json",
|
|
21
|
+
"package.json"
|
|
22
|
+
],
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@its-thepoe/alt-text": "1.0.0",
|
|
25
|
+
"@its-thepoe/design-and-refine": "1.0.0",
|
|
26
|
+
"@its-thepoe/design-engineering": "1.0.0",
|
|
27
|
+
"@its-thepoe/design-motion-principles": "1.0.0",
|
|
28
|
+
"@its-thepoe/family-taste": "1.0.0",
|
|
29
|
+
"@its-thepoe/write-a-skill": "1.0.0"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skills": [
|
|
3
|
+
{ "name": "alt-text", "package": "@its-thepoe/alt-text" },
|
|
4
|
+
{ "name": "design-and-refine", "package": "@its-thepoe/design-and-refine" },
|
|
5
|
+
{ "name": "design-engineering", "package": "@its-thepoe/design-engineering" },
|
|
6
|
+
{ "name": "design-motion-principles", "package": "@its-thepoe/design-motion-principles" },
|
|
7
|
+
{ "name": "family-taste", "package": "@its-thepoe/family-taste" },
|
|
8
|
+
{ "name": "write-a-skill", "package": "@its-thepoe/write-a-skill" }
|
|
9
|
+
]
|
|
10
|
+
}
|