claude-code-arcane 2.12.0 → 2.14.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 +16 -0
- package/agents/visualnovel/vn-producer.md +2 -2
- package/dist/cli.js +250 -275
- package/docs/USER-GUIDE.md +3 -3
- package/docs/collaborative-design.md +1 -1
- package/docs/context-management.md +5 -22
- package/docs/coordination-rules.md +1 -1
- package/docs/directory-structure.md +9 -4
- package/docs/gates/README.md +2 -2
- package/hooks/detect-division.sh +1 -1
- package/hooks/guard.py +445 -0
- package/hooks/guard.sh +18 -0
- package/hooks/log-agent-stop.sh +1 -1
- package/hooks/log-agent.sh +1 -1
- package/hooks/notify.sh +1 -1
- package/hooks/post-compact.sh +1 -1
- package/hooks/pre-compact.sh +1 -1
- package/hooks/session-start.sh +41 -5
- package/hooks/session-stop.sh +5 -1
- package/hooks/validate-commit.sh +11 -3
- package/hooks/worktree-stop.sh +48 -0
- package/package.json +1 -1
- package/profiles/audio.yaml +0 -1
- package/profiles/core.yaml +9 -4
- package/profiles/visual-novel.yaml +0 -1
- package/rules/implementation-workflow.md +24 -0
- package/skills/help/SKILL.md +2 -2
- package/skills/start/SKILL.md +2 -2
- package/skills/story-done/SKILL.md +2 -2
- package/skills/ux-design/SKILL.md +1 -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,84 @@ 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
|
-
]
|
|
974
|
-
}
|
|
975
|
-
],
|
|
976
|
-
PostCompact: [
|
|
977
|
-
{
|
|
978
|
-
matcher: "",
|
|
979
|
-
hooks: [
|
|
980
|
-
{
|
|
981
|
-
type: "command",
|
|
982
|
-
command: "bash .claude/hooks/post-compact.sh",
|
|
983
|
-
timeout: 10
|
|
984
|
-
}
|
|
985
|
-
]
|
|
923
|
+
hooks: [hook(target, "validate-assets.sh", 10), hook(target, "validate-skill-change.sh", 5)]
|
|
986
924
|
}
|
|
987
925
|
],
|
|
926
|
+
Notification: one("notify.sh", 10),
|
|
927
|
+
PreCompact: one("pre-compact.sh", 10),
|
|
928
|
+
PostCompact: one("post-compact.sh", 10),
|
|
988
929
|
Stop: [
|
|
989
|
-
{
|
|
990
|
-
matcher: "",
|
|
991
|
-
hooks: [
|
|
992
|
-
{
|
|
993
|
-
type: "command",
|
|
994
|
-
command: "bash .claude/hooks/session-stop.sh",
|
|
995
|
-
timeout: 10
|
|
996
|
-
}
|
|
997
|
-
]
|
|
998
|
-
}
|
|
930
|
+
{ matcher: "", hooks: [hook(target, "session-stop.sh", 10), hook(target, "worktree-stop.sh", 10)] }
|
|
999
931
|
],
|
|
1000
|
-
SubagentStart:
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
932
|
+
SubagentStart: one("log-agent.sh", 5),
|
|
933
|
+
SubagentStop: one("log-agent-stop.sh", 5)
|
|
934
|
+
};
|
|
935
|
+
}
|
|
936
|
+
function healHooks(hooks, target) {
|
|
937
|
+
const changes = [];
|
|
938
|
+
for (const [event, groups] of Object.entries(hooks)) {
|
|
939
|
+
if (!Array.isArray(groups)) continue;
|
|
940
|
+
for (const group of groups) {
|
|
941
|
+
const kept = (group.hooks ?? []).filter((entry) => {
|
|
942
|
+
const script = typeof entry.command === "string" && entry.command.match(ARCANE_COMMAND);
|
|
943
|
+
if (script && RETIRED_SCRIPTS.has(script[1])) {
|
|
944
|
+
changes.push(`retired hook: ${script[1]}`);
|
|
945
|
+
return false;
|
|
946
|
+
}
|
|
947
|
+
return true;
|
|
948
|
+
});
|
|
949
|
+
if (kept.length !== (group.hooks ?? []).length) group.hooks = kept;
|
|
950
|
+
}
|
|
951
|
+
hooks[event] = groups.filter((group) => (group.hooks ?? []).length > 0);
|
|
952
|
+
}
|
|
953
|
+
for (const groups of Object.values(hooks)) {
|
|
954
|
+
for (const group of groups ?? []) {
|
|
955
|
+
for (const entry of group.hooks ?? []) {
|
|
956
|
+
const legacy = typeof entry.command === "string" && entry.command.match(LEGACY_COMMAND);
|
|
957
|
+
if (legacy) {
|
|
958
|
+
entry.command = hookCommand(target, legacy[1]);
|
|
959
|
+
changes.push(`hook path: ${legacy[1]}`);
|
|
960
|
+
}
|
|
1022
961
|
}
|
|
1023
|
-
|
|
962
|
+
}
|
|
1024
963
|
}
|
|
964
|
+
const pre = hooks.PreToolUse ??= [];
|
|
965
|
+
const hasGuard = (group) => group.hooks?.some((h) => h.command?.includes("guard.sh"));
|
|
966
|
+
const bash = pre.find((g) => g.matcher === "Bash");
|
|
967
|
+
if (!bash) {
|
|
968
|
+
pre.unshift({ matcher: "Bash", hooks: [hook(target, "guard.sh", 10)] });
|
|
969
|
+
changes.push("guard for Bash");
|
|
970
|
+
} else if (!hasGuard(bash)) {
|
|
971
|
+
bash.hooks = [hook(target, "guard.sh", 10), ...bash.hooks ?? []];
|
|
972
|
+
changes.push("guard for Bash");
|
|
973
|
+
}
|
|
974
|
+
if (!pre.some((g) => g.matcher === FILE_TOOLS_MATCHER && hasGuard(g))) {
|
|
975
|
+
pre.push({ matcher: FILE_TOOLS_MATCHER, hooks: [hook(target, "guard.sh", 10)] });
|
|
976
|
+
changes.push("guard for file edits");
|
|
977
|
+
}
|
|
978
|
+
const stop = hooks.Stop ??= [];
|
|
979
|
+
if (!stop.some((g) => g.hooks?.some((h) => h.command?.includes("worktree-stop.sh")))) {
|
|
980
|
+
const group = stop.find((g) => !g.matcher);
|
|
981
|
+
if (group) group.hooks = [...group.hooks ?? [], hook(target, "worktree-stop.sh", 10)];
|
|
982
|
+
else stop.push({ matcher: "", hooks: [hook(target, "worktree-stop.sh", 10)] });
|
|
983
|
+
changes.push("worktree exit on stop");
|
|
984
|
+
}
|
|
985
|
+
return changes;
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
// src/installer.ts
|
|
989
|
+
var SETTINGS_TEMPLATE = {
|
|
990
|
+
$schema: "https://json.schemastore.org/claude-code-settings.json",
|
|
991
|
+
permissions: { allow: [], deny: [] }
|
|
1025
992
|
};
|
|
1026
993
|
var Installer = class {
|
|
1027
994
|
root;
|
|
@@ -1035,7 +1002,7 @@ var Installer = class {
|
|
|
1035
1002
|
constructor(merged, opts) {
|
|
1036
1003
|
this.root = opts.contentRoot ?? getPackageRoot();
|
|
1037
1004
|
this.target = opts.target;
|
|
1038
|
-
this.claudeDir =
|
|
1005
|
+
this.claudeDir = path9.join(opts.target, ".claude");
|
|
1039
1006
|
this.merged = merged;
|
|
1040
1007
|
this.dryRun = opts.dryRun;
|
|
1041
1008
|
this.shareFrom = opts.shareFrom;
|
|
@@ -1069,7 +1036,7 @@ var Installer = class {
|
|
|
1069
1036
|
}
|
|
1070
1037
|
backup() {
|
|
1071
1038
|
if (!fs7.existsSync(this.claudeDir)) return;
|
|
1072
|
-
const backup =
|
|
1039
|
+
const backup = path9.join(this.target, ".claude.bak");
|
|
1073
1040
|
if (this.dryRun) {
|
|
1074
1041
|
this.log(" [dry-run] Would backup .claude/ -> .claude.bak/");
|
|
1075
1042
|
return;
|
|
@@ -1083,10 +1050,10 @@ var Installer = class {
|
|
|
1083
1050
|
createDirs() {
|
|
1084
1051
|
if (this.dryRun) return;
|
|
1085
1052
|
for (const d of ["skills", "rules", "agents", "docs"]) {
|
|
1086
|
-
ensureDir(
|
|
1053
|
+
ensureDir(path9.join(this.claudeDir, d));
|
|
1087
1054
|
}
|
|
1088
1055
|
if (this.merged.output_styles.length > 0) {
|
|
1089
|
-
ensureDir(
|
|
1056
|
+
ensureDir(path9.join(this.claudeDir, "output-styles"));
|
|
1090
1057
|
}
|
|
1091
1058
|
}
|
|
1092
1059
|
get hasStatusline() {
|
|
@@ -1094,7 +1061,7 @@ var Installer = class {
|
|
|
1094
1061
|
}
|
|
1095
1062
|
copyHooks() {
|
|
1096
1063
|
this.log("\nHooks:");
|
|
1097
|
-
const hooksDir =
|
|
1064
|
+
const hooksDir = path9.join(this.claudeDir, "hooks");
|
|
1098
1065
|
if (this.dryRun) {
|
|
1099
1066
|
if (this.shareFrom) {
|
|
1100
1067
|
this.log(" [dry-run] hooks/ -> symlink from main worktree");
|
|
@@ -1105,7 +1072,7 @@ var Installer = class {
|
|
|
1105
1072
|
return;
|
|
1106
1073
|
}
|
|
1107
1074
|
if (this.shareFrom) {
|
|
1108
|
-
const sharedHooks =
|
|
1075
|
+
const sharedHooks = path9.join(this.shareFrom, ".claude", "hooks");
|
|
1109
1076
|
if (fs7.existsSync(sharedHooks)) {
|
|
1110
1077
|
const result = linkOrCopyDir(sharedHooks, hooksDir, "symlink");
|
|
1111
1078
|
if (result === "linked") {
|
|
@@ -1118,13 +1085,13 @@ var Installer = class {
|
|
|
1118
1085
|
return;
|
|
1119
1086
|
}
|
|
1120
1087
|
}
|
|
1121
|
-
const hooksSrc =
|
|
1088
|
+
const hooksSrc = path9.join(this.root, "hooks");
|
|
1122
1089
|
if (fs7.existsSync(hooksSrc)) {
|
|
1123
1090
|
ensureDir(hooksDir);
|
|
1124
1091
|
for (const entry of fs7.readdirSync(hooksSrc, { withFileTypes: true })) {
|
|
1125
1092
|
if (entry.name === "statusline.sh") continue;
|
|
1126
|
-
const src =
|
|
1127
|
-
const dst =
|
|
1093
|
+
const src = path9.join(hooksSrc, entry.name);
|
|
1094
|
+
const dst = path9.join(hooksDir, entry.name);
|
|
1128
1095
|
if (entry.isDirectory()) {
|
|
1129
1096
|
copyDirSync(src, dst);
|
|
1130
1097
|
} else {
|
|
@@ -1137,11 +1104,11 @@ var Installer = class {
|
|
|
1137
1104
|
}
|
|
1138
1105
|
copyStatusline() {
|
|
1139
1106
|
if (!this.hasStatusline) return;
|
|
1140
|
-
const statusline =
|
|
1107
|
+
const statusline = path9.join(this.root, "hooks", "statusline.sh");
|
|
1141
1108
|
if (fs7.existsSync(statusline)) {
|
|
1142
1109
|
fs7.copyFileSync(
|
|
1143
1110
|
statusline,
|
|
1144
|
-
|
|
1111
|
+
path9.join(this.claudeDir, "statusline.sh")
|
|
1145
1112
|
);
|
|
1146
1113
|
this.log(" [ok] statusline.sh (statusline profile)");
|
|
1147
1114
|
}
|
|
@@ -1149,12 +1116,12 @@ var Installer = class {
|
|
|
1149
1116
|
copySkills() {
|
|
1150
1117
|
this.log("\nSkills:");
|
|
1151
1118
|
for (const skill of this.merged.skills) {
|
|
1152
|
-
const src =
|
|
1119
|
+
const src = path9.join(this.root, "skills", skill);
|
|
1153
1120
|
if (!fs7.existsSync(src)) {
|
|
1154
1121
|
this.log(` WARN: Skill '${skill}' not found in skills/`);
|
|
1155
1122
|
continue;
|
|
1156
1123
|
}
|
|
1157
|
-
const dst =
|
|
1124
|
+
const dst = path9.join(this.claudeDir, "skills", skill);
|
|
1158
1125
|
if (this.dryRun) {
|
|
1159
1126
|
this.log(` [dry-run] ${skill}`);
|
|
1160
1127
|
} else {
|
|
@@ -1164,9 +1131,9 @@ var Installer = class {
|
|
|
1164
1131
|
}
|
|
1165
1132
|
const hasGamedev = this.merged.rules.gamedev.length > 0;
|
|
1166
1133
|
if (hasGamedev) {
|
|
1167
|
-
const tplSrc =
|
|
1134
|
+
const tplSrc = path9.join(this.root, "templates", "gamedev");
|
|
1168
1135
|
if (fs7.existsSync(tplSrc)) {
|
|
1169
|
-
const tplDst =
|
|
1136
|
+
const tplDst = path9.join(this.claudeDir, "skills", "_templates");
|
|
1170
1137
|
if (this.dryRun) {
|
|
1171
1138
|
this.log(" [dry-run] _templates/");
|
|
1172
1139
|
} else {
|
|
@@ -1179,7 +1146,7 @@ var Installer = class {
|
|
|
1179
1146
|
copyRules() {
|
|
1180
1147
|
this.log("\nRules:");
|
|
1181
1148
|
for (const rule of this.merged.rules.universal) {
|
|
1182
|
-
const src =
|
|
1149
|
+
const src = path9.join(this.root, "rules", `${rule}.md`);
|
|
1183
1150
|
if (!fs7.existsSync(src)) {
|
|
1184
1151
|
this.log(` WARN: Rule '${rule}.md' not found`);
|
|
1185
1152
|
continue;
|
|
@@ -1189,13 +1156,13 @@ var Installer = class {
|
|
|
1189
1156
|
} else {
|
|
1190
1157
|
fs7.copyFileSync(
|
|
1191
1158
|
src,
|
|
1192
|
-
|
|
1159
|
+
path9.join(this.claudeDir, "rules", `${rule}.md`)
|
|
1193
1160
|
);
|
|
1194
1161
|
this.log(` [ok] ${rule}.md`);
|
|
1195
1162
|
}
|
|
1196
1163
|
}
|
|
1197
1164
|
for (const rule of this.merged.rules.gamedev) {
|
|
1198
|
-
const src =
|
|
1165
|
+
const src = path9.join(this.root, "rules", "gamedev", `${rule}.md`);
|
|
1199
1166
|
if (!fs7.existsSync(src)) {
|
|
1200
1167
|
this.log(` WARN: Rule '${rule}.md' not found`);
|
|
1201
1168
|
continue;
|
|
@@ -1205,7 +1172,7 @@ var Installer = class {
|
|
|
1205
1172
|
} else {
|
|
1206
1173
|
fs7.copyFileSync(
|
|
1207
1174
|
src,
|
|
1208
|
-
|
|
1175
|
+
path9.join(this.claudeDir, "rules", `${rule}.md`)
|
|
1209
1176
|
);
|
|
1210
1177
|
this.log(` [ok] ${rule}.md`);
|
|
1211
1178
|
}
|
|
@@ -1215,7 +1182,7 @@ var Installer = class {
|
|
|
1215
1182
|
if (this.merged.output_styles.length === 0) return;
|
|
1216
1183
|
this.log("\nOutput styles:");
|
|
1217
1184
|
for (const style of this.merged.output_styles) {
|
|
1218
|
-
const src =
|
|
1185
|
+
const src = path9.join(this.root, "output-styles", `${style}.md`);
|
|
1219
1186
|
if (!fs7.existsSync(src)) {
|
|
1220
1187
|
this.log(` WARN: Output style '${style}.md' not found`);
|
|
1221
1188
|
continue;
|
|
@@ -1225,7 +1192,7 @@ var Installer = class {
|
|
|
1225
1192
|
} else {
|
|
1226
1193
|
fs7.copyFileSync(
|
|
1227
1194
|
src,
|
|
1228
|
-
|
|
1195
|
+
path9.join(this.claudeDir, "output-styles", `${style}.md`)
|
|
1229
1196
|
);
|
|
1230
1197
|
this.log(` [ok] ${style}.md`);
|
|
1231
1198
|
}
|
|
@@ -1249,8 +1216,8 @@ var Installer = class {
|
|
|
1249
1216
|
}
|
|
1250
1217
|
}
|
|
1251
1218
|
copyDocs() {
|
|
1252
|
-
const docsDir =
|
|
1253
|
-
const docsSrc =
|
|
1219
|
+
const docsDir = path9.join(this.claudeDir, "docs");
|
|
1220
|
+
const docsSrc = path9.join(this.root, "docs");
|
|
1254
1221
|
if (!fs7.existsSync(docsSrc)) return;
|
|
1255
1222
|
this.log("\nDocs:");
|
|
1256
1223
|
if (this.dryRun) {
|
|
@@ -1262,7 +1229,7 @@ var Installer = class {
|
|
|
1262
1229
|
return;
|
|
1263
1230
|
}
|
|
1264
1231
|
if (this.shareFrom) {
|
|
1265
|
-
const sharedDocs =
|
|
1232
|
+
const sharedDocs = path9.join(this.shareFrom, ".claude", "docs");
|
|
1266
1233
|
if (fs7.existsSync(sharedDocs)) {
|
|
1267
1234
|
const result = linkOrCopyDir(sharedDocs, docsDir, "symlink");
|
|
1268
1235
|
if (result === "linked") {
|
|
@@ -1282,6 +1249,7 @@ var Installer = class {
|
|
|
1282
1249
|
const settings = JSON.parse(JSON.stringify(SETTINGS_TEMPLATE));
|
|
1283
1250
|
settings.permissions.allow = [...this.merged.permissions.allow];
|
|
1284
1251
|
settings.permissions.deny = [...this.merged.permissions.deny];
|
|
1252
|
+
settings.hooks = defaultHooks(this.target);
|
|
1285
1253
|
if (this.hasStatusline) {
|
|
1286
1254
|
settings.statusLine = {
|
|
1287
1255
|
type: "command",
|
|
@@ -1298,7 +1266,7 @@ var Installer = class {
|
|
|
1298
1266
|
);
|
|
1299
1267
|
return;
|
|
1300
1268
|
}
|
|
1301
|
-
const settingsPath =
|
|
1269
|
+
const settingsPath = path9.join(this.claudeDir, "settings.json");
|
|
1302
1270
|
fs7.writeFileSync(
|
|
1303
1271
|
settingsPath,
|
|
1304
1272
|
JSON.stringify(settings, null, 2) + "\n",
|
|
@@ -1308,13 +1276,13 @@ var Installer = class {
|
|
|
1308
1276
|
}
|
|
1309
1277
|
};
|
|
1310
1278
|
function clean(target) {
|
|
1311
|
-
const claudeDir =
|
|
1312
|
-
const backup =
|
|
1279
|
+
const claudeDir = path9.join(target, ".claude");
|
|
1280
|
+
const backup = path9.join(target, ".claude.bak");
|
|
1313
1281
|
if (!fs7.existsSync(claudeDir)) {
|
|
1314
1282
|
console.log(`No .claude/ found in ${target} \u2014 nothing to clean.`);
|
|
1315
1283
|
return;
|
|
1316
1284
|
}
|
|
1317
|
-
const manifestFile =
|
|
1285
|
+
const manifestFile = path9.join(claudeDir, "arcane-manifest.json");
|
|
1318
1286
|
if (fs7.existsSync(manifestFile)) {
|
|
1319
1287
|
const data = JSON.parse(fs7.readFileSync(manifestFile, "utf-8"));
|
|
1320
1288
|
const profiles = (data.profiles ?? []).filter(
|
|
@@ -1324,7 +1292,7 @@ function clean(target) {
|
|
|
1324
1292
|
console.log(` Installed: ${data.installed_at ?? "unknown"}`);
|
|
1325
1293
|
const shared = data.worktree?.shared_dirs ?? [];
|
|
1326
1294
|
for (const dir of shared) {
|
|
1327
|
-
const p3 =
|
|
1295
|
+
const p3 = path9.join(claudeDir, dir);
|
|
1328
1296
|
if (isSymlinkOrJunction(p3)) {
|
|
1329
1297
|
fs7.unlinkSync(p3);
|
|
1330
1298
|
console.log(` [ok] Unlinked shared ${dir}/`);
|
|
@@ -1332,12 +1300,12 @@ function clean(target) {
|
|
|
1332
1300
|
}
|
|
1333
1301
|
}
|
|
1334
1302
|
for (const sub of ["hooks", "docs", "skills", "agents", "rules"]) {
|
|
1335
|
-
const p3 =
|
|
1303
|
+
const p3 = path9.join(claudeDir, sub);
|
|
1336
1304
|
safeRemove(p3);
|
|
1337
1305
|
}
|
|
1338
1306
|
const remaining = fs7.readdirSync(claudeDir);
|
|
1339
1307
|
for (const f of remaining) {
|
|
1340
|
-
const p3 =
|
|
1308
|
+
const p3 = path9.join(claudeDir, f);
|
|
1341
1309
|
safeRemove(p3);
|
|
1342
1310
|
}
|
|
1343
1311
|
if (fs7.existsSync(claudeDir)) {
|
|
@@ -1352,24 +1320,24 @@ function clean(target) {
|
|
|
1352
1320
|
|
|
1353
1321
|
// src/content-source.ts
|
|
1354
1322
|
import fs9 from "fs";
|
|
1355
|
-
import
|
|
1356
|
-
import
|
|
1323
|
+
import path11 from "path";
|
|
1324
|
+
import os3 from "os";
|
|
1357
1325
|
|
|
1358
1326
|
// src/cache.ts
|
|
1359
1327
|
import fs8 from "fs";
|
|
1360
|
-
import
|
|
1328
|
+
import path10 from "path";
|
|
1361
1329
|
function cacheDir() {
|
|
1362
|
-
return
|
|
1330
|
+
return path10.join(arcaneHome(), "cache");
|
|
1363
1331
|
}
|
|
1364
1332
|
function getCachePath(version) {
|
|
1365
|
-
return
|
|
1333
|
+
return path10.join(cacheDir(), version);
|
|
1366
1334
|
}
|
|
1367
1335
|
function isUsableContentRoot(dir) {
|
|
1368
|
-
return fs8.existsSync(
|
|
1336
|
+
return fs8.existsSync(path10.join(dir, "profiles")) && fs8.existsSync(path10.join(dir, "skills"));
|
|
1369
1337
|
}
|
|
1370
1338
|
function isCached(version) {
|
|
1371
1339
|
const versionDir = getCachePath(version);
|
|
1372
|
-
return fs8.existsSync(versionDir) && fs8.existsSync(
|
|
1340
|
+
return fs8.existsSync(versionDir) && fs8.existsSync(path10.join(versionDir, ".cache-meta.json")) && isUsableContentRoot(versionDir);
|
|
1373
1341
|
}
|
|
1374
1342
|
function getCachedContentRoot(version) {
|
|
1375
1343
|
if (!isCached(version)) return null;
|
|
@@ -1382,9 +1350,9 @@ function storeInCache(version, contentDir) {
|
|
|
1382
1350
|
}
|
|
1383
1351
|
fs8.mkdirSync(versionDir, { recursive: true });
|
|
1384
1352
|
for (const dir of ["skills", "rules", "agents", "hooks", "profiles", "templates", "docs", "output-styles"]) {
|
|
1385
|
-
const src =
|
|
1353
|
+
const src = path10.join(contentDir, dir);
|
|
1386
1354
|
if (fs8.existsSync(src)) {
|
|
1387
|
-
copyDirRecursive(src,
|
|
1355
|
+
copyDirRecursive(src, path10.join(versionDir, dir));
|
|
1388
1356
|
}
|
|
1389
1357
|
}
|
|
1390
1358
|
const meta = {
|
|
@@ -1392,7 +1360,7 @@ function storeInCache(version, contentDir) {
|
|
|
1392
1360
|
cached_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1393
1361
|
};
|
|
1394
1362
|
fs8.writeFileSync(
|
|
1395
|
-
|
|
1363
|
+
path10.join(versionDir, ".cache-meta.json"),
|
|
1396
1364
|
JSON.stringify(meta, null, 2) + "\n"
|
|
1397
1365
|
);
|
|
1398
1366
|
return versionDir;
|
|
@@ -1400,7 +1368,7 @@ function storeInCache(version, contentDir) {
|
|
|
1400
1368
|
function pruneCache(keepCount = 3) {
|
|
1401
1369
|
if (!fs8.existsSync(cacheDir())) return;
|
|
1402
1370
|
const entries = fs8.readdirSync(cacheDir(), { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => {
|
|
1403
|
-
const metaPath =
|
|
1371
|
+
const metaPath = path10.join(cacheDir(), e.name, ".cache-meta.json");
|
|
1404
1372
|
let cachedAt = 0;
|
|
1405
1373
|
if (fs8.existsSync(metaPath)) {
|
|
1406
1374
|
try {
|
|
@@ -1413,13 +1381,13 @@ function pruneCache(keepCount = 3) {
|
|
|
1413
1381
|
return { name: e.name, cachedAt };
|
|
1414
1382
|
}).sort((a, b) => b.cachedAt - a.cachedAt);
|
|
1415
1383
|
for (const entry of entries.slice(keepCount)) {
|
|
1416
|
-
fs8.rmSync(
|
|
1384
|
+
fs8.rmSync(path10.join(cacheDir(), entry.name), { recursive: true, force: true });
|
|
1417
1385
|
}
|
|
1418
1386
|
}
|
|
1419
1387
|
function listCachedVersions() {
|
|
1420
1388
|
if (!fs8.existsSync(cacheDir())) return [];
|
|
1421
1389
|
return fs8.readdirSync(cacheDir(), { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => {
|
|
1422
|
-
const metaPath =
|
|
1390
|
+
const metaPath = path10.join(cacheDir(), e.name, ".cache-meta.json");
|
|
1423
1391
|
let cachedAt = "unknown";
|
|
1424
1392
|
if (fs8.existsSync(metaPath)) {
|
|
1425
1393
|
try {
|
|
@@ -1434,8 +1402,8 @@ function listCachedVersions() {
|
|
|
1434
1402
|
function copyDirRecursive(src, dest) {
|
|
1435
1403
|
fs8.mkdirSync(dest, { recursive: true });
|
|
1436
1404
|
for (const entry of fs8.readdirSync(src, { withFileTypes: true })) {
|
|
1437
|
-
const srcPath =
|
|
1438
|
-
const destPath =
|
|
1405
|
+
const srcPath = path10.join(src, entry.name);
|
|
1406
|
+
const destPath = path10.join(dest, entry.name);
|
|
1439
1407
|
if (entry.isDirectory()) {
|
|
1440
1408
|
copyDirRecursive(srcPath, destPath);
|
|
1441
1409
|
} else {
|
|
@@ -1455,7 +1423,7 @@ var BundledContentSource = class {
|
|
|
1455
1423
|
}
|
|
1456
1424
|
async isAvailable() {
|
|
1457
1425
|
const root = getPackageRoot();
|
|
1458
|
-
return fs9.existsSync(
|
|
1426
|
+
return fs9.existsSync(path11.join(root, "skills"));
|
|
1459
1427
|
}
|
|
1460
1428
|
async getVersion() {
|
|
1461
1429
|
return getPackageVersion();
|
|
@@ -1495,7 +1463,7 @@ var GitHubContentSource = class {
|
|
|
1495
1463
|
if (isCached(version)) {
|
|
1496
1464
|
return getCachedContentRoot(version);
|
|
1497
1465
|
}
|
|
1498
|
-
const tmpDir = fs9.mkdtempSync(
|
|
1466
|
+
const tmpDir = fs9.mkdtempSync(path11.join(os3.tmpdir(), "arcane-github-"));
|
|
1499
1467
|
try {
|
|
1500
1468
|
await this.downloadAndExtract(tmpDir);
|
|
1501
1469
|
const contentRoot = storeInCache(version, this.findExtractedRoot(tmpDir));
|
|
@@ -1573,7 +1541,7 @@ var GitHubContentSource = class {
|
|
|
1573
1541
|
throw new Error(`Failed to download tarball: ${response.status}`);
|
|
1574
1542
|
}
|
|
1575
1543
|
const tar = await import("tar");
|
|
1576
|
-
const tarballPath =
|
|
1544
|
+
const tarballPath = path11.join(destDir, "repo.tar.gz");
|
|
1577
1545
|
const fileStream = fs9.createWriteStream(tarballPath);
|
|
1578
1546
|
const reader = response.body.getReader();
|
|
1579
1547
|
const writable = new WritableStream({
|
|
@@ -1604,7 +1572,7 @@ var GitHubContentSource = class {
|
|
|
1604
1572
|
(e) => e.isDirectory() && e.name !== "." && e.name !== ".."
|
|
1605
1573
|
);
|
|
1606
1574
|
if (dirs.length === 1) {
|
|
1607
|
-
return
|
|
1575
|
+
return path11.join(tmpDir, dirs[0].name);
|
|
1608
1576
|
}
|
|
1609
1577
|
return tmpDir;
|
|
1610
1578
|
}
|
|
@@ -1669,10 +1637,10 @@ function findLatestCache() {
|
|
|
1669
1637
|
}
|
|
1670
1638
|
|
|
1671
1639
|
// src/registry.ts
|
|
1672
|
-
import
|
|
1640
|
+
import path12 from "path";
|
|
1673
1641
|
var REGISTRY_VERSION = 1;
|
|
1674
1642
|
function registryPath() {
|
|
1675
|
-
return
|
|
1643
|
+
return path12.join(arcaneHome(), "installations.json");
|
|
1676
1644
|
}
|
|
1677
1645
|
function readRegistryFile() {
|
|
1678
1646
|
const p3 = registryPath();
|
|
@@ -1697,7 +1665,7 @@ function writeRegistryFile(registry) {
|
|
|
1697
1665
|
}
|
|
1698
1666
|
}
|
|
1699
1667
|
function registerInstallation(target) {
|
|
1700
|
-
const abs =
|
|
1668
|
+
const abs = path12.resolve(target);
|
|
1701
1669
|
const registry = readRegistryFile();
|
|
1702
1670
|
if (registry.installations.some((e) => e.path === abs)) {
|
|
1703
1671
|
return;
|
|
@@ -1722,7 +1690,7 @@ var PACKAGE_NAME = "claude-code-arcane";
|
|
|
1722
1690
|
function isArcaneSourceTree(dir) {
|
|
1723
1691
|
try {
|
|
1724
1692
|
const pkg = JSON.parse(
|
|
1725
|
-
fs10.readFileSync(
|
|
1693
|
+
fs10.readFileSync(path13.join(dir, "package.json"), "utf-8")
|
|
1726
1694
|
);
|
|
1727
1695
|
return pkg.name === PACKAGE_NAME;
|
|
1728
1696
|
} catch {
|
|
@@ -1754,8 +1722,8 @@ async function installCommand(profileExpr, opts) {
|
|
|
1754
1722
|
quiet: !profileExpr
|
|
1755
1723
|
});
|
|
1756
1724
|
const root = await source.getContentRoot();
|
|
1757
|
-
const profilesDir =
|
|
1758
|
-
const target =
|
|
1725
|
+
const profilesDir = path13.join(root, "profiles");
|
|
1726
|
+
const target = path13.resolve(opts.target ?? process.cwd());
|
|
1759
1727
|
if (await refusesSelfInstall(target, profileExpr, opts)) return;
|
|
1760
1728
|
if (!profileExpr) {
|
|
1761
1729
|
const profiles = listProfiles(profilesDir);
|
|
@@ -1828,20 +1796,20 @@ Installing profile: ${chalk2.cyan(profileExpr)}`)
|
|
|
1828
1796
|
|
|
1829
1797
|
// src/commands/add.ts
|
|
1830
1798
|
import fs12 from "fs";
|
|
1831
|
-
import
|
|
1799
|
+
import path15 from "path";
|
|
1832
1800
|
import chalk3 from "chalk";
|
|
1833
1801
|
|
|
1834
1802
|
// src/skills-catalog.ts
|
|
1835
1803
|
import fs11 from "fs";
|
|
1836
|
-
import
|
|
1804
|
+
import path14 from "path";
|
|
1837
1805
|
var FRONTMATTER_BYTES = 4096;
|
|
1838
1806
|
function listSkills(skillsDir) {
|
|
1839
1807
|
if (!fs11.existsSync(skillsDir)) return [];
|
|
1840
1808
|
return fs11.readdirSync(skillsDir, { withFileTypes: true }).filter(
|
|
1841
|
-
(d) => d.isDirectory() && !d.name.startsWith("_") && fs11.existsSync(
|
|
1809
|
+
(d) => d.isDirectory() && !d.name.startsWith("_") && fs11.existsSync(path14.join(skillsDir, d.name, "SKILL.md"))
|
|
1842
1810
|
).map((d) => ({
|
|
1843
1811
|
name: d.name,
|
|
1844
|
-
description: readDescription(
|
|
1812
|
+
description: readDescription(path14.join(skillsDir, d.name, "SKILL.md"))
|
|
1845
1813
|
})).sort((a, b) => a.name.localeCompare(b.name));
|
|
1846
1814
|
}
|
|
1847
1815
|
function readDescription(skillFile) {
|
|
@@ -1893,15 +1861,15 @@ async function addCommand(items = []) {
|
|
|
1893
1861
|
return;
|
|
1894
1862
|
}
|
|
1895
1863
|
const selected = await runAddWizard({
|
|
1896
|
-
profiles: listProfiles(
|
|
1897
|
-
skills: listSkills(
|
|
1864
|
+
profiles: listProfiles(path15.join(root, "profiles")),
|
|
1865
|
+
skills: listSkills(path15.join(root, "skills")),
|
|
1898
1866
|
installedProfiles,
|
|
1899
1867
|
installedSkills: manifest.installed_skills
|
|
1900
1868
|
});
|
|
1901
1869
|
if (!selected) return;
|
|
1902
1870
|
items = selected;
|
|
1903
1871
|
}
|
|
1904
|
-
const claudeDir =
|
|
1872
|
+
const claudeDir = path15.join(target, ".claude");
|
|
1905
1873
|
const added = [];
|
|
1906
1874
|
const skipped = [];
|
|
1907
1875
|
const notFound = [];
|
|
@@ -1911,7 +1879,7 @@ async function addCommand(items = []) {
|
|
|
1911
1879
|
for (const item of items) {
|
|
1912
1880
|
if (item.startsWith("+")) {
|
|
1913
1881
|
const requested = item.slice(1);
|
|
1914
|
-
const resolved = resolveProfile(
|
|
1882
|
+
const resolved = resolveProfile(path15.join(root, "profiles"), requested);
|
|
1915
1883
|
if (!resolved) {
|
|
1916
1884
|
console.error(chalk3.red(` Profile '${requested}' not found`));
|
|
1917
1885
|
continue;
|
|
@@ -1926,10 +1894,10 @@ async function addCommand(items = []) {
|
|
|
1926
1894
|
}
|
|
1927
1895
|
for (const rule of profile.rules.universal) {
|
|
1928
1896
|
if (!manifest.installed_rules.includes(rule)) {
|
|
1929
|
-
const src =
|
|
1897
|
+
const src = path15.join(root, "rules", `${rule}.md`);
|
|
1930
1898
|
if (fs12.existsSync(src)) {
|
|
1931
|
-
ensureDir(
|
|
1932
|
-
fs12.copyFileSync(src,
|
|
1899
|
+
ensureDir(path15.join(claudeDir, "rules"));
|
|
1900
|
+
fs12.copyFileSync(src, path15.join(claudeDir, "rules", `${rule}.md`));
|
|
1933
1901
|
manifest.installed_rules.push(rule);
|
|
1934
1902
|
addedRules.push(rule);
|
|
1935
1903
|
}
|
|
@@ -1937,10 +1905,10 @@ async function addCommand(items = []) {
|
|
|
1937
1905
|
}
|
|
1938
1906
|
for (const rule of profile.rules.gamedev) {
|
|
1939
1907
|
if (!manifest.installed_rules.includes(rule)) {
|
|
1940
|
-
const src =
|
|
1908
|
+
const src = path15.join(root, "rules", "gamedev", `${rule}.md`);
|
|
1941
1909
|
if (fs12.existsSync(src)) {
|
|
1942
|
-
ensureDir(
|
|
1943
|
-
fs12.copyFileSync(src,
|
|
1910
|
+
ensureDir(path15.join(claudeDir, "rules"));
|
|
1911
|
+
fs12.copyFileSync(src, path15.join(claudeDir, "rules", `${rule}.md`));
|
|
1944
1912
|
manifest.installed_rules.push(rule);
|
|
1945
1913
|
addedRules.push(rule);
|
|
1946
1914
|
}
|
|
@@ -1956,9 +1924,9 @@ async function addCommand(items = []) {
|
|
|
1956
1924
|
}
|
|
1957
1925
|
}
|
|
1958
1926
|
if (profileName === "statusline") {
|
|
1959
|
-
const statuslineSrc =
|
|
1927
|
+
const statuslineSrc = path15.join(root, "hooks", "statusline.sh");
|
|
1960
1928
|
if (fs12.existsSync(statuslineSrc)) {
|
|
1961
|
-
fs12.copyFileSync(statuslineSrc,
|
|
1929
|
+
fs12.copyFileSync(statuslineSrc, path15.join(claudeDir, "statusline.sh"));
|
|
1962
1930
|
statuslineAdded = true;
|
|
1963
1931
|
}
|
|
1964
1932
|
}
|
|
@@ -2041,10 +2009,10 @@ function persistAdditions(target, claudeDir, manifest, addedItems) {
|
|
|
2041
2009
|
updateManifestFields(target, updates);
|
|
2042
2010
|
}
|
|
2043
2011
|
function printCatalog(root, installedProfiles, installedSkills) {
|
|
2044
|
-
const profiles = listProfiles(
|
|
2012
|
+
const profiles = listProfiles(path15.join(root, "profiles")).filter(
|
|
2045
2013
|
(pr) => !installedProfiles.includes(pr.name)
|
|
2046
2014
|
);
|
|
2047
|
-
const skills = listSkills(
|
|
2015
|
+
const skills = listSkills(path15.join(root, "skills")).filter(
|
|
2048
2016
|
(sk) => !installedSkills.includes(sk.name)
|
|
2049
2017
|
);
|
|
2050
2018
|
if (profiles.length === 0 && skills.length === 0) {
|
|
@@ -2070,14 +2038,14 @@ function printCatalog(root, installedProfiles, installedSkills) {
|
|
|
2070
2038
|
}
|
|
2071
2039
|
function addSkill(root, target, skill, installed) {
|
|
2072
2040
|
if (installed.includes(skill)) return "skipped";
|
|
2073
|
-
const src =
|
|
2041
|
+
const src = path15.join(root, "skills", skill);
|
|
2074
2042
|
if (!fs12.existsSync(src)) return "not-found";
|
|
2075
|
-
const dst =
|
|
2043
|
+
const dst = path15.join(target, ".claude", "skills", skill);
|
|
2076
2044
|
copyDirSync(src, dst);
|
|
2077
2045
|
return "added";
|
|
2078
2046
|
}
|
|
2079
2047
|
function mergePermissions(claudeDir, newPerms) {
|
|
2080
|
-
const settingsPath =
|
|
2048
|
+
const settingsPath = path15.join(claudeDir, "settings.json");
|
|
2081
2049
|
if (!fs12.existsSync(settingsPath)) return;
|
|
2082
2050
|
const settings = readJsonSync(settingsPath);
|
|
2083
2051
|
const perms = settings.permissions ?? { allow: [], deny: [] };
|
|
@@ -2091,7 +2059,7 @@ function mergePermissions(claudeDir, newPerms) {
|
|
|
2091
2059
|
writeJsonSync(settingsPath, settings);
|
|
2092
2060
|
}
|
|
2093
2061
|
function addStatuslineToSettings(target, claudeDir) {
|
|
2094
|
-
const settingsPath =
|
|
2062
|
+
const settingsPath = path15.join(claudeDir, "settings.json");
|
|
2095
2063
|
if (!fs12.existsSync(settingsPath)) return;
|
|
2096
2064
|
const settings = readJsonSync(settingsPath);
|
|
2097
2065
|
if (settings.statusLine) return;
|
|
@@ -2104,7 +2072,7 @@ function addStatuslineToSettings(target, claudeDir) {
|
|
|
2104
2072
|
|
|
2105
2073
|
// src/commands/remove.ts
|
|
2106
2074
|
import fs13 from "fs";
|
|
2107
|
-
import
|
|
2075
|
+
import path16 from "path";
|
|
2108
2076
|
import chalk4 from "chalk";
|
|
2109
2077
|
var CORE_SKILLS = [
|
|
2110
2078
|
"commit",
|
|
@@ -2169,7 +2137,7 @@ async function removeCommand(items, opts = {}) {
|
|
|
2169
2137
|
}
|
|
2170
2138
|
manifest.total_skills = manifest.installed_skills.length;
|
|
2171
2139
|
manifest.total_rules = manifest.installed_rules.length;
|
|
2172
|
-
const mp =
|
|
2140
|
+
const mp = path16.join(target, ".claude", "arcane-manifest.json");
|
|
2173
2141
|
writeJsonSync(mp, manifest);
|
|
2174
2142
|
const totalRemoved = removedSkills.length + removedAgents.length + removedProfiles.length;
|
|
2175
2143
|
console.log(chalk4.bold(`
|
|
@@ -2190,7 +2158,7 @@ function removeSkill(target, skill, manifest) {
|
|
|
2190
2158
|
);
|
|
2191
2159
|
return "skipped";
|
|
2192
2160
|
}
|
|
2193
|
-
const skillDir =
|
|
2161
|
+
const skillDir = path16.join(target, ".claude", "skills", skill);
|
|
2194
2162
|
if (!fs13.existsSync(skillDir)) return "skipped";
|
|
2195
2163
|
fs13.rmSync(skillDir, { recursive: true, force: true });
|
|
2196
2164
|
manifest.installed_skills = manifest.installed_skills.filter(
|
|
@@ -2212,7 +2180,7 @@ function removeProfile(root, target, profileName, manifest) {
|
|
|
2212
2180
|
);
|
|
2213
2181
|
return { removed: false, skills: [], agents: [] };
|
|
2214
2182
|
}
|
|
2215
|
-
const resolved = resolveProfile(
|
|
2183
|
+
const resolved = resolveProfile(path16.join(root, "profiles"), installedName);
|
|
2216
2184
|
if (!resolved) {
|
|
2217
2185
|
console.warn(
|
|
2218
2186
|
chalk4.yellow(` WARNING: Profile '${installedName}.yaml' not found.`)
|
|
@@ -2227,7 +2195,7 @@ function removeProfile(root, target, profileName, manifest) {
|
|
|
2227
2195
|
const sharedAgents = /* @__PURE__ */ new Set();
|
|
2228
2196
|
const sharedRules = /* @__PURE__ */ new Set();
|
|
2229
2197
|
for (const rp of remainingProfiles) {
|
|
2230
|
-
const rpResolved = resolveProfile(
|
|
2198
|
+
const rpResolved = resolveProfile(path16.join(root, "profiles"), rp);
|
|
2231
2199
|
if (!rpResolved) continue;
|
|
2232
2200
|
const rpDef = parseProfile(rpResolved.file);
|
|
2233
2201
|
rpDef.skills.forEach((s) => sharedSkills.add(s));
|
|
@@ -2239,7 +2207,7 @@ function removeProfile(root, target, profileName, manifest) {
|
|
|
2239
2207
|
for (const skill of profile.skills) {
|
|
2240
2208
|
if (sharedSkills.has(skill)) continue;
|
|
2241
2209
|
if (CORE_SKILLS.includes(skill)) continue;
|
|
2242
|
-
const skillDir =
|
|
2210
|
+
const skillDir = path16.join(target, ".claude", "skills", skill);
|
|
2243
2211
|
if (fs13.existsSync(skillDir)) {
|
|
2244
2212
|
fs13.rmSync(skillDir, { recursive: true, force: true });
|
|
2245
2213
|
removedSkills.push(skill);
|
|
@@ -2249,7 +2217,7 @@ function removeProfile(root, target, profileName, manifest) {
|
|
|
2249
2217
|
);
|
|
2250
2218
|
}
|
|
2251
2219
|
const removedAgents = [];
|
|
2252
|
-
const claudeDir =
|
|
2220
|
+
const claudeDir = path16.join(target, ".claude");
|
|
2253
2221
|
for (const entry of profile.agents) {
|
|
2254
2222
|
if (sharedAgents.has(entry)) continue;
|
|
2255
2223
|
const { division } = parseAgentEntry(entry);
|
|
@@ -2264,7 +2232,7 @@ function removeProfile(root, target, profileName, manifest) {
|
|
|
2264
2232
|
...profile.rules.gamedev
|
|
2265
2233
|
]) {
|
|
2266
2234
|
if (sharedRules.has(rule)) continue;
|
|
2267
|
-
const ruleFile =
|
|
2235
|
+
const ruleFile = path16.join(target, ".claude", "rules", `${rule}.md`);
|
|
2268
2236
|
if (fs13.existsSync(ruleFile)) {
|
|
2269
2237
|
fs13.rmSync(ruleFile);
|
|
2270
2238
|
}
|
|
@@ -2273,18 +2241,18 @@ function removeProfile(root, target, profileName, manifest) {
|
|
|
2273
2241
|
);
|
|
2274
2242
|
}
|
|
2275
2243
|
if (profileName === "statusline") {
|
|
2276
|
-
const statuslineFile =
|
|
2244
|
+
const statuslineFile = path16.join(target, ".claude", "statusline.sh");
|
|
2277
2245
|
if (fs13.existsSync(statuslineFile)) {
|
|
2278
2246
|
fs13.rmSync(statuslineFile);
|
|
2279
2247
|
}
|
|
2280
|
-
removeStatuslineFromSettings(
|
|
2248
|
+
removeStatuslineFromSettings(path16.join(target, ".claude"));
|
|
2281
2249
|
}
|
|
2282
2250
|
manifest.profiles = remainingProfiles;
|
|
2283
2251
|
manifest.profile_command = remainingProfiles.filter((p3) => p3 !== "core").join("+");
|
|
2284
2252
|
return { removed: true, skills: removedSkills, agents: removedAgents };
|
|
2285
2253
|
}
|
|
2286
2254
|
function removeStatuslineFromSettings(claudeDir) {
|
|
2287
|
-
const settingsPath =
|
|
2255
|
+
const settingsPath = path16.join(claudeDir, "settings.json");
|
|
2288
2256
|
if (!fs13.existsSync(settingsPath)) return;
|
|
2289
2257
|
const settings = JSON.parse(fs13.readFileSync(settingsPath, "utf-8"));
|
|
2290
2258
|
if (settings.statusLine) {
|
|
@@ -2295,13 +2263,13 @@ function removeStatuslineFromSettings(claudeDir) {
|
|
|
2295
2263
|
|
|
2296
2264
|
// src/commands/list.ts
|
|
2297
2265
|
import fs14 from "fs";
|
|
2298
|
-
import
|
|
2266
|
+
import path17 from "path";
|
|
2299
2267
|
import chalk5 from "chalk";
|
|
2300
2268
|
async function listCommand() {
|
|
2301
2269
|
const source = await resolveContentSource({ quiet: true });
|
|
2302
2270
|
const root = await source.getContentRoot();
|
|
2303
|
-
const profilesDir =
|
|
2304
|
-
const skillsDir =
|
|
2271
|
+
const profilesDir = path17.join(root, "profiles");
|
|
2272
|
+
const skillsDir = path17.join(root, "skills");
|
|
2305
2273
|
const target = process.cwd();
|
|
2306
2274
|
const manifest = readManifest(target);
|
|
2307
2275
|
const profiles = listProfiles(profilesDir);
|
|
@@ -2386,8 +2354,8 @@ async function statusCommand() {
|
|
|
2386
2354
|
|
|
2387
2355
|
// src/commands/update.ts
|
|
2388
2356
|
import fs15 from "fs";
|
|
2389
|
-
import
|
|
2390
|
-
import
|
|
2357
|
+
import path18 from "path";
|
|
2358
|
+
import os4 from "os";
|
|
2391
2359
|
import chalk7 from "chalk";
|
|
2392
2360
|
|
|
2393
2361
|
// src/self-update.ts
|
|
@@ -2470,7 +2438,7 @@ async function updateCommand(opts) {
|
|
|
2470
2438
|
registerInstallation(cwd);
|
|
2471
2439
|
}
|
|
2472
2440
|
const targets = pruneRegistry().map((e) => e.path);
|
|
2473
|
-
const globalTarget =
|
|
2441
|
+
const globalTarget = os4.homedir();
|
|
2474
2442
|
if (!process.env.VITEST && fs15.existsSync(manifestPath(globalTarget)) && !targets.includes(globalTarget)) {
|
|
2475
2443
|
targets.push(globalTarget);
|
|
2476
2444
|
}
|
|
@@ -2540,11 +2508,11 @@ async function updateTarget(target, opts, sharedSource) {
|
|
|
2540
2508
|
const currentVersion = await contentSource.getVersion();
|
|
2541
2509
|
const installedVersion = manifest.source_version ?? manifest.arcane_version;
|
|
2542
2510
|
const profileNamesForSync = manifest.profile_command.split("+").filter(Boolean);
|
|
2543
|
-
const claudeDirForSync =
|
|
2511
|
+
const claudeDirForSync = path18.join(target, ".claude");
|
|
2544
2512
|
const earlySync = syncUnhashedFiles(
|
|
2545
2513
|
root,
|
|
2546
2514
|
claudeDirForSync,
|
|
2547
|
-
mergeProfiles(
|
|
2515
|
+
mergeProfiles(path18.join(root, "profiles"), profileNamesForSync),
|
|
2548
2516
|
opts.dryRun ?? false
|
|
2549
2517
|
);
|
|
2550
2518
|
if (earlySync.length > 0 && !opts.quiet) {
|
|
@@ -2578,13 +2546,13 @@ Update available: ${chalk7.red(installedVersion)} -> ${chalk7.green(currentVersi
|
|
|
2578
2546
|
);
|
|
2579
2547
|
}
|
|
2580
2548
|
const profileNames = manifest.profile_command.split("+").filter(Boolean);
|
|
2581
|
-
const profilesDir =
|
|
2549
|
+
const profilesDir = path18.join(root, "profiles");
|
|
2582
2550
|
const merged = mergeProfiles(profilesDir, profileNames);
|
|
2583
2551
|
const allRules = [...merged.rules.universal, ...merged.rules.gamedev];
|
|
2584
2552
|
const sourceHashes = computeSourceHashes(root, merged.skills, allRules, merged.agents);
|
|
2585
|
-
const claudeDir =
|
|
2553
|
+
const claudeDir = path18.join(target, ".claude");
|
|
2586
2554
|
const installedHashes = computeContentHashes(claudeDir);
|
|
2587
|
-
if (isSymlink(
|
|
2555
|
+
if (isSymlink(path18.join(claudeDir, "docs"))) {
|
|
2588
2556
|
sourceHashes.docs = {};
|
|
2589
2557
|
installedHashes.docs = {};
|
|
2590
2558
|
}
|
|
@@ -2877,18 +2845,18 @@ function applyUpdates(items, root, claudeDir, merged, force) {
|
|
|
2877
2845
|
}
|
|
2878
2846
|
function syncUnhashedFiles(root, claudeDir, merged, dryRun) {
|
|
2879
2847
|
const targets = [];
|
|
2880
|
-
const statuslineDst =
|
|
2848
|
+
const statuslineDst = path18.join(claudeDir, "statusline.sh");
|
|
2881
2849
|
if (merged.loaded.includes("statusline") || fs15.existsSync(statuslineDst)) {
|
|
2882
2850
|
targets.push({
|
|
2883
|
-
src:
|
|
2851
|
+
src: path18.join(root, "hooks", "statusline.sh"),
|
|
2884
2852
|
dst: statuslineDst,
|
|
2885
2853
|
label: "statusline.sh"
|
|
2886
2854
|
});
|
|
2887
2855
|
}
|
|
2888
2856
|
for (const style of merged.output_styles) {
|
|
2889
2857
|
targets.push({
|
|
2890
|
-
src:
|
|
2891
|
-
dst:
|
|
2858
|
+
src: path18.join(root, "output-styles", `${style}.md`),
|
|
2859
|
+
dst: path18.join(claudeDir, "output-styles", `${style}.md`),
|
|
2892
2860
|
label: `output-styles/${style}.md`
|
|
2893
2861
|
});
|
|
2894
2862
|
}
|
|
@@ -2896,21 +2864,21 @@ function syncUnhashedFiles(root, claudeDir, merged, dryRun) {
|
|
|
2896
2864
|
const { division, agent } = parseAgentEntry(raw);
|
|
2897
2865
|
if (!agent) continue;
|
|
2898
2866
|
targets.push({
|
|
2899
|
-
src:
|
|
2900
|
-
dst:
|
|
2867
|
+
src: path18.join(root, "agents", division, `${agent}.md`),
|
|
2868
|
+
dst: path18.join(claudeDir, "agents", division, `${agent}.md`),
|
|
2901
2869
|
label: `agents/${division}/${agent}.md`
|
|
2902
2870
|
});
|
|
2903
2871
|
}
|
|
2904
2872
|
if (merged.rules.gamedev.length > 0) {
|
|
2905
|
-
const templatesSrc =
|
|
2873
|
+
const templatesSrc = path18.join(root, "templates", "gamedev");
|
|
2906
2874
|
if (fs15.existsSync(templatesSrc)) {
|
|
2907
2875
|
for (const rel of fs15.readdirSync(templatesSrc, { recursive: true })) {
|
|
2908
|
-
const src =
|
|
2876
|
+
const src = path18.join(templatesSrc, rel);
|
|
2909
2877
|
if (!fs15.statSync(src).isFile()) continue;
|
|
2910
2878
|
targets.push({
|
|
2911
2879
|
src,
|
|
2912
|
-
dst:
|
|
2913
|
-
label: `skills/_templates/${String(rel).split(
|
|
2880
|
+
dst: path18.join(claudeDir, "skills", "_templates", rel),
|
|
2881
|
+
label: `skills/_templates/${String(rel).split(path18.sep).join("/")}`
|
|
2914
2882
|
});
|
|
2915
2883
|
}
|
|
2916
2884
|
}
|
|
@@ -2927,10 +2895,10 @@ function syncUnhashedFiles(root, claudeDir, merged, dryRun) {
|
|
|
2927
2895
|
if (fs15.existsSync(bak)) fs15.rmSync(bak, { force: true });
|
|
2928
2896
|
fs15.renameSync(t.dst, bak);
|
|
2929
2897
|
}
|
|
2930
|
-
fs15.mkdirSync(
|
|
2898
|
+
fs15.mkdirSync(path18.dirname(t.dst), { recursive: true });
|
|
2931
2899
|
fs15.copyFileSync(t.src, t.dst);
|
|
2932
2900
|
}
|
|
2933
|
-
const settingsPath =
|
|
2901
|
+
const settingsPath = path18.join(claudeDir, "settings.json");
|
|
2934
2902
|
if (fs15.existsSync(settingsPath)) {
|
|
2935
2903
|
try {
|
|
2936
2904
|
const settings = JSON.parse(fs15.readFileSync(settingsPath, "utf-8"));
|
|
@@ -2941,13 +2909,20 @@ function syncUnhashedFiles(root, claudeDir, merged, dryRun) {
|
|
|
2941
2909
|
settings.outputStyle = activeStyle;
|
|
2942
2910
|
dirty = true;
|
|
2943
2911
|
}
|
|
2944
|
-
const wanted = statuslineCommand(
|
|
2912
|
+
const wanted = statuslineCommand(path18.dirname(claudeDir));
|
|
2945
2913
|
const current = settings.statusLine;
|
|
2946
2914
|
if (wanted !== RELATIVE_STATUSLINE_COMMAND && current && current.command !== wanted) {
|
|
2947
2915
|
changed.push("settings.json (statusLine path)");
|
|
2948
2916
|
settings.statusLine = { ...current, command: wanted };
|
|
2949
2917
|
dirty = true;
|
|
2950
2918
|
}
|
|
2919
|
+
if (settings.hooks && typeof settings.hooks === "object") {
|
|
2920
|
+
const healed = healHooks(settings.hooks, path18.dirname(claudeDir));
|
|
2921
|
+
if (healed.length > 0) {
|
|
2922
|
+
changed.push(`settings.json (${healed.join(", ")})`);
|
|
2923
|
+
dirty = true;
|
|
2924
|
+
}
|
|
2925
|
+
}
|
|
2951
2926
|
if (dirty && !dryRun) {
|
|
2952
2927
|
fs15.writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}
|
|
2953
2928
|
`, "utf-8");
|
|
@@ -2967,7 +2942,7 @@ function applyItem(item, root, claudeDir) {
|
|
|
2967
2942
|
}
|
|
2968
2943
|
copyDirSync(src, dst);
|
|
2969
2944
|
} else {
|
|
2970
|
-
const dir =
|
|
2945
|
+
const dir = path18.dirname(dst);
|
|
2971
2946
|
if (!fs15.existsSync(dir)) {
|
|
2972
2947
|
fs15.mkdirSync(dir, { recursive: true });
|
|
2973
2948
|
}
|
|
@@ -2977,8 +2952,8 @@ function applyItem(item, root, claudeDir) {
|
|
|
2977
2952
|
function quarantineItem(item, claudeDir) {
|
|
2978
2953
|
const installed = getInstalledPath(item, claudeDir);
|
|
2979
2954
|
if (!fs15.existsSync(installed)) return;
|
|
2980
|
-
const quarantine =
|
|
2981
|
-
ensureDir(
|
|
2955
|
+
const quarantine = path18.join(claudeDir, TRASH_DIR, `${item.type}s`, item.name);
|
|
2956
|
+
ensureDir(path18.dirname(quarantine));
|
|
2982
2957
|
if (fs15.existsSync(quarantine)) {
|
|
2983
2958
|
fs15.rmSync(quarantine, { recursive: true, force: true });
|
|
2984
2959
|
}
|
|
@@ -2987,18 +2962,18 @@ function quarantineItem(item, claudeDir) {
|
|
|
2987
2962
|
function getSourcePath(item, root) {
|
|
2988
2963
|
switch (item.type) {
|
|
2989
2964
|
case "skill":
|
|
2990
|
-
return
|
|
2965
|
+
return path18.join(root, "skills", item.name);
|
|
2991
2966
|
case "rule": {
|
|
2992
|
-
const direct =
|
|
2967
|
+
const direct = path18.join(root, "rules", item.name);
|
|
2993
2968
|
if (fs15.existsSync(direct)) return direct;
|
|
2994
|
-
return
|
|
2969
|
+
return path18.join(root, "rules", "gamedev", item.name);
|
|
2995
2970
|
}
|
|
2996
2971
|
case "agent":
|
|
2997
|
-
return
|
|
2972
|
+
return path18.join(root, "agents", item.name);
|
|
2998
2973
|
case "hook":
|
|
2999
|
-
return
|
|
2974
|
+
return path18.join(root, "hooks", item.name);
|
|
3000
2975
|
case "doc":
|
|
3001
|
-
return
|
|
2976
|
+
return path18.join(root, "docs", item.name);
|
|
3002
2977
|
default:
|
|
3003
2978
|
return null;
|
|
3004
2979
|
}
|
|
@@ -3006,15 +2981,15 @@ function getSourcePath(item, root) {
|
|
|
3006
2981
|
function getInstalledPath(item, claudeDir) {
|
|
3007
2982
|
switch (item.type) {
|
|
3008
2983
|
case "skill":
|
|
3009
|
-
return
|
|
2984
|
+
return path18.join(claudeDir, "skills", item.name);
|
|
3010
2985
|
case "rule":
|
|
3011
|
-
return
|
|
2986
|
+
return path18.join(claudeDir, "rules", item.name);
|
|
3012
2987
|
case "agent":
|
|
3013
|
-
return
|
|
2988
|
+
return path18.join(claudeDir, "agents", item.name);
|
|
3014
2989
|
case "hook":
|
|
3015
|
-
return
|
|
2990
|
+
return path18.join(claudeDir, "hooks", item.name);
|
|
3016
2991
|
case "doc":
|
|
3017
|
-
return
|
|
2992
|
+
return path18.join(claudeDir, "docs", item.name);
|
|
3018
2993
|
}
|
|
3019
2994
|
}
|
|
3020
2995
|
function isSymlink(p3) {
|
|
@@ -3057,7 +3032,7 @@ async function cleanCommand(opts) {
|
|
|
3057
3032
|
|
|
3058
3033
|
// src/commands/worktree.ts
|
|
3059
3034
|
import fs16 from "fs";
|
|
3060
|
-
import
|
|
3035
|
+
import path19 from "path";
|
|
3061
3036
|
import { execSync as execSync2 } from "child_process";
|
|
3062
3037
|
import chalk9 from "chalk";
|
|
3063
3038
|
async function worktreeCommand(branch, opts) {
|
|
@@ -3068,7 +3043,7 @@ async function worktreeCommand(branch, opts) {
|
|
|
3068
3043
|
process.exit(1);
|
|
3069
3044
|
}
|
|
3070
3045
|
const repoRoot = wtInfo.isWorktree ? wtInfo.mainWorktreePath : cwd;
|
|
3071
|
-
const worktreePath = opts.path ?
|
|
3046
|
+
const worktreePath = opts.path ? path19.resolve(opts.path) : defaultWorktreePath(repoRoot, branch);
|
|
3072
3047
|
if (fs16.existsSync(worktreePath)) {
|
|
3073
3048
|
console.error(
|
|
3074
3049
|
chalk9.red(`Path already exists: ${worktreePath}`)
|
|
@@ -3116,7 +3091,7 @@ async function worktreeCommand(branch, opts) {
|
|
|
3116
3091
|
}
|
|
3117
3092
|
console.log(" Installing Arcane...");
|
|
3118
3093
|
const root = getPackageRoot();
|
|
3119
|
-
const profilesDir =
|
|
3094
|
+
const profilesDir = path19.join(root, "profiles");
|
|
3120
3095
|
const profileNames = profileExpr.split("+").filter(Boolean);
|
|
3121
3096
|
const merged = mergeProfiles(profilesDir, profileNames);
|
|
3122
3097
|
const shareFrom = opts.share !== false ? cwd : void 0;
|
|
@@ -3152,7 +3127,7 @@ async function worktreeCommand(branch, opts) {
|
|
|
3152
3127
|
}
|
|
3153
3128
|
}
|
|
3154
3129
|
if (opts.isolate) {
|
|
3155
|
-
const isolateScript =
|
|
3130
|
+
const isolateScript = path19.join(
|
|
3156
3131
|
worktreePath,
|
|
3157
3132
|
".claude",
|
|
3158
3133
|
"skills",
|
|
@@ -3194,12 +3169,12 @@ async function worktreeCommand(branch, opts) {
|
|
|
3194
3169
|
|
|
3195
3170
|
// src/commands/global.ts
|
|
3196
3171
|
import fs17 from "fs";
|
|
3197
|
-
import
|
|
3198
|
-
import
|
|
3172
|
+
import path20 from "path";
|
|
3173
|
+
import os5 from "os";
|
|
3199
3174
|
import chalk10 from "chalk";
|
|
3200
|
-
var CLAUDE_HOME =
|
|
3201
|
-
var SCRIPTS_DIR =
|
|
3202
|
-
var SETTINGS_PATH =
|
|
3175
|
+
var CLAUDE_HOME = path20.join(os5.homedir(), ".claude");
|
|
3176
|
+
var SCRIPTS_DIR = path20.join(CLAUDE_HOME, "scripts", "worktree-isolation");
|
|
3177
|
+
var SETTINGS_PATH = path20.join(CLAUDE_HOME, "settings.json");
|
|
3203
3178
|
async function globalCommand(opts) {
|
|
3204
3179
|
if (opts.status) {
|
|
3205
3180
|
showStatus();
|
|
@@ -3229,7 +3204,7 @@ function settingsParseable() {
|
|
|
3229
3204
|
function showStatus() {
|
|
3230
3205
|
console.log(chalk10.bold("\n=== Arcane Global Status ===\n"));
|
|
3231
3206
|
const hasScripts = fs17.existsSync(
|
|
3232
|
-
|
|
3207
|
+
path20.join(SCRIPTS_DIR, "apply.py")
|
|
3233
3208
|
);
|
|
3234
3209
|
console.log(
|
|
3235
3210
|
` Worktree scripts: ${hasScripts ? chalk10.green("installed") : chalk10.dim("not installed")}`
|
|
@@ -3247,7 +3222,7 @@ function showStatus() {
|
|
|
3247
3222
|
function installGlobal() {
|
|
3248
3223
|
console.log(chalk10.bold("\nInstalling Arcane global hooks...\n"));
|
|
3249
3224
|
const root = getPackageRoot();
|
|
3250
|
-
const scriptsSrc =
|
|
3225
|
+
const scriptsSrc = path20.join(
|
|
3251
3226
|
root,
|
|
3252
3227
|
"skills",
|
|
3253
3228
|
"worktree-isolation",
|
|
@@ -3261,8 +3236,8 @@ function installGlobal() {
|
|
|
3261
3236
|
}
|
|
3262
3237
|
fs17.mkdirSync(SCRIPTS_DIR, { recursive: true });
|
|
3263
3238
|
for (const file of ["apply.py", "lib.py"]) {
|
|
3264
|
-
const src =
|
|
3265
|
-
const dst =
|
|
3239
|
+
const src = path20.join(scriptsSrc, file);
|
|
3240
|
+
const dst = path20.join(SCRIPTS_DIR, file);
|
|
3266
3241
|
if (!fs17.existsSync(src)) {
|
|
3267
3242
|
console.warn(chalk10.yellow(` WARN: ${file} not found, skipping`));
|
|
3268
3243
|
continue;
|
|
@@ -3319,7 +3294,7 @@ function readSettings() {
|
|
|
3319
3294
|
}
|
|
3320
3295
|
}
|
|
3321
3296
|
function writeSettings(settings) {
|
|
3322
|
-
fs17.mkdirSync(
|
|
3297
|
+
fs17.mkdirSync(path20.dirname(SETTINGS_PATH), { recursive: true });
|
|
3323
3298
|
fs17.writeFileSync(
|
|
3324
3299
|
SETTINGS_PATH,
|
|
3325
3300
|
JSON.stringify(settings, null, 2) + "\n",
|
|
@@ -3331,8 +3306,8 @@ function isHookInstalled() {
|
|
|
3331
3306
|
const hooks = settings.hooks ?? {};
|
|
3332
3307
|
const sessionStart = hooks.SessionStart ?? [];
|
|
3333
3308
|
for (const entry of sessionStart) {
|
|
3334
|
-
for (const
|
|
3335
|
-
if (
|
|
3309
|
+
for (const hook2 of entry.hooks ?? []) {
|
|
3310
|
+
if (hook2.command?.includes("worktree-isolation/apply.py") && hook2.command?.includes("--quiet")) {
|
|
3336
3311
|
return true;
|
|
3337
3312
|
}
|
|
3338
3313
|
}
|
|
@@ -3378,10 +3353,10 @@ function removeHookFromSettings() {
|
|
|
3378
3353
|
|
|
3379
3354
|
// src/update-check.ts
|
|
3380
3355
|
import fs18 from "fs";
|
|
3381
|
-
import
|
|
3356
|
+
import path21 from "path";
|
|
3382
3357
|
import chalk11 from "chalk";
|
|
3383
3358
|
function checkFile() {
|
|
3384
|
-
return
|
|
3359
|
+
return path21.join(arcaneHome(), "last-check.json");
|
|
3385
3360
|
}
|
|
3386
3361
|
var CHECK_INTERVAL_MS = 4 * 60 * 60 * 1e3;
|
|
3387
3362
|
var GITHUB_OWNER2 = "SebastianLuser";
|
|
@@ -3519,7 +3494,7 @@ function readCachedCheck() {
|
|
|
3519
3494
|
function writeCachedCheck(result) {
|
|
3520
3495
|
try {
|
|
3521
3496
|
const file = checkFile();
|
|
3522
|
-
const dir =
|
|
3497
|
+
const dir = path21.dirname(file);
|
|
3523
3498
|
if (!fs18.existsSync(dir)) {
|
|
3524
3499
|
fs18.mkdirSync(dir, { recursive: true });
|
|
3525
3500
|
}
|