arcane-cli 1.3.1 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -1
- package/dist/assets/.arcane/governance/portable-bootstrap.md +1 -1
- package/dist/index.js +108 -15
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -237,12 +237,16 @@ Codex, VS Code Copilot and Claude Code each discover the user tier from their ow
|
|
|
237
237
|
|
|
238
238
|
```bash
|
|
239
239
|
spell agents init --user # ~/.arcane/agents.yaml plus one definition file per role
|
|
240
|
-
spell agents sync --user # renders ~/.
|
|
240
|
+
spell agents sync --user # renders ~/.claude/agents/<name>.md — read by VS Code and Claude Code
|
|
241
241
|
spell agents list --user # the roster the tier holds
|
|
242
242
|
```
|
|
243
243
|
|
|
244
244
|
Unlike spells, agent files are never delivered by `spell init` or `spell update`: they are rendered from your roster, whose naming strategy decides each file's name and contents, so an upgrade leaves them alone by design (ARC-047). And unlike skills, VS Code does **not** collapse two agents that share a name — a repository carrying its own `.github/agents` files adds a second entry beside the tier's rather than replacing it, so opting the repository out is what produces one set.
|
|
245
245
|
|
|
246
|
+
`~/.claude/agents` is read by VS Code **and** by Claude Code, so one file gives you the persona in both. Each description ends by saying the persona is for when you ask for it by name: Claude Code reads that field to decide whether to route work to a subagent unprompted, and twelve personas installed once per machine would otherwise start answering in projects that have nothing to do with Arcane. Change that sentence in `renderUserTierAgent` if you want the opposite.
|
|
247
|
+
|
|
248
|
+
A repository that opts in and has no roster of its own now takes the machine-wide one for its roster tables, rather than refusing.
|
|
249
|
+
|
|
246
250
|
**Then let a repository stop carrying its own copies.** `spell init` offers this when the machine already has a tier, and an existing repository opts in by setting one field:
|
|
247
251
|
|
|
248
252
|
```jsonc
|
|
@@ -259,6 +263,16 @@ spell doctor # fails loudly if the user tier it now depends on is missi
|
|
|
259
263
|
|
|
260
264
|
This is what removes the duplication: with the repository's own copies gone, one workspace holding ten Arcane projects shows one set of `/spell-*` entries, not ten. It is also what makes the answer predictable — while both tiers own a command name, which copy a client runs is not something you can read off the docs.
|
|
261
265
|
|
|
266
|
+
**Tired of answering that question in every new repository?** Tell the machine once:
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
spell update --user --default-scope user # or repo, to go back
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
That pre-selects the answer `spell init` offers in **new** repositories on this machine. It rewrites nothing that already exists, and the question is still asked, so a repository you want to keep self-contained is one keystroke away. An absent `spell_scope` means `repo` permanently, on every machine ([ARC-048](DECISIONS.md#arc-048--an-absent-spell_scope-means-repo-permanently-new-repositories-may-be-pre-selected)) — a preference decides what you are *offered*, never what an existing repository *means*.
|
|
273
|
+
|
|
274
|
+
If a repository that opted in ever lands on a machine with no tier, `spell update` there now says so, names the store path, and gives you both ways out.
|
|
275
|
+
|
|
262
276
|
**Shared, team and cloud repositories should stay `repo`.** The field is committed and inherited by every clone, so opting in tells *everyone* who clones that repository to get their spells from a machine-wide install they may not have. Reverse it by setting the field back and running `spell update`, which reinstalls every spell file.
|
|
263
277
|
|
|
264
278
|
---
|
|
@@ -52,7 +52,7 @@ Paste this entire file into any AI client (Claude web, ChatGPT, etc.) to give it
|
|
|
52
52
|
## Where Documents Live
|
|
53
53
|
|
|
54
54
|
- **Framework-managed standards:** `.arcane/governance/`. Spells reference this single installed layer; **do not create a duplicate root `governance/` tree. Enforcement: explicitly advisory prose (ARC-023) — no check scans a repo for a duplicate root-level `governance/` directory.**
|
|
55
|
-
- **Framework-managed spells:** `.arcane/spells/<id>.md` is each spell's one authored source (ARC-045). The files each AI client discovers — `.github/prompts/<id>.prompt.md` for Copilot, `.claude/commands/<id>.md` for Claude Code, `.agents/skills/<id>/SKILL.md` for Codex — are thin generated shims that point back at it; edit the canonical file, never a shim. **Enforcement: executable check (ARC-023) — `npm run check:self-host-parity` re-renders every shim from its canonical file and fails on any difference; `spell update` keeps a hand-edited shim untouched and names it rather than merging into it.** The same canonical spells can also be installed once per machine — `spell init --user` puts them at `~/.arcane/spells/<id>.md` with one Codex/Copilot skill under `~/.agents/skills/` and one Claude Code command under `~/.claude/commands/` per spell (ARC-045 decision 3); governance never moves up a tier. The agent roster follows the same shape (ARC-047): `spell agents init|sync --user` keeps `agents.yaml` and the role definitions at `~/.arcane` and renders one `~/.
|
|
55
|
+
- **Framework-managed spells:** `.arcane/spells/<id>.md` is each spell's one authored source (ARC-045). The files each AI client discovers — `.github/prompts/<id>.prompt.md` for Copilot, `.claude/commands/<id>.md` for Claude Code, `.agents/skills/<id>/SKILL.md` for Codex — are thin generated shims that point back at it; edit the canonical file, never a shim. **Enforcement: executable check (ARC-023) — `npm run check:self-host-parity` re-renders every shim from its canonical file and fails on any difference; `spell update` keeps a hand-edited shim untouched and names it rather than merging into it.** The same canonical spells can also be installed once per machine — `spell init --user` puts them at `~/.arcane/spells/<id>.md` with one Codex/Copilot skill under `~/.agents/skills/` and one Claude Code command under `~/.claude/commands/` per spell (ARC-045 decision 3); governance never moves up a tier. The agent roster follows the same shape (ARC-047): `spell agents init|sync --user` keeps `agents.yaml` and the role definitions at `~/.arcane` and renders one `~/.claude/agents/<name>.md` per agent -- the one home location read by both VS Code and Claude Code, so a persona shows up once in each rather than twice in one (ARC-047 decisions 3 and 11). Agent files are never installed by `spell init`/`spell update` at either tier -- they are rendered from the roster, whose naming strategy picks each file's name, which is why an upgrade never touches them. An absent `spell_scope` means `repo` permanently (ARC-048); a machine may set `default_spell_scope` in its own store to pre-select the answer `spell init` offers in NEW repositories, which never rewrites an existing one and never suppresses the question. A repository may then opt out of carrying its own copies entirely with `spell_scope: "user"` in `.arcane.json` (ARC-045 decision 4) — `spell update --prune` removes what it no longer manages, keeping anything you edited, and `spell doctor` fails loudly if the tier it now depends on is missing.
|
|
56
56
|
- **Project-owned orientation and continuity:** `README.md`, `project.md`, `TODO.md`, `DECISIONS.md`, `ai-context/`, and `journal/`. Arcane creates missing files once and preserves existing content.
|
|
57
57
|
- **Project/domain documents:** use explicit descriptive paths such as `docs/`, `security/`, `infrastructure/`, or a configured business root. These add project context alongside framework standards.
|
|
58
58
|
- **Overrides survive `arcane update`:** editing a managed `.arcane/governance/` standard no longer risks silent loss. Additive project documents were always safe; a hand-edit to a shipped standard now is too. **Enforcement: executable check (ARC-038, shipped BC-31)** — `spell update` compares each file's on-disk content against its recorded hash at install time: unchanged, it refreshes normally; edited, it fetches the exact previously-installed published version as a three-way merge base against your edit and the new vendor content, auto-merging non-overlapping changes and leaving standard conflict markers only on genuine collisions. If the prior published version can't be fetched, your edit is left untouched with a warning rather than replaced.
|
package/dist/index.js
CHANGED
|
@@ -1025,6 +1025,9 @@ function componentForSpellScope(component, spellScope) {
|
|
|
1025
1025
|
function effectiveSpellScope(manifest) {
|
|
1026
1026
|
return manifest.spell_scope ?? "repo";
|
|
1027
1027
|
}
|
|
1028
|
+
function preferredSpellScope(manifest) {
|
|
1029
|
+
return manifest?.default_spell_scope === "user" ? "user" : "repo";
|
|
1030
|
+
}
|
|
1028
1031
|
function spellIdsInStore(components) {
|
|
1029
1032
|
const ids = /* @__PURE__ */ new Set();
|
|
1030
1033
|
for (const component of components) {
|
|
@@ -1035,25 +1038,27 @@ function spellIdsInStore(components) {
|
|
|
1035
1038
|
}
|
|
1036
1039
|
return [...ids].sort();
|
|
1037
1040
|
}
|
|
1038
|
-
var USER_AGENT_FANOUT_DIR = ".
|
|
1041
|
+
var USER_AGENT_FANOUT_DIR = ".claude/agents";
|
|
1042
|
+
var LEGACY_USER_AGENT_FANOUT_DIR = ".copilot/agents";
|
|
1039
1043
|
var SPELL_FANOUT_CLIENTS = ["codex", "claude"];
|
|
1040
1044
|
var USER_FANOUT_PATHS = {
|
|
1041
1045
|
codex: (id) => `.agents/skills/${id}/SKILL.md`,
|
|
1042
1046
|
claude: (id) => `.claude/commands/${id}.md`
|
|
1043
1047
|
};
|
|
1044
1048
|
function userAgentFanoutPath(slug) {
|
|
1045
|
-
return `${USER_AGENT_FANOUT_DIR}/${slug}.
|
|
1049
|
+
return `${USER_AGENT_FANOUT_DIR}/${slug}.md`;
|
|
1046
1050
|
}
|
|
1047
1051
|
var FANOUT_CLIENT_LABELS = {
|
|
1048
1052
|
codex: "Codex/Copilot skills",
|
|
1049
1053
|
claude: "Claude Code commands",
|
|
1050
|
-
"copilot-agents": "
|
|
1054
|
+
"copilot-agents": "agent personas (VS Code + Claude Code)"
|
|
1051
1055
|
};
|
|
1052
1056
|
function clientOfFanoutPath(relativePath) {
|
|
1053
1057
|
const normalized = relativePath.replace(/\\/g, "/");
|
|
1054
1058
|
if (normalized.startsWith(".agents/skills/")) return "codex";
|
|
1055
1059
|
if (normalized.startsWith(".claude/commands/")) return "claude";
|
|
1056
1060
|
if (normalized.startsWith(`${USER_AGENT_FANOUT_DIR}/`)) return "copilot-agents";
|
|
1061
|
+
if (normalized.startsWith(`${LEGACY_USER_AGENT_FANOUT_DIR}/`)) return "copilot-agents";
|
|
1057
1062
|
return void 0;
|
|
1058
1063
|
}
|
|
1059
1064
|
async function readSpellSource(storeRoot, id, fallbackDir) {
|
|
@@ -2050,6 +2055,16 @@ ${allowed}
|
|
|
2050
2055
|
${denied}
|
|
2051
2056
|
`;
|
|
2052
2057
|
}
|
|
2058
|
+
function renderUserTierAgent(def, name) {
|
|
2059
|
+
const collapsed = def.persona.description.replace(/\s+/g, " ").trim();
|
|
2060
|
+
const firstSentence = /^.*?[.!?](?=\s|$)/.exec(collapsed)?.[0] ?? def.role;
|
|
2061
|
+
const repoFile = renderCopilotAgent(def, name);
|
|
2062
|
+
const body = repoFile.slice(repoFile.indexOf("---", 3) + 3);
|
|
2063
|
+
return `---
|
|
2064
|
+
name: ${name}
|
|
2065
|
+
description: ${def.role}. ${firstSentence} Use only when explicitly asked for ${name} by name; do not delegate to this persona automatically.
|
|
2066
|
+
---${body}`;
|
|
2067
|
+
}
|
|
2053
2068
|
function renderCopilotAgent(def, name) {
|
|
2054
2069
|
const firstLine = def.persona.description.split("\n")[0]?.trim() ?? def.role;
|
|
2055
2070
|
const rules = def.persona.behavioral_rules.map((r) => `- ${r}`).join("\n");
|
|
@@ -2130,7 +2145,7 @@ async function syncAgents(targetDir, assetsDir, roster, options = {}) {
|
|
|
2130
2145
|
const repoOptedOut = !userTier && options.spellScope === "user";
|
|
2131
2146
|
const userAgentFiles = [];
|
|
2132
2147
|
const unmanagedAgentFiles = [];
|
|
2133
|
-
const projectDefs = projectAgentsDir(targetDir, userTier ? "user" : "repo");
|
|
2148
|
+
const projectDefs = options.definitionsDir ?? projectAgentsDir(targetDir, userTier ? "user" : "repo");
|
|
2134
2149
|
const bundledDefs = join3(assetsDir, "agents");
|
|
2135
2150
|
const resolved = [];
|
|
2136
2151
|
for (const entry of roster.roster) {
|
|
@@ -2224,7 +2239,7 @@ async function syncAgents(targetDir, assetsDir, roster, options = {}) {
|
|
|
2224
2239
|
if (!def.clients.copilot?.agent_file) continue;
|
|
2225
2240
|
const slug = displayName.toLowerCase().replace(/\s+/g, "-");
|
|
2226
2241
|
const fileName = `${slug}.agent.md`;
|
|
2227
|
-
const content = renderCopilotAgent(def, displayName);
|
|
2242
|
+
const content = userTier ? renderUserTierAgent(def, displayName) : renderCopilotAgent(def, displayName);
|
|
2228
2243
|
if (userTier) {
|
|
2229
2244
|
const relativePath = userAgentFanoutPath(slug);
|
|
2230
2245
|
userAgentFiles.push({ relativePath, client: "copilot-agents", content });
|
|
@@ -2301,6 +2316,24 @@ async function requireUserStore(storeRoot) {
|
|
|
2301
2316
|
throw new Error("unreachable");
|
|
2302
2317
|
}
|
|
2303
2318
|
}
|
|
2319
|
+
async function resolveRoster(targetDir, scope, homeDir) {
|
|
2320
|
+
try {
|
|
2321
|
+
return {
|
|
2322
|
+
roster: await loadRoster(targetDir, scope),
|
|
2323
|
+
definitionsDir: projectAgentsDir(targetDir, scope),
|
|
2324
|
+
fromUserTier: scope === "user"
|
|
2325
|
+
};
|
|
2326
|
+
} catch (err) {
|
|
2327
|
+
const missing = err instanceof AgentRosterNotFoundError;
|
|
2328
|
+
if (!missing || scope !== "repo" || await repoSpellScope(targetDir) !== "user") throw err;
|
|
2329
|
+
const storeRoot = homeDir === void 0 ? userTierRoot() : userTierRoot(homeDir);
|
|
2330
|
+
return {
|
|
2331
|
+
roster: await loadRoster(storeRoot, "user"),
|
|
2332
|
+
definitionsDir: projectAgentsDir(storeRoot, "user"),
|
|
2333
|
+
fromUserTier: true
|
|
2334
|
+
};
|
|
2335
|
+
}
|
|
2336
|
+
}
|
|
2304
2337
|
async function repoSpellScope(targetDir) {
|
|
2305
2338
|
try {
|
|
2306
2339
|
return effectiveSpellScope(await readManifest(targetDir));
|
|
@@ -2487,7 +2520,7 @@ All 12 roles across ${allCategories.join(", ")}` : "\nPick individual roles from
|
|
|
2487
2520
|
printSuccess("Agent roster initialized");
|
|
2488
2521
|
console.log(` \u{1F4CB} Profile: ${profileId} \xB7 Naming: ${namingStrategy} \xB7 Agents: ${rosterEntries.length}`);
|
|
2489
2522
|
console.log(
|
|
2490
|
-
` \u{1F504} Synced: ${synced.length} outputs (${scope === "user" ? "
|
|
2523
|
+
` \u{1F504} Synced: ${synced.length} outputs (${scope === "user" ? "agent personas in ~/.claude/agents" : "Copilot, Claude, Codex, OpenClaw"})`
|
|
2491
2524
|
);
|
|
2492
2525
|
if (skipped.length > 0) {
|
|
2493
2526
|
console.log(` \u26A0\uFE0F Skipped: ${skipped.length} (${skipped.join(", ")})`);
|
|
@@ -2507,8 +2540,10 @@ All 12 roles across ${allCategories.join(", ")}` : "\nPick individual roles from
|
|
|
2507
2540
|
async function runAgentsSync(options, targetDir, assetsDir) {
|
|
2508
2541
|
const scope = options.scope ?? "repo";
|
|
2509
2542
|
let roster;
|
|
2543
|
+
let definitionsDir;
|
|
2544
|
+
let fromUserTier = false;
|
|
2510
2545
|
try {
|
|
2511
|
-
roster = await
|
|
2546
|
+
({ roster, definitionsDir, fromUserTier } = await resolveRoster(targetDir, scope));
|
|
2512
2547
|
} catch (err) {
|
|
2513
2548
|
if (err instanceof AgentRosterNotFoundError || err instanceof AgentConfigValidationError) {
|
|
2514
2549
|
console.error(err.message);
|
|
@@ -2522,11 +2557,16 @@ async function runAgentsSync(options, targetDir, assetsDir) {
|
|
|
2522
2557
|
} else {
|
|
2523
2558
|
console.log("\n Syncing agents...\n");
|
|
2524
2559
|
}
|
|
2560
|
+
if (fromUserTier && scope === "repo") {
|
|
2561
|
+
console.log(
|
|
2562
|
+
" This repository has no roster of its own and takes its spells from the user tier,\n so its roster tables are built from ~/.arcane/agents.yaml.\n"
|
|
2563
|
+
);
|
|
2564
|
+
}
|
|
2525
2565
|
const { synced, skipped, hasUnresolvedRoles } = await performAgentSync(
|
|
2526
2566
|
targetDir,
|
|
2527
2567
|
assetsDir,
|
|
2528
2568
|
roster,
|
|
2529
|
-
{ ...options, scope }
|
|
2569
|
+
{ ...options, scope, ...definitionsDir === void 0 ? {} : { definitionsDir } }
|
|
2530
2570
|
);
|
|
2531
2571
|
if (options.dryRun) {
|
|
2532
2572
|
console.log(`
|
|
@@ -3432,15 +3472,18 @@ async function runInit(options, targetDir, assetsDir, packageVersion) {
|
|
|
3432
3472
|
let spell_scope;
|
|
3433
3473
|
if (!options.profile && !options.dryRun) {
|
|
3434
3474
|
let storeVersion;
|
|
3475
|
+
let preferred = "repo";
|
|
3435
3476
|
try {
|
|
3436
|
-
|
|
3477
|
+
const store = await readManifest(userTierRoot());
|
|
3478
|
+
storeVersion = store.version;
|
|
3479
|
+
preferred = preferredSpellScope(store);
|
|
3437
3480
|
} catch {
|
|
3438
3481
|
}
|
|
3439
3482
|
if (storeVersion !== void 0) {
|
|
3440
3483
|
console.log();
|
|
3441
3484
|
const useTier = await confirm({
|
|
3442
3485
|
message: `This machine has an Arcane user tier (v${storeVersion}). Use it for this repository's spells instead of installing copies here?`,
|
|
3443
|
-
default:
|
|
3486
|
+
default: preferred === "user"
|
|
3444
3487
|
});
|
|
3445
3488
|
spell_scope = useTier ? "user" : "repo";
|
|
3446
3489
|
}
|
|
@@ -4282,6 +4325,31 @@ async function fetchPublishedFile(version, relativeAssetPath) {
|
|
|
4282
4325
|
}
|
|
4283
4326
|
|
|
4284
4327
|
// src/commands/update.ts
|
|
4328
|
+
async function missingTierWarning(scope, spellScope, homeDir) {
|
|
4329
|
+
if (scope !== "repo" || spellScope !== "user") return [];
|
|
4330
|
+
const storeRoot = homeDir === void 0 ? userTierRoot() : userTierRoot(homeDir);
|
|
4331
|
+
try {
|
|
4332
|
+
await readManifest(storeRoot);
|
|
4333
|
+
return [];
|
|
4334
|
+
} catch {
|
|
4335
|
+
return [
|
|
4336
|
+
"",
|
|
4337
|
+
` ! This repository takes its spells from the user tier, and ${storeRoot} is not installed.`,
|
|
4338
|
+
" No client can reach a spell here: the routing block in CLAUDE.md and AGENTS.md still",
|
|
4339
|
+
" names spells that resolve to nothing, so an agent improvises the workflow instead of",
|
|
4340
|
+
" failing. Fix it with one of:",
|
|
4341
|
+
"",
|
|
4342
|
+
" spell init --user # install the tier on this machine",
|
|
4343
|
+
' # or set "spell_scope": "repo" in .arcane.json and re-run `spell update`',
|
|
4344
|
+
""
|
|
4345
|
+
];
|
|
4346
|
+
}
|
|
4347
|
+
}
|
|
4348
|
+
function defaultScopePatch(options, manifest) {
|
|
4349
|
+
if (options.defaultScope === void 0) return void 0;
|
|
4350
|
+
if (manifest.default_spell_scope === options.defaultScope) return void 0;
|
|
4351
|
+
return { default_spell_scope: options.defaultScope };
|
|
4352
|
+
}
|
|
4285
4353
|
function migrateLegacyComponents(components) {
|
|
4286
4354
|
const out = [];
|
|
4287
4355
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -4395,6 +4463,7 @@ async function runUpdate(options, targetDir, assetsDir, packageVersion) {
|
|
|
4395
4463
|
const spellScope = effectiveSpellScope(manifest);
|
|
4396
4464
|
const readopted = findReadoptedSpellComponents(manifest.components, scope, spellScope);
|
|
4397
4465
|
const homeDir = dirname5(targetDir);
|
|
4466
|
+
for (const line of await missingTierWarning(scope, spellScope)) console.log(line);
|
|
4398
4467
|
if (scope === "repo") {
|
|
4399
4468
|
console.warn(
|
|
4400
4469
|
`WARNING: Arcane v${packageVersion} update safety notice: commit your work before updating.`
|
|
@@ -4442,8 +4511,9 @@ async function runUpdate(options, targetDir, assetsDir, packageVersion) {
|
|
|
4442
4511
|
fallbackDir: assetsDir
|
|
4443
4512
|
});
|
|
4444
4513
|
for (const line of describeFanoutOutcomes(fanout.outcomes, options.dryRun)) console.log(` ${line}`);
|
|
4445
|
-
|
|
4446
|
-
|
|
4514
|
+
const scopePatch = defaultScopePatch(options, manifest);
|
|
4515
|
+
if (!options.dryRun && (JSON.stringify(fanout.record) !== JSON.stringify(manifest.fanout ?? {}) || scopePatch !== void 0)) {
|
|
4516
|
+
await writeManifest(targetDir, { ...manifest, ...scopePatch, fanout: fanout.record });
|
|
4447
4517
|
}
|
|
4448
4518
|
}
|
|
4449
4519
|
return;
|
|
@@ -4689,7 +4759,8 @@ ${verb} ${orphansToReport.length} orphaned file(s) (tracked before this update,
|
|
|
4689
4759
|
version: packageVersion,
|
|
4690
4760
|
components: updatedComponents,
|
|
4691
4761
|
...retrofitPatch,
|
|
4692
|
-
...fanoutRecord !== void 0 ? { fanout: fanoutRecord } : {}
|
|
4762
|
+
...fanoutRecord !== void 0 ? { fanout: fanoutRecord } : {},
|
|
4763
|
+
...defaultScopePatch(options, manifest)
|
|
4693
4764
|
};
|
|
4694
4765
|
await writeManifest(targetDir, updated);
|
|
4695
4766
|
console.log(
|
|
@@ -7069,9 +7140,31 @@ program.command("add <component>").description("Add a component to an existing A
|
|
|
7069
7140
|
program.command("update").description("Update installed components to the current package version").option("--dry-run", "Preview what would be updated without making changes").option(
|
|
7070
7141
|
"--prune",
|
|
7071
7142
|
"Also delete orphaned managed files (tracked before this update, no longer part of any current component). Hash-checked: an edited file is reported, never silently deleted"
|
|
7072
|
-
).option("--user", USER_FLAG_DESCRIPTION).
|
|
7143
|
+
).option("--user", USER_FLAG_DESCRIPTION).option(
|
|
7144
|
+
"--default-scope <scope>",
|
|
7145
|
+
"With --user: remember repo | user as the answer `spell init` pre-selects for NEW repositories on this machine. Never rewrites an existing repository, and the question is still asked"
|
|
7146
|
+
).action(async (opts) => {
|
|
7147
|
+
if (opts.defaultScope !== void 0) {
|
|
7148
|
+
if (!opts.user) {
|
|
7149
|
+
console.error(
|
|
7150
|
+
"\n \u2717 --default-scope applies to the user tier only. It records a preference in\n ~/.arcane, not in a repository. Re-run it as:\n\n spell update --user --default-scope " + opts.defaultScope + "\n"
|
|
7151
|
+
);
|
|
7152
|
+
process.exit(1);
|
|
7153
|
+
}
|
|
7154
|
+
if (opts.defaultScope !== "repo" && opts.defaultScope !== "user") {
|
|
7155
|
+
console.error(
|
|
7156
|
+
"\n \u2717 --default-scope takes repo or user, not: " + opts.defaultScope + "\n"
|
|
7157
|
+
);
|
|
7158
|
+
process.exit(1);
|
|
7159
|
+
}
|
|
7160
|
+
}
|
|
7073
7161
|
await runUpdate(
|
|
7074
|
-
{
|
|
7162
|
+
{
|
|
7163
|
+
dryRun: opts.dryRun,
|
|
7164
|
+
prune: opts.prune,
|
|
7165
|
+
user: opts.user,
|
|
7166
|
+
...opts.defaultScope === void 0 ? {} : { defaultScope: opts.defaultScope }
|
|
7167
|
+
},
|
|
7075
7168
|
targetDirFor(opts),
|
|
7076
7169
|
ASSETS_DIR,
|
|
7077
7170
|
pkg.version
|