@hiver/skills 1.0.1
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 +394 -0
- package/build.js +15 -0
- package/collections/common/skills/create-prd/SKILL.md +43 -0
- package/collections/common/skills/discuss-problem/SKILL.md +55 -0
- package/collections/common/skills/explore-codebase/SKILL.md +79 -0
- package/collections/common/skills/prd-to-milestone/SKILL.md +40 -0
- package/collections/common/skills/refractor-and-clean/SKILL.md +41 -0
- package/collections/common/skills/solve-issue/SKILL.md +31 -0
- package/collections/common/skills/write-test-cases/SKILL.md +93 -0
- package/collections/web/agents/build-feature.md +42 -0
- package/collections/web/agents/build-milestone.md +61 -0
- package/collections/web/skills/build-component/SKILL.md +66 -0
- package/collections/web/skills/build-component/palette/README.md +3 -0
- package/collections/web/skills/build-component/palette/colors.md +87 -0
- package/collections/web/skills/build-component/references/best-practices.md +8 -0
- package/collections/web/skills/build-component/references/component-organization.md +21 -0
- package/collections/web/skills/build-component/references/figma-integration.md +5 -0
- package/collections/web/skills/build-component/references/icons.md +8 -0
- package/collections/web/skills/build-component/references/layout-and-structure.md +8 -0
- package/collections/web/skills/build-component/references/state-management.md +25 -0
- package/collections/web/skills/build-component/references/ui-kit-and-styling.md +13 -0
- package/collections/web/skills/build-component/typography/README.md +3 -0
- package/collections/web/skills/build-component/typography/variants.md +79 -0
- package/collections/web/skills/connect-api/SKILL.md +23 -0
- package/collections/web/skills/connect-api/references/api-call-structure.md +84 -0
- package/collections/web/skills/connect-api/references/best-practices.md +10 -0
- package/collections/web/skills/connect-api/references/data-fetchers.md +52 -0
- package/collections/web/skills/connect-api/references/error-handling.md +34 -0
- package/collections/web/skills/connect-api/references/hook-organization.md +11 -0
- package/collections/web/skills/connect-api/references/query-keys.md +21 -0
- package/collections/web/skills/connect-api/references/react-query-usage.md +83 -0
- package/collections/web/skills/connect-api/references/url-placeholders.md +17 -0
- package/collections/web/skills/scaffold-react-feature/SKILL.md +208 -0
- package/dist/cli.js +990 -0
- package/package.json +33 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,990 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli.jsx
|
|
4
|
+
import React10 from "react";
|
|
5
|
+
import { render, Box as Box9, Text as Text10 } from "ink";
|
|
6
|
+
|
|
7
|
+
// src/commands/Add.jsx
|
|
8
|
+
import React4, { useState as useState3, useEffect } from "react";
|
|
9
|
+
import { Box as Box3, Text as Text4, useApp } from "ink";
|
|
10
|
+
|
|
11
|
+
// src/components/Select.jsx
|
|
12
|
+
import React, { useState } from "react";
|
|
13
|
+
import { Box, Text, useInput } from "ink";
|
|
14
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
15
|
+
function Select({ items, onSelect, onBack, message }) {
|
|
16
|
+
const [cursor, setCursor] = useState(0);
|
|
17
|
+
useInput((_input, key) => {
|
|
18
|
+
if (key.upArrow) {
|
|
19
|
+
setCursor((prev) => prev > 0 ? prev - 1 : items.length - 1);
|
|
20
|
+
}
|
|
21
|
+
if (key.downArrow) {
|
|
22
|
+
setCursor((prev) => prev < items.length - 1 ? prev + 1 : 0);
|
|
23
|
+
}
|
|
24
|
+
if (key.return) {
|
|
25
|
+
onSelect(items[cursor].value);
|
|
26
|
+
}
|
|
27
|
+
if (key.escape && onBack) {
|
|
28
|
+
onBack();
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
32
|
+
/* @__PURE__ */ jsxs(Text, { bold: true, color: "cyan", children: [
|
|
33
|
+
"? ",
|
|
34
|
+
message,
|
|
35
|
+
/* @__PURE__ */ jsxs(Text, { color: "gray", children: [
|
|
36
|
+
" (\u2191\u2193 move, enter select",
|
|
37
|
+
onBack ? ", esc back" : "",
|
|
38
|
+
")"
|
|
39
|
+
] })
|
|
40
|
+
] }),
|
|
41
|
+
/* @__PURE__ */ jsx(Box, { flexDirection: "column", marginTop: 1, children: items.map((item, idx) => {
|
|
42
|
+
const isActive = idx === cursor;
|
|
43
|
+
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
44
|
+
/* @__PURE__ */ jsx(Text, { color: "cyan", children: isActive ? " \u276F " : " " }),
|
|
45
|
+
/* @__PURE__ */ jsx(Text, { color: isActive ? "yellow" : "white", bold: isActive, children: item.label })
|
|
46
|
+
] }, item.value);
|
|
47
|
+
}) })
|
|
48
|
+
] });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// src/components/MultiSelect.jsx
|
|
52
|
+
import React2, { useState as useState2 } from "react";
|
|
53
|
+
import { Box as Box2, Text as Text2, useInput as useInput2 } from "ink";
|
|
54
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
55
|
+
function MultiSelect({ items, onSubmit, onBack, message }) {
|
|
56
|
+
const [cursor, setCursor] = useState2(0);
|
|
57
|
+
const [selected, setSelected] = useState2(
|
|
58
|
+
new Set(items.filter((i) => i.checked).map((_, idx) => idx))
|
|
59
|
+
);
|
|
60
|
+
useInput2((input, key) => {
|
|
61
|
+
if (key.upArrow) {
|
|
62
|
+
setCursor((prev) => prev > 0 ? prev - 1 : items.length - 1);
|
|
63
|
+
}
|
|
64
|
+
if (key.downArrow) {
|
|
65
|
+
setCursor((prev) => prev < items.length - 1 ? prev + 1 : 0);
|
|
66
|
+
}
|
|
67
|
+
if (input === " ") {
|
|
68
|
+
setSelected((prev) => {
|
|
69
|
+
const next = new Set(prev);
|
|
70
|
+
if (next.has(cursor)) {
|
|
71
|
+
next.delete(cursor);
|
|
72
|
+
} else {
|
|
73
|
+
next.add(cursor);
|
|
74
|
+
}
|
|
75
|
+
return next;
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
if (key.return) {
|
|
79
|
+
const result = items.filter((_, idx) => selected.has(idx)).map((i) => i.value);
|
|
80
|
+
onSubmit(result);
|
|
81
|
+
}
|
|
82
|
+
if (key.escape && onBack) {
|
|
83
|
+
onBack();
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
|
|
87
|
+
/* @__PURE__ */ jsxs2(Text2, { bold: true, color: "cyan", children: [
|
|
88
|
+
"? ",
|
|
89
|
+
message,
|
|
90
|
+
/* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
|
|
91
|
+
" (\u2191\u2193 move, space select, enter confirm",
|
|
92
|
+
onBack ? ", esc back" : "",
|
|
93
|
+
")"
|
|
94
|
+
] })
|
|
95
|
+
] }),
|
|
96
|
+
/* @__PURE__ */ jsx2(Box2, { flexDirection: "column", marginTop: 1, children: items.map((item, idx) => {
|
|
97
|
+
const isActive = idx === cursor;
|
|
98
|
+
const isSelected = selected.has(idx);
|
|
99
|
+
const pointer = isActive ? "\u276F" : " ";
|
|
100
|
+
const check = isSelected ? "\u25C9" : "\u25EF";
|
|
101
|
+
const checkColor = isSelected ? "green" : "gray";
|
|
102
|
+
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginBottom: item.description ? 1 : 0, children: [
|
|
103
|
+
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
104
|
+
/* @__PURE__ */ jsxs2(Text2, { color: isActive ? "cyan" : void 0, children: [
|
|
105
|
+
pointer,
|
|
106
|
+
" "
|
|
107
|
+
] }),
|
|
108
|
+
/* @__PURE__ */ jsxs2(Text2, { color: checkColor, children: [
|
|
109
|
+
check,
|
|
110
|
+
" "
|
|
111
|
+
] }),
|
|
112
|
+
/* @__PURE__ */ jsx2(Text2, { color: isActive ? "yellow" : "white", bold: true, children: item.label })
|
|
113
|
+
] }),
|
|
114
|
+
item.description && /* @__PURE__ */ jsxs2(Text2, { color: isActive ? "cyan" : "gray", children: [
|
|
115
|
+
" ",
|
|
116
|
+
item.description
|
|
117
|
+
] })
|
|
118
|
+
] }, item.value);
|
|
119
|
+
}) })
|
|
120
|
+
] });
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// src/components/StatusMessage.jsx
|
|
124
|
+
import React3 from "react";
|
|
125
|
+
import { Text as Text3 } from "ink";
|
|
126
|
+
import { jsxs as jsxs3 } from "react/jsx-runtime";
|
|
127
|
+
function Success({ children }) {
|
|
128
|
+
return /* @__PURE__ */ jsxs3(Text3, { color: "green", children: [
|
|
129
|
+
"\u2713 ",
|
|
130
|
+
children
|
|
131
|
+
] });
|
|
132
|
+
}
|
|
133
|
+
function Warning({ children }) {
|
|
134
|
+
return /* @__PURE__ */ jsxs3(Text3, { color: "yellow", children: [
|
|
135
|
+
"\u26A0 ",
|
|
136
|
+
children
|
|
137
|
+
] });
|
|
138
|
+
}
|
|
139
|
+
function ErrorMsg({ children }) {
|
|
140
|
+
return /* @__PURE__ */ jsxs3(Text3, { color: "red", children: [
|
|
141
|
+
"\u2717 ",
|
|
142
|
+
children
|
|
143
|
+
] });
|
|
144
|
+
}
|
|
145
|
+
function Info({ children }) {
|
|
146
|
+
return /* @__PURE__ */ jsxs3(Text3, { color: "cyan", children: [
|
|
147
|
+
"\u2139 ",
|
|
148
|
+
children
|
|
149
|
+
] });
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// src/utils/targets.js
|
|
153
|
+
var targets = {
|
|
154
|
+
claude: {
|
|
155
|
+
name: "Claude Code",
|
|
156
|
+
skillsDir: ".claude/skills",
|
|
157
|
+
agentsDir: ".claude/agents"
|
|
158
|
+
},
|
|
159
|
+
cursor: {
|
|
160
|
+
name: "Cursor",
|
|
161
|
+
skillsDir: ".cursor/rules",
|
|
162
|
+
agentsDir: ".cursor/rules"
|
|
163
|
+
},
|
|
164
|
+
windsurf: {
|
|
165
|
+
name: "Windsurf",
|
|
166
|
+
skillsDir: ".windsurfrules",
|
|
167
|
+
agentsDir: ".windsurfrules"
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
var targetList = Object.entries(targets).map(([key, t]) => ({
|
|
171
|
+
label: t.name,
|
|
172
|
+
value: key
|
|
173
|
+
}));
|
|
174
|
+
|
|
175
|
+
// src/utils/collections.js
|
|
176
|
+
import path from "path";
|
|
177
|
+
import fs from "fs-extra";
|
|
178
|
+
import { fileURLToPath } from "url";
|
|
179
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
180
|
+
var __dirname = path.dirname(__filename);
|
|
181
|
+
var COLLECTIONS_DIR = path.resolve(__dirname, "../collections");
|
|
182
|
+
function getCollectionsDir() {
|
|
183
|
+
return COLLECTIONS_DIR;
|
|
184
|
+
}
|
|
185
|
+
function listCollections() {
|
|
186
|
+
if (!fs.existsSync(COLLECTIONS_DIR)) return [];
|
|
187
|
+
return fs.readdirSync(COLLECTIONS_DIR).filter(
|
|
188
|
+
(name) => fs.statSync(path.join(COLLECTIONS_DIR, name)).isDirectory()
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
function scanCollection(collectionName) {
|
|
192
|
+
const collectionDir = path.join(COLLECTIONS_DIR, collectionName);
|
|
193
|
+
const result = { skills: [], agents: [] };
|
|
194
|
+
const skillsDir = path.join(collectionDir, "skills");
|
|
195
|
+
if (fs.existsSync(skillsDir)) {
|
|
196
|
+
result.skills = fs.readdirSync(skillsDir).filter(
|
|
197
|
+
(name) => fs.statSync(path.join(skillsDir, name)).isDirectory()
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
const agentsDir = path.join(collectionDir, "agents");
|
|
201
|
+
if (fs.existsSync(agentsDir)) {
|
|
202
|
+
result.agents = fs.readdirSync(agentsDir).filter((name) => name.endsWith(".md")).map((name) => name.replace(".md", ""));
|
|
203
|
+
}
|
|
204
|
+
return result;
|
|
205
|
+
}
|
|
206
|
+
function parseFrontmatter(content) {
|
|
207
|
+
const match = content.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
208
|
+
if (!match) return {};
|
|
209
|
+
const fm = {};
|
|
210
|
+
for (const line of match[1].split("\n")) {
|
|
211
|
+
const idx = line.indexOf(":");
|
|
212
|
+
if (idx === -1) continue;
|
|
213
|
+
const key = line.slice(0, idx).trim();
|
|
214
|
+
let val = line.slice(idx + 1).trim();
|
|
215
|
+
if (val.startsWith("[") && val.endsWith("]")) {
|
|
216
|
+
val = val.slice(1, -1).split(",").map((s) => s.trim()).filter(Boolean);
|
|
217
|
+
}
|
|
218
|
+
fm[key] = val;
|
|
219
|
+
}
|
|
220
|
+
return fm;
|
|
221
|
+
}
|
|
222
|
+
function getSkillMeta(collectionName, skillName) {
|
|
223
|
+
const skillFile = path.join(COLLECTIONS_DIR, collectionName, "skills", skillName, "SKILL.md");
|
|
224
|
+
if (!fs.existsSync(skillFile)) return { description: "", dependencies: [] };
|
|
225
|
+
const content = fs.readFileSync(skillFile, "utf-8");
|
|
226
|
+
const fm = parseFrontmatter(content);
|
|
227
|
+
return {
|
|
228
|
+
description: fm.description || "",
|
|
229
|
+
dependencies: Array.isArray(fm.dependencies) ? fm.dependencies : []
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
function getAgentMeta(collectionName, agentName) {
|
|
233
|
+
const agentFile = path.join(COLLECTIONS_DIR, collectionName, "agents", `${agentName}.md`);
|
|
234
|
+
if (!fs.existsSync(agentFile)) return { description: "", dependencies: [] };
|
|
235
|
+
const content = fs.readFileSync(agentFile, "utf-8");
|
|
236
|
+
const fm = parseFrontmatter(content);
|
|
237
|
+
return {
|
|
238
|
+
description: fm.description || "",
|
|
239
|
+
dependencies: Array.isArray(fm.dependencies) ? fm.dependencies : []
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
function resolveDependencies(selected, selectedCollections) {
|
|
243
|
+
const installedNames = /* @__PURE__ */ new Set([
|
|
244
|
+
...selected.skills.map((s) => s.name),
|
|
245
|
+
...selected.agents.map((a) => a.name)
|
|
246
|
+
]);
|
|
247
|
+
const extra = { skills: [], agents: [] };
|
|
248
|
+
const allItems = [
|
|
249
|
+
...selected.skills.map((s) => ({ ...s, type: "skill" })),
|
|
250
|
+
...selected.agents.map((a) => ({ ...a, type: "agent" }))
|
|
251
|
+
];
|
|
252
|
+
for (const item of allItems) {
|
|
253
|
+
const meta = item.type === "skill" ? getSkillMeta(item.collection, item.name) : getAgentMeta(item.collection, item.name);
|
|
254
|
+
for (const dep of meta.dependencies) {
|
|
255
|
+
if (installedNames.has(dep)) continue;
|
|
256
|
+
const searchOrder = [.../* @__PURE__ */ new Set([...selectedCollections, ...listCollections()])];
|
|
257
|
+
for (const col of searchOrder) {
|
|
258
|
+
const { skills, agents } = scanCollection(col);
|
|
259
|
+
if (skills.includes(dep)) {
|
|
260
|
+
extra.skills.push({ collection: col, name: dep });
|
|
261
|
+
installedNames.add(dep);
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
if (agents.includes(dep)) {
|
|
265
|
+
extra.agents.push({ collection: col, name: dep });
|
|
266
|
+
installedNames.add(dep);
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return extra;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// src/utils/installer.js
|
|
276
|
+
import path2 from "path";
|
|
277
|
+
import fs2 from "fs-extra";
|
|
278
|
+
async function installSkill(collectionName, skillName, targetConfig, destRoot) {
|
|
279
|
+
const src = path2.join(getCollectionsDir(), collectionName, "skills", skillName);
|
|
280
|
+
const dest = path2.join(destRoot, targetConfig.skillsDir, skillName);
|
|
281
|
+
if (!fs2.existsSync(src)) return false;
|
|
282
|
+
await fs2.ensureDir(path2.dirname(dest));
|
|
283
|
+
await fs2.copy(src, dest, { overwrite: true });
|
|
284
|
+
return true;
|
|
285
|
+
}
|
|
286
|
+
async function installAgent(collectionName, agentName, targetConfig, destRoot) {
|
|
287
|
+
const src = path2.join(getCollectionsDir(), collectionName, "agents", `${agentName}.md`);
|
|
288
|
+
const dest = path2.join(destRoot, targetConfig.agentsDir, `${agentName}.md`);
|
|
289
|
+
if (!fs2.existsSync(src)) return false;
|
|
290
|
+
await fs2.ensureDir(path2.dirname(dest));
|
|
291
|
+
await fs2.copy(src, dest, { overwrite: true });
|
|
292
|
+
return true;
|
|
293
|
+
}
|
|
294
|
+
function listInstalled(targetConfig, destRoot) {
|
|
295
|
+
const result = { skills: [], agents: [] };
|
|
296
|
+
const skillsDir = path2.join(destRoot, targetConfig.skillsDir);
|
|
297
|
+
if (fs2.existsSync(skillsDir)) {
|
|
298
|
+
result.skills = fs2.readdirSync(skillsDir).filter((name) => {
|
|
299
|
+
const p = path2.join(skillsDir, name);
|
|
300
|
+
return fs2.statSync(p).isDirectory() && fs2.existsSync(path2.join(p, "SKILL.md"));
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
const agentsDir = path2.join(destRoot, targetConfig.agentsDir);
|
|
304
|
+
if (fs2.existsSync(agentsDir)) {
|
|
305
|
+
result.agents = fs2.readdirSync(agentsDir).filter((name) => name.endsWith(".md")).map((name) => name.replace(".md", ""));
|
|
306
|
+
}
|
|
307
|
+
return result;
|
|
308
|
+
}
|
|
309
|
+
async function removeSkill(skillName, targetConfig, destRoot) {
|
|
310
|
+
const skillPath = path2.join(destRoot, targetConfig.skillsDir, skillName);
|
|
311
|
+
if (fs2.existsSync(skillPath)) {
|
|
312
|
+
await fs2.remove(skillPath);
|
|
313
|
+
return true;
|
|
314
|
+
}
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
317
|
+
async function removeAgent(agentName, targetConfig, destRoot) {
|
|
318
|
+
const agentPath = path2.join(destRoot, targetConfig.agentsDir, `${agentName}.md`);
|
|
319
|
+
if (fs2.existsSync(agentPath)) {
|
|
320
|
+
await fs2.remove(agentPath);
|
|
321
|
+
return true;
|
|
322
|
+
}
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// src/commands/Add.jsx
|
|
327
|
+
import { jsx as jsx3, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
328
|
+
var STEPS = {
|
|
329
|
+
TARGET: "target",
|
|
330
|
+
COLLECTIONS: "collections",
|
|
331
|
+
TYPE: "type",
|
|
332
|
+
ITEMS: "items",
|
|
333
|
+
INSTALLING: "installing",
|
|
334
|
+
DONE: "done"
|
|
335
|
+
};
|
|
336
|
+
function Add({ directSkills }) {
|
|
337
|
+
const { exit } = useApp();
|
|
338
|
+
const [step, setStep] = useState3(STEPS.TARGET);
|
|
339
|
+
const [targetKey, setTargetKey] = useState3(null);
|
|
340
|
+
const [selectedCollection, setSelectedCollection] = useState3(null);
|
|
341
|
+
const [selectedType, setSelectedType] = useState3(null);
|
|
342
|
+
const [toInstall, setToInstall] = useState3({ skills: [], agents: [] });
|
|
343
|
+
const [results, setResults] = useState3({ skills: 0, agents: 0, deps: [] });
|
|
344
|
+
const destRoot = process.cwd();
|
|
345
|
+
useEffect(() => {
|
|
346
|
+
if (directSkills && directSkills.length > 0 && targetKey) {
|
|
347
|
+
installByName(directSkills, targets[targetKey], destRoot).then((res) => {
|
|
348
|
+
setResults(res);
|
|
349
|
+
setStep(STEPS.DONE);
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
}, [targetKey]);
|
|
353
|
+
async function installByName(names, targetConfig, dest) {
|
|
354
|
+
const collections = listCollections();
|
|
355
|
+
let skillCount = 0;
|
|
356
|
+
let agentCount = 0;
|
|
357
|
+
for (const name of names) {
|
|
358
|
+
for (const col of collections) {
|
|
359
|
+
const { skills, agents } = scanCollection(col);
|
|
360
|
+
if (skills.includes(name)) {
|
|
361
|
+
if (await installSkill(col, name, targetConfig, dest)) skillCount++;
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
if (agents.includes(name)) {
|
|
365
|
+
if (await installAgent(col, name, targetConfig, dest)) agentCount++;
|
|
366
|
+
break;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
return { skills: skillCount, agents: agentCount, deps: [] };
|
|
371
|
+
}
|
|
372
|
+
function getAvailableTypes(col) {
|
|
373
|
+
const { skills, agents } = scanCollection(col);
|
|
374
|
+
const types = [];
|
|
375
|
+
if (skills.length > 0) types.push({ label: `Skills (${skills.length})`, value: "skills" });
|
|
376
|
+
if (agents.length > 0) types.push({ label: `Agents (${agents.length})`, value: "agents" });
|
|
377
|
+
return types;
|
|
378
|
+
}
|
|
379
|
+
function handleTargetSelect(key) {
|
|
380
|
+
setTargetKey(key);
|
|
381
|
+
if (directSkills && directSkills.length > 0) return;
|
|
382
|
+
setStep(STEPS.COLLECTIONS);
|
|
383
|
+
}
|
|
384
|
+
function handleCollectionSelect(col) {
|
|
385
|
+
setSelectedCollection(col);
|
|
386
|
+
setToInstall({ skills: [], agents: [] });
|
|
387
|
+
const types = getAvailableTypes(col);
|
|
388
|
+
if (types.length === 1) {
|
|
389
|
+
setSelectedType(types[0].value);
|
|
390
|
+
setStep(STEPS.ITEMS);
|
|
391
|
+
} else if (types.length === 0) {
|
|
392
|
+
setStep(STEPS.DONE);
|
|
393
|
+
} else {
|
|
394
|
+
setStep(STEPS.TYPE);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
function handleTypeSelect(type) {
|
|
398
|
+
setSelectedType(type);
|
|
399
|
+
setStep(STEPS.ITEMS);
|
|
400
|
+
}
|
|
401
|
+
function handleItemsSelect(names) {
|
|
402
|
+
const col = selectedCollection;
|
|
403
|
+
if (selectedType === "skills") {
|
|
404
|
+
const newSkills = names.map((name) => ({ collection: col, name }));
|
|
405
|
+
setToInstall((prev) => ({ ...prev, skills: [...prev.skills, ...newSkills] }));
|
|
406
|
+
} else {
|
|
407
|
+
const newAgents = names.map((name) => ({ collection: col, name }));
|
|
408
|
+
setToInstall((prev) => ({ ...prev, agents: [...prev.agents, ...newAgents] }));
|
|
409
|
+
}
|
|
410
|
+
setStep(STEPS.INSTALLING);
|
|
411
|
+
}
|
|
412
|
+
function handleTargetBack() {
|
|
413
|
+
exit();
|
|
414
|
+
}
|
|
415
|
+
function handleCollectionsBack() {
|
|
416
|
+
setTargetKey(null);
|
|
417
|
+
setStep(STEPS.TARGET);
|
|
418
|
+
}
|
|
419
|
+
function handleTypeBack() {
|
|
420
|
+
setSelectedCollection(null);
|
|
421
|
+
setStep(STEPS.COLLECTIONS);
|
|
422
|
+
}
|
|
423
|
+
function handleItemsBack() {
|
|
424
|
+
const types = getAvailableTypes(selectedCollection);
|
|
425
|
+
if (types.length > 1) {
|
|
426
|
+
setSelectedType(null);
|
|
427
|
+
setStep(STEPS.TYPE);
|
|
428
|
+
} else {
|
|
429
|
+
setSelectedCollection(null);
|
|
430
|
+
setStep(STEPS.COLLECTIONS);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
useEffect(() => {
|
|
434
|
+
if (step !== STEPS.INSTALLING) return;
|
|
435
|
+
async function doInstall() {
|
|
436
|
+
const targetConfig = targets[targetKey];
|
|
437
|
+
const deps = resolveDependencies(toInstall, [selectedCollection]);
|
|
438
|
+
const allSkills = [...toInstall.skills, ...deps.skills];
|
|
439
|
+
const allAgents = [...toInstall.agents, ...deps.agents];
|
|
440
|
+
let skillCount = 0;
|
|
441
|
+
let agentCount = 0;
|
|
442
|
+
for (const { collection, name } of allSkills) {
|
|
443
|
+
if (await installSkill(collection, name, targetConfig, destRoot)) skillCount++;
|
|
444
|
+
}
|
|
445
|
+
for (const { collection, name } of allAgents) {
|
|
446
|
+
if (await installAgent(collection, name, targetConfig, destRoot)) agentCount++;
|
|
447
|
+
}
|
|
448
|
+
setResults({
|
|
449
|
+
skills: skillCount,
|
|
450
|
+
agents: agentCount,
|
|
451
|
+
deps: [...deps.skills, ...deps.agents]
|
|
452
|
+
});
|
|
453
|
+
setStep(STEPS.DONE);
|
|
454
|
+
}
|
|
455
|
+
doInstall();
|
|
456
|
+
}, [step]);
|
|
457
|
+
if (step === STEPS.TARGET) {
|
|
458
|
+
return /* @__PURE__ */ jsx3(
|
|
459
|
+
Select,
|
|
460
|
+
{
|
|
461
|
+
items: targetList,
|
|
462
|
+
onSelect: handleTargetSelect,
|
|
463
|
+
onBack: handleTargetBack,
|
|
464
|
+
message: "Where to install? (target LLM/IDE)"
|
|
465
|
+
}
|
|
466
|
+
);
|
|
467
|
+
}
|
|
468
|
+
if (step === STEPS.COLLECTIONS) {
|
|
469
|
+
const collections = listCollections();
|
|
470
|
+
const items = collections.map((c) => ({ label: c, value: c }));
|
|
471
|
+
return /* @__PURE__ */ jsx3(
|
|
472
|
+
Select,
|
|
473
|
+
{
|
|
474
|
+
items,
|
|
475
|
+
onSelect: handleCollectionSelect,
|
|
476
|
+
onBack: handleCollectionsBack,
|
|
477
|
+
message: "Select collection:"
|
|
478
|
+
}
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
if (step === STEPS.TYPE) {
|
|
482
|
+
const types = getAvailableTypes(selectedCollection);
|
|
483
|
+
return /* @__PURE__ */ jsx3(
|
|
484
|
+
Select,
|
|
485
|
+
{
|
|
486
|
+
items: types,
|
|
487
|
+
onSelect: handleTypeSelect,
|
|
488
|
+
onBack: handleTypeBack,
|
|
489
|
+
message: `What to install from "${selectedCollection}"?`
|
|
490
|
+
}
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
if (step === STEPS.ITEMS) {
|
|
494
|
+
const col = selectedCollection;
|
|
495
|
+
const { skills, agents } = scanCollection(col);
|
|
496
|
+
if (selectedType === "skills") {
|
|
497
|
+
const items = skills.map((s) => {
|
|
498
|
+
const meta = getSkillMeta(col, s);
|
|
499
|
+
return { label: s, value: s, description: meta.description, checked: false };
|
|
500
|
+
});
|
|
501
|
+
return /* @__PURE__ */ jsx3(
|
|
502
|
+
MultiSelect,
|
|
503
|
+
{
|
|
504
|
+
items,
|
|
505
|
+
onSubmit: handleItemsSelect,
|
|
506
|
+
onBack: handleItemsBack,
|
|
507
|
+
message: `Select skills from "${col}":`
|
|
508
|
+
}
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
if (selectedType === "agents") {
|
|
512
|
+
const items = agents.map((a) => {
|
|
513
|
+
const meta = getAgentMeta(col, a);
|
|
514
|
+
return { label: a, value: a, description: meta.description, checked: false };
|
|
515
|
+
});
|
|
516
|
+
return /* @__PURE__ */ jsx3(
|
|
517
|
+
MultiSelect,
|
|
518
|
+
{
|
|
519
|
+
items,
|
|
520
|
+
onSubmit: handleItemsSelect,
|
|
521
|
+
onBack: handleItemsBack,
|
|
522
|
+
message: `Select agents from "${col}":`
|
|
523
|
+
}
|
|
524
|
+
);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
if (step === STEPS.INSTALLING) {
|
|
528
|
+
return /* @__PURE__ */ jsx3(Text4, { color: "yellow", children: "Installing..." });
|
|
529
|
+
}
|
|
530
|
+
if (step === STEPS.DONE) {
|
|
531
|
+
setTimeout(() => exit(), 100);
|
|
532
|
+
return /* @__PURE__ */ jsxs4(Box3, { flexDirection: "column", marginTop: 1, children: [
|
|
533
|
+
results.deps.length > 0 && /* @__PURE__ */ jsxs4(Box3, { flexDirection: "column", marginBottom: 1, children: [
|
|
534
|
+
/* @__PURE__ */ jsx3(Info, { children: "Auto-installed dependencies:" }),
|
|
535
|
+
results.deps.map((d) => /* @__PURE__ */ jsxs4(Text4, { color: "cyan", children: [
|
|
536
|
+
" + ",
|
|
537
|
+
d.name,
|
|
538
|
+
" (from ",
|
|
539
|
+
d.collection,
|
|
540
|
+
")"
|
|
541
|
+
] }, d.name))
|
|
542
|
+
] }),
|
|
543
|
+
/* @__PURE__ */ jsxs4(Success, { children: [
|
|
544
|
+
"Installed ",
|
|
545
|
+
results.skills,
|
|
546
|
+
" skill(s), ",
|
|
547
|
+
results.agents,
|
|
548
|
+
" agent(s)",
|
|
549
|
+
targetKey && ` to ${targets[targetKey].skillsDir}/`
|
|
550
|
+
] })
|
|
551
|
+
] });
|
|
552
|
+
}
|
|
553
|
+
return null;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// src/commands/List.jsx
|
|
557
|
+
import React5 from "react";
|
|
558
|
+
import { Box as Box4, Text as Text5, useApp as useApp2 } from "ink";
|
|
559
|
+
import { jsx as jsx4, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
560
|
+
function List() {
|
|
561
|
+
const { exit } = useApp2();
|
|
562
|
+
const collections = listCollections();
|
|
563
|
+
setTimeout(() => exit(), 100);
|
|
564
|
+
if (collections.length === 0) {
|
|
565
|
+
return /* @__PURE__ */ jsx4(Text5, { color: "yellow", children: "No collections found." });
|
|
566
|
+
}
|
|
567
|
+
return /* @__PURE__ */ jsxs5(Box4, { flexDirection: "column", children: [
|
|
568
|
+
/* @__PURE__ */ jsxs5(Text5, { bold: true, color: "cyan", children: [
|
|
569
|
+
"\n",
|
|
570
|
+
"Available skills & agents",
|
|
571
|
+
"\n"
|
|
572
|
+
] }),
|
|
573
|
+
collections.map((col) => {
|
|
574
|
+
const { skills, agents } = scanCollection(col);
|
|
575
|
+
if (skills.length === 0 && agents.length === 0) return null;
|
|
576
|
+
return /* @__PURE__ */ jsxs5(Box4, { flexDirection: "column", marginBottom: 1, children: [
|
|
577
|
+
/* @__PURE__ */ jsxs5(Text5, { bold: true, color: "yellow", children: [
|
|
578
|
+
" ",
|
|
579
|
+
"\u{1F4E6} ",
|
|
580
|
+
col
|
|
581
|
+
] }),
|
|
582
|
+
skills.length > 0 && /* @__PURE__ */ jsxs5(Box4, { flexDirection: "column", marginLeft: 2, marginTop: 1, children: [
|
|
583
|
+
/* @__PURE__ */ jsxs5(Text5, { bold: true, color: "magenta", children: [
|
|
584
|
+
" ",
|
|
585
|
+
"Skills"
|
|
586
|
+
] }),
|
|
587
|
+
skills.map((s) => {
|
|
588
|
+
const meta = getSkillMeta(col, s);
|
|
589
|
+
return /* @__PURE__ */ jsxs5(Box4, { flexDirection: "column", marginBottom: 1, marginLeft: 2, children: [
|
|
590
|
+
/* @__PURE__ */ jsxs5(Text5, { bold: true, color: "green", children: [
|
|
591
|
+
" ",
|
|
592
|
+
s
|
|
593
|
+
] }),
|
|
594
|
+
meta.description && /* @__PURE__ */ jsxs5(Text5, { color: "gray", children: [
|
|
595
|
+
" ",
|
|
596
|
+
meta.description
|
|
597
|
+
] })
|
|
598
|
+
] }, s);
|
|
599
|
+
})
|
|
600
|
+
] }),
|
|
601
|
+
agents.length > 0 && /* @__PURE__ */ jsxs5(Box4, { flexDirection: "column", marginLeft: 2, marginTop: 1, children: [
|
|
602
|
+
/* @__PURE__ */ jsxs5(Text5, { bold: true, color: "magenta", children: [
|
|
603
|
+
" ",
|
|
604
|
+
"Agents"
|
|
605
|
+
] }),
|
|
606
|
+
agents.map((a) => {
|
|
607
|
+
const meta = getAgentMeta(col, a);
|
|
608
|
+
return /* @__PURE__ */ jsxs5(Box4, { flexDirection: "column", marginBottom: 1, marginLeft: 2, children: [
|
|
609
|
+
/* @__PURE__ */ jsxs5(Text5, { bold: true, color: "blue", children: [
|
|
610
|
+
" ",
|
|
611
|
+
a
|
|
612
|
+
] }),
|
|
613
|
+
meta.description && /* @__PURE__ */ jsxs5(Text5, { color: "gray", children: [
|
|
614
|
+
" ",
|
|
615
|
+
meta.description
|
|
616
|
+
] })
|
|
617
|
+
] }, a);
|
|
618
|
+
})
|
|
619
|
+
] })
|
|
620
|
+
] }, col);
|
|
621
|
+
})
|
|
622
|
+
] });
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// src/commands/Update.jsx
|
|
626
|
+
import React8, { useState as useState4, useEffect as useEffect2 } from "react";
|
|
627
|
+
import { Box as Box7, Text as Text8, useApp as useApp3 } from "ink";
|
|
628
|
+
import path3 from "path";
|
|
629
|
+
import fs3 from "fs-extra";
|
|
630
|
+
|
|
631
|
+
// src/components/Confirm.jsx
|
|
632
|
+
import React6 from "react";
|
|
633
|
+
import { Box as Box5, Text as Text6, useInput as useInput3 } from "ink";
|
|
634
|
+
import { jsx as jsx5, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
635
|
+
function Confirm({ message, onConfirm, onBack }) {
|
|
636
|
+
useInput3((input, key) => {
|
|
637
|
+
if (input === "y" || input === "Y") {
|
|
638
|
+
onConfirm(true);
|
|
639
|
+
}
|
|
640
|
+
if (input === "n" || input === "N") {
|
|
641
|
+
onConfirm(false);
|
|
642
|
+
}
|
|
643
|
+
if (key.escape && onBack) {
|
|
644
|
+
onBack();
|
|
645
|
+
}
|
|
646
|
+
});
|
|
647
|
+
return /* @__PURE__ */ jsx5(Box5, { children: /* @__PURE__ */ jsxs6(Text6, { bold: true, color: "cyan", children: [
|
|
648
|
+
"? ",
|
|
649
|
+
message,
|
|
650
|
+
/* @__PURE__ */ jsxs6(Text6, { color: "gray", children: [
|
|
651
|
+
" (y/n",
|
|
652
|
+
onBack ? ", esc back" : "",
|
|
653
|
+
")"
|
|
654
|
+
] })
|
|
655
|
+
] }) });
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
// src/components/DiffView.jsx
|
|
659
|
+
import React7 from "react";
|
|
660
|
+
import { Box as Box6, Text as Text7 } from "ink";
|
|
661
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
662
|
+
function DiffView({ patch }) {
|
|
663
|
+
const lines = patch.split("\n");
|
|
664
|
+
return /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", marginLeft: 2, children: lines.map((line, idx) => {
|
|
665
|
+
let color = void 0;
|
|
666
|
+
if (line.startsWith("+") && !line.startsWith("+++")) color = "green";
|
|
667
|
+
else if (line.startsWith("-") && !line.startsWith("---")) color = "red";
|
|
668
|
+
else if (line.startsWith("@@")) color = "cyan";
|
|
669
|
+
return /* @__PURE__ */ jsx6(Text7, { color, children: line }, idx);
|
|
670
|
+
}) });
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
// src/utils/diffUtil.js
|
|
674
|
+
import { createTwoFilesPatch } from "diff";
|
|
675
|
+
function computeDiff(filePath, localContent, latestContent) {
|
|
676
|
+
return createTwoFilesPatch(
|
|
677
|
+
`local/${filePath}`,
|
|
678
|
+
`latest/${filePath}`,
|
|
679
|
+
localContent,
|
|
680
|
+
latestContent,
|
|
681
|
+
"",
|
|
682
|
+
""
|
|
683
|
+
);
|
|
684
|
+
}
|
|
685
|
+
function hasChanges(localContent, latestContent) {
|
|
686
|
+
return localContent.trim() !== latestContent.trim();
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// src/commands/Update.jsx
|
|
690
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
691
|
+
var STEPS2 = {
|
|
692
|
+
TARGET: "target",
|
|
693
|
+
CHECKING: "checking",
|
|
694
|
+
CONFIRM: "confirm",
|
|
695
|
+
DONE: "done"
|
|
696
|
+
};
|
|
697
|
+
function Update() {
|
|
698
|
+
const { exit } = useApp3();
|
|
699
|
+
const [step, setStep] = useState4(STEPS2.TARGET);
|
|
700
|
+
const [targetKey, setTargetKey] = useState4(null);
|
|
701
|
+
const [changedItems, setChangedItems] = useState4([]);
|
|
702
|
+
const [currentIdx, setCurrentIdx] = useState4(0);
|
|
703
|
+
const [updatedCount, setUpdatedCount] = useState4(0);
|
|
704
|
+
const [skippedCount, setSkippedCount] = useState4(0);
|
|
705
|
+
const destRoot = process.cwd();
|
|
706
|
+
function findSource(name, type) {
|
|
707
|
+
for (const col of listCollections()) {
|
|
708
|
+
const { skills, agents } = scanCollection(col);
|
|
709
|
+
if (type === "skill" && skills.includes(name)) return { collection: col };
|
|
710
|
+
if (type === "agent" && agents.includes(name)) return { collection: col };
|
|
711
|
+
}
|
|
712
|
+
return null;
|
|
713
|
+
}
|
|
714
|
+
useEffect2(() => {
|
|
715
|
+
if (step !== STEPS2.CHECKING) return;
|
|
716
|
+
const targetConfig = targets[targetKey];
|
|
717
|
+
const installed = listInstalled(targetConfig, destRoot);
|
|
718
|
+
const changed = [];
|
|
719
|
+
for (const skillName of installed.skills) {
|
|
720
|
+
const source = findSource(skillName, "skill");
|
|
721
|
+
if (!source) continue;
|
|
722
|
+
const localFile = path3.join(destRoot, targetConfig.skillsDir, skillName, "SKILL.md");
|
|
723
|
+
const latestFile = path3.join(getCollectionsDir(), source.collection, "skills", skillName, "SKILL.md");
|
|
724
|
+
if (!fs3.existsSync(localFile) || !fs3.existsSync(latestFile)) continue;
|
|
725
|
+
const localContent = fs3.readFileSync(localFile, "utf-8");
|
|
726
|
+
const latestContent = fs3.readFileSync(latestFile, "utf-8");
|
|
727
|
+
if (hasChanges(localContent, latestContent)) {
|
|
728
|
+
changed.push({
|
|
729
|
+
name: skillName,
|
|
730
|
+
type: "skill",
|
|
731
|
+
collection: source.collection,
|
|
732
|
+
patch: computeDiff("SKILL.md", localContent, latestContent)
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
for (const agentName of installed.agents) {
|
|
737
|
+
const source = findSource(agentName, "agent");
|
|
738
|
+
if (!source) continue;
|
|
739
|
+
const localFile = path3.join(destRoot, targetConfig.agentsDir, `${agentName}.md`);
|
|
740
|
+
const latestFile = path3.join(getCollectionsDir(), source.collection, "agents", `${agentName}.md`);
|
|
741
|
+
if (!fs3.existsSync(localFile) || !fs3.existsSync(latestFile)) continue;
|
|
742
|
+
const localContent = fs3.readFileSync(localFile, "utf-8");
|
|
743
|
+
const latestContent = fs3.readFileSync(latestFile, "utf-8");
|
|
744
|
+
if (hasChanges(localContent, latestContent)) {
|
|
745
|
+
changed.push({
|
|
746
|
+
name: agentName,
|
|
747
|
+
type: "agent",
|
|
748
|
+
collection: source.collection,
|
|
749
|
+
patch: computeDiff(`${agentName}.md`, localContent, latestContent)
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
setChangedItems(changed);
|
|
754
|
+
if (changed.length === 0) {
|
|
755
|
+
setStep(STEPS2.DONE);
|
|
756
|
+
} else {
|
|
757
|
+
setCurrentIdx(0);
|
|
758
|
+
setStep(STEPS2.CONFIRM);
|
|
759
|
+
}
|
|
760
|
+
}, [step]);
|
|
761
|
+
function handleTargetSelect(key) {
|
|
762
|
+
setTargetKey(key);
|
|
763
|
+
setStep(STEPS2.CHECKING);
|
|
764
|
+
}
|
|
765
|
+
function handleConfirm(yes) {
|
|
766
|
+
const item = changedItems[currentIdx];
|
|
767
|
+
const targetConfig = targets[targetKey];
|
|
768
|
+
if (yes) {
|
|
769
|
+
if (item.type === "skill") {
|
|
770
|
+
const src = path3.join(getCollectionsDir(), item.collection, "skills", item.name);
|
|
771
|
+
const dest = path3.join(destRoot, targetConfig.skillsDir, item.name);
|
|
772
|
+
fs3.copySync(src, dest, { overwrite: true });
|
|
773
|
+
} else {
|
|
774
|
+
const src = path3.join(getCollectionsDir(), item.collection, "agents", `${item.name}.md`);
|
|
775
|
+
const dest = path3.join(destRoot, targetConfig.agentsDir, `${item.name}.md`);
|
|
776
|
+
fs3.copySync(src, dest, { overwrite: true });
|
|
777
|
+
}
|
|
778
|
+
setUpdatedCount((c) => c + 1);
|
|
779
|
+
} else {
|
|
780
|
+
setSkippedCount((c) => c + 1);
|
|
781
|
+
}
|
|
782
|
+
if (currentIdx + 1 < changedItems.length) {
|
|
783
|
+
setCurrentIdx((i) => i + 1);
|
|
784
|
+
} else {
|
|
785
|
+
setStep(STEPS2.DONE);
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
if (step === STEPS2.TARGET) {
|
|
789
|
+
return /* @__PURE__ */ jsx7(Select, { items: targetList, onSelect: handleTargetSelect, onBack: () => exit(), message: "Which target to update?" });
|
|
790
|
+
}
|
|
791
|
+
if (step === STEPS2.CHECKING) {
|
|
792
|
+
return /* @__PURE__ */ jsx7(Text8, { color: "yellow", children: "Checking for updates..." });
|
|
793
|
+
}
|
|
794
|
+
if (step === STEPS2.CONFIRM) {
|
|
795
|
+
const item = changedItems[currentIdx];
|
|
796
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
797
|
+
/* @__PURE__ */ jsxs7(Warning, { children: [
|
|
798
|
+
item.name,
|
|
799
|
+
": local differs from latest"
|
|
800
|
+
] }),
|
|
801
|
+
/* @__PURE__ */ jsx7(DiffView, { patch: item.patch }),
|
|
802
|
+
/* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx7(Confirm, { message: `Update ${item.name}?`, onConfirm: handleConfirm }) })
|
|
803
|
+
] });
|
|
804
|
+
}
|
|
805
|
+
if (step === STEPS2.DONE) {
|
|
806
|
+
setTimeout(() => exit(), 100);
|
|
807
|
+
if (changedItems.length === 0) {
|
|
808
|
+
return /* @__PURE__ */ jsx7(Success, { children: "All installed skills are up to date." });
|
|
809
|
+
}
|
|
810
|
+
return /* @__PURE__ */ jsxs7(Success, { children: [
|
|
811
|
+
updatedCount,
|
|
812
|
+
" updated, ",
|
|
813
|
+
skippedCount,
|
|
814
|
+
" skipped"
|
|
815
|
+
] });
|
|
816
|
+
}
|
|
817
|
+
return null;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
// src/commands/Remove.jsx
|
|
821
|
+
import React9, { useState as useState5 } from "react";
|
|
822
|
+
import { Box as Box8, Text as Text9, useApp as useApp4 } from "ink";
|
|
823
|
+
import path4 from "path";
|
|
824
|
+
import fs4 from "fs-extra";
|
|
825
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
826
|
+
var STEPS3 = {
|
|
827
|
+
TARGET: "target",
|
|
828
|
+
CONFIRM: "confirm",
|
|
829
|
+
DONE: "done"
|
|
830
|
+
};
|
|
831
|
+
function Remove({ skillNames }) {
|
|
832
|
+
const { exit } = useApp4();
|
|
833
|
+
const [step, setStep] = useState5(STEPS3.TARGET);
|
|
834
|
+
const [targetKey, setTargetKey] = useState5(null);
|
|
835
|
+
const [currentIdx, setCurrentIdx] = useState5(0);
|
|
836
|
+
const [removedCount, setRemovedCount] = useState5(0);
|
|
837
|
+
const [notFound, setNotFound] = useState5([]);
|
|
838
|
+
const destRoot = process.cwd();
|
|
839
|
+
function handleTargetSelect(key) {
|
|
840
|
+
setTargetKey(key);
|
|
841
|
+
const targetConfig = targets[key];
|
|
842
|
+
const missing = [];
|
|
843
|
+
for (const name of skillNames) {
|
|
844
|
+
const skillPath = path4.join(destRoot, targetConfig.skillsDir, name);
|
|
845
|
+
const agentPath = path4.join(destRoot, targetConfig.agentsDir, `${name}.md`);
|
|
846
|
+
if (!fs4.existsSync(skillPath) && !fs4.existsSync(agentPath)) {
|
|
847
|
+
missing.push(name);
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
setNotFound(missing);
|
|
851
|
+
const existing = skillNames.filter((n) => !missing.includes(n));
|
|
852
|
+
if (existing.length === 0) {
|
|
853
|
+
setStep(STEPS3.DONE);
|
|
854
|
+
} else {
|
|
855
|
+
setStep(STEPS3.CONFIRM);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
async function handleConfirm(yes) {
|
|
859
|
+
if (yes) {
|
|
860
|
+
const name = getExisting()[currentIdx];
|
|
861
|
+
const targetConfig = targets[targetKey];
|
|
862
|
+
const skillRemoved = await removeSkill(name, targetConfig, destRoot);
|
|
863
|
+
const agentRemoved = !skillRemoved ? await removeAgent(name, targetConfig, destRoot) : false;
|
|
864
|
+
if (skillRemoved || agentRemoved) {
|
|
865
|
+
setRemovedCount((c) => c + 1);
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
if (currentIdx + 1 < getExisting().length) {
|
|
869
|
+
setCurrentIdx((i) => i + 1);
|
|
870
|
+
} else {
|
|
871
|
+
setStep(STEPS3.DONE);
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
function getExisting() {
|
|
875
|
+
return skillNames.filter((n) => !notFound.includes(n));
|
|
876
|
+
}
|
|
877
|
+
if (step === STEPS3.TARGET) {
|
|
878
|
+
return /* @__PURE__ */ jsx8(
|
|
879
|
+
Select,
|
|
880
|
+
{
|
|
881
|
+
items: targetList,
|
|
882
|
+
onSelect: handleTargetSelect,
|
|
883
|
+
onBack: () => exit(),
|
|
884
|
+
message: "Which target to remove from?"
|
|
885
|
+
}
|
|
886
|
+
);
|
|
887
|
+
}
|
|
888
|
+
if (step === STEPS3.CONFIRM) {
|
|
889
|
+
const name = getExisting()[currentIdx];
|
|
890
|
+
return /* @__PURE__ */ jsx8(Confirm, { message: `Remove "${name}"?`, onConfirm: handleConfirm });
|
|
891
|
+
}
|
|
892
|
+
if (step === STEPS3.DONE) {
|
|
893
|
+
setTimeout(() => exit(), 100);
|
|
894
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
895
|
+
notFound.map((n) => /* @__PURE__ */ jsxs8(ErrorMsg, { children: [
|
|
896
|
+
"Not found: ",
|
|
897
|
+
n
|
|
898
|
+
] }, n)),
|
|
899
|
+
/* @__PURE__ */ jsxs8(Success, { children: [
|
|
900
|
+
removedCount,
|
|
901
|
+
" removed"
|
|
902
|
+
] })
|
|
903
|
+
] });
|
|
904
|
+
}
|
|
905
|
+
return null;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
// src/cli.jsx
|
|
909
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
910
|
+
var args = process.argv.slice(2);
|
|
911
|
+
var command = args[0];
|
|
912
|
+
var restArgs = args.slice(1);
|
|
913
|
+
function Help() {
|
|
914
|
+
const commands = [
|
|
915
|
+
{ name: "add [skills...]", desc: "Install skills/agents (interactive if no args)" },
|
|
916
|
+
{ name: "list", desc: "List all available skills and agents" },
|
|
917
|
+
{ name: "update", desc: "Update installed skills to latest version" },
|
|
918
|
+
{ name: "remove <skills>", desc: "Remove installed skills/agents" },
|
|
919
|
+
{ name: "help", desc: "Show this help message" }
|
|
920
|
+
];
|
|
921
|
+
const examples = [
|
|
922
|
+
"npx @hiver/skills add",
|
|
923
|
+
"npx @hiver/skills add discuss-problem create-prd",
|
|
924
|
+
"npx @hiver/skills list",
|
|
925
|
+
"npx @hiver/skills update",
|
|
926
|
+
"npx @hiver/skills remove discuss-problem"
|
|
927
|
+
];
|
|
928
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginTop: 1, marginBottom: 1, children: [
|
|
929
|
+
/* @__PURE__ */ jsxs9(Box9, { marginBottom: 1, children: [
|
|
930
|
+
/* @__PURE__ */ jsx9(Text10, { bold: true, color: "cyan", children: "@hiver/skills" }),
|
|
931
|
+
/* @__PURE__ */ jsx9(Text10, { color: "gray", children: " \u2014 Shared Claude Code skills & agents" })
|
|
932
|
+
] }),
|
|
933
|
+
/* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginBottom: 1, children: [
|
|
934
|
+
/* @__PURE__ */ jsx9(Text10, { bold: true, color: "yellow", children: "Commands" }),
|
|
935
|
+
commands.map((cmd) => /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginLeft: 2, marginBottom: 1, children: [
|
|
936
|
+
/* @__PURE__ */ jsxs9(Text10, { bold: true, color: "green", children: [
|
|
937
|
+
" ",
|
|
938
|
+
cmd.name
|
|
939
|
+
] }),
|
|
940
|
+
/* @__PURE__ */ jsxs9(Text10, { color: "gray", children: [
|
|
941
|
+
" ",
|
|
942
|
+
cmd.desc
|
|
943
|
+
] })
|
|
944
|
+
] }, cmd.name))
|
|
945
|
+
] }),
|
|
946
|
+
/* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
947
|
+
/* @__PURE__ */ jsx9(Text10, { bold: true, color: "yellow", children: "Examples" }),
|
|
948
|
+
examples.map((ex, i) => /* @__PURE__ */ jsxs9(Text10, { color: "white", children: [
|
|
949
|
+
" $ ",
|
|
950
|
+
ex
|
|
951
|
+
] }, i))
|
|
952
|
+
] })
|
|
953
|
+
] });
|
|
954
|
+
}
|
|
955
|
+
function App() {
|
|
956
|
+
switch (command) {
|
|
957
|
+
case "add":
|
|
958
|
+
return /* @__PURE__ */ jsx9(Add, { directSkills: restArgs.length > 0 ? restArgs : null });
|
|
959
|
+
case "list":
|
|
960
|
+
return /* @__PURE__ */ jsx9(List, {});
|
|
961
|
+
case "update":
|
|
962
|
+
return /* @__PURE__ */ jsx9(Update, {});
|
|
963
|
+
case "remove":
|
|
964
|
+
if (restArgs.length === 0) {
|
|
965
|
+
return /* @__PURE__ */ jsxs9(Text10, { color: "red", children: [
|
|
966
|
+
"Usage: hiver-skills remove ",
|
|
967
|
+
"<skill1> [skill2] ..."
|
|
968
|
+
] });
|
|
969
|
+
}
|
|
970
|
+
return /* @__PURE__ */ jsx9(Remove, { skillNames: restArgs });
|
|
971
|
+
case "help":
|
|
972
|
+
case "--help":
|
|
973
|
+
case "-h":
|
|
974
|
+
case void 0:
|
|
975
|
+
return /* @__PURE__ */ jsx9(Help, {});
|
|
976
|
+
default:
|
|
977
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
978
|
+
/* @__PURE__ */ jsxs9(Text10, { color: "red", children: [
|
|
979
|
+
"Unknown command: ",
|
|
980
|
+
command
|
|
981
|
+
] }),
|
|
982
|
+
/* @__PURE__ */ jsxs9(Text10, { color: "gray", children: [
|
|
983
|
+
"Run ",
|
|
984
|
+
/* @__PURE__ */ jsx9(Text10, { color: "green", children: "hiver-skills help" }),
|
|
985
|
+
" for usage."
|
|
986
|
+
] })
|
|
987
|
+
] });
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
render(/* @__PURE__ */ jsx9(App, {}));
|