arcane-cli 0.15.9 → 0.16.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ const require = __createRequire(import.meta.url);
6
6
  import { Command } from "commander";
7
7
  import { createRequire as createRequire2 } from "module";
8
8
  import { fileURLToPath } from "url";
9
- import { dirname as dirname3, join as join10 } from "path";
9
+ import { dirname as dirname3, join as join11 } from "path";
10
10
 
11
11
  // src/commands/init.ts
12
12
  import { select as select2, confirm } from "@inquirer/prompts";
@@ -305,13 +305,13 @@ var COMPONENTS = [
305
305
  // Spell prompts — installed to .github/prompts/ (VS Code Copilot convention)
306
306
  {
307
307
  name: "spell-prompts",
308
- description: "Full Arcane spell prompt library (33 spells)",
308
+ description: "Full Arcane spell prompt library (34 spells)",
309
309
  files: [
310
310
  ".github/prompts/spell-arcane-version.prompt.md",
311
311
  ".github/prompts/spell-architect.prompt.md",
312
312
  ".github/prompts/spell-brainstorm.prompt.md",
313
313
  ".github/prompts/spell-scope.prompt.md",
314
- ".github/prompts/spell-bootstrap-business.prompt.md",
314
+ ".github/prompts/spell-summon-venture.prompt.md",
315
315
  ".github/prompts/spell-bug.prompt.md",
316
316
  ".github/prompts/spell-bump.prompt.md",
317
317
  ".github/prompts/spell-check-drift.prompt.md",
@@ -338,6 +338,7 @@ var COMPONENTS = [
338
338
  ".github/prompts/spell-create-pull-request.prompt.md",
339
339
  ".github/prompts/spell-document.prompt.md",
340
340
  ".github/prompts/spell-feedback.prompt.md",
341
+ ".github/prompts/spell-manifest.prompt.md",
341
342
  ".github/prompts/spell-save-idea.prompt.md",
342
343
  ".github/prompts/spell-status.prompt.md",
343
344
  ".github/prompts/spell-review-batch.prompt.md"
@@ -346,8 +347,12 @@ var COMPONENTS = [
346
347
  // Templates
347
348
  {
348
349
  name: "venture-template",
349
- description: "Starter template for a new business venture folder",
350
- files: [".arcane/templates/venture-template/overview.md"]
350
+ description: "Starter template for a new business venture folder \u2014 overview plus idea/todo books",
351
+ files: [
352
+ ".arcane/templates/venture-template/overview.md",
353
+ ".arcane/templates/venture-template/IDEAS.md",
354
+ ".arcane/templates/venture-template/TODO.md"
355
+ ]
351
356
  },
352
357
  // Session continuity — files required for spell-close-session / spell-open-session
353
358
  {
@@ -378,13 +383,13 @@ var COMPONENTS = [
378
383
  // Claude commands
379
384
  {
380
385
  name: "claude-commands",
381
- description: "Claude Code slash command wrappers for all 33 Arcane spells",
386
+ description: "Claude Code slash command wrappers for all 34 Arcane spells",
382
387
  files: [
383
388
  ".claude/commands/spell-arcane-version.md",
384
389
  ".claude/commands/spell-architect.md",
385
390
  ".claude/commands/spell-brainstorm.md",
386
391
  ".claude/commands/spell-scope.md",
387
- ".claude/commands/spell-bootstrap-business.md",
392
+ ".claude/commands/spell-summon-venture.md",
388
393
  ".claude/commands/spell-bug.md",
389
394
  ".claude/commands/spell-bump.md",
390
395
  ".claude/commands/spell-check-drift.md",
@@ -411,6 +416,7 @@ var COMPONENTS = [
411
416
  ".claude/commands/spell-create-pull-request.md",
412
417
  ".claude/commands/spell-document.md",
413
418
  ".claude/commands/spell-feedback.md",
419
+ ".claude/commands/spell-manifest.md",
414
420
  ".claude/commands/spell-save-idea.md",
415
421
  ".claude/commands/spell-status.md",
416
422
  ".claude/commands/spell-review-batch.md"
@@ -1593,15 +1599,140 @@ async function runAgentsList(targetDir) {
1593
1599
 
1594
1600
  // src/modules/git.ts
1595
1601
  import { execFile } from "child_process";
1596
- import { promisify } from "util";
1597
- var execFileAsync = promisify(execFile);
1598
- async function inspectGitRepository(cwd) {
1599
- try {
1600
- const { stdout: stdout2 } = await execFileAsync(
1602
+ var GitTimeoutError = class extends Error {
1603
+ constructor(message, options) {
1604
+ super(message, options);
1605
+ this.name = "GitTimeoutError";
1606
+ }
1607
+ };
1608
+ var NETWORK_SUBCOMMANDS = /* @__PURE__ */ new Set(["fetch", "pull", "push", "clone", "ls-remote", "send-email"]);
1609
+ var WRITE_SUBCOMMANDS = /* @__PURE__ */ new Set([
1610
+ "init",
1611
+ "commit",
1612
+ "add",
1613
+ "checkout",
1614
+ "switch",
1615
+ "merge",
1616
+ "rebase",
1617
+ "reset",
1618
+ "tag",
1619
+ "mv",
1620
+ "rm",
1621
+ "cherry-pick",
1622
+ "revert",
1623
+ "stash",
1624
+ "apply",
1625
+ "am",
1626
+ "restore",
1627
+ "submodule",
1628
+ "sparse-checkout",
1629
+ "maintenance",
1630
+ "notes"
1631
+ ]);
1632
+ var AMBIGUOUS_AS_WRITE = /* @__PURE__ */ new Set(["branch", "config", "remote", "worktree"]);
1633
+ var READ_SUBCOMMANDS = /* @__PURE__ */ new Set([
1634
+ "status",
1635
+ "rev-parse",
1636
+ "log",
1637
+ "diff",
1638
+ "show",
1639
+ "describe",
1640
+ "cat-file",
1641
+ "ls-files",
1642
+ "ls-tree",
1643
+ "symbolic-ref",
1644
+ "merge-base",
1645
+ "blame",
1646
+ "shortlog",
1647
+ "grep",
1648
+ "reflog",
1649
+ "diff-tree",
1650
+ "rev-list",
1651
+ "name-rev",
1652
+ "count-objects",
1653
+ "verify-commit",
1654
+ "verify-tag",
1655
+ "help",
1656
+ "version"
1657
+ ]);
1658
+ var GLOBAL_FLAGS_WITH_SEPARATE_VALUE = /* @__PURE__ */ new Set(["-c", "-C"]);
1659
+ var DEFAULT_TIMEOUTS_MS = {
1660
+ read: 15e3,
1661
+ write: 3e4,
1662
+ network: 12e4
1663
+ };
1664
+ function classifyGitCommand(args) {
1665
+ let subcommand;
1666
+ for (let i = 0; i < args.length; i++) {
1667
+ const arg = args[i];
1668
+ if (GLOBAL_FLAGS_WITH_SEPARATE_VALUE.has(arg)) {
1669
+ i++;
1670
+ continue;
1671
+ }
1672
+ if (arg.startsWith("-")) continue;
1673
+ subcommand = arg;
1674
+ break;
1675
+ }
1676
+ if (!subcommand) return "read";
1677
+ if (NETWORK_SUBCOMMANDS.has(subcommand)) return "network";
1678
+ if (WRITE_SUBCOMMANDS.has(subcommand) || AMBIGUOUS_AS_WRITE.has(subcommand)) return "write";
1679
+ if (READ_SUBCOMMANDS.has(subcommand)) return "read";
1680
+ return "write";
1681
+ }
1682
+ function buildGitEnv() {
1683
+ return {
1684
+ ...process.env,
1685
+ GIT_TERMINAL_PROMPT: "0",
1686
+ GCM_INTERACTIVE: "Never",
1687
+ GIT_OPTIONAL_LOCKS: "0"
1688
+ };
1689
+ }
1690
+ function runGit(cwd, args, options = {}) {
1691
+ const commandClass = options.commandClass ?? classifyGitCommand(args);
1692
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUTS_MS[commandClass] ?? DEFAULT_TIMEOUTS_MS.write;
1693
+ return new Promise((resolve, reject) => {
1694
+ const child = execFile(
1601
1695
  "git",
1602
- ["rev-parse", "--is-inside-work-tree"],
1603
- { cwd }
1696
+ args,
1697
+ {
1698
+ cwd,
1699
+ env: buildGitEnv(),
1700
+ timeout: timeoutMs,
1701
+ // SIGTERM (Node's default) rather than SIGKILL: gives git a chance
1702
+ // to run its own cleanup on POSIX, relevant to EF-20's own concern
1703
+ // about a killed operation leaving refs/locks in an uncertain state.
1704
+ // Moot on Windows -- Node docs: both map to an abrupt TerminateProcess
1705
+ // there -- but this module isn't Windows-gated.
1706
+ killSignal: "SIGTERM",
1707
+ windowsHide: true,
1708
+ encoding: "utf8"
1709
+ },
1710
+ (error, stdout, stderr) => {
1711
+ if (error) {
1712
+ if (error.killed) {
1713
+ reject(
1714
+ new GitTimeoutError(
1715
+ // NOTE: embeds the raw args, including any secrets a future
1716
+ // network-class caller might pass (e.g. a credentialed
1717
+ // clone URL). No current caller does; redact before one does.
1718
+ `git ${args.join(" ")} timed out after ${timeoutMs}ms (class: ${commandClass})`,
1719
+ { cause: error }
1720
+ )
1721
+ );
1722
+ return;
1723
+ }
1724
+ reject(error);
1725
+ return;
1726
+ }
1727
+ resolve({ stdout, stderr });
1728
+ }
1604
1729
  );
1730
+ child.stdin?.end();
1731
+ });
1732
+ }
1733
+ async function inspectGitRepository(cwd) {
1734
+ try {
1735
+ const { stdout: stdout2 } = await runGit(cwd, ["rev-parse", "--is-inside-work-tree"]);
1605
1736
  if (stdout2.trim() !== "true") {
1606
1737
  return { status: "not-repository" };
1607
1738
  }
@@ -1609,15 +1740,11 @@ async function inspectGitRepository(cwd) {
1609
1740
  return { status: "not-repository" };
1610
1741
  }
1611
1742
  try {
1612
- await execFileAsync("git", ["rev-parse", "--verify", "HEAD"], { cwd });
1743
+ await runGit(cwd, ["rev-parse", "--verify", "HEAD"]);
1613
1744
  } catch {
1614
1745
  return { status: "no-commits" };
1615
1746
  }
1616
- const { stdout } = await execFileAsync(
1617
- "git",
1618
- ["status", "--porcelain", "-uall"],
1619
- { cwd }
1620
- );
1747
+ const { stdout } = await runGit(cwd, ["status", "--porcelain", "-uall"]);
1621
1748
  const lines = stdout.trim();
1622
1749
  return {
1623
1750
  status: "ready",
@@ -1626,7 +1753,7 @@ async function inspectGitRepository(cwd) {
1626
1753
  }
1627
1754
  async function countUncommittedChanges(cwd) {
1628
1755
  try {
1629
- const { stdout } = await execFileAsync("git", ["status", "--porcelain", "-uall"], { cwd });
1756
+ const { stdout } = await runGit(cwd, ["status", "--porcelain", "-uall"]);
1630
1757
  const lines = stdout.trim();
1631
1758
  return lines.length === 0 ? 0 : lines.split("\n").length;
1632
1759
  } catch {
@@ -1824,11 +1951,21 @@ async function runInit(options, targetDir, assetsDir, packageVersion) {
1824
1951
  printDryRun(`Would initialize with profile "${profile}" \u2014 ${fileCount} files`);
1825
1952
  return;
1826
1953
  }
1954
+ let role;
1955
+ if (!options.profile) {
1956
+ console.log();
1957
+ const isHub = await confirm({
1958
+ message: "Will this repo manage other ventures as a hub? (idea books, spell-manifest promotion, venture registry)",
1959
+ default: false
1960
+ });
1961
+ role = isHub ? "hub" : "consumer";
1962
+ }
1827
1963
  const manifest = {
1828
1964
  version: packageVersion,
1829
1965
  profile,
1830
1966
  installedAt: (/* @__PURE__ */ new Date()).toISOString(),
1831
- components: installedComponents
1967
+ components: installedComponents,
1968
+ ...role ? { role } : {}
1832
1969
  };
1833
1970
  await writeManifest(targetDir, manifest);
1834
1971
  printSuccess(`Initialized with profile "${profile}" \u2014 ${fileCount} files installed`);
@@ -1946,7 +2083,87 @@ async function runAdd(name, options, targetDir, assetsDir, packageVersion) {
1946
2083
  }
1947
2084
 
1948
2085
  // src/commands/update.ts
2086
+ import { join as join7 } from "path";
2087
+
2088
+ // src/modules/hub.ts
2089
+ import { confirm as confirm2 } from "@inquirer/prompts";
2090
+ import { readdir as readdir3, writeFile as writeFile5 } from "fs/promises";
1949
2091
  import { join as join6 } from "path";
2092
+ var MANIFEST_RETROFITS = [
2093
+ {
2094
+ field: "role",
2095
+ needsRetrofit: (m) => m.role === void 0,
2096
+ ask: async () => {
2097
+ const isHub = await confirm2({
2098
+ message: "Will this repo manage other ventures as a hub? (idea books, spell-manifest promotion, venture registry)",
2099
+ default: false
2100
+ });
2101
+ return { role: isHub ? "hub" : "consumer" };
2102
+ }
2103
+ }
2104
+ ];
2105
+ async function runManifestRetrofits(manifest) {
2106
+ const applicable = MANIFEST_RETROFITS.filter((r) => r.needsRetrofit(manifest));
2107
+ if (applicable.length === 0) return {};
2108
+ console.log();
2109
+ console.log(
2110
+ `This install predates ${applicable.length} manifest field${applicable.length === 1 ? "" : "s"} -- a couple of quick questions:`
2111
+ );
2112
+ let patch = {};
2113
+ for (const retrofit of applicable) {
2114
+ const answer = await retrofit.ask();
2115
+ patch = { ...patch, ...answer };
2116
+ }
2117
+ return patch;
2118
+ }
2119
+ async function offerRegistryScaffold(targetDir, businessRoot) {
2120
+ const registryPath = join6(targetDir, businessRoot, "registry.json");
2121
+ if (await fileExists(registryPath)) return;
2122
+ const businessRootPath = join6(targetDir, businessRoot);
2123
+ let ventureDirs;
2124
+ try {
2125
+ const entries = await readdir3(businessRootPath, { withFileTypes: true });
2126
+ ventureDirs = entries.filter((e) => e.isDirectory() && e.name !== "_template").map((e) => e.name);
2127
+ } catch {
2128
+ return;
2129
+ }
2130
+ if (ventureDirs.length === 0) return;
2131
+ console.log();
2132
+ const scaffold = await confirm2({
2133
+ message: `Found ${ventureDirs.length} folder${ventureDirs.length === 1 ? "" : "s"} under ${businessRoot}/ -- scaffold ${businessRoot}/registry.json from them?`,
2134
+ default: true
2135
+ });
2136
+ if (!scaffold) return;
2137
+ const ventures = {};
2138
+ for (const slug of ventureDirs) {
2139
+ const include = await confirm2({ message: ` Include "${slug}"?`, default: true });
2140
+ if (!include) continue;
2141
+ ventures[slug] = {
2142
+ name: slug,
2143
+ aliases: [],
2144
+ status: "active",
2145
+ visibility: "private",
2146
+ ownership: "llc",
2147
+ tracking: "none",
2148
+ repos: []
2149
+ };
2150
+ }
2151
+ if (Object.keys(ventures).length === 0) {
2152
+ console.log(" No ventures selected -- registry not created.");
2153
+ return;
2154
+ }
2155
+ const registry = {
2156
+ _comment: "Hub-owned venture registry. Private. Never installed or modified by spell update. Seeded by the retrofit wizard -- review and enrich (aliases, ownership, tracking, clones) before relying on it.",
2157
+ updated: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
2158
+ ventures
2159
+ };
2160
+ await writeFile5(registryPath, JSON.stringify(registry, null, 2) + "\n", "utf-8");
2161
+ console.log(
2162
+ ` \u2713 Scaffolded ${businessRoot}/registry.json with ${Object.keys(ventures).length} venture(s).`
2163
+ );
2164
+ }
2165
+
2166
+ // src/commands/update.ts
1950
2167
  async function runUpdate(options, targetDir, assetsDir, packageVersion) {
1951
2168
  let manifest;
1952
2169
  try {
@@ -2013,8 +2230,8 @@ async function runUpdate(options, targetDir, assetsDir, packageVersion) {
2013
2230
  }
2014
2231
  const updatedFiles = [];
2015
2232
  for (const file of component.files) {
2016
- const srcPath = join6(assetsDir, file);
2017
- const preserveExisting = component.skipExisting && await fileExists(join6(targetDir, file));
2233
+ const srcPath = join7(assetsDir, file);
2234
+ const preserveExisting = component.skipExisting && await fileExists(join7(targetDir, file));
2018
2235
  if (preserveExisting) {
2019
2236
  console.log(` ${options.dryRun ? "[dry-run] Would preserve" : "Preserved"}: ${file}`);
2020
2237
  } else if (options.dryRun) {
@@ -2027,7 +2244,7 @@ async function runUpdate(options, targetDir, assetsDir, packageVersion) {
2027
2244
  updatedFiles.push(file);
2028
2245
  }
2029
2246
  for (const dir of component.directories ?? []) {
2030
- const srcDirPath = join6(assetsDir, dir);
2247
+ const srcDirPath = join7(assetsDir, dir);
2031
2248
  if (options.dryRun) {
2032
2249
  console.log(` [dry-run] Would update directory: ${dir}/`);
2033
2250
  } else {
@@ -2047,19 +2264,34 @@ async function runUpdate(options, targetDir, assetsDir, packageVersion) {
2047
2264
  `
2048
2265
  [dry-run] Would update ${fileCount} files.`
2049
2266
  );
2267
+ const applicableRetrofits = MANIFEST_RETROFITS.filter((r) => r.needsRetrofit(manifest));
2268
+ if (applicableRetrofits.length > 0) {
2269
+ console.log(
2270
+ `[dry-run] Would ask ${applicableRetrofits.length} manifest retrofit question${applicableRetrofits.length === 1 ? "" : "s"}: ${applicableRetrofits.map((r) => r.field).join(", ")}.`
2271
+ );
2272
+ }
2050
2273
  return;
2051
2274
  }
2052
- const updated = { ...manifest, version: packageVersion, components: updatedComponents };
2275
+ const retrofitPatch = await runManifestRetrofits(manifest);
2276
+ const updated = {
2277
+ ...manifest,
2278
+ version: packageVersion,
2279
+ components: updatedComponents,
2280
+ ...retrofitPatch
2281
+ };
2053
2282
  await writeManifest(targetDir, updated);
2054
2283
  console.log(
2055
2284
  `
2056
2285
  \u2713 Updated ${fileCount} files.`
2057
2286
  );
2287
+ if (manifest.role !== "hub" && updated.role === "hub") {
2288
+ await offerRegistryScaffold(targetDir, updated.business_root ?? "ventures");
2289
+ }
2058
2290
  }
2059
2291
 
2060
2292
  // src/modules/version-check.ts
2061
- import { readFile as readFile4, writeFile as writeFile5, mkdir as mkdir4 } from "fs/promises";
2062
- import { dirname, join as join7 } from "path";
2293
+ import { readFile as readFile4, writeFile as writeFile6, mkdir as mkdir4 } from "fs/promises";
2294
+ import { dirname, join as join8 } from "path";
2063
2295
  import { homedir as homedir2 } from "os";
2064
2296
  import { createRequire } from "module";
2065
2297
  var require2 = createRequire(import.meta.url);
@@ -2073,7 +2305,7 @@ function getFeedUrl() {
2073
2305
  }
2074
2306
  return url;
2075
2307
  }
2076
- var DEFAULT_CACHE_PATH = join7(homedir2(), ".arcane", "version-cache.json");
2308
+ var DEFAULT_CACHE_PATH = join8(homedir2(), ".arcane", "version-cache.json");
2077
2309
  var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
2078
2310
  async function readCache(cachePath) {
2079
2311
  try {
@@ -2090,7 +2322,7 @@ async function readCache(cachePath) {
2090
2322
  async function writeCache(cachePath, latest) {
2091
2323
  await mkdir4(dirname(cachePath), { recursive: true });
2092
2324
  const entry = { latest, checkedAt: (/* @__PURE__ */ new Date()).toISOString() };
2093
- await writeFile5(cachePath, JSON.stringify(entry, null, 2), "utf-8");
2325
+ await writeFile6(cachePath, JSON.stringify(entry, null, 2), "utf-8");
2094
2326
  }
2095
2327
  async function getLatestVersion(feedUrl) {
2096
2328
  const normalizedBase = feedUrl.endsWith("/") ? feedUrl : feedUrl + "/";
@@ -2203,8 +2435,8 @@ async function runStatus(targetDir, packageVersion) {
2203
2435
 
2204
2436
  // src/commands/uninstall.ts
2205
2437
  import { rm as rm2 } from "fs/promises";
2206
- import { join as join8 } from "path";
2207
- import { confirm as confirm2 } from "@inquirer/prompts";
2438
+ import { join as join9 } from "path";
2439
+ import { confirm as confirm3 } from "@inquirer/prompts";
2208
2440
  async function runUninstall(options, targetDir) {
2209
2441
  let manifest;
2210
2442
  try {
@@ -2234,7 +2466,7 @@ async function runUninstall(options, targetDir) {
2234
2466
  return;
2235
2467
  }
2236
2468
  if (!options.yes) {
2237
- const confirmed = await confirm2({
2469
+ const confirmed = await confirm3({
2238
2470
  message: `This will remove ${totalFiles} files and .arcane.json. Continue?`,
2239
2471
  default: false
2240
2472
  });
@@ -2246,7 +2478,7 @@ async function runUninstall(options, targetDir) {
2246
2478
  let removed = 0;
2247
2479
  for (const component of manifest.components) {
2248
2480
  for (const file of component.files) {
2249
- const filePath = join8(targetDir, file);
2481
+ const filePath = join9(targetDir, file);
2250
2482
  try {
2251
2483
  await rm2(filePath, { force: true });
2252
2484
  removed++;
@@ -2254,16 +2486,16 @@ async function runUninstall(options, targetDir) {
2254
2486
  }
2255
2487
  }
2256
2488
  }
2257
- await rm2(join8(targetDir, ".arcane.json"), { force: true });
2489
+ await rm2(join9(targetDir, ".arcane.json"), { force: true });
2258
2490
  const agentDirs = [
2259
2491
  ".github/agents",
2260
2492
  ".arcane/agents",
2261
2493
  ".arcane/generated"
2262
2494
  ];
2263
2495
  for (const dir of agentDirs) {
2264
- await rm2(join8(targetDir, dir), { recursive: true, force: true });
2496
+ await rm2(join9(targetDir, dir), { recursive: true, force: true });
2265
2497
  }
2266
- await rm2(join8(targetDir, ".arcane/agents.yaml"), { force: true });
2498
+ await rm2(join9(targetDir, ".arcane/agents.yaml"), { force: true });
2267
2499
  const markerFiles = [
2268
2500
  "CLAUDE.md",
2269
2501
  "AGENTS.md",
@@ -2278,9 +2510,9 @@ async function runUninstall(options, targetDir) {
2278
2510
 
2279
2511
  // src/commands/doctor.ts
2280
2512
  import { access as access3, readFile as readFile5, mkdir as mkdir5, copyFile as fsCopyFile2 } from "fs/promises";
2281
- import { join as join9, dirname as dirname2 } from "path";
2513
+ import { join as join10, dirname as dirname2 } from "path";
2282
2514
  import { execFile as execFile2 } from "child_process";
2283
- import { promisify as promisify2 } from "util";
2515
+ import { promisify } from "util";
2284
2516
 
2285
2517
  // src/config/incidents.ts
2286
2518
  var INCIDENT_QUEUE = [
@@ -2348,7 +2580,7 @@ function evaluateIncidentGate(records) {
2348
2580
  }
2349
2581
 
2350
2582
  // src/commands/doctor.ts
2351
- var execFileAsync2 = promisify2(execFile2);
2583
+ var execFileAsync = promisify(execFile2);
2352
2584
  async function checkNodeVersion() {
2353
2585
  const name = "Node.js version";
2354
2586
  const raw = process.version;
@@ -2366,13 +2598,13 @@ async function checkVSCodeExtension(id, label) {
2366
2598
  const name = `VS Code extension: ${label}`;
2367
2599
  const home = process.env["HOME"] ?? "";
2368
2600
  const extDirs = [
2369
- join9(home, ".vscode", "extensions"),
2370
- join9(home, ".vscode-insiders", "extensions")
2601
+ join10(home, ".vscode", "extensions"),
2602
+ join10(home, ".vscode-insiders", "extensions")
2371
2603
  ];
2372
2604
  for (const dir of extDirs) {
2373
2605
  try {
2374
- const { readdir: readdir3 } = await import("fs/promises");
2375
- const entries = await readdir3(dir);
2606
+ const { readdir: readdir4 } = await import("fs/promises");
2607
+ const entries = await readdir4(dir);
2376
2608
  const prefix = id.toLowerCase() + "-";
2377
2609
  const found = entries.some(
2378
2610
  (e) => e.toLowerCase() === id.toLowerCase() || e.toLowerCase().startsWith(prefix)
@@ -2386,7 +2618,7 @@ async function checkVSCodeExtension(id, label) {
2386
2618
  let installBin = "code";
2387
2619
  for (const bin of ["code", "code-insiders"]) {
2388
2620
  try {
2389
- await execFileAsync2(bin, ["--version"]);
2621
+ await execFileAsync(bin, ["--version"]);
2390
2622
  installBin = bin;
2391
2623
  break;
2392
2624
  } catch {
@@ -2401,11 +2633,11 @@ async function checkVSCodeExtension(id, label) {
2401
2633
  }
2402
2634
  async function checkArcaneManifest(targetDir) {
2403
2635
  const name = "Arcane manifest (.arcane.json)";
2404
- const manifestPath2 = join9(targetDir, ".arcane.json");
2636
+ const manifestPath2 = join10(targetDir, ".arcane.json");
2405
2637
  try {
2406
2638
  await access3(manifestPath2);
2407
2639
  } catch {
2408
- const selfHostedManifestPath = join9(targetDir, "src", "assets", ".arcane.json");
2640
+ const selfHostedManifestPath = join10(targetDir, "src", "assets", ".arcane.json");
2409
2641
  try {
2410
2642
  const raw = await readFile5(selfHostedManifestPath, "utf8");
2411
2643
  const manifest = JSON.parse(raw);
@@ -2474,7 +2706,7 @@ async function checkSessionContinuity(targetDir) {
2474
2706
  const results = [];
2475
2707
  for (const file of SESSION_CONTINUITY_FILES) {
2476
2708
  const name = `Session file: ${file}`;
2477
- const filePath = join9(targetDir, file);
2709
+ const filePath = join10(targetDir, file);
2478
2710
  try {
2479
2711
  await access3(filePath);
2480
2712
  results.push({ name, passed: true, message: "present" });
@@ -2492,11 +2724,11 @@ async function checkSessionContinuity(targetDir) {
2492
2724
  async function fixSessionContinuity(targetDir, assetsDir) {
2493
2725
  const created = [];
2494
2726
  for (const file of SESSION_CONTINUITY_FILES) {
2495
- const destPath = join9(targetDir, file);
2727
+ const destPath = join10(targetDir, file);
2496
2728
  try {
2497
2729
  await access3(destPath);
2498
2730
  } catch {
2499
- const srcPath = join9(assetsDir, file);
2731
+ const srcPath = join10(assetsDir, file);
2500
2732
  await mkdir5(dirname2(destPath), { recursive: true });
2501
2733
  await fsCopyFile2(srcPath, destPath);
2502
2734
  created.push(file);
@@ -2558,7 +2790,7 @@ async function runDoctor(targetDir, options = {}, assetsDir) {
2558
2790
  import { realpathSync } from "fs";
2559
2791
  var require3 = createRequire2(import.meta.url);
2560
2792
  var pkg = require3("../package.json");
2561
- var ASSETS_DIR = process.env["ARCANE_ASSETS_DIR"] ?? join10(dirname3(fileURLToPath(import.meta.url)), "assets");
2793
+ var ASSETS_DIR = process.env["ARCANE_ASSETS_DIR"] ?? join11(dirname3(fileURLToPath(import.meta.url)), "assets");
2562
2794
  var program = new Command();
2563
2795
  program.name("spell").description("Arcane framework CLI \u2014 scaffold and manage governance files").version(pkg.version).action(async () => {
2564
2796
  await printWelcome(pkg.version, process.cwd());