@hiver/skills 1.0.4 → 1.0.5

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 +117 -34
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -646,8 +646,8 @@ function List() {
646
646
  // src/commands/Update.jsx
647
647
  import React8, { useState as useState4, useEffect as useEffect2 } from "react";
648
648
  import { Box as Box7, Text as Text8, useApp as useApp3 } from "ink";
649
- import path3 from "path";
650
- import fs3 from "fs-extra";
649
+ import path4 from "path";
650
+ import fs4 from "fs-extra";
651
651
 
652
652
  // src/components/Confirm.jsx
653
653
  import React6 from "react";
@@ -692,6 +692,8 @@ function DiffView({ patch }) {
692
692
  }
693
693
 
694
694
  // src/utils/diffUtil.js
695
+ import path3 from "path";
696
+ import fs3 from "fs-extra";
695
697
  import { createTwoFilesPatch } from "diff";
696
698
  function computeDiff(filePath, localContent, latestContent) {
697
699
  return createTwoFilesPatch(
@@ -706,6 +708,63 @@ function computeDiff(filePath, localContent, latestContent) {
706
708
  function hasChanges(localContent, latestContent) {
707
709
  return localContent.trim() !== latestContent.trim();
708
710
  }
711
+ function walkFiles(dir) {
712
+ const out = [];
713
+ if (!fs3.existsSync(dir)) return out;
714
+ const entries = fs3.readdirSync(dir, { withFileTypes: true });
715
+ for (const entry of entries) {
716
+ const full = path3.join(dir, entry.name);
717
+ if (entry.isDirectory()) {
718
+ for (const sub of walkFiles(full)) out.push(path3.join(entry.name, sub));
719
+ } else if (entry.isFile()) {
720
+ out.push(entry.name);
721
+ }
722
+ }
723
+ return out;
724
+ }
725
+ function isBinary(buf) {
726
+ const sample = buf.subarray(0, Math.min(buf.length, 8e3));
727
+ for (let i = 0; i < sample.length; i++) {
728
+ if (sample[i] === 0) return true;
729
+ }
730
+ return false;
731
+ }
732
+ function hasDirChanges(localDir, latestDir) {
733
+ const localFiles = new Set(walkFiles(localDir));
734
+ const latestFiles = new Set(walkFiles(latestDir));
735
+ if (localFiles.size !== latestFiles.size) return true;
736
+ for (const rel of latestFiles) {
737
+ if (!localFiles.has(rel)) return true;
738
+ const a = fs3.readFileSync(path3.join(localDir, rel));
739
+ const b = fs3.readFileSync(path3.join(latestDir, rel));
740
+ if (!a.equals(b)) return true;
741
+ }
742
+ for (const rel of localFiles) {
743
+ if (!latestFiles.has(rel)) return true;
744
+ }
745
+ return false;
746
+ }
747
+ function computeDirDiff(localDir, latestDir) {
748
+ const localFiles = new Set(walkFiles(localDir));
749
+ const latestFiles = new Set(walkFiles(latestDir));
750
+ const allFiles = /* @__PURE__ */ new Set([...localFiles, ...latestFiles]);
751
+ const parts = [];
752
+ for (const rel of [...allFiles].sort()) {
753
+ const localBuf = localFiles.has(rel) ? fs3.readFileSync(path3.join(localDir, rel)) : Buffer.alloc(0);
754
+ const latestBuf = latestFiles.has(rel) ? fs3.readFileSync(path3.join(latestDir, rel)) : Buffer.alloc(0);
755
+ if (localBuf.equals(latestBuf)) continue;
756
+ if (isBinary(localBuf) || isBinary(latestBuf)) {
757
+ const verb = !localFiles.has(rel) ? "added" : !latestFiles.has(rel) ? "removed" : "changed";
758
+ parts.push(`Binary file ${rel} ${verb} (${localBuf.length} \u2192 ${latestBuf.length} bytes)`);
759
+ continue;
760
+ }
761
+ const localContent = localBuf.toString("utf-8");
762
+ const latestContent = latestBuf.toString("utf-8");
763
+ if (localContent.trim() === latestContent.trim()) continue;
764
+ parts.push(computeDiff(rel, localContent, latestContent));
765
+ }
766
+ return parts.join("\n");
767
+ }
709
768
 
710
769
  // src/commands/Update.jsx
711
770
  import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
@@ -718,10 +777,10 @@ var STEPS2 = {
718
777
  DONE: "done"
719
778
  };
720
779
  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";
780
+ const skillPath = path4.join(destRoot, targetConfig.skillsDir, name, "SKILL.md");
781
+ if (fs4.existsSync(skillPath)) return "skill";
782
+ const agentPath = path4.join(destRoot, targetConfig.agentsDir, `${name}.md`);
783
+ if (fs4.existsSync(agentPath)) return "agent";
725
784
  return null;
726
785
  }
727
786
  function Update({ directSkills }) {
@@ -737,6 +796,7 @@ function Update({ directSkills }) {
737
796
  const [installedCount, setInstalledCount] = useState4(0);
738
797
  const [missingNames, setMissingNames] = useState4([]);
739
798
  const [directInfo, setDirectInfo] = useState4([]);
799
+ const [skipReasons, setSkipReasons] = useState4([]);
740
800
  const destRoot = process.cwd();
741
801
  function handleTargetSelect(key) {
742
802
  setTargetKey(key);
@@ -832,46 +892,60 @@ function Update({ directSkills }) {
832
892
  advance();
833
893
  return;
834
894
  }
835
- let localFile;
836
- let latestFile;
837
895
  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`);
896
+ const localDir = path4.join(destRoot, targetConfig.skillsDir, item.name);
897
+ const latestDir = path4.join(getCollectionsDir(), collection, "skills", item.name);
898
+ if (!fs4.existsSync(localDir) || !fs4.existsSync(latestDir)) {
899
+ recordSkip(item.name, "source or local directory missing");
900
+ advance();
901
+ return;
902
+ }
903
+ if (!hasDirChanges(localDir, latestDir)) {
904
+ recordSkip(item.name, `already up-to-date with ${collection}`);
905
+ advance();
906
+ return;
907
+ }
908
+ setCurrentPatch(computeDirDiff(localDir, latestDir));
909
+ setStep(STEPS2.CONFIRM);
910
+ return;
843
911
  }
844
- if (!fs3.existsSync(localFile) || !fs3.existsSync(latestFile)) {
912
+ const localFile = path4.join(destRoot, targetConfig.agentsDir, `${item.name}.md`);
913
+ const latestFile = path4.join(getCollectionsDir(), collection, "agents", `${item.name}.md`);
914
+ if (!fs4.existsSync(localFile) || !fs4.existsSync(latestFile)) {
915
+ recordSkip(item.name, "source or local file missing");
845
916
  advance();
846
917
  return;
847
918
  }
848
- const localContent = fs3.readFileSync(localFile, "utf-8");
849
- const latestContent = fs3.readFileSync(latestFile, "utf-8");
919
+ const localContent = fs4.readFileSync(localFile, "utf-8");
920
+ const latestContent = fs4.readFileSync(latestFile, "utf-8");
850
921
  if (!hasChanges(localContent, latestContent)) {
851
- setSkippedCount((c) => c + 1);
922
+ recordSkip(item.name, `already up-to-date with ${collection}`);
852
923
  advance();
853
924
  return;
854
925
  }
855
- const filename = item.type === "skill" ? "SKILL.md" : `${item.name}.md`;
856
- setCurrentPatch(computeDiff(filename, localContent, latestContent));
926
+ setCurrentPatch(computeDiff(`${item.name}.md`, localContent, latestContent));
857
927
  setStep(STEPS2.CONFIRM);
858
928
  }
929
+ function recordSkip(name, reason) {
930
+ setSkippedCount((c) => c + 1);
931
+ setSkipReasons((prev) => [...prev, { name, reason }]);
932
+ }
859
933
  function handleConfirm(yes) {
860
934
  const item = queue[currentIdx];
861
935
  const targetConfig = targets[targetKey];
862
936
  if (yes) {
863
937
  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 });
938
+ const src = path4.join(getCollectionsDir(), selectedCollection, "skills", item.name);
939
+ const dest = path4.join(destRoot, targetConfig.skillsDir, item.name);
940
+ fs4.copySync(src, dest, { overwrite: true });
867
941
  } 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 });
942
+ const src = path4.join(getCollectionsDir(), selectedCollection, "agents", `${item.name}.md`);
943
+ const dest = path4.join(destRoot, targetConfig.agentsDir, `${item.name}.md`);
944
+ fs4.copySync(src, dest, { overwrite: true });
871
945
  }
872
946
  setUpdatedCount((c) => c + 1);
873
947
  } else {
874
- setSkippedCount((c) => c + 1);
948
+ recordSkip(item.name, "declined");
875
949
  }
876
950
  advance();
877
951
  }
@@ -975,17 +1049,26 @@ function Update({ directSkills }) {
975
1049
  }
976
1050
  return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
977
1051
  missingNames.length > 0 && /* @__PURE__ */ jsxs7(ErrorMsg, { children: [
978
- "Skipped: ",
1052
+ "Not found: ",
979
1053
  missingNames.join(", ")
980
1054
  ] }),
981
- /* @__PURE__ */ jsxs7(Success, { children: [
1055
+ skipReasons.length > 0 && /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", marginTop: 1, children: [
1056
+ /* @__PURE__ */ jsx7(Info, { children: "Skipped:" }),
1057
+ skipReasons.map((s, i) => /* @__PURE__ */ jsxs7(Text8, { color: "gray", children: [
1058
+ " \u2022 ",
1059
+ s.name,
1060
+ " \u2014 ",
1061
+ s.reason
1062
+ ] }, `${s.name}-${i}`))
1063
+ ] }),
1064
+ /* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsxs7(Success, { children: [
982
1065
  updatedCount,
983
1066
  " updated, ",
984
1067
  installedCount,
985
1068
  " installed, ",
986
1069
  skippedCount,
987
1070
  " skipped"
988
- ] })
1071
+ ] }) })
989
1072
  ] });
990
1073
  }
991
1074
  return null;
@@ -994,8 +1077,8 @@ function Update({ directSkills }) {
994
1077
  // src/commands/Remove.jsx
995
1078
  import React9, { useState as useState5 } from "react";
996
1079
  import { Box as Box8, Text as Text9, useApp as useApp4 } from "ink";
997
- import path4 from "path";
998
- import fs4 from "fs-extra";
1080
+ import path5 from "path";
1081
+ import fs5 from "fs-extra";
999
1082
  import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1000
1083
  var STEPS3 = {
1001
1084
  TARGET: "target",
@@ -1015,9 +1098,9 @@ function Remove({ skillNames }) {
1015
1098
  const targetConfig = targets[key];
1016
1099
  const missing = [];
1017
1100
  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)) {
1101
+ const skillPath = path5.join(destRoot, targetConfig.skillsDir, name);
1102
+ const agentPath = path5.join(destRoot, targetConfig.agentsDir, `${name}.md`);
1103
+ if (!fs5.existsSync(skillPath) && !fs5.existsSync(agentPath)) {
1021
1104
  missing.push(name);
1022
1105
  }
1023
1106
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hiver/skills",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "Shared Claude Code skills & agents for Hiver teams",
5
5
  "type": "module",
6
6
  "bin": {