claude-code-arcane 2.12.0 → 2.13.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/CHANGELOG.md +8 -0
- package/README.md +1 -1
- package/dist/cli.js +243 -277
- package/docs/USER-GUIDE.md +4 -4
- package/docs/directory-structure.md +6 -1
- package/hooks/guard.py +445 -0
- package/hooks/guard.sh +18 -0
- package/hooks/validate-commit.sh +11 -3
- package/package.json +1 -1
- package/profiles/audio.yaml +0 -1
- package/profiles/core.yaml +8 -4
- package/profiles/visual-novel.yaml +0 -1
- package/hooks/validate-push.sh +0 -21
- package/hooks/validate-secrets.sh +0 -23
package/dist/cli.js
CHANGED
|
@@ -5,7 +5,7 @@ import { Command } from "commander";
|
|
|
5
5
|
|
|
6
6
|
// src/commands/install.ts
|
|
7
7
|
import fs10 from "fs";
|
|
8
|
-
import
|
|
8
|
+
import path13 from "path";
|
|
9
9
|
import chalk2 from "chalk";
|
|
10
10
|
import * as p2 from "@clack/prompts";
|
|
11
11
|
|
|
@@ -415,7 +415,7 @@ function hint(description, max = 90) {
|
|
|
415
415
|
|
|
416
416
|
// src/installer.ts
|
|
417
417
|
import fs7 from "fs";
|
|
418
|
-
import
|
|
418
|
+
import path9 from "path";
|
|
419
419
|
|
|
420
420
|
// src/utils.ts
|
|
421
421
|
import { fileURLToPath } from "url";
|
|
@@ -873,40 +873,37 @@ function computeSourceHashes(packageRoot, skills, rules, agents) {
|
|
|
873
873
|
return hashes;
|
|
874
874
|
}
|
|
875
875
|
|
|
876
|
-
// src/
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
876
|
+
// src/hook-commands.ts
|
|
877
|
+
import os2 from "os";
|
|
878
|
+
import path8 from "path";
|
|
879
|
+
var FILE_TOOLS_MATCHER = "Write|Edit|MultiEdit|NotebookEdit";
|
|
880
|
+
var LEGACY_COMMAND = /^bash \.claude\/hooks\/([\w.-]+\.sh)$/;
|
|
881
|
+
var ARCANE_COMMAND = /^bash "?(?:\$CLAUDE_PROJECT_DIR\/|[^"]*\/)?\.claude\/hooks\/([\w.-]+\.sh)"?$/;
|
|
882
|
+
var RETIRED_SCRIPTS = /* @__PURE__ */ new Set(["validate-push.sh", "validate-secrets.sh"]);
|
|
883
|
+
function hookCommand(target, script) {
|
|
884
|
+
if (path8.resolve(target) === path8.resolve(os2.homedir())) {
|
|
885
|
+
const absolute = path8.join(target, ".claude", "hooks", script).replace(/\\/g, "/");
|
|
886
|
+
return `bash "${absolute}"`;
|
|
887
|
+
}
|
|
888
|
+
return `bash "$CLAUDE_PROJECT_DIR/.claude/hooks/${script}"`;
|
|
889
|
+
}
|
|
890
|
+
function hook(target, script, timeout) {
|
|
891
|
+
return { type: "command", command: hookCommand(target, script), timeout };
|
|
892
|
+
}
|
|
893
|
+
function defaultHooks(target) {
|
|
894
|
+
const one = (script, timeout) => [
|
|
895
|
+
{ matcher: "", hooks: [hook(target, script, timeout)] }
|
|
896
|
+
];
|
|
897
|
+
return {
|
|
881
898
|
SessionStart: [
|
|
882
899
|
{
|
|
883
900
|
matcher: "",
|
|
884
901
|
hooks: [
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
{
|
|
891
|
-
type: "command",
|
|
892
|
-
command: "bash .claude/hooks/detect-division.sh",
|
|
893
|
-
timeout: 10
|
|
894
|
-
},
|
|
895
|
-
{
|
|
896
|
-
type: "command",
|
|
897
|
-
command: "bash .claude/hooks/detect-unreal.sh",
|
|
898
|
-
timeout: 10
|
|
899
|
-
},
|
|
900
|
-
{
|
|
901
|
-
type: "command",
|
|
902
|
-
command: "bash .claude/hooks/detect-gaps.sh",
|
|
903
|
-
timeout: 10
|
|
904
|
-
},
|
|
905
|
-
{
|
|
906
|
-
type: "command",
|
|
907
|
-
command: "bash .claude/hooks/check-update.sh",
|
|
908
|
-
timeout: 10
|
|
909
|
-
}
|
|
902
|
+
hook(target, "session-start.sh", 10),
|
|
903
|
+
hook(target, "detect-division.sh", 10),
|
|
904
|
+
hook(target, "detect-unreal.sh", 10),
|
|
905
|
+
hook(target, "detect-gaps.sh", 10),
|
|
906
|
+
hook(target, "check-update.sh", 10)
|
|
910
907
|
]
|
|
911
908
|
}
|
|
912
909
|
],
|
|
@@ -914,114 +911,75 @@ var SETTINGS_TEMPLATE = {
|
|
|
914
911
|
{
|
|
915
912
|
matcher: "Bash",
|
|
916
913
|
hooks: [
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
command: "bash .claude/hooks/validate-commit.sh",
|
|
920
|
-
timeout: 15
|
|
921
|
-
},
|
|
922
|
-
{
|
|
923
|
-
type: "command",
|
|
924
|
-
command: "bash .claude/hooks/validate-push.sh",
|
|
925
|
-
timeout: 10
|
|
926
|
-
},
|
|
927
|
-
{
|
|
928
|
-
type: "command",
|
|
929
|
-
command: "bash .claude/hooks/validate-secrets.sh",
|
|
930
|
-
timeout: 10
|
|
931
|
-
}
|
|
914
|
+
hook(target, "guard.sh", 10),
|
|
915
|
+
hook(target, "validate-commit.sh", 15)
|
|
932
916
|
]
|
|
933
|
-
}
|
|
917
|
+
},
|
|
918
|
+
{ matcher: FILE_TOOLS_MATCHER, hooks: [hook(target, "guard.sh", 10)] }
|
|
934
919
|
],
|
|
935
920
|
PostToolUse: [
|
|
936
921
|
{
|
|
937
922
|
matcher: "Write|Edit",
|
|
938
|
-
hooks: [
|
|
939
|
-
{
|
|
940
|
-
type: "command",
|
|
941
|
-
command: "bash .claude/hooks/validate-assets.sh",
|
|
942
|
-
timeout: 10
|
|
943
|
-
},
|
|
944
|
-
{
|
|
945
|
-
type: "command",
|
|
946
|
-
command: "bash .claude/hooks/validate-skill-change.sh",
|
|
947
|
-
timeout: 5
|
|
948
|
-
}
|
|
949
|
-
]
|
|
950
|
-
}
|
|
951
|
-
],
|
|
952
|
-
Notification: [
|
|
953
|
-
{
|
|
954
|
-
matcher: "",
|
|
955
|
-
hooks: [
|
|
956
|
-
{
|
|
957
|
-
type: "command",
|
|
958
|
-
command: "bash .claude/hooks/notify.sh",
|
|
959
|
-
timeout: 10
|
|
960
|
-
}
|
|
961
|
-
]
|
|
962
|
-
}
|
|
963
|
-
],
|
|
964
|
-
PreCompact: [
|
|
965
|
-
{
|
|
966
|
-
matcher: "",
|
|
967
|
-
hooks: [
|
|
968
|
-
{
|
|
969
|
-
type: "command",
|
|
970
|
-
command: "bash .claude/hooks/pre-compact.sh",
|
|
971
|
-
timeout: 10
|
|
972
|
-
}
|
|
973
|
-
]
|
|
923
|
+
hooks: [hook(target, "validate-assets.sh", 10), hook(target, "validate-skill-change.sh", 5)]
|
|
974
924
|
}
|
|
975
925
|
],
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
{
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
{
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
]
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
]
|
|
1010
|
-
}
|
|
1011
|
-
],
|
|
1012
|
-
SubagentStop: [
|
|
1013
|
-
{
|
|
1014
|
-
matcher: "",
|
|
1015
|
-
hooks: [
|
|
1016
|
-
{
|
|
1017
|
-
type: "command",
|
|
1018
|
-
command: "bash .claude/hooks/log-agent-stop.sh",
|
|
1019
|
-
timeout: 5
|
|
1020
|
-
}
|
|
1021
|
-
]
|
|
926
|
+
Notification: one("notify.sh", 10),
|
|
927
|
+
PreCompact: one("pre-compact.sh", 10),
|
|
928
|
+
PostCompact: one("post-compact.sh", 10),
|
|
929
|
+
Stop: one("session-stop.sh", 10),
|
|
930
|
+
SubagentStart: one("log-agent.sh", 5),
|
|
931
|
+
SubagentStop: one("log-agent-stop.sh", 5)
|
|
932
|
+
};
|
|
933
|
+
}
|
|
934
|
+
function healHooks(hooks, target) {
|
|
935
|
+
const changes = [];
|
|
936
|
+
for (const [event, groups] of Object.entries(hooks)) {
|
|
937
|
+
if (!Array.isArray(groups)) continue;
|
|
938
|
+
for (const group of groups) {
|
|
939
|
+
const kept = (group.hooks ?? []).filter((entry) => {
|
|
940
|
+
const script = typeof entry.command === "string" && entry.command.match(ARCANE_COMMAND);
|
|
941
|
+
if (script && RETIRED_SCRIPTS.has(script[1])) {
|
|
942
|
+
changes.push(`retired hook: ${script[1]}`);
|
|
943
|
+
return false;
|
|
944
|
+
}
|
|
945
|
+
return true;
|
|
946
|
+
});
|
|
947
|
+
if (kept.length !== (group.hooks ?? []).length) group.hooks = kept;
|
|
948
|
+
}
|
|
949
|
+
hooks[event] = groups.filter((group) => (group.hooks ?? []).length > 0);
|
|
950
|
+
}
|
|
951
|
+
for (const groups of Object.values(hooks)) {
|
|
952
|
+
for (const group of groups ?? []) {
|
|
953
|
+
for (const entry of group.hooks ?? []) {
|
|
954
|
+
const legacy = typeof entry.command === "string" && entry.command.match(LEGACY_COMMAND);
|
|
955
|
+
if (legacy) {
|
|
956
|
+
entry.command = hookCommand(target, legacy[1]);
|
|
957
|
+
changes.push(`hook path: ${legacy[1]}`);
|
|
958
|
+
}
|
|
1022
959
|
}
|
|
1023
|
-
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
const pre = hooks.PreToolUse ??= [];
|
|
963
|
+
const hasGuard = (group) => group.hooks?.some((h) => h.command?.includes("guard.sh"));
|
|
964
|
+
const bash = pre.find((g) => g.matcher === "Bash");
|
|
965
|
+
if (!bash) {
|
|
966
|
+
pre.unshift({ matcher: "Bash", hooks: [hook(target, "guard.sh", 10)] });
|
|
967
|
+
changes.push("guard for Bash");
|
|
968
|
+
} else if (!hasGuard(bash)) {
|
|
969
|
+
bash.hooks = [hook(target, "guard.sh", 10), ...bash.hooks ?? []];
|
|
970
|
+
changes.push("guard for Bash");
|
|
1024
971
|
}
|
|
972
|
+
if (!pre.some((g) => g.matcher === FILE_TOOLS_MATCHER && hasGuard(g))) {
|
|
973
|
+
pre.push({ matcher: FILE_TOOLS_MATCHER, hooks: [hook(target, "guard.sh", 10)] });
|
|
974
|
+
changes.push("guard for file edits");
|
|
975
|
+
}
|
|
976
|
+
return changes;
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
// src/installer.ts
|
|
980
|
+
var SETTINGS_TEMPLATE = {
|
|
981
|
+
$schema: "https://json.schemastore.org/claude-code-settings.json",
|
|
982
|
+
permissions: { allow: [], deny: [] }
|
|
1025
983
|
};
|
|
1026
984
|
var Installer = class {
|
|
1027
985
|
root;
|
|
@@ -1035,7 +993,7 @@ var Installer = class {
|
|
|
1035
993
|
constructor(merged, opts) {
|
|
1036
994
|
this.root = opts.contentRoot ?? getPackageRoot();
|
|
1037
995
|
this.target = opts.target;
|
|
1038
|
-
this.claudeDir =
|
|
996
|
+
this.claudeDir = path9.join(opts.target, ".claude");
|
|
1039
997
|
this.merged = merged;
|
|
1040
998
|
this.dryRun = opts.dryRun;
|
|
1041
999
|
this.shareFrom = opts.shareFrom;
|
|
@@ -1069,7 +1027,7 @@ var Installer = class {
|
|
|
1069
1027
|
}
|
|
1070
1028
|
backup() {
|
|
1071
1029
|
if (!fs7.existsSync(this.claudeDir)) return;
|
|
1072
|
-
const backup =
|
|
1030
|
+
const backup = path9.join(this.target, ".claude.bak");
|
|
1073
1031
|
if (this.dryRun) {
|
|
1074
1032
|
this.log(" [dry-run] Would backup .claude/ -> .claude.bak/");
|
|
1075
1033
|
return;
|
|
@@ -1083,10 +1041,10 @@ var Installer = class {
|
|
|
1083
1041
|
createDirs() {
|
|
1084
1042
|
if (this.dryRun) return;
|
|
1085
1043
|
for (const d of ["skills", "rules", "agents", "docs"]) {
|
|
1086
|
-
ensureDir(
|
|
1044
|
+
ensureDir(path9.join(this.claudeDir, d));
|
|
1087
1045
|
}
|
|
1088
1046
|
if (this.merged.output_styles.length > 0) {
|
|
1089
|
-
ensureDir(
|
|
1047
|
+
ensureDir(path9.join(this.claudeDir, "output-styles"));
|
|
1090
1048
|
}
|
|
1091
1049
|
}
|
|
1092
1050
|
get hasStatusline() {
|
|
@@ -1094,7 +1052,7 @@ var Installer = class {
|
|
|
1094
1052
|
}
|
|
1095
1053
|
copyHooks() {
|
|
1096
1054
|
this.log("\nHooks:");
|
|
1097
|
-
const hooksDir =
|
|
1055
|
+
const hooksDir = path9.join(this.claudeDir, "hooks");
|
|
1098
1056
|
if (this.dryRun) {
|
|
1099
1057
|
if (this.shareFrom) {
|
|
1100
1058
|
this.log(" [dry-run] hooks/ -> symlink from main worktree");
|
|
@@ -1105,7 +1063,7 @@ var Installer = class {
|
|
|
1105
1063
|
return;
|
|
1106
1064
|
}
|
|
1107
1065
|
if (this.shareFrom) {
|
|
1108
|
-
const sharedHooks =
|
|
1066
|
+
const sharedHooks = path9.join(this.shareFrom, ".claude", "hooks");
|
|
1109
1067
|
if (fs7.existsSync(sharedHooks)) {
|
|
1110
1068
|
const result = linkOrCopyDir(sharedHooks, hooksDir, "symlink");
|
|
1111
1069
|
if (result === "linked") {
|
|
@@ -1118,13 +1076,13 @@ var Installer = class {
|
|
|
1118
1076
|
return;
|
|
1119
1077
|
}
|
|
1120
1078
|
}
|
|
1121
|
-
const hooksSrc =
|
|
1079
|
+
const hooksSrc = path9.join(this.root, "hooks");
|
|
1122
1080
|
if (fs7.existsSync(hooksSrc)) {
|
|
1123
1081
|
ensureDir(hooksDir);
|
|
1124
1082
|
for (const entry of fs7.readdirSync(hooksSrc, { withFileTypes: true })) {
|
|
1125
1083
|
if (entry.name === "statusline.sh") continue;
|
|
1126
|
-
const src =
|
|
1127
|
-
const dst =
|
|
1084
|
+
const src = path9.join(hooksSrc, entry.name);
|
|
1085
|
+
const dst = path9.join(hooksDir, entry.name);
|
|
1128
1086
|
if (entry.isDirectory()) {
|
|
1129
1087
|
copyDirSync(src, dst);
|
|
1130
1088
|
} else {
|
|
@@ -1137,11 +1095,11 @@ var Installer = class {
|
|
|
1137
1095
|
}
|
|
1138
1096
|
copyStatusline() {
|
|
1139
1097
|
if (!this.hasStatusline) return;
|
|
1140
|
-
const statusline =
|
|
1098
|
+
const statusline = path9.join(this.root, "hooks", "statusline.sh");
|
|
1141
1099
|
if (fs7.existsSync(statusline)) {
|
|
1142
1100
|
fs7.copyFileSync(
|
|
1143
1101
|
statusline,
|
|
1144
|
-
|
|
1102
|
+
path9.join(this.claudeDir, "statusline.sh")
|
|
1145
1103
|
);
|
|
1146
1104
|
this.log(" [ok] statusline.sh (statusline profile)");
|
|
1147
1105
|
}
|
|
@@ -1149,12 +1107,12 @@ var Installer = class {
|
|
|
1149
1107
|
copySkills() {
|
|
1150
1108
|
this.log("\nSkills:");
|
|
1151
1109
|
for (const skill of this.merged.skills) {
|
|
1152
|
-
const src =
|
|
1110
|
+
const src = path9.join(this.root, "skills", skill);
|
|
1153
1111
|
if (!fs7.existsSync(src)) {
|
|
1154
1112
|
this.log(` WARN: Skill '${skill}' not found in skills/`);
|
|
1155
1113
|
continue;
|
|
1156
1114
|
}
|
|
1157
|
-
const dst =
|
|
1115
|
+
const dst = path9.join(this.claudeDir, "skills", skill);
|
|
1158
1116
|
if (this.dryRun) {
|
|
1159
1117
|
this.log(` [dry-run] ${skill}`);
|
|
1160
1118
|
} else {
|
|
@@ -1164,9 +1122,9 @@ var Installer = class {
|
|
|
1164
1122
|
}
|
|
1165
1123
|
const hasGamedev = this.merged.rules.gamedev.length > 0;
|
|
1166
1124
|
if (hasGamedev) {
|
|
1167
|
-
const tplSrc =
|
|
1125
|
+
const tplSrc = path9.join(this.root, "templates", "gamedev");
|
|
1168
1126
|
if (fs7.existsSync(tplSrc)) {
|
|
1169
|
-
const tplDst =
|
|
1127
|
+
const tplDst = path9.join(this.claudeDir, "skills", "_templates");
|
|
1170
1128
|
if (this.dryRun) {
|
|
1171
1129
|
this.log(" [dry-run] _templates/");
|
|
1172
1130
|
} else {
|
|
@@ -1179,7 +1137,7 @@ var Installer = class {
|
|
|
1179
1137
|
copyRules() {
|
|
1180
1138
|
this.log("\nRules:");
|
|
1181
1139
|
for (const rule of this.merged.rules.universal) {
|
|
1182
|
-
const src =
|
|
1140
|
+
const src = path9.join(this.root, "rules", `${rule}.md`);
|
|
1183
1141
|
if (!fs7.existsSync(src)) {
|
|
1184
1142
|
this.log(` WARN: Rule '${rule}.md' not found`);
|
|
1185
1143
|
continue;
|
|
@@ -1189,13 +1147,13 @@ var Installer = class {
|
|
|
1189
1147
|
} else {
|
|
1190
1148
|
fs7.copyFileSync(
|
|
1191
1149
|
src,
|
|
1192
|
-
|
|
1150
|
+
path9.join(this.claudeDir, "rules", `${rule}.md`)
|
|
1193
1151
|
);
|
|
1194
1152
|
this.log(` [ok] ${rule}.md`);
|
|
1195
1153
|
}
|
|
1196
1154
|
}
|
|
1197
1155
|
for (const rule of this.merged.rules.gamedev) {
|
|
1198
|
-
const src =
|
|
1156
|
+
const src = path9.join(this.root, "rules", "gamedev", `${rule}.md`);
|
|
1199
1157
|
if (!fs7.existsSync(src)) {
|
|
1200
1158
|
this.log(` WARN: Rule '${rule}.md' not found`);
|
|
1201
1159
|
continue;
|
|
@@ -1205,7 +1163,7 @@ var Installer = class {
|
|
|
1205
1163
|
} else {
|
|
1206
1164
|
fs7.copyFileSync(
|
|
1207
1165
|
src,
|
|
1208
|
-
|
|
1166
|
+
path9.join(this.claudeDir, "rules", `${rule}.md`)
|
|
1209
1167
|
);
|
|
1210
1168
|
this.log(` [ok] ${rule}.md`);
|
|
1211
1169
|
}
|
|
@@ -1215,7 +1173,7 @@ var Installer = class {
|
|
|
1215
1173
|
if (this.merged.output_styles.length === 0) return;
|
|
1216
1174
|
this.log("\nOutput styles:");
|
|
1217
1175
|
for (const style of this.merged.output_styles) {
|
|
1218
|
-
const src =
|
|
1176
|
+
const src = path9.join(this.root, "output-styles", `${style}.md`);
|
|
1219
1177
|
if (!fs7.existsSync(src)) {
|
|
1220
1178
|
this.log(` WARN: Output style '${style}.md' not found`);
|
|
1221
1179
|
continue;
|
|
@@ -1225,7 +1183,7 @@ var Installer = class {
|
|
|
1225
1183
|
} else {
|
|
1226
1184
|
fs7.copyFileSync(
|
|
1227
1185
|
src,
|
|
1228
|
-
|
|
1186
|
+
path9.join(this.claudeDir, "output-styles", `${style}.md`)
|
|
1229
1187
|
);
|
|
1230
1188
|
this.log(` [ok] ${style}.md`);
|
|
1231
1189
|
}
|
|
@@ -1249,8 +1207,8 @@ var Installer = class {
|
|
|
1249
1207
|
}
|
|
1250
1208
|
}
|
|
1251
1209
|
copyDocs() {
|
|
1252
|
-
const docsDir =
|
|
1253
|
-
const docsSrc =
|
|
1210
|
+
const docsDir = path9.join(this.claudeDir, "docs");
|
|
1211
|
+
const docsSrc = path9.join(this.root, "docs");
|
|
1254
1212
|
if (!fs7.existsSync(docsSrc)) return;
|
|
1255
1213
|
this.log("\nDocs:");
|
|
1256
1214
|
if (this.dryRun) {
|
|
@@ -1262,7 +1220,7 @@ var Installer = class {
|
|
|
1262
1220
|
return;
|
|
1263
1221
|
}
|
|
1264
1222
|
if (this.shareFrom) {
|
|
1265
|
-
const sharedDocs =
|
|
1223
|
+
const sharedDocs = path9.join(this.shareFrom, ".claude", "docs");
|
|
1266
1224
|
if (fs7.existsSync(sharedDocs)) {
|
|
1267
1225
|
const result = linkOrCopyDir(sharedDocs, docsDir, "symlink");
|
|
1268
1226
|
if (result === "linked") {
|
|
@@ -1282,6 +1240,7 @@ var Installer = class {
|
|
|
1282
1240
|
const settings = JSON.parse(JSON.stringify(SETTINGS_TEMPLATE));
|
|
1283
1241
|
settings.permissions.allow = [...this.merged.permissions.allow];
|
|
1284
1242
|
settings.permissions.deny = [...this.merged.permissions.deny];
|
|
1243
|
+
settings.hooks = defaultHooks(this.target);
|
|
1285
1244
|
if (this.hasStatusline) {
|
|
1286
1245
|
settings.statusLine = {
|
|
1287
1246
|
type: "command",
|
|
@@ -1298,7 +1257,7 @@ var Installer = class {
|
|
|
1298
1257
|
);
|
|
1299
1258
|
return;
|
|
1300
1259
|
}
|
|
1301
|
-
const settingsPath =
|
|
1260
|
+
const settingsPath = path9.join(this.claudeDir, "settings.json");
|
|
1302
1261
|
fs7.writeFileSync(
|
|
1303
1262
|
settingsPath,
|
|
1304
1263
|
JSON.stringify(settings, null, 2) + "\n",
|
|
@@ -1308,13 +1267,13 @@ var Installer = class {
|
|
|
1308
1267
|
}
|
|
1309
1268
|
};
|
|
1310
1269
|
function clean(target) {
|
|
1311
|
-
const claudeDir =
|
|
1312
|
-
const backup =
|
|
1270
|
+
const claudeDir = path9.join(target, ".claude");
|
|
1271
|
+
const backup = path9.join(target, ".claude.bak");
|
|
1313
1272
|
if (!fs7.existsSync(claudeDir)) {
|
|
1314
1273
|
console.log(`No .claude/ found in ${target} \u2014 nothing to clean.`);
|
|
1315
1274
|
return;
|
|
1316
1275
|
}
|
|
1317
|
-
const manifestFile =
|
|
1276
|
+
const manifestFile = path9.join(claudeDir, "arcane-manifest.json");
|
|
1318
1277
|
if (fs7.existsSync(manifestFile)) {
|
|
1319
1278
|
const data = JSON.parse(fs7.readFileSync(manifestFile, "utf-8"));
|
|
1320
1279
|
const profiles = (data.profiles ?? []).filter(
|
|
@@ -1324,7 +1283,7 @@ function clean(target) {
|
|
|
1324
1283
|
console.log(` Installed: ${data.installed_at ?? "unknown"}`);
|
|
1325
1284
|
const shared = data.worktree?.shared_dirs ?? [];
|
|
1326
1285
|
for (const dir of shared) {
|
|
1327
|
-
const p3 =
|
|
1286
|
+
const p3 = path9.join(claudeDir, dir);
|
|
1328
1287
|
if (isSymlinkOrJunction(p3)) {
|
|
1329
1288
|
fs7.unlinkSync(p3);
|
|
1330
1289
|
console.log(` [ok] Unlinked shared ${dir}/`);
|
|
@@ -1332,12 +1291,12 @@ function clean(target) {
|
|
|
1332
1291
|
}
|
|
1333
1292
|
}
|
|
1334
1293
|
for (const sub of ["hooks", "docs", "skills", "agents", "rules"]) {
|
|
1335
|
-
const p3 =
|
|
1294
|
+
const p3 = path9.join(claudeDir, sub);
|
|
1336
1295
|
safeRemove(p3);
|
|
1337
1296
|
}
|
|
1338
1297
|
const remaining = fs7.readdirSync(claudeDir);
|
|
1339
1298
|
for (const f of remaining) {
|
|
1340
|
-
const p3 =
|
|
1299
|
+
const p3 = path9.join(claudeDir, f);
|
|
1341
1300
|
safeRemove(p3);
|
|
1342
1301
|
}
|
|
1343
1302
|
if (fs7.existsSync(claudeDir)) {
|
|
@@ -1352,24 +1311,24 @@ function clean(target) {
|
|
|
1352
1311
|
|
|
1353
1312
|
// src/content-source.ts
|
|
1354
1313
|
import fs9 from "fs";
|
|
1355
|
-
import
|
|
1356
|
-
import
|
|
1314
|
+
import path11 from "path";
|
|
1315
|
+
import os3 from "os";
|
|
1357
1316
|
|
|
1358
1317
|
// src/cache.ts
|
|
1359
1318
|
import fs8 from "fs";
|
|
1360
|
-
import
|
|
1319
|
+
import path10 from "path";
|
|
1361
1320
|
function cacheDir() {
|
|
1362
|
-
return
|
|
1321
|
+
return path10.join(arcaneHome(), "cache");
|
|
1363
1322
|
}
|
|
1364
1323
|
function getCachePath(version) {
|
|
1365
|
-
return
|
|
1324
|
+
return path10.join(cacheDir(), version);
|
|
1366
1325
|
}
|
|
1367
1326
|
function isUsableContentRoot(dir) {
|
|
1368
|
-
return fs8.existsSync(
|
|
1327
|
+
return fs8.existsSync(path10.join(dir, "profiles")) && fs8.existsSync(path10.join(dir, "skills"));
|
|
1369
1328
|
}
|
|
1370
1329
|
function isCached(version) {
|
|
1371
1330
|
const versionDir = getCachePath(version);
|
|
1372
|
-
return fs8.existsSync(versionDir) && fs8.existsSync(
|
|
1331
|
+
return fs8.existsSync(versionDir) && fs8.existsSync(path10.join(versionDir, ".cache-meta.json")) && isUsableContentRoot(versionDir);
|
|
1373
1332
|
}
|
|
1374
1333
|
function getCachedContentRoot(version) {
|
|
1375
1334
|
if (!isCached(version)) return null;
|
|
@@ -1382,9 +1341,9 @@ function storeInCache(version, contentDir) {
|
|
|
1382
1341
|
}
|
|
1383
1342
|
fs8.mkdirSync(versionDir, { recursive: true });
|
|
1384
1343
|
for (const dir of ["skills", "rules", "agents", "hooks", "profiles", "templates", "docs", "output-styles"]) {
|
|
1385
|
-
const src =
|
|
1344
|
+
const src = path10.join(contentDir, dir);
|
|
1386
1345
|
if (fs8.existsSync(src)) {
|
|
1387
|
-
copyDirRecursive(src,
|
|
1346
|
+
copyDirRecursive(src, path10.join(versionDir, dir));
|
|
1388
1347
|
}
|
|
1389
1348
|
}
|
|
1390
1349
|
const meta = {
|
|
@@ -1392,7 +1351,7 @@ function storeInCache(version, contentDir) {
|
|
|
1392
1351
|
cached_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1393
1352
|
};
|
|
1394
1353
|
fs8.writeFileSync(
|
|
1395
|
-
|
|
1354
|
+
path10.join(versionDir, ".cache-meta.json"),
|
|
1396
1355
|
JSON.stringify(meta, null, 2) + "\n"
|
|
1397
1356
|
);
|
|
1398
1357
|
return versionDir;
|
|
@@ -1400,7 +1359,7 @@ function storeInCache(version, contentDir) {
|
|
|
1400
1359
|
function pruneCache(keepCount = 3) {
|
|
1401
1360
|
if (!fs8.existsSync(cacheDir())) return;
|
|
1402
1361
|
const entries = fs8.readdirSync(cacheDir(), { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => {
|
|
1403
|
-
const metaPath =
|
|
1362
|
+
const metaPath = path10.join(cacheDir(), e.name, ".cache-meta.json");
|
|
1404
1363
|
let cachedAt = 0;
|
|
1405
1364
|
if (fs8.existsSync(metaPath)) {
|
|
1406
1365
|
try {
|
|
@@ -1413,13 +1372,13 @@ function pruneCache(keepCount = 3) {
|
|
|
1413
1372
|
return { name: e.name, cachedAt };
|
|
1414
1373
|
}).sort((a, b) => b.cachedAt - a.cachedAt);
|
|
1415
1374
|
for (const entry of entries.slice(keepCount)) {
|
|
1416
|
-
fs8.rmSync(
|
|
1375
|
+
fs8.rmSync(path10.join(cacheDir(), entry.name), { recursive: true, force: true });
|
|
1417
1376
|
}
|
|
1418
1377
|
}
|
|
1419
1378
|
function listCachedVersions() {
|
|
1420
1379
|
if (!fs8.existsSync(cacheDir())) return [];
|
|
1421
1380
|
return fs8.readdirSync(cacheDir(), { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => {
|
|
1422
|
-
const metaPath =
|
|
1381
|
+
const metaPath = path10.join(cacheDir(), e.name, ".cache-meta.json");
|
|
1423
1382
|
let cachedAt = "unknown";
|
|
1424
1383
|
if (fs8.existsSync(metaPath)) {
|
|
1425
1384
|
try {
|
|
@@ -1434,8 +1393,8 @@ function listCachedVersions() {
|
|
|
1434
1393
|
function copyDirRecursive(src, dest) {
|
|
1435
1394
|
fs8.mkdirSync(dest, { recursive: true });
|
|
1436
1395
|
for (const entry of fs8.readdirSync(src, { withFileTypes: true })) {
|
|
1437
|
-
const srcPath =
|
|
1438
|
-
const destPath =
|
|
1396
|
+
const srcPath = path10.join(src, entry.name);
|
|
1397
|
+
const destPath = path10.join(dest, entry.name);
|
|
1439
1398
|
if (entry.isDirectory()) {
|
|
1440
1399
|
copyDirRecursive(srcPath, destPath);
|
|
1441
1400
|
} else {
|
|
@@ -1455,7 +1414,7 @@ var BundledContentSource = class {
|
|
|
1455
1414
|
}
|
|
1456
1415
|
async isAvailable() {
|
|
1457
1416
|
const root = getPackageRoot();
|
|
1458
|
-
return fs9.existsSync(
|
|
1417
|
+
return fs9.existsSync(path11.join(root, "skills"));
|
|
1459
1418
|
}
|
|
1460
1419
|
async getVersion() {
|
|
1461
1420
|
return getPackageVersion();
|
|
@@ -1495,7 +1454,7 @@ var GitHubContentSource = class {
|
|
|
1495
1454
|
if (isCached(version)) {
|
|
1496
1455
|
return getCachedContentRoot(version);
|
|
1497
1456
|
}
|
|
1498
|
-
const tmpDir = fs9.mkdtempSync(
|
|
1457
|
+
const tmpDir = fs9.mkdtempSync(path11.join(os3.tmpdir(), "arcane-github-"));
|
|
1499
1458
|
try {
|
|
1500
1459
|
await this.downloadAndExtract(tmpDir);
|
|
1501
1460
|
const contentRoot = storeInCache(version, this.findExtractedRoot(tmpDir));
|
|
@@ -1573,7 +1532,7 @@ var GitHubContentSource = class {
|
|
|
1573
1532
|
throw new Error(`Failed to download tarball: ${response.status}`);
|
|
1574
1533
|
}
|
|
1575
1534
|
const tar = await import("tar");
|
|
1576
|
-
const tarballPath =
|
|
1535
|
+
const tarballPath = path11.join(destDir, "repo.tar.gz");
|
|
1577
1536
|
const fileStream = fs9.createWriteStream(tarballPath);
|
|
1578
1537
|
const reader = response.body.getReader();
|
|
1579
1538
|
const writable = new WritableStream({
|
|
@@ -1604,7 +1563,7 @@ var GitHubContentSource = class {
|
|
|
1604
1563
|
(e) => e.isDirectory() && e.name !== "." && e.name !== ".."
|
|
1605
1564
|
);
|
|
1606
1565
|
if (dirs.length === 1) {
|
|
1607
|
-
return
|
|
1566
|
+
return path11.join(tmpDir, dirs[0].name);
|
|
1608
1567
|
}
|
|
1609
1568
|
return tmpDir;
|
|
1610
1569
|
}
|
|
@@ -1669,10 +1628,10 @@ function findLatestCache() {
|
|
|
1669
1628
|
}
|
|
1670
1629
|
|
|
1671
1630
|
// src/registry.ts
|
|
1672
|
-
import
|
|
1631
|
+
import path12 from "path";
|
|
1673
1632
|
var REGISTRY_VERSION = 1;
|
|
1674
1633
|
function registryPath() {
|
|
1675
|
-
return
|
|
1634
|
+
return path12.join(arcaneHome(), "installations.json");
|
|
1676
1635
|
}
|
|
1677
1636
|
function readRegistryFile() {
|
|
1678
1637
|
const p3 = registryPath();
|
|
@@ -1697,7 +1656,7 @@ function writeRegistryFile(registry) {
|
|
|
1697
1656
|
}
|
|
1698
1657
|
}
|
|
1699
1658
|
function registerInstallation(target) {
|
|
1700
|
-
const abs =
|
|
1659
|
+
const abs = path12.resolve(target);
|
|
1701
1660
|
const registry = readRegistryFile();
|
|
1702
1661
|
if (registry.installations.some((e) => e.path === abs)) {
|
|
1703
1662
|
return;
|
|
@@ -1722,7 +1681,7 @@ var PACKAGE_NAME = "claude-code-arcane";
|
|
|
1722
1681
|
function isArcaneSourceTree(dir) {
|
|
1723
1682
|
try {
|
|
1724
1683
|
const pkg = JSON.parse(
|
|
1725
|
-
fs10.readFileSync(
|
|
1684
|
+
fs10.readFileSync(path13.join(dir, "package.json"), "utf-8")
|
|
1726
1685
|
);
|
|
1727
1686
|
return pkg.name === PACKAGE_NAME;
|
|
1728
1687
|
} catch {
|
|
@@ -1754,8 +1713,8 @@ async function installCommand(profileExpr, opts) {
|
|
|
1754
1713
|
quiet: !profileExpr
|
|
1755
1714
|
});
|
|
1756
1715
|
const root = await source.getContentRoot();
|
|
1757
|
-
const profilesDir =
|
|
1758
|
-
const target =
|
|
1716
|
+
const profilesDir = path13.join(root, "profiles");
|
|
1717
|
+
const target = path13.resolve(opts.target ?? process.cwd());
|
|
1759
1718
|
if (await refusesSelfInstall(target, profileExpr, opts)) return;
|
|
1760
1719
|
if (!profileExpr) {
|
|
1761
1720
|
const profiles = listProfiles(profilesDir);
|
|
@@ -1828,20 +1787,20 @@ Installing profile: ${chalk2.cyan(profileExpr)}`)
|
|
|
1828
1787
|
|
|
1829
1788
|
// src/commands/add.ts
|
|
1830
1789
|
import fs12 from "fs";
|
|
1831
|
-
import
|
|
1790
|
+
import path15 from "path";
|
|
1832
1791
|
import chalk3 from "chalk";
|
|
1833
1792
|
|
|
1834
1793
|
// src/skills-catalog.ts
|
|
1835
1794
|
import fs11 from "fs";
|
|
1836
|
-
import
|
|
1795
|
+
import path14 from "path";
|
|
1837
1796
|
var FRONTMATTER_BYTES = 4096;
|
|
1838
1797
|
function listSkills(skillsDir) {
|
|
1839
1798
|
if (!fs11.existsSync(skillsDir)) return [];
|
|
1840
1799
|
return fs11.readdirSync(skillsDir, { withFileTypes: true }).filter(
|
|
1841
|
-
(d) => d.isDirectory() && !d.name.startsWith("_") && fs11.existsSync(
|
|
1800
|
+
(d) => d.isDirectory() && !d.name.startsWith("_") && fs11.existsSync(path14.join(skillsDir, d.name, "SKILL.md"))
|
|
1842
1801
|
).map((d) => ({
|
|
1843
1802
|
name: d.name,
|
|
1844
|
-
description: readDescription(
|
|
1803
|
+
description: readDescription(path14.join(skillsDir, d.name, "SKILL.md"))
|
|
1845
1804
|
})).sort((a, b) => a.name.localeCompare(b.name));
|
|
1846
1805
|
}
|
|
1847
1806
|
function readDescription(skillFile) {
|
|
@@ -1893,15 +1852,15 @@ async function addCommand(items = []) {
|
|
|
1893
1852
|
return;
|
|
1894
1853
|
}
|
|
1895
1854
|
const selected = await runAddWizard({
|
|
1896
|
-
profiles: listProfiles(
|
|
1897
|
-
skills: listSkills(
|
|
1855
|
+
profiles: listProfiles(path15.join(root, "profiles")),
|
|
1856
|
+
skills: listSkills(path15.join(root, "skills")),
|
|
1898
1857
|
installedProfiles,
|
|
1899
1858
|
installedSkills: manifest.installed_skills
|
|
1900
1859
|
});
|
|
1901
1860
|
if (!selected) return;
|
|
1902
1861
|
items = selected;
|
|
1903
1862
|
}
|
|
1904
|
-
const claudeDir =
|
|
1863
|
+
const claudeDir = path15.join(target, ".claude");
|
|
1905
1864
|
const added = [];
|
|
1906
1865
|
const skipped = [];
|
|
1907
1866
|
const notFound = [];
|
|
@@ -1911,7 +1870,7 @@ async function addCommand(items = []) {
|
|
|
1911
1870
|
for (const item of items) {
|
|
1912
1871
|
if (item.startsWith("+")) {
|
|
1913
1872
|
const requested = item.slice(1);
|
|
1914
|
-
const resolved = resolveProfile(
|
|
1873
|
+
const resolved = resolveProfile(path15.join(root, "profiles"), requested);
|
|
1915
1874
|
if (!resolved) {
|
|
1916
1875
|
console.error(chalk3.red(` Profile '${requested}' not found`));
|
|
1917
1876
|
continue;
|
|
@@ -1926,10 +1885,10 @@ async function addCommand(items = []) {
|
|
|
1926
1885
|
}
|
|
1927
1886
|
for (const rule of profile.rules.universal) {
|
|
1928
1887
|
if (!manifest.installed_rules.includes(rule)) {
|
|
1929
|
-
const src =
|
|
1888
|
+
const src = path15.join(root, "rules", `${rule}.md`);
|
|
1930
1889
|
if (fs12.existsSync(src)) {
|
|
1931
|
-
ensureDir(
|
|
1932
|
-
fs12.copyFileSync(src,
|
|
1890
|
+
ensureDir(path15.join(claudeDir, "rules"));
|
|
1891
|
+
fs12.copyFileSync(src, path15.join(claudeDir, "rules", `${rule}.md`));
|
|
1933
1892
|
manifest.installed_rules.push(rule);
|
|
1934
1893
|
addedRules.push(rule);
|
|
1935
1894
|
}
|
|
@@ -1937,10 +1896,10 @@ async function addCommand(items = []) {
|
|
|
1937
1896
|
}
|
|
1938
1897
|
for (const rule of profile.rules.gamedev) {
|
|
1939
1898
|
if (!manifest.installed_rules.includes(rule)) {
|
|
1940
|
-
const src =
|
|
1899
|
+
const src = path15.join(root, "rules", "gamedev", `${rule}.md`);
|
|
1941
1900
|
if (fs12.existsSync(src)) {
|
|
1942
|
-
ensureDir(
|
|
1943
|
-
fs12.copyFileSync(src,
|
|
1901
|
+
ensureDir(path15.join(claudeDir, "rules"));
|
|
1902
|
+
fs12.copyFileSync(src, path15.join(claudeDir, "rules", `${rule}.md`));
|
|
1944
1903
|
manifest.installed_rules.push(rule);
|
|
1945
1904
|
addedRules.push(rule);
|
|
1946
1905
|
}
|
|
@@ -1956,9 +1915,9 @@ async function addCommand(items = []) {
|
|
|
1956
1915
|
}
|
|
1957
1916
|
}
|
|
1958
1917
|
if (profileName === "statusline") {
|
|
1959
|
-
const statuslineSrc =
|
|
1918
|
+
const statuslineSrc = path15.join(root, "hooks", "statusline.sh");
|
|
1960
1919
|
if (fs12.existsSync(statuslineSrc)) {
|
|
1961
|
-
fs12.copyFileSync(statuslineSrc,
|
|
1920
|
+
fs12.copyFileSync(statuslineSrc, path15.join(claudeDir, "statusline.sh"));
|
|
1962
1921
|
statuslineAdded = true;
|
|
1963
1922
|
}
|
|
1964
1923
|
}
|
|
@@ -2041,10 +2000,10 @@ function persistAdditions(target, claudeDir, manifest, addedItems) {
|
|
|
2041
2000
|
updateManifestFields(target, updates);
|
|
2042
2001
|
}
|
|
2043
2002
|
function printCatalog(root, installedProfiles, installedSkills) {
|
|
2044
|
-
const profiles = listProfiles(
|
|
2003
|
+
const profiles = listProfiles(path15.join(root, "profiles")).filter(
|
|
2045
2004
|
(pr) => !installedProfiles.includes(pr.name)
|
|
2046
2005
|
);
|
|
2047
|
-
const skills = listSkills(
|
|
2006
|
+
const skills = listSkills(path15.join(root, "skills")).filter(
|
|
2048
2007
|
(sk) => !installedSkills.includes(sk.name)
|
|
2049
2008
|
);
|
|
2050
2009
|
if (profiles.length === 0 && skills.length === 0) {
|
|
@@ -2070,14 +2029,14 @@ function printCatalog(root, installedProfiles, installedSkills) {
|
|
|
2070
2029
|
}
|
|
2071
2030
|
function addSkill(root, target, skill, installed) {
|
|
2072
2031
|
if (installed.includes(skill)) return "skipped";
|
|
2073
|
-
const src =
|
|
2032
|
+
const src = path15.join(root, "skills", skill);
|
|
2074
2033
|
if (!fs12.existsSync(src)) return "not-found";
|
|
2075
|
-
const dst =
|
|
2034
|
+
const dst = path15.join(target, ".claude", "skills", skill);
|
|
2076
2035
|
copyDirSync(src, dst);
|
|
2077
2036
|
return "added";
|
|
2078
2037
|
}
|
|
2079
2038
|
function mergePermissions(claudeDir, newPerms) {
|
|
2080
|
-
const settingsPath =
|
|
2039
|
+
const settingsPath = path15.join(claudeDir, "settings.json");
|
|
2081
2040
|
if (!fs12.existsSync(settingsPath)) return;
|
|
2082
2041
|
const settings = readJsonSync(settingsPath);
|
|
2083
2042
|
const perms = settings.permissions ?? { allow: [], deny: [] };
|
|
@@ -2091,7 +2050,7 @@ function mergePermissions(claudeDir, newPerms) {
|
|
|
2091
2050
|
writeJsonSync(settingsPath, settings);
|
|
2092
2051
|
}
|
|
2093
2052
|
function addStatuslineToSettings(target, claudeDir) {
|
|
2094
|
-
const settingsPath =
|
|
2053
|
+
const settingsPath = path15.join(claudeDir, "settings.json");
|
|
2095
2054
|
if (!fs12.existsSync(settingsPath)) return;
|
|
2096
2055
|
const settings = readJsonSync(settingsPath);
|
|
2097
2056
|
if (settings.statusLine) return;
|
|
@@ -2104,7 +2063,7 @@ function addStatuslineToSettings(target, claudeDir) {
|
|
|
2104
2063
|
|
|
2105
2064
|
// src/commands/remove.ts
|
|
2106
2065
|
import fs13 from "fs";
|
|
2107
|
-
import
|
|
2066
|
+
import path16 from "path";
|
|
2108
2067
|
import chalk4 from "chalk";
|
|
2109
2068
|
var CORE_SKILLS = [
|
|
2110
2069
|
"commit",
|
|
@@ -2169,7 +2128,7 @@ async function removeCommand(items, opts = {}) {
|
|
|
2169
2128
|
}
|
|
2170
2129
|
manifest.total_skills = manifest.installed_skills.length;
|
|
2171
2130
|
manifest.total_rules = manifest.installed_rules.length;
|
|
2172
|
-
const mp =
|
|
2131
|
+
const mp = path16.join(target, ".claude", "arcane-manifest.json");
|
|
2173
2132
|
writeJsonSync(mp, manifest);
|
|
2174
2133
|
const totalRemoved = removedSkills.length + removedAgents.length + removedProfiles.length;
|
|
2175
2134
|
console.log(chalk4.bold(`
|
|
@@ -2190,7 +2149,7 @@ function removeSkill(target, skill, manifest) {
|
|
|
2190
2149
|
);
|
|
2191
2150
|
return "skipped";
|
|
2192
2151
|
}
|
|
2193
|
-
const skillDir =
|
|
2152
|
+
const skillDir = path16.join(target, ".claude", "skills", skill);
|
|
2194
2153
|
if (!fs13.existsSync(skillDir)) return "skipped";
|
|
2195
2154
|
fs13.rmSync(skillDir, { recursive: true, force: true });
|
|
2196
2155
|
manifest.installed_skills = manifest.installed_skills.filter(
|
|
@@ -2212,7 +2171,7 @@ function removeProfile(root, target, profileName, manifest) {
|
|
|
2212
2171
|
);
|
|
2213
2172
|
return { removed: false, skills: [], agents: [] };
|
|
2214
2173
|
}
|
|
2215
|
-
const resolved = resolveProfile(
|
|
2174
|
+
const resolved = resolveProfile(path16.join(root, "profiles"), installedName);
|
|
2216
2175
|
if (!resolved) {
|
|
2217
2176
|
console.warn(
|
|
2218
2177
|
chalk4.yellow(` WARNING: Profile '${installedName}.yaml' not found.`)
|
|
@@ -2227,7 +2186,7 @@ function removeProfile(root, target, profileName, manifest) {
|
|
|
2227
2186
|
const sharedAgents = /* @__PURE__ */ new Set();
|
|
2228
2187
|
const sharedRules = /* @__PURE__ */ new Set();
|
|
2229
2188
|
for (const rp of remainingProfiles) {
|
|
2230
|
-
const rpResolved = resolveProfile(
|
|
2189
|
+
const rpResolved = resolveProfile(path16.join(root, "profiles"), rp);
|
|
2231
2190
|
if (!rpResolved) continue;
|
|
2232
2191
|
const rpDef = parseProfile(rpResolved.file);
|
|
2233
2192
|
rpDef.skills.forEach((s) => sharedSkills.add(s));
|
|
@@ -2239,7 +2198,7 @@ function removeProfile(root, target, profileName, manifest) {
|
|
|
2239
2198
|
for (const skill of profile.skills) {
|
|
2240
2199
|
if (sharedSkills.has(skill)) continue;
|
|
2241
2200
|
if (CORE_SKILLS.includes(skill)) continue;
|
|
2242
|
-
const skillDir =
|
|
2201
|
+
const skillDir = path16.join(target, ".claude", "skills", skill);
|
|
2243
2202
|
if (fs13.existsSync(skillDir)) {
|
|
2244
2203
|
fs13.rmSync(skillDir, { recursive: true, force: true });
|
|
2245
2204
|
removedSkills.push(skill);
|
|
@@ -2249,7 +2208,7 @@ function removeProfile(root, target, profileName, manifest) {
|
|
|
2249
2208
|
);
|
|
2250
2209
|
}
|
|
2251
2210
|
const removedAgents = [];
|
|
2252
|
-
const claudeDir =
|
|
2211
|
+
const claudeDir = path16.join(target, ".claude");
|
|
2253
2212
|
for (const entry of profile.agents) {
|
|
2254
2213
|
if (sharedAgents.has(entry)) continue;
|
|
2255
2214
|
const { division } = parseAgentEntry(entry);
|
|
@@ -2264,7 +2223,7 @@ function removeProfile(root, target, profileName, manifest) {
|
|
|
2264
2223
|
...profile.rules.gamedev
|
|
2265
2224
|
]) {
|
|
2266
2225
|
if (sharedRules.has(rule)) continue;
|
|
2267
|
-
const ruleFile =
|
|
2226
|
+
const ruleFile = path16.join(target, ".claude", "rules", `${rule}.md`);
|
|
2268
2227
|
if (fs13.existsSync(ruleFile)) {
|
|
2269
2228
|
fs13.rmSync(ruleFile);
|
|
2270
2229
|
}
|
|
@@ -2273,18 +2232,18 @@ function removeProfile(root, target, profileName, manifest) {
|
|
|
2273
2232
|
);
|
|
2274
2233
|
}
|
|
2275
2234
|
if (profileName === "statusline") {
|
|
2276
|
-
const statuslineFile =
|
|
2235
|
+
const statuslineFile = path16.join(target, ".claude", "statusline.sh");
|
|
2277
2236
|
if (fs13.existsSync(statuslineFile)) {
|
|
2278
2237
|
fs13.rmSync(statuslineFile);
|
|
2279
2238
|
}
|
|
2280
|
-
removeStatuslineFromSettings(
|
|
2239
|
+
removeStatuslineFromSettings(path16.join(target, ".claude"));
|
|
2281
2240
|
}
|
|
2282
2241
|
manifest.profiles = remainingProfiles;
|
|
2283
2242
|
manifest.profile_command = remainingProfiles.filter((p3) => p3 !== "core").join("+");
|
|
2284
2243
|
return { removed: true, skills: removedSkills, agents: removedAgents };
|
|
2285
2244
|
}
|
|
2286
2245
|
function removeStatuslineFromSettings(claudeDir) {
|
|
2287
|
-
const settingsPath =
|
|
2246
|
+
const settingsPath = path16.join(claudeDir, "settings.json");
|
|
2288
2247
|
if (!fs13.existsSync(settingsPath)) return;
|
|
2289
2248
|
const settings = JSON.parse(fs13.readFileSync(settingsPath, "utf-8"));
|
|
2290
2249
|
if (settings.statusLine) {
|
|
@@ -2295,13 +2254,13 @@ function removeStatuslineFromSettings(claudeDir) {
|
|
|
2295
2254
|
|
|
2296
2255
|
// src/commands/list.ts
|
|
2297
2256
|
import fs14 from "fs";
|
|
2298
|
-
import
|
|
2257
|
+
import path17 from "path";
|
|
2299
2258
|
import chalk5 from "chalk";
|
|
2300
2259
|
async function listCommand() {
|
|
2301
2260
|
const source = await resolveContentSource({ quiet: true });
|
|
2302
2261
|
const root = await source.getContentRoot();
|
|
2303
|
-
const profilesDir =
|
|
2304
|
-
const skillsDir =
|
|
2262
|
+
const profilesDir = path17.join(root, "profiles");
|
|
2263
|
+
const skillsDir = path17.join(root, "skills");
|
|
2305
2264
|
const target = process.cwd();
|
|
2306
2265
|
const manifest = readManifest(target);
|
|
2307
2266
|
const profiles = listProfiles(profilesDir);
|
|
@@ -2386,8 +2345,8 @@ async function statusCommand() {
|
|
|
2386
2345
|
|
|
2387
2346
|
// src/commands/update.ts
|
|
2388
2347
|
import fs15 from "fs";
|
|
2389
|
-
import
|
|
2390
|
-
import
|
|
2348
|
+
import path18 from "path";
|
|
2349
|
+
import os4 from "os";
|
|
2391
2350
|
import chalk7 from "chalk";
|
|
2392
2351
|
|
|
2393
2352
|
// src/self-update.ts
|
|
@@ -2470,7 +2429,7 @@ async function updateCommand(opts) {
|
|
|
2470
2429
|
registerInstallation(cwd);
|
|
2471
2430
|
}
|
|
2472
2431
|
const targets = pruneRegistry().map((e) => e.path);
|
|
2473
|
-
const globalTarget =
|
|
2432
|
+
const globalTarget = os4.homedir();
|
|
2474
2433
|
if (!process.env.VITEST && fs15.existsSync(manifestPath(globalTarget)) && !targets.includes(globalTarget)) {
|
|
2475
2434
|
targets.push(globalTarget);
|
|
2476
2435
|
}
|
|
@@ -2540,11 +2499,11 @@ async function updateTarget(target, opts, sharedSource) {
|
|
|
2540
2499
|
const currentVersion = await contentSource.getVersion();
|
|
2541
2500
|
const installedVersion = manifest.source_version ?? manifest.arcane_version;
|
|
2542
2501
|
const profileNamesForSync = manifest.profile_command.split("+").filter(Boolean);
|
|
2543
|
-
const claudeDirForSync =
|
|
2502
|
+
const claudeDirForSync = path18.join(target, ".claude");
|
|
2544
2503
|
const earlySync = syncUnhashedFiles(
|
|
2545
2504
|
root,
|
|
2546
2505
|
claudeDirForSync,
|
|
2547
|
-
mergeProfiles(
|
|
2506
|
+
mergeProfiles(path18.join(root, "profiles"), profileNamesForSync),
|
|
2548
2507
|
opts.dryRun ?? false
|
|
2549
2508
|
);
|
|
2550
2509
|
if (earlySync.length > 0 && !opts.quiet) {
|
|
@@ -2578,13 +2537,13 @@ Update available: ${chalk7.red(installedVersion)} -> ${chalk7.green(currentVersi
|
|
|
2578
2537
|
);
|
|
2579
2538
|
}
|
|
2580
2539
|
const profileNames = manifest.profile_command.split("+").filter(Boolean);
|
|
2581
|
-
const profilesDir =
|
|
2540
|
+
const profilesDir = path18.join(root, "profiles");
|
|
2582
2541
|
const merged = mergeProfiles(profilesDir, profileNames);
|
|
2583
2542
|
const allRules = [...merged.rules.universal, ...merged.rules.gamedev];
|
|
2584
2543
|
const sourceHashes = computeSourceHashes(root, merged.skills, allRules, merged.agents);
|
|
2585
|
-
const claudeDir =
|
|
2544
|
+
const claudeDir = path18.join(target, ".claude");
|
|
2586
2545
|
const installedHashes = computeContentHashes(claudeDir);
|
|
2587
|
-
if (isSymlink(
|
|
2546
|
+
if (isSymlink(path18.join(claudeDir, "docs"))) {
|
|
2588
2547
|
sourceHashes.docs = {};
|
|
2589
2548
|
installedHashes.docs = {};
|
|
2590
2549
|
}
|
|
@@ -2877,18 +2836,18 @@ function applyUpdates(items, root, claudeDir, merged, force) {
|
|
|
2877
2836
|
}
|
|
2878
2837
|
function syncUnhashedFiles(root, claudeDir, merged, dryRun) {
|
|
2879
2838
|
const targets = [];
|
|
2880
|
-
const statuslineDst =
|
|
2839
|
+
const statuslineDst = path18.join(claudeDir, "statusline.sh");
|
|
2881
2840
|
if (merged.loaded.includes("statusline") || fs15.existsSync(statuslineDst)) {
|
|
2882
2841
|
targets.push({
|
|
2883
|
-
src:
|
|
2842
|
+
src: path18.join(root, "hooks", "statusline.sh"),
|
|
2884
2843
|
dst: statuslineDst,
|
|
2885
2844
|
label: "statusline.sh"
|
|
2886
2845
|
});
|
|
2887
2846
|
}
|
|
2888
2847
|
for (const style of merged.output_styles) {
|
|
2889
2848
|
targets.push({
|
|
2890
|
-
src:
|
|
2891
|
-
dst:
|
|
2849
|
+
src: path18.join(root, "output-styles", `${style}.md`),
|
|
2850
|
+
dst: path18.join(claudeDir, "output-styles", `${style}.md`),
|
|
2892
2851
|
label: `output-styles/${style}.md`
|
|
2893
2852
|
});
|
|
2894
2853
|
}
|
|
@@ -2896,21 +2855,21 @@ function syncUnhashedFiles(root, claudeDir, merged, dryRun) {
|
|
|
2896
2855
|
const { division, agent } = parseAgentEntry(raw);
|
|
2897
2856
|
if (!agent) continue;
|
|
2898
2857
|
targets.push({
|
|
2899
|
-
src:
|
|
2900
|
-
dst:
|
|
2858
|
+
src: path18.join(root, "agents", division, `${agent}.md`),
|
|
2859
|
+
dst: path18.join(claudeDir, "agents", division, `${agent}.md`),
|
|
2901
2860
|
label: `agents/${division}/${agent}.md`
|
|
2902
2861
|
});
|
|
2903
2862
|
}
|
|
2904
2863
|
if (merged.rules.gamedev.length > 0) {
|
|
2905
|
-
const templatesSrc =
|
|
2864
|
+
const templatesSrc = path18.join(root, "templates", "gamedev");
|
|
2906
2865
|
if (fs15.existsSync(templatesSrc)) {
|
|
2907
2866
|
for (const rel of fs15.readdirSync(templatesSrc, { recursive: true })) {
|
|
2908
|
-
const src =
|
|
2867
|
+
const src = path18.join(templatesSrc, rel);
|
|
2909
2868
|
if (!fs15.statSync(src).isFile()) continue;
|
|
2910
2869
|
targets.push({
|
|
2911
2870
|
src,
|
|
2912
|
-
dst:
|
|
2913
|
-
label: `skills/_templates/${String(rel).split(
|
|
2871
|
+
dst: path18.join(claudeDir, "skills", "_templates", rel),
|
|
2872
|
+
label: `skills/_templates/${String(rel).split(path18.sep).join("/")}`
|
|
2914
2873
|
});
|
|
2915
2874
|
}
|
|
2916
2875
|
}
|
|
@@ -2927,10 +2886,10 @@ function syncUnhashedFiles(root, claudeDir, merged, dryRun) {
|
|
|
2927
2886
|
if (fs15.existsSync(bak)) fs15.rmSync(bak, { force: true });
|
|
2928
2887
|
fs15.renameSync(t.dst, bak);
|
|
2929
2888
|
}
|
|
2930
|
-
fs15.mkdirSync(
|
|
2889
|
+
fs15.mkdirSync(path18.dirname(t.dst), { recursive: true });
|
|
2931
2890
|
fs15.copyFileSync(t.src, t.dst);
|
|
2932
2891
|
}
|
|
2933
|
-
const settingsPath =
|
|
2892
|
+
const settingsPath = path18.join(claudeDir, "settings.json");
|
|
2934
2893
|
if (fs15.existsSync(settingsPath)) {
|
|
2935
2894
|
try {
|
|
2936
2895
|
const settings = JSON.parse(fs15.readFileSync(settingsPath, "utf-8"));
|
|
@@ -2941,13 +2900,20 @@ function syncUnhashedFiles(root, claudeDir, merged, dryRun) {
|
|
|
2941
2900
|
settings.outputStyle = activeStyle;
|
|
2942
2901
|
dirty = true;
|
|
2943
2902
|
}
|
|
2944
|
-
const wanted = statuslineCommand(
|
|
2903
|
+
const wanted = statuslineCommand(path18.dirname(claudeDir));
|
|
2945
2904
|
const current = settings.statusLine;
|
|
2946
2905
|
if (wanted !== RELATIVE_STATUSLINE_COMMAND && current && current.command !== wanted) {
|
|
2947
2906
|
changed.push("settings.json (statusLine path)");
|
|
2948
2907
|
settings.statusLine = { ...current, command: wanted };
|
|
2949
2908
|
dirty = true;
|
|
2950
2909
|
}
|
|
2910
|
+
if (settings.hooks && typeof settings.hooks === "object") {
|
|
2911
|
+
const healed = healHooks(settings.hooks, path18.dirname(claudeDir));
|
|
2912
|
+
if (healed.length > 0) {
|
|
2913
|
+
changed.push(`settings.json (${healed.join(", ")})`);
|
|
2914
|
+
dirty = true;
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2951
2917
|
if (dirty && !dryRun) {
|
|
2952
2918
|
fs15.writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}
|
|
2953
2919
|
`, "utf-8");
|
|
@@ -2967,7 +2933,7 @@ function applyItem(item, root, claudeDir) {
|
|
|
2967
2933
|
}
|
|
2968
2934
|
copyDirSync(src, dst);
|
|
2969
2935
|
} else {
|
|
2970
|
-
const dir =
|
|
2936
|
+
const dir = path18.dirname(dst);
|
|
2971
2937
|
if (!fs15.existsSync(dir)) {
|
|
2972
2938
|
fs15.mkdirSync(dir, { recursive: true });
|
|
2973
2939
|
}
|
|
@@ -2977,8 +2943,8 @@ function applyItem(item, root, claudeDir) {
|
|
|
2977
2943
|
function quarantineItem(item, claudeDir) {
|
|
2978
2944
|
const installed = getInstalledPath(item, claudeDir);
|
|
2979
2945
|
if (!fs15.existsSync(installed)) return;
|
|
2980
|
-
const quarantine =
|
|
2981
|
-
ensureDir(
|
|
2946
|
+
const quarantine = path18.join(claudeDir, TRASH_DIR, `${item.type}s`, item.name);
|
|
2947
|
+
ensureDir(path18.dirname(quarantine));
|
|
2982
2948
|
if (fs15.existsSync(quarantine)) {
|
|
2983
2949
|
fs15.rmSync(quarantine, { recursive: true, force: true });
|
|
2984
2950
|
}
|
|
@@ -2987,18 +2953,18 @@ function quarantineItem(item, claudeDir) {
|
|
|
2987
2953
|
function getSourcePath(item, root) {
|
|
2988
2954
|
switch (item.type) {
|
|
2989
2955
|
case "skill":
|
|
2990
|
-
return
|
|
2956
|
+
return path18.join(root, "skills", item.name);
|
|
2991
2957
|
case "rule": {
|
|
2992
|
-
const direct =
|
|
2958
|
+
const direct = path18.join(root, "rules", item.name);
|
|
2993
2959
|
if (fs15.existsSync(direct)) return direct;
|
|
2994
|
-
return
|
|
2960
|
+
return path18.join(root, "rules", "gamedev", item.name);
|
|
2995
2961
|
}
|
|
2996
2962
|
case "agent":
|
|
2997
|
-
return
|
|
2963
|
+
return path18.join(root, "agents", item.name);
|
|
2998
2964
|
case "hook":
|
|
2999
|
-
return
|
|
2965
|
+
return path18.join(root, "hooks", item.name);
|
|
3000
2966
|
case "doc":
|
|
3001
|
-
return
|
|
2967
|
+
return path18.join(root, "docs", item.name);
|
|
3002
2968
|
default:
|
|
3003
2969
|
return null;
|
|
3004
2970
|
}
|
|
@@ -3006,15 +2972,15 @@ function getSourcePath(item, root) {
|
|
|
3006
2972
|
function getInstalledPath(item, claudeDir) {
|
|
3007
2973
|
switch (item.type) {
|
|
3008
2974
|
case "skill":
|
|
3009
|
-
return
|
|
2975
|
+
return path18.join(claudeDir, "skills", item.name);
|
|
3010
2976
|
case "rule":
|
|
3011
|
-
return
|
|
2977
|
+
return path18.join(claudeDir, "rules", item.name);
|
|
3012
2978
|
case "agent":
|
|
3013
|
-
return
|
|
2979
|
+
return path18.join(claudeDir, "agents", item.name);
|
|
3014
2980
|
case "hook":
|
|
3015
|
-
return
|
|
2981
|
+
return path18.join(claudeDir, "hooks", item.name);
|
|
3016
2982
|
case "doc":
|
|
3017
|
-
return
|
|
2983
|
+
return path18.join(claudeDir, "docs", item.name);
|
|
3018
2984
|
}
|
|
3019
2985
|
}
|
|
3020
2986
|
function isSymlink(p3) {
|
|
@@ -3057,7 +3023,7 @@ async function cleanCommand(opts) {
|
|
|
3057
3023
|
|
|
3058
3024
|
// src/commands/worktree.ts
|
|
3059
3025
|
import fs16 from "fs";
|
|
3060
|
-
import
|
|
3026
|
+
import path19 from "path";
|
|
3061
3027
|
import { execSync as execSync2 } from "child_process";
|
|
3062
3028
|
import chalk9 from "chalk";
|
|
3063
3029
|
async function worktreeCommand(branch, opts) {
|
|
@@ -3068,7 +3034,7 @@ async function worktreeCommand(branch, opts) {
|
|
|
3068
3034
|
process.exit(1);
|
|
3069
3035
|
}
|
|
3070
3036
|
const repoRoot = wtInfo.isWorktree ? wtInfo.mainWorktreePath : cwd;
|
|
3071
|
-
const worktreePath = opts.path ?
|
|
3037
|
+
const worktreePath = opts.path ? path19.resolve(opts.path) : defaultWorktreePath(repoRoot, branch);
|
|
3072
3038
|
if (fs16.existsSync(worktreePath)) {
|
|
3073
3039
|
console.error(
|
|
3074
3040
|
chalk9.red(`Path already exists: ${worktreePath}`)
|
|
@@ -3116,7 +3082,7 @@ async function worktreeCommand(branch, opts) {
|
|
|
3116
3082
|
}
|
|
3117
3083
|
console.log(" Installing Arcane...");
|
|
3118
3084
|
const root = getPackageRoot();
|
|
3119
|
-
const profilesDir =
|
|
3085
|
+
const profilesDir = path19.join(root, "profiles");
|
|
3120
3086
|
const profileNames = profileExpr.split("+").filter(Boolean);
|
|
3121
3087
|
const merged = mergeProfiles(profilesDir, profileNames);
|
|
3122
3088
|
const shareFrom = opts.share !== false ? cwd : void 0;
|
|
@@ -3152,7 +3118,7 @@ async function worktreeCommand(branch, opts) {
|
|
|
3152
3118
|
}
|
|
3153
3119
|
}
|
|
3154
3120
|
if (opts.isolate) {
|
|
3155
|
-
const isolateScript =
|
|
3121
|
+
const isolateScript = path19.join(
|
|
3156
3122
|
worktreePath,
|
|
3157
3123
|
".claude",
|
|
3158
3124
|
"skills",
|
|
@@ -3194,12 +3160,12 @@ async function worktreeCommand(branch, opts) {
|
|
|
3194
3160
|
|
|
3195
3161
|
// src/commands/global.ts
|
|
3196
3162
|
import fs17 from "fs";
|
|
3197
|
-
import
|
|
3198
|
-
import
|
|
3163
|
+
import path20 from "path";
|
|
3164
|
+
import os5 from "os";
|
|
3199
3165
|
import chalk10 from "chalk";
|
|
3200
|
-
var CLAUDE_HOME =
|
|
3201
|
-
var SCRIPTS_DIR =
|
|
3202
|
-
var SETTINGS_PATH =
|
|
3166
|
+
var CLAUDE_HOME = path20.join(os5.homedir(), ".claude");
|
|
3167
|
+
var SCRIPTS_DIR = path20.join(CLAUDE_HOME, "scripts", "worktree-isolation");
|
|
3168
|
+
var SETTINGS_PATH = path20.join(CLAUDE_HOME, "settings.json");
|
|
3203
3169
|
async function globalCommand(opts) {
|
|
3204
3170
|
if (opts.status) {
|
|
3205
3171
|
showStatus();
|
|
@@ -3229,7 +3195,7 @@ function settingsParseable() {
|
|
|
3229
3195
|
function showStatus() {
|
|
3230
3196
|
console.log(chalk10.bold("\n=== Arcane Global Status ===\n"));
|
|
3231
3197
|
const hasScripts = fs17.existsSync(
|
|
3232
|
-
|
|
3198
|
+
path20.join(SCRIPTS_DIR, "apply.py")
|
|
3233
3199
|
);
|
|
3234
3200
|
console.log(
|
|
3235
3201
|
` Worktree scripts: ${hasScripts ? chalk10.green("installed") : chalk10.dim("not installed")}`
|
|
@@ -3247,7 +3213,7 @@ function showStatus() {
|
|
|
3247
3213
|
function installGlobal() {
|
|
3248
3214
|
console.log(chalk10.bold("\nInstalling Arcane global hooks...\n"));
|
|
3249
3215
|
const root = getPackageRoot();
|
|
3250
|
-
const scriptsSrc =
|
|
3216
|
+
const scriptsSrc = path20.join(
|
|
3251
3217
|
root,
|
|
3252
3218
|
"skills",
|
|
3253
3219
|
"worktree-isolation",
|
|
@@ -3261,8 +3227,8 @@ function installGlobal() {
|
|
|
3261
3227
|
}
|
|
3262
3228
|
fs17.mkdirSync(SCRIPTS_DIR, { recursive: true });
|
|
3263
3229
|
for (const file of ["apply.py", "lib.py"]) {
|
|
3264
|
-
const src =
|
|
3265
|
-
const dst =
|
|
3230
|
+
const src = path20.join(scriptsSrc, file);
|
|
3231
|
+
const dst = path20.join(SCRIPTS_DIR, file);
|
|
3266
3232
|
if (!fs17.existsSync(src)) {
|
|
3267
3233
|
console.warn(chalk10.yellow(` WARN: ${file} not found, skipping`));
|
|
3268
3234
|
continue;
|
|
@@ -3319,7 +3285,7 @@ function readSettings() {
|
|
|
3319
3285
|
}
|
|
3320
3286
|
}
|
|
3321
3287
|
function writeSettings(settings) {
|
|
3322
|
-
fs17.mkdirSync(
|
|
3288
|
+
fs17.mkdirSync(path20.dirname(SETTINGS_PATH), { recursive: true });
|
|
3323
3289
|
fs17.writeFileSync(
|
|
3324
3290
|
SETTINGS_PATH,
|
|
3325
3291
|
JSON.stringify(settings, null, 2) + "\n",
|
|
@@ -3331,8 +3297,8 @@ function isHookInstalled() {
|
|
|
3331
3297
|
const hooks = settings.hooks ?? {};
|
|
3332
3298
|
const sessionStart = hooks.SessionStart ?? [];
|
|
3333
3299
|
for (const entry of sessionStart) {
|
|
3334
|
-
for (const
|
|
3335
|
-
if (
|
|
3300
|
+
for (const hook2 of entry.hooks ?? []) {
|
|
3301
|
+
if (hook2.command?.includes("worktree-isolation/apply.py") && hook2.command?.includes("--quiet")) {
|
|
3336
3302
|
return true;
|
|
3337
3303
|
}
|
|
3338
3304
|
}
|
|
@@ -3378,10 +3344,10 @@ function removeHookFromSettings() {
|
|
|
3378
3344
|
|
|
3379
3345
|
// src/update-check.ts
|
|
3380
3346
|
import fs18 from "fs";
|
|
3381
|
-
import
|
|
3347
|
+
import path21 from "path";
|
|
3382
3348
|
import chalk11 from "chalk";
|
|
3383
3349
|
function checkFile() {
|
|
3384
|
-
return
|
|
3350
|
+
return path21.join(arcaneHome(), "last-check.json");
|
|
3385
3351
|
}
|
|
3386
3352
|
var CHECK_INTERVAL_MS = 4 * 60 * 60 * 1e3;
|
|
3387
3353
|
var GITHUB_OWNER2 = "SebastianLuser";
|
|
@@ -3519,7 +3485,7 @@ function readCachedCheck() {
|
|
|
3519
3485
|
function writeCachedCheck(result) {
|
|
3520
3486
|
try {
|
|
3521
3487
|
const file = checkFile();
|
|
3522
|
-
const dir =
|
|
3488
|
+
const dir = path21.dirname(file);
|
|
3523
3489
|
if (!fs18.existsSync(dir)) {
|
|
3524
3490
|
fs18.mkdirSync(dir, { recursive: true });
|
|
3525
3491
|
}
|