@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.
Files changed (35) hide show
  1. package/README.md +394 -0
  2. package/build.js +15 -0
  3. package/collections/common/skills/create-prd/SKILL.md +43 -0
  4. package/collections/common/skills/discuss-problem/SKILL.md +55 -0
  5. package/collections/common/skills/explore-codebase/SKILL.md +79 -0
  6. package/collections/common/skills/prd-to-milestone/SKILL.md +40 -0
  7. package/collections/common/skills/refractor-and-clean/SKILL.md +41 -0
  8. package/collections/common/skills/solve-issue/SKILL.md +31 -0
  9. package/collections/common/skills/write-test-cases/SKILL.md +93 -0
  10. package/collections/web/agents/build-feature.md +42 -0
  11. package/collections/web/agents/build-milestone.md +61 -0
  12. package/collections/web/skills/build-component/SKILL.md +66 -0
  13. package/collections/web/skills/build-component/palette/README.md +3 -0
  14. package/collections/web/skills/build-component/palette/colors.md +87 -0
  15. package/collections/web/skills/build-component/references/best-practices.md +8 -0
  16. package/collections/web/skills/build-component/references/component-organization.md +21 -0
  17. package/collections/web/skills/build-component/references/figma-integration.md +5 -0
  18. package/collections/web/skills/build-component/references/icons.md +8 -0
  19. package/collections/web/skills/build-component/references/layout-and-structure.md +8 -0
  20. package/collections/web/skills/build-component/references/state-management.md +25 -0
  21. package/collections/web/skills/build-component/references/ui-kit-and-styling.md +13 -0
  22. package/collections/web/skills/build-component/typography/README.md +3 -0
  23. package/collections/web/skills/build-component/typography/variants.md +79 -0
  24. package/collections/web/skills/connect-api/SKILL.md +23 -0
  25. package/collections/web/skills/connect-api/references/api-call-structure.md +84 -0
  26. package/collections/web/skills/connect-api/references/best-practices.md +10 -0
  27. package/collections/web/skills/connect-api/references/data-fetchers.md +52 -0
  28. package/collections/web/skills/connect-api/references/error-handling.md +34 -0
  29. package/collections/web/skills/connect-api/references/hook-organization.md +11 -0
  30. package/collections/web/skills/connect-api/references/query-keys.md +21 -0
  31. package/collections/web/skills/connect-api/references/react-query-usage.md +83 -0
  32. package/collections/web/skills/connect-api/references/url-placeholders.md +17 -0
  33. package/collections/web/skills/scaffold-react-feature/SKILL.md +208 -0
  34. package/dist/cli.js +990 -0
  35. 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, {}));