claude-code-arcane 2.7.1 → 2.8.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 (151) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +1 -1
  3. package/agents/ai/ai-architect.md +1 -1
  4. package/agents/ai/data-engineer.md +1 -1
  5. package/agents/ai/data-scientist.md +1 -1
  6. package/agents/ai/ml-engineer.md +1 -1
  7. package/agents/audio/audio-director.md +32 -40
  8. package/agents/audio/audio-programmer.md +32 -45
  9. package/agents/audio/audio-qa.md +36 -47
  10. package/agents/audio/composer.md +32 -42
  11. package/agents/audio/technical-sound-designer.md +32 -43
  12. package/agents/audio/voice-director.md +33 -42
  13. package/agents/business/business-ops-lead.md +1 -0
  14. package/agents/business/financial-analyst.md +1 -0
  15. package/agents/business/sales-engineer.md +1 -0
  16. package/agents/career/career-strategist.md +1 -1
  17. package/agents/career/cv-reviewer.md +1 -1
  18. package/agents/career/hiring-manager.md +1 -1
  19. package/agents/career/mock-interviewer.md +1 -1
  20. package/agents/clevel/ceo-advisor.md +2 -2
  21. package/agents/clevel/cfo-advisor.md +2 -2
  22. package/agents/clevel/chief-of-staff.md +2 -2
  23. package/agents/clevel/cmo-advisor.md +2 -2
  24. package/agents/clevel/coo-advisor.md +2 -2
  25. package/agents/clevel/cto-advisor.md +2 -2
  26. package/agents/devops/cloud-architect.md +2 -3
  27. package/agents/devops/devops-engineer.md +48 -0
  28. package/agents/devops/platform-lead.md +2 -3
  29. package/agents/devops/sre-lead.md +2 -3
  30. package/agents/ecommerce/ecommerce-architect.md +1 -0
  31. package/agents/engineering/android-engineer.md +1 -0
  32. package/agents/engineering/backend-architect.md +3 -4
  33. package/agents/engineering/chief-technology-officer.md +53 -0
  34. package/agents/engineering/database-architect.md +2 -3
  35. package/agents/engineering/dotnet-engineer.md +2 -1
  36. package/agents/engineering/e2e-tester.md +2 -1
  37. package/agents/engineering/flutter-engineer.md +1 -0
  38. package/agents/engineering/frontend-architect.md +3 -4
  39. package/agents/engineering/go-engineer.md +2 -1
  40. package/agents/engineering/ios-engineer.md +1 -0
  41. package/agents/engineering/mobile-lead.md +59 -0
  42. package/agents/engineering/nestjs-engineer.md +2 -1
  43. package/agents/engineering/nextjs-engineer.md +2 -1
  44. package/agents/engineering/nextjs-reviewer.md +4 -3
  45. package/agents/engineering/node-engineer.md +2 -1
  46. package/agents/engineering/react-engineer.md +2 -1
  47. package/agents/engineering/react-native-engineer.md +2 -1
  48. package/agents/engineering/sql-specialist.md +3 -2
  49. package/agents/engineering/vp-engineering.md +54 -0
  50. package/agents/freelance/client-screener.md +1 -1
  51. package/agents/freelance/discovery-call.md +1 -1
  52. package/agents/freelance/pipeline-strategist.md +1 -1
  53. package/agents/freelance/proposal-reviewer.md +1 -1
  54. package/agents/game/accessibility-specialist.md +39 -51
  55. package/agents/game/ai-programmer.md +32 -44
  56. package/agents/game/analytics-engineer.md +32 -44
  57. package/agents/game/art-director.md +1 -0
  58. package/agents/game/creative-director.md +48 -0
  59. package/agents/game/economy-designer.md +35 -60
  60. package/agents/game/game-designer.md +1 -0
  61. package/agents/game/gameplay-programmer.md +32 -46
  62. package/agents/game/lead-programmer.md +2 -1
  63. package/agents/game/level-designer.md +35 -60
  64. package/agents/game/live-ops-designer.md +36 -56
  65. package/agents/game/narrative-director.md +1 -0
  66. package/agents/game/network-programmer.md +32 -44
  67. package/agents/game/performance-analyst.md +32 -44
  68. package/agents/game/producer.md +50 -0
  69. package/agents/game/qa-lead.md +3 -2
  70. package/agents/game/qa-tester-gamedev.md +33 -45
  71. package/agents/game/release-manager.md +2 -1
  72. package/agents/game/sound-designer.md +33 -45
  73. package/agents/game/systems-designer.md +35 -60
  74. package/agents/game/technical-artist.md +32 -44
  75. package/agents/game/technical-director.md +2 -1
  76. package/agents/game/ue-blueprint-specialist.md +36 -48
  77. package/agents/game/ue-gas-specialist.md +36 -48
  78. package/agents/game/ue-replication-specialist.md +36 -48
  79. package/agents/game/ue-umg-specialist.md +36 -48
  80. package/agents/game/unity-shader-specialist.md +36 -48
  81. package/agents/game/unity-specialist.md +36 -51
  82. package/agents/game/unreal-specialist.md +36 -48
  83. package/agents/game/ux-designer.md +35 -55
  84. package/agents/game/world-builder.md +35 -60
  85. package/agents/game/writer.md +32 -50
  86. package/agents/integrations/api-tools-specialist.md +2 -2
  87. package/agents/integrations/comms-tools-specialist.md +2 -2
  88. package/agents/integrations/design-tools-specialist.md +53 -7
  89. package/agents/integrations/docs-tools-specialist.md +18 -2
  90. package/agents/integrations/integrations-architect.md +2 -2
  91. package/agents/integrations/project-tools-specialist.md +18 -2
  92. package/agents/management/delivery-manager.md +2 -1
  93. package/agents/management/program-director.md +2 -1
  94. package/agents/management/project-manager.md +2 -2
  95. package/agents/management/scrum-master.md +2 -2
  96. package/agents/marketing/content-lead.md +1 -0
  97. package/agents/marketing/growth-lead.md +1 -0
  98. package/agents/marketing/marketing-analyst.md +1 -0
  99. package/agents/marketing/marketing-director.md +1 -0
  100. package/agents/marketing/seo-lead.md +1 -0
  101. package/agents/product/chief-product-officer.md +1 -0
  102. package/agents/product/design-system-lead.md +3 -3
  103. package/agents/product/product-manager.md +2 -2
  104. package/agents/product/ui-lead.md +2 -4
  105. package/agents/product/ux-lead.md +2 -5
  106. package/agents/quality/qa-director.md +2 -5
  107. package/agents/quality/qa-engineer.md +1 -0
  108. package/agents/quality/security-architect.md +2 -5
  109. package/agents/regulatory/compliance-officer.md +1 -0
  110. package/agents/regulatory/quality-manager.md +1 -0
  111. package/agents/regulatory/regulatory-director.md +1 -0
  112. package/agents/visualnovel/vn-comfyui-artist.md +35 -14
  113. package/agents/visualnovel/vn-narrative-director.md +36 -30
  114. package/agents/visualnovel/vn-producer.md +36 -12
  115. package/agents/visualnovel/vn-renpy-developer.md +34 -12
  116. package/agents/visualnovel/vn-scene-director.md +34 -9
  117. package/agents/visualnovel/vn-ui-designer.md +36 -20
  118. package/dist/cli.js +366 -316
  119. package/docs/USER-GUIDE.md +26 -1
  120. package/docs/agent-hierarchy.md +42 -2
  121. package/docs/collaborative-design.md +2 -1
  122. package/docs/directory-structure.md +18 -13
  123. package/package.json +1 -1
  124. package/profiles/backend-go.yaml +1 -0
  125. package/profiles/docs.yaml +0 -2
  126. package/profiles/security.yaml +1 -1
  127. package/profiles/unity-design.yaml +2 -0
  128. package/profiles/unity-dev.yaml +2 -0
  129. package/profiles/unreal-dev.yaml +1 -0
  130. package/profiles/visual-novel.yaml +1 -0
  131. package/skills/architecture-review/SKILL.md +1 -0
  132. package/skills/balance-check/SKILL.md +1 -0
  133. package/skills/code-review/SKILL.md +49 -16
  134. package/skills/create-epics/SKILL.md +2 -1
  135. package/skills/create-stories/SKILL.md +5 -4
  136. package/skills/database-indexing/SKILL.md +2 -1
  137. package/skills/help/SKILL.md +6 -2
  138. package/skills/hotfix/SKILL.md +11 -3
  139. package/skills/patch-notes/SKILL.md +2 -1
  140. package/skills/perf-profile/SKILL.md +0 -1
  141. package/skills/prototype/SKILL.md +0 -2
  142. package/skills/qa-plan/SKILL.md +2 -1
  143. package/skills/retrospective/SKILL.md +4 -2
  144. package/skills/security-audit/SKILL.md +2 -1
  145. package/skills/skill-auditor/SKILL.md +16 -4
  146. package/skills/skill-auditor/references/frontmatter-spec.md +93 -86
  147. package/skills/slo-sli/SKILL.md +2 -2
  148. package/skills/sprint-plan/SKILL.md +4 -2
  149. package/skills/story-readiness/SKILL.md +1 -1
  150. package/skills/ux-design/SKILL.md +2 -1
  151. package/skills/ux-review/SKILL.md +2 -1
package/dist/cli.js CHANGED
@@ -4,7 +4,7 @@
4
4
  import { Command } from "commander";
5
5
 
6
6
  // src/commands/install.ts
7
- import path11 from "path";
7
+ import path12 from "path";
8
8
  import chalk2 from "chalk";
9
9
 
10
10
  // src/profiles.ts
@@ -374,8 +374,8 @@ function hint(description, max = 90) {
374
374
  }
375
375
 
376
376
  // src/installer.ts
377
- import fs6 from "fs";
378
- import path7 from "path";
377
+ import fs7 from "fs";
378
+ import path8 from "path";
379
379
 
380
380
  // src/utils.ts
381
381
  import { fileURLToPath } from "url";
@@ -434,11 +434,66 @@ function safeRemove(p2) {
434
434
  }
435
435
  }
436
436
 
437
- // src/manifest.ts
437
+ // src/agent-entries.ts
438
+ import fs4 from "fs";
438
439
  import path4 from "path";
