claude-code-arcane 2.3.0 → 2.5.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.
Files changed (141) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +131 -41
  3. package/agents/game/ue-blueprint-specialist.md +125 -0
  4. package/agents/game/ue-gas-specialist.md +133 -0
  5. package/agents/game/ue-umg-specialist.md +131 -0
  6. package/dist/cli.js +642 -478
  7. package/docs/SKILLS-CATALOG.md +42 -3
  8. package/docs/USER-GUIDE.md +28 -54
  9. package/hooks/detect-unreal.sh +90 -0
  10. package/package.json +2 -1
  11. package/profiles/agile.yaml +1 -1
  12. package/profiles/ai.yaml +1 -1
  13. package/profiles/android-native.yaml +1 -1
  14. package/profiles/audio.yaml +1 -1
  15. package/profiles/backend-dotnet.yaml +1 -1
  16. package/profiles/backend-go.yaml +1 -1
  17. package/profiles/backend-nestjs.yaml +1 -1
  18. package/profiles/backend-nextjs.yaml +1 -1
  19. package/profiles/backend-ts.yaml +1 -1
  20. package/profiles/business.yaml +1 -1
  21. package/profiles/clevel.yaml +1 -1
  22. package/profiles/clickup.yaml +1 -1
  23. package/profiles/core.yaml +1 -1
  24. package/profiles/database.yaml +1 -1
  25. package/profiles/design.yaml +1 -1
  26. package/profiles/docs.yaml +1 -1
  27. package/profiles/ecommerce.yaml +1 -1
  28. package/profiles/finance.yaml +1 -1
  29. package/profiles/flutter.yaml +1 -1
  30. package/profiles/freelance.yaml +1 -1
  31. package/profiles/frontend.yaml +1 -1
  32. package/profiles/infra.yaml +1 -1
  33. package/profiles/integrations.yaml +1 -1
  34. package/profiles/ios-native.yaml +1 -1
  35. package/profiles/jira.yaml +1 -1
  36. package/profiles/job-hunt.yaml +1 -1
  37. package/profiles/marketing.yaml +1 -1
  38. package/profiles/mobile.yaml +1 -1
  39. package/profiles/regulatory.yaml +1 -1
  40. package/profiles/second-brain.yaml +1 -1
  41. package/profiles/security.yaml +1 -1
  42. package/profiles/self-improving.yaml +1 -1
  43. package/profiles/statusline.yaml +1 -1
  44. package/profiles/testing.yaml +1 -1
  45. package/profiles/unity-design.yaml +1 -1
  46. package/profiles/unity-dev.yaml +1 -1
  47. package/profiles/unreal-dev.yaml +97 -0
  48. package/profiles/visual-novel.yaml +1 -1
  49. package/rules/gamedev/unreal-code.md +125 -0
  50. package/skills/arcane-list/SKILL.md +12 -7
  51. package/skills/install-mcp/SKILL.md +135 -46
  52. package/skills/install-mcp/references/unreal-operations.md +55 -0
  53. package/skills/ue-actor-component-architecture/SKILL.md +509 -0
  54. package/skills/ue-actor-component-architecture/references/actor-lifecycle.md +301 -0
  55. package/skills/ue-actor-component-architecture/references/component-types.md +592 -0
  56. package/skills/ue-ai-navigation/SKILL.md +526 -0
  57. package/skills/ue-ai-navigation/references/behavior-tree-patterns.md +392 -0
  58. package/skills/ue-ai-navigation/references/behavior-tree-workflow.md +107 -0
  59. package/skills/ue-ai-navigation/references/custom-bttask.md +104 -0
  60. package/skills/ue-ai-navigation/references/eqs-reference.md +423 -0
  61. package/skills/ue-animation-system/SKILL.md +521 -0
  62. package/skills/ue-animation-system/references/anim-notify-reference.md +486 -0
  63. package/skills/ue-animation-system/references/locomotion-setup.md +550 -0
  64. package/skills/ue-async-threading/SKILL.md +433 -0
  65. package/skills/ue-async-threading/references/thread-safety-guide.md +213 -0
  66. package/skills/ue-async-threading/references/threading-patterns.md +345 -0
  67. package/skills/ue-audio-system/SKILL.md +527 -0
  68. package/skills/ue-audio-system/references/audio-setup-patterns.md +700 -0
  69. package/skills/ue-blueprints/SKILL.md +134 -0
  70. package/skills/ue-blueprints/references/communication.md +82 -0
  71. package/skills/ue-character-movement/SKILL.md +475 -0
  72. package/skills/ue-character-movement/references/cmc-extension-patterns.md +335 -0
  73. package/skills/ue-character-movement/references/movement-pipeline.md +189 -0
  74. package/skills/ue-cpp-foundations/SKILL.md +533 -0
  75. package/skills/ue-cpp-foundations/references/components-and-gc.md +107 -0
  76. package/skills/ue-cpp-foundations/references/container-patterns.md +544 -0
  77. package/skills/ue-cpp-foundations/references/delegate-patterns.md +497 -0
  78. package/skills/ue-cpp-foundations/references/gameplay-classes-cpp.md +136 -0
  79. package/skills/ue-cpp-foundations/references/property-specifiers.md +306 -0
  80. package/skills/ue-data-assets-tables/SKILL.md +527 -0
  81. package/skills/ue-data-assets-tables/references/asset-loading-patterns.md +306 -0
  82. package/skills/ue-data-assets-tables/references/data-driven-design.md +378 -0
  83. package/skills/ue-editor-tools/SKILL.md +523 -0
  84. package/skills/ue-editor-tools/references/detail-customization-patterns.md +395 -0
  85. package/skills/ue-editor-tools/references/editor-module-setup.md +480 -0
  86. package/skills/ue-game-features/SKILL.md +477 -0
  87. package/skills/ue-game-features/references/experience-system.md +222 -0
  88. package/skills/ue-game-features/references/game-feature-patterns.md +308 -0
  89. package/skills/ue-gameplay-abilities/SKILL.md +518 -0
  90. package/skills/ue-gameplay-abilities/references/ability-task-reference.md +391 -0
  91. package/skills/ue-gameplay-abilities/references/gameplay-effect-reference.md +397 -0
  92. package/skills/ue-gameplay-abilities/references/gas-setup-patterns.md +369 -0
  93. package/skills/ue-gameplay-framework/SKILL.md +466 -0
  94. package/skills/ue-gameplay-framework/references/framework-class-map.md +243 -0
  95. package/skills/ue-input-system/SKILL.md +394 -0
  96. package/skills/ue-input-system/references/cpp-setup.md +131 -0
  97. package/skills/ue-input-system/references/enhanced-input-workflow.md +117 -0
  98. package/skills/ue-input-system/references/input-action-reference.md +731 -0
  99. package/skills/ue-mass-entity/SKILL.md +476 -0
  100. package/skills/ue-mass-entity/references/mass-entity-patterns.md +319 -0
  101. package/skills/ue-mass-entity/references/mass-fragment-reference.md +119 -0
  102. package/skills/ue-materials-rendering/SKILL.md +526 -0
  103. package/skills/ue-materials-rendering/references/material-parameter-reference.md +216 -0
  104. package/skills/ue-materials-rendering/references/post-process-settings.md +383 -0
  105. package/skills/ue-module-build-system/SKILL.md +487 -0
  106. package/skills/ue-module-build-system/references/build-cs-reference.md +418 -0
  107. package/skills/ue-module-build-system/references/common-build-errors.md +381 -0
  108. package/skills/ue-naming-conventions/SKILL.md +142 -0
  109. package/skills/ue-naming-conventions/references/asset-name-modifiers.md +233 -0
  110. package/skills/ue-naming-conventions/references/content-structure.md +83 -0
  111. package/skills/ue-networking-replication/SKILL.md +452 -0
  112. package/skills/ue-networking-replication/references/replication-patterns.md +526 -0
  113. package/skills/ue-networking-replication/references/rpc-decision-guide.md +393 -0
  114. package/skills/ue-niagara-effects/SKILL.md +531 -0
  115. package/skills/ue-niagara-effects/references/niagara-data-interfaces.md +491 -0
  116. package/skills/ue-niagara-effects/references/niagara-parameter-types.md +153 -0
  117. package/skills/ue-niagara-effects/references/niagara-workflow.md +115 -0
  118. package/skills/ue-packaging/SKILL.md +116 -0
  119. package/skills/ue-physics-collision/SKILL.md +423 -0
  120. package/skills/ue-physics-collision/references/collision-channel-setup.md +218 -0
  121. package/skills/ue-physics-collision/references/trace-patterns.md +499 -0
  122. package/skills/ue-procedural-generation/SKILL.md +505 -0
  123. package/skills/ue-procedural-generation/references/pcg-node-reference.md +381 -0
  124. package/skills/ue-procedural-generation/references/procedural-mesh-patterns.md +940 -0
  125. package/skills/ue-project-context/SKILL.md +305 -0
  126. package/skills/ue-sequencer-cinematics/SKILL.md +506 -0
  127. package/skills/ue-sequencer-cinematics/references/sequencer-patterns.md +629 -0
  128. package/skills/ue-serialization-savegames/SKILL.md +524 -0
  129. package/skills/ue-serialization-savegames/references/save-system-architecture.md +705 -0
  130. package/skills/ue-state-trees/SKILL.md +489 -0
  131. package/skills/ue-state-trees/references/state-tree-mass-integration.md +184 -0
  132. package/skills/ue-state-trees/references/state-tree-patterns.md +321 -0
  133. package/skills/ue-testing-debugging/SKILL.md +520 -0
  134. package/skills/ue-testing-debugging/references/automation-test-patterns.md +387 -0
  135. package/skills/ue-testing-debugging/references/profiling-commands.md +357 -0
  136. package/skills/ue-ui-umg-slate/SKILL.md +431 -0
  137. package/skills/ue-ui-umg-slate/references/common-ui-setup.md +484 -0
  138. package/skills/ue-ui-umg-slate/references/widget-types.md +487 -0
  139. package/skills/ue-world-level-streaming/SKILL.md +518 -0
  140. package/skills/ue-world-level-streaming/references/streaming-patterns.md +414 -0
  141. package/templates/gamedev/unreal-project-context.md +107 -0
package/dist/cli.js CHANGED
@@ -4,8 +4,8 @@
4
4
  import { Command } from "commander";
5
5
 
6
6
  // src/commands/install.ts
7
- import path10 from "path";
8
- import chalk from "chalk";
7
+ import path11 from "path";
8
+ import chalk2 from "chalk";
9
9
 
10
10
  // src/profiles.ts
11
11
  import fs from "fs";
