@jackss119/shelf 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/shelf.mjs +6 -0
- package/lib/commands/shelf.mjs +185 -50
- package/lib/manifest.mjs +2 -0
- package/package.json +1 -1
package/bin/shelf.mjs
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
cmdShelfSync,
|
|
8
8
|
cmdShelfInit,
|
|
9
9
|
cmdShelfAgents,
|
|
10
|
+
cmdShelfAdopt,
|
|
10
11
|
cmdShelfHome,
|
|
11
12
|
} from "../lib/commands/shelf.mjs";
|
|
12
13
|
|
|
@@ -26,6 +27,8 @@ Usage:
|
|
|
26
27
|
shelf init 项目接入(幂等):交互多选 agent 目标,或
|
|
27
28
|
--agents claude,codex,kimi / all
|
|
28
29
|
shelf agents [add <名>] 查看 / 追加 agent 目标(claude / codex / kimi)
|
|
30
|
+
shelf adopt 收编外来技能:实体目录迁入正本重链 + 未记账技能
|
|
31
|
+
登记进账本 local 段(不上货架)
|
|
29
32
|
shelf home [--update] 查看货架在哪、什么模式;--update 拉取最新
|
|
30
33
|
|
|
31
34
|
货架定位:SHELF_HOME 环境变量 > 本 clone > ~/.shelfrc > 托管档口 ~/.shelf/home
|
|
@@ -60,6 +63,9 @@ try {
|
|
|
60
63
|
case "agents":
|
|
61
64
|
await cmdShelfAgents(rest);
|
|
62
65
|
break;
|
|
66
|
+
case "adopt":
|
|
67
|
+
await cmdShelfAdopt(rest);
|
|
68
|
+
break;
|
|
63
69
|
case "home":
|
|
64
70
|
await cmdShelfHome(rest);
|
|
65
71
|
break;
|
package/lib/commands/shelf.mjs
CHANGED
|
@@ -751,19 +751,10 @@ function ensureGitignore(cwd, lines) {
|
|
|
751
751
|
return missing;
|
|
752
752
|
}
|
|
753
753
|
|
|
754
|
-
//
|
|
755
|
-
function refreshMirror(primaryAbs, mirrorAbs) {
|
|
756
|
-
if (!fs.existsSync(primaryAbs)) return false;
|
|
757
|
-
if (fs.existsSync(mirrorAbs) && contentHash(mirrorAbs) === contentHash(primaryAbs)) return false;
|
|
758
|
-
fs.rmSync(mirrorAbs, { recursive: true, force: true });
|
|
759
|
-
fs.mkdirSync(path.dirname(mirrorAbs), { recursive: true });
|
|
760
|
-
copyFiltered(primaryAbs, mirrorAbs);
|
|
761
|
-
return true;
|
|
762
|
-
}
|
|
754
|
+
// ---- agent 目标与技能正本(SHELF 决策 #24/#26)----
|
|
763
755
|
|
|
764
|
-
//
|
|
765
|
-
|
|
766
|
-
// ---- agent 目标(SHELF 决策 #24)----
|
|
756
|
+
// 项目里技能的唯一正本;所有 agent 目录都是指向它的链接
|
|
757
|
+
const CANONICAL_SKILLS_DIR = "ai/jaSkills";
|
|
767
758
|
|
|
768
759
|
const AGENT_TARGETS = {
|
|
769
760
|
claude: { skillsDir: ".claude/skills", doc: { input: "agents/claude/CLAUDE.md", dest: "CLAUDE.md" }, ignore: [".claude/", "CLAUDE.md"] },
|
|
@@ -772,7 +763,7 @@ const AGENT_TARGETS = {
|
|
|
772
763
|
};
|
|
773
764
|
const TARGET_PRIORITY = ["claude", "codex", "kimi"];
|
|
774
765
|
|
|
775
|
-
// "claude,codex" / "all" →
|
|
766
|
+
// "claude,codex" / "all" → 校验并按固定顺序去重
|
|
776
767
|
function parseTargetNames(spec) {
|
|
777
768
|
const names = spec === "all"
|
|
778
769
|
? [...TARGET_PRIORITY]
|
|
@@ -788,7 +779,7 @@ function parseTargetNames(spec) {
|
|
|
788
779
|
async function askTargets() {
|
|
789
780
|
const rl = makeLineReader();
|
|
790
781
|
try {
|
|
791
|
-
console.log("要接入哪些 agent
|
|
782
|
+
console.log("要接入哪些 agent?(各自的技能目录会链接到正本 " + CANONICAL_SKILLS_DIR + "/)");
|
|
792
783
|
TARGET_PRIORITY.forEach((n, i) => {
|
|
793
784
|
const t = AGENT_TARGETS[n];
|
|
794
785
|
console.log(" " + (i + 1) + ". " + n + " (" + t.skillsDir + "/ · " + t.doc.dest + ")");
|
|
@@ -807,12 +798,58 @@ async function askTargets() {
|
|
|
807
798
|
}
|
|
808
799
|
}
|
|
809
800
|
|
|
810
|
-
//
|
|
811
|
-
//
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
const
|
|
801
|
+
// 把 agent 的技能目录做成指向正本的链接(Windows junction 免管理员 / POSIX 相对 symlink)。
|
|
802
|
+
// 遇到老式实体目录(复制镜像时代)自动迁移:内容并入正本(同名跳过),原地替换为链接。
|
|
803
|
+
// 返回 "ok"(已正确)/ "created" / "fixed"
|
|
804
|
+
function ensureSkillsLink(cwd, targetName) {
|
|
805
|
+
const rel = AGENT_TARGETS[targetName].skillsDir;
|
|
806
|
+
const linkPath = path.join(cwd, ...rel.split("/"));
|
|
807
|
+
const canonical = path.join(cwd, ...CANONICAL_SKILLS_DIR.split("/"));
|
|
808
|
+
fs.mkdirSync(canonical, { recursive: true });
|
|
809
|
+
fs.mkdirSync(path.dirname(linkPath), { recursive: true });
|
|
810
|
+
|
|
811
|
+
let st = null;
|
|
812
|
+
try { st = fs.lstatSync(linkPath); } catch { st = null; }
|
|
813
|
+
|
|
814
|
+
if (st && st.isSymbolicLink()) {
|
|
815
|
+
try {
|
|
816
|
+
if (fs.realpathSync(linkPath) === fs.realpathSync(canonical)) return "ok";
|
|
817
|
+
} catch { /* 断链 → 重建 */ }
|
|
818
|
+
fs.rmSync(linkPath, { recursive: true, force: true });
|
|
819
|
+
} else if (st && st.isDirectory()) {
|
|
820
|
+
let moved = 0;
|
|
821
|
+
let skipped = 0;
|
|
822
|
+
for (const e of fs.readdirSync(linkPath, { withFileTypes: true })) {
|
|
823
|
+
if (IGNORE_NAMES.has(e.name)) continue;
|
|
824
|
+
const from = path.join(linkPath, e.name);
|
|
825
|
+
const to = path.join(canonical, e.name);
|
|
826
|
+
if (fs.existsSync(to)) {
|
|
827
|
+
skipped++;
|
|
828
|
+
continue;
|
|
829
|
+
}
|
|
830
|
+
fs.cpSync(from, to, { recursive: true });
|
|
831
|
+
moved++;
|
|
832
|
+
}
|
|
833
|
+
fs.rmSync(linkPath, { recursive: true, force: true });
|
|
834
|
+
if (moved || skipped) {
|
|
835
|
+
console.log("↪ " + rel + " 原为实体目录:迁入正本 " + moved + " 项"
|
|
836
|
+
+ (skipped ? ",同名跳过 " + skipped + " 项" : "") + ",已替换为链接");
|
|
837
|
+
}
|
|
838
|
+
} else if (st) {
|
|
839
|
+
fs.rmSync(linkPath, { force: true });
|
|
840
|
+
}
|
|
815
841
|
|
|
842
|
+
if (process.platform === "win32") {
|
|
843
|
+
fs.symlinkSync(canonical, linkPath, "junction");
|
|
844
|
+
} else {
|
|
845
|
+
fs.symlinkSync(path.relative(path.dirname(linkPath), canonical), linkPath, "dir");
|
|
846
|
+
}
|
|
847
|
+
return st ? "fixed" : "created";
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
// 植入引擎(init 与 agents add 共用):协议文档去重植入 + multica/JASKILL +
|
|
851
|
+
// common 技能拉进正本 ai/jaSkills + 各目标技能目录链接化 + gitignore
|
|
852
|
+
async function plantForTargets(ctx, manifest, targets) {
|
|
816
853
|
const plan = [];
|
|
817
854
|
const seenDoc = new Set();
|
|
818
855
|
for (const n of targets) {
|
|
@@ -830,14 +867,12 @@ async function plantForTargets(ctx, manifest, targets) {
|
|
|
830
867
|
if (!e.isDirectory() || IGNORE_NAMES.has(e.name)) continue;
|
|
831
868
|
plan.push({
|
|
832
869
|
input: common.realRel + "/" + e.name,
|
|
833
|
-
dest:
|
|
834
|
-
mirrors: mirrorDirs.map((d) => d + "/" + e.name),
|
|
870
|
+
dest: CANONICAL_SKILLS_DIR + "/" + e.name,
|
|
835
871
|
});
|
|
836
872
|
}
|
|
837
873
|
}
|
|
838
874
|
|
|
839
875
|
const counters = newCounters();
|
|
840
|
-
let mirrored = 0;
|
|
841
876
|
for (const item of plan) {
|
|
842
877
|
const hit = resolveShelfPath(ctx.shelfDir, item.input);
|
|
843
878
|
if (!hit) {
|
|
@@ -846,35 +881,30 @@ async function plantForTargets(ctx, manifest, targets) {
|
|
|
846
881
|
: "✗ 货架上找不到 " + item.input + "(检查货架是否最新)");
|
|
847
882
|
continue;
|
|
848
883
|
}
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
if (refreshMirror(destAbs, path.resolve(process.cwd(), m))) mirrored++;
|
|
856
|
-
}
|
|
857
|
-
}
|
|
884
|
+
await pullEntry(ctx, hit.realRel, manifest, counters, safeAsk, path.resolve(process.cwd(), item.dest));
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
let linksMade = 0;
|
|
888
|
+
for (const n of targets) {
|
|
889
|
+
if (ensureSkillsLink(process.cwd(), n) !== "ok") linksMade++;
|
|
858
890
|
}
|
|
859
891
|
|
|
860
892
|
const ignoreLines = [...new Set(targets.flatMap((n) => AGENT_TARGETS[n].ignore))];
|
|
861
893
|
const added = ensureGitignore(process.cwd(), ignoreLines);
|
|
862
|
-
return { counters,
|
|
894
|
+
return { counters, linksMade, added };
|
|
863
895
|
}
|
|
864
896
|
|
|
865
897
|
function printPlantResult(targets, r) {
|
|
866
898
|
printSummary(r.counters);
|
|
867
|
-
if (r.
|
|
899
|
+
if (r.linksMade) console.log("⛓ 建立/修复链接 " + r.linksMade + " 个");
|
|
868
900
|
if (r.added.length) console.log("✓ .gitignore 补行: " + r.added.join(", "));
|
|
869
901
|
console.log("");
|
|
870
902
|
console.log("工作区已接入货架(agent 目标: " + targets.join(" + ") + "):");
|
|
871
903
|
const docs = [...new Set(targets.map((n) => AGENT_TARGETS[n].doc.dest))].join(" / ");
|
|
872
904
|
console.log(" " + docs + " 工作协议(真源在货架,shelf sync 保持最新)");
|
|
873
905
|
console.log(" ai/JASKILL.md 基础技能名册");
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
: "";
|
|
877
|
-
console.log(" " + AGENT_TARGETS[targets[0]].skillsDir + "/ common 技能正本" + mirrorText);
|
|
906
|
+
console.log(" " + CANONICAL_SKILLS_DIR + "/ 技能唯一正本(改技能、拉技能都在这)");
|
|
907
|
+
console.log(" " + targets.map((n) => AGENT_TARGETS[n].skillsDir).join(" · ") + " → 指向正本的链接");
|
|
878
908
|
console.log(" .shelf.json 记账本(进项目 git,队友 clone 后 shelf init 即还原)");
|
|
879
909
|
}
|
|
880
910
|
|
|
@@ -897,7 +927,7 @@ export async function cmdShelfInit(argv = []) {
|
|
|
897
927
|
targets = parseTargetNames(agentsFlag);
|
|
898
928
|
} else if (manifest.agents?.length) {
|
|
899
929
|
targets = TARGET_PRIORITY.filter((n) => manifest.agents.includes(n));
|
|
900
|
-
console.log("(沿用已配置的 agent 目标: " + targets.join(", ") + ";调整用 shelf agents add 或 --agents
|
|
930
|
+
console.log("(沿用已配置的 agent 目标: " + targets.join(", ") + ";调整用 shelf agents add 或 --agents)");
|
|
901
931
|
} else if (INTERACTIVE) {
|
|
902
932
|
targets = await askTargets();
|
|
903
933
|
if (!targets || !targets.length) {
|
|
@@ -911,8 +941,27 @@ export async function cmdShelfInit(argv = []) {
|
|
|
911
941
|
|
|
912
942
|
const result = await plantForTargets(ctx, manifest, targets);
|
|
913
943
|
manifest.agents = targets;
|
|
944
|
+
|
|
945
|
+
// 账本还原(2026-08-30 用户确认):标准植入清单之外、账上有而本地缺失的货架物品一并补拉,
|
|
946
|
+
// 换设备后一条 init 全就位,无需再跑 sync
|
|
947
|
+
let restored = 0;
|
|
948
|
+
for (const [key, rec] of Object.entries(manifest.shelf ?? {})) {
|
|
949
|
+
if (!rec.localPath) continue;
|
|
950
|
+
const destAbs = path.resolve(process.cwd(), rec.localPath);
|
|
951
|
+
if (fs.existsSync(destAbs)) continue;
|
|
952
|
+
const hit = resolveShelfPath(ctx.shelfDir, key);
|
|
953
|
+
if (!hit) {
|
|
954
|
+
console.log("⚠ 账上物品 " + displayPath(key) + " 在货架上已不在原位——跑 shelf sync 处理搬家/下架");
|
|
955
|
+
continue;
|
|
956
|
+
}
|
|
957
|
+
await pullEntry(ctx, hit.realRel, manifest, result.counters, safeAsk, destAbs);
|
|
958
|
+
restored++;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
await reconcileLocalSkills(manifest);
|
|
914
962
|
saveManifest(manifest);
|
|
915
963
|
printPlantResult(targets, result);
|
|
964
|
+
if (restored) console.log("↺ 按账本补还原 " + restored + " 项(标准清单之外)");
|
|
916
965
|
} finally {
|
|
917
966
|
ctx.cleanup();
|
|
918
967
|
}
|
|
@@ -926,10 +975,11 @@ export async function cmdShelfAgents(argv) {
|
|
|
926
975
|
const sub = argv[0];
|
|
927
976
|
|
|
928
977
|
if (sub === undefined) {
|
|
929
|
-
console.log("
|
|
978
|
+
console.log("技能正本: " + CANONICAL_SKILLS_DIR + "/(改技能、拉技能都在这)");
|
|
979
|
+
console.log("已配置: " + (current.length ? current.join(", ") : "(无——先跑 shelf init)"));
|
|
930
980
|
for (const n of TARGET_PRIORITY) {
|
|
931
981
|
const t = AGENT_TARGETS[n];
|
|
932
|
-
console.log(" " + (current.includes(n) ? "●" : "○") + " " + n + " " + t.skillsDir + "/ · " + t.doc.dest);
|
|
982
|
+
console.log(" " + (current.includes(n) ? "●" : "○") + " " + n + " " + t.skillsDir + "/ → 链接 · " + t.doc.dest);
|
|
933
983
|
}
|
|
934
984
|
console.log("添加: shelf agents add <名>");
|
|
935
985
|
return;
|
|
@@ -959,6 +1009,88 @@ export async function cmdShelfAgents(argv) {
|
|
|
959
1009
|
}
|
|
960
1010
|
}
|
|
961
1011
|
|
|
1012
|
+
// ---- 本地技能台账(SHELF 决策 #27 · 增量对账)----
|
|
1013
|
+
|
|
1014
|
+
// local 段维护:在场→刷指纹;升格→移除;缺失→告警但保留记录(仅 adopt 交互提供清账);
|
|
1015
|
+
// 新出现的未追踪目录→登记。init / sync / adopt 共用。
|
|
1016
|
+
async function reconcileLocalSkills(manifest, { allowPrune = false } = {}) {
|
|
1017
|
+
const canonical = path.join(process.cwd(), ...CANONICAL_SKILLS_DIR.split("/"));
|
|
1018
|
+
const tracked = new Set(
|
|
1019
|
+
Object.values(manifest.shelf ?? {}).map((e) => toPosix(e.localPath ?? "")),
|
|
1020
|
+
);
|
|
1021
|
+
const prev = manifest.local ?? {};
|
|
1022
|
+
const next = {};
|
|
1023
|
+
const c = { registered: 0, refreshed: 0, missing: 0, graduated: 0 };
|
|
1024
|
+
|
|
1025
|
+
const dirs = fs.existsSync(canonical)
|
|
1026
|
+
? fs.readdirSync(canonical, { withFileTypes: true })
|
|
1027
|
+
.filter((e) => e.isDirectory() && !IGNORE_NAMES.has(e.name))
|
|
1028
|
+
.map((e) => e.name)
|
|
1029
|
+
: [];
|
|
1030
|
+
const present = new Set(dirs);
|
|
1031
|
+
|
|
1032
|
+
for (const [name, entry] of Object.entries(prev)) {
|
|
1033
|
+
if (tracked.has(CANONICAL_SKILLS_DIR + "/" + name)) {
|
|
1034
|
+
c.graduated++;
|
|
1035
|
+
continue;
|
|
1036
|
+
}
|
|
1037
|
+
if (present.has(name)) {
|
|
1038
|
+
const hash = contentHash(path.join(canonical, name));
|
|
1039
|
+
if (hash !== entry.contentHash) c.refreshed++;
|
|
1040
|
+
next[name] = { ...entry, contentHash: hash };
|
|
1041
|
+
continue;
|
|
1042
|
+
}
|
|
1043
|
+
c.missing++;
|
|
1044
|
+
console.log("⚠ 登记过的本地技能缺失: " + name + "(登记于 " + (entry.addedAt ?? "?")
|
|
1045
|
+
+ ",上次指纹 " + String(entry.contentHash ?? "").slice(7, 19) + "…)——重装后跑 shelf sync 即恢复入账");
|
|
1046
|
+
if (allowPrune && INTERACTIVE) {
|
|
1047
|
+
const act = await choose(" [k]保留记录 / [r]清账? ", [{ key: "k" }, { key: "r" }]);
|
|
1048
|
+
if (act === "r") continue;
|
|
1049
|
+
}
|
|
1050
|
+
next[name] = entry;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
for (const name of dirs) {
|
|
1054
|
+
if (next[name] || tracked.has(CANONICAL_SKILLS_DIR + "/" + name)) continue;
|
|
1055
|
+
next[name] = {
|
|
1056
|
+
contentHash: contentHash(path.join(canonical, name)),
|
|
1057
|
+
addedAt: todayISO(),
|
|
1058
|
+
origin: "adopted",
|
|
1059
|
+
};
|
|
1060
|
+
console.log("✚ 登记本地技能: " + name);
|
|
1061
|
+
c.registered++;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
manifest.local = next;
|
|
1065
|
+
return c;
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
// ---- 子命令:adopt(收编外来技能,SHELF 决策 #27)----
|
|
1069
|
+
|
|
1070
|
+
export async function cmdShelfAdopt() {
|
|
1071
|
+
const manifest = loadManifest();
|
|
1072
|
+
|
|
1073
|
+
// agent 技能目录若被外部安装器换成了实体目录:内容迁入正本,恢复链接
|
|
1074
|
+
let relinked = 0;
|
|
1075
|
+
for (const n of manifest.agents ?? []) {
|
|
1076
|
+
if (!AGENT_TARGETS[n]) continue;
|
|
1077
|
+
if (ensureSkillsLink(process.cwd(), n) !== "ok") relinked++;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
const c = await reconcileLocalSkills(manifest, { allowPrune: true });
|
|
1081
|
+
saveManifest(manifest);
|
|
1082
|
+
|
|
1083
|
+
console.log("");
|
|
1084
|
+
console.log(
|
|
1085
|
+
"Adopt: " + c.registered + " 新登记, " + c.refreshed + " 指纹更新, " + c.missing + " 缺失保留, "
|
|
1086
|
+
+ c.graduated + " 已升格/移除, " + relinked + " 目录收编重链;"
|
|
1087
|
+
+ "local 段共 " + Object.keys(manifest.local).length + " 个本地技能",
|
|
1088
|
+
);
|
|
1089
|
+
if (Object.keys(manifest.local).length) {
|
|
1090
|
+
console.log("(本地技能不参与货架对账;想跨项目复用: shelf create " + CANONICAL_SKILLS_DIR + "/<名> --to skills/<包>)");
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
|
|
962
1094
|
// ---- 子命令:home(查看/更新档口)----
|
|
963
1095
|
|
|
964
1096
|
export async function cmdShelfHome(argv) {
|
|
@@ -1137,23 +1269,26 @@ export async function cmdShelfSync(argv) {
|
|
|
1137
1269
|
c.pushed++;
|
|
1138
1270
|
}
|
|
1139
1271
|
|
|
1140
|
-
//
|
|
1141
|
-
let
|
|
1142
|
-
if (!dryRun) {
|
|
1143
|
-
for (const
|
|
1144
|
-
if (!
|
|
1145
|
-
|
|
1146
|
-
for (const m of rec.mirrors) {
|
|
1147
|
-
if (refreshMirror(primary, path.resolve(process.cwd(), m))) mirrorFixed++;
|
|
1148
|
-
}
|
|
1272
|
+
// 链接完整性(决策 #26):已配置目标的技能目录必须是指向 ai/jaSkills 的链接
|
|
1273
|
+
let linksFixed = 0;
|
|
1274
|
+
if (!dryRun && manifest.agents?.length) {
|
|
1275
|
+
for (const n of manifest.agents) {
|
|
1276
|
+
if (!AGENT_TARGETS[n]) continue;
|
|
1277
|
+
if (ensureSkillsLink(process.cwd(), n) !== "ok") linksFixed++;
|
|
1149
1278
|
}
|
|
1150
1279
|
}
|
|
1151
1280
|
|
|
1281
|
+
let localStat = null;
|
|
1282
|
+
if (!dryRun) localStat = await reconcileLocalSkills(manifest);
|
|
1283
|
+
|
|
1152
1284
|
if (!dryRun) saveManifest(manifest);
|
|
1153
1285
|
console.log("");
|
|
1154
1286
|
console.log(
|
|
1155
1287
|
`Sync${dryRun ? "(dry-run)" : ""}: ${c.same} 一致, ${c.updated} 更新本地, ${c.pushed} 推上库, ` +
|
|
1156
|
-
`${c.cleaned} 清账, ${c.skipped} 跳过, ${
|
|
1288
|
+
`${c.cleaned} 清账, ${c.skipped} 跳过, ${linksFixed} 链接修复, ${pending.length} 待决` +
|
|
1289
|
+
(localStat && (localStat.registered || localStat.refreshed || localStat.missing)
|
|
1290
|
+
? `;本地技能: ${localStat.registered} 新登记 / ${localStat.refreshed} 指纹更新 / ${localStat.missing} 缺失`
|
|
1291
|
+
: ""),
|
|
1157
1292
|
);
|
|
1158
1293
|
if (pending.length) {
|
|
1159
1294
|
for (const x of pending) console.log(` · ${x}`);
|
package/lib/manifest.mjs
CHANGED
|
@@ -28,11 +28,13 @@ export function loadManifest(cwd = process.cwd()) {
|
|
|
28
28
|
skillsDir: ".claude/skills",
|
|
29
29
|
skills: {},
|
|
30
30
|
shelf: {},
|
|
31
|
+
local: {},
|
|
31
32
|
};
|
|
32
33
|
}
|
|
33
34
|
const data = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
34
35
|
data.skills ??= {};
|
|
35
36
|
data.shelf ??= {};
|
|
37
|
+
data.local ??= {};
|
|
36
38
|
return data;
|
|
37
39
|
}
|
|
38
40
|
|
package/package.json
CHANGED