440
+ function parseAgentEntry(raw) {
441
+ const trimmed = raw.trim().replace(/\/+$/, "");
442
+ const slash = trimmed.indexOf("/");
443
+ if (slash < 0) return { raw: trimmed, division: trimmed };
444
+ return {
445
+ raw: trimmed,
446
+ division: trimmed.slice(0, slash),
447
+ agent: trimmed.slice(slash + 1)
448
+ };
449
+ }
450
+ function isGranular(raw) {
451
+ return parseAgentEntry(raw).agent !== void 0;
452
+ }
453
+ function agentSourcePath(root, raw) {
454
+ const { division, agent } = parseAgentEntry(raw);
455
+ const p2 = agent ? path4.join(root, "agents", division, `${agent}.md`) : path4.join(root, "agents", division);
456
+ return fs4.existsSync(p2) ? p2 : null;
457
+ }
458
+ function copyAgentEntry(root, claudeDir, raw) {
459
+ const src = agentSourcePath(root, raw);
460
+ if (!src) return null;
461
+ const { division, agent } = parseAgentEntry(raw);
462
+ const dstDir = path4.join(claudeDir, "agents", division);
463
+ if (agent) {
464
+ ensureDir(dstDir);
465
+ fs4.copyFileSync(src, path4.join(dstDir, `${agent}.md`));
466
+ return 1;
467
+ }
468
+ copyDirSync(src, dstDir);
469
+ return fs4.readdirSync(dstDir, { recursive: true }).filter((f) => String(f).endsWith(".md")).length;
470
+ }
471
+ function removeAgentEntry(claudeDir, raw) {
472
+ const { division, agent } = parseAgentEntry(raw);
473
+ const dstDir = path4.join(claudeDir, "agents", division);
474
+ if (!agent) {
475
+ if (!fs4.existsSync(dstDir)) return false;
476
+ fs4.rmSync(dstDir, { recursive: true, force: true });
477
+ return true;
478
+ }
479
+ const file = path4.join(dstDir, `${agent}.md`);
480
+ if (!fs4.existsSync(file)) return false;
481
+ safeRemove(file);
482
+ if (fs4.existsSync(dstDir) && fs4.readdirSync(dstDir).length === 0) {
483
+ fs4.rmSync(dstDir, { recursive: true, force: true });
484
+ }
485
+ return true;
486
+ }
487
+ function agentEntryLabel(raw) {
488
+ const { division, agent } = parseAgentEntry(raw);
489
+ return agent ? `agents/${division}/${agent}.md` : `agents/${division}/`;
490
+ }
491
+
492
+ // src/manifest.ts
493
+ import path5 from "path";
439
494
  var MANIFEST_FILE = "arcane-manifest.json";
440
495
  function manifestPath(target) {
441
- return path4.join(target, ".claude", MANIFEST_FILE);
496
+ return path5.join(target, ".claude", MANIFEST_FILE);
442
497
  }
