claude-code-arcane 2.7.1 → 2.9.1

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 (158) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/README.md +2 -2
  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 -47
  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 +487 -322
  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/hooks/statusline.sh +17 -4
  124. package/output-styles/arcane.md +53 -0
  125. package/package.json +2 -1
  126. package/profiles/backend-go.yaml +1 -0
  127. package/profiles/core.yaml +4 -0
  128. package/profiles/docs.yaml +0 -2
  129. package/profiles/security.yaml +1 -1
  130. package/profiles/unity-design.yaml +2 -0
  131. package/profiles/unity-dev.yaml +2 -0
  132. package/profiles/unreal-dev.yaml +1 -0
  133. package/profiles/visual-novel.yaml +1 -0
  134. package/rules/code-comments.md +63 -0
  135. package/rules/gamedev/audio-code.md +0 -1
  136. package/rules/gamedev/gameplay-code.md +0 -1
  137. package/rules/gamedev/shader-code.md +0 -1
  138. package/skills/architecture-review/SKILL.md +1 -0
  139. package/skills/balance-check/SKILL.md +1 -0
  140. package/skills/code-review/SKILL.md +49 -16
  141. package/skills/create-epics/SKILL.md +2 -1
  142. package/skills/create-stories/SKILL.md +5 -4
  143. package/skills/database-indexing/SKILL.md +2 -1
  144. package/skills/help/SKILL.md +6 -2
  145. package/skills/hotfix/SKILL.md +11 -3
  146. package/skills/patch-notes/SKILL.md +2 -1
  147. package/skills/perf-profile/SKILL.md +0 -1
  148. package/skills/prototype/SKILL.md +0 -2
  149. package/skills/qa-plan/SKILL.md +2 -1
  150. package/skills/retrospective/SKILL.md +4 -2
  151. package/skills/security-audit/SKILL.md +2 -1
  152. package/skills/skill-auditor/SKILL.md +16 -4
  153. package/skills/skill-auditor/references/frontmatter-spec.md +93 -86
  154. package/skills/slo-sli/SKILL.md +2 -2
  155. package/skills/sprint-plan/SKILL.md +4 -2
  156. package/skills/story-readiness/SKILL.md +1 -1
  157. package/skills/ux-design/SKILL.md +2 -1
  158. 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
@@ -26,6 +26,7 @@ function parseProfile(filePath) {
26
26
  },
27
27
  agents: raw.agents ?? [],
28
28
  hooks: raw.hooks ?? [],