@@ -18,7 +18,7 @@ function parseProfile(filePath) {
18
18
  return {
19
19
  name: String(raw.name ?? path.basename(filePath, ".yaml")),
20
20
  description: String(raw.description ?? ""),
21
- type: raw.type ?? "addon",
21
+ category: String(raw.category ?? "other"),
22
22
  skills: raw.skills ?? [],
23
23
  rules: {
24
24
  universal: rules.universal ?? [],
@@ -32,6 +32,29 @@ function parseProfile(filePath) {
32
32
  }
33
33
  };
34
34
  }
35
+ var CATEGORY_ORDER = [
36
+ { id: "backend", label: "Backend" },
37
+ { id: "frontend", label: "Frontend & Design" },
38
+ { id: "mobile", label: "Mobile" },
39
+ { id: "gamedev", label: "Gamedev" },
40
+ { id: "platform", label: "Platform & Quality" },
41
+ { id: "management", label: "Project Management" },
42
+ { id: "business", label: "Business" },
43
+ { id: "personal", label: "Personal" },
44
+ { id: "utilities", label: "Utilities" }
45
+ ];
46
+ function groupByCategory(profiles) {
47
+ const known = new Set(CATEGORY_ORDER.map((c) => c.id));
48
+ const groups = CATEGORY_ORDER.map((c) => ({
49
+ ...c,
50
+ profiles: profiles.filter((p2) => p2.category === c.id)
51
+ }));
52
+ const rest = profiles.filter((p2) => !known.has(p2.category));
53
+ if (rest.length > 0) {
54
+ groups.push({ id: "other", label: "Other", profiles: rest });
55
+ }
56
+ return groups.filter((g) => g.profiles.length > 0);
57
+ }
35
58
  function listProfiles(profilesDir) {
36
59
  if (!fs.existsSync(profilesDir)) return [];
37
60
  return fs.readdirSync(profilesDir).filter((f) => f.endsWith(".yaml") && f !== "core.yaml").map((f) => parseProfile(path.join(profilesDir, f))).sort((a, b) => a.name.localeCompare(b.name));
@@ -105,77 +128,215 @@ function mergeProfiles(profilesDir, profileNames) {
105
128
  };
106
129
  }
107
130
 
131
+ // src/detect.ts
132
+ import fs2 from "fs";
133
+ import path2 from "path";
134
+ function hasFile(dir, rel) {
135
+ return fs2.existsSync(path2.join(dir, rel));
136
+ }
137
+ function topLevelMatch(dir, suffix) {
138
+ try {
139
+ return fs2.readdirSync(dir).find((f) => f.endsWith(suffix));
140
+ } catch {
141
+ return void 0;
142
+ }
143
+ }
144
+ function readPackageDeps(dir) {
145
+ try {
146
+ const raw = JSON.parse(
147
+ fs2.readFileSync(path2.join(dir, "package.json"), "utf-8")
148
+ );
149
+ return {
150
+ ...raw.dependencies ?? {},
151
+ ...raw.devDependencies ?? {}
152
+ };
153
+ } catch {
154
+ return {};
155
+ }
156
+ }
157
+ function detectStack(dir) {
158
+ const out = [];
159
+ const isUnity = hasFile(dir, "ProjectSettings/ProjectVersion.txt");
160
+ if (isUnity) {
161
+ out.push({ profile: "unity-dev", reason: "ProjectSettings/ProjectVersion.txt" });
162
+ }
163
+ const uproject = topLevelMatch(dir, ".uproject");
164
+ if (uproject) {
165
+ out.push({ profile: "unreal-dev", reason: uproject });
166
+ }
167
+ const isFlutter = hasFile(dir, "pubspec.yaml");
168
+ if (isFlutter) {
169
+ out.push({ profile: "flutter", reason: "pubspec.yaml" });
170
+ }
171
+ if (hasFile(dir, "go.mod")) {
172
+ out.push({ profile: "backend-go", reason: "go.mod" });
173
+ }
174
+ let isReactNative = false;
175
+ if (hasFile(dir, "package.json")) {
176
+ const deps = readPackageDeps(dir);
177
+ if (deps["@nestjs/core"]) {
178
+ out.push({ profile: "backend-nestjs", reason: "package.json: @nestjs/core" });
179
+ } else if (deps["next"]) {
180
+ out.push({ profile: "backend-nextjs", reason: "package.json: next" });
181
+ } else if (deps["react-native"] || deps["expo"]) {
182
+ isReactNative = true;
183
+ out.push({ profile: "mobile", reason: "package.json: react-native/expo" });
184
+ } else if (deps["react"] || deps["vue"] || deps["svelte"]) {
185
+ out.push({ profile: "frontend", reason: "package.json: react/vue/svelte" });
186
+ } else if (deps["fastify"] || deps["express"] || deps["koa"] || deps["hono"]) {
187
+ out.push({ profile: "backend-ts", reason: "package.json: server framework" });
188
+ } else if (deps["typescript"]) {
189
+ out.push({ profile: "backend-ts", reason: "package.json: typescript" });
190
+ }
191
+ }
192
+ if (!isUnity) {
193
+ const dotnet = topLevelMatch(dir, ".sln") ?? topLevelMatch(dir, ".csproj");
194
+ if (dotnet) {
195
+ out.push({ profile: "backend-dotnet", reason: dotnet });
196
+ }
197
+ }
198
+ if (!isFlutter && !isReactNative) {
199
+ if ((hasFile(dir, "settings.gradle") || hasFile(dir, "settings.gradle.kts")) && hasFile(dir, "app")) {
200
+ out.push({ profile: "android-native", reason: "settings.gradle + app/" });
201
+ }
202
+ const ios = topLevelMatch(dir, ".xcodeproj") ?? topLevelMatch(dir, ".xcworkspace") ?? (hasFile(dir, "Podfile") ? "Podfile" : void 0);
203
+ if (ios) {
204
+ out.push({ profile: "ios-native", reason: ios });
205
+ }
206
+ }
207
+ return out;
208
+ }
209
+
210
+ // src/wizard.ts
211
+ import * as p from "@clack/prompts";
212
+ import chalk from "chalk";
213
+ async function runInstallWizard(profiles, detections) {
214
+ p.intro(chalk.cyan("claude-code-arcane install"));
215
+ const available = new Set(profiles.map((pr) => pr.name));
216
+ const suggested = detections.filter((d) => available.has(d.profile));
217
+ if (suggested.length > 0) {
218
+ p.note(
219
+ suggested.map((d) => `${chalk.green(d.profile)} ${chalk.dim(`(${d.reason})`)}`).join("\n"),
220
+ "Detected stack \u2014 pre-selected below"
221
+ );
222
+ } else {
223
+ p.note(
224
+ "No stack detected in this directory.\nPick whatever fits your work \u2014 profiles combine freely.",
225
+ "No suggestions"
226
+ );
227
+ }
228
+ const options = {};
229
+ for (const group of groupByCategory(profiles)) {
230
+ options[group.label] = group.profiles.map((pr) => ({
231
+ value: pr.name,
232
+ label: pr.name,
233
+ hint: pr.description
234
+ }));
235
+ }
236
+ const selected = await p.groupMultiselect({
237
+ message: "Select profiles (space to toggle, enter to confirm)",
238
+ options,
239
+ initialValues: suggested.map((d) => d.profile),
240
+ required: false
241
+ });
242
+ if (p.isCancel(selected)) {
243
+ p.cancel("Install cancelled.");
244
+ return null;
245
+ }
246
+ const names = selected;
247
+ if (names.length === 0) {
248
+ p.outro(
249
+ chalk.dim(
250
+ "Nothing selected. Run again, or install directly: npx claude-code-arcane install backend-ts+testing"
251
+ )
252
+ );
253
+ return null;
254
+ }
255
+ const expr = names.join("+");
256
+ const ok = await p.confirm({
257
+ message: `Install ${chalk.cyan(expr)} (plus core, always included)?`
258
+ });
259
+ if (p.isCancel(ok) || !ok) {
260
+ p.cancel("Install cancelled.");
261
+ return null;
262
+ }
263
+ p.outro(
264
+ chalk.dim(`Next time, skip the menu: npx claude-code-arcane install ${expr}`)
265
+ );
266
+ return names;
267
+ }
268
+
108
269
  // src/installer.ts
109
- import fs5 from "fs";
110
- import path6 from "path";
270
+ import fs6 from "fs";
271
+ import path7 from "path";
111
272
 
112
273
  // src/utils.ts
113
274
  import { fileURLToPath } from "url";
114
- import path2 from "path";
115
- import fs2 from "fs";
275
+ import path3 from "path";
276
+ import fs3 from "fs";
116
277
  function getPackageRoot() {
117
278
  const thisFile = fileURLToPath(import.meta.url);
118
- return path2.resolve(path2.dirname(thisFile), "..");
279
+ return path3.resolve(path3.dirname(thisFile), "..");
119
280
  }
120
281
  var COPY_SKIP = /* @__PURE__ */ new Set(["__pycache__", ".pytest_cache"]);
121
282
  function isBuildArtifact(name) {
122
283
  return COPY_SKIP.has(name) || name.endsWith(".pyc");
123
284
  }
124
285
  function copyDirSync(src, dest) {
125
- fs2.mkdirSync(dest, { recursive: true });
126
- for (const entry of fs2.readdirSync(src, { withFileTypes: true })) {
286
+ fs3.mkdirSync(dest, { recursive: true });
287
+ for (const entry of fs3.readdirSync(src, { withFileTypes: true })) {
127
288
  if (isBuildArtifact(entry.name)) continue;
128
- const srcPath = path2.join(src, entry.name);
129
- const destPath = path2.join(dest, entry.name);
289
+ const srcPath = path3.join(src, entry.name);
290
+ const destPath = path3.join(dest, entry.name);
130
291
  if (entry.isDirectory()) {
131
292
  copyDirSync(srcPath, destPath);
132
293
  } else {
133
- fs2.copyFileSync(srcPath, destPath);
294
+ fs3.copyFileSync(srcPath, destPath);
134
295
  }
135
296
  }
136
297
  }
137
298
  function ensureDir(dir) {
138
- fs2.mkdirSync(dir, { recursive: true });
299
+ fs3.mkdirSync(dir, { recursive: true });
139
300
  }
140
- function fileExists(p) {
141
- return fs2.existsSync(p);
301
+ function fileExists(p2) {
302
+ return fs3.existsSync(p2);
142
303
  }
143
- function readJsonSync(p) {
144
- return JSON.parse(fs2.readFileSync(p, "utf-8"));
304
+ function readJsonSync(p2) {
305
+ return JSON.parse(fs3.readFileSync(p2, "utf-8"));
145
306
  }
146
- function writeJsonSync(p, data) {
147
- fs2.writeFileSync(p, JSON.stringify(data, null, 2) + "\n", "utf-8");
307
+ function writeJsonSync(p2, data) {
308
+ fs3.writeFileSync(p2, JSON.stringify(data, null, 2) + "\n", "utf-8");
148
309
  }
149
- function isSymlinkOrJunction(p) {
310
+ function isSymlinkOrJunction(p2) {
150
311
  try {
151
- return fs2.lstatSync(p).isSymbolicLink();
312
+ return fs3.lstatSync(p2).isSymbolicLink();
152
313
  } catch {
153
314
  return false;
154
315
  }
155
316
  }
156
317
  function getPackageVersion() {
157
- const pkgPath = path2.join(getPackageRoot(), "package.json");
318
+ const pkgPath = path3.join(getPackageRoot(), "package.json");
158
319
  const pkg = readJsonSync(pkgPath);
159
320
  return pkg.version;
160
321
  }
161
- function safeRemove(p) {
162
- if (isSymlinkOrJunction(p)) {
163
- fs2.unlinkSync(p);
164
- } else if (fs2.existsSync(p)) {
165
- fs2.rmSync(p, { recursive: true, force: true });
322
+ function safeRemove(p2) {
323
+ if (isSymlinkOrJunction(p2)) {
324
+ fs3.unlinkSync(p2);
325
+ } else if (fs3.existsSync(p2)) {
326
+ fs3.rmSync(p2, { recursive: true, force: true });
166
327
  }
167
328
  }
168
329
 
169
330
  // src/manifest.ts
170
- import path3 from "path";
331
+ import path4 from "path";
171
332
  var MANIFEST_FILE = "arcane-manifest.json";
172
333
  function manifestPath(target) {
173
- return path3.join(target, ".claude", MANIFEST_FILE);
334
+ return path4.join(target, ".claude", MANIFEST_FILE);
174
335
  }
175
336
  function readManifest(target) {
176
- const p = manifestPath(target);
177
- if (!fileExists(p)) return null;
178
- return readJsonSync(p);
337
+ const p2 = manifestPath(target);
338
+ if (!fileExists(p2)) return null;
339
+ return readJsonSync(p2);
179
340
  }
180
341
  function writeManifest(target, merged, profileCommand, packageRoot, worktreeOrOpts) {
181
342
  let opts = {};
@@ -224,8 +385,8 @@ function updateManifestFields(target, updates) {
224
385
 
225
386
  // src/worktree.ts
226
387
  import { execSync } from "child_process";
227
- import fs3 from "fs";
228
- import path4 from "path";
388
+ import fs4 from "fs";
389
+ import path5 from "path";
229
390
  function getWorktreeInfo(target) {
230
391
  try {
231
392
  execSync("git rev-parse --is-inside-work-tree", {
@@ -246,10 +407,10 @@ function getWorktreeInfo(target) {
246
407
  encoding: "utf-8",
247
408
  stdio: "pipe"
248
409
  }).trim();
249
- const resolvedCommon = path4.resolve(target, gitCommonDir);
250
- const resolvedGit = path4.resolve(target, gitDir);
410
+ const resolvedCommon = path5.resolve(target, gitCommonDir);
411
+ const resolvedGit = path5.resolve(target, gitDir);
251
412
  const isWorktree = resolvedCommon !== resolvedGit;
252
- const mainWorktreePath = isWorktree ? path4.resolve(resolvedCommon, "..") : target;
413
+ const mainWorktreePath = isWorktree ? path5.resolve(resolvedCommon, "..") : target;
253
414
  let currentBranch = "HEAD";
254
415
  try {
255
416
  currentBranch = execSync("git branch --show-current", {
@@ -264,7 +425,7 @@ function getWorktreeInfo(target) {
264
425
  isWorktree,
265
426
  mainWorktreePath,
266
427
  currentBranch,
267
- worktreeId: path4.basename(target),
428
+ worktreeId: path5.basename(target),
268
429
  allWorktrees
269
430
  };
270
431
  }
@@ -300,17 +461,17 @@ function parseWorktreeList(cwd) {
300
461
  }
301
462
  }
302
463
  function findMainArcaneInstall(mainPath) {
303
- return fs3.existsSync(
304
- path4.join(mainPath, ".claude", "arcane-manifest.json")
464
+ return fs4.existsSync(
465
+ path5.join(mainPath, ".claude", "arcane-manifest.json")
305
466
  );
306
467
  }
307
468
  function linkOrCopyDir(src, dest, mode) {
308
469
  if (mode === "symlink") {
309
470
  try {
310
- const parentDir = path4.dirname(dest);
311
- fs3.mkdirSync(parentDir, { recursive: true });
471
+ const parentDir = path5.dirname(dest);
472
+ fs4.mkdirSync(parentDir, { recursive: true });
312
473
  const type = process.platform === "win32" ? "junction" : "dir";
313
- fs3.symlinkSync(src, dest, type);
474
+ fs4.symlinkSync(src, dest, type);
314
475
  return "linked";
315
476
  } catch {
316
477
  }
@@ -322,9 +483,9 @@ function sanitizeBranchForPath(branch) {
322
483
  return branch.replace(/^(feat|fix|feature|hotfix)\//, "").replace(/\//g, "-").replace(/[^a-zA-Z0-9._-]/g, "-");
323
484
  }
324
485
  function defaultWorktreePath(repoRoot, branch) {
325
- const repoName = path4.basename(repoRoot);
486
+ const repoName = path5.basename(repoRoot);
326
487
  const sanitized = sanitizeBranchForPath(branch);
327
- return path4.resolve(repoRoot, "..", `${repoName}-${sanitized}`);
488
+ return path5.resolve(repoRoot, "..", `${repoName}-${sanitized}`);
328
489
  }
329
490
  function createGitWorktree(repoRoot, worktreePath, branch, base) {
330
491
  const branchExists = checkBranchExists(repoRoot, branch);
@@ -366,20 +527,20 @@ function detectPackageManager(dir) {
366
527
  { lockfile: "package-lock.json", command: "npm install" }
367
528
  ];
368
529
  for (const m of managers) {
369
- if (fs3.existsSync(path4.join(dir, m.lockfile))) return m;
530
+ if (fs4.existsSync(path5.join(dir, m.lockfile))) return m;
370
531
  }
371
- if (fs3.existsSync(path4.join(dir, "package.json"))) {
532
+ if (fs4.existsSync(path5.join(dir, "package.json"))) {
372
533
  return { lockfile: "package.json", command: "npm install" };
373
534
  }
374
535
  return null;
375
536
  }
376
537
 
377
538
  // src/content-hash.ts
378
- import fs4 from "fs";
379
- import path5 from "path";
539
+ import fs5 from "fs";
540
+ import path6 from "path";
380
541
  import { createHash } from "crypto";
381
542
  function hashFile(filePath) {
382
- const content = fs4.readFileSync(filePath);
543
+ const content = fs5.readFileSync(filePath);
383
544
  return createHash("sha256").update(content).digest("hex");
384
545
  }
385
546
  function hashDirectory(dirPath) {
@@ -388,10 +549,10 @@ function hashDirectory(dirPath) {
388
549
  return hashes;
389
550
  }
390
551
  function walkDir(base, current, hashes) {
391
- for (const entry of fs4.readdirSync(current, { withFileTypes: true })) {
552
+ for (const entry of fs5.readdirSync(current, { withFileTypes: true })) {
392
553
  if (isBuildArtifact(entry.name)) continue;
393
- const fullPath = path5.join(current, entry.name);
394
- const relPath = path5.relative(base, fullPath).split(path5.sep).join("/");
554
+ const fullPath = path6.join(current, entry.name);
555
+ const relPath = path6.relative(base, fullPath).split(path6.sep).join("/");
395
556
  if (entry.isDirectory()) {
396
557
  walkDir(base, fullPath, hashes);
397
558
  } else {
@@ -406,39 +567,39 @@ function computeContentHashes(claudeDir) {
406
567
  agents: {},
407
568
  hooks: {}
408
569
  };
409
- const skillsDir = path5.join(claudeDir, "skills");
410
- if (fs4.existsSync(skillsDir)) {
411
- for (const entry of fs4.readdirSync(skillsDir, { withFileTypes: true })) {
570
+ const skillsDir = path6.join(claudeDir, "skills");
571
+ if (fs5.existsSync(skillsDir)) {
572
+ for (const entry of fs5.readdirSync(skillsDir, { withFileTypes: true })) {
412
573
  if (entry.isDirectory()) {
413
- const dirHash = hashDirectory(path5.join(skillsDir, entry.name));
574
+ const dirHash = hashDirectory(path6.join(skillsDir, entry.name));
414
575
  const combined = createHash("sha256").update(JSON.stringify(dirHash)).digest("hex");
415
576
  hashes.skills[entry.name] = combined;
416
577
  }
417
578
  }
418
579
  }
419
- const rulesDir = path5.join(claudeDir, "rules");
420
- if (fs4.existsSync(rulesDir)) {
421
- for (const entry of fs4.readdirSync(rulesDir, { withFileTypes: true })) {
580
+ const rulesDir = path6.join(claudeDir, "rules");
581
+ if (fs5.existsSync(rulesDir)) {
582
+ for (const entry of fs5.readdirSync(rulesDir, { withFileTypes: true })) {
422
583
  if (!entry.isDirectory()) {
423
- hashes.rules[entry.name] = hashFile(path5.join(rulesDir, entry.name));
584
+ hashes.rules[entry.name] = hashFile(path6.join(rulesDir, entry.name));
424
585
  }
425
586
  }
426
587
  }
427
- const agentsDir = path5.join(claudeDir, "agents");
428
- if (fs4.existsSync(agentsDir)) {
429
- for (const entry of fs4.readdirSync(agentsDir, { withFileTypes: true })) {
588
+ const agentsDir = path6.join(claudeDir, "agents");
589
+ if (fs5.existsSync(agentsDir)) {
590
+ for (const entry of fs5.readdirSync(agentsDir, { withFileTypes: true })) {
430
591
  if (entry.isDirectory()) {
431
- const dirHash = hashDirectory(path5.join(agentsDir, entry.name));
592
+ const dirHash = hashDirectory(path6.join(agentsDir, entry.name));
432
593
  const combined = createHash("sha256").update(JSON.stringify(dirHash)).digest("hex");
433
594
  hashes.agents[entry.name] = combined;
434
595
  }
435
596
  }
436
597
  }
437
- const hooksDir = path5.join(claudeDir, "hooks");
438
- if (fs4.existsSync(hooksDir)) {
439
- for (const entry of fs4.readdirSync(hooksDir, { withFileTypes: true })) {
598
+ const hooksDir = path6.join(claudeDir, "hooks");
599
+ if (fs5.existsSync(hooksDir)) {
600
+ for (const entry of fs5.readdirSync(hooksDir, { withFileTypes: true })) {
440
601
  if (!entry.isDirectory()) {
441
- hashes.hooks[entry.name] = hashFile(path5.join(hooksDir, entry.name));
602
+ hashes.hooks[entry.name] = hashFile(path6.join(hooksDir, entry.name));
442
603
  }
443
604
  }
444
605
  }
@@ -452,8 +613,8 @@ function computeSourceHashes(packageRoot, skills, rules, agents) {
452
613
  hooks: {}
453
614
  };
454
615
  for (const skill of skills) {
455
- const skillDir = path5.join(packageRoot, "skills", skill);
456
- if (fs4.existsSync(skillDir)) {
616
+ const skillDir = path6.join(packageRoot, "skills", skill);
617
+ if (fs5.existsSync(skillDir)) {
457
618
  const dirHash = hashDirectory(skillDir);
458
619
  const combined = createHash("sha256").update(JSON.stringify(dirHash)).digest("hex");
459
620
  hashes.skills[skill] = combined;
@@ -461,29 +622,29 @@ function computeSourceHashes(packageRoot, skills, rules, agents) {
461
622
  }
462
623
  for (const rule of rules) {
463
624
  const ruleName = rule.endsWith(".md") ? rule : `${rule}.md`;
464
- const rulePath = path5.join(packageRoot, "rules", ruleName);
465
- if (fs4.existsSync(rulePath)) {
625
+ const rulePath = path6.join(packageRoot, "rules", ruleName);
626
+ if (fs5.existsSync(rulePath)) {
466
627
  hashes.rules[ruleName] = hashFile(rulePath);
467
628
  } else {
468
- const gamedevPath = path5.join(packageRoot, "rules", "gamedev", ruleName);
469
- if (fs4.existsSync(gamedevPath)) {
629
+ const gamedevPath = path6.join(packageRoot, "rules", "gamedev", ruleName);
630
+ if (fs5.existsSync(gamedevPath)) {
470
631
  hashes.rules[ruleName] = hashFile(gamedevPath);
471
632
  }
472
633
  }
473
634
  }
474
635
  for (const agent of agents) {
475
- const agentDir = path5.join(packageRoot, "agents", agent);
476
- if (fs4.existsSync(agentDir)) {
636
+ const agentDir = path6.join(packageRoot, "agents", agent);
637
+ if (fs5.existsSync(agentDir)) {
477
638
  const dirHash = hashDirectory(agentDir);
478
639
  const combined = createHash("sha256").update(JSON.stringify(dirHash)).digest("hex");
479
640
  hashes.agents[agent] = combined;
480
641
  }
481
642
  }
482
- const hooksDir = path5.join(packageRoot, "hooks");
483
- if (fs4.existsSync(hooksDir)) {
484
- for (const entry of fs4.readdirSync(hooksDir, { withFileTypes: true })) {
643
+ const hooksDir = path6.join(packageRoot, "hooks");
644
+ if (fs5.existsSync(hooksDir)) {
645
+ for (const entry of fs5.readdirSync(hooksDir, { withFileTypes: true })) {
485
646
  if (!entry.isDirectory()) {
486
- hashes.hooks[entry.name] = hashFile(path5.join(hooksDir, entry.name));
647
+ hashes.hooks[entry.name] = hashFile(path6.join(hooksDir, entry.name));
487
648
  }
488
649
  }
489
650
  }
@@ -509,6 +670,11 @@ var SETTINGS_TEMPLATE = {
509
670
  command: "bash .claude/hooks/detect-division.sh",
510
671
  timeout: 10
511
672
  },
673
+ {
674
+ type: "command",
675
+ command: "bash .claude/hooks/detect-unreal.sh",
676
+ timeout: 10
677
+ },
512
678
  {
513
679
  type: "command",
514
680
  command: "bash .claude/hooks/detect-gaps.sh",
@@ -647,7 +813,7 @@ var Installer = class {
647
813
  constructor(merged, opts) {
648
814
  this.root = opts.contentRoot ?? getPackageRoot();
649
815
  this.target = opts.target;
650
- this.claudeDir = path6.join(opts.target, ".claude");
816
+ this.claudeDir = path7.join(opts.target, ".claude");
651
817
  this.merged = merged;
652
818
  this.dryRun = opts.dryRun;
653
819
  this.shareFrom = opts.shareFrom;
@@ -679,22 +845,22 @@ var Installer = class {
679
845
  return this.logs;
680
846
  }
681
847
  backup() {
682
- if (!fs5.existsSync(this.claudeDir)) return;
683
- const backup = path6.join(this.target, ".claude.bak");
848
+ if (!fs6.existsSync(this.claudeDir)) return;
849
+ const backup = path7.join(this.target, ".claude.bak");
684
850
  if (this.dryRun) {
685
851
  this.log(" [dry-run] Would backup .claude/ -> .claude.bak/");
686
852
  return;
687
853
  }
688
- if (fs5.existsSync(backup)) {
689
- fs5.rmSync(backup, { recursive: true, force: true });
854
+ if (fs6.existsSync(backup)) {
855
+ fs6.rmSync(backup, { recursive: true, force: true });
690
856
  }
691
- fs5.renameSync(this.claudeDir, backup);
857
+ fs6.renameSync(this.claudeDir, backup);
692
858
  this.log(" Backed up .claude/ -> .claude.bak/");
693
859
  }
694
860
  createDirs() {
695
861
  if (this.dryRun) return;
696
862
  for (const d of ["skills", "rules", "agents", "docs"]) {
697
- ensureDir(path6.join(this.claudeDir, d));
863
+ ensureDir(path7.join(this.claudeDir, d));
698
864
  }
699
865
  }
700
866
  get hasStatusline() {
@@ -702,7 +868,7 @@ var Installer = class {
702
868
  }
703
869
  copyHooks() {
704
870
  this.log("\nHooks:");
705
- const hooksDir = path6.join(this.claudeDir, "hooks");
871
+ const hooksDir = path7.join(this.claudeDir, "hooks");
706
872
  if (this.dryRun) {
707
873
  if (this.shareFrom) {
708
874
  this.log(" [dry-run] hooks/ -> symlink from main worktree");
@@ -713,8 +879,8 @@ var Installer = class {
713
879
  return;
714
880
  }
715
881
  if (this.shareFrom) {
716
- const sharedHooks = path6.join(this.shareFrom, ".claude", "hooks");
717
- if (fs5.existsSync(sharedHooks)) {
882
+ const sharedHooks = path7.join(this.shareFrom, ".claude", "hooks");
883
+ if (fs6.existsSync(sharedHooks)) {
718
884
  const result = linkOrCopyDir(sharedHooks, hooksDir, "symlink");
719
885
  if (result === "linked") {
720
886
  this.sharedDirs.push("hooks");
@@ -726,17 +892,17 @@ var Installer = class {
726
892
  return;
727
893
  }
728
894
  }
729
- const hooksSrc = path6.join(this.root, "hooks");
730
- if (fs5.existsSync(hooksSrc)) {
895
+ const hooksSrc = path7.join(this.root, "hooks");
896
+ if (fs6.existsSync(hooksSrc)) {
731
897
  ensureDir(hooksDir);
732
- for (const entry of fs5.readdirSync(hooksSrc, { withFileTypes: true })) {
898
+ for (const entry of fs6.readdirSync(hooksSrc, { withFileTypes: true })) {
733
899
  if (entry.name === "statusline.sh") continue;
734
- const src = path6.join(hooksSrc, entry.name);
735
- const dst = path6.join(hooksDir, entry.name);
900
+ const src = path7.join(hooksSrc, entry.name);
901
+ const dst = path7.join(hooksDir, entry.name);
736
902
  if (entry.isDirectory()) {
737
903
  copyDirSync(src, dst);
738
904
  } else {
739
- fs5.copyFileSync(src, dst);
905
+ fs6.copyFileSync(src, dst);
740
906
  }
741
907
  }
742
908
  this.log(" [ok] hooks/");
@@ -745,24 +911,24 @@ var Installer = class {
745
911
  }
746
912
  copyStatusline() {
747
913
  if (!this.hasStatusline) return;
748
- const statusline = path6.join(this.root, "hooks", "statusline.sh");
749
- if (fs5.existsSync(statusline)) {
750
- fs5.copyFileSync(
914
+ const statusline = path7.join(this.root, "hooks", "statusline.sh");
915
+ if (fs6.existsSync(statusline)) {
916
+ fs6.copyFileSync(
751
917
  statusline,
752
- path6.join(this.claudeDir, "statusline.sh")
918
+ path7.join(this.claudeDir, "statusline.sh")
753
919
  );
754
- this.log(" [ok] statusline.sh (+statusline addon)");
920
+ this.log(" [ok] statusline.sh (statusline profile)");
755
921
  }
756
922
  }
757
923
  copySkills() {
758
924
  this.log("\nSkills:");
759
925
  for (const skill of this.merged.skills) {
760
- const src = path6.join(this.root, "skills", skill);
761
- if (!fs5.existsSync(src)) {
926
+ const src = path7.join(this.root, "skills", skill);
927
+ if (!fs6.existsSync(src)) {
762
928
  this.log(` WARN: Skill '${skill}' not found in skills/`);
763
929
  continue;
764
930
  }
765
- const dst = path6.join(this.claudeDir, "skills", skill);
931
+ const dst = path7.join(this.claudeDir, "skills", skill);
766
932
  if (this.dryRun) {
767
933
  this.log(` [dry-run] ${skill}`);
768
934
  } else {
@@ -772,9 +938,9 @@ var Installer = class {
772
938
  }
773
939
  const hasGamedev = this.merged.rules.gamedev.length > 0;
774
940
  if (hasGamedev) {
775
- const tplSrc = path6.join(this.root, "templates", "gamedev");
776
- if (fs5.existsSync(tplSrc)) {
777
- const tplDst = path6.join(this.claudeDir, "skills", "_templates");
941
+ const tplSrc = path7.join(this.root, "templates", "gamedev");
942
+ if (fs6.existsSync(tplSrc)) {
943
+ const tplDst = path7.join(this.claudeDir, "skills", "_templates");
778
944
  if (this.dryRun) {
779
945
  this.log(" [dry-run] _templates/");
780
946
  } else {
@@ -787,33 +953,33 @@ var Installer = class {
787
953
  copyRules() {
788
954
  this.log("\nRules:");
789
955
  for (const rule of this.merged.rules.universal) {
790
- const src = path6.join(this.root, "rules", `${rule}.md`);
791
- if (!fs5.existsSync(src)) {
956
+ const src = path7.join(this.root, "rules", `${rule}.md`);
957
+ if (!fs6.existsSync(src)) {
792
958
  this.log(` WARN: Rule '${rule}.md' not found`);
793
959
  continue;
794
960
  }
795
961
  if (this.dryRun) {
796
962
  this.log(` [dry-run] ${rule}.md`);
797
963
  } else {
798
- fs5.copyFileSync(
964
+ fs6.copyFileSync(
799
965
  src,
800
- path6.join(this.claudeDir, "rules", `${rule}.md`)
966
+ path7.join(this.claudeDir, "rules", `${rule}.md`)
801
967
  );
802
968
  this.log(` [ok] ${rule}.md`);
803
969
  }
804
970
  }
805
971
  for (const rule of this.merged.rules.gamedev) {
806
- const src = path6.join(this.root, "rules", "gamedev", `${rule}.md`);
807
- if (!fs5.existsSync(src)) {
972
+ const src = path7.join(this.root, "rules", "gamedev", `${rule}.md`);
973
+ if (!fs6.existsSync(src)) {
808
974
  this.log(` WARN: Rule '${rule}.md' not found`);
809
975
  continue;
810
976
  }
811
977
  if (this.dryRun) {
812
978
  this.log(` [dry-run] ${rule}.md`);
813
979
  } else {
814
- fs5.copyFileSync(
980
+ fs6.copyFileSync(
815
981
  src,
816
- path6.join(this.claudeDir, "rules", `${rule}.md`)
982
+ path7.join(this.claudeDir, "rules", `${rule}.md`)
817
983
  );
818
984
  this.log(` [ok] ${rule}.md`);
819
985
  }
@@ -823,25 +989,25 @@ var Installer = class {
823
989
  if (this.merged.agents.length === 0) return;
824
990
  this.log("\nAgents:");
825
991
  for (const agentDir of this.merged.agents) {
826
- const src = path6.join(this.root, "agents", agentDir);
827
- if (!fs5.existsSync(src)) {
992
+ const src = path7.join(this.root, "agents", agentDir);
993
+ if (!fs6.existsSync(src)) {
828
994
  this.log(` WARN: Agent dir '${agentDir}' not found`);
829
995
  continue;
830
996
  }
831
- const dst = path6.join(this.claudeDir, "agents", agentDir);
997
+ const dst = path7.join(this.claudeDir, "agents", agentDir);
832
998
  if (this.dryRun) {
833
999
  this.log(` [dry-run] agents/${agentDir}/`);
834
1000
  } else {
835
1001
  copyDirSync(src, dst);
836
- const count = fs5.readdirSync(dst, { recursive: true }).filter((f) => String(f).endsWith(".md")).length;
1002
+ const count = fs6.readdirSync(dst, { recursive: true }).filter((f) => String(f).endsWith(".md")).length;
837
1003
  this.log(` [ok] agents/${agentDir}/ (${count} agents)`);
838
1004
  }
839
1005
  }
840
1006
  }
841
1007
  copyDocs() {
842
- const docsDir = path6.join(this.claudeDir, "docs");
843
- const docsSrc = path6.join(this.root, "docs");
844
- if (!fs5.existsSync(docsSrc)) return;
1008
+ const docsDir = path7.join(this.claudeDir, "docs");
1009
+ const docsSrc = path7.join(this.root, "docs");
1010
+ if (!fs6.existsSync(docsSrc)) return;
845
1011
  this.log("\nDocs:");
846
1012
  if (this.dryRun) {
847
1013
  if (this.shareFrom) {
@@ -852,8 +1018,8 @@ var Installer = class {
852
1018
  return;
853
1019
  }
854
1020
  if (this.shareFrom) {
855
- const sharedDocs = path6.join(this.shareFrom, ".claude", "docs");
856
- if (fs5.existsSync(sharedDocs)) {
1021
+ const sharedDocs = path7.join(this.shareFrom, ".claude", "docs");
1022
+ if (fs6.existsSync(sharedDocs)) {
857
1023
  const result = linkOrCopyDir(sharedDocs, docsDir, "symlink");
858
1024
  if (result === "linked") {
859
1025
  this.sharedDirs.push("docs");
@@ -884,8 +1050,8 @@ var Installer = class {
884
1050
  );
885
1051
  return;
886
1052
  }
887
- const settingsPath = path6.join(this.claudeDir, "settings.json");
888
- fs5.writeFileSync(
1053
+ const settingsPath = path7.join(this.claudeDir, "settings.json");
1054
+ fs6.writeFileSync(
889
1055
  settingsPath,
890
1056
  JSON.stringify(settings, null, 2) + "\n",
891
1057
  "utf-8"
@@ -894,65 +1060,65 @@ var Installer = class {
894
1060
  }
895
1061
  };
896
1062
  function clean(target) {
897
- const claudeDir = path6.join(target, ".claude");
898
- const backup = path6.join(target, ".claude.bak");
899
- if (!fs5.existsSync(claudeDir)) {
1063
+ const claudeDir = path7.join(target, ".claude");
1064
+ const backup = path7.join(target, ".claude.bak");
1065
+ if (!fs6.existsSync(claudeDir)) {
900
1066
  console.log(`No .claude/ found in ${target} \u2014 nothing to clean.`);
901
1067
  return;
902
1068
  }
903
- const manifestFile = path6.join(claudeDir, "arcane-manifest.json");
904
- if (fs5.existsSync(manifestFile)) {
905
- const data = JSON.parse(fs5.readFileSync(manifestFile, "utf-8"));
1069
+ const manifestFile = path7.join(claudeDir, "arcane-manifest.json");
1070
+ if (fs6.existsSync(manifestFile)) {
1071
+ const data = JSON.parse(fs6.readFileSync(manifestFile, "utf-8"));
906
1072
  const profiles = (data.profiles ?? []).filter(
907
- (p) => p !== "core"
1073
+ (p2) => p2 !== "core"
908
1074
  );
909
1075
  console.log(` Current profile: ${profiles.join(" + ")}`);
910
1076
  console.log(` Installed: ${data.installed_at ?? "unknown"}`);
911
1077
  const shared = data.worktree?.shared_dirs ?? [];
912
1078
  for (const dir of shared) {
913
- const p = path6.join(claudeDir, dir);
914
- if (isSymlinkOrJunction(p)) {
915
- fs5.unlinkSync(p);
1079
+ const p2 = path7.join(claudeDir, dir);
1080
+ if (isSymlinkOrJunction(p2)) {
1081
+ fs6.unlinkSync(p2);
916
1082
  console.log(` [ok] Unlinked shared ${dir}/`);
917
1083
  }
918
1084
  }
919
1085
  }
920
1086
  for (const sub of ["hooks", "docs", "skills", "agents", "rules"]) {
921
- const p = path6.join(claudeDir, sub);
922
- safeRemove(p);
1087
+ const p2 = path7.join(claudeDir, sub);
1088
+ safeRemove(p2);
923
1089
  }
924
- const remaining = fs5.readdirSync(claudeDir);
1090
+ const remaining = fs6.readdirSync(claudeDir);
925
1091
  for (const f of remaining) {
926
- const p = path6.join(claudeDir, f);
927
- safeRemove(p);
1092
+ const p2 = path7.join(claudeDir, f);
1093
+ safeRemove(p2);
928
1094
  }
929
- if (fs5.existsSync(claudeDir)) {
930
- fs5.rmSync(claudeDir, { recursive: true, force: true });
1095
+ if (fs6.existsSync(claudeDir)) {
1096
+ fs6.rmSync(claudeDir, { recursive: true, force: true });
931
1097
  }
932
- if (fs5.existsSync(backup)) {
933
- fs5.rmSync(backup, { recursive: true, force: true });
1098
+ if (fs6.existsSync(backup)) {
1099
+ fs6.rmSync(backup, { recursive: true, force: true });
934
1100
  }
935
1101
  console.log(`
936
1102
  [ok] Removed .claude/ from ${target}`);
937
1103
  }
938
1104
 
939
1105
  // src/content-source.ts
940
- import fs7 from "fs";
941
- import path8 from "path";
1106
+ import fs8 from "fs";
1107
+ import path9 from "path";
942
1108
  import os2 from "os";
943
1109
 
944
1110
  // src/cache.ts
945
- import fs6 from "fs";
946
- import path7 from "path";
1111
+ import fs7 from "fs";
1112
+ import path8 from "path";
947
1113
  import os from "os";
948
- var ARCANE_DIR = path7.join(os.homedir(), ".arcane");
949
- var CACHE_DIR = path7.join(ARCANE_DIR, "cache");
1114
+ var ARCANE_DIR = path8.join(os.homedir(), ".arcane");
1115
+ var CACHE_DIR = path8.join(ARCANE_DIR, "cache");
950
1116
  function getCachePath(version) {
951
- return path7.join(CACHE_DIR, version);
1117
+ return path8.join(CACHE_DIR, version);
952
1118
  }
953
1119
  function isCached(version) {
954
1120
  const versionDir = getCachePath(version);
955
- return fs6.existsSync(versionDir) && fs6.existsSync(path7.join(versionDir, ".cache-meta.json"));
1121
+ return fs7.existsSync(versionDir) && fs7.existsSync(path8.join(versionDir, ".cache-meta.json"));
956
1122
  }
957
1123
  function getCachedContentRoot(version) {
958
1124
  if (!isCached(version)) return null;
@@ -960,34 +1126,34 @@ function getCachedContentRoot(version) {
960
1126
  }
961
1127
  function storeInCache(version, contentDir) {
962
1128
  const versionDir = getCachePath(version);
963
- if (fs6.existsSync(versionDir)) {
964
- fs6.rmSync(versionDir, { recursive: true, force: true });
1129
+ if (fs7.existsSync(versionDir)) {
1130
+ fs7.rmSync(versionDir, { recursive: true, force: true });
965
1131
  }
966
- fs6.mkdirSync(versionDir, { recursive: true });
1132
+ fs7.mkdirSync(versionDir, { recursive: true });
967
1133
  for (const dir of ["skills", "rules", "agents", "hooks", "profiles", "templates", "docs"]) {
968
- const src = path7.join(contentDir, dir);
969
- if (fs6.existsSync(src)) {
970
- copyDirRecursive(src, path7.join(versionDir, dir));
1134
+ const src = path8.join(contentDir, dir);
1135
+ if (fs7.existsSync(src)) {
1136
+ copyDirRecursive(src, path8.join(versionDir, dir));
971
1137
  }
972
1138
  }
973
1139
  const meta = {
974
1140
  version,
975
1141
  cached_at: (/* @__PURE__ */ new Date()).toISOString()
976
1142
  };
977
- fs6.writeFileSync(
978
- path7.join(versionDir, ".cache-meta.json"),
1143
+ fs7.writeFileSync(
1144
+ path8.join(versionDir, ".cache-meta.json"),
979
1145
  JSON.stringify(meta, null, 2) + "\n"
980
1146
  );
981
1147
  return versionDir;
982
1148
  }
983
1149
  function pruneCache(keepCount = 3) {
984
- if (!fs6.existsSync(CACHE_DIR)) return;
985
- const entries = fs6.readdirSync(CACHE_DIR, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => {
986
- const metaPath = path7.join(CACHE_DIR, e.name, ".cache-meta.json");
1150
+ if (!fs7.existsSync(CACHE_DIR)) return;
1151
+ const entries = fs7.readdirSync(CACHE_DIR, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => {
1152
+ const metaPath = path8.join(CACHE_DIR, e.name, ".cache-meta.json");
987
1153
  let cachedAt = 0;
988
- if (fs6.existsSync(metaPath)) {
1154
+ if (fs7.existsSync(metaPath)) {
989
1155
  try {
990
- const meta = JSON.parse(fs6.readFileSync(metaPath, "utf-8"));
1156
+ const meta = JSON.parse(fs7.readFileSync(metaPath, "utf-8"));
991
1157
  cachedAt = new Date(meta.cached_at).getTime();
992
1158
  } catch {
993
1159
  cachedAt = 0;
@@ -996,18 +1162,18 @@ function pruneCache(keepCount = 3) {
996
1162
  return { name: e.name, cachedAt };
997
1163
  }).sort((a, b) => b.cachedAt - a.cachedAt);
998
1164
  for (const entry of entries.slice(keepCount)) {
999
- fs6.rmSync(path7.join(CACHE_DIR, entry.name), { recursive: true, force: true });
1165
+ fs7.rmSync(path8.join(CACHE_DIR, entry.name), { recursive: true, force: true });
1000
1166
  }
1001
1167
  }
1002
1168
  function copyDirRecursive(src, dest) {
1003
- fs6.mkdirSync(dest, { recursive: true });
1004
- for (const entry of fs6.readdirSync(src, { withFileTypes: true })) {
1005
- const srcPath = path7.join(src, entry.name);
1006
- const destPath = path7.join(dest, entry.name);
1169
+ fs7.mkdirSync(dest, { recursive: true });
1170
+ for (const entry of fs7.readdirSync(src, { withFileTypes: true })) {
1171
+ const srcPath = path8.join(src, entry.name);
1172
+ const destPath = path8.join(dest, entry.name);
1007
1173
  if (entry.isDirectory()) {
1008
1174
  copyDirRecursive(srcPath, destPath);
1009
1175
  } else {
1010
- fs6.copyFileSync(srcPath, destPath);
1176
+ fs7.copyFileSync(srcPath, destPath);
1011
1177
  }
1012
1178
  }
1013
1179
  }
@@ -1023,7 +1189,7 @@ var BundledContentSource = class {
1023
1189
  }
1024
1190
  async isAvailable() {
1025
1191
  const root = getPackageRoot();
1026
- return fs7.existsSync(path8.join(root, "skills"));
1192
+ return fs8.existsSync(path9.join(root, "skills"));
1027
1193
  }
1028
1194
  async getVersion() {
1029
1195
  return getPackageVersion();
@@ -1063,14 +1229,14 @@ var GitHubContentSource = class {
1063
1229
  if (isCached(version)) {
1064
1230
  return getCachedContentRoot(version);
1065
1231
  }
1066
- const tmpDir = fs7.mkdtempSync(path8.join(os2.tmpdir(), "arcane-github-"));
1232
+ const tmpDir = fs8.mkdtempSync(path9.join(os2.tmpdir(), "arcane-github-"));
1067
1233
  try {
1068
1234
  await this.downloadAndExtract(tmpDir);
1069
1235
  const contentRoot = storeInCache(version, this.findExtractedRoot(tmpDir));
1070
1236
  pruneCache(3);
1071
1237
  return contentRoot;
1072
1238
  } finally {
1073
- fs7.rmSync(tmpDir, { recursive: true, force: true });
1239
+ fs8.rmSync(tmpDir, { recursive: true, force: true });
1074
1240
  }
1075
1241
  }
1076
1242
  async isAvailable() {
@@ -1126,8 +1292,8 @@ var GitHubContentSource = class {
1126
1292
  throw new Error(`Failed to download tarball: ${response.status}`);
1127
1293
  }
1128
1294
  const tar = await import("tar");
1129
- const tarballPath = path8.join(destDir, "repo.tar.gz");
1130
- const fileStream = fs7.createWriteStream(tarballPath);
1295
+ const tarballPath = path9.join(destDir, "repo.tar.gz");
1296
+ const fileStream = fs8.createWriteStream(tarballPath);
1131
1297
  const reader = response.body.getReader();
1132
1298
  const writable = new WritableStream({
1133
1299
  write(chunk) {
@@ -1152,12 +1318,12 @@ var GitHubContentSource = class {
1152
1318
  });
1153
1319
  }
1154
1320
  findExtractedRoot(tmpDir) {
1155
- const entries = fs7.readdirSync(tmpDir, { withFileTypes: true });
1321
+ const entries = fs8.readdirSync(tmpDir, { withFileTypes: true });
1156
1322
  const dirs = entries.filter(
1157
1323
  (e) => e.isDirectory() && e.name !== "." && e.name !== ".."
1158
1324
  );
1159
1325
  if (dirs.length === 1) {
1160
- return path8.join(tmpDir, dirs[0].name);
1326
+ return path9.join(tmpDir, dirs[0].name);
1161
1327
  }
1162
1328
  return tmpDir;
1163
1329
  }
@@ -1212,14 +1378,14 @@ async function resolveContentSourceForVersion(version, opts = {}) {
1212
1378
  return source;
1213
1379
  }
1214
1380
  function findLatestCache() {
1215
- const cacheDir = path8.join(os2.homedir(), ".arcane", "cache");
1216
- if (!fs7.existsSync(cacheDir)) return null;
1217
- const entries = fs7.readdirSync(cacheDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => {
1218
- const metaPath = path8.join(cacheDir, e.name, ".cache-meta.json");
1381
+ const cacheDir = path9.join(os2.homedir(), ".arcane", "cache");
1382
+ if (!fs8.existsSync(cacheDir)) return null;
1383
+ const entries = fs8.readdirSync(cacheDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => {
1384
+ const metaPath = path9.join(cacheDir, e.name, ".cache-meta.json");
1219
1385
  let cachedAt = 0;
1220
- if (fs7.existsSync(metaPath)) {
1386
+ if (fs8.existsSync(metaPath)) {
1221
1387
  try {
1222
- const meta = JSON.parse(fs7.readFileSync(metaPath, "utf-8"));
1388
+ const meta = JSON.parse(fs8.readFileSync(metaPath, "utf-8"));
1223
1389
  cachedAt = new Date(meta.cached_at).getTime();
1224
1390
  } catch {
1225
1391
  cachedAt = 0;
@@ -1231,22 +1397,22 @@ function findLatestCache() {
1231
1397
  }
1232
1398
 
1233
1399
  // src/registry.ts
1234
- import path9 from "path";
1400
+ import path10 from "path";
1235
1401
  import os3 from "os";
1236
1402
  var REGISTRY_VERSION = 1;
1237
1403
  function arcaneHome() {
1238
- return process.env.ARCANE_HOME ?? path9.join(os3.homedir(), ".arcane");
1404
+ return process.env.ARCANE_HOME ?? path10.join(os3.homedir(), ".arcane");
1239
1405
  }
1240
1406
  function registryPath() {
1241
- return path9.join(arcaneHome(), "installations.json");
1407
+ return path10.join(arcaneHome(), "installations.json");
1242
1408
  }
1243
1409
  function readRegistryFile() {
1244
- const p = registryPath();
1245
- if (!fileExists(p)) {
1410
+ const p2 = registryPath();
1411
+ if (!fileExists(p2)) {
1246
1412
  return { version: REGISTRY_VERSION, installations: [] };
1247
1413
  }
1248
1414
  try {
1249
- const data = readJsonSync(p);
1415
+ const data = readJsonSync(p2);
1250
1416
  return {
1251
1417
  version: REGISTRY_VERSION,
1252
1418
  installations: Array.isArray(data.installations) ? data.installations : []
@@ -1263,7 +1429,7 @@ function writeRegistryFile(registry) {
1263
1429
  }
1264
1430
  }
1265
1431
  function registerInstallation(target) {
1266
- const abs = path9.resolve(target);
1432
+ const abs = path10.resolve(target);
1267
1433
  const registry = readRegistryFile();
1268
1434
  if (registry.installations.some((e) => e.path === abs)) {
1269
1435
  return;
@@ -1290,39 +1456,42 @@ async function installCommand(profileExpr, opts) {
1290
1456
  quiet: !profileExpr
1291
1457
  });
1292
1458
  const root = await source.getContentRoot();
1293
- const profilesDir = path10.join(root, "profiles");
1294
- const target = path10.resolve(opts.target ?? process.cwd());
1459
+ const profilesDir = path11.join(root, "profiles");
1460
+ const target = path11.resolve(opts.target ?? process.cwd());
1295
1461
  if (!profileExpr) {
1296
1462
  const profiles = listProfiles(profilesDir);
1297
- const bases = profiles.filter((p) => p.type === "base");
1298
- const addons = profiles.filter((p) => p.type === "addon");
1299
- console.log(chalk.bold("\nAvailable profiles:\n"));
1300
- console.log(chalk.cyan(" Base (pick one):"));
1301
- for (const p of bases) {
1302
- console.log(` ${chalk.green(p.name.padEnd(20))} ${p.description}`);
1303
- }
1304
- console.log(chalk.cyan("\n Addons (combine with +):"));
1305
- for (const p of addons) {
1463
+ if (process.stdin.isTTY && process.stdout.isTTY) {
1464
+ const selected = await runInstallWizard(profiles, detectStack(target));
1465
+ if (!selected) return;
1466
+ profileExpr = selected.join("+");
1467
+ } else {
1468
+ console.log(chalk2.bold("\nAvailable profiles (combine freely with +):\n"));
1469
+ for (const group of groupByCategory(profiles)) {
1470
+ console.log(chalk2.cyan(` ${group.label}:`));
1471
+ for (const p2 of group.profiles) {
1472
+ console.log(
1473
+ ` ${chalk2.green(p2.name.padEnd(20))} ${p2.description}`
1474
+ );
1475
+ }
1476
+ console.log();
1477
+ }
1306
1478
  console.log(
1307
- ` ${chalk.green(("+" + p.name).padEnd(20))} ${p.description}`
1479
+ chalk2.dim(" Usage: npx claude-code-arcane install backend-ts+agile+testing\n")
1308
1480
  );
1481
+ return;
1309
1482
  }
1310
- console.log(
1311
- chalk.dim("\n Usage: npx claude-code-arcane install backend-ts+agile+testing\n")
1312
- );
1313
- return;
1314
1483
  }
1315
1484
  const profileNames = profileExpr.split("+").filter(Boolean);
1316
1485
  console.log(
1317
- chalk.bold(`
1318
- Installing profile: ${chalk.cyan(profileExpr)}`)
1486
+ chalk2.bold(`
1487
+ Installing profile: ${chalk2.cyan(profileExpr)}`)
1319
1488
  );
1320
1489
  console.log(` Target: ${target}`);
1321
1490
  const wtInfo = getWorktreeInfo(target);
1322
1491
  let shareFrom = opts.shareFrom;
1323
1492
  let worktreeMeta;
1324
1493
  if (wtInfo?.isWorktree) {
1325
- console.log(chalk.blue(` Worktree: yes (main: ${wtInfo.mainWorktreePath})`));
1494
+ console.log(chalk2.blue(` Worktree: yes (main: ${wtInfo.mainWorktreePath})`));
1326
1495
  worktreeMeta = {
1327
1496
  is_worktree: true,
1328
1497
  main_worktree: wtInfo.mainWorktreePath
@@ -1330,13 +1499,13 @@ Installing profile: ${chalk.cyan(profileExpr)}`)
1330
1499
  if (!shareFrom && findMainArcaneInstall(wtInfo.mainWorktreePath)) {
1331
1500
  shareFrom = wtInfo.mainWorktreePath;
1332
1501
  console.log(
1333
- chalk.blue(" Sharing: hooks + docs from main worktree")
1502
+ chalk2.blue(" Sharing: hooks + docs from main worktree")
1334
1503
  );
1335
1504
  }
1336
1505
  } else if (wtInfo) {
1337
1506
  worktreeMeta = { is_worktree: false, main_worktree: target };
1338
1507
  }
1339
- if (opts.dryRun) console.log(chalk.yellow(" Mode: dry-run\n"));
1508
+ if (opts.dryRun) console.log(chalk2.yellow(" Mode: dry-run\n"));
1340
1509
  const merged = mergeProfiles(profilesDir, profileNames);
1341
1510
  console.log(` Profiles loaded: ${merged.loaded.join(", ")}`);
1342
1511
  console.log(` Skills: ${merged.skills.length}`);
@@ -1354,14 +1523,14 @@ Installing profile: ${chalk.cyan(profileExpr)}`)
1354
1523
  installer.run(profileExpr, worktreeMeta);
1355
1524
  if (!opts.dryRun) {
1356
1525
  registerInstallation(target);
1357
- console.log(chalk.green("\n Installation complete."));
1526
+ console.log(chalk2.green("\n Installation complete."));
1358
1527
  }
1359
1528
  }
1360
1529
 
1361
1530
  // src/commands/add.ts
1362
- import fs8 from "fs";
1363
- import path11 from "path";
1364
- import chalk2 from "chalk";
1531
+ import fs9 from "fs";
1532
+ import path12 from "path";
1533
+ import chalk3 from "chalk";
1365
1534
  async function addCommand(items) {
1366
1535
  const target = process.cwd();
1367
1536
  const source = await resolveContentSource({ quiet: true });
@@ -1369,13 +1538,13 @@ async function addCommand(items) {
1369
1538
  const manifest = readManifest(target);
1370
1539
  if (!manifest) {
1371
1540
  console.error(
1372
- chalk2.red(
1541
+ chalk3.red(
1373
1542
  "No arcane-manifest.json found. Run `npx claude-code-arcane install` first."
1374
1543
  )
1375
1544
  );
1376
1545
  process.exit(1);
1377
1546
  }
1378
- const claudeDir = path11.join(target, ".claude");
1547
+ const claudeDir = path12.join(target, ".claude");
1379
1548
  const added = [];
1380
1549
  const skipped = [];
1381
1550
  const notFound = [];
@@ -1385,9 +1554,9 @@ async function addCommand(items) {
1385
1554
  for (const item of items) {
1386
1555
  if (item.startsWith("+")) {
1387
1556
  const profileName = item.slice(1);
1388
- const profilePath = path11.join(root, "profiles", `${profileName}.yaml`);
1389
- if (!fs8.existsSync(profilePath)) {
1390
- console.error(chalk2.red(` Profile '${profileName}' not found`));
1557
+ const profilePath = path12.join(root, "profiles", `${profileName}.yaml`);
1558
+ if (!fs9.existsSync(profilePath)) {
1559
+ console.error(chalk3.red(` Profile '${profileName}' not found`));
1391
1560
  continue;
1392
1561
  }
1393
1562
  const profile = parseProfile(profilePath);
@@ -1399,10 +1568,10 @@ async function addCommand(items) {
1399
1568
  }
1400
1569
  for (const rule of profile.rules.universal) {
1401
1570
  if (!manifest.installed_rules.includes(rule)) {
1402
- const src = path11.join(root, "rules", `${rule}.md`);
1403
- if (fs8.existsSync(src)) {
1404
- ensureDir(path11.join(claudeDir, "rules"));
1405
- fs8.copyFileSync(src, path11.join(claudeDir, "rules", `${rule}.md`));
1571
+ const src = path12.join(root, "rules", `${rule}.md`);
1572
+ if (fs9.existsSync(src)) {
1573
+ ensureDir(path12.join(claudeDir, "rules"));
1574
+ fs9.copyFileSync(src, path12.join(claudeDir, "rules", `${rule}.md`));
1406
1575
  manifest.installed_rules.push(rule);
1407
1576
  addedRules.push(rule);
1408
1577
  }
@@ -1410,10 +1579,10 @@ async function addCommand(items) {
1410
1579
  }
1411
1580
  for (const rule of profile.rules.gamedev) {
1412
1581
  if (!manifest.installed_rules.includes(rule)) {
1413
- const src = path11.join(root, "rules", "gamedev", `${rule}.md`);
1414
- if (fs8.existsSync(src)) {
1415
- ensureDir(path11.join(claudeDir, "rules"));
1416
- fs8.copyFileSync(src, path11.join(claudeDir, "rules", `${rule}.md`));
1582
+ const src = path12.join(root, "rules", "gamedev", `${rule}.md`);
1583
+ if (fs9.existsSync(src)) {
1584
+ ensureDir(path12.join(claudeDir, "rules"));
1585
+ fs9.copyFileSync(src, path12.join(claudeDir, "rules", `${rule}.md`));
1417
1586
  manifest.installed_rules.push(rule);
1418
1587
  addedRules.push(rule);
1419
1588
  }
@@ -1421,9 +1590,9 @@ async function addCommand(items) {
1421
1590
  }
1422
1591
  for (const agentDir of profile.agents) {
1423
1592
  if (!manifest.installed_agents.includes(agentDir)) {
1424
- const src = path11.join(root, "agents", agentDir);
1425
- if (fs8.existsSync(src)) {
1426
- const dst = path11.join(claudeDir, "agents", agentDir);
1593
+ const src = path12.join(root, "agents", agentDir);
1594
+ if (fs9.existsSync(src)) {
1595
+ const dst = path12.join(claudeDir, "agents", agentDir);
1427
1596
  copyDirSync(src, dst);
1428
1597
  manifest.installed_agents.push(agentDir);
1429
1598
  addedAgents.push(agentDir);
@@ -1431,9 +1600,9 @@ async function addCommand(items) {
1431
1600
  }
1432
1601
  }
1433
1602
  if (profileName === "statusline") {
1434
- const statuslineSrc = path11.join(root, "hooks", "statusline.sh");
1435
- if (fs8.existsSync(statuslineSrc)) {
1436
- fs8.copyFileSync(statuslineSrc, path11.join(claudeDir, "statusline.sh"));
1603
+ const statuslineSrc = path12.join(root, "hooks", "statusline.sh");
1604
+ if (fs9.existsSync(statuslineSrc)) {
1605
+ fs9.copyFileSync(statuslineSrc, path12.join(claudeDir, "statusline.sh"));
1437
1606
  statuslineAdded = true;
1438
1607
  }
1439
1608
  }
@@ -1442,7 +1611,7 @@ async function addCommand(items) {
1442
1611
  }
1443
1612
  if (!manifest.profiles.includes(profileName)) {
1444
1613
  manifest.profiles.push(profileName);
1445
- manifest.profile_command = manifest.profiles.filter((p) => p !== "core").join("+");
1614
+ manifest.profile_command = manifest.profiles.filter((p2) => p2 !== "core").join("+");
1446
1615
  }
1447
1616
  if (statuslineAdded) {
1448
1617
  addStatuslineToSettings(claudeDir);
@@ -1467,28 +1636,28 @@ async function addCommand(items) {
1467
1636
  };
1468
1637
  writeManifest(target, merged, manifest.profile_command, root);
1469
1638
  const totalAdded = added.length + addedRules.length + addedAgents.length + (statuslineAdded ? 1 : 0);
1470
- console.log(chalk2.bold(`
1639
+ console.log(chalk3.bold(`
1471
1640
  Added ${totalAdded} items:`));
1472
- for (const s of added) console.log(chalk2.green(` [ok] skill: ${s}`));
1473
- for (const r of addedRules) console.log(chalk2.green(` [ok] rule: ${r}`));
1474
- for (const a of addedAgents) console.log(chalk2.green(` [ok] agents: ${a}/`));
1475
- if (statuslineAdded) console.log(chalk2.green(" [ok] statusline.sh"));
1641
+ for (const s of added) console.log(chalk3.green(` [ok] skill: ${s}`));
1642
+ for (const r of addedRules) console.log(chalk3.green(` [ok] rule: ${r}`));
1643
+ for (const a of addedAgents) console.log(chalk3.green(` [ok] agents: ${a}/`));
1644
+ if (statuslineAdded) console.log(chalk3.green(" [ok] statusline.sh"));
1476
1645
  for (const s of skipped)
1477
- console.log(chalk2.dim(` [skip] ${s} (already installed)`));
1646
+ console.log(chalk3.dim(` [skip] ${s} (already installed)`));
1478
1647
  for (const s of notFound)
1479
- console.log(chalk2.red(` [miss] ${s} (not found in source)`));
1648
+ console.log(chalk3.red(` [miss] ${s} (not found in source)`));
1480
1649
  }
1481
1650
  function addSkill(root, target, skill, installed) {
1482
1651
  if (installed.includes(skill)) return "skipped";
1483
- const src = path11.join(root, "skills", skill);
1484
- if (!fs8.existsSync(src)) return "not-found";
1485
- const dst = path11.join(target, ".claude", "skills", skill);
1652
+ const src = path12.join(root, "skills", skill);
1653
+ if (!fs9.existsSync(src)) return "not-found";
1654
+ const dst = path12.join(target, ".claude", "skills", skill);
1486
1655
  copyDirSync(src, dst);
1487
1656
  return "added";
1488
1657
  }
1489
1658
  function mergePermissions(claudeDir, newPerms) {
1490
- const settingsPath = path11.join(claudeDir, "settings.json");
1491
- if (!fs8.existsSync(settingsPath)) return;
1659
+ const settingsPath = path12.join(claudeDir, "settings.json");
1660
+ if (!fs9.existsSync(settingsPath)) return;
1492
1661
  const settings = readJsonSync(settingsPath);
1493
1662
  const perms = settings.permissions ?? { allow: [], deny: [] };
1494
1663
  const allowSet = new Set(perms.allow);
@@ -1501,8 +1670,8 @@ function mergePermissions(claudeDir, newPerms) {
1501
1670
  writeJsonSync(settingsPath, settings);
1502
1671
  }
1503
1672
  function addStatuslineToSettings(claudeDir) {
1504
- const settingsPath = path11.join(claudeDir, "settings.json");
1505
- if (!fs8.existsSync(settingsPath)) return;
1673
+ const settingsPath = path12.join(claudeDir, "settings.json");
1674
+ if (!fs9.existsSync(settingsPath)) return;
1506
1675
  const settings = readJsonSync(settingsPath);
1507
1676
  if (settings.statusLine) return;
1508
1677
  settings.statusLine = {
@@ -1513,9 +1682,9 @@ function addStatuslineToSettings(claudeDir) {
1513
1682
  }
1514
1683
 
1515
1684
  // src/commands/remove.ts
1516
- import fs9 from "fs";
1517
- import path12 from "path";
1518
- import chalk3 from "chalk";
1685
+ import fs10 from "fs";
1686
+ import path13 from "path";
1687
+ import chalk4 from "chalk";
1519
1688
  var CORE_SKILLS = [
1520
1689
  "commit",
1521
1690
  "create-pr",
@@ -1544,7 +1713,7 @@ async function removeCommand(items, opts = {}) {
1544
1713
  const manifest = readManifest(target);
1545
1714
  if (!manifest) {
1546
1715
  console.error(
1547
- chalk3.red(
1716
+ chalk4.red(
1548
1717
  "No arcane-manifest.json found. Run `npx claude-code-arcane install` first."
1549
1718
  )
1550
1719
  );
@@ -1579,30 +1748,30 @@ async function removeCommand(items, opts = {}) {
1579
1748
  }
1580
1749
  manifest.total_skills = manifest.installed_skills.length;
1581
1750
  manifest.total_rules = manifest.installed_rules.length;
1582
- const mp = path12.join(target, ".claude", "arcane-manifest.json");
1751
+ const mp = path13.join(target, ".claude", "arcane-manifest.json");
1583
1752
  writeJsonSync(mp, manifest);
1584
1753
  const totalRemoved = removedSkills.length + removedAgents.length + removedProfiles.length;
1585
- console.log(chalk3.bold(`
1754
+ console.log(chalk4.bold(`
1586
1755
  Removed ${totalRemoved} items:`));
1587
- for (const p of removedProfiles)
1588
- console.log(chalk3.magenta(` [ok] +${p} (profile)`));
1756
+ for (const p2 of removedProfiles)
1757
+ console.log(chalk4.magenta(` [ok] +${p2} (profile)`));
1589
1758
  for (const s of removedSkills)
1590
- console.log(chalk3.green(` [ok] ${s} (skill)`));
1759
+ console.log(chalk4.green(` [ok] ${s} (skill)`));
1591
1760
  for (const a of removedAgents)
1592
- console.log(chalk3.cyan(` [ok] ${a} (agent dir)`));
1761
+ console.log(chalk4.cyan(` [ok] ${a} (agent dir)`));
1593
1762
  for (const s of skipped)
1594
- console.log(chalk3.dim(` [skip] ${s}`));
1763
+ console.log(chalk4.dim(` [skip] ${s}`));
1595
1764
  }
1596
1765
  function removeSkill(target, skill, manifest) {
1597
1766
  if (CORE_SKILLS.includes(skill)) {
1598
1767
  console.warn(
1599
- chalk3.yellow(` WARNING: '${skill}' is a core skill. Skipping.`)
1768
+ chalk4.yellow(` WARNING: '${skill}' is a core skill. Skipping.`)
1600
1769
  );
1601
1770
  return "skipped";
1602
1771
  }
1603
- const skillDir = path12.join(target, ".claude", "skills", skill);
1604
- if (!fs9.existsSync(skillDir)) return "skipped";
1605
- fs9.rmSync(skillDir, { recursive: true, force: true });
1772
+ const skillDir = path13.join(target, ".claude", "skills", skill);
1773
+ if (!fs10.existsSync(skillDir)) return "skipped";
1774
+ fs10.rmSync(skillDir, { recursive: true, force: true });
1606
1775
  manifest.installed_skills = manifest.installed_skills.filter(
1607
1776
  (s) => s !== skill
1608
1777
  );
@@ -1611,33 +1780,33 @@ function removeSkill(target, skill, manifest) {
1611
1780
  function removeProfile(root, target, profileName, manifest) {
1612
1781
  if (profileName === "core") {
1613
1782
  console.warn(
1614
- chalk3.yellow(" WARNING: Cannot remove core profile. Skipping.")
1783
+ chalk4.yellow(" WARNING: Cannot remove core profile. Skipping.")
1615
1784
  );
1616
1785
  return { removed: false, skills: [], agents: [] };
1617
1786
  }
1618
1787
  if (!manifest.profiles.includes(profileName)) {
1619
1788
  console.warn(
1620
- chalk3.yellow(` WARNING: Profile '${profileName}' is not installed.`)
1789
+ chalk4.yellow(` WARNING: Profile '${profileName}' is not installed.`)
1621
1790
  );
1622
1791
  return { removed: false, skills: [], agents: [] };
1623
1792
  }
1624
- const profilePath = path12.join(root, "profiles", `${profileName}.yaml`);
1625
- if (!fs9.existsSync(profilePath)) {
1793
+ const profilePath = path13.join(root, "profiles", `${profileName}.yaml`);
1794
+ if (!fs10.existsSync(profilePath)) {
1626
1795
  console.warn(
1627
- chalk3.yellow(` WARNING: Profile '${profileName}.yaml' not found.`)
1796
+ chalk4.yellow(` WARNING: Profile '${profileName}.yaml' not found.`)
1628
1797
  );
1629
1798
  return { removed: false, skills: [], agents: [] };
1630
1799
  }
1631
1800
  const profile = parseProfile(profilePath);
1632
1801
  const remainingProfiles = manifest.profiles.filter(
1633
- (p) => p !== profileName
1802
+ (p2) => p2 !== profileName
1634
1803
  );
1635
1804
  const sharedSkills = /* @__PURE__ */ new Set();
1636
1805
  const sharedAgents = /* @__PURE__ */ new Set();
1637
1806
  const sharedRules = /* @__PURE__ */ new Set();
1638
1807
  for (const rp of remainingProfiles) {
1639
- const rpPath = path12.join(root, "profiles", `${rp}.yaml`);
1640
- if (!fs9.existsSync(rpPath)) continue;
1808
+ const rpPath = path13.join(root, "profiles", `${rp}.yaml`);
1809
+ if (!fs10.existsSync(rpPath)) continue;
1641
1810
  const rpDef = parseProfile(rpPath);
1642
1811
  rpDef.skills.forEach((s) => sharedSkills.add(s));
1643
1812
  rpDef.agents.forEach((a) => sharedAgents.add(a));
@@ -1648,9 +1817,9 @@ function removeProfile(root, target, profileName, manifest) {
1648
1817
  for (const skill of profile.skills) {
1649
1818
  if (sharedSkills.has(skill)) continue;
1650
1819
  if (CORE_SKILLS.includes(skill)) continue;
1651
- const skillDir = path12.join(target, ".claude", "skills", skill);
1652
- if (fs9.existsSync(skillDir)) {
1653
- fs9.rmSync(skillDir, { recursive: true, force: true });
1820
+ const skillDir = path13.join(target, ".claude", "skills", skill);
1821
+ if (fs10.existsSync(skillDir)) {
1822
+ fs10.rmSync(skillDir, { recursive: true, force: true });
1654
1823
  removedSkills.push(skill);
1655
1824
  }
1656
1825
  manifest.installed_skills = manifest.installed_skills.filter(
@@ -1660,9 +1829,9 @@ function removeProfile(root, target, profileName, manifest) {
1660
1829
  const removedAgents = [];
1661
1830
  for (const agent of profile.agents) {
1662
1831
  if (sharedAgents.has(agent)) continue;
1663
- const agentDir = path12.join(target, ".claude", "agents", agent);
1664
- if (fs9.existsSync(agentDir)) {
1665
- fs9.rmSync(agentDir, { recursive: true, force: true });
1832
+ const agentDir = path13.join(target, ".claude", "agents", agent);
1833
+ if (fs10.existsSync(agentDir)) {
1834
+ fs10.rmSync(agentDir, { recursive: true, force: true });
1666
1835
  removedAgents.push(agent);
1667
1836
  }
1668
1837
  manifest.installed_agents = manifest.installed_agents.filter(
@@ -1674,138 +1843,133 @@ function removeProfile(root, target, profileName, manifest) {
1674
1843
  ...profile.rules.gamedev
1675
1844
  ]) {
1676
1845
  if (sharedRules.has(rule)) continue;
1677
- const ruleFile = path12.join(target, ".claude", "rules", `${rule}.md`);
1678
- if (fs9.existsSync(ruleFile)) {
1679
- fs9.rmSync(ruleFile);
1846
+ const ruleFile = path13.join(target, ".claude", "rules", `${rule}.md`);
1847
+ if (fs10.existsSync(ruleFile)) {
1848
+ fs10.rmSync(ruleFile);
1680
1849
  }
1681
1850
  manifest.installed_rules = manifest.installed_rules.filter(
1682
1851
  (r) => r !== rule
1683
1852
  );
1684
1853
  }
1685
1854
  if (profileName === "statusline") {
1686
- const statuslineFile = path12.join(target, ".claude", "statusline.sh");
1687
- if (fs9.existsSync(statuslineFile)) {
1688
- fs9.rmSync(statuslineFile);
1855
+ const statuslineFile = path13.join(target, ".claude", "statusline.sh");
1856
+ if (fs10.existsSync(statuslineFile)) {
1857
+ fs10.rmSync(statuslineFile);
1689
1858
  }
1690
- removeStatuslineFromSettings(path12.join(target, ".claude"));
1859
+ removeStatuslineFromSettings(path13.join(target, ".claude"));
1691
1860
  }
1692
1861
  manifest.profiles = remainingProfiles;
1693
- manifest.profile_command = remainingProfiles.filter((p) => p !== "core").join("+");
1862
+ manifest.profile_command = remainingProfiles.filter((p2) => p2 !== "core").join("+");
1694
1863
  return { removed: true, skills: removedSkills, agents: removedAgents };
1695
1864
  }
1696
1865
  function removeStatuslineFromSettings(claudeDir) {
1697
- const settingsPath = path12.join(claudeDir, "settings.json");
1698
- if (!fs9.existsSync(settingsPath)) return;
1699
- const settings = JSON.parse(fs9.readFileSync(settingsPath, "utf-8"));
1866
+ const settingsPath = path13.join(claudeDir, "settings.json");
1867
+ if (!fs10.existsSync(settingsPath)) return;
1868
+ const settings = JSON.parse(fs10.readFileSync(settingsPath, "utf-8"));
1700
1869
  if (settings.statusLine) {
1701
1870
  delete settings.statusLine;
1702
- fs9.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
1871
+ fs10.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
1703
1872
  }
1704
1873
  }
1705
1874
 
1706
1875
  // src/commands/list.ts
1707
- import fs10 from "fs";
1708
- import path13 from "path";
1709
- import chalk4 from "chalk";
1876
+ import fs11 from "fs";
1877
+ import path14 from "path";
1878
+ import chalk5 from "chalk";
1710
1879
  async function listCommand() {
1711
1880
  const source = await resolveContentSource({ quiet: true });
1712
1881
  const root = await source.getContentRoot();
1713
- const profilesDir = path13.join(root, "profiles");
1714
- const skillsDir = path13.join(root, "skills");
1882
+ const profilesDir = path14.join(root, "profiles");
1883
+ const skillsDir = path14.join(root, "skills");
1715
1884
  const target = process.cwd();
1716
1885
  const manifest = readManifest(target);
1717
1886
  const profiles = listProfiles(profilesDir);
1718
- const bases = profiles.filter((p) => p.type === "base");
1719
- const addons = profiles.filter((p) => p.type === "addon");
1720
1887
  const installedSkills = new Set(manifest?.installed_skills ?? []);
1721
- console.log(chalk4.bold("\n=== Available Profiles ===\n"));
1722
- console.log(chalk4.cyan("Base:"));
1723
- for (const p of bases) {
1724
- const tag = manifest?.profiles.includes(p.name) ? chalk4.green(" [installed]") : "";
1725
- console.log(
1726
- ` ${chalk4.green(p.name.padEnd(20))} ${p.description}${tag}`
1727
- );
1728
- }
1729
- console.log(chalk4.cyan("\nAddons:"));
1730
- for (const p of addons) {
1731
- const tag = manifest?.profiles.includes(p.name) ? chalk4.green(" [installed]") : "";
1732
- console.log(
1733
- ` ${chalk4.green(("+" + p.name).padEnd(20))} ${p.description}${tag}`
1734
- );
1888
+ console.log(chalk5.bold("\n=== Available Profiles ===\n"));
1889
+ console.log(chalk5.dim("Profiles combine freely: install a+b+c\n"));
1890
+ for (const group of groupByCategory(profiles)) {
1891
+ console.log(chalk5.cyan(`${group.label}:`));
1892
+ for (const p2 of group.profiles) {
1893
+ const tag = manifest?.profiles.includes(p2.name) ? chalk5.green(" [installed]") : "";
1894
+ console.log(
1895
+ ` ${chalk5.green(p2.name.padEnd(20))} ${p2.description}${tag}`
1896
+ );
1897
+ }
1898
+ console.log();
1735
1899
  }
1736
- if (fs10.existsSync(skillsDir)) {
1737
- const allSkills = fs10.readdirSync(skillsDir, { withFileTypes: true }).filter(
1738
- (d) => d.isDirectory() && !d.name.startsWith("_") && fs10.existsSync(path13.join(skillsDir, d.name, "SKILL.md"))
1900
+ if (fs11.existsSync(skillsDir)) {
1901
+ const allSkills = fs11.readdirSync(skillsDir, { withFileTypes: true }).filter(
1902
+ (d) => d.isDirectory() && !d.name.startsWith("_") && fs11.existsSync(path14.join(skillsDir, d.name, "SKILL.md"))
1739
1903
  ).map((d) => d.name).sort();
1740
- console.log(chalk4.bold(`
1904
+ console.log(chalk5.bold(`
1741
1905
  === Skills (${allSkills.length}) ===
1742
1906
  `));
1743
1907
  const cols = 3;
1744
1908
  for (let i = 0; i < allSkills.length; i += cols) {
1745
1909
  const row = allSkills.slice(i, i + cols).map((s) => {
1746
- const tag = installedSkills.has(s) ? chalk4.green("*") : " ";
1910
+ const tag = installedSkills.has(s) ? chalk5.green("*") : " ";
1747
1911
  return `${tag} ${s.padEnd(30)}`;
1748
1912
  });
1749
1913
  console.log(" " + row.join(""));
1750
1914
  }
1751
1915
  if (manifest) {
1752
- console.log(chalk4.dim("\n * = installed in current project"));
1916
+ console.log(chalk5.dim("\n * = installed in current project"));
1753
1917
  }
1754
1918
  }
1755
1919
  }
1756
1920
 
1757
1921
  // src/commands/status.ts
1758
- import chalk5 from "chalk";
1922
+ import chalk6 from "chalk";
1759
1923
  async function statusCommand() {
1760
1924
  const target = process.cwd();
1761
1925
  const manifest = readManifest(target);
1762
1926
  if (!manifest) {
1763
1927
  console.log(
1764
- chalk5.yellow("No Arcane installation found in this project.")
1928
+ chalk6.yellow("No Arcane installation found in this project.")
1765
1929
  );
1766
- console.log(chalk5.dim("Run `npx claude-code-arcane install <profile>` to install."));
1930
+ console.log(chalk6.dim("Run `npx claude-code-arcane install <profile>` to install."));
1767
1931
  return;
1768
1932
  }
1769
- const profiles = manifest.profiles.filter((p) => p !== "core");
1770
- console.log(chalk5.bold("\n=== Arcane Status ===\n"));
1771
- console.log(` Profiles: ${chalk5.cyan(profiles.join(" + "))}`);
1933
+ const profiles = manifest.profiles.filter((p2) => p2 !== "core");
1934
+ console.log(chalk6.bold("\n=== Arcane Status ===\n"));
1935
+ console.log(` Profiles: ${chalk6.cyan(profiles.join(" + "))}`);
1772
1936
  console.log(` Installed: ${manifest.installed_at}`);
1773
1937
  console.log(` Version: ${manifest.arcane_version}`);
1774
1938
  console.log(` CLI: ${manifest.cli}`);
1775
- console.log(` Source: ${chalk5.dim(manifest.source)}`);
1939
+ console.log(` Source: ${chalk6.dim(manifest.source)}`);
1776
1940
  if (manifest.worktree) {
1777
1941
  const wt = manifest.worktree;
1778
1942
  console.log(
1779
- ` Worktree: ${wt.is_worktree ? chalk5.blue("yes") : "no"} (main: ${chalk5.dim(wt.main_worktree)})`
1943
+ ` Worktree: ${wt.is_worktree ? chalk6.blue("yes") : "no"} (main: ${chalk6.dim(wt.main_worktree)})`
1780
1944
  );
1781
1945
  if (wt.shared_dirs.length > 0) {
1782
1946
  console.log(
1783
- ` Shared: ${chalk5.blue(wt.shared_dirs.join(", "))} (symlinked)`
1947
+ ` Shared: ${chalk6.blue(wt.shared_dirs.join(", "))} (symlinked)`
1784
1948
  );
1785
1949
  }
1786
1950
  }
1787
- console.log(chalk5.bold(`
1951
+ console.log(chalk6.bold(`
1788
1952
  Skills (${manifest.total_skills}):`));
1789
1953
  const skills = manifest.installed_skills ?? [];
1790
1954
  for (let i = 0; i < skills.length; i += 5) {
1791
1955
  const row = skills.slice(i, i + 5).map((s) => s.padEnd(25)).join("");
1792
1956
  console.log(` ${row}`);
1793
1957
  }
1794
- console.log(chalk5.bold(`
1958
+ console.log(chalk6.bold(`
1795
1959
  Rules (${manifest.total_rules}):`));
1796
1960
  const rules = manifest.installed_rules ?? [];
1797
1961
  console.log(` ${rules.join(", ")}`);
1798
- console.log(chalk5.bold("\n Agents:"));
1962
+ console.log(chalk6.bold("\n Agents:"));
1799
1963
  const agents = manifest.installed_agents ?? [];
1800
1964
  console.log(` ${agents.join(", ")}`);
1801
1965
  console.log();
1802
1966
  }
1803
1967
 
1804
1968
  // src/commands/update.ts
1805
- import fs11 from "fs";
1806
- import path14 from "path";
1969
+ import fs12 from "fs";
1970
+ import path15 from "path";
1807
1971
  import os4 from "os";
1808
- import chalk6 from "chalk";
1972
+ import chalk7 from "chalk";
1809
1973
 
1810
1974
  // src/self-update.ts
1811
1975
  import { spawnSync } from "child_process";
@@ -1867,45 +2031,45 @@ async function updateCommand(opts) {
1867
2031
  });
1868
2032
  if (!opts.quiet) {
1869
2033
  if (self.updated) {
1870
- console.log(chalk6.green("npm package updated to latest."));
2034
+ console.log(chalk7.green("npm package updated to latest."));
1871
2035
  } else if (self.skipped && self.reason !== "dev/test environment") {
1872
- console.log(chalk6.dim(`npm self-update skipped (${self.reason}).`));
2036
+ console.log(chalk7.dim(`npm self-update skipped (${self.reason}).`));
1873
2037
  } else if (!self.skipped) {
1874
- console.log(chalk6.yellow(`npm self-update failed: ${self.reason}`));
2038
+ console.log(chalk7.yellow(`npm self-update failed: ${self.reason}`));
1875
2039
  }
1876
2040
  }
1877
2041
  const cwd = process.cwd();
1878
- if (fs11.existsSync(manifestPath(cwd))) {
2042
+ if (fs12.existsSync(manifestPath(cwd))) {
1879
2043
  registerInstallation(cwd);
1880
2044
  }
1881
2045
  const targets = pruneRegistry().map((e) => e.path);
1882
2046
  const globalTarget = os4.homedir();
1883
- if (!process.env.VITEST && fs11.existsSync(manifestPath(globalTarget)) && !targets.includes(globalTarget)) {
2047
+ if (!process.env.VITEST && fs12.existsSync(manifestPath(globalTarget)) && !targets.includes(globalTarget)) {
1884
2048
  targets.push(globalTarget);
1885
2049
  }
1886
2050
  if (targets.length === 0) {
1887
2051
  if (!opts.quiet) {
1888
- console.log(chalk6.red("\nNo Arcane installations found. Run 'arcane install' first."));
2052
+ console.log(chalk7.red("\nNo Arcane installations found. Run 'arcane install' first."));
1889
2053
  }
1890
2054
  return;
1891
2055
  }
1892
2056
  if (!opts.quiet) {
1893
2057
  console.log(
1894
- chalk6.bold(`
2058
+ chalk7.bold(`
1895
2059
  Updating ${targets.length} installation${targets.length === 1 ? "" : "s"}...`)
1896
2060
  );
1897
2061
  }
1898
2062
  const results = [];
1899
2063
  for (const target of targets) {
1900
2064
  if (!opts.quiet) {
1901
- console.log(chalk6.bold.cyan(`
2065
+ console.log(chalk7.bold.cyan(`
1902
2066
  \u2022 ${target}`));
1903
2067
  }
1904
2068
  try {
1905
2069
  results.push(await updateTarget(target, opts));
1906
2070
  } catch (err) {
1907
2071
  if (!opts.quiet) {
1908
- console.log(chalk6.red(` Update failed: ${err.message}`));
2072
+ console.log(chalk7.red(` Update failed: ${err.message}`));
1909
2073
  }
1910
2074
  results.push({
1911
2075
  target,
@@ -1924,7 +2088,7 @@ async function updateTarget(target, opts) {
1924
2088
  const manifest = readManifest(target);
1925
2089
  if (!manifest) {
1926
2090
  if (!opts.quiet) {
1927
- console.log(chalk6.red("No Arcane installation found. Run 'arcane install' first."));
2091
+ console.log(chalk7.red("No Arcane installation found. Run 'arcane install' first."));
1928
2092
  }
1929
2093
  return { target, status: "no-manifest", updated: 0, skipped: 0, removed: 0 };
1930
2094
  }
@@ -1937,7 +2101,7 @@ async function updateTarget(target, opts) {
1937
2101
  const installedVersion = manifest.source_version ?? manifest.arcane_version;
1938
2102
  if (installedVersion === currentVersion && !opts.force) {
1939
2103
  if (!opts.quiet) {
1940
- console.log(chalk6.green(`Already up to date (v${currentVersion}).`));
2104
+ console.log(chalk7.green(`Already up to date (v${currentVersion}).`));
1941
2105
  }
1942
2106
  return {
1943
2107
  target,
@@ -1951,16 +2115,16 @@ async function updateTarget(target, opts) {
1951
2115
  }
1952
2116
  if (!opts.quiet) {
1953
2117
  console.log(
1954
- chalk6.bold(`
1955
- Update available: ${chalk6.red(installedVersion)} -> ${chalk6.green(currentVersion)}`)
2118
+ chalk7.bold(`
2119
+ Update available: ${chalk7.red(installedVersion)} -> ${chalk7.green(currentVersion)}`)
1956
2120
  );
1957
2121
  }
1958
2122
  const profileNames = manifest.profile_command.split("+").filter(Boolean);
1959
- const profilesDir = path14.join(root, "profiles");
2123
+ const profilesDir = path15.join(root, "profiles");
1960
2124
  const merged = mergeProfiles(profilesDir, profileNames);
1961
2125
  const allRules = [...merged.rules.universal, ...merged.rules.gamedev];
1962
2126
  const sourceHashes = computeSourceHashes(root, merged.skills, allRules, merged.agents);
1963
- const claudeDir = path14.join(target, ".claude");
2127
+ const claudeDir = path15.join(target, ".claude");
1964
2128
  const installedHashes = computeContentHashes(claudeDir);
1965
2129
  const items = computeUpdatePlan(
1966
2130
  manifest.content_hashes ?? null,
@@ -1988,7 +2152,7 @@ Update available: ${chalk6.red(installedVersion)} -> ${chalk6.green(currentVersi
1988
2152
  });
1989
2153
  }
1990
2154
  if (!opts.quiet) {
1991
- console.log(chalk6.green("\nNo changes to apply."));
2155
+ console.log(chalk7.green("\nNo changes to apply."));
1992
2156
  }
1993
2157
  return {
1994
2158
  target,
@@ -2025,7 +2189,7 @@ Update available: ${chalk6.red(installedVersion)} -> ${chalk6.green(currentVersi
2025
2189
  total_rules: allRules.length
2026
2190
  });
2027
2191
  if (!opts.quiet) {
2028
- console.log(chalk6.green(`
2192
+ console.log(chalk7.green(`
2029
2193
  Updated to v${currentVersion}.`));
2030
2194
  console.log(` ${updates.length} updated, ${skipped.length} skipped, ${removed.length} removed`);
2031
2195
  }
@@ -2040,12 +2204,12 @@ Updated to v${currentVersion}.`));
2040
2204
  };
2041
2205
  }
2042
2206
  function printGeneralSummary(results, isDryRun) {
2043
- const prefix = isDryRun ? chalk6.yellow("[dry-run] ") : "";
2207
+ const prefix = isDryRun ? chalk7.yellow("[dry-run] ") : "";
2044
2208
  const updated = results.filter((r) => r.status === "updated" || r.status === "dry-run").length;
2045
2209
  const unchanged = results.filter((r) => r.status === "up-to-date" || r.status === "no-changes").length;
2046
- console.log(chalk6.bold(`
2210
+ console.log(chalk7.bold(`
2047
2211
  ${prefix}Done. ${results.length} installation${results.length === 1 ? "" : "s"} processed:`));
2048
- console.log(` ${chalk6.green(updated)} ${isDryRun ? "would change" : "updated"}, ${chalk6.dim(unchanged + " unchanged")}`);
2212
+ console.log(` ${chalk7.green(updated)} ${isDryRun ? "would change" : "updated"}, ${chalk7.dim(unchanged + " unchanged")}`);
2049
2213
  }
2050
2214
  function computeUpdatePlan(manifestHashes, installedHashes, sourceHashes) {
2051
2215
  const items = [];
@@ -2086,7 +2250,7 @@ function resolveAction(manifestHash, installedHash, sourceHash) {
2086
2250
  return "conflict";
2087
2251
  }
2088
2252
  function printUpdateSummary(items, isDryRun) {
2089
- const prefix = isDryRun ? chalk6.yellow("[dry-run] ") : "";
2253
+ const prefix = isDryRun ? chalk7.yellow("[dry-run] ") : "";
2090
2254
  const updates = items.filter((i) => i.action === "update");
2091
2255
  const adds = items.filter((i) => i.action === "add");
2092
2256
  const conflicts = items.filter((i) => i.action === "conflict");
@@ -2094,37 +2258,37 @@ function printUpdateSummary(items, isDryRun) {
2094
2258
  const removed = items.filter((i) => i.action === "remove");
2095
2259
  if (updates.length > 0) {
2096
2260
  console.log(`
2097
- ${prefix}${chalk6.cyan("Will update:")}`);
2261
+ ${prefix}${chalk7.cyan("Will update:")}`);
2098
2262
  for (const item of updates) {
2099
- console.log(` ${chalk6.green("~")} ${item.type}: ${item.name}`);
2263
+ console.log(` ${chalk7.green("~")} ${item.type}: ${item.name}`);
2100
2264
  }
2101
2265
  }
2102
2266
  if (adds.length > 0) {
2103
2267
  console.log(`
2104
- ${prefix}${chalk6.cyan("Will add:")}`);
2268
+ ${prefix}${chalk7.cyan("Will add:")}`);
2105
2269
  for (const item of adds) {
2106
- console.log(` ${chalk6.green("+")} ${item.type}: ${item.name}`);
2270
+ console.log(` ${chalk7.green("+")} ${item.type}: ${item.name}`);
2107
2271
  }
2108
2272
  }
2109
2273
  if (removed.length > 0) {
2110
2274
  console.log(`
2111
- ${prefix}${chalk6.cyan("Will remove:")}`);
2275
+ ${prefix}${chalk7.cyan("Will remove:")}`);
2112
2276
  for (const item of removed) {
2113
- console.log(` ${chalk6.red("-")} ${item.type}: ${item.name}`);
2277
+ console.log(` ${chalk7.red("-")} ${item.type}: ${item.name}`);
2114
2278
  }
2115
2279
  }
2116
2280
  if (conflicts.length > 0) {
2117
2281
  console.log(`
2118
- ${prefix}${chalk6.yellow("Conflicts (will backup + update):")}`);
2282
+ ${prefix}${chalk7.yellow("Conflicts (will backup + update):")}`);
2119
2283
  for (const item of conflicts) {
2120
- console.log(` ${chalk6.yellow("!")} ${item.type}: ${item.name}`);
2284
+ console.log(` ${chalk7.yellow("!")} ${item.type}: ${item.name}`);
2121
2285
  }
2122
2286
  }
2123
2287
  if (customized.length > 0) {
2124
2288
  console.log(`
2125
- ${prefix}${chalk6.dim("Skipped (locally customized):")}`);
2289
+ ${prefix}${chalk7.dim("Skipped (locally customized):")}`);
2126
2290
  for (const item of customized) {
2127
- console.log(` ${chalk6.dim(" " + item.type + ": " + item.name)}`);
2291
+ console.log(` ${chalk7.dim(" " + item.type + ": " + item.name)}`);
2128
2292
  }
2129
2293
  }
2130
2294
  }
@@ -2153,48 +2317,48 @@ function applyUpdates(items, root, claudeDir, merged, force) {
2153
2317
  function applyItem(item, root, claudeDir) {
2154
2318
  const src = getSourcePath(item, root);
2155
2319
  const dst = getInstalledPath(item, claudeDir);
2156
- if (!src || !fs11.existsSync(src)) return;
2320
+ if (!src || !fs12.existsSync(src)) return;
2157
2321
  if (item.type === "skill" || item.type === "agent") {
2158
- if (fs11.existsSync(dst)) {
2159
- fs11.rmSync(dst, { recursive: true, force: true });
2322
+ if (fs12.existsSync(dst)) {
2323
+ fs12.rmSync(dst, { recursive: true, force: true });
2160
2324
  }
2161
2325
  copyDirSync(src, dst);
2162
2326
  } else {
2163
- const dir = path14.dirname(dst);
2164
- if (!fs11.existsSync(dir)) {
2165
- fs11.mkdirSync(dir, { recursive: true });
2327
+ const dir = path15.dirname(dst);
2328
+ if (!fs12.existsSync(dir)) {
2329
+ fs12.mkdirSync(dir, { recursive: true });
2166
2330
  }
2167
- fs11.copyFileSync(src, dst);
2331
+ fs12.copyFileSync(src, dst);
2168
2332
  }
2169
2333
  }
2170
2334
  function backupItem(item, claudeDir) {
2171
2335
  const installed = getInstalledPath(item, claudeDir);
2172
- if (!fs11.existsSync(installed)) return;
2336
+ if (!fs12.existsSync(installed)) return;
2173
2337
  const backupPath = installed + ".bak";
2174
- if (fs11.existsSync(backupPath)) {
2175
- fs11.rmSync(backupPath, { recursive: true, force: true });
2338
+ if (fs12.existsSync(backupPath)) {
2339
+ fs12.rmSync(backupPath, { recursive: true, force: true });
2176
2340
  }
2177
- fs11.renameSync(installed, backupPath);
2341
+ fs12.renameSync(installed, backupPath);
2178
2342
  }
2179
2343
  function removeItem(item, claudeDir) {
2180
2344
  const installed = getInstalledPath(item, claudeDir);
2181
- if (fs11.existsSync(installed)) {
2182
- fs11.rmSync(installed, { recursive: true, force: true });
2345
+ if (fs12.existsSync(installed)) {
2346
+ fs12.rmSync(installed, { recursive: true, force: true });
2183
2347
  }
2184
2348
  }
2185
2349
  function getSourcePath(item, root) {
2186
2350
  switch (item.type) {
2187
2351
  case "skill":
2188
- return path14.join(root, "skills", item.name);
2352
+ return path15.join(root, "skills", item.name);
2189
2353
  case "rule": {
2190
- const direct = path14.join(root, "rules", item.name);
2191
- if (fs11.existsSync(direct)) return direct;
2192
- return path14.join(root, "rules", "gamedev", item.name);
2354
+ const direct = path15.join(root, "rules", item.name);
2355
+ if (fs12.existsSync(direct)) return direct;
2356
+ return path15.join(root, "rules", "gamedev", item.name);
2193
2357
  }
2194
2358
  case "agent":
2195
- return path14.join(root, "agents", item.name);
2359
+ return path15.join(root, "agents", item.name);
2196
2360
  case "hook":
2197
- return path14.join(root, "hooks", item.name);
2361
+ return path15.join(root, "hooks", item.name);
2198
2362
  default:
2199
2363
  return null;
2200
2364
  }
@@ -2202,26 +2366,26 @@ function getSourcePath(item, root) {
2202
2366
  function getInstalledPath(item, claudeDir) {
2203
2367
  switch (item.type) {
2204
2368
  case "skill":
2205
- return path14.join(claudeDir, "skills", item.name);
2369
+ return path15.join(claudeDir, "skills", item.name);
2206
2370
  case "rule":
2207
- return path14.join(claudeDir, "rules", item.name);
2371
+ return path15.join(claudeDir, "rules", item.name);
2208
2372
  case "agent":
2209
- return path14.join(claudeDir, "agents", item.name);
2373
+ return path15.join(claudeDir, "agents", item.name);
2210
2374
  case "hook":
2211
- return path14.join(claudeDir, "hooks", item.name);
2375
+ return path15.join(claudeDir, "hooks", item.name);
2212
2376
  }
2213
2377
  }
2214
2378
 
2215
2379
  // src/commands/clean.ts
2216
- import chalk7 from "chalk";
2380
+ import chalk8 from "chalk";
2217
2381
  async function cleanCommand(opts) {
2218
2382
  const target = process.cwd();
2219
2383
  const manifest = readManifest(target);
2220
2384
  if (!manifest) {
2221
- console.log(chalk7.yellow("No Arcane installation found."));
2385
+ console.log(chalk8.yellow("No Arcane installation found."));
2222
2386
  return;
2223
2387
  }
2224
- console.log(chalk7.bold("\nThis will remove:"));
2388
+ console.log(chalk8.bold("\nThis will remove:"));
2225
2389
  console.log(` - ${manifest.total_skills} skills in .claude/skills/`);
2226
2390
  console.log(` - ${manifest.total_rules} rules in .claude/rules/`);
2227
2391
  console.log(
@@ -2232,33 +2396,33 @@ async function cleanCommand(opts) {
2232
2396
  console.log(" - arcane-manifest.json");
2233
2397
  if (!opts.force) {
2234
2398
  console.log(
2235
- chalk7.yellow("\n Pass --force to confirm removal.\n")
2399
+ chalk8.yellow("\n Pass --force to confirm removal.\n")
2236
2400
  );
2237
2401
  return;
2238
2402
  }
2239
2403
  clean(target);
2240
2404
  console.log(
2241
- chalk7.green("\n Run `npx claude-code-arcane install` to reinstall.\n")
2405
+ chalk8.green("\n Run `npx claude-code-arcane install` to reinstall.\n")
2242
2406
  );
2243
2407
  }
2244
2408
 
2245
2409
  // src/commands/worktree.ts
2246
- import fs12 from "fs";
2247
- import path15 from "path";
2410
+ import fs13 from "fs";
2411
+ import path16 from "path";
2248
2412
  import { execSync as execSync2 } from "child_process";
2249
- import chalk8 from "chalk";
2413
+ import chalk9 from "chalk";
2250
2414
  async function worktreeCommand(branch, opts) {
2251
2415
  const cwd = process.cwd();
2252
2416
  const wtInfo = getWorktreeInfo(cwd);
2253
2417
  if (!wtInfo) {
2254
- console.error(chalk8.red("Not a git repository."));
2418
+ console.error(chalk9.red("Not a git repository."));
2255
2419
  process.exit(1);
2256
2420
  }
2257
2421
  const repoRoot = wtInfo.isWorktree ? wtInfo.mainWorktreePath : cwd;
2258
- const worktreePath = opts.path ? path15.resolve(opts.path) : defaultWorktreePath(repoRoot, branch);
2259
- if (fs12.existsSync(worktreePath)) {
2422
+ const worktreePath = opts.path ? path16.resolve(opts.path) : defaultWorktreePath(repoRoot, branch);
2423
+ if (fs13.existsSync(worktreePath)) {
2260
2424
  console.error(
2261
- chalk8.red(`Path already exists: ${worktreePath}`)
2425
+ chalk9.red(`Path already exists: ${worktreePath}`)
2262
2426
  );
2263
2427
  process.exit(1);
2264
2428
  }
@@ -2268,7 +2432,7 @@ async function worktreeCommand(branch, opts) {
2268
2432
  if (manifest) {
2269
2433
  profileExpr = manifest.profile_command;
2270
2434
  console.log(
2271
- chalk8.dim(
2435
+ chalk9.dim(
2272
2436
  ` Inheriting profile from current installation: ${profileExpr}`
2273
2437
  )
2274
2438
  );
@@ -2276,34 +2440,34 @@ async function worktreeCommand(branch, opts) {
2276
2440
  }
2277
2441
  if (!profileExpr) {
2278
2442
  console.error(
2279
- chalk8.red(
2443
+ chalk9.red(
2280
2444
  "No profile specified and no existing installation to inherit from.\nUse --profile to specify one: npx claude-code-arcane worktree feat/api --profile backend-ts+agile"
2281
2445
  )
2282
2446
  );
2283
2447
  process.exit(1);
2284
2448
  }
2285
- console.log(chalk8.bold("\nCreating worktree:"));
2286
- console.log(` Branch: ${chalk8.cyan(branch)}`);
2449
+ console.log(chalk9.bold("\nCreating worktree:"));
2450
+ console.log(` Branch: ${chalk9.cyan(branch)}`);
2287
2451
  console.log(` Path: ${worktreePath}`);
2288
- console.log(` Profile: ${chalk8.green(profileExpr)}`);
2452
+ console.log(` Profile: ${chalk9.green(profileExpr)}`);
2289
2453
  if (opts.base) console.log(` Base: ${opts.base}`);
2290
2454
  if (opts.dryRun) {
2291
- console.log(chalk8.yellow("\n [dry-run] Would create worktree and install Arcane."));
2455
+ console.log(chalk9.yellow("\n [dry-run] Would create worktree and install Arcane."));
2292
2456
  return;
2293
2457
  }
2294
2458
  console.log("\n Creating git worktree...");
2295
2459
  try {
2296
2460
  createGitWorktree(repoRoot, worktreePath, branch, opts.base);
2297
- console.log(chalk8.green(" [ok] Git worktree created"));
2461
+ console.log(chalk9.green(" [ok] Git worktree created"));
2298
2462
  } catch (err) {
2299
2463
  console.error(
2300
- chalk8.red(` Failed to create worktree: ${err.message}`)
2464
+ chalk9.red(` Failed to create worktree: ${err.message}`)
2301
2465
  );
2302
2466
  process.exit(1);
2303
2467
  }
2304
2468
  console.log(" Installing Arcane...");
2305
2469
  const root = getPackageRoot();
2306
- const profilesDir = path15.join(root, "profiles");
2470
+ const profilesDir = path16.join(root, "profiles");
2307
2471
  const profileNames = profileExpr.split("+").filter(Boolean);
2308
2472
  const merged = mergeProfiles(profilesDir, profileNames);
2309
2473
  const shareFrom = opts.share !== false ? cwd : void 0;
@@ -2318,7 +2482,7 @@ async function worktreeCommand(branch, opts) {
2318
2482
  main_worktree: repoRoot
2319
2483
  });
2320
2484
  registerInstallation(worktreePath);
2321
- console.log(chalk8.green(" [ok] Arcane installed"));
2485
+ console.log(chalk9.green(" [ok] Arcane installed"));
2322
2486
  if (opts.installDeps) {
2323
2487
  const pm = detectPackageManager(worktreePath);
2324
2488
  if (pm) {
@@ -2330,22 +2494,22 @@ async function worktreeCommand(branch, opts) {
2330
2494
  stdio: "pipe",
2331
2495
  timeout: 12e4
2332
2496
  });
2333
- console.log(chalk8.green(` [ok] Dependencies installed`));
2497
+ console.log(chalk9.green(` [ok] Dependencies installed`));
2334
2498
  } catch {
2335
2499
  console.warn(
2336
- chalk8.yellow(" WARN: Dependency install failed. Run manually.")
2500
+ chalk9.yellow(" WARN: Dependency install failed. Run manually.")
2337
2501
  );
2338
2502
  }
2339
2503
  }
2340
2504
  }
2341
2505
  if (opts.isolate) {
2342
- const isolateScript = path15.join(
2506
+ const isolateScript = path16.join(
2343
2507
  worktreePath,
2344
2508
  ".claude",
2345
2509
  "skills",
2346
2510
  "worktree-isolation"
2347
2511
  );
2348
- if (fs12.existsSync(isolateScript)) {
2512
+ if (fs13.existsSync(isolateScript)) {
2349
2513
  console.log(" Applying worktree isolation...");
2350
2514
  try {
2351
2515
  execSync2(
@@ -2357,36 +2521,36 @@ async function worktreeCommand(branch, opts) {
2357
2521
  timeout: 3e4
2358
2522
  }
2359
2523
  );
2360
- console.log(chalk8.green(" [ok] Worktree isolation applied"));
2524
+ console.log(chalk9.green(" [ok] Worktree isolation applied"));
2361
2525
  } catch {
2362
2526
  console.warn(
2363
- chalk8.yellow(
2527
+ chalk9.yellow(
2364
2528
  " WARN: Worktree isolation failed. Run /worktree-isolation manually."
2365
2529
  )
2366
2530
  );
2367
2531
  }
2368
2532
  } else {
2369
2533
  console.warn(
2370
- chalk8.yellow(
2534
+ chalk9.yellow(
2371
2535
  " WARN: worktree-isolation skill not installed. Add it with: npx claude-code-arcane add worktree-isolation"
2372
2536
  )
2373
2537
  );
2374
2538
  }
2375
2539
  }
2376
- console.log(chalk8.bold.green("\nWorktree ready:"));
2377
- console.log(` ${chalk8.cyan(`cd ${worktreePath}`)}`);
2378
- console.log(` ${chalk8.dim("claude")}
2540
+ console.log(chalk9.bold.green("\nWorktree ready:"));
2541
+ console.log(` ${chalk9.cyan(`cd ${worktreePath}`)}`);
2542
+ console.log(` ${chalk9.dim("claude")}
2379
2543
  `);
2380
2544
  }
2381
2545
 
2382
2546
  // src/commands/global.ts
2383
- import fs13 from "fs";
2384
- import path16 from "path";
2547
+ import fs14 from "fs";
2548
+ import path17 from "path";
2385
2549
  import os5 from "os";
2386
- import chalk9 from "chalk";
2387
- var CLAUDE_HOME = path16.join(os5.homedir(), ".claude");
2388
- var SCRIPTS_DIR = path16.join(CLAUDE_HOME, "scripts", "worktree-isolation");
2389
- var SETTINGS_PATH = path16.join(CLAUDE_HOME, "settings.json");
2550
+ import chalk10 from "chalk";
2551
+ var CLAUDE_HOME = path17.join(os5.homedir(), ".claude");
2552
+ var SCRIPTS_DIR = path17.join(CLAUDE_HOME, "scripts", "worktree-isolation");
2553
+ var SETTINGS_PATH = path17.join(CLAUDE_HOME, "settings.json");
2390
2554
  async function globalCommand(opts) {
2391
2555
  if (opts.status) {
2392
2556
  showStatus();
@@ -2399,100 +2563,100 @@ async function globalCommand(opts) {
2399
2563
  installGlobal();
2400
2564
  }
2401
2565
  function showStatus() {
2402
- console.log(chalk9.bold("\n=== Arcane Global Status ===\n"));
2403
- const hasScripts = fs13.existsSync(
2404
- path16.join(SCRIPTS_DIR, "apply.py")
2566
+ console.log(chalk10.bold("\n=== Arcane Global Status ===\n"));
2567
+ const hasScripts = fs14.existsSync(
2568
+ path17.join(SCRIPTS_DIR, "apply.py")
2405
2569
  );
2406
2570
  console.log(
2407
- ` Worktree scripts: ${hasScripts ? chalk9.green("installed") : chalk9.dim("not installed")}`
2571
+ ` Worktree scripts: ${hasScripts ? chalk10.green("installed") : chalk10.dim("not installed")}`
2408
2572
  );
2409
2573
  if (hasScripts) {
2410
- console.log(` ${chalk9.dim(SCRIPTS_DIR)}`);
2574
+ console.log(` ${chalk10.dim(SCRIPTS_DIR)}`);
2411
2575
  }
2412
2576
  const hookInstalled = isHookInstalled();
2413
2577
  console.log(
2414
- ` SessionStart hook: ${hookInstalled ? chalk9.green("active") : chalk9.dim("not configured")}`
2578
+ ` SessionStart hook: ${hookInstalled ? chalk10.green("active") : chalk10.dim("not configured")}`
2415
2579
  );
2416
- console.log(` ${chalk9.dim(SETTINGS_PATH)}`);
2580
+ console.log(` ${chalk10.dim(SETTINGS_PATH)}`);
2417
2581
  console.log();
2418
2582
  }
2419
2583
  function installGlobal() {
2420
- console.log(chalk9.bold("\nInstalling Arcane global hooks...\n"));
2584
+ console.log(chalk10.bold("\nInstalling Arcane global hooks...\n"));
2421
2585
  const root = getPackageRoot();
2422
- const scriptsSrc = path16.join(
2586
+ const scriptsSrc = path17.join(
2423
2587
  root,
2424
2588
  "skills",
2425
2589
  "worktree-isolation",
2426
2590
  "scripts"
2427
2591
  );
2428
- if (!fs13.existsSync(scriptsSrc)) {
2592
+ if (!fs14.existsSync(scriptsSrc)) {
2429
2593
  console.error(
2430
- chalk9.red(" worktree-isolation scripts not found in package.")
2594
+ chalk10.red(" worktree-isolation scripts not found in package.")
2431
2595
  );
2432
2596
  process.exit(1);
2433
2597
  }
2434
- fs13.mkdirSync(SCRIPTS_DIR, { recursive: true });
2598
+ fs14.mkdirSync(SCRIPTS_DIR, { recursive: true });
2435
2599
  for (const file of ["apply.py", "lib.py"]) {
2436
- const src = path16.join(scriptsSrc, file);
2437
- const dst = path16.join(SCRIPTS_DIR, file);
2438
- if (!fs13.existsSync(src)) {
2439
- console.warn(chalk9.yellow(` WARN: ${file} not found, skipping`));
2600
+ const src = path17.join(scriptsSrc, file);
2601
+ const dst = path17.join(SCRIPTS_DIR, file);
2602
+ if (!fs14.existsSync(src)) {
2603
+ console.warn(chalk10.yellow(` WARN: ${file} not found, skipping`));
2440
2604
  continue;
2441
2605
  }
2442
- if (fs13.existsSync(dst) && fs13.readFileSync(src).equals(fs13.readFileSync(dst))) {
2443
- console.log(chalk9.dim(` [skip] ${file} (already up-to-date)`));
2606
+ if (fs14.existsSync(dst) && fs14.readFileSync(src).equals(fs14.readFileSync(dst))) {
2607
+ console.log(chalk10.dim(` [skip] ${file} (already up-to-date)`));
2444
2608
  } else {
2445
- fs13.copyFileSync(src, dst);
2609
+ fs14.copyFileSync(src, dst);
2446
2610
  if (process.platform !== "win32") {
2447
- fs13.chmodSync(dst, 493);
2611
+ fs14.chmodSync(dst, 493);
2448
2612
  }
2449
- console.log(chalk9.green(` [ok] ${file} -> ${SCRIPTS_DIR}`));
2613
+ console.log(chalk10.green(` [ok] ${file} -> ${SCRIPTS_DIR}`));
2450
2614
  }
2451
2615
  }
2452
2616
  if (isHookInstalled()) {
2453
2617
  console.log(
2454
- chalk9.dim(" [skip] SessionStart hook (already configured)")
2618
+ chalk10.dim(" [skip] SessionStart hook (already configured)")
2455
2619
  );
2456
2620
  } else {
2457
2621
  addHookToSettings();
2458
2622
  console.log(
2459
- chalk9.green(" [ok] SessionStart hook added to ~/.claude/settings.json")
2623
+ chalk10.green(" [ok] SessionStart hook added to ~/.claude/settings.json")
2460
2624
  );
2461
2625
  }
2462
- console.log(chalk9.bold.green("\n Global hooks installed."));
2626
+ console.log(chalk10.bold.green("\n Global hooks installed."));
2463
2627
  console.log(
2464
- chalk9.dim(
2628
+ chalk10.dim(
2465
2629
  " Worktree isolation will auto-apply on every new Claude Code session.\n"
2466
2630
  )
2467
2631
  );
2468
2632
  }
2469
2633
  function removeGlobal() {
2470
- console.log(chalk9.bold("\nRemoving Arcane global hooks...\n"));
2471
- if (fs13.existsSync(SCRIPTS_DIR)) {
2472
- fs13.rmSync(SCRIPTS_DIR, { recursive: true, force: true });
2473
- console.log(chalk9.green(" [ok] Removed worktree-isolation scripts"));
2634
+ console.log(chalk10.bold("\nRemoving Arcane global hooks...\n"));
2635
+ if (fs14.existsSync(SCRIPTS_DIR)) {
2636
+ fs14.rmSync(SCRIPTS_DIR, { recursive: true, force: true });
2637
+ console.log(chalk10.green(" [ok] Removed worktree-isolation scripts"));
2474
2638
  } else {
2475
- console.log(chalk9.dim(" [skip] Scripts not found"));
2639
+ console.log(chalk10.dim(" [skip] Scripts not found"));
2476
2640
  }
2477
2641
  if (isHookInstalled()) {
2478
2642
  removeHookFromSettings();
2479
- console.log(chalk9.green(" [ok] Removed SessionStart hook"));
2643
+ console.log(chalk10.green(" [ok] Removed SessionStart hook"));
2480
2644
  } else {
2481
- console.log(chalk9.dim(" [skip] Hook not configured"));
2645
+ console.log(chalk10.dim(" [skip] Hook not configured"));
2482
2646
  }
2483
- console.log(chalk9.bold.green("\n Global hooks removed.\n"));
2647
+ console.log(chalk10.bold.green("\n Global hooks removed.\n"));
2484
2648
  }
2485
2649
  function readSettings() {
2486
- if (!fs13.existsSync(SETTINGS_PATH)) return {};
2650
+ if (!fs14.existsSync(SETTINGS_PATH)) return {};
2487
2651
  try {
2488
- return JSON.parse(fs13.readFileSync(SETTINGS_PATH, "utf-8"));
2652
+ return JSON.parse(fs14.readFileSync(SETTINGS_PATH, "utf-8"));
2489
2653
  } catch {
2490
2654
  return {};
2491
2655
  }
2492
2656
  }
2493
2657
  function writeSettings(settings) {
2494
- fs13.mkdirSync(path16.dirname(SETTINGS_PATH), { recursive: true });
2495
- fs13.writeFileSync(
2658
+ fs14.mkdirSync(path17.dirname(SETTINGS_PATH), { recursive: true });
2659
+ fs14.writeFileSync(
2496
2660
  SETTINGS_PATH,
2497
2661
  JSON.stringify(settings, null, 2) + "\n",
2498
2662
  "utf-8"
@@ -2549,11 +2713,11 @@ function removeHookFromSettings() {
2549
2713
  }
2550
2714
 
2551
2715
  // src/update-check.ts
2552
- import fs14 from "fs";
2553
- import path17 from "path";
2716
+ import fs15 from "fs";
2717
+ import path18 from "path";
2554
2718
  import os6 from "os";
2555
- import chalk10 from "chalk";
2556
- var CHECK_FILE = path17.join(os6.homedir(), ".arcane", "last-check.json");
2719
+ import chalk11 from "chalk";
2720
+ var CHECK_FILE = path18.join(os6.homedir(), ".arcane", "last-check.json");
2557
2721
  var CHECK_INTERVAL_MS = 4 * 60 * 60 * 1e3;
2558
2722
  var GITHUB_OWNER2 = "SebastianLuser";
2559
2723
  var GITHUB_REPO2 = "Claude-Code-Arcane";
@@ -2593,11 +2757,11 @@ async function checkForUpdates(opts = {}) {
2593
2757
  }
2594
2758
  function printUpdateNotice(localVersion, remoteSha) {
2595
2759
  console.log(
2596
- chalk10.yellow(`
2760
+ chalk11.yellow(`
2597
2761
  Arcane update available: ${localVersion} \u2192 ${remoteSha}`)
2598
2762
  );
2599
2763
  console.log(
2600
- chalk10.dim(" Run: arcane update (--dry-run to preview)\n")
2764
+ chalk11.dim(" Run: arcane update (--dry-run to preview)\n")
2601
2765
  );
2602
2766
  }
2603
2767
  async function getLatestCommitSha() {
@@ -2621,19 +2785,19 @@ async function getLatestCommitSha() {
2621
2785
  }
2622
2786
  function readCachedCheck() {
2623
2787
  try {
2624
- if (!fs14.existsSync(CHECK_FILE)) return null;
2625
- return JSON.parse(fs14.readFileSync(CHECK_FILE, "utf-8"));
2788
+ if (!fs15.existsSync(CHECK_FILE)) return null;
2789
+ return JSON.parse(fs15.readFileSync(CHECK_FILE, "utf-8"));
2626
2790
  } catch {
2627
2791
  return null;
2628
2792
  }
2629
2793
  }
2630
2794
  function writeCachedCheck(result) {
2631
2795
  try {
2632
- const dir = path17.dirname(CHECK_FILE);
2633
- if (!fs14.existsSync(dir)) {
2634
- fs14.mkdirSync(dir, { recursive: true });
2796
+ const dir = path18.dirname(CHECK_FILE);
2797
+ if (!fs15.existsSync(dir)) {
2798
+ fs15.mkdirSync(dir, { recursive: true });
2635
2799
  }
2636
- fs14.writeFileSync(CHECK_FILE, JSON.stringify(result, null, 2) + "\n");
2800
+ fs15.writeFileSync(CHECK_FILE, JSON.stringify(result, null, 2) + "\n");
2637
2801
  } catch {
2638
2802
  }
2639
2803
  }
@@ -2644,7 +2808,7 @@ program.name("arcane").description(
2644
2808
  "Claude Code Arcane \u2014 Skills, agents, hooks and rules for Claude Code"
2645
2809
  ).version(getPackageVersion());
2646
2810
  program.command("install [profile]").description(
2647
- "Install a profile (e.g., backend-ts+agile). Without args, shows available profiles."
2811
+ "Install profiles (e.g., backend-ts+agile). Without args, opens an interactive picker."
2648
2812
  ).option("-t, --target <dir>", "Target project directory", process.cwd()).option("-n, --dry-run", "Show what would be installed without changing files").option("-f, --force", "Overwrite without backup prompt").option(
2649
2813
  "-s, --share-from <dir>",
2650
2814
  "Share hooks/docs from an existing Arcane installation (e.g., main worktree)"