443
498
  function readManifest(target) {
444
499
  const p2 = manifestPath(target);
@@ -492,8 +547,8 @@ function updateManifestFields(target, updates) {
492
547
 
493
548
  // src/worktree.ts
494
549
  import { execSync } from "child_process";
495
- import fs4 from "fs";
496
- import path5 from "path";
550
+ import fs5 from "fs";
551
+ import path6 from "path";
497
552
  function getWorktreeInfo(target) {
498
553
  try {
499
554
  execSync("git rev-parse --is-inside-work-tree", {
@@ -514,10 +569,10 @@ function getWorktreeInfo(target) {
514
569
  encoding: "utf-8",
515
570
  stdio: "pipe"
516
571
  }).trim();
517
- const resolvedCommon = path5.resolve(target, gitCommonDir);
518
- const resolvedGit = path5.resolve(target, gitDir);
572
+ const resolvedCommon = path6.resolve(target, gitCommonDir);
573
+ const resolvedGit = path6.resolve(target, gitDir);
519
574
  const isWorktree = resolvedCommon !== resolvedGit;
520
- const mainWorktreePath = isWorktree ? path5.resolve(resolvedCommon, "..") : target;
575
+ const mainWorktreePath = isWorktree ? path6.resolve(resolvedCommon, "..") : target;
521
576
  let currentBranch = "HEAD";
522
577
  try {
523
578
  currentBranch = execSync("git branch --show-current", {
@@ -532,7 +587,7 @@ function getWorktreeInfo(target) {
532
587
  isWorktree,
533
588
  mainWorktreePath,
534
589
  currentBranch,
535
- worktreeId: path5.basename(target),
590
+ worktreeId: path6.basename(target),
536
591
  allWorktrees
537
592
  };
538
593
  }
@@ -568,17 +623,17 @@ function parseWorktreeList(cwd) {
568
623
  }
569
624
  }
570
625
  function findMainArcaneInstall(mainPath) {
571
- return fs4.existsSync(
572
- path5.join(mainPath, ".claude", "arcane-manifest.json")
626
+ return fs5.existsSync(
627
+ path6.join(mainPath, ".claude", "arcane-manifest.json")
573
628
  );
574
629
  }
575
630
  function linkOrCopyDir(src, dest, mode) {
576
631
  if (mode === "symlink") {
577
632
  try {
578
- const parentDir = path5.dirname(dest);
579
- fs4.mkdirSync(parentDir, { recursive: true });
633
+ const parentDir = path6.dirname(dest);
634
+ fs5.mkdirSync(parentDir, { recursive: true });
580
635
  const type = process.platform === "win32" ? "junction" : "dir";
581
- fs4.symlinkSync(src, dest, type);
636
+ fs5.symlinkSync(src, dest, type);
582
637
  return "linked";
583
638
  } catch {
584
639
  }
@@ -590,9 +645,9 @@ function sanitizeBranchForPath(branch) {
590
645
  return branch.replace(/^(feat|fix|feature|hotfix)\//, "").replace(/\//g, "-").replace(/[^a-zA-Z0-9._-]/g, "-");
591
646
  }
592
647
  function defaultWorktreePath(repoRoot, branch) {
593
- const repoName = path5.basename(repoRoot);
648
+ const repoName = path6.basename(repoRoot);
594
649
  const sanitized = sanitizeBranchForPath(branch);
595
- return path5.resolve(repoRoot, "..", `${repoName}-${sanitized}`);
650
+ return path6.resolve(repoRoot, "..", `${repoName}-${sanitized}`);
596
651
  }
597
652
  function createGitWorktree(repoRoot, worktreePath, branch, base) {
598
653
  const branchExists = checkBranchExists(repoRoot, branch);
@@ -634,20 +689,20 @@ function detectPackageManager(dir) {
634
689
  { lockfile: "package-lock.json", command: "npm install" }
635
690
  ];
636
691
  for (const m of managers) {
637
- if (fs4.existsSync(path5.join(dir, m.lockfile))) return m;
692
+ if (fs5.existsSync(path6.join(dir, m.lockfile))) return m;
638
693
  }
639
- if (fs4.existsSync(path5.join(dir, "package.json"))) {
694
+ if (fs5.existsSync(path6.join(dir, "package.json"))) {
640
695
  return { lockfile: "package.json", command: "npm install" };
641
696
  }
642
697
  return null;
643
698
  }
644
699
 
645
700
  // src/content-hash.ts
646
- import fs5 from "fs";
647
- import path6 from "path";
701
+ import fs6 from "fs";
702
+ import path7 from "path";
648
703
  import { createHash } from "crypto";
649
704
  function hashFile(filePath) {
650
- const content = fs5.readFileSync(filePath);
705
+ const content = fs6.readFileSync(filePath);
651
706
  return createHash("sha256").update(content).digest("hex");
652
707
  }
653
708
  function hashDirectory(dirPath) {
@@ -656,10 +711,10 @@ function hashDirectory(dirPath) {
656
711
  return hashes;
657
712
  }
658
713
  function walkDir(base, current, hashes) {
659
- for (const entry of fs5.readdirSync(current, { withFileTypes: true })) {
714
+ for (const entry of fs6.readdirSync(current, { withFileTypes: true })) {
660
715
  if (isBuildArtifact(entry.name)) continue;
661
- const fullPath = path6.join(current, entry.name);
662
- const relPath = path6.relative(base, fullPath).split(path6.sep).join("/");
716
+ const fullPath = path7.join(current, entry.name);
717
+ const relPath = path7.relative(base, fullPath).split(path7.sep).join("/");
663
718
  if (entry.isDirectory()) {
664
719
  walkDir(base, fullPath, hashes);
665
720
  } else {
@@ -674,39 +729,39 @@ function computeContentHashes(claudeDir) {
674
729
  agents: {},
675
730
  hooks: {}
676
731
  };
677
- const skillsDir = path6.join(claudeDir, "skills");
678
- if (fs5.existsSync(skillsDir)) {
679
- for (const entry of fs5.readdirSync(skillsDir, { withFileTypes: true })) {
732
+ const skillsDir = path7.join(claudeDir, "skills");
733
+ if (fs6.existsSync(skillsDir)) {
734
+ for (const entry of fs6.readdirSync(skillsDir, { withFileTypes: true })) {
680
735
  if (entry.isDirectory()) {
681
- const dirHash = hashDirectory(path6.join(skillsDir, entry.name));
736
+ const dirHash = hashDirectory(path7.join(skillsDir, entry.name));
682
737
  const combined = createHash("sha256").update(JSON.stringify(dirHash)).digest("hex");
683
738
  hashes.skills[entry.name] = combined;
684
739
  }
685
740
  }
686
741
  }
687
- const rulesDir = path6.join(claudeDir, "rules");
688
- if (fs5.existsSync(rulesDir)) {
689
- for (const entry of fs5.readdirSync(rulesDir, { withFileTypes: true })) {
742
+ const rulesDir = path7.join(claudeDir, "rules");
743
+ if (fs6.existsSync(rulesDir)) {
744
+ for (const entry of fs6.readdirSync(rulesDir, { withFileTypes: true })) {
690
745
  if (!entry.isDirectory()) {
691
- hashes.rules[entry.name] = hashFile(path6.join(rulesDir, entry.name));
746
+ hashes.rules[entry.name] = hashFile(path7.join(rulesDir, entry.name));
692
747
  }
693
748
  }
694
749
  }
695
- const agentsDir = path6.join(claudeDir, "agents");
696
- if (fs5.existsSync(agentsDir)) {
697
- for (const entry of fs5.readdirSync(agentsDir, { withFileTypes: true })) {
750
+ const agentsDir = path7.join(claudeDir, "agents");
751
+ if (fs6.existsSync(agentsDir)) {
752
+ for (const entry of fs6.readdirSync(agentsDir, { withFileTypes: true })) {
698
753
  if (entry.isDirectory()) {
699
- const dirHash = hashDirectory(path6.join(agentsDir, entry.name));
754
+ const dirHash = hashDirectory(path7.join(agentsDir, entry.name));
700
755
  const combined = createHash("sha256").update(JSON.stringify(dirHash)).digest("hex");
701
756
  hashes.agents[entry.name] = combined;
702
757
  }
703
758
  }
704
759
  }
705
- const hooksDir = path6.join(claudeDir, "hooks");
706
- if (fs5.existsSync(hooksDir)) {
707
- for (const entry of fs5.readdirSync(hooksDir, { withFileTypes: true })) {
760
+ const hooksDir = path7.join(claudeDir, "hooks");
761
+ if (fs6.existsSync(hooksDir)) {
762
+ for (const entry of fs6.readdirSync(hooksDir, { withFileTypes: true })) {
708
763
  if (!entry.isDirectory()) {
709
- hashes.hooks[entry.name] = hashFile(path6.join(hooksDir, entry.name));
764
+ hashes.hooks[entry.name] = hashFile(path7.join(hooksDir, entry.name));
710
765
  }
711
766
  }
712
767
  }
@@ -720,8 +775,8 @@ function computeSourceHashes(packageRoot, skills, rules, agents) {
720
775
  hooks: {}
721
776
  };
722
777
  for (const skill of skills) {
723
- const skillDir = path6.join(packageRoot, "skills", skill);
724
- if (fs5.existsSync(skillDir)) {
778
+ const skillDir = path7.join(packageRoot, "skills", skill);
779
+ if (fs6.existsSync(skillDir)) {
725
780
  const dirHash = hashDirectory(skillDir);
726
781
  const combined = createHash("sha256").update(JSON.stringify(dirHash)).digest("hex");
727
782
  hashes.skills[skill] = combined;
@@ -729,29 +784,29 @@ function computeSourceHashes(packageRoot, skills, rules, agents) {
729
784
  }
730
785
  for (const rule of rules) {
731
786
  const ruleName = rule.endsWith(".md") ? rule : `${rule}.md`;
732
- const rulePath = path6.join(packageRoot, "rules", ruleName);
733
- if (fs5.existsSync(rulePath)) {
787
+ const rulePath = path7.join(packageRoot, "rules", ruleName);
788
+ if (fs6.existsSync(rulePath)) {
734
789
  hashes.rules[ruleName] = hashFile(rulePath);
735
790
  } else {
736
- const gamedevPath = path6.join(packageRoot, "rules", "gamedev", ruleName);
737
- if (fs5.existsSync(gamedevPath)) {
791
+ const gamedevPath = path7.join(packageRoot, "rules", "gamedev", ruleName);
792
+ if (fs6.existsSync(gamedevPath)) {
738
793
  hashes.rules[ruleName] = hashFile(gamedevPath);
739
794
  }
740
795
  }
741
796
  }
742
797
  for (const agent of agents) {
743
- const agentDir = path6.join(packageRoot, "agents", agent);
744
- if (fs5.existsSync(agentDir)) {
798
+ const agentDir = path7.join(packageRoot, "agents", agent);
799
+ if (fs6.existsSync(agentDir)) {
745
800
  const dirHash = hashDirectory(agentDir);
746
801
  const combined = createHash("sha256").update(JSON.stringify(dirHash)).digest("hex");
747
802
  hashes.agents[agent] = combined;
748
803
  }
749
804
  }
750
- const hooksDir = path6.join(packageRoot, "hooks");
751
- if (fs5.existsSync(hooksDir)) {
752
- for (const entry of fs5.readdirSync(hooksDir, { withFileTypes: true })) {
805
+ const hooksDir = path7.join(packageRoot, "hooks");
806
+ if (fs6.existsSync(hooksDir)) {
807
+ for (const entry of fs6.readdirSync(hooksDir, { withFileTypes: true })) {
753
808
  if (!entry.isDirectory()) {
754
- hashes.hooks[entry.name] = hashFile(path6.join(hooksDir, entry.name));
809
+ hashes.hooks[entry.name] = hashFile(path7.join(hooksDir, entry.name));
755
810
  }
756
811
  }
757
812
  }
@@ -920,7 +975,7 @@ var Installer = class {
920
975
  constructor(merged, opts) {
921
976
  this.root = opts.contentRoot ?? getPackageRoot();
922
977
  this.target = opts.target;
923
- this.claudeDir = path7.join(opts.target, ".claude");
978
+ this.claudeDir = path8.join(opts.target, ".claude");
924
979
  this.merged = merged;
925
980
  this.dryRun = opts.dryRun;
926
981
  this.shareFrom = opts.shareFrom;
@@ -952,22 +1007,22 @@ var Installer = class {
952
1007
  return this.logs;
953
1008
  }
954
1009
  backup() {
955
- if (!fs6.existsSync(this.claudeDir)) return;
956
- const backup = path7.join(this.target, ".claude.bak");
1010
+ if (!fs7.existsSync(this.claudeDir)) return;
1011
+ const backup = path8.join(this.target, ".claude.bak");
957
1012
  if (this.dryRun) {
958
1013
  this.log(" [dry-run] Would backup .claude/ -> .claude.bak/");
959
1014
  return;
960
1015
  }
961
- if (fs6.existsSync(backup)) {
962
- fs6.rmSync(backup, { recursive: true, force: true });
1016
+ if (fs7.existsSync(backup)) {
1017
+ fs7.rmSync(backup, { recursive: true, force: true });
963
1018
  }
964
- fs6.renameSync(this.claudeDir, backup);
1019
+ fs7.renameSync(this.claudeDir, backup);
965
1020
  this.log(" Backed up .claude/ -> .claude.bak/");
966
1021
  }
967
1022
  createDirs() {
968
1023
  if (this.dryRun) return;
969
1024
  for (const d of ["skills", "rules", "agents", "docs"]) {
970
- ensureDir(path7.join(this.claudeDir, d));
1025
+ ensureDir(path8.join(this.claudeDir, d));
971
1026
  }
972
1027
  }
973
1028
  get hasStatusline() {
@@ -975,7 +1030,7 @@ var Installer = class {
975
1030
  }
976
1031
  copyHooks() {
977
1032
  this.log("\nHooks:");
978
- const hooksDir = path7.join(this.claudeDir, "hooks");
1033
+ const hooksDir = path8.join(this.claudeDir, "hooks");
979
1034
  if (this.dryRun) {
980
1035
  if (this.shareFrom) {
981
1036
  this.log(" [dry-run] hooks/ -> symlink from main worktree");
@@ -986,8 +1041,8 @@ var Installer = class {
986
1041
  return;
987
1042
  }
988
1043
  if (this.shareFrom) {
989
- const sharedHooks = path7.join(this.shareFrom, ".claude", "hooks");
990
- if (fs6.existsSync(sharedHooks)) {
1044
+ const sharedHooks = path8.join(this.shareFrom, ".claude", "hooks");
1045
+ if (fs7.existsSync(sharedHooks)) {
991
1046
  const result = linkOrCopyDir(sharedHooks, hooksDir, "symlink");
992
1047
  if (result === "linked") {
993
1048
  this.sharedDirs.push("hooks");
@@ -999,17 +1054,17 @@ var Installer = class {
999
1054
  return;
1000
1055
  }
1001
1056
  }
1002
- const hooksSrc = path7.join(this.root, "hooks");
1003
- if (fs6.existsSync(hooksSrc)) {
1057
+ const hooksSrc = path8.join(this.root, "hooks");
1058
+ if (fs7.existsSync(hooksSrc)) {
1004
1059
  ensureDir(hooksDir);
1005
- for (const entry of fs6.readdirSync(hooksSrc, { withFileTypes: true })) {
1060
+ for (const entry of fs7.readdirSync(hooksSrc, { withFileTypes: true })) {
1006
1061
  if (entry.name === "statusline.sh") continue;
1007
- const src = path7.join(hooksSrc, entry.name);
1008
- const dst = path7.join(hooksDir, entry.name);
1062
+ const src = path8.join(hooksSrc, entry.name);
1063
+ const dst = path8.join(hooksDir, entry.name);
1009
1064
  if (entry.isDirectory()) {
1010
1065
  copyDirSync(src, dst);
1011
1066
  } else {
1012
- fs6.copyFileSync(src, dst);
1067
+ fs7.copyFileSync(src, dst);
1013
1068
  }
1014
1069
  }
1015
1070
  this.log(" [ok] hooks/");
@@ -1018,11 +1073,11 @@ var Installer = class {
1018
1073
  }
1019
1074
  copyStatusline() {
1020
1075
  if (!this.hasStatusline) return;
1021
- const statusline = path7.join(this.root, "hooks", "statusline.sh");
1022
- if (fs6.existsSync(statusline)) {
1023
- fs6.copyFileSync(
1076
+ const statusline = path8.join(this.root, "hooks", "statusline.sh");
1077
+ if (fs7.existsSync(statusline)) {
1078
+ fs7.copyFileSync(
1024
1079
  statusline,
1025
- path7.join(this.claudeDir, "statusline.sh")
1080
+ path8.join(this.claudeDir, "statusline.sh")
1026
1081
  );
1027
1082
  this.log(" [ok] statusline.sh (statusline profile)");
1028
1083
  }
@@ -1030,12 +1085,12 @@ var Installer = class {
1030
1085
  copySkills() {
1031
1086
  this.log("\nSkills:");
1032
1087
  for (const skill of this.merged.skills) {
1033
- const src = path7.join(this.root, "skills", skill);
1034
- if (!fs6.existsSync(src)) {
1088
+ const src = path8.join(this.root, "skills", skill);
1089
+ if (!fs7.existsSync(src)) {
1035
1090
  this.log(` WARN: Skill '${skill}' not found in skills/`);
1036
1091
  continue;
1037
1092
  }
1038
- const dst = path7.join(this.claudeDir, "skills", skill);
1093
+ const dst = path8.join(this.claudeDir, "skills", skill);
1039
1094
  if (this.dryRun) {
1040
1095
  this.log(` [dry-run] ${skill}`);
1041
1096
  } else {
@@ -1045,9 +1100,9 @@ var Installer = class {
1045
1100
  }
1046
1101
  const hasGamedev = this.merged.rules.gamedev.length > 0;
1047
1102
  if (hasGamedev) {
1048
- const tplSrc = path7.join(this.root, "templates", "gamedev");
1049
- if (fs6.existsSync(tplSrc)) {
1050
- const tplDst = path7.join(this.claudeDir, "skills", "_templates");
1103
+ const tplSrc = path8.join(this.root, "templates", "gamedev");
1104
+ if (fs7.existsSync(tplSrc)) {
1105
+ const tplDst = path8.join(this.claudeDir, "skills", "_templates");
1051
1106
  if (this.dryRun) {
1052
1107
  this.log(" [dry-run] _templates/");
1053
1108
  } else {
@@ -1060,33 +1115,33 @@ var Installer = class {
1060
1115
  copyRules() {
1061
1116
  this.log("\nRules:");
1062
1117
  for (const rule of this.merged.rules.universal) {
1063
- const src = path7.join(this.root, "rules", `${rule}.md`);
1064
- if (!fs6.existsSync(src)) {
1118
+ const src = path8.join(this.root, "rules", `${rule}.md`);
1119
+ if (!fs7.existsSync(src)) {
1065
1120
  this.log(` WARN: Rule '${rule}.md' not found`);
1066
1121
  continue;
1067
1122
  }
1068
1123
  if (this.dryRun) {
1069
1124
  this.log(` [dry-run] ${rule}.md`);
1070
1125
  } else {
1071
- fs6.copyFileSync(
1126
+ fs7.copyFileSync(
1072
1127
  src,
1073
- path7.join(this.claudeDir, "rules", `${rule}.md`)
1128
+ path8.join(this.claudeDir, "rules", `${rule}.md`)
1074
1129
  );
1075
1130
  this.log(` [ok] ${rule}.md`);
1076
1131
  }
1077
1132
  }
1078
1133
  for (const rule of this.merged.rules.gamedev) {
1079
- const src = path7.join(this.root, "rules", "gamedev", `${rule}.md`);
1080
- if (!fs6.existsSync(src)) {
1134
+ const src = path8.join(this.root, "rules", "gamedev", `${rule}.md`);
1135
+ if (!fs7.existsSync(src)) {
1081
1136
  this.log(` WARN: Rule '${rule}.md' not found`);
1082
1137
  continue;
1083
1138
  }
1084
1139
  if (this.dryRun) {
1085
1140
  this.log(` [dry-run] ${rule}.md`);
1086
1141
  } else {
1087
- fs6.copyFileSync(
1142
+ fs7.copyFileSync(
1088
1143
  src,
1089
- path7.join(this.claudeDir, "rules", `${rule}.md`)
1144
+ path8.join(this.claudeDir, "rules", `${rule}.md`)
1090
1145
  );
1091
1146
  this.log(` [ok] ${rule}.md`);
1092
1147
  }
@@ -1095,26 +1150,24 @@ var Installer = class {
1095
1150
  copyAgents() {
1096
1151
  if (this.merged.agents.length === 0) return;
1097
1152
  this.log("\nAgents:");
1098
- for (const agentDir of this.merged.agents) {
1099
- const src = path7.join(this.root, "agents", agentDir);
1100
- if (!fs6.existsSync(src)) {
1101
- this.log(` WARN: Agent dir '${agentDir}' not found`);
1153
+ for (const entry of this.merged.agents) {
1154
+ const label = agentEntryLabel(entry);
1155
+ if (!agentSourcePath(this.root, entry)) {
1156
+ this.log(` WARN: Agent entry '${entry}' not found`);
1102
1157
  continue;
1103
1158
  }
1104
- const dst = path7.join(this.claudeDir, "agents", agentDir);
1105
1159
  if (this.dryRun) {
1106
- this.log(` [dry-run] agents/${agentDir}/`);
1107
- } else {
1108
- copyDirSync(src, dst);
1109
- const count = fs6.readdirSync(dst, { recursive: true }).filter((f) => String(f).endsWith(".md")).length;
1110
- this.log(` [ok] agents/${agentDir}/ (${count} agents)`);
1160
+ this.log(` [dry-run] ${label}`);
1161
+ continue;
1111
1162
  }
1163
+ const count = copyAgentEntry(this.root, this.claudeDir, entry);
1164
+ this.log(` [ok] ${label}${count === 1 ? "" : ` (${count} agents)`}`);
1112
1165
  }
1113
1166
  }
1114
1167
  copyDocs() {
1115
- const docsDir = path7.join(this.claudeDir, "docs");
1116
- const docsSrc = path7.join(this.root, "docs");
1117
- if (!fs6.existsSync(docsSrc)) return;
1168
+ const docsDir = path8.join(this.claudeDir, "docs");
1169
+ const docsSrc = path8.join(this.root, "docs");
1170
+ if (!fs7.existsSync(docsSrc)) return;
1118
1171
  this.log("\nDocs:");
1119
1172
  if (this.dryRun) {
1120
1173
  if (this.shareFrom) {
@@ -1125,8 +1178,8 @@ var Installer = class {
1125
1178
  return;
1126
1179
  }
1127
1180
  if (this.shareFrom) {
1128
- const sharedDocs = path7.join(this.shareFrom, ".claude", "docs");
1129
- if (fs6.existsSync(sharedDocs)) {
1181
+ const sharedDocs = path8.join(this.shareFrom, ".claude", "docs");
1182
+ if (fs7.existsSync(sharedDocs)) {
1130
1183
  const result = linkOrCopyDir(sharedDocs, docsDir, "symlink");
1131
1184
  if (result === "linked") {
1132
1185
  this.sharedDirs.push("docs");
@@ -1157,8 +1210,8 @@ var Installer = class {
1157
1210
  );
1158
1211
  return;
1159
1212
  }
1160
- const settingsPath = path7.join(this.claudeDir, "settings.json");
1161
- fs6.writeFileSync(
1213
+ const settingsPath = path8.join(this.claudeDir, "settings.json");
1214
+ fs7.writeFileSync(
1162
1215
  settingsPath,
1163
1216
  JSON.stringify(settings, null, 2) + "\n",
1164
1217
  "utf-8"
@@ -1167,15 +1220,15 @@ var Installer = class {
1167
1220
  }
1168
1221
  };
1169
1222
  function clean(target) {
1170
- const claudeDir = path7.join(target, ".claude");
1171
- const backup = path7.join(target, ".claude.bak");
1172
- if (!fs6.existsSync(claudeDir)) {
1223
+ const claudeDir = path8.join(target, ".claude");
1224
+ const backup = path8.join(target, ".claude.bak");
1225
+ if (!fs7.existsSync(claudeDir)) {
1173
1226
  console.log(`No .claude/ found in ${target} \u2014 nothing to clean.`);
1174
1227
  return;
1175
1228
  }
1176
- const manifestFile = path7.join(claudeDir, "arcane-manifest.json");
1177
- if (fs6.existsSync(manifestFile)) {
1178
- const data = JSON.parse(fs6.readFileSync(manifestFile, "utf-8"));
1229
+ const manifestFile = path8.join(claudeDir, "arcane-manifest.json");
1230
+ if (fs7.existsSync(manifestFile)) {
1231
+ const data = JSON.parse(fs7.readFileSync(manifestFile, "utf-8"));
1179
1232
  const profiles = (data.profiles ?? []).filter(
1180
1233
  (p2) => p2 !== "core"
1181
1234
  );
@@ -1183,49 +1236,49 @@ function clean(target) {
1183
1236
  console.log(` Installed: ${data.installed_at ?? "unknown"}`);
1184
1237
  const shared = data.worktree?.shared_dirs ?? [];
1185
1238
  for (const dir of shared) {
1186
- const p2 = path7.join(claudeDir, dir);
1239
+ const p2 = path8.join(claudeDir, dir);
1187
1240
  if (isSymlinkOrJunction(p2)) {
1188
- fs6.unlinkSync(p2);
1241
+ fs7.unlinkSync(p2);
1189
1242
  console.log(` [ok] Unlinked shared ${dir}/`);
1190
1243
  }
1191
1244
  }
1192
1245
  }
1193
1246
  for (const sub of ["hooks", "docs", "skills", "agents", "rules"]) {
1194
- const p2 = path7.join(claudeDir, sub);
1247
+ const p2 = path8.join(claudeDir, sub);
1195
1248
  safeRemove(p2);
1196
1249
  }
1197
- const remaining = fs6.readdirSync(claudeDir);
1250
+ const remaining = fs7.readdirSync(claudeDir);
1198
1251
  for (const f of remaining) {
1199
- const p2 = path7.join(claudeDir, f);
1252
+ const p2 = path8.join(claudeDir, f);
1200
1253
  safeRemove(p2);
1201
1254
  }
1202
- if (fs6.existsSync(claudeDir)) {
1203
- fs6.rmSync(claudeDir, { recursive: true, force: true });
1255
+ if (fs7.existsSync(claudeDir)) {
1256
+ fs7.rmSync(claudeDir, { recursive: true, force: true });
1204
1257
  }
1205
- if (fs6.existsSync(backup)) {
1206
- fs6.rmSync(backup, { recursive: true, force: true });
1258
+ if (fs7.existsSync(backup)) {
1259
+ fs7.rmSync(backup, { recursive: true, force: true });
1207
1260
  }
1208
1261
  console.log(`
1209
1262
  [ok] Removed .claude/ from ${target}`);
1210
1263
  }
1211
1264
 
1212
1265
  // src/content-source.ts
1213
- import fs8 from "fs";
1214
- import path9 from "path";
1266
+ import fs9 from "fs";
1267
+ import path10 from "path";
1215
1268
  import os2 from "os";
1216
1269
 
1217
1270
  // src/cache.ts
1218
- import fs7 from "fs";
1219
- import path8 from "path";
1271
+ import fs8 from "fs";
1272
+ import path9 from "path";
1220
1273
  import os from "os";
1221
- var ARCANE_DIR = path8.join(os.homedir(), ".arcane");
1222
- var CACHE_DIR = path8.join(ARCANE_DIR, "cache");
1274
+ var ARCANE_DIR = path9.join(os.homedir(), ".arcane");
1275
+ var CACHE_DIR = path9.join(ARCANE_DIR, "cache");
1223
1276
  function getCachePath(version) {
1224
- return path8.join(CACHE_DIR, version);
1277
+ return path9.join(CACHE_DIR, version);
1225
1278
  }
1226
1279
  function isCached(version) {
1227
1280
  const versionDir = getCachePath(version);
1228
- return fs7.existsSync(versionDir) && fs7.existsSync(path8.join(versionDir, ".cache-meta.json"));
1281
+ return fs8.existsSync(versionDir) && fs8.existsSync(path9.join(versionDir, ".cache-meta.json"));
1229
1282
  }
1230
1283
  function getCachedContentRoot(version) {
1231
1284
  if (!isCached(version)) return null;
@@ -1233,34 +1286,34 @@ function getCachedContentRoot(version) {
1233
1286
  }
1234
1287
  function storeInCache(version, contentDir) {
1235
1288
  const versionDir = getCachePath(version);
1236
- if (fs7.existsSync(versionDir)) {
1237
- fs7.rmSync(versionDir, { recursive: true, force: true });
1289
+ if (fs8.existsSync(versionDir)) {
1290
+ fs8.rmSync(versionDir, { recursive: true, force: true });
1238
1291
  }
1239
- fs7.mkdirSync(versionDir, { recursive: true });
1292
+ fs8.mkdirSync(versionDir, { recursive: true });
1240
1293
  for (const dir of ["skills", "rules", "agents", "hooks", "profiles", "templates", "docs"]) {
1241
- const src = path8.join(contentDir, dir);
1242
- if (fs7.existsSync(src)) {
1243
- copyDirRecursive(src, path8.join(versionDir, dir));
1294
+ const src = path9.join(contentDir, dir);
1295
+ if (fs8.existsSync(src)) {
1296
+ copyDirRecursive(src, path9.join(versionDir, dir));
1244
1297
  }
1245
1298
  }
1246
1299
  const meta = {
1247
1300
  version,
1248
1301
  cached_at: (/* @__PURE__ */ new Date()).toISOString()
1249
1302
  };
1250
- fs7.writeFileSync(
1251
- path8.join(versionDir, ".cache-meta.json"),
1303
+ fs8.writeFileSync(
1304
+ path9.join(versionDir, ".cache-meta.json"),
1252
1305
  JSON.stringify(meta, null, 2) + "\n"
1253
1306
  );
1254
1307
  return versionDir;
1255
1308
  }
1256
1309
  function pruneCache(keepCount = 3) {
1257
- if (!fs7.existsSync(CACHE_DIR)) return;
1258
- const entries = fs7.readdirSync(CACHE_DIR, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => {
1259
- const metaPath = path8.join(CACHE_DIR, e.name, ".cache-meta.json");
1310
+ if (!fs8.existsSync(CACHE_DIR)) return;
1311
+ const entries = fs8.readdirSync(CACHE_DIR, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => {
1312
+ const metaPath = path9.join(CACHE_DIR, e.name, ".cache-meta.json");
1260
1313
  let cachedAt = 0;
1261
- if (fs7.existsSync(metaPath)) {
1314
+ if (fs8.existsSync(metaPath)) {
1262
1315
  try {
1263
- const meta = JSON.parse(fs7.readFileSync(metaPath, "utf-8"));
1316
+ const meta = JSON.parse(fs8.readFileSync(metaPath, "utf-8"));
1264
1317
  cachedAt = new Date(meta.cached_at).getTime();
1265
1318
  } catch {
1266
1319
  cachedAt = 0;
@@ -1269,18 +1322,18 @@ function pruneCache(keepCount = 3) {
1269
1322
  return { name: e.name, cachedAt };
1270
1323
  }).sort((a, b) => b.cachedAt - a.cachedAt);
1271
1324
  for (const entry of entries.slice(keepCount)) {
1272
- fs7.rmSync(path8.join(CACHE_DIR, entry.name), { recursive: true, force: true });
1325
+ fs8.rmSync(path9.join(CACHE_DIR, entry.name), { recursive: true, force: true });
1273
1326
  }
1274
1327
  }
1275
1328
  function copyDirRecursive(src, dest) {
1276
- fs7.mkdirSync(dest, { recursive: true });
1277
- for (const entry of fs7.readdirSync(src, { withFileTypes: true })) {
1278
- const srcPath = path8.join(src, entry.name);
1279
- const destPath = path8.join(dest, entry.name);
1329
+ fs8.mkdirSync(dest, { recursive: true });
1330
+ for (const entry of fs8.readdirSync(src, { withFileTypes: true })) {
1331
+ const srcPath = path9.join(src, entry.name);
1332
+ const destPath = path9.join(dest, entry.name);
1280
1333
  if (entry.isDirectory()) {
1281
1334
  copyDirRecursive(srcPath, destPath);
1282
1335
  } else {
1283
- fs7.copyFileSync(srcPath, destPath);
1336
+ fs8.copyFileSync(srcPath, destPath);
1284
1337
  }
1285
1338
  }
1286
1339
  }
@@ -1296,7 +1349,7 @@ var BundledContentSource = class {
1296
1349
  }
1297
1350
  async isAvailable() {
1298
1351
  const root = getPackageRoot();
1299
- return fs8.existsSync(path9.join(root, "skills"));
1352
+ return fs9.existsSync(path10.join(root, "skills"));
1300
1353
  }
1301
1354
  async getVersion() {
1302
1355
  return getPackageVersion();
@@ -1336,14 +1389,14 @@ var GitHubContentSource = class {
1336
1389
  if (isCached(version)) {
1337
1390
  return getCachedContentRoot(version);
1338
1391
  }
1339
- const tmpDir = fs8.mkdtempSync(path9.join(os2.tmpdir(), "arcane-github-"));
1392
+ const tmpDir = fs9.mkdtempSync(path10.join(os2.tmpdir(), "arcane-github-"));
1340
1393
  try {
1341
1394
  await this.downloadAndExtract(tmpDir);
1342
1395
  const contentRoot = storeInCache(version, this.findExtractedRoot(tmpDir));
1343
1396
  pruneCache(3);
1344
1397
  return contentRoot;
1345
1398
  } finally {
1346
- fs8.rmSync(tmpDir, { recursive: true, force: true });
1399
+ fs9.rmSync(tmpDir, { recursive: true, force: true });
1347
1400
  }
1348
1401
  }
1349
1402
  async isAvailable() {
@@ -1399,8 +1452,8 @@ var GitHubContentSource = class {
1399
1452
  throw new Error(`Failed to download tarball: ${response.status}`);
1400
1453
  }
1401
1454
  const tar = await import("tar");
1402
- const tarballPath = path9.join(destDir, "repo.tar.gz");
1403
- const fileStream = fs8.createWriteStream(tarballPath);
1455
+ const tarballPath = path10.join(destDir, "repo.tar.gz");
1456
+ const fileStream = fs9.createWriteStream(tarballPath);
1404
1457
  const reader = response.body.getReader();
1405
1458
  const writable = new WritableStream({
1406
1459
  write(chunk) {
@@ -1425,12 +1478,12 @@ var GitHubContentSource = class {
1425
1478
  });
1426
1479
  }
1427
1480
  findExtractedRoot(tmpDir) {
1428
- const entries = fs8.readdirSync(tmpDir, { withFileTypes: true });
1481
+ const entries = fs9.readdirSync(tmpDir, { withFileTypes: true });
1429
1482
  const dirs = entries.filter(
1430
1483
  (e) => e.isDirectory() && e.name !== "." && e.name !== ".."
1431
1484
  );
1432
1485
  if (dirs.length === 1) {
1433
- return path9.join(tmpDir, dirs[0].name);
1486
+ return path10.join(tmpDir, dirs[0].name);
1434
1487
  }
1435
1488
  return tmpDir;
1436
1489
  }
@@ -1485,14 +1538,14 @@ async function resolveContentSourceForVersion(version, opts = {}) {
1485
1538
  return source;
1486
1539
  }
1487
1540
  function findLatestCache() {
1488
- const cacheDir = path9.join(os2.homedir(), ".arcane", "cache");
1489
- if (!fs8.existsSync(cacheDir)) return null;
1490
- const entries = fs8.readdirSync(cacheDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => {
1491
- const metaPath = path9.join(cacheDir, e.name, ".cache-meta.json");
1541
+ const cacheDir = path10.join(os2.homedir(), ".arcane", "cache");
1542
+ if (!fs9.existsSync(cacheDir)) return null;
1543
+ const entries = fs9.readdirSync(cacheDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => {
1544
+ const metaPath = path10.join(cacheDir, e.name, ".cache-meta.json");
1492
1545
  let cachedAt = 0;
1493
- if (fs8.existsSync(metaPath)) {
1546
+ if (fs9.existsSync(metaPath)) {
1494
1547
  try {
1495
- const meta = JSON.parse(fs8.readFileSync(metaPath, "utf-8"));
1548
+ const meta = JSON.parse(fs9.readFileSync(metaPath, "utf-8"));
1496
1549
  cachedAt = new Date(meta.cached_at).getTime();
1497
1550
  } catch {
1498
1551
  cachedAt = 0;
@@ -1504,14 +1557,14 @@ function findLatestCache() {
1504
1557
  }
1505
1558
 
1506
1559
  // src/registry.ts
1507
- import path10 from "path";
1560
+ import path11 from "path";
1508
1561
  import os3 from "os";
1509
1562
  var REGISTRY_VERSION = 1;
1510
1563
  function arcaneHome() {
1511
- return process.env.ARCANE_HOME ?? path10.join(os3.homedir(), ".arcane");
1564
+ return process.env.ARCANE_HOME ?? path11.join(os3.homedir(), ".arcane");
1512
1565
  }
1513
1566
  function registryPath() {
1514
- return path10.join(arcaneHome(), "installations.json");
1567
+ return path11.join(arcaneHome(), "installations.json");
1515
1568
  }
1516
1569
  function readRegistryFile() {
1517
1570
  const p2 = registryPath();
@@ -1536,7 +1589,7 @@ function writeRegistryFile(registry) {
1536
1589
  }
1537
1590
  }
1538
1591
  function registerInstallation(target) {
1539
- const abs = path10.resolve(target);
1592
+ const abs = path11.resolve(target);
1540
1593
  const registry = readRegistryFile();
1541
1594
  if (registry.installations.some((e) => e.path === abs)) {
1542
1595
  return;
@@ -1563,8 +1616,8 @@ async function installCommand(profileExpr, opts) {
1563
1616
  quiet: !profileExpr
1564
1617
  });
1565
1618
  const root = await source.getContentRoot();
1566
- const profilesDir = path11.join(root, "profiles");
1567
- const target = path11.resolve(opts.target ?? process.cwd());
1619
+ const profilesDir = path12.join(root, "profiles");
1620
+ const target = path12.resolve(opts.target ?? process.cwd());
1568
1621
  if (!profileExpr) {
1569
1622
  const profiles = listProfiles(profilesDir);
1570
1623
  if (process.stdin.isTTY && process.stdout.isTTY) {
@@ -1635,21 +1688,21 @@ Installing profile: ${chalk2.cyan(profileExpr)}`)
1635
1688
  }
1636
1689
 
1637
1690
  // src/commands/add.ts
1638
- import fs10 from "fs";
1639
- import path13 from "path";
1691
+ import fs11 from "fs";
1692
+ import path14 from "path";
1640
1693
  import chalk3 from "chalk";
1641
1694
 
1642
1695
  // src/skills-catalog.ts
1643
- import fs9 from "fs";
1644
- import path12 from "path";
1696
+ import fs10 from "fs";
1697
+ import path13 from "path";
1645
1698
  var FRONTMATTER_BYTES = 4096;
1646
1699
  function listSkills(skillsDir) {
1647
- if (!fs9.existsSync(skillsDir)) return [];
1648
- return fs9.readdirSync(skillsDir, { withFileTypes: true }).filter(
1649
- (d) => d.isDirectory() && !d.name.startsWith("_") && fs9.existsSync(path12.join(skillsDir, d.name, "SKILL.md"))
1700
+ if (!fs10.existsSync(skillsDir)) return [];
1701
+ return fs10.readdirSync(skillsDir, { withFileTypes: true }).filter(
1702
+ (d) => d.isDirectory() && !d.name.startsWith("_") && fs10.existsSync(path13.join(skillsDir, d.name, "SKILL.md"))
1650
1703
  ).map((d) => ({
1651
1704
  name: d.name,
1652
- description: readDescription(path12.join(skillsDir, d.name, "SKILL.md"))
1705
+ description: readDescription(path13.join(skillsDir, d.name, "SKILL.md"))
1653
1706
  })).sort((a, b) => a.name.localeCompare(b.name));
1654
1707
  }
1655
1708
  function readDescription(skillFile) {
@@ -1670,13 +1723,13 @@ function readDescription(skillFile) {
1670
1723
  return inline.replace(/^["']|["']$/g, "").trim();
1671
1724
  }
1672
1725
  function readHead(file) {
1673
- const fd = fs9.openSync(file, "r");
1726
+ const fd = fs10.openSync(file, "r");
1674
1727
  try {
1675
1728
  const buf = Buffer.alloc(FRONTMATTER_BYTES);
1676
- const read = fs9.readSync(fd, buf, 0, FRONTMATTER_BYTES, 0);
1729
+ const read = fs10.readSync(fd, buf, 0, FRONTMATTER_BYTES, 0);
1677
1730
  return buf.subarray(0, read).toString("utf-8");
1678
1731
  } finally {
1679
- fs9.closeSync(fd);
1732
+ fs10.closeSync(fd);
1680
1733
  }
1681
1734
  }
1682
1735
 
@@ -1700,15 +1753,15 @@ async function addCommand(items = []) {
1700
1753
  return;
1701
1754
  }
1702
1755
  const selected = await runAddWizard({
1703
- profiles: listProfiles(path13.join(root, "profiles")),
1704
- skills: listSkills(path13.join(root, "skills")),
1756
+ profiles: listProfiles(path14.join(root, "profiles")),
1757
+ skills: listSkills(path14.join(root, "skills")),
1705
1758
  installedProfiles: manifest.profiles,
1706
1759
  installedSkills: manifest.installed_skills
1707
1760
  });
1708
1761
  if (!selected) return;
1709
1762
  items = selected;
1710
1763
  }
1711
- const claudeDir = path13.join(target, ".claude");
1764
+ const claudeDir = path14.join(target, ".claude");
1712
1765
  const added = [];
1713
1766
  const skipped = [];
1714
1767
  const notFound = [];
@@ -1718,8 +1771,8 @@ async function addCommand(items = []) {
1718
1771
  for (const item of items) {
1719
1772
  if (item.startsWith("+")) {
1720
1773
  const profileName = item.slice(1);
1721
- const profilePath = path13.join(root, "profiles", `${profileName}.yaml`);
1722
- if (!fs10.existsSync(profilePath)) {
1774
+ const profilePath = path14.join(root, "profiles", `${profileName}.yaml`);
1775
+ if (!fs11.existsSync(profilePath)) {
1723
1776
  console.error(chalk3.red(` Profile '${profileName}' not found`));
1724
1777
  continue;
1725
1778
  }
@@ -1732,10 +1785,10 @@ async function addCommand(items = []) {
1732
1785
  }
1733
1786
  for (const rule of profile.rules.universal) {
1734
1787
  if (!manifest.installed_rules.includes(rule)) {
1735
- const src = path13.join(root, "rules", `${rule}.md`);
1736
- if (fs10.existsSync(src)) {
1737
- ensureDir(path13.join(claudeDir, "rules"));
1738
- fs10.copyFileSync(src, path13.join(claudeDir, "rules", `${rule}.md`));
1788
+ const src = path14.join(root, "rules", `${rule}.md`);
1789
+ if (fs11.existsSync(src)) {
1790
+ ensureDir(path14.join(claudeDir, "rules"));
1791
+ fs11.copyFileSync(src, path14.join(claudeDir, "rules", `${rule}.md`));
1739
1792
  manifest.installed_rules.push(rule);
1740
1793
  addedRules.push(rule);
1741
1794
  }
@@ -1743,30 +1796,28 @@ async function addCommand(items = []) {
1743
1796
  }
1744
1797
  for (const rule of profile.rules.gamedev) {
1745
1798
  if (!manifest.installed_rules.includes(rule)) {
1746
- const src = path13.join(root, "rules", "gamedev", `${rule}.md`);
1747
- if (fs10.existsSync(src)) {
1748
- ensureDir(path13.join(claudeDir, "rules"));
1749
- fs10.copyFileSync(src, path13.join(claudeDir, "rules", `${rule}.md`));
1799
+ const src = path14.join(root, "rules", "gamedev", `${rule}.md`);
1800
+ if (fs11.existsSync(src)) {
1801
+ ensureDir(path14.join(claudeDir, "rules"));
1802
+ fs11.copyFileSync(src, path14.join(claudeDir, "rules", `${rule}.md`));
1750
1803
  manifest.installed_rules.push(rule);
1751
1804
  addedRules.push(rule);
1752
1805
  }
1753
1806
  }
1754
1807
  }
1755
- for (const agentDir of profile.agents) {
1756
- if (!manifest.installed_agents.includes(agentDir)) {
1757
- const src = path13.join(root, "agents", agentDir);
1758
- if (fs10.existsSync(src)) {
1759
- const dst = path13.join(claudeDir, "agents", agentDir);
1760
- copyDirSync(src, dst);
1761
- manifest.installed_agents.push(agentDir);
1762
- addedAgents.push(agentDir);
1763
- }
1808
+ for (const entry of profile.agents) {
1809
+ const { division } = parseAgentEntry(entry);
1810
+ if (manifest.installed_agents.includes(entry)) continue;
1811
+ if (isGranular(entry) && manifest.installed_agents.includes(division)) continue;
1812
+ if (copyAgentEntry(root, claudeDir, entry) !== null) {
1813
+ manifest.installed_agents.push(entry);
1814
+ addedAgents.push(entry);
1764
1815
  }
1765
1816
  }
1766
1817
  if (profileName === "statusline") {
1767
- const statuslineSrc = path13.join(root, "hooks", "statusline.sh");
1768
- if (fs10.existsSync(statuslineSrc)) {
1769
- fs10.copyFileSync(statuslineSrc, path13.join(claudeDir, "statusline.sh"));
1818
+ const statuslineSrc = path14.join(root, "hooks", "statusline.sh");
1819
+ if (fs11.existsSync(statuslineSrc)) {
1820
+ fs11.copyFileSync(statuslineSrc, path14.join(claudeDir, "statusline.sh"));
1770
1821
  statuslineAdded = true;
1771
1822
  }
1772
1823
  }
@@ -1808,7 +1859,7 @@ Added ${totalAdded} items:`
1808
1859
  );
1809
1860
  for (const s of added) console.log(chalk3.green(` [ok] skill: ${s}`));
1810
1861
  for (const r of addedRules) console.log(chalk3.green(` [ok] rule: ${r}`));
1811
- for (const a of addedAgents) console.log(chalk3.green(` [ok] agents: ${a}/`));
1862
+ for (const a of addedAgents) console.log(chalk3.green(` [ok] ${agentEntryLabel(a)}`));
1812
1863
  if (statuslineAdded) console.log(chalk3.green(" [ok] statusline.sh"));
1813
1864
  for (const s of skipped)
1814
1865
  console.log(chalk3.dim(` [skip] ${s} (already installed)`));
@@ -1824,10 +1875,10 @@ Added ${totalAdded} items:`
1824
1875
  }
1825
1876
  }
1826
1877
  function printCatalog(root, installedProfiles, installedSkills) {
1827
- const profiles = listProfiles(path13.join(root, "profiles")).filter(
1878
+ const profiles = listProfiles(path14.join(root, "profiles")).filter(
1828
1879
  (pr) => !installedProfiles.includes(pr.name)
1829
1880
  );
1830
- const skills = listSkills(path13.join(root, "skills")).filter(
1881
+ const skills = listSkills(path14.join(root, "skills")).filter(
1831
1882
  (sk) => !installedSkills.includes(sk.name)
1832
1883
  );
1833
1884
  if (profiles.length === 0 && skills.length === 0) {
@@ -1853,15 +1904,15 @@ function printCatalog(root, installedProfiles, installedSkills) {
1853
1904
  }
1854
1905
  function addSkill(root, target, skill, installed) {
1855
1906
  if (installed.includes(skill)) return "skipped";
1856
- const src = path13.join(root, "skills", skill);
1857
- if (!fs10.existsSync(src)) return "not-found";
1858
- const dst = path13.join(target, ".claude", "skills", skill);
1907
+ const src = path14.join(root, "skills", skill);
1908
+ if (!fs11.existsSync(src)) return "not-found";
1909
+ const dst = path14.join(target, ".claude", "skills", skill);
1859
1910
  copyDirSync(src, dst);
1860
1911
  return "added";
1861
1912
  }
1862
1913
  function mergePermissions(claudeDir, newPerms) {
1863
- const settingsPath = path13.join(claudeDir, "settings.json");
1864
- if (!fs10.existsSync(settingsPath)) return;
1914
+ const settingsPath = path14.join(claudeDir, "settings.json");
1915
+ if (!fs11.existsSync(settingsPath)) return;
1865
1916
  const settings = readJsonSync(settingsPath);
1866
1917
  const perms = settings.permissions ?? { allow: [], deny: [] };
1867
1918
  const allowSet = new Set(perms.allow);
@@ -1874,8 +1925,8 @@ function mergePermissions(claudeDir, newPerms) {
1874
1925
  writeJsonSync(settingsPath, settings);
1875
1926
  }
1876
1927
  function addStatuslineToSettings(claudeDir) {
1877
- const settingsPath = path13.join(claudeDir, "settings.json");
1878
- if (!fs10.existsSync(settingsPath)) return;
1928
+ const settingsPath = path14.join(claudeDir, "settings.json");
1929
+ if (!fs11.existsSync(settingsPath)) return;
1879
1930
  const settings = readJsonSync(settingsPath);
1880
1931
  if (settings.statusLine) return;
1881
1932
  settings.statusLine = {
@@ -1886,8 +1937,8 @@ function addStatuslineToSettings(claudeDir) {
1886
1937
  }
1887
1938
 
1888
1939
  // src/commands/remove.ts
1889
- import fs11 from "fs";
1890
- import path14 from "path";
1940
+ import fs12 from "fs";
1941
+ import path15 from "path";
1891
1942
  import chalk4 from "chalk";
1892
1943
  var CORE_SKILLS = [
1893
1944
  "commit",
@@ -1952,7 +2003,7 @@ async function removeCommand(items, opts = {}) {
1952
2003
  }
1953
2004
  manifest.total_skills = manifest.installed_skills.length;
1954
2005
  manifest.total_rules = manifest.installed_rules.length;
1955
- const mp = path14.join(target, ".claude", "arcane-manifest.json");
2006
+ const mp = path15.join(target, ".claude", "arcane-manifest.json");
1956
2007
  writeJsonSync(mp, manifest);
1957
2008
  const totalRemoved = removedSkills.length + removedAgents.length + removedProfiles.length;
1958
2009
  console.log(chalk4.bold(`
@@ -1973,9 +2024,9 @@ function removeSkill(target, skill, manifest) {
1973
2024
  );
1974
2025
  return "skipped";
1975
2026
  }
1976
- const skillDir = path14.join(target, ".claude", "skills", skill);
1977
- if (!fs11.existsSync(skillDir)) return "skipped";
1978
- fs11.rmSync(skillDir, { recursive: true, force: true });
2027
+ const skillDir = path15.join(target, ".claude", "skills", skill);
2028
+ if (!fs12.existsSync(skillDir)) return "skipped";
2029
+ fs12.rmSync(skillDir, { recursive: true, force: true });
1979
2030
  manifest.installed_skills = manifest.installed_skills.filter(
1980
2031
  (s) => s !== skill
1981
2032
  );
@@ -1994,8 +2045,8 @@ function removeProfile(root, target, profileName, manifest) {
1994
2045
  );
1995
2046
  return { removed: false, skills: [], agents: [] };
1996
2047
  }
1997
- const profilePath = path14.join(root, "profiles", `${profileName}.yaml`);
1998
- if (!fs11.existsSync(profilePath)) {
2048
+ const profilePath = path15.join(root, "profiles", `${profileName}.yaml`);
2049
+ if (!fs12.existsSync(profilePath)) {
1999
2050
  console.warn(
2000
2051
  chalk4.yellow(` WARNING: Profile '${profileName}.yaml' not found.`)
2001
2052
  );
@@ -2009,8 +2060,8 @@ function removeProfile(root, target, profileName, manifest) {
2009
2060
  const sharedAgents = /* @__PURE__ */ new Set();
2010
2061
  const sharedRules = /* @__PURE__ */ new Set();
2011
2062
  for (const rp of remainingProfiles) {
2012
- const rpPath = path14.join(root, "profiles", `${rp}.yaml`);
2013
- if (!fs11.existsSync(rpPath)) continue;
2063
+ const rpPath = path15.join(root, "profiles", `${rp}.yaml`);
2064
+ if (!fs12.existsSync(rpPath)) continue;
2014
2065
  const rpDef = parseProfile(rpPath);
2015
2066
  rpDef.skills.forEach((s) => sharedSkills.add(s));
2016
2067
  rpDef.agents.forEach((a) => sharedAgents.add(a));
@@ -2021,9 +2072,9 @@ function removeProfile(root, target, profileName, manifest) {
2021
2072
  for (const skill of profile.skills) {
2022
2073
  if (sharedSkills.has(skill)) continue;
2023
2074
  if (CORE_SKILLS.includes(skill)) continue;
2024
- const skillDir = path14.join(target, ".claude", "skills", skill);
2025
- if (fs11.existsSync(skillDir)) {
2026
- fs11.rmSync(skillDir, { recursive: true, force: true });
2075
+ const skillDir = path15.join(target, ".claude", "skills", skill);
2076
+ if (fs12.existsSync(skillDir)) {
2077
+ fs12.rmSync(skillDir, { recursive: true, force: true });
2027
2078
  removedSkills.push(skill);
2028
2079
  }
2029
2080
  manifest.installed_skills = manifest.installed_skills.filter(
@@ -2031,15 +2082,14 @@ function removeProfile(root, target, profileName, manifest) {
2031
2082
  );
2032
2083
  }
2033
2084
  const removedAgents = [];
2034
- for (const agent of profile.agents) {
2035
- if (sharedAgents.has(agent)) continue;
2036
- const agentDir = path14.join(target, ".claude", "agents", agent);
2037
- if (fs11.existsSync(agentDir)) {
2038
- fs11.rmSync(agentDir, { recursive: true, force: true });
2039
- removedAgents.push(agent);
2040
- }
2085
+ const claudeDir = path15.join(target, ".claude");
2086
+ for (const entry of profile.agents) {
2087
+ if (sharedAgents.has(entry)) continue;
2088
+ const { division } = parseAgentEntry(entry);
2089
+ if (isGranular(entry) && sharedAgents.has(division)) continue;
2090
+ if (removeAgentEntry(claudeDir, entry)) removedAgents.push(entry);
2041
2091
  manifest.installed_agents = manifest.installed_agents.filter(
2042
- (a) => a !== agent
2092
+ (a) => a !== entry
2043
2093
  );
2044
2094
  }
2045
2095
  for (const rule of [
@@ -2047,44 +2097,44 @@ function removeProfile(root, target, profileName, manifest) {
2047
2097
  ...profile.rules.gamedev
2048
2098
  ]) {
2049
2099
  if (sharedRules.has(rule)) continue;
2050
- const ruleFile = path14.join(target, ".claude", "rules", `${rule}.md`);
2051
- if (fs11.existsSync(ruleFile)) {
2052
- fs11.rmSync(ruleFile);
2100
+ const ruleFile = path15.join(target, ".claude", "rules", `${rule}.md`);
2101
+ if (fs12.existsSync(ruleFile)) {
2102
+ fs12.rmSync(ruleFile);
2053
2103
  }
2054
2104
  manifest.installed_rules = manifest.installed_rules.filter(
2055
2105
  (r) => r !== rule
2056
2106
  );
2057
2107
  }
2058
2108
  if (profileName === "statusline") {
2059
- const statuslineFile = path14.join(target, ".claude", "statusline.sh");
2060
- if (fs11.existsSync(statuslineFile)) {
2061
- fs11.rmSync(statuslineFile);
2109
+ const statuslineFile = path15.join(target, ".claude", "statusline.sh");
2110
+ if (fs12.existsSync(statuslineFile)) {
2111
+ fs12.rmSync(statuslineFile);
2062
2112
  }
2063
- removeStatuslineFromSettings(path14.join(target, ".claude"));
2113
+ removeStatuslineFromSettings(path15.join(target, ".claude"));
2064
2114
  }
2065
2115
  manifest.profiles = remainingProfiles;
2066
2116
  manifest.profile_command = remainingProfiles.filter((p2) => p2 !== "core").join("+");
2067
2117
  return { removed: true, skills: removedSkills, agents: removedAgents };
2068
2118
  }
2069
2119
  function removeStatuslineFromSettings(claudeDir) {
2070
- const settingsPath = path14.join(claudeDir, "settings.json");
2071
- if (!fs11.existsSync(settingsPath)) return;
2072
- const settings = JSON.parse(fs11.readFileSync(settingsPath, "utf-8"));
2120
+ const settingsPath = path15.join(claudeDir, "settings.json");
2121
+ if (!fs12.existsSync(settingsPath)) return;
2122
+ const settings = JSON.parse(fs12.readFileSync(settingsPath, "utf-8"));
2073
2123
  if (settings.statusLine) {
2074
2124
  delete settings.statusLine;
2075
- fs11.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
2125
+ fs12.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
2076
2126
  }
2077
2127
  }
2078
2128
 
2079
2129
  // src/commands/list.ts
2080
- import fs12 from "fs";
2081
- import path15 from "path";
2130
+ import fs13 from "fs";
2131
+ import path16 from "path";
2082
2132
  import chalk5 from "chalk";
2083
2133
  async function listCommand() {
2084
2134
  const source = await resolveContentSource({ quiet: true });
2085
2135
  const root = await source.getContentRoot();
2086
- const profilesDir = path15.join(root, "profiles");
2087
- const skillsDir = path15.join(root, "skills");
2136
+ const profilesDir = path16.join(root, "profiles");
2137
+ const skillsDir = path16.join(root, "skills");
2088
2138
  const target = process.cwd();
2089
2139
  const manifest = readManifest(target);
2090
2140
  const profiles = listProfiles(profilesDir);
@@ -2101,7 +2151,7 @@ async function listCommand() {
2101
2151
  }
2102
2152
  console.log();
2103
2153
  }
2104
- if (fs12.existsSync(skillsDir)) {
2154
+ if (fs13.existsSync(skillsDir)) {
2105
2155
  const allSkills = listSkills(skillsDir).map((s) => s.name);
2106
2156
  console.log(chalk5.bold(`
2107
2157
  === Skills (${allSkills.length}) ===
@@ -2168,8 +2218,8 @@ async function statusCommand() {
2168
2218
  }
2169
2219
 
2170
2220
  // src/commands/update.ts
2171
- import fs13 from "fs";
2172
- import path16 from "path";
2221
+ import fs14 from "fs";
2222
+ import path17 from "path";
2173
2223
  import os4 from "os";
2174
2224
  import chalk7 from "chalk";
2175
2225
 
@@ -2245,12 +2295,12 @@ async function updateCommand(opts) {
2245
2295
  }
2246
2296
  }
2247
2297
  const cwd = process.cwd();
2248
- if (fs13.existsSync(manifestPath(cwd))) {
2298
+ if (fs14.existsSync(manifestPath(cwd))) {
2249
2299
  registerInstallation(cwd);
2250
2300
  }
2251
2301
  const targets = pruneRegistry().map((e) => e.path);
2252
2302
  const globalTarget = os4.homedir();
2253
- if (!process.env.VITEST && fs13.existsSync(manifestPath(globalTarget)) && !targets.includes(globalTarget)) {
2303
+ if (!process.env.VITEST && fs14.existsSync(manifestPath(globalTarget)) && !targets.includes(globalTarget)) {
2254
2304
  targets.push(globalTarget);
2255
2305
  }
2256
2306
  if (targets.length === 0) {
@@ -2326,11 +2376,11 @@ Update available: ${chalk7.red(installedVersion)} -> ${chalk7.green(currentVersi
2326
2376
  );
2327
2377
  }
2328
2378
  const profileNames = manifest.profile_command.split("+").filter(Boolean);
2329
- const profilesDir = path16.join(root, "profiles");
2379
+ const profilesDir = path17.join(root, "profiles");
2330
2380
  const merged = mergeProfiles(profilesDir, profileNames);
2331
2381
  const allRules = [...merged.rules.universal, ...merged.rules.gamedev];
2332
2382
  const sourceHashes = computeSourceHashes(root, merged.skills, allRules, merged.agents);
2333
- const claudeDir = path16.join(target, ".claude");
2383
+ const claudeDir = path17.join(target, ".claude");
2334
2384
  const installedHashes = computeContentHashes(claudeDir);
2335
2385
  const items = computeUpdatePlan(
2336
2386
  manifest.content_hashes ?? null,
@@ -2523,48 +2573,48 @@ function applyUpdates(items, root, claudeDir, merged, force) {
2523
2573
  function applyItem(item, root, claudeDir) {
2524
2574
  const src = getSourcePath(item, root);
2525
2575
  const dst = getInstalledPath(item, claudeDir);
2526
- if (!src || !fs13.existsSync(src)) return;
2576
+ if (!src || !fs14.existsSync(src)) return;
2527
2577
  if (item.type === "skill" || item.type === "agent") {
2528
- if (fs13.existsSync(dst)) {
2529
- fs13.rmSync(dst, { recursive: true, force: true });
2578
+ if (fs14.existsSync(dst)) {
2579
+ fs14.rmSync(dst, { recursive: true, force: true });
2530
2580
  }
2531
2581
  copyDirSync(src, dst);
2532
2582
  } else {
2533
- const dir = path16.dirname(dst);
2534
- if (!fs13.existsSync(dir)) {
2535
- fs13.mkdirSync(dir, { recursive: true });
2583
+ const dir = path17.dirname(dst);
2584
+ if (!fs14.existsSync(dir)) {
2585
+ fs14.mkdirSync(dir, { recursive: true });
2536
2586
  }
2537
- fs13.copyFileSync(src, dst);
2587
+ fs14.copyFileSync(src, dst);
2538
2588
  }
2539
2589
  }
2540
2590
  function backupItem(item, claudeDir) {
2541
2591
  const installed = getInstalledPath(item, claudeDir);
2542
- if (!fs13.existsSync(installed)) return;
2592
+ if (!fs14.existsSync(installed)) return;
2543
2593
  const backupPath = installed + ".bak";
2544
- if (fs13.existsSync(backupPath)) {
2545
- fs13.rmSync(backupPath, { recursive: true, force: true });
2594
+ if (fs14.existsSync(backupPath)) {
2595
+ fs14.rmSync(backupPath, { recursive: true, force: true });
2546
2596
  }
2547
- fs13.renameSync(installed, backupPath);
2597
+ fs14.renameSync(installed, backupPath);
2548
2598
  }
2549
2599
  function removeItem(item, claudeDir) {
2550
2600
  const installed = getInstalledPath(item, claudeDir);
2551
- if (fs13.existsSync(installed)) {
2552
- fs13.rmSync(installed, { recursive: true, force: true });
2601
+ if (fs14.existsSync(installed)) {
2602
+ fs14.rmSync(installed, { recursive: true, force: true });
2553
2603
  }
2554
2604
  }
2555
2605
  function getSourcePath(item, root) {
2556
2606
  switch (item.type) {
2557
2607
  case "skill":
2558
- return path16.join(root, "skills", item.name);
2608
+ return path17.join(root, "skills", item.name);
2559
2609
  case "rule": {
2560
- const direct = path16.join(root, "rules", item.name);
2561
- if (fs13.existsSync(direct)) return direct;
2562
- return path16.join(root, "rules", "gamedev", item.name);
2610
+ const direct = path17.join(root, "rules", item.name);
2611
+ if (fs14.existsSync(direct)) return direct;
2612
+ return path17.join(root, "rules", "gamedev", item.name);
2563
2613
  }
2564
2614
  case "agent":
2565
- return path16.join(root, "agents", item.name);
2615
+ return path17.join(root, "agents", item.name);
2566
2616
  case "hook":
2567
- return path16.join(root, "hooks", item.name);
2617
+ return path17.join(root, "hooks", item.name);
2568
2618
  default:
2569
2619
  return null;
2570
2620
  }
@@ -2572,13 +2622,13 @@ function getSourcePath(item, root) {
2572
2622
  function getInstalledPath(item, claudeDir) {
2573
2623
  switch (item.type) {
2574
2624
  case "skill":
2575
- return path16.join(claudeDir, "skills", item.name);
2625
+ return path17.join(claudeDir, "skills", item.name);
2576
2626
  case "rule":
2577
- return path16.join(claudeDir, "rules", item.name);
2627
+ return path17.join(claudeDir, "rules", item.name);
2578
2628
  case "agent":
2579
- return path16.join(claudeDir, "agents", item.name);
2629
+ return path17.join(claudeDir, "agents", item.name);
2580
2630
  case "hook":
2581
- return path16.join(claudeDir, "hooks", item.name);
2631
+ return path17.join(claudeDir, "hooks", item.name);
2582
2632
  }
2583
2633
  }
2584
2634
 
@@ -2613,8 +2663,8 @@ async function cleanCommand(opts) {
2613
2663
  }
2614
2664
 
2615
2665
  // src/commands/worktree.ts
2616
- import fs14 from "fs";
2617
- import path17 from "path";
2666
+ import fs15 from "fs";
2667
+ import path18 from "path";
2618
2668
  import { execSync as execSync2 } from "child_process";
2619
2669
  import chalk9 from "chalk";
2620
2670
  async function worktreeCommand(branch, opts) {
@@ -2625,8 +2675,8 @@ async function worktreeCommand(branch, opts) {
2625
2675
  process.exit(1);
2626
2676
  }
2627
2677
  const repoRoot = wtInfo.isWorktree ? wtInfo.mainWorktreePath : cwd;
2628
- const worktreePath = opts.path ? path17.resolve(opts.path) : defaultWorktreePath(repoRoot, branch);
2629
- if (fs14.existsSync(worktreePath)) {
2678
+ const worktreePath = opts.path ? path18.resolve(opts.path) : defaultWorktreePath(repoRoot, branch);
2679
+ if (fs15.existsSync(worktreePath)) {
2630
2680
  console.error(
2631
2681
  chalk9.red(`Path already exists: ${worktreePath}`)
2632
2682
  );
@@ -2673,7 +2723,7 @@ async function worktreeCommand(branch, opts) {
2673
2723
  }
2674
2724
  console.log(" Installing Arcane...");
2675
2725
  const root = getPackageRoot();
2676
- const profilesDir = path17.join(root, "profiles");
2726
+ const profilesDir = path18.join(root, "profiles");
2677
2727
  const profileNames = profileExpr.split("+").filter(Boolean);
2678
2728
  const merged = mergeProfiles(profilesDir, profileNames);
2679
2729
  const shareFrom = opts.share !== false ? cwd : void 0;
@@ -2709,13 +2759,13 @@ async function worktreeCommand(branch, opts) {
2709
2759
  }
2710
2760
  }
2711
2761
  if (opts.isolate) {
2712
- const isolateScript = path17.join(
2762
+ const isolateScript = path18.join(
2713
2763
  worktreePath,
2714
2764
  ".claude",
2715
2765
  "skills",
2716
2766
  "worktree-isolation"
2717
2767
  );
2718
- if (fs14.existsSync(isolateScript)) {
2768
+ if (fs15.existsSync(isolateScript)) {
2719
2769
  console.log(" Applying worktree isolation...");
2720
2770
  try {
2721
2771
  execSync2(
@@ -2750,13 +2800,13 @@ async function worktreeCommand(branch, opts) {
2750
2800
  }
2751
2801
 
2752
2802
  // src/commands/global.ts
2753
- import fs15 from "fs";
2754
- import path18 from "path";
2803
+ import fs16 from "fs";
2804
+ import path19 from "path";
2755
2805
  import os5 from "os";
2756
2806
  import chalk10 from "chalk";
2757
- var CLAUDE_HOME = path18.join(os5.homedir(), ".claude");
2758
- var SCRIPTS_DIR = path18.join(CLAUDE_HOME, "scripts", "worktree-isolation");
2759
- var SETTINGS_PATH = path18.join(CLAUDE_HOME, "settings.json");
2807
+ var CLAUDE_HOME = path19.join(os5.homedir(), ".claude");
2808
+ var SCRIPTS_DIR = path19.join(CLAUDE_HOME, "scripts", "worktree-isolation");
2809
+ var SETTINGS_PATH = path19.join(CLAUDE_HOME, "settings.json");
2760
2810
  async function globalCommand(opts) {
2761
2811
  if (opts.status) {
2762
2812
  showStatus();
@@ -2770,8 +2820,8 @@ async function globalCommand(opts) {
2770
2820
  }
2771
2821
  function showStatus() {
2772
2822
  console.log(chalk10.bold("\n=== Arcane Global Status ===\n"));
2773
- const hasScripts = fs15.existsSync(
2774
- path18.join(SCRIPTS_DIR, "apply.py")
2823
+ const hasScripts = fs16.existsSync(
2824
+ path19.join(SCRIPTS_DIR, "apply.py")
2775
2825
  );
2776
2826
  console.log(
2777
2827
  ` Worktree scripts: ${hasScripts ? chalk10.green("installed") : chalk10.dim("not installed")}`
@@ -2789,32 +2839,32 @@ function showStatus() {
2789
2839
  function installGlobal() {
2790
2840
  console.log(chalk10.bold("\nInstalling Arcane global hooks...\n"));
2791
2841
  const root = getPackageRoot();
2792
- const scriptsSrc = path18.join(
2842
+ const scriptsSrc = path19.join(
2793
2843
  root,
2794
2844
  "skills",
2795
2845
  "worktree-isolation",
2796
2846
  "scripts"
2797
2847
  );
2798
- if (!fs15.existsSync(scriptsSrc)) {
2848
+ if (!fs16.existsSync(scriptsSrc)) {
2799
2849
  console.error(
2800
2850
  chalk10.red(" worktree-isolation scripts not found in package.")
2801
2851
  );
2802
2852
  process.exit(1);
2803
2853
  }
2804
- fs15.mkdirSync(SCRIPTS_DIR, { recursive: true });
2854
+ fs16.mkdirSync(SCRIPTS_DIR, { recursive: true });
2805
2855
  for (const file of ["apply.py", "lib.py"]) {
2806
- const src = path18.join(scriptsSrc, file);
2807
- const dst = path18.join(SCRIPTS_DIR, file);
2808
- if (!fs15.existsSync(src)) {
2856
+ const src = path19.join(scriptsSrc, file);
2857
+ const dst = path19.join(SCRIPTS_DIR, file);
2858
+ if (!fs16.existsSync(src)) {
2809
2859
  console.warn(chalk10.yellow(` WARN: ${file} not found, skipping`));
2810
2860
  continue;
2811
2861
  }
2812
- if (fs15.existsSync(dst) && fs15.readFileSync(src).equals(fs15.readFileSync(dst))) {
2862
+ if (fs16.existsSync(dst) && fs16.readFileSync(src).equals(fs16.readFileSync(dst))) {
2813
2863
  console.log(chalk10.dim(` [skip] ${file} (already up-to-date)`));
2814
2864
  } else {
2815
- fs15.copyFileSync(src, dst);
2865
+ fs16.copyFileSync(src, dst);
2816
2866
  if (process.platform !== "win32") {
2817
- fs15.chmodSync(dst, 493);
2867
+ fs16.chmodSync(dst, 493);
2818
2868
  }
2819
2869
  console.log(chalk10.green(` [ok] ${file} -> ${SCRIPTS_DIR}`));
2820
2870
  }
@@ -2838,8 +2888,8 @@ function installGlobal() {
2838
2888
  }
2839
2889
  function removeGlobal() {
2840
2890
  console.log(chalk10.bold("\nRemoving Arcane global hooks...\n"));
2841
- if (fs15.existsSync(SCRIPTS_DIR)) {
2842
- fs15.rmSync(SCRIPTS_DIR, { recursive: true, force: true });
2891
+ if (fs16.existsSync(SCRIPTS_DIR)) {
2892
+ fs16.rmSync(SCRIPTS_DIR, { recursive: true, force: true });
2843
2893
  console.log(chalk10.green(" [ok] Removed worktree-isolation scripts"));
2844
2894
  } else {
2845
2895
  console.log(chalk10.dim(" [skip] Scripts not found"));
@@ -2853,16 +2903,16 @@ function removeGlobal() {
2853
2903
  console.log(chalk10.bold.green("\n Global hooks removed.\n"));
2854
2904
  }
2855
2905
  function readSettings() {
2856
- if (!fs15.existsSync(SETTINGS_PATH)) return {};
2906
+ if (!fs16.existsSync(SETTINGS_PATH)) return {};
2857
2907
  try {
2858
- return JSON.parse(fs15.readFileSync(SETTINGS_PATH, "utf-8"));
2908
+ return JSON.parse(fs16.readFileSync(SETTINGS_PATH, "utf-8"));
2859
2909
  } catch {
2860
2910
  return {};
2861
2911
  }
2862
2912
  }
2863
2913
  function writeSettings(settings) {
2864
- fs15.mkdirSync(path18.dirname(SETTINGS_PATH), { recursive: true });
2865
- fs15.writeFileSync(
2914
+ fs16.mkdirSync(path19.dirname(SETTINGS_PATH), { recursive: true });
2915
+ fs16.writeFileSync(
2866
2916
  SETTINGS_PATH,
2867
2917
  JSON.stringify(settings, null, 2) + "\n",
2868
2918
  "utf-8"
@@ -2919,11 +2969,11 @@ function removeHookFromSettings() {
2919
2969
  }
2920
2970
 
2921
2971
  // src/update-check.ts
2922
- import fs16 from "fs";
2923
- import path19 from "path";
2972
+ import fs17 from "fs";
2973
+ import path20 from "path";
2924
2974
  import os6 from "os";
2925
2975
  import chalk11 from "chalk";
2926
- var CHECK_FILE = path19.join(os6.homedir(), ".arcane", "last-check.json");
2976
+ var CHECK_FILE = path20.join(os6.homedir(), ".arcane", "last-check.json");
2927
2977
  var CHECK_INTERVAL_MS = 4 * 60 * 60 * 1e3;
2928
2978
  var GITHUB_OWNER2 = "SebastianLuser";
2929
2979
  var GITHUB_REPO2 = "Claude-Code-Arcane";
@@ -3048,19 +3098,19 @@ async function getLatestNpmVersion() {
3048
3098
  }
3049
3099
  function readCachedCheck() {
3050
3100
  try {
3051
- if (!fs16.existsSync(CHECK_FILE)) return null;
3052
- return JSON.parse(fs16.readFileSync(CHECK_FILE, "utf-8"));
3101
+ if (!fs17.existsSync(CHECK_FILE)) return null;
3102
+ return JSON.parse(fs17.readFileSync(CHECK_FILE, "utf-8"));
3053
3103
  } catch {
3054
3104
  return null;
3055
3105
  }
3056
3106
  }
3057
3107
  function writeCachedCheck(result) {
3058
3108
  try {
3059
- const dir = path19.dirname(CHECK_FILE);
3060
- if (!fs16.existsSync(dir)) {
3061
- fs16.mkdirSync(dir, { recursive: true });
3109
+ const dir = path20.dirname(CHECK_FILE);
3110
+ if (!fs17.existsSync(dir)) {
3111
+ fs17.mkdirSync(dir, { recursive: true });
3062
3112
  }
3063
- fs16.writeFileSync(CHECK_FILE, JSON.stringify(result, null, 2) + "\n");
3113
+ fs17.writeFileSync(CHECK_FILE, JSON.stringify(result, null, 2) + "\n");
3064
3114
  } catch {
3065
3115
  }
3066
3116
  }