@hiver/skills 1.0.4 → 1.0.6

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 (2) hide show
  1. package/dist/cli.js +407 -127
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -5,8 +5,8 @@ import React10 from "react";
5
5
  import { render, Box as Box9, Text as Text10 } from "ink";
6
6
 
7
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";
8
+ import React5, { useState as useState3, useEffect } from "react";
9
+ import { Box as Box4, Text as Text5, useApp } from "ink";
10
10
 
11
11
  // src/components/Select.jsx
12
12
  import React, { useState } from "react";
@@ -149,28 +149,36 @@ function Info({ children }) {
149
149
  ] });
150
150
  }
151
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
- }));
152
+ // src/components/Confirm.jsx
153
+ import React4 from "react";
154
+ import { Box as Box3, Text as Text4, useInput as useInput3 } from "ink";
155
+ import { jsx as jsx3, jsxs as jsxs4 } from "react/jsx-runtime";
156
+ function Confirm({ message, onConfirm, onBack }) {
157
+ useInput3((input, key) => {
158
+ if (input === "y" || input === "Y") {
159
+ onConfirm(true);
160
+ }
161
+ if (input === "n" || input === "N") {
162
+ onConfirm(false);
163
+ }
164
+ if (key.escape && onBack) {
165
+ onBack();
166
+ }
167
+ });
168
+ return /* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsxs4(Text4, { bold: true, color: "cyan", children: [
169
+ "? ",
170
+ message,
171
+ /* @__PURE__ */ jsxs4(Text4, { color: "gray", children: [
172
+ " (y/n",
173
+ onBack ? ", esc back" : "",
174
+ ")"
175
+ ] })
176
+ ] }) });
177
+ }
178
+
179
+ // src/utils/installer.js
180
+ import path2 from "path";
181
+ import fs2 from "fs-extra";
174
182
 
175
183
  // src/utils/collections.js
176
184
  import path from "path";
@@ -282,8 +290,6 @@ function resolveDependencies(selected, selectedCollections) {
282
290
  }
283
291
 
284
292
  // src/utils/installer.js