29
+ output_styles: raw.output_styles ?? [],
29
30
  permissions: {
30
31
  allow: perms.allow ?? [],
31
32
  deny: perms.deny ?? []
@@ -67,6 +68,7 @@ function mergeProfiles(profilesDir, profileNames) {
67
68
  const seenRulesGamedev = /* @__PURE__ */ new Set();
68
69
  const seenAgents = /* @__PURE__ */ new Set();
69
70
  const seenHooks = /* @__PURE__ */ new Set();
71
+ const seenOutputStyles = /* @__PURE__ */ new Set();
70
72
  const allPermAllow = [];
71
73
  const allPermDeny = [];
72
74
  const skills = [];
@@ -74,6 +76,7 @@ function mergeProfiles(profilesDir, profileNames) {
74
76
  const rulesGamedev = [];
75
77
  const agents = [];
76
78
  const hooks = [];
79
+ const outputStyles = [];
77
80
  for (const name of namesToLoad) {
78
81
  const filePath = path.join(profilesDir, `${name}.yaml`);
79
82
  if (!fs.existsSync(filePath)) {
@@ -112,6 +115,12 @@ function mergeProfiles(profilesDir, profileNames) {
112
115
  hooks.push(h);
113
116
  }
114
117
  }
118
+ for (const o of profile.output_styles) {
119
+ if (!seenOutputStyles.has(o)) {
120
+ seenOutputStyles.add(o);
121
+ outputStyles.push(o);
122
+ }
123
+ }
115
124
  allPermAllow.push(...profile.permissions.allow);
116
125
  allPermDeny.push(...profile.permissions.deny);
117
126
  }
@@ -121,6 +130,7 @@ function mergeProfiles(profilesDir, profileNames) {
121
130
  rules: { universal: rulesUniv, gamedev: rulesGamedev },
122
131
  agents,
123
132
  hooks,
133
+ output_styles: outputStyles,
124
134
  permissions: {
125
135
  allow: [...new Set(allPermAllow)],
126
136
  deny: [...new Set(allPermDeny)]
@@ -374,8 +384,8 @@ function hint(description, max = 90) {
374
384
  }
375
385
 
376
386
  // src/installer.ts
377
- import fs6 from "fs";
378
- import path7 from "path";
387
+ import fs7 from "fs";
388
+ import path8 from "path";
379
389
 
380
390
  // src/utils.ts
381
391
  import { fileURLToPath } from "url";
@@ -434,11 +444,66 @@ function safeRemove(p2) {
434
444
  }
435
445
  }
436
446
 
437
- // src/manifest.ts
447
+ // src/agent-entries.ts
448
+ import fs4 from "fs";
438
449
  import path4 from "path";
450
+ function parseAgentEntry(raw) {
451
+ const trimmed = raw.trim().replace(/\/+$/, "");
452
+ const slash = trimmed.indexOf("/");
453
+ if (slash < 0) return { raw: trimmed, division: trimmed };
454
+ return {
455
+ raw: trimmed,
456
+ division: trimmed.slice(0, slash),
457
+ agent: trimmed.slice(slash + 1)
458
+ };
459
+ }
460
+ function isGranular(raw) {
461
+ return parseAgentEntry(raw).agent !== void 0;
462
+ }
463
+ function agentSourcePath(root, raw) {
464
+ const { division, agent } = parseAgentEntry(raw);
465
+ const p2 = agent ? path4.join(root, "agents", division, `${agent}.md`) : path4.join(root, "agents", division);
466
+ return fs4.existsSync(p2) ? p2 : null;
467
+ }
468
+ function copyAgentEntry(root, claudeDir, raw) {
469
+ const src = agentSourcePath(root, raw);
470
+ if (!src) return null;
471
+ const { division, agent } = parseAgentEntry(raw);
472
+ const dstDir = path4.join(claudeDir, "agents", division);
473
+ if (agent) {
474
+ ensureDir(dstDir);
475
+ fs4.copyFileSync(src, path4.join(dstDir, `${agent}.md`));
476
+ return 1;
477
+ }
478
+ copyDirSync(src, dstDir);
479
+ return fs4.readdirSync(dstDir, { recursive: true }).filter((f) => String(f).endsWith(".md")).length;
480
+ }
481
+ function removeAgentEntry(claudeDir, raw) {
482
+ const { division, agent } = parseAgentEntry(raw);
483
+ const dstDir = path4.join(claudeDir, "agents", division);
484
+ if (!agent) {
485
+ if (!fs4.existsSync(dstDir)) return false;
486
+ fs4.rmSync(dstDir, { recursive: true, force: true });
487
+ return true;
488
+ }
489
+ const file = path4.join(dstDir, `${agent}.md`);
490
+ if (!fs4.existsSync(file)) return false;
491
+ safeRemove(file);
492
+ if (fs4.existsSync(dstDir) && fs4.readdirSync(dstDir).length === 0) {
493
+ fs4.rmSync(dstDir, { recursive: true, force: true });
494
+ }
495
+ return true;
496
+ }
497
+ function agentEntryLabel(raw) {
498
+ const { division, agent } = parseAgentEntry(raw);
499
+ return agent ? `agents/${division}/${agent}.md` : `agents/${division}/`;
500
+ }
501
+
502
+ // src/manifest.ts
503
+ import path5 from "path";
439
504
  var MANIFEST_FILE = "arcane-manifest.json";
440
505
  function manifestPath(target) {
441
- return path4.join(target, ".claude", MANIFEST_FILE);
506
+ return path5.join(target, ".claude", MANIFEST_FILE);
442
507
  }
443
508
  function readManifest(target) {
444
509
  const p2 = manifestPath(target);
@@ -492,8 +557,8 @@ function updateManifestFields(target, updates) {
492
557
 
493
558
  // src/worktree.ts
494
559
  import { execSync } from "child_process";
495
- import fs4 from "fs";
496
- import path5 from "path";
560
+ import fs5 from "fs";
561
+ import path6 from "path";
497
562
  function getWorktreeInfo(target) {
498
563
  try {
499
564
  execSync("git rev-parse --is-inside-work-tree", {
@@ -514,10 +579,10 @@ function getWorktreeInfo(target) {
514
579
  encoding: "utf-8",
515
580
  stdio: "pipe"
516
581
  }).trim();
517
- const resolvedCommon = path5.resolve(target, gitCommonDir);
518
- const resolvedGit = path5.resolve(target, gitDir);
582
+ const resolvedCommon = path6.resolve(target, gitCommonDir);
583
+ const resolvedGit = path6.resolve(target, gitDir);
519
584
  const isWorktree = resolvedCommon !== resolvedGit;
520
- const mainWorktreePath = isWorktree ? path5.resolve(resolvedCommon, "..") : target;
585
+ const mainWorktreePath = isWorktree ? path6.resolve(resolvedCommon, "..") : target;
521
586
  let currentBranch = "HEAD";
522
587
  try {
523
588
  currentBranch = execSync("git branch --show-current", {
@@ -532,7 +597,7 @@ function getWorktreeInfo(target) {
532
597
  isWorktree,
533
598
  mainWorktreePath,
534
599
  currentBranch,
535
- worktreeId: path5.basename(target),
600
+ worktreeId: path6.basename(target),
536
601
  allWorktrees
537
602
  };
538
603
  }
@@ -568,17 +633,17 @@ function parseWorktreeList(cwd) {
568
633
  }
569
634
  }
570
635
  function findMainArcaneInstall(mainPath) {
571
- return fs4.existsSync(
572
- path5.join(mainPath, ".claude", "arcane-manifest.json")
636
+ return fs5.existsSync(
637
+ path6.join(mainPath, ".claude", "arcane-manifest.json")
573
638
  );
574
639
  }
575
640
  function linkOrCopyDir(src, dest, mode) {
576
641
  if (mode === "symlink") {
577
642
  try {
578
- const parentDir = path5.dirname(dest);
579
- fs4.mkdirSync(parentDir, { recursive: true });
643
+ const parentDir = path6.dirname(dest);
644
+ fs5.mkdirSync(parentDir, { recursive: true });
580
645
  const type = process.platform === "win32" ? "junction" : "dir";
581
- fs4.symlinkSync(src, dest, type);
646
+ fs5.symlinkSync(src, dest, type);
582
647
  return "linked";
583
648
  } catch {
584
649
  }
@@ -590,9 +655,9 @@ function sanitizeBranchForPath(branch) {
590
655
  return branch.replace(/^(feat|fix|feature|hotfix)\//, "").replace(/\//g, "-").replace(/[^a-zA-Z0-9._-]/g, "-");
591
656
  }
592
657
  function defaultWorktreePath(repoRoot, branch) {
593
- const repoName = path5.basename(repoRoot);
658
+ const repoName = path6.basename(repoRoot);
594
659
  const sanitized = sanitizeBranchForPath(branch);
595
- return path5.resolve(repoRoot, "..", `${repoName}-${sanitized}`);
660
+ return path6.resolve(repoRoot, "..", `${repoName}-${sanitized}`);
596
661
  }
597
662
  function createGitWorktree(repoRoot, worktreePath, branch, base) {
598
663
  const branchExists = checkBranchExists(repoRoot, branch);
@@ -634,20 +699,20 @@ function detectPackageManager(dir) {
634
699
  { lockfile: "package-lock.json", command: "npm install" }
635
700
  ];
636
701
  for (const m of managers) {
637
- if (fs4.existsSync(path5.join(dir, m.lockfile))) return m;
702
+ if (fs5.existsSync(path6.join(dir, m.lockfile))) return m;
638
703
  }
639
- if (fs4.existsSync(path5.join(dir, "package.json"))) {
704
+ if (fs5.existsSync(path6.join(dir, "package.json"))) {
640
705
  return { lockfile: "package.json", command: "npm install" };
641
706
  }
642
707
  return null;
643
708
  }
644
709
 
645
710
  // src/content-hash.ts
646
- import fs5 from "fs";
647
- import path6 from "path";
711
+ import fs6 from "fs";
712
+ import path7 from "path";
648
713
  import { createHash } from "crypto";
649
714
  function hashFile(filePath) {
650
- const content = fs5.readFileSync(filePath);
715
+ const content = fs6.readFileSync(filePath);
651
716
  return createHash("sha256").update(content).digest("hex");
652
717
  }
653
718
  function hashDirectory(dirPath) {
@@ -656,10 +721,10 @@ function hashDirectory(dirPath) {
656
721
  return hashes;
657
722
  }
658
723
  function walkDir(base, current, hashes) {
659
- for (const entry of fs5.readdirSync(current, { withFileTypes: true })) {
724
+ for (const entry of fs6.readdirSync(current, { withFileTypes: true })) {
660
725
  if (isBuildArtifact(entry.name)) continue;
661
- const fullPath = path6.join(current, entry.name);
662
- const relPath = path6.relative(base, fullPath).split(path6.sep).join("/");
726
+ const fullPath = path7.join(current, entry.name);
727
+ const relPath = path7.relative(base, fullPath).split(path7.sep).join("/");
663
728
  if (entry.isDirectory()) {
664
729
  walkDir(base, fullPath, hashes);
665
730
  } else {
@@ -674,39 +739,39 @@ function computeContentHashes(claudeDir) {
674
739
  agents: {},
675
740
  hooks: {}
676
741
  };
677
- const skillsDir = path6.join(claudeDir, "skills");
678
- if (fs5.existsSync(skillsDir)) {
679
- for (const entry of fs5.readdirSync(skillsDir, { withFileTypes: true })) {
742
+ const skillsDir = path7.join(claudeDir, "skills");
743
+ if (fs6.existsSync(skillsDir)) {
744
+ for (const entry of fs6.readdirSync(skillsDir, { withFileTypes: true })) {
680
745
  if (entry.isDirectory()) {
681
- const dirHash = hashDirectory(path6.join(skillsDir, entry.name));
746
+ const dirHash = hashDirectory(path7.join(skillsDir, entry.name));
682
747
  const combined = createHash("sha256").update(JSON.stringify(dirHash)).digest("hex");
683
748
  hashes.skills[entry.name] = combined;
684
749
  }
685
750
  }
686
751
  }
687
- const rulesDir = path6.join(claudeDir, "rules");
688
- if (fs5.existsSync(rulesDir)) {
689
- for (const entry of fs5.readdirSync(rulesDir, { withFileTypes: true })) {
752
+ const rulesDir = path7.join(claudeDir, "rules");
753
+ if (fs6.existsSync(rulesDir)) {
754
+ for (const entry of fs6.readdirSync(rulesDir, { withFileTypes: true })) {
690
755
  if (!entry.isDirectory()) {
691
- hashes.rules[entry.name] = hashFile(path6.join(rulesDir, entry.name));
756
+ hashes.rules[entry.name] = hashFile(path7.join(rulesDir, entry.name));
692
757
  }
693
758
  }
694
759
  }
695
- const agentsDir = path6.join(claudeDir, "agents");
696
- if (fs5.existsSync(agentsDir)) {
697
- for (const entry of fs5.readdirSync(agentsDir, { withFileTypes: true })) {
760
+ const agentsDir = path7.join(claudeDir, "agents");
761
+ if (fs6.existsSync(agentsDir)) {
762
+ for (const entry of fs6.readdirSync(agentsDir, { withFileTypes: true })) {
698
763
  if (entry.isDirectory()) {
699
- const dirHash = hashDirectory(path6.join(agentsDir, entry.name));
764
+ const dirHash = hashDirectory(path7.join(agentsDir, entry.name));
700
765
  const combined = createHash("sha256").update(JSON.stringify(dirHash)).digest("hex");
701
766
  hashes.agents[entry.name] = combined;
702
767
  }
703
768
  }
704
769
  }
705
- const hooksDir = path6.join(claudeDir, "hooks");
706
- if (fs5.existsSync(hooksDir)) {
707
- for (const entry of fs5.readdirSync(hooksDir, { withFileTypes: true })) {
770
+ const hooksDir = path7.join(claudeDir, "hooks");
771
+ if (fs6.existsSync(hooksDir)) {
772
+ for (const entry of fs6.readdirSync(hooksDir, { withFileTypes: true })) {
708
773
  if (!entry.isDirectory()) {
709
- hashes.hooks[entry.name] = hashFile(path6.join(hooksDir, entry.name));
774
+ hashes.hooks[entry.name] = hashFile(path7.join(hooksDir, entry.name));
710
775
  }
711
776
  }
712
777
  }
@@ -720,8 +785,8 @@ function computeSourceHashes(packageRoot, skills, rules, agents) {
720
785
  hooks: {}
721
786
  };
722
787
  for (const skill of skills) {
723
- const skillDir = path6.join(packageRoot, "skills", skill);
724
- if (fs5.existsSync(skillDir)) {
788
+ const skillDir = path7.join(packageRoot, "skills", skill);
789
+ if (fs6.existsSync(skillDir)) {
725
790
  const dirHash = hashDirectory(skillDir);
726
791
  const combined = createHash("sha256").update(JSON.stringify(dirHash)).digest("hex");
727
792
  hashes.skills[skill] = combined;
@@ -729,29 +794,30 @@ function computeSourceHashes(packageRoot, skills, rules, agents) {
729
794
  }
730
795
  for (const rule of rules) {
731
796
  const ruleName = rule.endsWith(".md") ? rule : `${rule}.md`;
732
- const rulePath = path6.join(packageRoot, "rules", ruleName);
733
- if (fs5.existsSync(rulePath)) {
797
+ const rulePath = path7.join(packageRoot, "rules", ruleName);
798
+ if (fs6.existsSync(rulePath)) {
734
799
  hashes.rules[ruleName] = hashFile(rulePath);
735
800
  } else {
736
- const gamedevPath = path6.join(packageRoot, "rules", "gamedev", ruleName);
737
- if (fs5.existsSync(gamedevPath)) {
801
+ const gamedevPath = path7.join(packageRoot, "rules", "gamedev", ruleName);
802
+ if (fs6.existsSync(gamedevPath)) {
738
803
  hashes.rules[ruleName] = hashFile(gamedevPath);
739
804
  }
740
805
  }
741
806
  }
742
807
  for (const agent of agents) {
743
- const agentDir = path6.join(packageRoot, "agents", agent);
744
- if (fs5.existsSync(agentDir)) {
808
+ const agentDir = path7.join(packageRoot, "agents", agent);
809
+ if (fs6.existsSync(agentDir)) {
745
810
  const dirHash = hashDirectory(agentDir);
746
811
  const combined = createHash("sha256").update(JSON.stringify(dirHash)).digest("hex");
747
812
  hashes.agents[agent] = combined;
748
813
  }
749
814
  }
750
- const hooksDir = path6.join(packageRoot, "hooks");
751
- if (fs5.existsSync(hooksDir)) {
752
- for (const entry of fs5.readdirSync(hooksDir, { withFileTypes: true })) {
815
+ const hooksDir = path7.join(packageRoot, "hooks");
816
+ if (fs6.existsSync(hooksDir)) {
817
+ for (const entry of fs6.readdirSync(hooksDir, { withFileTypes: true })) {
818
+ if (entry.name === "statusline.sh") continue;
753
819
  if (!entry.isDirectory()) {
754
- hashes.hooks[entry.name] = hashFile(path6.join(hooksDir, entry.name));
820
+ hashes.hooks[entry.name] = hashFile(path7.join(hooksDir, entry.name));
755
821
  }
756
822
  }
757
823
  }
@@ -920,7 +986,7 @@ var Installer = class {
920
986
  constructor(merged, opts) {
921
987
  this.root = opts.contentRoot ?? getPackageRoot();
922
988
  this.target = opts.target;
923
- this.claudeDir = path7.join(opts.target, ".claude");
989
+ this.claudeDir = path8.join(opts.target, ".claude");
924
990
  this.merged = merged;
925
991
  this.dryRun = opts.dryRun;
926
992
  this.shareFrom = opts.shareFrom;
@@ -935,6 +1001,7 @@ var Installer = class {
935
1001
  this.copyHooks();
936
1002
  this.copySkills();
937
1003
  this.copyRules();
1004
+ this.copyOutputStyles();
938
1005
  this.copyAgents();
939
1006
  this.copyDocs();
940
1007
  this.generateSettings();
@@ -952,22 +1019,25 @@ var Installer = class {
952
1019
  return this.logs;
953
1020
  }
954
1021
  backup() {
955
- if (!fs6.existsSync(this.claudeDir)) return;
956
- const backup = path7.join(this.target, ".claude.bak");
1022
+ if (!fs7.existsSync(this.claudeDir)) return;
1023
+ const backup = path8.join(this.target, ".claude.bak");
957
1024
  if (this.dryRun) {
958
1025
  this.log(" [dry-run] Would backup .claude/ -> .claude.bak/");
959
1026
  return;
960
1027
  }
961
- if (fs6.existsSync(backup)) {
962
- fs6.rmSync(backup, { recursive: true, force: true });
1028
+ if (fs7.existsSync(backup)) {
1029
+ fs7.rmSync(backup, { recursive: true, force: true });
963
1030
  }
964
- fs6.renameSync(this.claudeDir, backup);
1031
+ fs7.renameSync(this.claudeDir, backup);
965
1032
  this.log(" Backed up .claude/ -> .claude.bak/");
966
1033
  }
967
1034
  createDirs() {
968
1035
  if (this.dryRun) return;
969
1036
  for (const d of ["skills", "rules", "agents", "docs"]) {
970
- ensureDir(path7.join(this.claudeDir, d));
1037
+ ensureDir(path8.join(this.claudeDir, d));
1038
+ }
1039
+ if (this.merged.output_styles.length > 0) {
1040
+ ensureDir(path8.join(this.claudeDir, "output-styles"));
971
1041
  }
972
1042
  }
973
1043
  get hasStatusline() {
@@ -975,7 +1045,7 @@ var Installer = class {
975
1045
  }
976
1046
  copyHooks() {
977
1047
  this.log("\nHooks:");
978
- const hooksDir = path7.join(this.claudeDir, "hooks");
1048
+ const hooksDir = path8.join(this.claudeDir, "hooks");
979
1049
  if (this.dryRun) {
980
1050
  if (this.shareFrom) {
981
1051
  this.log(" [dry-run] hooks/ -> symlink from main worktree");
@@ -986,8 +1056,8 @@ var Installer = class {
986
1056
  return;
987
1057
  }
988
1058
  if (this.shareFrom) {
989
- const sharedHooks = path7.join(this.shareFrom, ".claude", "hooks");
990
- if (fs6.existsSync(sharedHooks)) {
1059
+ const sharedHooks = path8.join(this.shareFrom, ".claude", "hooks");
1060
+ if (fs7.existsSync(sharedHooks)) {
991
1061
  const result = linkOrCopyDir(sharedHooks, hooksDir, "symlink");
992
1062
  if (result === "linked") {
993
1063
  this.sharedDirs.push("hooks");
@@ -999,17 +1069,17 @@ var Installer = class {
999
1069
  return;
1000
1070
  }
1001
1071
  }
1002
- const hooksSrc = path7.join(this.root, "hooks");
1003
- if (fs6.existsSync(hooksSrc)) {
1072
+ const hooksSrc = path8.join(this.root, "hooks");
1073
+ if (fs7.existsSync(hooksSrc)) {
1004
1074
  ensureDir(hooksDir);
1005
- for (const entry of fs6.readdirSync(hooksSrc, { withFileTypes: true })) {
1075
+ for (const entry of fs7.readdirSync(hooksSrc, { withFileTypes: true })) {
1006
1076
  if (entry.name === "statusline.sh") continue;
1007
- const src = path7.join(hooksSrc, entry.name);
1008
- const dst = path7.join(hooksDir, entry.name);
1077
+ const src = path8.join(hooksSrc, entry.name);
1078
+ const dst = path8.join(hooksDir, entry.name);
1009
1079
  if (entry.isDirectory()) {
1010
1080
  copyDirSync(src, dst);
1011
1081
  } else {
1012
- fs6.copyFileSync(src, dst);
1082
+ fs7.copyFileSync(src, dst);
1013
1083
  }
1014
1084
  }
1015
1085
  this.log(" [ok] hooks/");
@@ -1018,11 +1088,11 @@ var Installer = class {
1018
1088
  }
1019
1089
  copyStatusline() {
1020
1090
  if (!this.hasStatusline) return;
1021
- const statusline = path7.join(this.root, "hooks", "statusline.sh");
1022
- if (fs6.existsSync(statusline)) {
1023
- fs6.copyFileSync(
1091
+ const statusline = path8.join(this.root, "hooks", "statusline.sh");
1092
+ if (fs7.existsSync(statusline)) {
1093
+ fs7.copyFileSync(
1024
1094
  statusline,
1025
- path7.join(this.claudeDir, "statusline.sh")
1095
+ path8.join(this.claudeDir, "statusline.sh")
1026
1096
  );
1027
1097
  this.log(" [ok] statusline.sh (statusline profile)");
1028
1098
  }
@@ -1030,12 +1100,12 @@ var Installer = class {
1030
1100
  copySkills() {
1031
1101
  this.log("\nSkills:");
1032
1102
  for (const skill of this.merged.skills) {
1033
- const src = path7.join(this.root, "skills", skill);
1034
- if (!fs6.existsSync(src)) {
1103
+ const src = path8.join(this.root, "skills", skill);
1104
+ if (!fs7.existsSync(src)) {
1035
1105
  this.log(` WARN: Skill '${skill}' not found in skills/`);
1036
1106
  continue;
1037
1107
  }
1038
- const dst = path7.join(this.claudeDir, "skills", skill);
1108
+ const dst = path8.join(this.claudeDir, "skills", skill);
1039
1109
  if (this.dryRun) {
1040
1110
  this.log(` [dry-run] ${skill}`);
1041
1111
  } else {
@@ -1045,9 +1115,9 @@ var Installer = class {
1045
1115
  }
1046
1116
  const hasGamedev = this.merged.rules.gamedev.length > 0;
1047
1117
  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");
1118
+ const tplSrc = path8.join(this.root, "templates", "gamedev");
1119
+ if (fs7.existsSync(tplSrc)) {
1120
+ const tplDst = path8.join(this.claudeDir, "skills", "_templates");
1051
1121
  if (this.dryRun) {
1052
1122
  this.log(" [dry-run] _templates/");
1053
1123
  } else {
@@ -1060,61 +1130,79 @@ var Installer = class {
1060
1130
  copyRules() {
1061
1131
  this.log("\nRules:");
1062
1132
  for (const rule of this.merged.rules.universal) {
1063
- const src = path7.join(this.root, "rules", `${rule}.md`);
1064
- if (!fs6.existsSync(src)) {
1133
+ const src = path8.join(this.root, "rules", `${rule}.md`);
1134
+ if (!fs7.existsSync(src)) {
1065
1135
  this.log(` WARN: Rule '${rule}.md' not found`);
1066
1136
  continue;
1067
1137
  }
1068
1138
  if (this.dryRun) {
1069
1139
  this.log(` [dry-run] ${rule}.md`);
1070
1140
  } else {
1071
- fs6.copyFileSync(
1141
+ fs7.copyFileSync(
1072
1142
  src,
1073
- path7.join(this.claudeDir, "rules", `${rule}.md`)
1143
+ path8.join(this.claudeDir, "rules", `${rule}.md`)
1074
1144
  );
1075
1145
  this.log(` [ok] ${rule}.md`);
1076
1146
  }
1077
1147
  }
1078
1148
  for (const rule of this.merged.rules.gamedev) {
1079
- const src = path7.join(this.root, "rules", "gamedev", `${rule}.md`);
1080
- if (!fs6.existsSync(src)) {
1149
+ const src = path8.join(this.root, "rules", "gamedev", `${rule}.md`);
1150
+ if (!fs7.existsSync(src)) {
1081
1151
  this.log(` WARN: Rule '${rule}.md' not found`);
1082
1152
  continue;
1083
1153
  }
1084
1154
  if (this.dryRun) {
1085
1155
  this.log(` [dry-run] ${rule}.md`);
1086
1156
  } else {
1087
- fs6.copyFileSync(
1157
+ fs7.copyFileSync(
1088
1158
  src,
1089
- path7.join(this.claudeDir, "rules", `${rule}.md`)
1159
+ path8.join(this.claudeDir, "rules", `${rule}.md`)
1090
1160
  );
1091
1161
  this.log(` [ok] ${rule}.md`);
1092
1162
  }
1093
1163
  }
1094
1164
  }
1165
+ copyOutputStyles() {
1166
+ if (this.merged.output_styles.length === 0) return;
1167
+ this.log("\nOutput styles:");
1168
+ for (const style of this.merged.output_styles) {
1169
+ const src = path8.join(this.root, "output-styles", `${style}.md`);
1170
+ if (!fs7.existsSync(src)) {
1171
+ this.log(` WARN: Output style '${style}.md' not found`);
1172
+ continue;
1173
+ }
1174
+ if (this.dryRun) {
1175
+ this.log(` [dry-run] ${style}.md`);
1176
+ } else {
1177
+ fs7.copyFileSync(
1178
+ src,
1179
+ path8.join(this.claudeDir, "output-styles", `${style}.md`)
1180
+ );
1181
+ this.log(` [ok] ${style}.md`);
1182
+ }
1183
+ }
1184
+ }
1095
1185
  copyAgents() {
1096
1186
  if (this.merged.agents.length === 0) return;
1097
1187
  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`);
1188
+ for (const entry of this.merged.agents) {
1189
+ const label = agentEntryLabel(entry);
1190
+ if (!agentSourcePath(this.root, entry)) {
1191
+ this.log(` WARN: Agent entry '${entry}' not found`);
1102
1192
  continue;
1103
1193
  }
1104
- const dst = path7.join(this.claudeDir, "agents", agentDir);
1105
1194
  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)`);
1195
+ this.log(` [dry-run] ${label}`);
1196
+ continue;
1111
1197
  }
1198
+ const count = copyAgentEntry(this.root, this.claudeDir, entry);
1199
+ this.log(` [ok] ${label}${count === 1 ? "" : ` (${count} agents)`}`);
1112
1200
  }
1113
1201
  }
1114
1202
  copyDocs() {
1115
- const docsDir = path7.join(this.claudeDir, "docs");
1116
- const docsSrc = path7.join(this.root, "docs");
1117
- if (!fs6.existsSync(docsSrc)) return;
1203
+ const docsDir = path8.join(this.claudeDir, "docs");
1204
+ const docsSrc = path8.join(this.root, "docs");
1205
+ if (!fs7.existsSync(docsSrc)) return;
1118
1206
  this.log("\nDocs:");
1119
1207
  if (this.dryRun) {
1120
1208
  if (this.shareFrom) {
@@ -1125,8 +1213,8 @@ var Installer = class {
1125
1213
  return;
1126
1214
  }
1127
1215
  if (this.shareFrom) {
1128
- const sharedDocs = path7.join(this.shareFrom, ".claude", "docs");
1129
- if (fs6.existsSync(sharedDocs)) {
1216
+ const sharedDocs = path8.join(this.shareFrom, ".claude", "docs");
1217
+ if (fs7.existsSync(sharedDocs)) {
1130
1218
  const result = linkOrCopyDir(sharedDocs, docsDir, "symlink");
1131
1219
  if (result === "linked") {
1132
1220
  this.sharedDirs.push("docs");
@@ -1151,14 +1239,18 @@ var Installer = class {
1151
1239
  command: "bash .claude/statusline.sh"
1152
1240
  };
1153
1241
  }
1242
+ const activeStyle = this.merged.output_styles[0];
1243
+ if (activeStyle) {
1244
+ settings.outputStyle = activeStyle;
1245
+ }
1154
1246
  if (this.dryRun) {
1155
1247
  this.log(
1156
1248
  ` [dry-run] settings.json (${this.merged.permissions.allow.length} allow, ${this.merged.permissions.deny.length} deny)`
1157
1249
  );
1158
1250
  return;
1159
1251
  }
1160
- const settingsPath = path7.join(this.claudeDir, "settings.json");
1161
- fs6.writeFileSync(
1252
+ const settingsPath = path8.join(this.claudeDir, "settings.json");
1253
+ fs7.writeFileSync(
1162
1254
  settingsPath,
1163
1255
  JSON.stringify(settings, null, 2) + "\n",
1164
1256
  "utf-8"
@@ -1167,15 +1259,15 @@ var Installer = class {
1167
1259
  }
1168
1260
  };
1169
1261
  function clean(target) {
1170
- const claudeDir = path7.join(target, ".claude");
1171
- const backup = path7.join(target, ".claude.bak");
1172
- if (!fs6.existsSync(claudeDir)) {
1262
+ const claudeDir = path8.join(target, ".claude");
1263
+ const backup = path8.join(target, ".claude.bak");
1264
+ if (!fs7.existsSync(claudeDir)) {
1173
1265
  console.log(`No .claude/ found in ${target} \u2014 nothing to clean.`);
1174
1266
  return;
1175
1267
  }
1176
- const manifestFile = path7.join(claudeDir, "arcane-manifest.json");
1177
- if (fs6.existsSync(manifestFile)) {
1178
- const data = JSON.parse(fs6.readFileSync(manifestFile, "utf-8"));
1268
+ const manifestFile = path8.join(claudeDir, "arcane-manifest.json");
1269
+ if (fs7.existsSync(manifestFile)) {
1270
+ const data = JSON.parse(fs7.readFileSync(manifestFile, "utf-8"));
1179
1271
  const profiles = (data.profiles ?? []).filter(
1180
1272
  (p2) => p2 !== "core"
1181
1273
  );
@@ -1183,49 +1275,49 @@ function clean(target) {
1183
1275
  console.log(` Installed: ${data.installed_at ?? "unknown"}`);
1184
1276
  const shared = data.worktree?.shared_dirs ?? [];
1185
1277
  for (const dir of shared) {
1186
- const p2 = path7.join(claudeDir, dir);
1278
+ const p2 = path8.join(claudeDir, dir);
1187
1279
  if (isSymlinkOrJunction(p2)) {
1188
- fs6.unlinkSync(p2);
1280
+ fs7.unlinkSync(p2);
1189
1281
  console.log(` [ok] Unlinked shared ${dir}/`);
1190
1282
  }
1191
1283
  }
1192
1284
  }
1193
1285
  for (const sub of ["hooks", "docs", "skills", "agents", "rules"]) {
1194
- const p2 = path7.join(claudeDir, sub);
1286
+ const p2 = path8.join(claudeDir, sub);
1195
1287
  safeRemove(p2);
1196
1288
  }
1197
- const remaining = fs6.readdirSync(claudeDir);
1289
+ const remaining = fs7.readdirSync(claudeDir);
1198
1290
  for (const f of remaining) {
1199
- const p2 = path7.join(claudeDir, f);
1291
+ const p2 = path8.join(claudeDir, f);
1200
1292
  safeRemove(p2);
1201
1293
  }
1202
- if (fs6.existsSync(claudeDir)) {
1203
- fs6.rmSync(claudeDir, { recursive: true, force: true });
1294
+ if (fs7.existsSync(claudeDir)) {
1295
+ fs7.rmSync(claudeDir, { recursive: true, force: true });
1204
1296
  }
1205
- if (fs6.existsSync(backup)) {
1206
- fs6.rmSync(backup, { recursive: true, force: true });
1297
+ if (fs7.existsSync(backup)) {
1298
+ fs7.rmSync(backup, { recursive: true, force: true });
1207
1299
  }
1208
1300
  console.log(`
1209
1301
  [ok] Removed .claude/ from ${target}`);
1210
1302
  }
1211
1303
 
1212
1304
  // src/content-source.ts
1213
- import fs8 from "fs";
1214
- import path9 from "path";
1305
+ import fs9 from "fs";
1306
+ import path10 from "path";
1215
1307
  import os2 from "os";
1216
1308
 
1217
1309
  // src/cache.ts
1218
- import fs7 from "fs";
1219
- import path8 from "path";
1310
+ import fs8 from "fs";
1311
+ import path9 from "path";
1220
1312
  import os from "os";
1221
- var ARCANE_DIR = path8.join(os.homedir(), ".arcane");
1222
- var CACHE_DIR = path8.join(ARCANE_DIR, "cache");
1313
+ var ARCANE_DIR = path9.join(os.homedir(), ".arcane");
1314
+ var CACHE_DIR = path9.join(ARCANE_DIR, "cache");
1223
1315
  function getCachePath(version) {
1224
- return path8.join(CACHE_DIR, version);
1316
+ return path9.join(CACHE_DIR, version);
1225
1317
  }
1226
1318
  function isCached(version) {
1227
1319
  const versionDir = getCachePath(version);
1228
- return fs7.existsSync(versionDir) && fs7.existsSync(path8.join(versionDir, ".cache-meta.json"));
1320
+ return fs8.existsSync(versionDir) && fs8.existsSync(path9.join(versionDir, ".cache-meta.json"));
1229
1321
  }
1230
1322
  function getCachedContentRoot(version) {
1231
1323
  if (!isCached(version)) return null;
@@ -1233,34 +1325,34 @@ function getCachedContentRoot(version) {
1233
1325
  }
1234
1326
  function storeInCache(version, contentDir) {
1235
1327
  const versionDir = getCachePath(version);
1236
- if (fs7.existsSync(versionDir)) {
1237
- fs7.rmSync(versionDir, { recursive: true, force: true });
1328
+ if (fs8.existsSync(versionDir)) {
1329
+ fs8.rmSync(versionDir, { recursive: true, force: true });
1238
1330
  }
1239
- fs7.mkdirSync(versionDir, { recursive: true });
1240
- 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));
1331
+ fs8.mkdirSync(versionDir, { recursive: true });
1332
+ for (const dir of ["skills", "rules", "agents", "hooks", "profiles", "templates", "docs", "output-styles"]) {
1333
+ const src = path9.join(contentDir, dir);
1334
+ if (fs8.existsSync(src)) {
1335
+ copyDirRecursive(src, path9.join(versionDir, dir));
1244
1336
  }
1245
1337
  }
1246
1338
  const meta = {
1247
1339
  version,
1248
1340
  cached_at: (/* @__PURE__ */ new Date()).toISOString()
1249
1341
  };
1250
- fs7.writeFileSync(
1251
- path8.join(versionDir, ".cache-meta.json"),
1342
+ fs8.writeFileSync(
1343
+ path9.join(versionDir, ".cache-meta.json"),
1252
1344
  JSON.stringify(meta, null, 2) + "\n"
1253
1345
  );
1254
1346
  return versionDir;
1255
1347
  }
1256
1348
  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");
1349
+ if (!fs8.existsSync(CACHE_DIR)) return;
1350
+ const entries = fs8.readdirSync(CACHE_DIR, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => {
1351
+ const metaPath = path9.join(CACHE_DIR, e.name, ".cache-meta.json");
1260
1352
  let cachedAt = 0;
1261
- if (fs7.existsSync(metaPath)) {
1353
+ if (fs8.existsSync(metaPath)) {
1262
1354
  try {
1263
- const meta = JSON.parse(fs7.readFileSync(metaPath, "utf-8"));
1355
+ const meta = JSON.parse(fs8.readFileSync(metaPath, "utf-8"));
1264
1356
  cachedAt = new Date(meta.cached_at).getTime();
1265
1357
  } catch {
1266
1358
  cachedAt = 0;
@@ -1269,18 +1361,18 @@ function pruneCache(keepCount = 3) {
1269
1361
  return { name: e.name, cachedAt };
1270
1362
  }).sort((a, b) => b.cachedAt - a.cachedAt);
1271
1363
  for (const entry of entries.slice(keepCount)) {
1272
- fs7.rmSync(path8.join(CACHE_DIR, entry.name), { recursive: true, force: true });
1364
+ fs8.rmSync(path9.join(CACHE_DIR, entry.name), { recursive: true, force: true });
1273
1365
  }
1274
1366
  }
1275
1367
  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);
1368
+ fs8.mkdirSync(dest, { recursive: true });
1369
+ for (const entry of fs8.readdirSync(src, { withFileTypes: true })) {
1370
+ const srcPath = path9.join(src, entry.name);
1371
+ const destPath = path9.join(dest, entry.name);
1280
1372
  if (entry.isDirectory()) {
1281
1373
  copyDirRecursive(srcPath, destPath);
1282
1374
  } else {
1283
- fs7.copyFileSync(srcPath, destPath);
1375
+ fs8.copyFileSync(srcPath, destPath);
1284
1376
  }
1285
1377
  }
1286
1378
  }
@@ -1296,7 +1388,7 @@ var BundledContentSource = class {
1296
1388
  }
1297
1389
  async isAvailable() {
1298
1390
  const root = getPackageRoot();
1299
- return fs8.existsSync(path9.join(root, "skills"));
1391
+ return fs9.existsSync(path10.join(root, "skills"));
1300
1392
  }
1301
1393
  async getVersion() {
1302
1394
  return getPackageVersion();
@@ -1336,14 +1428,14 @@ var GitHubContentSource = class {
1336
1428
  if (isCached(version)) {
1337
1429
  return getCachedContentRoot(version);
1338
1430
  }
1339
- const tmpDir = fs8.mkdtempSync(path9.join(os2.tmpdir(), "arcane-github-"));
1431
+ const tmpDir = fs9.mkdtempSync(path10.join(os2.tmpdir(), "arcane-github-"));
1340
1432
  try {
1341
1433
  await this.downloadAndExtract(tmpDir);
1342
1434
  const contentRoot = storeInCache(version, this.findExtractedRoot(tmpDir));
1343
1435
  pruneCache(3);
1344
1436
  return contentRoot;
1345
1437
  } finally {
1346
- fs8.rmSync(tmpDir, { recursive: true, force: true });
1438
+ fs9.rmSync(tmpDir, { recursive: true, force: true });
1347
1439
  }
1348
1440
  }
1349
1441
  async isAvailable() {
@@ -1399,8 +1491,8 @@ var GitHubContentSource = class {
1399
1491
  throw new Error(`Failed to download tarball: ${response.status}`);
1400
1492
  }
1401
1493
  const tar = await import("tar");
1402
- const tarballPath = path9.join(destDir, "repo.tar.gz");
1403
- const fileStream = fs8.createWriteStream(tarballPath);
1494
+ const tarballPath = path10.join(destDir, "repo.tar.gz");
1495
+ const fileStream = fs9.createWriteStream(tarballPath);
1404
1496
  const reader = response.body.getReader();
1405
1497
  const writable = new WritableStream({
1406
1498
  write(chunk) {
@@ -1425,12 +1517,12 @@ var GitHubContentSource = class {
1425
1517
  });
1426
1518
  }
1427
1519
  findExtractedRoot(tmpDir) {
1428
- const entries = fs8.readdirSync(tmpDir, { withFileTypes: true });
1520
+ const entries = fs9.readdirSync(tmpDir, { withFileTypes: true });
1429
1521
  const dirs = entries.filter(
1430
1522
  (e) => e.isDirectory() && e.name !== "." && e.name !== ".."
1431
1523
  );
1432
1524
  if (dirs.length === 1) {
1433
- return path9.join(tmpDir, dirs[0].name);
1525
+ return path10.join(tmpDir, dirs[0].name);
1434
1526
  }
1435
1527
  return tmpDir;
1436
1528
  }
@@ -1485,14 +1577,14 @@ async function resolveContentSourceForVersion(version, opts = {}) {
1485
1577
  return source;
1486
1578
  }
1487
1579
  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");
1580
+ const cacheDir = path10.join(os2.homedir(), ".arcane", "cache");
1581
+ if (!fs9.existsSync(cacheDir)) return null;
1582
+ const entries = fs9.readdirSync(cacheDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => {
1583
+ const metaPath = path10.join(cacheDir, e.name, ".cache-meta.json");
1492
1584
  let cachedAt = 0;
1493
- if (fs8.existsSync(metaPath)) {
1585
+ if (fs9.existsSync(metaPath)) {
1494
1586
  try {
1495
- const meta = JSON.parse(fs8.readFileSync(metaPath, "utf-8"));
1587
+ const meta = JSON.parse(fs9.readFileSync(metaPath, "utf-8"));
1496
1588
  cachedAt = new Date(meta.cached_at).getTime();
1497
1589
  } catch {
1498
1590
  cachedAt = 0;
@@ -1504,14 +1596,14 @@ function findLatestCache() {
1504
1596
  }
1505
1597
 
1506
1598
  // src/registry.ts
1507
- import path10 from "path";
1599
+ import path11 from "path";
1508
1600
  import os3 from "os";
1509
1601
  var REGISTRY_VERSION = 1;
1510
1602
  function arcaneHome() {
1511
- return process.env.ARCANE_HOME ?? path10.join(os3.homedir(), ".arcane");
1603
+ return process.env.ARCANE_HOME ?? path11.join(os3.homedir(), ".arcane");
1512
1604
  }
1513
1605
  function registryPath() {
1514
- return path10.join(arcaneHome(), "installations.json");
1606
+ return path11.join(arcaneHome(), "installations.json");
1515
1607
  }
1516
1608
  function readRegistryFile() {
1517
1609
  const p2 = registryPath();
@@ -1536,7 +1628,7 @@ function writeRegistryFile(registry) {
1536
1628
  }
1537
1629
  }
1538
1630
  function registerInstallation(target) {
1539
- const abs = path10.resolve(target);
1631
+ const abs = path11.resolve(target);
1540
1632
  const registry = readRegistryFile();
1541
1633
  if (registry.installations.some((e) => e.path === abs)) {
1542
1634
  return;
@@ -1563,8 +1655,8 @@ async function installCommand(profileExpr, opts) {
1563
1655
  quiet: !profileExpr
1564
1656
  });
1565
1657
  const root = await source.getContentRoot();
1566
- const profilesDir = path11.join(root, "profiles");
1567
- const target = path11.resolve(opts.target ?? process.cwd());
1658
+ const profilesDir = path12.join(root, "profiles");
1659
+ const target = path12.resolve(opts.target ?? process.cwd());
1568
1660
  if (!profileExpr) {
1569
1661
  const profiles = listProfiles(profilesDir);
1570
1662
  if (process.stdin.isTTY && process.stdout.isTTY) {
@@ -1635,21 +1727,21 @@ Installing profile: ${chalk2.cyan(profileExpr)}`)
1635
1727
  }
1636
1728
 
1637
1729
  // src/commands/add.ts
1638
- import fs10 from "fs";
1639
- import path13 from "path";
1730
+ import fs11 from "fs";
1731
+ import path14 from "path";
1640
1732
  import chalk3 from "chalk";
1641
1733
 
1642
1734
  // src/skills-catalog.ts
1643
- import fs9 from "fs";
1644
- import path12 from "path";
1735
+ import fs10 from "fs";
1736
+ import path13 from "path";
1645
1737
  var FRONTMATTER_BYTES = 4096;
1646
1738
  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"))
1739
+ if (!fs10.existsSync(skillsDir)) return [];
1740
+ return fs10.readdirSync(skillsDir, { withFileTypes: true }).filter(
1741
+ (d) => d.isDirectory() && !d.name.startsWith("_") && fs10.existsSync(path13.join(skillsDir, d.name, "SKILL.md"))
1650
1742
  ).map((d) => ({
1651
1743
  name: d.name,
1652
- description: readDescription(path12.join(skillsDir, d.name, "SKILL.md"))
1744
+ description: readDescription(path13.join(skillsDir, d.name, "SKILL.md"))
1653
1745
  })).sort((a, b) => a.name.localeCompare(b.name));
1654
1746
  }
1655
1747
  function readDescription(skillFile) {
@@ -1670,13 +1762,13 @@ function readDescription(skillFile) {
1670
1762
  return inline.replace(/^["']|["']$/g, "").trim();
1671
1763
  }
1672
1764
  function readHead(file) {
1673
- const fd = fs9.openSync(file, "r");
1765
+ const fd = fs10.openSync(file, "r");
1674
1766
  try {
1675
1767
  const buf = Buffer.alloc(FRONTMATTER_BYTES);
1676
- const read = fs9.readSync(fd, buf, 0, FRONTMATTER_BYTES, 0);
1768
+ const read = fs10.readSync(fd, buf, 0, FRONTMATTER_BYTES, 0);
1677
1769
  return buf.subarray(0, read).toString("utf-8");
1678
1770
  } finally {
1679
- fs9.closeSync(fd);
1771
+ fs10.closeSync(fd);
1680
1772
  }
1681
1773
  }
1682
1774
 
@@ -1700,15 +1792,15 @@ async function addCommand(items = []) {
1700
1792
  return;
1701
1793
  }
1702
1794
  const selected = await runAddWizard({
1703
- profiles: listProfiles(path13.join(root, "profiles")),
1704
- skills: listSkills(path13.join(root, "skills")),
1795
+ profiles: listProfiles(path14.join(root, "profiles")),
1796
+ skills: listSkills(path14.join(root, "skills")),
1705
1797
  installedProfiles: manifest.profiles,
1706
1798
  installedSkills: manifest.installed_skills
1707
1799
  });
1708
1800
  if (!selected) return;
1709
1801
  items = selected;
1710
1802
  }
1711
- const claudeDir = path13.join(target, ".claude");
1803
+ const claudeDir = path14.join(target, ".claude");
1712
1804
  const added = [];
1713
1805
  const skipped = [];
1714
1806
  const notFound = [];
@@ -1718,8 +1810,8 @@ async function addCommand(items = []) {
1718
1810
  for (const item of items) {
1719
1811
  if (item.startsWith("+")) {
1720
1812
  const profileName = item.slice(1);
1721
- const profilePath = path13.join(root, "profiles", `${profileName}.yaml`);
1722
- if (!fs10.existsSync(profilePath)) {
1813
+ const profilePath = path14.join(root, "profiles", `${profileName}.yaml`);
1814
+ if (!fs11.existsSync(profilePath)) {
1723
1815
  console.error(chalk3.red(` Profile '${profileName}' not found`));
1724
1816
  continue;
1725
1817
  }
@@ -1732,10 +1824,10 @@ async function addCommand(items = []) {
1732
1824
  }
1733
1825
  for (const rule of profile.rules.universal) {
1734
1826
  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`));
1827
+ const src = path14.join(root, "rules", `${rule}.md`);
1828
+ if (fs11.existsSync(src)) {
1829
+ ensureDir(path14.join(claudeDir, "rules"));
1830
+ fs11.copyFileSync(src, path14.join(claudeDir, "rules", `${rule}.md`));
1739
1831
  manifest.installed_rules.push(rule);
1740
1832
  addedRules.push(rule);
1741
1833
  }
@@ -1743,30 +1835,28 @@ async function addCommand(items = []) {
1743
1835
  }
1744
1836
  for (const rule of profile.rules.gamedev) {
1745
1837
  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`));
1838
+ const src = path14.join(root, "rules", "gamedev", `${rule}.md`);
1839
+ if (fs11.existsSync(src)) {
1840
+ ensureDir(path14.join(claudeDir, "rules"));
1841
+ fs11.copyFileSync(src, path14.join(claudeDir, "rules", `${rule}.md`));
1750
1842
  manifest.installed_rules.push(rule);
1751
1843
  addedRules.push(rule);
1752
1844
  }
1753
1845
  }
1754
1846
  }
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
- }
1847
+ for (const entry of profile.agents) {
1848
+ const { division } = parseAgentEntry(entry);
1849
+ if (manifest.installed_agents.includes(entry)) continue;
1850
+ if (isGranular(entry) && manifest.installed_agents.includes(division)) continue;
1851
+ if (copyAgentEntry(root, claudeDir, entry) !== null) {
1852
+ manifest.installed_agents.push(entry);
1853
+ addedAgents.push(entry);
1764
1854
  }
1765
1855
  }
1766
1856
  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"));
1857
+ const statuslineSrc = path14.join(root, "hooks", "statusline.sh");
1858
+ if (fs11.existsSync(statuslineSrc)) {
1859
+ fs11.copyFileSync(statuslineSrc, path14.join(claudeDir, "statusline.sh"));
1770
1860
  statuslineAdded = true;
1771
1861
  }
1772
1862
  }
@@ -1796,6 +1886,7 @@ async function addCommand(items = []) {
1796
1886
  rules: { universal: manifest.installed_rules, gamedev: [] },
1797
1887
  agents: manifest.installed_agents,
1798
1888
  hooks: [],
1889
+ output_styles: [],
1799
1890
  permissions: { allow: [], deny: [] }
1800
1891
  };
1801
1892
  writeManifest(target, merged, manifest.profile_command, root);
@@ -1808,7 +1899,7 @@ Added ${totalAdded} items:`
1808
1899
  );
1809
1900
  for (const s of added) console.log(chalk3.green(` [ok] skill: ${s}`));
1810
1901
  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}/`));
1902
+ for (const a of addedAgents) console.log(chalk3.green(` [ok] ${agentEntryLabel(a)}`));
1812
1903
  if (statuslineAdded) console.log(chalk3.green(" [ok] statusline.sh"));
1813
1904
  for (const s of skipped)
1814
1905
  console.log(chalk3.dim(` [skip] ${s} (already installed)`));
@@ -1824,10 +1915,10 @@ Added ${totalAdded} items:`
1824
1915
  }
1825
1916
  }
1826
1917
  function printCatalog(root, installedProfiles, installedSkills) {
1827
- const profiles = listProfiles(path13.join(root, "profiles")).filter(
1918
+ const profiles = listProfiles(path14.join(root, "profiles")).filter(
1828
1919
  (pr) => !installedProfiles.includes(pr.name)
1829
1920
  );
1830
- const skills = listSkills(path13.join(root, "skills")).filter(
1921
+ const skills = listSkills(path14.join(root, "skills")).filter(
1831
1922
  (sk) => !installedSkills.includes(sk.name)
1832
1923
  );
1833
1924
  if (profiles.length === 0 && skills.length === 0) {
@@ -1853,15 +1944,15 @@ function printCatalog(root, installedProfiles, installedSkills) {
1853
1944
  }
1854
1945
  function addSkill(root, target, skill, installed) {
1855
1946
  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);
1947
+ const src = path14.join(root, "skills", skill);
1948
+ if (!fs11.existsSync(src)) return "not-found";
1949
+ const dst = path14.join(target, ".claude", "skills", skill);
1859
1950
  copyDirSync(src, dst);
1860
1951
  return "added";
1861
1952
  }
1862
1953
  function mergePermissions(claudeDir, newPerms) {
1863
- const settingsPath = path13.join(claudeDir, "settings.json");
1864
- if (!fs10.existsSync(settingsPath)) return;
1954
+ const settingsPath = path14.join(claudeDir, "settings.json");
1955
+ if (!fs11.existsSync(settingsPath)) return;
1865
1956
  const settings = readJsonSync(settingsPath);
1866
1957
  const perms = settings.permissions ?? { allow: [], deny: [] };
1867
1958
  const allowSet = new Set(perms.allow);
@@ -1874,8 +1965,8 @@ function mergePermissions(claudeDir, newPerms) {
1874
1965
  writeJsonSync(settingsPath, settings);
1875
1966
  }
1876
1967
  function addStatuslineToSettings(claudeDir) {
1877
- const settingsPath = path13.join(claudeDir, "settings.json");
1878
- if (!fs10.existsSync(settingsPath)) return;
1968
+ const settingsPath = path14.join(claudeDir, "settings.json");
1969
+ if (!fs11.existsSync(settingsPath)) return;
1879
1970
  const settings = readJsonSync(settingsPath);
1880
1971
  if (settings.statusLine) return;
1881
1972
  settings.statusLine = {
@@ -1886,8 +1977,8 @@ function addStatuslineToSettings(claudeDir) {
1886
1977
  }
1887
1978
 
1888
1979
  // src/commands/remove.ts
1889
- import fs11 from "fs";
1890
- import path14 from "path";
1980
+ import fs12 from "fs";
1981
+ import path15 from "path";
1891
1982
  import chalk4 from "chalk";
1892
1983
  var CORE_SKILLS = [
1893
1984
  "commit",
@@ -1952,7 +2043,7 @@ async function removeCommand(items, opts = {}) {
1952
2043
  }
1953
2044
  manifest.total_skills = manifest.installed_skills.length;
1954
2045
  manifest.total_rules = manifest.installed_rules.length;
1955
- const mp = path14.join(target, ".claude", "arcane-manifest.json");
2046
+ const mp = path15.join(target, ".claude", "arcane-manifest.json");
1956
2047
  writeJsonSync(mp, manifest);
1957
2048
  const totalRemoved = removedSkills.length + removedAgents.length + removedProfiles.length;
1958
2049
  console.log(chalk4.bold(`
@@ -1973,9 +2064,9 @@ function removeSkill(target, skill, manifest) {
1973
2064
  );
1974
2065
  return "skipped";
1975
2066
  }
1976
- const skillDir = path14.join(target, ".claude", "skills", skill);
1977
- if (!fs11.existsSync(skillDir)) return "skipped";
1978
- fs11.rmSync(skillDir, { recursive: true, force: true });
2067
+ const skillDir = path15.join(target, ".claude", "skills", skill);
2068
+ if (!fs12.existsSync(skillDir)) return "skipped";
2069
+ fs12.rmSync(skillDir, { recursive: true, force: true });
1979
2070
  manifest.installed_skills = manifest.installed_skills.filter(
1980
2071
  (s) => s !== skill
1981
2072
  );
@@ -1994,8 +2085,8 @@ function removeProfile(root, target, profileName, manifest) {
1994
2085
  );
1995
2086
  return { removed: false, skills: [], agents: [] };
1996
2087
  }
1997
- const profilePath = path14.join(root, "profiles", `${profileName}.yaml`);
1998
- if (!fs11.existsSync(profilePath)) {
2088
+ const profilePath = path15.join(root, "profiles", `${profileName}.yaml`);
2089
+ if (!fs12.existsSync(profilePath)) {
1999
2090
  console.warn(
2000
2091
  chalk4.yellow(` WARNING: Profile '${profileName}.yaml' not found.`)
2001
2092
  );
@@ -2009,8 +2100,8 @@ function removeProfile(root, target, profileName, manifest) {
2009
2100
  const sharedAgents = /* @__PURE__ */ new Set();
2010
2101
  const sharedRules = /* @__PURE__ */ new Set();
2011
2102
  for (const rp of remainingProfiles) {
2012
- const rpPath = path14.join(root, "profiles", `${rp}.yaml`);
2013
- if (!fs11.existsSync(rpPath)) continue;
2103
+ const rpPath = path15.join(root, "profiles", `${rp}.yaml`);
2104
+ if (!fs12.existsSync(rpPath)) continue;
2014
2105
  const rpDef = parseProfile(rpPath);
2015
2106
  rpDef.skills.forEach((s) => sharedSkills.add(s));
2016
2107
  rpDef.agents.forEach((a) => sharedAgents.add(a));
@@ -2021,9 +2112,9 @@ function removeProfile(root, target, profileName, manifest) {
2021
2112
  for (const skill of profile.skills) {
2022
2113
  if (sharedSkills.has(skill)) continue;
2023
2114
  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 });
2115
+ const skillDir = path15.join(target, ".claude", "skills", skill);
2116
+ if (fs12.existsSync(skillDir)) {
2117
+ fs12.rmSync(skillDir, { recursive: true, force: true });
2027
2118
  removedSkills.push(skill);
2028
2119
  }
2029
2120
  manifest.installed_skills = manifest.installed_skills.filter(
@@ -2031,15 +2122,14 @@ function removeProfile(root, target, profileName, manifest) {
2031
2122
  );
2032
2123
  }
2033
2124
  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
- }
2125
+ const claudeDir = path15.join(target, ".claude");
2126
+ for (const entry of profile.agents) {
2127
+ if (sharedAgents.has(entry)) continue;
2128
+ const { division } = parseAgentEntry(entry);
2129
+ if (isGranular(entry) && sharedAgents.has(division)) continue;
2130
+ if (removeAgentEntry(claudeDir, entry)) removedAgents.push(entry);
2041
2131
  manifest.installed_agents = manifest.installed_agents.filter(
2042
- (a) => a !== agent
2132
+ (a) => a !== entry
2043
2133
  );
2044
2134
  }
2045
2135
  for (const rule of [
@@ -2047,44 +2137,44 @@ function removeProfile(root, target, profileName, manifest) {
2047
2137
  ...profile.rules.gamedev
2048
2138
  ]) {
2049
2139
  if (sharedRules.has(rule)) continue;
2050
- const ruleFile = path14.join(target, ".claude", "rules", `${rule}.md`);
2051
- if (fs11.existsSync(ruleFile)) {
2052
- fs11.rmSync(ruleFile);
2140
+ const ruleFile = path15.join(target, ".claude", "rules", `${rule}.md`);
2141
+ if (fs12.existsSync(ruleFile)) {
2142
+ fs12.rmSync(ruleFile);
2053
2143
  }
2054
2144
  manifest.installed_rules = manifest.installed_rules.filter(
2055
2145
  (r) => r !== rule
2056
2146
  );
2057
2147
  }
2058
2148
  if (profileName === "statusline") {
2059
- const statuslineFile = path14.join(target, ".claude", "statusline.sh");
2060
- if (fs11.existsSync(statuslineFile)) {
2061
- fs11.rmSync(statuslineFile);
2149
+ const statuslineFile = path15.join(target, ".claude", "statusline.sh");
2150
+ if (fs12.existsSync(statuslineFile)) {
2151
+ fs12.rmSync(statuslineFile);
2062
2152
  }
2063
- removeStatuslineFromSettings(path14.join(target, ".claude"));
2153
+ removeStatuslineFromSettings(path15.join(target, ".claude"));
2064
2154
  }
2065
2155
  manifest.profiles = remainingProfiles;
2066
2156
  manifest.profile_command = remainingProfiles.filter((p2) => p2 !== "core").join("+");
2067
2157
  return { removed: true, skills: removedSkills, agents: removedAgents };
2068
2158
  }
2069
2159
  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"));
2160
+ const settingsPath = path15.join(claudeDir, "settings.json");
2161
+ if (!fs12.existsSync(settingsPath)) return;
2162
+ const settings = JSON.parse(fs12.readFileSync(settingsPath, "utf-8"));
2073
2163
  if (settings.statusLine) {
2074
2164
  delete settings.statusLine;
2075
- fs11.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
2165
+ fs12.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
2076
2166
  }
2077
2167
  }
2078
2168
 
2079
2169
  // src/commands/list.ts
2080
- import fs12 from "fs";
2081
- import path15 from "path";
2170
+ import fs13 from "fs";
2171
+ import path16 from "path";
2082
2172
  import chalk5 from "chalk";
2083
2173
  async function listCommand() {
2084
2174
  const source = await resolveContentSource({ quiet: true });
2085
2175
  const root = await source.getContentRoot();
2086
- const profilesDir = path15.join(root, "profiles");
2087
- const skillsDir = path15.join(root, "skills");
2176
+ const profilesDir = path16.join(root, "profiles");
2177
+ const skillsDir = path16.join(root, "skills");
2088
2178
  const target = process.cwd();
2089
2179
  const manifest = readManifest(target);
2090
2180
  const profiles = listProfiles(profilesDir);
@@ -2101,7 +2191,7 @@ async function listCommand() {
2101
2191
  }
2102
2192
  console.log();
2103
2193
  }
2104
- if (fs12.existsSync(skillsDir)) {
2194
+ if (fs13.existsSync(skillsDir)) {
2105
2195
  const allSkills = listSkills(skillsDir).map((s) => s.name);
2106
2196
  console.log(chalk5.bold(`
2107
2197
  === Skills (${allSkills.length}) ===
@@ -2168,8 +2258,8 @@ async function statusCommand() {
2168
2258
  }
2169
2259
 
2170
2260
  // src/commands/update.ts
2171
- import fs13 from "fs";
2172
- import path16 from "path";
2261
+ import fs14 from "fs";
2262
+ import path17 from "path";
2173
2263
  import os4 from "os";
2174
2264
  import chalk7 from "chalk";
2175
2265
 
@@ -2245,12 +2335,12 @@ async function updateCommand(opts) {
2245
2335
  }
2246
2336
  }
2247
2337
  const cwd = process.cwd();
2248
- if (fs13.existsSync(manifestPath(cwd))) {
2338
+ if (fs14.existsSync(manifestPath(cwd))) {
2249
2339
  registerInstallation(cwd);
2250
2340
  }
2251
2341
  const targets = pruneRegistry().map((e) => e.path);
2252
2342
  const globalTarget = os4.homedir();
2253
- if (!process.env.VITEST && fs13.existsSync(manifestPath(globalTarget)) && !targets.includes(globalTarget)) {
2343
+ if (!process.env.VITEST && fs14.existsSync(manifestPath(globalTarget)) && !targets.includes(globalTarget)) {
2254
2344
  targets.push(globalTarget);
2255
2345
  }
2256
2346
  if (targets.length === 0) {
@@ -2305,16 +2395,34 @@ async function updateTarget(target, opts) {
2305
2395
  const root = await contentSource.getContentRoot();
2306
2396
  const currentVersion = await contentSource.getVersion();
2307
2397
  const installedVersion = manifest.source_version ?? manifest.arcane_version;
2398
+ const profileNamesForSync = manifest.profile_command.split("+").filter(Boolean);
2399
+ const claudeDirForSync = path17.join(target, ".claude");
2400
+ const earlySync = syncUnhashedFiles(
2401
+ root,
2402
+ claudeDirForSync,
2403
+ mergeProfiles(path17.join(root, "profiles"), profileNamesForSync),
2404
+ opts.dryRun ?? false
2405
+ );
2406
+ if (earlySync.length > 0 && !opts.quiet) {
2407
+ const prefix = opts.dryRun ? "[dry-run] " : "";
2408
+ console.log(`
2409
+ ${prefix}${chalk7.cyan("Refreshed:")}`);
2410
+ for (const label of earlySync) {
2411
+ console.log(` ~ ${label}`);
2412
+ }
2413
+ }
2308
2414
  if (installedVersion === currentVersion && !opts.force) {
2309
2415
  if (!opts.quiet) {
2310
- console.log(chalk7.green(`Already up to date (v${currentVersion}).`));
2416
+ console.log(
2417
+ earlySync.length > 0 ? chalk7.green(`Refreshed ${earlySync.length} file(s); otherwise up to date (v${currentVersion}).`) : chalk7.green(`Already up to date (v${currentVersion}).`)
2418
+ );
2311
2419
  }
2312
2420
  return {
2313
2421
  target,
2314
- status: "up-to-date",
2422
+ status: earlySync.length > 0 ? "updated" : "up-to-date",
2315
2423
  fromVersion: installedVersion,
2316
2424
  toVersion: currentVersion,
2317
- updated: 0,
2425
+ updated: earlySync.length,
2318
2426
  skipped: 0,
2319
2427
  removed: 0
2320
2428
  };
@@ -2326,12 +2434,13 @@ Update available: ${chalk7.red(installedVersion)} -> ${chalk7.green(currentVersi
2326
2434
  );
2327
2435
  }
2328
2436
  const profileNames = manifest.profile_command.split("+").filter(Boolean);
2329
- const profilesDir = path16.join(root, "profiles");
2437
+ const profilesDir = path17.join(root, "profiles");
2330
2438
  const merged = mergeProfiles(profilesDir, profileNames);
2331
2439
  const allRules = [...merged.rules.universal, ...merged.rules.gamedev];
2332
2440
  const sourceHashes = computeSourceHashes(root, merged.skills, allRules, merged.agents);
2333
- const claudeDir = path16.join(target, ".claude");
2441
+ const claudeDir = path17.join(target, ".claude");
2334
2442
  const installedHashes = computeContentHashes(claudeDir);
2443
+ const unhashed = earlySync;
2335
2444
  const items = computeUpdatePlan(
2336
2445
  manifest.content_hashes ?? null,
2337
2446
  installedHashes,
@@ -2340,10 +2449,11 @@ Update available: ${chalk7.red(installedVersion)} -> ${chalk7.green(currentVersi
2340
2449
  const updates = items.filter((i) => i.action === "update" || i.action === "add" || i.action === "conflict");
2341
2450
  const skipped = items.filter((i) => i.action === "skip-customized" || i.action === "skip-unchanged");
2342
2451
  const removed = items.filter((i) => i.action === "remove");
2452
+ const forcible = opts.force ? items.filter((i) => i.action === "skip-customized") : [];
2343
2453
  if (!opts.quiet) {
2344
2454
  printUpdateSummary(items, opts.dryRun ?? false);
2345
2455
  }
2346
- if (updates.length === 0 && removed.length === 0) {
2456
+ if (updates.length === 0 && removed.length === 0 && forcible.length === 0) {
2347
2457
  if (!opts.dryRun) {
2348
2458
  updateManifestFields(target, {
2349
2459
  arcane_version: currentVersion,
@@ -2358,7 +2468,10 @@ Update available: ${chalk7.red(installedVersion)} -> ${chalk7.green(currentVersi
2358
2468
  });
2359
2469
  }
2360
2470
  if (!opts.quiet) {
2361
- console.log(chalk7.green("\nNo changes to apply."));
2471
+ console.log(
2472
+ unhashed.length > 0 ? chalk7.green(`
2473
+ Refreshed ${unhashed.length} file(s); no other changes to apply.`) : chalk7.green("\nNo changes to apply.")
2474
+ );
2362
2475
  }
2363
2476
  return {
2364
2477
  target,
@@ -2520,51 +2633,103 @@ function applyUpdates(items, root, claudeDir, merged, force) {
2520
2633
  }
2521
2634
  }
2522
2635
  }
2636
+ function syncUnhashedFiles(root, claudeDir, merged, dryRun) {
2637
+ const targets = [];
2638
+ const statuslineDst = path17.join(claudeDir, "statusline.sh");
2639
+ if (merged.loaded.includes("statusline") || fs14.existsSync(statuslineDst)) {
2640
+ targets.push({
2641
+ src: path17.join(root, "hooks", "statusline.sh"),
2642
+ dst: statuslineDst,
2643
+ label: "statusline.sh"
2644
+ });
2645
+ }
2646
+ for (const style of merged.output_styles) {
2647
+ targets.push({
2648
+ src: path17.join(root, "output-styles", `${style}.md`),
2649
+ dst: path17.join(claudeDir, "output-styles", `${style}.md`),
2650
+ label: `output-styles/${style}.md`
2651
+ });
2652
+ }
2653
+ const changed = [];
2654
+ for (const t of targets) {
2655
+ if (!fs14.existsSync(t.src)) continue;
2656
+ const src = fs14.readFileSync(t.src);
2657
+ if (fs14.existsSync(t.dst) && src.equals(fs14.readFileSync(t.dst))) continue;
2658
+ changed.push(t.label);
2659
+ if (dryRun) continue;
2660
+ if (fs14.existsSync(t.dst)) {
2661
+ const bak = `${t.dst}.bak`;
2662
+ if (fs14.existsSync(bak)) fs14.rmSync(bak, { force: true });
2663
+ fs14.renameSync(t.dst, bak);
2664
+ }
2665
+ fs14.mkdirSync(path17.dirname(t.dst), { recursive: true });
2666
+ fs14.copyFileSync(t.src, t.dst);
2667
+ }
2668
+ const activeStyle = merged.output_styles[0];
2669
+ if (activeStyle) {
2670
+ const settingsPath = path17.join(claudeDir, "settings.json");
2671
+ if (fs14.existsSync(settingsPath)) {
2672
+ try {
2673
+ const settings = JSON.parse(fs14.readFileSync(settingsPath, "utf-8"));
2674
+ if (settings.outputStyle !== activeStyle) {
2675
+ changed.push(`settings.json (outputStyle: ${activeStyle})`);
2676
+ if (!dryRun) {
2677
+ settings.outputStyle = activeStyle;
2678
+ fs14.writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}
2679
+ `, "utf-8");
2680
+ }
2681
+ }
2682
+ } catch {
2683
+ }
2684
+ }
2685
+ }
2686
+ return changed;
2687
+ }
2523
2688
  function applyItem(item, root, claudeDir) {
2524
2689
  const src = getSourcePath(item, root);
2525
2690
  const dst = getInstalledPath(item, claudeDir);
2526
- if (!src || !fs13.existsSync(src)) return;
2691
+ if (!src || !fs14.existsSync(src)) return;
2527
2692
  if (item.type === "skill" || item.type === "agent") {
2528
- if (fs13.existsSync(dst)) {
2529
- fs13.rmSync(dst, { recursive: true, force: true });
2693
+ if (fs14.existsSync(dst)) {
2694
+ fs14.rmSync(dst, { recursive: true, force: true });
2530
2695
  }
2531
2696
  copyDirSync(src, dst);
2532
2697
  } else {
2533
- const dir = path16.dirname(dst);
2534
- if (!fs13.existsSync(dir)) {
2535
- fs13.mkdirSync(dir, { recursive: true });
2698
+ const dir = path17.dirname(dst);
2699
+ if (!fs14.existsSync(dir)) {
2700
+ fs14.mkdirSync(dir, { recursive: true });
2536
2701
  }
2537
- fs13.copyFileSync(src, dst);
2702
+ fs14.copyFileSync(src, dst);
2538
2703
  }
2539
2704
  }
2540
2705
  function backupItem(item, claudeDir) {
2541
2706
  const installed = getInstalledPath(item, claudeDir);
2542
- if (!fs13.existsSync(installed)) return;
2707
+ if (!fs14.existsSync(installed)) return;
2543
2708
  const backupPath = installed + ".bak";
2544
- if (fs13.existsSync(backupPath)) {
2545
- fs13.rmSync(backupPath, { recursive: true, force: true });
2709
+ if (fs14.existsSync(backupPath)) {
2710
+ fs14.rmSync(backupPath, { recursive: true, force: true });
2546
2711
  }
2547
- fs13.renameSync(installed, backupPath);
2712
+ fs14.renameSync(installed, backupPath);
2548
2713
  }
2549
2714
  function removeItem(item, claudeDir) {
2550
2715
  const installed = getInstalledPath(item, claudeDir);
2551
- if (fs13.existsSync(installed)) {
2552
- fs13.rmSync(installed, { recursive: true, force: true });
2716
+ if (fs14.existsSync(installed)) {
2717
+ fs14.rmSync(installed, { recursive: true, force: true });
2553
2718
  }
2554
2719
  }
2555
2720
  function getSourcePath(item, root) {
2556
2721
  switch (item.type) {
2557
2722
  case "skill":
2558
- return path16.join(root, "skills", item.name);
2723
+ return path17.join(root, "skills", item.name);
2559
2724
  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);
2725
+ const direct = path17.join(root, "rules", item.name);
2726
+ if (fs14.existsSync(direct)) return direct;
2727
+ return path17.join(root, "rules", "gamedev", item.name);
2563
2728
  }
2564
2729
  case "agent":
2565
- return path16.join(root, "agents", item.name);
2730
+ return path17.join(root, "agents", item.name);
2566
2731
  case "hook":
2567
- return path16.join(root, "hooks", item.name);
2732
+ return path17.join(root, "hooks", item.name);
2568
2733
  default:
2569
2734
  return null;
2570
2735
  }
@@ -2572,13 +2737,13 @@ function getSourcePath(item, root) {
2572
2737
  function getInstalledPath(item, claudeDir) {
2573
2738
  switch (item.type) {
2574
2739
  case "skill":
2575
- return path16.join(claudeDir, "skills", item.name);
2740
+ return path17.join(claudeDir, "skills", item.name);
2576
2741
  case "rule":
2577
- return path16.join(claudeDir, "rules", item.name);
2742
+ return path17.join(claudeDir, "rules", item.name);
2578
2743
  case "agent":
2579
- return path16.join(claudeDir, "agents", item.name);
2744
+ return path17.join(claudeDir, "agents", item.name);
2580
2745
  case "hook":
2581
- return path16.join(claudeDir, "hooks", item.name);
2746
+ return path17.join(claudeDir, "hooks", item.name);
2582
2747
  }
2583
2748
  }
2584
2749
 
@@ -2613,8 +2778,8 @@ async function cleanCommand(opts) {
2613
2778
  }
2614
2779
 
2615
2780
  // src/commands/worktree.ts
2616
- import fs14 from "fs";
2617
- import path17 from "path";
2781
+ import fs15 from "fs";
2782
+ import path18 from "path";
2618
2783
  import { execSync as execSync2 } from "child_process";
2619
2784
  import chalk9 from "chalk";
2620
2785
  async function worktreeCommand(branch, opts) {
@@ -2625,8 +2790,8 @@ async function worktreeCommand(branch, opts) {
2625
2790
  process.exit(1);
2626
2791
  }
2627
2792
  const repoRoot = wtInfo.isWorktree ? wtInfo.mainWorktreePath : cwd;
2628
- const worktreePath = opts.path ? path17.resolve(opts.path) : defaultWorktreePath(repoRoot, branch);
2629
- if (fs14.existsSync(worktreePath)) {
2793
+ const worktreePath = opts.path ? path18.resolve(opts.path) : defaultWorktreePath(repoRoot, branch);
2794
+ if (fs15.existsSync(worktreePath)) {
2630
2795
  console.error(
2631
2796
  chalk9.red(`Path already exists: ${worktreePath}`)
2632
2797
  );
@@ -2673,7 +2838,7 @@ async function worktreeCommand(branch, opts) {
2673
2838
  }
2674
2839
  console.log(" Installing Arcane...");
2675
2840
  const root = getPackageRoot();
2676
- const profilesDir = path17.join(root, "profiles");
2841
+ const profilesDir = path18.join(root, "profiles");
2677
2842
  const profileNames = profileExpr.split("+").filter(Boolean);
2678
2843
  const merged = mergeProfiles(profilesDir, profileNames);
2679
2844
  const shareFrom = opts.share !== false ? cwd : void 0;
@@ -2709,13 +2874,13 @@ async function worktreeCommand(branch, opts) {
2709
2874
  }
2710
2875
  }
2711
2876
  if (opts.isolate) {
2712
- const isolateScript = path17.join(
2877
+ const isolateScript = path18.join(
2713
2878
  worktreePath,
2714
2879
  ".claude",
2715
2880
  "skills",
2716
2881
  "worktree-isolation"
2717
2882
  );
2718
- if (fs14.existsSync(isolateScript)) {
2883
+ if (fs15.existsSync(isolateScript)) {
2719
2884
  console.log(" Applying worktree isolation...");
2720
2885
  try {
2721
2886
  execSync2(
@@ -2750,13 +2915,13 @@ async function worktreeCommand(branch, opts) {
2750
2915
  }
2751
2916
 
2752
2917
  // src/commands/global.ts
2753
- import fs15 from "fs";
2754
- import path18 from "path";
2918
+ import fs16 from "fs";
2919
+ import path19 from "path";
2755
2920
  import os5 from "os";
2756
2921
  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");
2922
+ var CLAUDE_HOME = path19.join(os5.homedir(), ".claude");
2923
+ var SCRIPTS_DIR = path19.join(CLAUDE_HOME, "scripts", "worktree-isolation");
2924
+ var SETTINGS_PATH = path19.join(CLAUDE_HOME, "settings.json");
2760
2925
  async function globalCommand(opts) {
2761
2926
  if (opts.status) {
2762
2927
  showStatus();
@@ -2770,8 +2935,8 @@ async function globalCommand(opts) {
2770
2935
  }
2771
2936
  function showStatus() {
2772
2937
  console.log(chalk10.bold("\n=== Arcane Global Status ===\n"));
2773
- const hasScripts = fs15.existsSync(
2774
- path18.join(SCRIPTS_DIR, "apply.py")
2938
+ const hasScripts = fs16.existsSync(
2939
+ path19.join(SCRIPTS_DIR, "apply.py")
2775
2940
  );
2776
2941
  console.log(
2777
2942
  ` Worktree scripts: ${hasScripts ? chalk10.green("installed") : chalk10.dim("not installed")}`
@@ -2789,32 +2954,32 @@ function showStatus() {
2789
2954
  function installGlobal() {
2790
2955
  console.log(chalk10.bold("\nInstalling Arcane global hooks...\n"));
2791
2956
  const root = getPackageRoot();
2792
- const scriptsSrc = path18.join(
2957
+ const scriptsSrc = path19.join(
2793
2958
  root,
2794
2959
  "skills",
2795
2960
  "worktree-isolation",
2796
2961
  "scripts"
2797
2962
  );
2798
- if (!fs15.existsSync(scriptsSrc)) {
2963
+ if (!fs16.existsSync(scriptsSrc)) {
2799
2964
  console.error(
2800
2965
  chalk10.red(" worktree-isolation scripts not found in package.")
2801
2966
  );
2802
2967
  process.exit(1);
2803
2968
  }
2804
- fs15.mkdirSync(SCRIPTS_DIR, { recursive: true });
2969
+ fs16.mkdirSync(SCRIPTS_DIR, { recursive: true });
2805
2970
  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)) {
2971
+ const src = path19.join(scriptsSrc, file);
2972
+ const dst = path19.join(SCRIPTS_DIR, file);
2973
+ if (!fs16.existsSync(src)) {
2809
2974
  console.warn(chalk10.yellow(` WARN: ${file} not found, skipping`));
2810
2975
  continue;
2811
2976
  }
2812
- if (fs15.existsSync(dst) && fs15.readFileSync(src).equals(fs15.readFileSync(dst))) {
2977
+ if (fs16.existsSync(dst) && fs16.readFileSync(src).equals(fs16.readFileSync(dst))) {
2813
2978
  console.log(chalk10.dim(` [skip] ${file} (already up-to-date)`));
2814
2979
  } else {
2815
- fs15.copyFileSync(src, dst);
2980
+ fs16.copyFileSync(src, dst);
2816
2981
  if (process.platform !== "win32") {
2817
- fs15.chmodSync(dst, 493);
2982
+ fs16.chmodSync(dst, 493);
2818
2983
  }
2819
2984
  console.log(chalk10.green(` [ok] ${file} -> ${SCRIPTS_DIR}`));
2820
2985
  }
@@ -2838,8 +3003,8 @@ function installGlobal() {
2838
3003
  }
2839
3004
  function removeGlobal() {
2840
3005
  console.log(chalk10.bold("\nRemoving Arcane global hooks...\n"));
2841
- if (fs15.existsSync(SCRIPTS_DIR)) {
2842
- fs15.rmSync(SCRIPTS_DIR, { recursive: true, force: true });
3006
+ if (fs16.existsSync(SCRIPTS_DIR)) {
3007
+ fs16.rmSync(SCRIPTS_DIR, { recursive: true, force: true });
2843
3008
  console.log(chalk10.green(" [ok] Removed worktree-isolation scripts"));
2844
3009
  } else {
2845
3010
  console.log(chalk10.dim(" [skip] Scripts not found"));
@@ -2853,16 +3018,16 @@ function removeGlobal() {
2853
3018
  console.log(chalk10.bold.green("\n Global hooks removed.\n"));
2854
3019
  }
2855
3020
  function readSettings() {
2856
- if (!fs15.existsSync(SETTINGS_PATH)) return {};
3021
+ if (!fs16.existsSync(SETTINGS_PATH)) return {};
2857
3022
  try {
2858
- return JSON.parse(fs15.readFileSync(SETTINGS_PATH, "utf-8"));
3023
+ return JSON.parse(fs16.readFileSync(SETTINGS_PATH, "utf-8"));
2859
3024
  } catch {
2860
3025
  return {};
2861
3026
  }
2862
3027
  }
2863
3028
  function writeSettings(settings) {
2864
- fs15.mkdirSync(path18.dirname(SETTINGS_PATH), { recursive: true });
2865
- fs15.writeFileSync(
3029
+ fs16.mkdirSync(path19.dirname(SETTINGS_PATH), { recursive: true });
3030
+ fs16.writeFileSync(
2866
3031
  SETTINGS_PATH,
2867
3032
  JSON.stringify(settings, null, 2) + "\n",
2868
3033
  "utf-8"
@@ -2919,11 +3084,11 @@ function removeHookFromSettings() {
2919
3084
  }
2920
3085
 
2921
3086
  // src/update-check.ts
2922
- import fs16 from "fs";
2923
- import path19 from "path";
3087
+ import fs17 from "fs";
3088
+ import path20 from "path";
2924
3089
  import os6 from "os";
2925
3090
  import chalk11 from "chalk";
2926
- var CHECK_FILE = path19.join(os6.homedir(), ".arcane", "last-check.json");
3091
+ var CHECK_FILE = path20.join(os6.homedir(), ".arcane", "last-check.json");
2927
3092
  var CHECK_INTERVAL_MS = 4 * 60 * 60 * 1e3;
2928
3093
  var GITHUB_OWNER2 = "SebastianLuser";
2929
3094
  var GITHUB_REPO2 = "Claude-Code-Arcane";
@@ -3048,19 +3213,19 @@ async function getLatestNpmVersion() {
3048
3213
  }
3049
3214
  function readCachedCheck() {
3050
3215
  try {
3051
- if (!fs16.existsSync(CHECK_FILE)) return null;
3052
- return JSON.parse(fs16.readFileSync(CHECK_FILE, "utf-8"));
3216
+ if (!fs17.existsSync(CHECK_FILE)) return null;
3217
+ return JSON.parse(fs17.readFileSync(CHECK_FILE, "utf-8"));
3053
3218
  } catch {
3054
3219
  return null;
3055
3220
  }
3056
3221
  }
3057
3222
  function writeCachedCheck(result) {
3058
3223
  try {
3059
- const dir = path19.dirname(CHECK_FILE);
3060
- if (!fs16.existsSync(dir)) {
3061
- fs16.mkdirSync(dir, { recursive: true });
3224
+ const dir = path20.dirname(CHECK_FILE);
3225
+ if (!fs17.existsSync(dir)) {
3226
+ fs17.mkdirSync(dir, { recursive: true });
3062
3227
  }
3063
- fs16.writeFileSync(CHECK_FILE, JSON.stringify(result, null, 2) + "\n");
3228
+ fs17.writeFileSync(CHECK_FILE, JSON.stringify(result, null, 2) + "\n");
3064
3229
  } catch {
3065
3230
  }
3066
3231
  }