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/README.md +5 -5
- package/dist/assets/.arcane/governance/development-methodology.md +4 -1
- package/dist/assets/.arcane/governance/git-conventions.md +7 -0
- package/dist/assets/.arcane/governance/portable-bootstrap.md +1 -1
- package/dist/assets/.arcane/templates/venture-template/IDEAS.md +11 -0
- package/dist/assets/.arcane/templates/venture-template/TODO.md +8 -0
- package/dist/assets/.claude/commands/spell-manifest.md +9 -0
- package/dist/assets/.claude/commands/spell-summon-venture.md +9 -0
- package/dist/assets/.github/prompts/spell-check-drift.prompt.md +7 -0
- package/dist/assets/.github/prompts/spell-feedback.prompt.md +29 -5
- package/dist/assets/.github/prompts/spell-manifest.prompt.md +132 -0
- package/dist/assets/.github/prompts/spell-save-idea.prompt.md +36 -5
- package/dist/assets/.github/prompts/spell-summon-venture.prompt.md +109 -0
- package/dist/assets/.github/prompts/spell-todo.prompt.md +40 -4
- package/dist/index.js +283 -51
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/assets/.claude/commands/spell-bootstrap-business.md +0 -9
- package/dist/assets/.github/prompts/spell-bootstrap-business.prompt.md +0 -73
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
|
|
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 (
|
|
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-
|
|
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: [
|
|
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
|
|
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-
|
|
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
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
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
|
-
|
|
1603
|
-
{
|
|
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
|
|
1743
|
+
await runGit(cwd, ["rev-parse", "--verify", "HEAD"]);
|
|
1613
1744
|
} catch {
|
|
1614
1745
|
return { status: "no-commits" };
|
|
1615
1746
|
}
|
|
1616
|
-
const { stdout } = await
|
|
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
|
|
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 =
|
|
2017
|
-
const preserveExisting = component.skipExisting && await fileExists(
|
|
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 =
|
|
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
|
|
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
|
|
2062
|
-
import { dirname, join as
|
|
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 =
|
|
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
|
|
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
|
|
2207
|
-
import { confirm as
|
|
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
|
|
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 =
|
|
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(
|
|
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(
|
|
2496
|
+
await rm2(join9(targetDir, dir), { recursive: true, force: true });
|
|
2265
2497
|
}
|
|
2266
|
-
await rm2(
|
|
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
|
|
2513
|
+
import { join as join10, dirname as dirname2 } from "path";
|
|
2282
2514
|
import { execFile as execFile2 } from "child_process";
|
|
2283
|
-
import { promisify
|
|
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
|
|
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
|
-
|
|
2370
|
-
|
|
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:
|
|
2375
|
-
const entries = await
|
|
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
|
|
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 =
|
|
2636
|
+
const manifestPath2 = join10(targetDir, ".arcane.json");
|
|
2405
2637
|
try {
|
|
2406
2638
|
await access3(manifestPath2);
|
|
2407
2639
|
} catch {
|
|
2408
|
-
const selfHostedManifestPath =
|
|
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 =
|
|
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 =
|
|
2727
|
+
const destPath = join10(targetDir, file);
|
|
2496
2728
|
try {
|
|
2497
2729
|
await access3(destPath);
|
|
2498
2730
|
} catch {
|
|
2499
|
-
const srcPath =
|
|
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"] ??
|
|
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());
|