285
- import path2 from "path";
286
- import fs2 from "fs-extra";
287
293
  async function installSkill(collectionName, skillName, targetConfig, destRoot) {
288
294
  const src = path2.join(getCollectionsDir(), collectionName, "skills", skillName);
289
295
  const dest = path2.join(destRoot, targetConfig.skillsDir, skillName);
@@ -332,17 +338,41 @@ async function removeAgent(agentName, targetConfig, destRoot) {
332
338
  return false;
333
339
  }
334
340
 
341
+ // src/utils/targets.js
342
+ var targets = {
343
+ claude: {
344
+ name: "Claude Code",
345
+ skillsDir: ".claude/skills",
346
+ agentsDir: ".claude/agents"
347
+ },
348
+ cursor: {
349
+ name: "Cursor",
350
+ skillsDir: ".cursor/rules",
351
+ agentsDir: ".cursor/rules"
352
+ },
353
+ windsurf: {
354
+ name: "Windsurf",
355
+ skillsDir: ".windsurfrules",
356
+ agentsDir: ".windsurfrules"
357
+ }
358
+ };
359
+ var targetList = Object.entries(targets).map(([key, t]) => ({
360
+ label: t.name,
361
+ value: key
362
+ }));
363
+
335
364
  // src/commands/Add.jsx
336
- import { jsx as jsx3, jsxs as jsxs4 } from "react/jsx-runtime";
365
+ import { jsx as jsx4, jsxs as jsxs5 } from "react/jsx-runtime";
337
366
  var STEPS = {
338
367
  TARGET: "target",
339
368
  COLLECTIONS: "collections",
340
369
  TYPE: "type",
341
370
  ITEMS: "items",
371
+ CONFIRM_ALL: "confirm_all",
342
372
  INSTALLING: "installing",
343
373
  DONE: "done"
344
374
  };
345
- function Add({ directSkills }) {
375
+ function Add({ directSkills, addAll }) {
346
376
  const { exit } = useApp();
347
377
  const [step, setStep] = useState3(STEPS.TARGET);
348
378
  const [targetKey, setTargetKey] = useState3(null);
@@ -351,6 +381,7 @@ function Add({ directSkills }) {
351
381
  const [toInstall, setToInstall] = useState3({ skills: [], agents: [] });
352
382
  const [results, setResults] = useState3({ skills: 0, agents: 0, deps: [] });
353
383
  const [directMissing, setDirectMissing] = useState3([]);
384
+ const [allPlan, setAllPlan] = useState3(null);
354
385
  const destRoot = process.cwd();
355
386
  function getAvailableTypes(col) {
356
387
  const { skills, agents } = scanCollection(col);
@@ -365,6 +396,32 @@ function Add({ directSkills }) {
365
396
  }
366
397
  function handleCollectionSelect(col) {
367
398
  setSelectedCollection(col);
399
+ if (addAll) {
400
+ const targetConfig = targets[targetKey];
401
+ const { skills, agents } = scanCollection(col);
402
+ const installed = listInstalled(targetConfig, destRoot);
403
+ const installedSkillSet = new Set(installed.skills);
404
+ const installedAgentSet = new Set(installed.agents);
405
+ const newSkills = skills.filter((n) => !installedSkillSet.has(n));
406
+ const newAgents = agents.filter((n) => !installedAgentSet.has(n));
407
+ const alreadySkills = skills.filter((n) => installedSkillSet.has(n));
408
+ const alreadyAgents = agents.filter((n) => installedAgentSet.has(n));
409
+ if (newSkills.length === 0 && newAgents.length === 0) {
410
+ setDirectMissing(
411
+ [...alreadySkills, ...alreadyAgents].map((n) => `${n} (already installed)`)
412
+ );
413
+ setStep(STEPS.DONE);
414
+ return;
415
+ }
416
+ setAllPlan({
417
+ skills: newSkills,
418
+ agents: newAgents,
419
+ alreadyInstalled: [...alreadySkills, ...alreadyAgents],
420
+ collection: col
421
+ });
422
+ setStep(STEPS.CONFIRM_ALL);
423
+ return;
424
+ }
368
425
  if (directSkills && directSkills.length > 0) {
369
426
  const { skills, agents } = scanCollection(col);
370
427
  const skillItems = [];
@@ -410,6 +467,21 @@ function Add({ directSkills }) {
410
467
  }
411
468
  setStep(STEPS.INSTALLING);
412
469
  }
470
+ function handleConfirmAllAdd(yes) {
471
+ if (!yes) {
472
+ setStep(STEPS.DONE);
473
+ return;
474
+ }
475
+ const col = allPlan.collection;
476
+ setToInstall({
477
+ skills: allPlan.skills.map((name) => ({ collection: col, name })),
478
+ agents: allPlan.agents.map((name) => ({ collection: col, name }))
479
+ });
480
+ if (allPlan.alreadyInstalled.length > 0) {
481
+ setDirectMissing(allPlan.alreadyInstalled.map((n) => `${n} (already installed)`));
482
+ }
483
+ setStep(STEPS.INSTALLING);
484
+ }
413
485
  function handleTargetBack() {
414
486
  exit();
415
487
  }
@@ -456,7 +528,7 @@ function Add({ directSkills }) {
456
528
  doInstall();
457
529
  }, [step]);
458
530
  if (step === STEPS.TARGET) {
459
- return /* @__PURE__ */ jsx3(
531
+ return /* @__PURE__ */ jsx4(
460
532
  Select,
461
533
  {
462
534
  items: targetList,
@@ -479,13 +551,13 @@ function Add({ directSkills }) {
479
551
  const items = collections.map((c) => ({ label: c, value: c }));
480
552
  if (items.length === 0) {
481
553
  setTimeout(() => exit(), 100);
482
- return /* @__PURE__ */ jsxs4(ErrorMsg, { children: [
554
+ return /* @__PURE__ */ jsxs5(ErrorMsg, { children: [
483
555
  "None of: ",
484
556
  directSkills.join(", "),
485
557
  " found in any collection."
486
558
  ] });
487
559
  }
488
- return /* @__PURE__ */ jsx3(
560
+ return /* @__PURE__ */ jsx4(
489
561
  Select,
490
562
  {
491
563
  items,
@@ -497,7 +569,7 @@ function Add({ directSkills }) {
497
569
  }
498
570
  if (step === STEPS.TYPE) {
499
571
  const types = getAvailableTypes(selectedCollection);
500
- return /* @__PURE__ */ jsx3(
572
+ return /* @__PURE__ */ jsx4(
501
573
  Select,
502
574
  {
503
575
  items: types,
@@ -515,7 +587,7 @@ function Add({ directSkills }) {
515
587
  const meta = getSkillMeta(col, s);
516
588
  return { label: s, value: s, description: meta.description, checked: false };
517
589
  });
518
- return /* @__PURE__ */ jsx3(
590
+ return /* @__PURE__ */ jsx4(
519
591
  MultiSelect,
520
592
  {
521
593
  items,
@@ -530,7 +602,7 @@ function Add({ directSkills }) {
530
602
  const meta = getAgentMeta(col, a);
531
603
  return { label: a, value: a, description: meta.description, checked: false };
532
604
  });
533
- return /* @__PURE__ */ jsx3(
605
+ return /* @__PURE__ */ jsx4(
534
606
  MultiSelect,
535
607
  {
536
608
  items,
@@ -541,19 +613,53 @@ function Add({ directSkills }) {
541
613
  );
542
614
  }
543
615
  }
616
+ if (step === STEPS.CONFIRM_ALL) {
617
+ const total = allPlan.skills.length + allPlan.agents.length;
618
+ return /* @__PURE__ */ jsxs5(Box4, { flexDirection: "column", children: [
619
+ /* @__PURE__ */ jsxs5(Warning, { children: [
620
+ "Will install ",
621
+ allPlan.skills.length,
622
+ " skill(s), ",
623
+ allPlan.agents.length,
624
+ ' agent(s) from "',
625
+ allPlan.collection,
626
+ '":'
627
+ ] }),
628
+ /* @__PURE__ */ jsxs5(Box4, { flexDirection: "column", marginTop: 1, children: [
629
+ allPlan.skills.map((n) => /* @__PURE__ */ jsxs5(Text5, { color: "green", children: [
630
+ " + ",
631
+ n,
632
+ " (skill)"
633
+ ] }, `s-${n}`)),
634
+ allPlan.agents.map((n) => /* @__PURE__ */ jsxs5(Text5, { color: "green", children: [
635
+ " + ",
636
+ n,
637
+ " (agent)"
638
+ ] }, `a-${n}`))
639
+ ] }),
640
+ allPlan.alreadyInstalled.length > 0 && /* @__PURE__ */ jsxs5(Box4, { flexDirection: "column", marginTop: 1, children: [
641
+ /* @__PURE__ */ jsx4(Info, { children: "Already installed (will skip):" }),
642
+ allPlan.alreadyInstalled.map((n) => /* @__PURE__ */ jsxs5(Text5, { color: "gray", children: [
643
+ " \u2022 ",
644
+ n
645
+ ] }, `x-${n}`))
646
+ ] }),
647
+ /* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(Confirm, { message: `Install all ${total} item(s)?`, onConfirm: handleConfirmAllAdd }) })
648
+ ] });
649
+ }
544
650
  if (step === STEPS.INSTALLING) {
545
- return /* @__PURE__ */ jsx3(Text4, { color: "yellow", children: "Installing..." });
651
+ return /* @__PURE__ */ jsx4(Text5, { color: "yellow", children: "Installing..." });
546
652
  }
547
653
  if (step === STEPS.DONE) {
548
654
  setTimeout(() => exit(), 100);
549
- return /* @__PURE__ */ jsxs4(Box3, { flexDirection: "column", marginTop: 1, children: [
550
- directMissing.length > 0 && /* @__PURE__ */ jsx3(Box3, { marginBottom: 1, children: /* @__PURE__ */ jsxs4(ErrorMsg, { children: [
655
+ return /* @__PURE__ */ jsxs5(Box4, { flexDirection: "column", marginTop: 1, children: [
656
+ directMissing.length > 0 && /* @__PURE__ */ jsx4(Box4, { marginBottom: 1, children: /* @__PURE__ */ jsxs5(ErrorMsg, { children: [
551
657
  "Not found in any collection: ",
552
658
  directMissing.join(", ")
553
659
  ] }) }),
554
- results.deps.length > 0 && /* @__PURE__ */ jsxs4(Box3, { flexDirection: "column", marginBottom: 1, children: [
555
- /* @__PURE__ */ jsx3(Info, { children: "Auto-installed dependencies:" }),
556
- results.deps.map((d) => /* @__PURE__ */ jsxs4(Text4, { color: "cyan", children: [
660
+ results.deps.length > 0 && /* @__PURE__ */ jsxs5(Box4, { flexDirection: "column", marginBottom: 1, children: [
661
+ /* @__PURE__ */ jsx4(Info, { children: "Auto-installed dependencies:" }),
662
+ results.deps.map((d) => /* @__PURE__ */ jsxs5(Text5, { color: "cyan", children: [
557
663
  " + ",
558
664
  d.name,
559
665
  " (from ",
@@ -561,7 +667,7 @@ function Add({ directSkills }) {
561
667
  ")"
562
668
  ] }, d.name))
563
669
  ] }),
564
- /* @__PURE__ */ jsxs4(Success, { children: [
670
+ /* @__PURE__ */ jsxs5(Success, { children: [
565
671
  "Installed ",
566
672
  results.skills,
567
673
  " skill(s), ",
@@ -575,18 +681,18 @@ function Add({ directSkills }) {
575
681
  }
576
682
 
577
683
  // src/commands/List.jsx
578
- import React5 from "react";
579
- import { Box as Box4, Text as Text5, useApp as useApp2 } from "ink";
580
- import { jsx as jsx4, jsxs as jsxs5 } from "react/jsx-runtime";
684
+ import React6 from "react";
685
+ import { Box as Box5, Text as Text6, useApp as useApp2 } from "ink";
686
+ import { jsx as jsx5, jsxs as jsxs6 } from "react/jsx-runtime";
581
687
  function List() {
582
688
  const { exit } = useApp2();
583
689
  const collections = listCollections();
584
690
  setTimeout(() => exit(), 100);
585
691
  if (collections.length === 0) {
586
- return /* @__PURE__ */ jsx4(Text5, { color: "yellow", children: "No collections found." });
692
+ return /* @__PURE__ */ jsx5(Text6, { color: "yellow", children: "No collections found." });
587
693
  }
588
- return /* @__PURE__ */ jsxs5(Box4, { flexDirection: "column", children: [
589
- /* @__PURE__ */ jsxs5(Text5, { bold: true, color: "cyan", children: [
694
+ return /* @__PURE__ */ jsxs6(Box5, { flexDirection: "column", children: [
695
+ /* @__PURE__ */ jsxs6(Text6, { bold: true, color: "cyan", children: [
590
696
  "\n",
591
697
  "Available skills & agents",
592
698
  "\n"
@@ -594,44 +700,44 @@ function List() {
594
700
  collections.map((col) => {
595
701
  const { skills, agents } = scanCollection(col);
596
702
  if (skills.length === 0 && agents.length === 0) return null;
597
- return /* @__PURE__ */ jsxs5(Box4, { flexDirection: "column", marginBottom: 1, children: [
598
- /* @__PURE__ */ jsxs5(Text5, { bold: true, color: "yellow", children: [
703
+ return /* @__PURE__ */ jsxs6(Box5, { flexDirection: "column", marginBottom: 1, children: [
704
+ /* @__PURE__ */ jsxs6(Text6, { bold: true, color: "yellow", children: [
599
705
  " ",
600
706
  "\u{1F4E6} ",
601
707
  col
602
708
  ] }),
603
- skills.length > 0 && /* @__PURE__ */ jsxs5(Box4, { flexDirection: "column", marginLeft: 2, marginTop: 1, children: [
604
- /* @__PURE__ */ jsxs5(Text5, { bold: true, color: "magenta", children: [
709
+ skills.length > 0 && /* @__PURE__ */ jsxs6(Box5, { flexDirection: "column", marginLeft: 2, marginTop: 1, children: [
710
+ /* @__PURE__ */ jsxs6(Text6, { bold: true, color: "magenta", children: [
605
711
  " ",
606
712
  "Skills"
607
713
  ] }),
608
714
  skills.map((s) => {
609
715
  const meta = getSkillMeta(col, s);
610
- return /* @__PURE__ */ jsxs5(Box4, { flexDirection: "column", marginBottom: 1, marginLeft: 2, children: [
611
- /* @__PURE__ */ jsxs5(Text5, { bold: true, color: "green", children: [
716
+ return /* @__PURE__ */ jsxs6(Box5, { flexDirection: "column", marginBottom: 1, marginLeft: 2, children: [
717
+ /* @__PURE__ */ jsxs6(Text6, { bold: true, color: "green", children: [
612
718
  " ",
613
719
  s
614
720
  ] }),
615
- meta.description && /* @__PURE__ */ jsxs5(Text5, { color: "gray", children: [
721
+ meta.description && /* @__PURE__ */ jsxs6(Text6, { color: "gray", children: [
616
722
  " ",
617
723
  meta.description
618
724
  ] })
619
725
  ] }, s);
620
726
  })
621
727
  ] }),
622
- agents.length > 0 && /* @__PURE__ */ jsxs5(Box4, { flexDirection: "column", marginLeft: 2, marginTop: 1, children: [
623
- /* @__PURE__ */ jsxs5(Text5, { bold: true, color: "magenta", children: [
728
+ agents.length > 0 && /* @__PURE__ */ jsxs6(Box5, { flexDirection: "column", marginLeft: 2, marginTop: 1, children: [
729
+ /* @__PURE__ */ jsxs6(Text6, { bold: true, color: "magenta", children: [
624
730
  " ",
625
731
  "Agents"
626
732
  ] }),
627
733
  agents.map((a) => {
628
734
  const meta = getAgentMeta(col, a);
629
- return /* @__PURE__ */ jsxs5(Box4, { flexDirection: "column", marginBottom: 1, marginLeft: 2, children: [
630
- /* @__PURE__ */ jsxs5(Text5, { bold: true, color: "blue", children: [
735
+ return /* @__PURE__ */ jsxs6(Box5, { flexDirection: "column", marginBottom: 1, marginLeft: 2, children: [
736
+ /* @__PURE__ */ jsxs6(Text6, { bold: true, color: "blue", children: [
631
737
  " ",
632
738
  a
633
739
  ] }),
634
- meta.description && /* @__PURE__ */ jsxs5(Text5, { color: "gray", children: [
740
+ meta.description && /* @__PURE__ */ jsxs6(Text6, { color: "gray", children: [
635
741
  " ",
636
742
  meta.description
637
743
  ] })
@@ -646,35 +752,8 @@ function List() {
646
752
  // src/commands/Update.jsx
647
753
  import React8, { useState as useState4, useEffect as useEffect2 } from "react";
648
754
  import { Box as Box7, Text as Text8, useApp as useApp3 } from "ink";
649
- import path3 from "path";
650
- import fs3 from "fs-extra";
651
-
652
- // src/components/Confirm.jsx
653
- import React6 from "react";
654
- import { Box as Box5, Text as Text6, useInput as useInput3 } from "ink";
655
- import { jsx as jsx5, jsxs as jsxs6 } from "react/jsx-runtime";
656
- function Confirm({ message, onConfirm, onBack }) {
657
- useInput3((input, key) => {
658
- if (input === "y" || input === "Y") {
659
- onConfirm(true);
660
- }
661
- if (input === "n" || input === "N") {
662
- onConfirm(false);
663
- }
664
- if (key.escape && onBack) {
665
- onBack();
666
- }
667
- });
668
- return /* @__PURE__ */ jsx5(Box5, { children: /* @__PURE__ */ jsxs6(Text6, { bold: true, color: "cyan", children: [
669
- "? ",
670
- message,
671
- /* @__PURE__ */ jsxs6(Text6, { color: "gray", children: [
672
- " (y/n",
673
- onBack ? ", esc back" : "",
674
- ")"
675
- ] })
676
- ] }) });
677
- }
755
+ import path4 from "path";
756
+ import fs4 from "fs-extra";
678
757
 
679
758
  // src/components/DiffView.jsx
680
759
  import React7 from "react";
@@ -692,6 +771,8 @@ function DiffView({ patch }) {
692
771
  }
693
772
 
694
773
  // src/utils/diffUtil.js
774
+ import path3 from "path";
775
+ import fs3 from "fs-extra";
695
776
  import { createTwoFilesPatch } from "diff";
696
777
  function computeDiff(filePath, localContent, latestContent) {
697
778
  return createTwoFilesPatch(
@@ -706,6 +787,63 @@ function computeDiff(filePath, localContent, latestContent) {
706
787
  function hasChanges(localContent, latestContent) {
707
788
  return localContent.trim() !== latestContent.trim();
708
789
  }
790
+ function walkFiles(dir) {
791
+ const out = [];
792
+ if (!fs3.existsSync(dir)) return out;
793
+ const entries = fs3.readdirSync(dir, { withFileTypes: true });
794
+ for (const entry of entries) {
795
+ const full = path3.join(dir, entry.name);
796
+ if (entry.isDirectory()) {
797
+ for (const sub of walkFiles(full)) out.push(path3.join(entry.name, sub));
798
+ } else if (entry.isFile()) {
799
+ out.push(entry.name);
800
+ }
801
+ }
802
+ return out;
803
+ }
804
+ function isBinary(buf) {
805
+ const sample = buf.subarray(0, Math.min(buf.length, 8e3));
806
+ for (let i = 0; i < sample.length; i++) {
807
+ if (sample[i] === 0) return true;
808
+ }
809
+ return false;
810
+ }
811
+ function hasDirChanges(localDir, latestDir) {
812
+ const localFiles = new Set(walkFiles(localDir));
813
+ const latestFiles = new Set(walkFiles(latestDir));
814
+ if (localFiles.size !== latestFiles.size) return true;
815
+ for (const rel of latestFiles) {
816
+ if (!localFiles.has(rel)) return true;
817
+ const a = fs3.readFileSync(path3.join(localDir, rel));
818
+ const b = fs3.readFileSync(path3.join(latestDir, rel));
819
+ if (!a.equals(b)) return true;
820
+ }
821
+ for (const rel of localFiles) {
822
+ if (!latestFiles.has(rel)) return true;
823
+ }
824
+ return false;
825
+ }
826
+ function computeDirDiff(localDir, latestDir) {
827
+ const localFiles = new Set(walkFiles(localDir));
828
+ const latestFiles = new Set(walkFiles(latestDir));
829
+ const allFiles = /* @__PURE__ */ new Set([...localFiles, ...latestFiles]);
830
+ const parts = [];
831
+ for (const rel of [...allFiles].sort()) {
832
+ const localBuf = localFiles.has(rel) ? fs3.readFileSync(path3.join(localDir, rel)) : Buffer.alloc(0);
833
+ const latestBuf = latestFiles.has(rel) ? fs3.readFileSync(path3.join(latestDir, rel)) : Buffer.alloc(0);
834
+ if (localBuf.equals(latestBuf)) continue;
835
+ if (isBinary(localBuf) || isBinary(latestBuf)) {
836
+ const verb = !localFiles.has(rel) ? "added" : !latestFiles.has(rel) ? "removed" : "changed";
837
+ parts.push(`Binary file ${rel} ${verb} (${localBuf.length} \u2192 ${latestBuf.length} bytes)`);
838
+ continue;
839
+ }
840
+ const localContent = localBuf.toString("utf-8");
841
+ const latestContent = latestBuf.toString("utf-8");
842
+ if (localContent.trim() === latestContent.trim()) continue;
843
+ parts.push(computeDiff(rel, localContent, latestContent));
844
+ }
845
+ return parts.join("\n");
846
+ }
709
847
 
710
848
  // src/commands/Update.jsx
711
849
  import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
@@ -715,16 +853,17 @@ var STEPS2 = {
715
853
  ITEMS: "items",
716
854
  RESOLVE: "resolve",
717
855
  CONFIRM: "confirm",
856
+ CONFIRM_ALL: "confirm_all",
718
857
  DONE: "done"
719
858
  };
720
859
  function detectType(name, targetConfig, destRoot) {
721
- const skillPath = path3.join(destRoot, targetConfig.skillsDir, name, "SKILL.md");
722
- if (fs3.existsSync(skillPath)) return "skill";
723
- const agentPath = path3.join(destRoot, targetConfig.agentsDir, `${name}.md`);
724
- if (fs3.existsSync(agentPath)) return "agent";
860
+ const skillPath = path4.join(destRoot, targetConfig.skillsDir, name, "SKILL.md");
861
+ if (fs4.existsSync(skillPath)) return "skill";
862
+ const agentPath = path4.join(destRoot, targetConfig.agentsDir, `${name}.md`);
863
+ if (fs4.existsSync(agentPath)) return "agent";
725
864
  return null;
726
865
  }
727
- function Update({ directSkills }) {
866
+ function Update({ directSkills, updateAll }) {
728
867
  const { exit } = useApp3();
729
868
  const [step, setStep] = useState4(STEPS2.TARGET);
730
869
  const [targetKey, setTargetKey] = useState4(null);
@@ -737,6 +876,8 @@ function Update({ directSkills }) {
737
876
  const [installedCount, setInstalledCount] = useState4(0);
738
877
  const [missingNames, setMissingNames] = useState4([]);
739
878
  const [directInfo, setDirectInfo] = useState4([]);
879
+ const [skipReasons, setSkipReasons] = useState4([]);
880
+ const [allBundle, setAllBundle] = useState4(null);
740
881
  const destRoot = process.cwd();
741
882
  function handleTargetSelect(key) {
742
883
  setTargetKey(key);
@@ -773,6 +914,57 @@ function Update({ directSkills }) {
773
914
  }
774
915
  function handleCollectionSelect(collection) {
775
916
  setSelectedCollection(collection);
917
+ if (updateAll) {
918
+ const targetConfig = targets[targetKey];
919
+ const installed = listInstalled(targetConfig, destRoot);
920
+ const { skills, agents } = scanCollection(collection);
921
+ const items = [];
922
+ const localSkipped = [];
923
+ const parts = [];
924
+ for (const name of installed.skills) {
925
+ if (!skills.includes(name)) {
926
+ localSkipped.push({ name, reason: `not in ${collection}` });
927
+ continue;
928
+ }
929
+ const localDir = path4.join(destRoot, targetConfig.skillsDir, name);
930
+ const latestDir = path4.join(getCollectionsDir(), collection, "skills", name);
931
+ if (!hasDirChanges(localDir, latestDir)) {
932
+ localSkipped.push({ name, reason: `already up-to-date with ${collection}` });
933
+ continue;
934
+ }
935
+ items.push({ name, type: "skill" });
936
+ parts.push(`### ${name} (skill)
937
+ ${computeDirDiff(localDir, latestDir)}`);
938
+ }
939
+ for (const name of installed.agents) {
940
+ if (!agents.includes(name)) {
941
+ localSkipped.push({ name, reason: `not in ${collection}` });
942
+ continue;
943
+ }
944
+ const localFile = path4.join(destRoot, targetConfig.agentsDir, `${name}.md`);
945
+ const latestFile = path4.join(getCollectionsDir(), collection, "agents", `${name}.md`);
946
+ const localContent = fs4.readFileSync(localFile, "utf-8");
947
+ const latestContent = fs4.readFileSync(latestFile, "utf-8");
948
+ if (!hasChanges(localContent, latestContent)) {
949
+ localSkipped.push({ name, reason: `already up-to-date with ${collection}` });
950
+ continue;
951
+ }
952
+ items.push({ name, type: "agent" });
953
+ parts.push(`### ${name} (agent)
954
+ ${computeDiff(`${name}.md`, localContent, latestContent)}`);
955
+ }
956
+ if (localSkipped.length > 0) {
957
+ setSkipReasons((prev) => [...prev, ...localSkipped]);
958
+ setSkippedCount((c) => c + localSkipped.length);
959
+ }
960
+ if (items.length === 0) {
961
+ setStep(STEPS2.DONE);
962
+ return;
963
+ }
964
+ setAllBundle({ items, patch: parts.join("\n\n") });
965
+ setStep(STEPS2.CONFIRM_ALL);
966
+ return;
967
+ }
776
968
  if (directSkills && directSkills.length > 0) {
777
969
  const items = [];
778
970
  const skipped = [];
@@ -832,46 +1024,88 @@ function Update({ directSkills }) {
832
1024
  advance();
833
1025
  return;
834
1026
  }
835
- let localFile;
836
- let latestFile;
837
1027
  if (item.type === "skill") {
838
- localFile = path3.join(destRoot, targetConfig.skillsDir, item.name, "SKILL.md");
839
- latestFile = path3.join(getCollectionsDir(), collection, "skills", item.name, "SKILL.md");
840
- } else {
841
- localFile = path3.join(destRoot, targetConfig.agentsDir, `${item.name}.md`);
842
- latestFile = path3.join(getCollectionsDir(), collection, "agents", `${item.name}.md`);
1028
+ const localDir = path4.join(destRoot, targetConfig.skillsDir, item.name);
1029
+ const latestDir = path4.join(getCollectionsDir(), collection, "skills", item.name);
1030
+ if (!fs4.existsSync(localDir) || !fs4.existsSync(latestDir)) {
1031
+ recordSkip(item.name, "source or local directory missing");
1032
+ advance();
1033
+ return;
1034
+ }
1035
+ if (!hasDirChanges(localDir, latestDir)) {
1036
+ recordSkip(item.name, `already up-to-date with ${collection}`);
1037
+ advance();
1038
+ return;
1039
+ }
1040
+ setCurrentPatch(computeDirDiff(localDir, latestDir));
1041
+ setStep(STEPS2.CONFIRM);
1042
+ return;
843
1043
  }
844
- if (!fs3.existsSync(localFile) || !fs3.existsSync(latestFile)) {
1044
+ const localFile = path4.join(destRoot, targetConfig.agentsDir, `${item.name}.md`);
1045
+ const latestFile = path4.join(getCollectionsDir(), collection, "agents", `${item.name}.md`);
1046
+ if (!fs4.existsSync(localFile) || !fs4.existsSync(latestFile)) {
1047
+ recordSkip(item.name, "source or local file missing");
845
1048
  advance();
846
1049
  return;
847
1050
  }
848
- const localContent = fs3.readFileSync(localFile, "utf-8");
849
- const latestContent = fs3.readFileSync(latestFile, "utf-8");
1051
+ const localContent = fs4.readFileSync(localFile, "utf-8");
1052
+ const latestContent = fs4.readFileSync(latestFile, "utf-8");
850
1053
  if (!hasChanges(localContent, latestContent)) {
851
- setSkippedCount((c) => c + 1);
1054
+ recordSkip(item.name, `already up-to-date with ${collection}`);
852
1055
  advance();
853
1056
  return;
854
1057
  }
855
- const filename = item.type === "skill" ? "SKILL.md" : `${item.name}.md`;
856
- setCurrentPatch(computeDiff(filename, localContent, latestContent));
1058
+ setCurrentPatch(computeDiff(`${item.name}.md`, localContent, latestContent));
857
1059
  setStep(STEPS2.CONFIRM);
858
1060
  }
1061
+ function recordSkip(name, reason) {
1062
+ setSkippedCount((c) => c + 1);
1063
+ setSkipReasons((prev) => [...prev, { name, reason }]);
1064
+ }
1065
+ function handleConfirmAll(yes) {
1066
+ const targetConfig = targets[targetKey];
1067
+ if (!yes) {
1068
+ const declined = allBundle.items.map((it) => ({
1069
+ name: it.name,
1070
+ reason: "declined (bulk)"
1071
+ }));
1072
+ setSkipReasons((prev) => [...prev, ...declined]);
1073
+ setSkippedCount((c) => c + declined.length);
1074
+ setStep(STEPS2.DONE);
1075
+ return;
1076
+ }
1077
+ let updated = 0;
1078
+ for (const item of allBundle.items) {
1079
+ if (item.type === "skill") {
1080
+ const src = path4.join(getCollectionsDir(), selectedCollection, "skills", item.name);
1081
+ const dest = path4.join(destRoot, targetConfig.skillsDir, item.name);
1082
+ fs4.copySync(src, dest, { overwrite: true });
1083
+ } else {
1084
+ const src = path4.join(getCollectionsDir(), selectedCollection, "agents", `${item.name}.md`);
1085
+ const dest = path4.join(destRoot, targetConfig.agentsDir, `${item.name}.md`);
1086
+ fs4.copySync(src, dest, { overwrite: true });
1087
+ }
1088
+ updated++;
1089
+ }
1090
+ setUpdatedCount((c) => c + updated);
1091
+ setStep(STEPS2.DONE);
1092
+ }
859
1093
  function handleConfirm(yes) {
860
1094
  const item = queue[currentIdx];
861
1095
  const targetConfig = targets[targetKey];
862
1096
  if (yes) {
863
1097
  if (item.type === "skill") {
864
- const src = path3.join(getCollectionsDir(), selectedCollection, "skills", item.name);
865
- const dest = path3.join(destRoot, targetConfig.skillsDir, item.name);
866
- fs3.copySync(src, dest, { overwrite: true });
1098
+ const src = path4.join(getCollectionsDir(), selectedCollection, "skills", item.name);
1099
+ const dest = path4.join(destRoot, targetConfig.skillsDir, item.name);
1100
+ fs4.copySync(src, dest, { overwrite: true });
867
1101
  } else {
868
- const src = path3.join(getCollectionsDir(), selectedCollection, "agents", `${item.name}.md`);
869
- const dest = path3.join(destRoot, targetConfig.agentsDir, `${item.name}.md`);
870
- fs3.copySync(src, dest, { overwrite: true });
1102
+ const src = path4.join(getCollectionsDir(), selectedCollection, "agents", `${item.name}.md`);
1103
+ const dest = path4.join(destRoot, targetConfig.agentsDir, `${item.name}.md`);
1104
+ fs4.copySync(src, dest, { overwrite: true });
871
1105
  }
872
1106
  setUpdatedCount((c) => c + 1);
873
1107
  } else {
874
- setSkippedCount((c) => c + 1);
1108
+ recordSkip(item.name, "declined");
875
1109
  }
876
1110
  advance();
877
1111
  }
@@ -956,6 +1190,23 @@ function Update({ directSkills }) {
956
1190
  if (step === STEPS2.RESOLVE) {
957
1191
  return /* @__PURE__ */ jsx7(Text8, { color: "yellow", children: "Checking..." });
958
1192
  }
1193
+ if (step === STEPS2.CONFIRM_ALL) {
1194
+ return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
1195
+ /* @__PURE__ */ jsxs7(Warning, { children: [
1196
+ allBundle.items.length,
1197
+ " item(s) differ from ",
1198
+ selectedCollection
1199
+ ] }),
1200
+ /* @__PURE__ */ jsx7(DiffView, { patch: allBundle.patch }),
1201
+ /* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx7(
1202
+ Confirm,
1203
+ {
1204
+ message: `Apply all ${allBundle.items.length} updates?`,
1205
+ onConfirm: handleConfirmAll
1206
+ }
1207
+ ) })
1208
+ ] });
1209
+ }
959
1210
  if (step === STEPS2.CONFIRM) {
960
1211
  const item = queue[currentIdx];
961
1212
  return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
@@ -975,17 +1226,26 @@ function Update({ directSkills }) {
975
1226
  }
976
1227
  return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
977
1228
  missingNames.length > 0 && /* @__PURE__ */ jsxs7(ErrorMsg, { children: [
978
- "Skipped: ",
1229
+ "Not found: ",
979
1230
  missingNames.join(", ")
980
1231
  ] }),
981
- /* @__PURE__ */ jsxs7(Success, { children: [
1232
+ skipReasons.length > 0 && /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", marginTop: 1, children: [
1233
+ /* @__PURE__ */ jsx7(Info, { children: "Skipped:" }),
1234
+ skipReasons.map((s, i) => /* @__PURE__ */ jsxs7(Text8, { color: "gray", children: [
1235
+ " \u2022 ",
1236
+ s.name,
1237
+ " \u2014 ",
1238
+ s.reason
1239
+ ] }, `${s.name}-${i}`))
1240
+ ] }),
1241
+ /* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsxs7(Success, { children: [
982
1242
  updatedCount,
983
1243
  " updated, ",
984
1244
  installedCount,
985
1245
  " installed, ",
986
1246
  skippedCount,
987
1247
  " skipped"
988
- ] })
1248
+ ] }) })
989
1249
  ] });
990
1250
  }
991
1251
  return null;
@@ -994,8 +1254,8 @@ function Update({ directSkills }) {
994
1254
  // src/commands/Remove.jsx
995
1255
  import React9, { useState as useState5 } from "react";
996
1256
  import { Box as Box8, Text as Text9, useApp as useApp4 } from "ink";
997
- import path4 from "path";
998
- import fs4 from "fs-extra";
1257
+ import path5 from "path";
1258
+ import fs5 from "fs-extra";
999
1259
  import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1000
1260
  var STEPS3 = {
1001
1261
  TARGET: "target",
@@ -1015,9 +1275,9 @@ function Remove({ skillNames }) {
1015
1275
  const targetConfig = targets[key];
1016
1276
  const missing = [];
1017
1277
  for (const name of skillNames) {
1018
- const skillPath = path4.join(destRoot, targetConfig.skillsDir, name);
1019
- const agentPath = path4.join(destRoot, targetConfig.agentsDir, `${name}.md`);
1020
- if (!fs4.existsSync(skillPath) && !fs4.existsSync(agentPath)) {
1278
+ const skillPath = path5.join(destRoot, targetConfig.skillsDir, name);
1279
+ const agentPath = path5.join(destRoot, targetConfig.agentsDir, `${name}.md`);
1280
+ if (!fs5.existsSync(skillPath) && !fs5.existsSync(agentPath)) {
1021
1281
  missing.push(name);
1022
1282
  }
1023
1283
  }
@@ -1086,18 +1346,20 @@ var command = args[0];
1086
1346
  var restArgs = args.slice(1);
1087
1347
  function Help() {
1088
1348
  const commands = [
1089
- { name: "add [skills...]", desc: "Install skills/agents (interactive if no args)" },
1349
+ { name: "add [skills...] [--all]", desc: "Install skills/agents (interactive if no args; --all installs everything in chosen collection)" },
1090
1350
  { name: "list", desc: "List all available skills and agents" },
1091
- { name: "update [skills...]", desc: "Update installed skills (interactive if no args)" },
1351
+ { name: "update [skills...] [--all]", desc: "Update installed skills (interactive if no args; --all updates everything in chosen collection)" },
1092
1352
  { name: "remove <skills>", desc: "Remove installed skills/agents" },
1093
1353
  { name: "help", desc: "Show this help message" }
1094
1354
  ];
1095
1355
  const examples = [
1096
1356
  "npx @hiver/skills add",
1097
1357
  "npx @hiver/skills add discuss-problem create-prd",
1358
+ "npx @hiver/skills add --all",
1098
1359
  "npx @hiver/skills list",
1099
1360
  "npx @hiver/skills update",
1100
1361
  "npx @hiver/skills update build-component",
1362
+ "npx @hiver/skills update --all",
1101
1363
  "npx @hiver/skills remove discuss-problem"
1102
1364
  ];
1103
1365
  return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginTop: 1, marginBottom: 1, children: [
@@ -1129,12 +1391,30 @@ function Help() {
1129
1391
  }
1130
1392
  function App() {
1131
1393
  switch (command) {
1132
- case "add":
1133
- return /* @__PURE__ */ jsx9(Add, { directSkills: restArgs.length > 0 ? restArgs : null });
1394
+ case "add": {
1395
+ const all = restArgs.includes("--all");
1396
+ const names = restArgs.filter((a) => !a.startsWith("--"));
1397
+ return /* @__PURE__ */ jsx9(
1398
+ Add,
1399
+ {
1400
+ directSkills: names.length > 0 ? names : null,
1401
+ addAll: all
1402
+ }
1403
+ );
1404
+ }
1134
1405
  case "list":
1135
1406
  return /* @__PURE__ */ jsx9(List, {});
1136
- case "update":
1137
- return /* @__PURE__ */ jsx9(Update, { directSkills: restArgs.length > 0 ? restArgs : null });
1407
+ case "update": {
1408
+ const all = restArgs.includes("--all");
1409
+ const names = restArgs.filter((a) => !a.startsWith("--"));
1410
+ return /* @__PURE__ */ jsx9(
1411
+ Update,
1412
+ {
1413
+ directSkills: names.length > 0 ? names : null,
1414
+ updateAll: all
1415
+ }
1416
+ );
1417
+ }
1138
1418
  case "remove":
1139
1419
  if (restArgs.length === 0) {
1140
1420
  return /* @__PURE__ */ jsxs9(Text10, { color: "red", children: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hiver/skills",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "Shared Claude Code skills & agents for Hiver teams",
5
5
  "type": "module",
6
6
  "bin": {