@hiver/skills 1.0.3 → 1.0.4

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 +277 -102
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -239,6 +239,15 @@ function getAgentMeta(collectionName, agentName) {
239
239
  dependencies: Array.isArray(fm.dependencies) ? fm.dependencies : []
240
240
  };
241
241
  }
242
+ function findCollectionsContaining(name, type) {
243
+ const matches = [];
244
+ for (const col of listCollections()) {
245
+ const { skills, agents } = scanCollection(col);
246
+ if (type === "skill" && skills.includes(name)) matches.push(col);
247
+ if (type === "agent" && agents.includes(name)) matches.push(col);
248
+ }
249
+ return matches;
250
+ }
242
251
  function resolveDependencies(selected, selectedCollections) {
243
252
  const installedNames = /* @__PURE__ */ new Set([
244
253
  ...selected.skills.map((s) => s.name),
@@ -341,34 +350,8 @@ function Add({ directSkills }) {
341
350
  const [selectedType, setSelectedType] = useState3(null);
342
351
  const [toInstall, setToInstall] = useState3({ skills: [], agents: [] });
343
352
  const [results, setResults] = useState3({ skills: 0, agents: 0, deps: [] });
353
+ const [directMissing, setDirectMissing] = useState3([]);
344
354
  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
355
  function getAvailableTypes(col) {
373
356
  const { skills, agents } = scanCollection(col);
374
357
  const types = [];
@@ -378,11 +361,29 @@ function Add({ directSkills }) {
378
361
  }
379
362
  function handleTargetSelect(key) {
380
363
  setTargetKey(key);
381
- if (directSkills && directSkills.length > 0) return;
382
364
  setStep(STEPS.COLLECTIONS);
383
365
  }
384
366
  function handleCollectionSelect(col) {
385
367
  setSelectedCollection(col);
368
+ if (directSkills && directSkills.length > 0) {
369
+ const { skills, agents } = scanCollection(col);
370
+ const skillItems = [];
371
+ const agentItems = [];
372
+ const missing = [];
373
+ for (const name of directSkills) {
374
+ if (skills.includes(name)) skillItems.push({ collection: col, name });
375
+ else if (agents.includes(name)) agentItems.push({ collection: col, name });
376
+ else missing.push(`${name} (not in ${col})`);
377
+ }
378
+ setDirectMissing(missing);
379
+ setToInstall({ skills: skillItems, agents: agentItems });
380
+ if (skillItems.length === 0 && agentItems.length === 0) {
381
+ setStep(STEPS.DONE);
382
+ } else {
383
+ setStep(STEPS.INSTALLING);
384
+ }
385
+ return;
386
+ }
386
387
  setToInstall({ skills: [], agents: [] });
387
388
  const types = getAvailableTypes(col);
388
389
  if (types.length === 1) {
@@ -466,8 +467,24 @@ function Add({ directSkills }) {
466
467
  );
467
468
  }
468
469
  if (step === STEPS.COLLECTIONS) {
469
- const collections = listCollections();
470
+ let collections = listCollections();
471
+ if (directSkills && directSkills.length > 0) {
472
+ const union = /* @__PURE__ */ new Set();
473
+ for (const name of directSkills) {
474
+ for (const c of findCollectionsContaining(name, "skill")) union.add(c);
475
+ for (const c of findCollectionsContaining(name, "agent")) union.add(c);
476
+ }
477
+ collections = collections.filter((c) => union.has(c));
478
+ }
470
479
  const items = collections.map((c) => ({ label: c, value: c }));
480
+ if (items.length === 0) {
481
+ setTimeout(() => exit(), 100);
482
+ return /* @__PURE__ */ jsxs4(ErrorMsg, { children: [
483
+ "None of: ",
484
+ directSkills.join(", "),
485
+ " found in any collection."
486
+ ] });
487
+ }
471
488
  return /* @__PURE__ */ jsx3(
472
489
  Select,
473
490
  {
@@ -530,6 +547,10 @@ function Add({ directSkills }) {
530
547
  if (step === STEPS.DONE) {
531
548
  setTimeout(() => exit(), 100);
532
549
  return /* @__PURE__ */ jsxs4(Box3, { flexDirection: "column", marginTop: 1, children: [
550
+ directMissing.length > 0 && /* @__PURE__ */ jsx3(Box3, { marginBottom: 1, children: /* @__PURE__ */ jsxs4(ErrorMsg, { children: [
551
+ "Not found in any collection: ",
552
+ directMissing.join(", ")
553
+ ] }) }),
533
554
  results.deps.length > 0 && /* @__PURE__ */ jsxs4(Box3, { flexDirection: "column", marginBottom: 1, children: [
534
555
  /* @__PURE__ */ jsx3(Info, { children: "Auto-installed dependencies:" }),
535
556
  results.deps.map((d) => /* @__PURE__ */ jsxs4(Text4, { color: "cyan", children: [
@@ -690,88 +711,161 @@ function hasChanges(localContent, latestContent) {
690
711
  import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
691
712
  var STEPS2 = {
692
713
  TARGET: "target",
693
- CHECKING: "checking",
714
+ COLLECTION: "collection",
715
+ ITEMS: "items",
716
+ RESOLVE: "resolve",
694
717
  CONFIRM: "confirm",
695
718
  DONE: "done"
696
719
  };
697
- function Update() {
720
+ 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";
725
+ return null;
726
+ }
727
+ function Update({ directSkills }) {
698
728
  const { exit } = useApp3();
699
729
  const [step, setStep] = useState4(STEPS2.TARGET);
700
730
  const [targetKey, setTargetKey] = useState4(null);
701
- const [changedItems, setChangedItems] = useState4([]);
731
+ const [selectedCollection, setSelectedCollection] = useState4(null);
732
+ const [queue, setQueue] = useState4([]);
702
733
  const [currentIdx, setCurrentIdx] = useState4(0);
734
+ const [currentPatch, setCurrentPatch] = useState4(null);
703
735
  const [updatedCount, setUpdatedCount] = useState4(0);
704
736
  const [skippedCount, setSkippedCount] = useState4(0);
737
+ const [installedCount, setInstalledCount] = useState4(0);
738
+ const [missingNames, setMissingNames] = useState4([]);
739
+ const [directInfo, setDirectInfo] = useState4([]);
705
740
  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 };
741
+ function handleTargetSelect(key) {
742
+ setTargetKey(key);
743
+ if (directSkills && directSkills.length > 0) {
744
+ const targetConfig = targets[key];
745
+ const info = [];
746
+ const missing = [];
747
+ for (const name of directSkills) {
748
+ let type = detectType(name, targetConfig, destRoot);
749
+ let installed = type !== null;
750
+ if (!type) {
751
+ if (findCollectionsContaining(name, "skill").length > 0) type = "skill";
752
+ else if (findCollectionsContaining(name, "agent").length > 0) type = "agent";
753
+ }
754
+ if (!type) {
755
+ missing.push(name);
756
+ continue;
757
+ }
758
+ const cols = findCollectionsContaining(name, type);
759
+ if (cols.length === 0) {
760
+ missing.push(name);
761
+ continue;
762
+ }
763
+ info.push({ name, type, installed, collections: cols });
764
+ }
765
+ setMissingNames(missing);
766
+ setDirectInfo(info);
767
+ if (info.length === 0) {
768
+ setStep(STEPS2.DONE);
769
+ return;
770
+ }
711
771
  }
712
- return null;
772
+ setStep(STEPS2.COLLECTION);
713
773
  }
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
- });
774
+ function handleCollectionSelect(collection) {
775
+ setSelectedCollection(collection);
776
+ if (directSkills && directSkills.length > 0) {
777
+ const items = [];
778
+ const skipped = [];
779
+ for (const entry of directInfo) {
780
+ if (entry.collections.includes(collection)) {
781
+ items.push({ name: entry.name, type: entry.type, installed: entry.installed });
782
+ } else {
783
+ skipped.push(`${entry.name} (not in ${collection})`);
784
+ }
734
785
  }
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
- });
786
+ if (skipped.length > 0) {
787
+ setMissingNames((prev) => [...prev, ...skipped]);
788
+ }
789
+ setQueue(items);
790
+ setCurrentIdx(0);
791
+ if (items.length === 0) {
792
+ setStep(STEPS2.DONE);
793
+ } else {
794
+ setStep(STEPS2.RESOLVE);
751
795
  }
796
+ } else {
797
+ setStep(STEPS2.ITEMS);
752
798
  }
753
- setChangedItems(changed);
754
- if (changed.length === 0) {
799
+ }
800
+ function handleItemsSubmit(values) {
801
+ const items = values.map((v) => {
802
+ const [type, name] = v.split(":");
803
+ return { name, type, installed: true };
804
+ });
805
+ setQueue(items);
806
+ setCurrentIdx(0);
807
+ if (items.length === 0) {
755
808
  setStep(STEPS2.DONE);
756
809
  } else {
757
- setCurrentIdx(0);
758
- setStep(STEPS2.CONFIRM);
810
+ setStep(STEPS2.RESOLVE);
759
811
  }
760
- }, [step]);
761
- function handleTargetSelect(key) {
762
- setTargetKey(key);
763
- setStep(STEPS2.CHECKING);
812
+ }
813
+ useEffect2(() => {
814
+ if (step !== STEPS2.RESOLVE) return;
815
+ const item = queue[currentIdx];
816
+ if (!item) {
817
+ setStep(STEPS2.DONE);
818
+ return;
819
+ }
820
+ processItem(item);
821
+ }, [step, currentIdx]);
822
+ async function processItem(item) {
823
+ const targetConfig = targets[targetKey];
824
+ const collection = selectedCollection;
825
+ if (!item.installed) {
826
+ if (item.type === "skill") {
827
+ await installSkill(collection, item.name, targetConfig, destRoot);
828
+ } else {
829
+ await installAgent(collection, item.name, targetConfig, destRoot);
830
+ }
831
+ setInstalledCount((c) => c + 1);
832
+ advance();
833
+ return;
834
+ }
835
+ let localFile;
836
+ let latestFile;
837
+ 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`);
843
+ }
844
+ if (!fs3.existsSync(localFile) || !fs3.existsSync(latestFile)) {
845
+ advance();
846
+ return;
847
+ }
848
+ const localContent = fs3.readFileSync(localFile, "utf-8");
849
+ const latestContent = fs3.readFileSync(latestFile, "utf-8");
850
+ if (!hasChanges(localContent, latestContent)) {
851
+ setSkippedCount((c) => c + 1);
852
+ advance();
853
+ return;
854
+ }
855
+ const filename = item.type === "skill" ? "SKILL.md" : `${item.name}.md`;
856
+ setCurrentPatch(computeDiff(filename, localContent, latestContent));
857
+ setStep(STEPS2.CONFIRM);
764
858
  }
765
859
  function handleConfirm(yes) {
766
- const item = changedItems[currentIdx];
860
+ const item = queue[currentIdx];
767
861
  const targetConfig = targets[targetKey];
768
862
  if (yes) {
769
863
  if (item.type === "skill") {
770
- const src = path3.join(getCollectionsDir(), item.collection, "skills", item.name);
864
+ const src = path3.join(getCollectionsDir(), selectedCollection, "skills", item.name);
771
865
  const dest = path3.join(destRoot, targetConfig.skillsDir, item.name);
772
866
  fs3.copySync(src, dest, { overwrite: true });
773
867
  } else {
774
- const src = path3.join(getCollectionsDir(), item.collection, "agents", `${item.name}.md`);
868
+ const src = path3.join(getCollectionsDir(), selectedCollection, "agents", `${item.name}.md`);
775
869
  const dest = path3.join(destRoot, targetConfig.agentsDir, `${item.name}.md`);
776
870
  fs3.copySync(src, dest, { overwrite: true });
777
871
  }
@@ -779,39 +873,119 @@ function Update() {
779
873
  } else {
780
874
  setSkippedCount((c) => c + 1);
781
875
  }
782
- if (currentIdx + 1 < changedItems.length) {
876
+ advance();
877
+ }
878
+ function advance() {
879
+ setCurrentPatch(null);
880
+ if (currentIdx + 1 < queue.length) {
783
881
  setCurrentIdx((i) => i + 1);
882
+ setStep(STEPS2.RESOLVE);
784
883
  } else {
785
884
  setStep(STEPS2.DONE);
786
885
  }
787
886
  }
788
887
  if (step === STEPS2.TARGET) {
789
- return /* @__PURE__ */ jsx7(Select, { items: targetList, onSelect: handleTargetSelect, onBack: () => exit(), message: "Which target to update?" });
888
+ return /* @__PURE__ */ jsx7(
889
+ Select,
890
+ {
891
+ items: targetList,
892
+ onSelect: handleTargetSelect,
893
+ onBack: () => exit(),
894
+ message: "Which target to update?"
895
+ }
896
+ );
897
+ }
898
+ if (step === STEPS2.COLLECTION) {
899
+ let collectionNames;
900
+ if (directSkills && directSkills.length > 0) {
901
+ const union = /* @__PURE__ */ new Set();
902
+ for (const entry of directInfo) {
903
+ for (const c of entry.collections) union.add(c);
904
+ }
905
+ collectionNames = listCollections().filter((c) => union.has(c));
906
+ } else {
907
+ collectionNames = listCollections();
908
+ }
909
+ const items = collectionNames.map((c) => ({ label: c, value: c }));
910
+ return /* @__PURE__ */ jsx7(
911
+ Select,
912
+ {
913
+ items,
914
+ onSelect: handleCollectionSelect,
915
+ onBack: () => setStep(STEPS2.TARGET),
916
+ message: "Pick collection to update from:"
917
+ }
918
+ );
919
+ }
920
+ if (step === STEPS2.ITEMS) {
921
+ const targetConfig = targets[targetKey];
922
+ const installed = listInstalled(targetConfig, destRoot);
923
+ const { skills, agents } = scanCollection(selectedCollection);
924
+ const items = [
925
+ ...installed.skills.filter((name) => skills.includes(name)).map((name) => ({
926
+ label: `${name} (skill)`,
927
+ value: `skill:${name}`,
928
+ checked: false
929
+ })),
930
+ ...installed.agents.filter((name) => agents.includes(name)).map((name) => ({
931
+ label: `${name} (agent)`,
932
+ value: `agent:${name}`,
933
+ checked: false
934
+ }))
935
+ ];
936
+ if (items.length === 0) {
937
+ setTimeout(() => exit(), 100);
938
+ return /* @__PURE__ */ jsxs7(Info, { children: [
939
+ 'No installed skills/agents from "',
940
+ selectedCollection,
941
+ '" under ',
942
+ targetConfig.skillsDir,
943
+ "/."
944
+ ] });
945
+ }
946
+ return /* @__PURE__ */ jsx7(
947
+ MultiSelect,
948
+ {
949
+ items,
950
+ onSubmit: handleItemsSubmit,
951
+ onBack: () => setStep(STEPS2.COLLECTION),
952
+ message: `Select skills/agents from "${selectedCollection}" to update:`
953
+ }
954
+ );
790
955
  }
791
- if (step === STEPS2.CHECKING) {
792
- return /* @__PURE__ */ jsx7(Text8, { color: "yellow", children: "Checking for updates..." });
956
+ if (step === STEPS2.RESOLVE) {
957
+ return /* @__PURE__ */ jsx7(Text8, { color: "yellow", children: "Checking..." });
793
958
  }
794
959
  if (step === STEPS2.CONFIRM) {
795
- const item = changedItems[currentIdx];
960
+ const item = queue[currentIdx];
796
961
  return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
797
962
  /* @__PURE__ */ jsxs7(Warning, { children: [
798
963
  item.name,
799
- ": local differs from latest"
964
+ ": local differs from ",
965
+ selectedCollection
800
966
  ] }),
801
- /* @__PURE__ */ jsx7(DiffView, { patch: item.patch }),
967
+ /* @__PURE__ */ jsx7(DiffView, { patch: currentPatch }),
802
968
  /* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx7(Confirm, { message: `Update ${item.name}?`, onConfirm: handleConfirm }) })
803
969
  ] });
804
970
  }
805
971
  if (step === STEPS2.DONE) {
806
972
  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"
973
+ if (queue.length === 0 && missingNames.length === 0) {
974
+ return /* @__PURE__ */ jsx7(Success, { children: "Nothing to update." });
975
+ }
976
+ return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
977
+ missingNames.length > 0 && /* @__PURE__ */ jsxs7(ErrorMsg, { children: [
978
+ "Skipped: ",
979
+ missingNames.join(", ")
980
+ ] }),
981
+ /* @__PURE__ */ jsxs7(Success, { children: [
982
+ updatedCount,
983
+ " updated, ",
984
+ installedCount,
985
+ " installed, ",
986
+ skippedCount,
987
+ " skipped"
988
+ ] })
815
989
  ] });
816
990
  }
817
991
  return null;
@@ -914,7 +1088,7 @@ function Help() {
914
1088
  const commands = [
915
1089
  { name: "add [skills...]", desc: "Install skills/agents (interactive if no args)" },
916
1090
  { name: "list", desc: "List all available skills and agents" },
917
- { name: "update", desc: "Update installed skills to latest version" },
1091
+ { name: "update [skills...]", desc: "Update installed skills (interactive if no args)" },
918
1092
  { name: "remove <skills>", desc: "Remove installed skills/agents" },
919
1093
  { name: "help", desc: "Show this help message" }
920
1094
  ];
@@ -923,6 +1097,7 @@ function Help() {
923
1097
  "npx @hiver/skills add discuss-problem create-prd",
924
1098
  "npx @hiver/skills list",
925
1099
  "npx @hiver/skills update",
1100
+ "npx @hiver/skills update build-component",
926
1101
  "npx @hiver/skills remove discuss-problem"
927
1102
  ];
928
1103
  return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginTop: 1, marginBottom: 1, children: [
@@ -959,7 +1134,7 @@ function App() {
959
1134
  case "list":
960
1135
  return /* @__PURE__ */ jsx9(List, {});
961
1136
  case "update":
962
- return /* @__PURE__ */ jsx9(Update, {});
1137
+ return /* @__PURE__ */ jsx9(Update, { directSkills: restArgs.length > 0 ? restArgs : null });
963
1138
  case "remove":
964
1139
  if (restArgs.length === 0) {
965
1140
  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.3",
3
+ "version": "1.0.4",
4
4
  "description": "Shared Claude Code skills & agents for Hiver teams",
5
5
  "type": "module",
6
6
  "bin": {