arcane-cli 1.12.0 → 1.12.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.
package/dist/index.js
CHANGED
|
@@ -6,11 +6,11 @@ const require = __createRequire(import.meta.url);
|
|
|
6
6
|
import { Command } from "commander";
|
|
7
7
|
import { createRequire as createRequire3 } from "module";
|
|
8
8
|
import { fileURLToPath } from "url";
|
|
9
|
-
import { dirname as
|
|
9
|
+
import { dirname as dirname13, join as join23 } from "path";
|
|
10
10
|
|
|
11
11
|
// src/commands/init.ts
|
|
12
12
|
import { select as select3, confirm as confirm2, input as input3 } from "@inquirer/prompts";
|
|
13
|
-
import { dirname as
|
|
13
|
+
import { dirname as dirname5, join as join8 } from "path";
|
|
14
14
|
import chalk2 from "chalk";
|
|
15
15
|
|
|
16
16
|
// src/modules/copier.ts
|
|
@@ -1417,7 +1417,7 @@ var WORDMARK = [
|
|
|
1417
1417
|
var INDENT = " ";
|
|
1418
1418
|
var LOCKUP_WIDTH = INDENT.length + Math.max(...WORDMARK.map((l) => l.length));
|
|
1419
1419
|
function sleep(ms) {
|
|
1420
|
-
return new Promise((
|
|
1420
|
+
return new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
1421
1421
|
}
|
|
1422
1422
|
function isAnimatable() {
|
|
1423
1423
|
return !!process.stdout.isTTY && process.env["TERM"] !== "dumb" && process.env["CI"] !== "true" && process.env["ARCANE_DISABLE_ANIMATION"] !== "true" && (process.stdout.columns ?? 80) >= LOCKUP_WIDTH;
|
|
@@ -2849,7 +2849,7 @@ function buildGitEnv() {
|
|
|
2849
2849
|
function runGit(cwd, args, options = {}) {
|
|
2850
2850
|
const commandClass = options.commandClass ?? classifyGitCommand(args);
|
|
2851
2851
|
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUTS_MS[commandClass] ?? DEFAULT_TIMEOUTS_MS.write;
|
|
2852
|
-
return new Promise((
|
|
2852
|
+
return new Promise((resolve6, reject) => {
|
|
2853
2853
|
const child = execFile(
|
|
2854
2854
|
"git",
|
|
2855
2855
|
args,
|
|
@@ -2883,7 +2883,7 @@ function runGit(cwd, args, options = {}) {
|
|
|
2883
2883
|
reject(error);
|
|
2884
2884
|
return;
|
|
2885
2885
|
}
|
|
2886
|
-
|
|
2886
|
+
resolve6({ stdout, stderr });
|
|
2887
2887
|
}
|
|
2888
2888
|
);
|
|
2889
2889
|
child.stdin?.end();
|
|
@@ -3336,7 +3336,27 @@ async function offerRegistryScaffold(targetDir, businessRoot) {
|
|
|
3336
3336
|
// src/modules/push-safety.ts
|
|
3337
3337
|
import { mkdir as mkdir5, readFile as readFile7, writeFile as writeFile8, chmod, access as access3, rm as rm4, readdir as readdir6 } from "fs/promises";
|
|
3338
3338
|
import { constants } from "fs";
|
|
3339
|
-
import { join as
|
|
3339
|
+
import { join as join7, resolve as resolve3, dirname as dirname4, basename as basename2 } from "path";
|
|
3340
|
+
|
|
3341
|
+
// src/modules/real-path.ts
|
|
3342
|
+
import { realpathSync } from "fs";
|
|
3343
|
+
import { basename, dirname as dirname3, join as join6, resolve as resolve2 } from "path";
|
|
3344
|
+
function resolveRealPath(path4) {
|
|
3345
|
+
const missing = [];
|
|
3346
|
+
let existing = path4;
|
|
3347
|
+
for (; ; ) {
|
|
3348
|
+
try {
|
|
3349
|
+
return join6(realpathSync.native(existing), ...missing);
|
|
3350
|
+
} catch {
|
|
3351
|
+
const parent = dirname3(existing);
|
|
3352
|
+
if (parent === existing) return resolve2(path4);
|
|
3353
|
+
missing.unshift(basename(existing));
|
|
3354
|
+
existing = parent;
|
|
3355
|
+
}
|
|
3356
|
+
}
|
|
3357
|
+
}
|
|
3358
|
+
|
|
3359
|
+
// src/modules/push-safety.ts
|
|
3340
3360
|
var DISABLED_PUSH_URL = "arcane-push-blocked-run-spell-unblock-push://this-repository";
|
|
3341
3361
|
var NO_PUSH_URL_MARKER = (remote) => `remote.${remote}.arcaneHadNoPushUrl`;
|
|
3342
3362
|
var ARCANE_HOOKS_DIR = ".arcane/hooks";
|
|
@@ -3365,10 +3385,12 @@ function isArcaneHooksPath(root, value) {
|
|
|
3365
3385
|
const windows = process.platform === "win32";
|
|
3366
3386
|
const normalize = (p) => {
|
|
3367
3387
|
const separated = windows ? p.replace(/\\/g, "/") : p;
|
|
3368
|
-
const
|
|
3388
|
+
const resolved = resolve3(root, separated);
|
|
3389
|
+
const absolute = (windows ? resolved.replace(/\\/g, "/") : resolved).replace(/\/+$/, "");
|
|
3369
3390
|
return windows ? absolute.toLowerCase() : absolute;
|
|
3370
3391
|
};
|
|
3371
|
-
|
|
3392
|
+
if (normalize(value) === normalize(ARCANE_HOOKS_DIR)) return true;
|
|
3393
|
+
return normalize(resolveRealPath(resolve3(root, value))) === normalize(join7(resolveRealPath(root), ARCANE_HOOKS_DIR));
|
|
3372
3394
|
}
|
|
3373
3395
|
var PRE_PUSH_HOOK_BODY = `#!/bin/sh
|
|
3374
3396
|
# Installed by Arcane because this repository's .arcane.json sets
|
|
@@ -3413,8 +3435,8 @@ async function repositoryRoot(cwd) {
|
|
|
3413
3435
|
]);
|
|
3414
3436
|
const commonDir = stdout.trim();
|
|
3415
3437
|
if (commonDir !== "") {
|
|
3416
|
-
const parent =
|
|
3417
|
-
if (
|
|
3438
|
+
const parent = dirname4(commonDir);
|
|
3439
|
+
if (basename2(commonDir) === ".git" && parent !== "") return parent;
|
|
3418
3440
|
}
|
|
3419
3441
|
} catch {
|
|
3420
3442
|
}
|
|
@@ -3427,10 +3449,11 @@ async function repositoryRoot(cwd) {
|
|
|
3427
3449
|
return cwd;
|
|
3428
3450
|
}
|
|
3429
3451
|
async function hookFilePath(cwd, hookName = "pre-push") {
|
|
3430
|
-
return
|
|
3452
|
+
return join7(await repositoryRoot(cwd), ARCANE_HOOKS_DIR, hookName);
|
|
3431
3453
|
}
|
|
3432
3454
|
async function hooksPathValue(cwd) {
|
|
3433
|
-
|
|
3455
|
+
const value = join7(await repositoryRoot(cwd), ARCANE_HOOKS_DIR);
|
|
3456
|
+
return process.platform === "win32" ? value.replace(/\\/g, "/") : value;
|
|
3434
3457
|
}
|
|
3435
3458
|
async function installHook(cwd, hookName, body) {
|
|
3436
3459
|
const root = await repositoryRoot(cwd);
|
|
@@ -3449,11 +3472,11 @@ async function installHook(cwd, hookName, body) {
|
|
|
3449
3472
|
return { status: "refused-default-hooks", hooks: displaced };
|
|
3450
3473
|
}
|
|
3451
3474
|
}
|
|
3452
|
-
const hookPath =
|
|
3475
|
+
const hookPath = join7(root, ARCANE_HOOKS_DIR, hookName);
|
|
3453
3476
|
if (existing !== void 0 && await hookFileMatches(cwd, hookName, body)) {
|
|
3454
3477
|
return { status: "already-ours" };
|
|
3455
3478
|
}
|
|
3456
|
-
await mkdir5(
|
|
3479
|
+
await mkdir5(join7(root, ARCANE_HOOKS_DIR), { recursive: true });
|
|
3457
3480
|
await writeFile8(hookPath, body, "utf-8");
|
|
3458
3481
|
await chmod(hookPath, 493);
|
|
3459
3482
|
await runGit(cwd, ["config", "--local", "core.hooksPath", await hooksPathValue(cwd)]);
|
|
@@ -3498,7 +3521,7 @@ async function defaultHooks(cwd) {
|
|
|
3498
3521
|
let hooksDir;
|
|
3499
3522
|
try {
|
|
3500
3523
|
const { stdout } = await runGit(cwd, ["rev-parse", "--git-common-dir"]);
|
|
3501
|
-
hooksDir =
|
|
3524
|
+
hooksDir = join7(resolve3(cwd, stdout.trim()), "hooks");
|
|
3502
3525
|
} catch {
|
|
3503
3526
|
return [];
|
|
3504
3527
|
}
|
|
@@ -3534,7 +3557,7 @@ async function removeHook(cwd, hookName) {
|
|
|
3534
3557
|
await rm4(await hookFilePath(cwd, hookName), { force: true });
|
|
3535
3558
|
} catch {
|
|
3536
3559
|
}
|
|
3537
|
-
const hooksDir =
|
|
3560
|
+
const hooksDir = join7(root, ARCANE_HOOKS_DIR);
|
|
3538
3561
|
let remaining = [];
|
|
3539
3562
|
try {
|
|
3540
3563
|
remaining = await readdir6(hooksDir);
|
|
@@ -3937,7 +3960,7 @@ async function runInit(options, targetDir, assetsDir, packageVersion) {
|
|
|
3937
3960
|
}
|
|
3938
3961
|
let flags = {};
|
|
3939
3962
|
try {
|
|
3940
|
-
flags = resolveManifestFlags(options.manifestFlags, {},
|
|
3963
|
+
flags = resolveManifestFlags(options.manifestFlags, {}, join8(targetDir, ".arcane.json"));
|
|
3941
3964
|
} catch (err) {
|
|
3942
3965
|
if (!(err instanceof ManifestFlagError)) throw err;
|
|
3943
3966
|
console.error(`
|
|
@@ -4072,7 +4095,7 @@ async function runInit(options, targetDir, assetsDir, packageVersion) {
|
|
|
4072
4095
|
const installedFiles = [];
|
|
4073
4096
|
const fileHashes = {};
|
|
4074
4097
|
for (const file of component.files) {
|
|
4075
|
-
const srcPath =
|
|
4098
|
+
const srcPath = join8(assetsDir, component.sourceOverrides?.[file] ?? file);
|
|
4076
4099
|
if (options.dryRun) {
|
|
4077
4100
|
printDryRun(`Would copy: ${file}`);
|
|
4078
4101
|
} else {
|
|
@@ -4089,7 +4112,7 @@ async function runInit(options, targetDir, assetsDir, packageVersion) {
|
|
|
4089
4112
|
fileCount++;
|
|
4090
4113
|
}
|
|
4091
4114
|
for (const dir of component.directories ?? []) {
|
|
4092
|
-
const srcDirPath =
|
|
4115
|
+
const srcDirPath = join8(assetsDir, dir);
|
|
4093
4116
|
if (options.dryRun) {
|
|
4094
4117
|
printDryRun(`Would copy directory: ${dir}/`);
|
|
4095
4118
|
} else {
|
|
@@ -4118,7 +4141,7 @@ async function runInit(options, targetDir, assetsDir, packageVersion) {
|
|
|
4118
4141
|
if (options.dryRun) {
|
|
4119
4142
|
if (hasAgents) {
|
|
4120
4143
|
const agentFiles = await import("fs").then(
|
|
4121
|
-
(fsModule) => fsModule.promises.readdir(
|
|
4144
|
+
(fsModule) => fsModule.promises.readdir(join8(assetsDir, "agents"))
|
|
4122
4145
|
);
|
|
4123
4146
|
const yamlFiles = agentFiles.filter((f) => f.endsWith(".yaml")).sort();
|
|
4124
4147
|
printDryRun("Would run agent setup (interactive):");
|
|
@@ -4357,7 +4380,7 @@ async function runInitUser(options, storeRoot, assetsDir, packageVersion) {
|
|
|
4357
4380
|
console.log(chalk2.hex("#a855f7")(`
|
|
4358
4381
|
\u2726 Arcane v${packageVersion}
|
|
4359
4382
|
`));
|
|
4360
|
-
const homeDir =
|
|
4383
|
+
const homeDir = dirname5(storeRoot);
|
|
4361
4384
|
const components = USER_TIER_COMPONENTS.map(
|
|
4362
4385
|
(name) => componentForScope(getComponent(name), "user")
|
|
4363
4386
|
);
|
|
@@ -4375,7 +4398,7 @@ async function runInitUser(options, storeRoot, assetsDir, packageVersion) {
|
|
|
4375
4398
|
for (const id of spellIds) {
|
|
4376
4399
|
for (const client2 of ["codex", "claude"]) {
|
|
4377
4400
|
const relativePath = USER_FANOUT_PATHS[client2](id);
|
|
4378
|
-
if (await fileExists(
|
|
4401
|
+
if (await fileExists(join8(homeDir, relativePath))) {
|
|
4379
4402
|
printDryRun(`Would leave alone (exists, not written by Arcane): ~/${relativePath}`);
|
|
4380
4403
|
} else {
|
|
4381
4404
|
printDryRun(`Would write: ~/${relativePath}`);
|
|
@@ -4393,7 +4416,7 @@ async function runInitUser(options, storeRoot, assetsDir, packageVersion) {
|
|
|
4393
4416
|
const installedFiles = [];
|
|
4394
4417
|
const fileHashes = {};
|
|
4395
4418
|
for (const file of component.files) {
|
|
4396
|
-
const srcPath =
|
|
4419
|
+
const srcPath = join8(assetsDir, component.sourceOverrides?.[file] ?? file);
|
|
4397
4420
|
fileHashes[file] = await copyFile(srcPath, storeRoot, file, { force: options.force });
|
|
4398
4421
|
installedFiles.push(file);
|
|
4399
4422
|
fileCount++;
|
|
@@ -4440,23 +4463,23 @@ async function runInitUser(options, storeRoot, assetsDir, packageVersion) {
|
|
|
4440
4463
|
|
|
4441
4464
|
// src/modules/component-install.ts
|
|
4442
4465
|
import { readFile as readFile8, stat, writeFile as writeFile9 } from "fs/promises";
|
|
4443
|
-
import { join as
|
|
4466
|
+
import { join as join9 } from "path";
|
|
4444
4467
|
async function planComponentInstall(component, targetDir, assetsDir, opts = {}) {
|
|
4445
4468
|
const files = [];
|
|
4446
4469
|
const stateOf = async (file, src) => {
|
|
4447
|
-
const dest =
|
|
4470
|
+
const dest = join9(targetDir, file);
|
|
4448
4471
|
if (!await fileExists(dest)) return { file, src, state: "missing" };
|
|
4449
4472
|
if ((await stat(dest)).isDirectory()) return { file, src, state: "directory" };
|
|
4450
4473
|
const same = await hashFile(dest) === await hashFile(src);
|
|
4451
4474
|
return { file, src, state: same ? "identical" : "differs" };
|
|
4452
4475
|
};
|
|
4453
4476
|
for (const file of component.files) {
|
|
4454
|
-
files.push(await stateOf(file,
|
|
4477
|
+
files.push(await stateOf(file, join9(assetsDir, component.sourceOverrides?.[file] ?? file)));
|
|
4455
4478
|
}
|
|
4456
4479
|
for (const dir of component.directories ?? []) {
|
|
4457
|
-
const srcDir =
|
|
4480
|
+
const srcDir = join9(assetsDir, dir);
|
|
4458
4481
|
for (const file of await listDirectoryFiles(srcDir, dir)) {
|
|
4459
|
-
files.push(await stateOf(file,
|
|
4482
|
+
files.push(await stateOf(file, join9(assetsDir, file)));
|
|
4460
4483
|
}
|
|
4461
4484
|
}
|
|
4462
4485
|
const of = (...states) => files.filter((f) => states.includes(f.state));
|
|
@@ -4474,7 +4497,7 @@ async function executeInstallPlan(plan, targetDir) {
|
|
|
4474
4497
|
const written = [];
|
|
4475
4498
|
try {
|
|
4476
4499
|
for (const { file, src, state } of plan.toCopy) {
|
|
4477
|
-
const previous = state === "missing" ? null : await readFile8(
|
|
4500
|
+
const previous = state === "missing" ? null : await readFile8(join9(targetDir, file));
|
|
4478
4501
|
fileHashes[file] = await copyFile(src, targetDir, file, { force: true });
|
|
4479
4502
|
written.push({ file, previous });
|
|
4480
4503
|
files.push(file);
|
|
@@ -4492,7 +4515,7 @@ async function rollback(written, targetDir) {
|
|
|
4492
4515
|
await removeWithin(targetDir, file);
|
|
4493
4516
|
await removeEmptyAncestors(targetDir, file);
|
|
4494
4517
|
} else {
|
|
4495
|
-
await writeFile9(
|
|
4518
|
+
await writeFile9(join9(targetDir, file), previous);
|
|
4496
4519
|
}
|
|
4497
4520
|
} catch {
|
|
4498
4521
|
}
|
|
@@ -4628,7 +4651,7 @@ async function runAdd(names, options, targetDir, assetsDir, packageVersion) {
|
|
|
4628
4651
|
}
|
|
4629
4652
|
|
|
4630
4653
|
// src/commands/update.ts
|
|
4631
|
-
import { dirname as
|
|
4654
|
+
import { dirname as dirname6, join as join11 } from "path";
|
|
4632
4655
|
import { readFile as readFile9, writeFile as writeFile10 } from "fs/promises";
|
|
4633
4656
|
|
|
4634
4657
|
// src/modules/merge3.ts
|
|
@@ -4760,7 +4783,7 @@ function merge3(base, ours, theirs) {
|
|
|
4760
4783
|
}
|
|
4761
4784
|
|
|
4762
4785
|
// src/modules/registry-changes.ts
|
|
4763
|
-
import { join as
|
|
4786
|
+
import { join as join10 } from "path";
|
|
4764
4787
|
function findMissingRequires(installed, profile) {
|
|
4765
4788
|
const names = new Set(installed.flatMap((c) => LEGACY_COMPONENT_MIGRATIONS[c.name] ?? [c.name]));
|
|
4766
4789
|
const promised = listProfiles().find((p) => p.id === profile)?.components;
|
|
@@ -4806,7 +4829,7 @@ async function findNewlyAvailable(targetDir, profile, installed, spellScope) {
|
|
|
4806
4829
|
const alreadyPresent = [];
|
|
4807
4830
|
if (component.skipExisting) {
|
|
4808
4831
|
for (const file of component.files) {
|
|
4809
|
-
if (await fileExists(
|
|
4832
|
+
if (await fileExists(join10(targetDir, file))) alreadyPresent.push(file);
|
|
4810
4833
|
}
|
|
4811
4834
|
if (component.files.length > 0 && alreadyPresent.length === component.files.length) continue;
|
|
4812
4835
|
}
|
|
@@ -5065,7 +5088,7 @@ async function resolveOrphan(targetDir, file, recordedHash, prune) {
|
|
|
5065
5088
|
console.log(` ! Refused (path escapes the target directory): ${file}`);
|
|
5066
5089
|
return "reported";
|
|
5067
5090
|
}
|
|
5068
|
-
const filePath =
|
|
5091
|
+
const filePath = join11(targetDir, file);
|
|
5069
5092
|
if (!await fileExists(filePath)) return "not-found";
|
|
5070
5093
|
if (!prune) return "reported";
|
|
5071
5094
|
if (recordedHash === void 0) {
|
|
@@ -5087,7 +5110,7 @@ async function isReleasableUserOwnedFile(targetDir, file, recordedHash) {
|
|
|
5087
5110
|
} catch {
|
|
5088
5111
|
return false;
|
|
5089
5112
|
}
|
|
5090
|
-
const filePath =
|
|
5113
|
+
const filePath = join11(targetDir, file);
|
|
5091
5114
|
if (!await fileExists(filePath)) return false;
|
|
5092
5115
|
if (recordedHash === void 0) return true;
|
|
5093
5116
|
return !await fileMatchesHash(filePath, recordedHash);
|
|
@@ -5098,7 +5121,7 @@ async function retainReportedOrphan(targetDir, file, recordedHash, into) {
|
|
|
5098
5121
|
} catch {
|
|
5099
5122
|
return;
|
|
5100
5123
|
}
|
|
5101
|
-
if (!await fileExists(
|
|
5124
|
+
if (!await fileExists(join11(targetDir, file))) return;
|
|
5102
5125
|
if (!into.files.includes(file)) into.files.push(file);
|
|
5103
5126
|
if (recordedHash !== void 0) {
|
|
5104
5127
|
into.fileHashes ??= {};
|
|
@@ -5127,7 +5150,7 @@ async function findMissingTrackedFiles(targetDir, components, scope = "repo", sp
|
|
|
5127
5150
|
const candidates = readopted.has(installed.name) ? component.files : installed.files;
|
|
5128
5151
|
for (const file of candidates) {
|
|
5129
5152
|
if (!component.files.includes(file)) continue;
|
|
5130
|
-
if (!await fileExists(
|
|
5153
|
+
if (!await fileExists(join11(targetDir, file))) missing.push(file);
|
|
5131
5154
|
}
|
|
5132
5155
|
}
|
|
5133
5156
|
return missing;
|
|
@@ -5169,7 +5192,7 @@ async function runUpdate(options, targetDir, assetsDir, packageVersion) {
|
|
|
5169
5192
|
const movedFilesPresent = /* @__PURE__ */ new Set();
|
|
5170
5193
|
for (const move of COMPONENT_FILE_MOVES) {
|
|
5171
5194
|
for (const file of move.files) {
|
|
5172
|
-
if (await fileExists(
|
|
5195
|
+
if (await fileExists(join11(targetDir, file))) movedFilesPresent.add(file);
|
|
5173
5196
|
}
|
|
5174
5197
|
}
|
|
5175
5198
|
manifest = {
|
|
@@ -5200,7 +5223,7 @@ async function runUpdate(options, targetDir, assetsDir, packageVersion) {
|
|
|
5200
5223
|
return;
|
|
5201
5224
|
}
|
|
5202
5225
|
try {
|
|
5203
|
-
flagPatch = resolveManifestFlags(options.manifestFlags, manifest,
|
|
5226
|
+
flagPatch = resolveManifestFlags(options.manifestFlags, manifest, join11(targetDir, ".arcane.json"));
|
|
5204
5227
|
} catch (err) {
|
|
5205
5228
|
if (!(err instanceof ManifestFlagError)) throw err;
|
|
5206
5229
|
console.error(`
|
|
@@ -5213,7 +5236,7 @@ async function runUpdate(options, targetDir, assetsDir, packageVersion) {
|
|
|
5213
5236
|
const answered = { ...manifest, ...flagPatch };
|
|
5214
5237
|
const spellScope = effectiveSpellScope(manifest);
|
|
5215
5238
|
const readopted = findReadoptedSpellComponents(manifest.components, scope, spellScope);
|
|
5216
|
-
const homeDir =
|
|
5239
|
+
const homeDir = dirname6(targetDir);
|
|
5217
5240
|
for (const line of await missingTierWarning(scope, spellScope)) console.log(line);
|
|
5218
5241
|
if (scope === "repo") {
|
|
5219
5242
|
console.warn(
|
|
@@ -5408,8 +5431,8 @@ async function runUpdate(options, targetDir, assetsDir, packageVersion) {
|
|
|
5408
5431
|
const updatedFiles = [];
|
|
5409
5432
|
const fileHashes = {};
|
|
5410
5433
|
for (const file of component.files) {
|
|
5411
|
-
const srcPath =
|
|
5412
|
-
const targetExists = await fileExists(
|
|
5434
|
+
const srcPath = join11(assetsDir, component.sourceOverrides?.[file] ?? file);
|
|
5435
|
+
const targetExists = await fileExists(join11(targetDir, file));
|
|
5413
5436
|
const preserveExisting = Boolean(component.skipExisting) && targetExists;
|
|
5414
5437
|
const recordedHash = installed.fileHashes?.[file];
|
|
5415
5438
|
if (component.initOnly && !targetExists) {
|
|
@@ -5429,7 +5452,7 @@ async function runUpdate(options, targetDir, assetsDir, packageVersion) {
|
|
|
5429
5452
|
continue;
|
|
5430
5453
|
}
|
|
5431
5454
|
}
|
|
5432
|
-
const editedByOperator = recordedHash !== void 0 && targetExists && !preserveExisting && !await fileMatchesHash(
|
|
5455
|
+
const editedByOperator = recordedHash !== void 0 && targetExists && !preserveExisting && !await fileMatchesHash(join11(targetDir, file), recordedHash);
|
|
5433
5456
|
if (preserveExisting) {
|
|
5434
5457
|
console.log(` ${options.dryRun ? "[dry-run] Would preserve" : "Preserved"}: ${file}`);
|
|
5435
5458
|
} else if (editedByOperator && isClientShimPath(file)) {
|
|
@@ -5456,11 +5479,11 @@ async function runUpdate(options, targetDir, assetsDir, packageVersion) {
|
|
|
5456
5479
|
handled = true;
|
|
5457
5480
|
} else {
|
|
5458
5481
|
const [currentContent, newContent] = await Promise.all([
|
|
5459
|
-
readFile9(
|
|
5482
|
+
readFile9(join11(targetDir, file), "utf-8"),
|
|
5460
5483
|
readFile9(srcPath, "utf-8")
|
|
5461
5484
|
]);
|
|
5462
5485
|
const result = merge3(oldVendorContent, currentContent, newContent);
|
|
5463
|
-
await writeFile10(
|
|
5486
|
+
await writeFile10(join11(targetDir, file), result.content, "utf-8");
|
|
5464
5487
|
fileHashes[file] = await hashFile(srcPath);
|
|
5465
5488
|
if (result.hasConflict) {
|
|
5466
5489
|
conflictedFiles.push(file);
|
|
@@ -5482,7 +5505,7 @@ async function runUpdate(options, targetDir, assetsDir, packageVersion) {
|
|
|
5482
5505
|
}
|
|
5483
5506
|
}
|
|
5484
5507
|
for (const dir of component.directories ?? []) {
|
|
5485
|
-
const srcDirPath =
|
|
5508
|
+
const srcDirPath = join11(assetsDir, dir);
|
|
5486
5509
|
if (options.dryRun) {
|
|
5487
5510
|
console.log(` [dry-run] Would update directory: ${dir}/`);
|
|
5488
5511
|
} else {
|
|
@@ -5642,11 +5665,11 @@ ${verb} ${orphansToReport.length} orphaned file(s) (tracked before this update,
|
|
|
5642
5665
|
}
|
|
5643
5666
|
|
|
5644
5667
|
// src/commands/status.ts
|
|
5645
|
-
import { dirname as
|
|
5668
|
+
import { dirname as dirname8 } from "path";
|
|
5646
5669
|
|
|
5647
5670
|
// src/modules/version-check.ts
|
|
5648
5671
|
import { readFile as readFile10, writeFile as writeFile11, mkdir as mkdir6 } from "fs/promises";
|
|
5649
|
-
import { dirname as
|
|
5672
|
+
import { dirname as dirname7, join as join12 } from "path";
|
|
5650
5673
|
import { homedir as homedir3 } from "os";
|
|
5651
5674
|
import { createRequire } from "module";
|
|
5652
5675
|
var require2 = createRequire(import.meta.url);
|
|
@@ -5660,7 +5683,7 @@ function getFeedUrl() {
|
|
|
5660
5683
|
}
|
|
5661
5684
|
return url;
|
|
5662
5685
|
}
|
|
5663
|
-
var DEFAULT_CACHE_PATH =
|
|
5686
|
+
var DEFAULT_CACHE_PATH = join12(homedir3(), ".arcane", "version-cache.json");
|
|
5664
5687
|
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
5665
5688
|
async function readCache(cachePath) {
|
|
5666
5689
|
try {
|
|
@@ -5675,7 +5698,7 @@ async function readCache(cachePath) {
|
|
|
5675
5698
|
}
|
|
5676
5699
|
}
|
|
5677
5700
|
async function writeCache(cachePath, latest) {
|
|
5678
|
-
await mkdir6(
|
|
5701
|
+
await mkdir6(dirname7(cachePath), { recursive: true });
|
|
5679
5702
|
const entry = { latest, checkedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
5680
5703
|
await writeFile11(cachePath, JSON.stringify(entry, null, 2), "utf-8");
|
|
5681
5704
|
}
|
|
@@ -5816,7 +5839,7 @@ async function runStatus(targetDir, packageVersion, options = {}) {
|
|
|
5816
5839
|
console.log("");
|
|
5817
5840
|
if (scope === "user") {
|
|
5818
5841
|
console.log(` Scope: user \u2014 ${targetDir}`);
|
|
5819
|
-
const health = await inspectUserTierFanout(
|
|
5842
|
+
const health = await inspectUserTierFanout(dirname8(targetDir), manifest.fanout);
|
|
5820
5843
|
console.log(
|
|
5821
5844
|
` Client files: ${health.total} (${health.byClient.codex} ${FANOUT_CLIENT_LABELS.codex}, ${health.byClient.claude} ${FANOUT_CLIENT_LABELS.claude})`
|
|
5822
5845
|
);
|
|
@@ -5879,7 +5902,7 @@ async function runStatus(targetDir, packageVersion, options = {}) {
|
|
|
5879
5902
|
|
|
5880
5903
|
// src/commands/uninstall.ts
|
|
5881
5904
|
import { rm as rm5 } from "fs/promises";
|
|
5882
|
-
import { dirname as
|
|
5905
|
+
import { dirname as dirname9, join as join13 } from "path";
|
|
5883
5906
|
import { confirm as confirm3 } from "@inquirer/prompts";
|
|
5884
5907
|
async function runUninstall(options, targetDir) {
|
|
5885
5908
|
let manifest;
|
|
@@ -5962,16 +5985,16 @@ Run \`spell unblock-push\` first (interactive), then uninstall.`
|
|
|
5962
5985
|
}
|
|
5963
5986
|
}
|
|
5964
5987
|
}
|
|
5965
|
-
await rm5(
|
|
5988
|
+
await rm5(join13(targetDir, ".arcane.json"), { force: true });
|
|
5966
5989
|
const agentDirs = [
|
|
5967
5990
|
".github/agents",
|
|
5968
5991
|
".arcane/agents",
|
|
5969
5992
|
".arcane/generated"
|
|
5970
5993
|
];
|
|
5971
5994
|
for (const dir of agentDirs) {
|
|
5972
|
-
await rm5(
|
|
5995
|
+
await rm5(join13(targetDir, dir), { recursive: true, force: true });
|
|
5973
5996
|
}
|
|
5974
|
-
await rm5(
|
|
5997
|
+
await rm5(join13(targetDir, ".arcane/agents.yaml"), { force: true });
|
|
5975
5998
|
const markerFiles = [
|
|
5976
5999
|
"CLAUDE.md",
|
|
5977
6000
|
"AGENTS.md",
|
|
@@ -5988,7 +6011,7 @@ Run \`spell unblock-push\` first (interactive), then uninstall.`
|
|
|
5988
6011
|
\u2713 Uninstalled \u2014 ${removed} files removed.`);
|
|
5989
6012
|
}
|
|
5990
6013
|
async function uninstallUserTier(options, storeRoot, manifest) {
|
|
5991
|
-
const homeDir =
|
|
6014
|
+
const homeDir = dirname9(storeRoot);
|
|
5992
6015
|
const storeFiles = manifest.components.flatMap((c) => c.files);
|
|
5993
6016
|
const recordedCount = Object.keys(manifest.fanout ?? {}).length;
|
|
5994
6017
|
if (options.dryRun) {
|
|
@@ -6033,10 +6056,10 @@ async function uninstallUserTier(options, storeRoot, manifest) {
|
|
|
6033
6056
|
console.log(` ! Skipped ${file}: ${err instanceof Error ? err.message : String(err)}`);
|
|
6034
6057
|
}
|
|
6035
6058
|
}
|
|
6036
|
-
await rm5(
|
|
6037
|
-
await rm5(
|
|
6038
|
-
await rm5(
|
|
6039
|
-
await removeDirectoryIfEmpty(
|
|
6059
|
+
await rm5(join13(storeRoot, ".arcane.json"), { force: true });
|
|
6060
|
+
await rm5(join13(storeRoot, "agents"), { recursive: true, force: true });
|
|
6061
|
+
await rm5(join13(storeRoot, "agents.yaml"), { force: true });
|
|
6062
|
+
await removeDirectoryIfEmpty(join13(storeRoot, USER_TIER_SPELLS_DIR));
|
|
6040
6063
|
await removeDirectoryIfEmpty(storeRoot);
|
|
6041
6064
|
for (const line of describeFanoutOutcomes(fanout.outcomes)) console.log(` ${line}`);
|
|
6042
6065
|
const pruned = fanout.outcomes.filter((o) => o.status === "pruned").length;
|
|
@@ -6049,7 +6072,7 @@ async function uninstallUserTier(options, storeRoot, manifest) {
|
|
|
6049
6072
|
|
|
6050
6073
|
// src/commands/doctor.ts
|
|
6051
6074
|
import { access as access4, readdir as readdir9, readFile as readFile13, mkdir as mkdir7, copyFile as fsCopyFile } from "fs/promises";
|
|
6052
|
-
import { join as
|
|
6075
|
+
import { join as join16, dirname as dirname10 } from "path";
|
|
6053
6076
|
|
|
6054
6077
|
// src/config/incidents.ts
|
|
6055
6078
|
var INCIDENT_QUEUE = [
|
|
@@ -6118,7 +6141,7 @@ function evaluateIncidentGate(records) {
|
|
|
6118
6141
|
|
|
6119
6142
|
// src/modules/denylist-scan.ts
|
|
6120
6143
|
import { readFile as readFile11, readdir as readdir7 } from "fs/promises";
|
|
6121
|
-
import { join as
|
|
6144
|
+
import { join as join14, relative } from "path";
|
|
6122
6145
|
function escapeRegExp(value) {
|
|
6123
6146
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
6124
6147
|
}
|
|
@@ -6169,7 +6192,7 @@ async function collectScannableFiles(root, repoRoot = root, extensions = DEFAULT
|
|
|
6169
6192
|
return collected;
|
|
6170
6193
|
}
|
|
6171
6194
|
for (const entry of entries) {
|
|
6172
|
-
const absolutePath =
|
|
6195
|
+
const absolutePath = join14(root, entry.name);
|
|
6173
6196
|
if (entry.isDirectory()) {
|
|
6174
6197
|
if (SKIP_DIRECTORIES.has(entry.name)) continue;
|
|
6175
6198
|
collected.push(...await collectScannableFiles(absolutePath, repoRoot, extensions));
|
|
@@ -6196,7 +6219,7 @@ import { existsSync, readFileSync } from "fs";
|
|
|
6196
6219
|
import { win32 } from "path";
|
|
6197
6220
|
var EXTERNAL_CLI_TIMEOUT_MS = 1e4;
|
|
6198
6221
|
function execFileWithTimeout(file, args, timeoutMs, options = {}) {
|
|
6199
|
-
return new Promise((
|
|
6222
|
+
return new Promise((resolve6, reject) => {
|
|
6200
6223
|
const child = execFile2(
|
|
6201
6224
|
file,
|
|
6202
6225
|
args,
|
|
@@ -6212,7 +6235,7 @@ function execFileWithTimeout(file, args, timeoutMs, options = {}) {
|
|
|
6212
6235
|
reject(error);
|
|
6213
6236
|
return;
|
|
6214
6237
|
}
|
|
6215
|
-
|
|
6238
|
+
resolve6({ stdout, stderr });
|
|
6216
6239
|
}
|
|
6217
6240
|
);
|
|
6218
6241
|
child.stdin?.end();
|
|
@@ -6430,7 +6453,7 @@ function parseAdoRemote(url) {
|
|
|
6430
6453
|
|
|
6431
6454
|
// src/modules/placeholders.ts
|
|
6432
6455
|
import { readdir as readdir8, readFile as readFile12 } from "fs/promises";
|
|
6433
|
-
import { join as
|
|
6456
|
+
import { join as join15 } from "path";
|
|
6434
6457
|
var PLACEHOLDER_STANDARD_FILE = "spell-authoring-standards.md";
|
|
6435
6458
|
var RUNTIME_PLACEHOLDERS_START = "<!-- runtime-placeholders:start -->";
|
|
6436
6459
|
var RUNTIME_PLACEHOLDERS_END = "<!-- runtime-placeholders:end -->";
|
|
@@ -6525,7 +6548,7 @@ function parseRuntimePlaceholders(standardContent) {
|
|
|
6525
6548
|
async function scanGovernancePlaceholders(governanceDir) {
|
|
6526
6549
|
let standard;
|
|
6527
6550
|
try {
|
|
6528
|
-
standard = await readFile12(
|
|
6551
|
+
standard = await readFile12(join15(governanceDir, PLACEHOLDER_STANDARD_FILE), "utf8");
|
|
6529
6552
|
} catch (err) {
|
|
6530
6553
|
if (err.code === "ENOENT") {
|
|
6531
6554
|
return { status: "skipped", reason: `${PLACEHOLDER_STANDARD_FILE} is not installed` };
|
|
@@ -6544,7 +6567,7 @@ async function scanGovernancePlaceholders(governanceDir) {
|
|
|
6544
6567
|
const templateDocs = [];
|
|
6545
6568
|
const findings = [];
|
|
6546
6569
|
for (const file of files) {
|
|
6547
|
-
const content = await readFile12(
|
|
6570
|
+
const content = await readFile12(join15(governanceDir, file), "utf8");
|
|
6548
6571
|
const status = parseFrontmatterStatus(content);
|
|
6549
6572
|
if (status === "template") templateDocs.push(file);
|
|
6550
6573
|
if (status !== "active") continue;
|
|
@@ -6575,8 +6598,8 @@ async function checkNodeVersion() {
|
|
|
6575
6598
|
async function checkVSCodeExtension(id, label2, homeDir = resolveHomeDir()) {
|
|
6576
6599
|
const name = `VS Code extension: ${label2}`;
|
|
6577
6600
|
const flavours = [
|
|
6578
|
-
{ dir:
|
|
6579
|
-
{ dir:
|
|
6601
|
+
{ dir: join16(homeDir, ".vscode", "extensions"), bin: "code" },
|
|
6602
|
+
{ dir: join16(homeDir, ".vscode-insiders", "extensions"), bin: "code-insiders" }
|
|
6580
6603
|
];
|
|
6581
6604
|
let installBin = null;
|
|
6582
6605
|
for (const flavour of flavours) {
|
|
@@ -6604,11 +6627,11 @@ async function checkVSCodeExtension(id, label2, homeDir = resolveHomeDir()) {
|
|
|
6604
6627
|
}
|
|
6605
6628
|
async function checkArcaneManifest(targetDir) {
|
|
6606
6629
|
const name = "Arcane manifest (.arcane.json)";
|
|
6607
|
-
const manifestPath2 =
|
|
6630
|
+
const manifestPath2 = join16(targetDir, ".arcane.json");
|
|
6608
6631
|
try {
|
|
6609
6632
|
await access4(manifestPath2);
|
|
6610
6633
|
} catch {
|
|
6611
|
-
const selfHostedManifestPath =
|
|
6634
|
+
const selfHostedManifestPath = join16(targetDir, "src", "assets", ".arcane.json");
|
|
6612
6635
|
try {
|
|
6613
6636
|
const raw = await readFile13(selfHostedManifestPath, "utf8");
|
|
6614
6637
|
const manifest = JSON.parse(raw);
|
|
@@ -6714,7 +6737,7 @@ async function checkSessionContinuity(targetDir) {
|
|
|
6714
6737
|
const results = [];
|
|
6715
6738
|
for (const file of SESSION_CONTINUITY_FILES) {
|
|
6716
6739
|
const name = `Session file: ${file}`;
|
|
6717
|
-
const filePath =
|
|
6740
|
+
const filePath = join16(targetDir, file);
|
|
6718
6741
|
try {
|
|
6719
6742
|
await access4(filePath);
|
|
6720
6743
|
results.push({ name, passed: true, message: "present" });
|
|
@@ -6732,12 +6755,12 @@ async function checkSessionContinuity(targetDir) {
|
|
|
6732
6755
|
async function fixSessionContinuity(targetDir, assetsDir) {
|
|
6733
6756
|
const created = [];
|
|
6734
6757
|
for (const file of SESSION_CONTINUITY_FILES) {
|
|
6735
|
-
const destPath =
|
|
6758
|
+
const destPath = join16(targetDir, file);
|
|
6736
6759
|
try {
|
|
6737
6760
|
await access4(destPath);
|
|
6738
6761
|
} catch {
|
|
6739
|
-
const srcPath =
|
|
6740
|
-
await mkdir7(
|
|
6762
|
+
const srcPath = join16(assetsDir, file);
|
|
6763
|
+
await mkdir7(dirname10(destPath), { recursive: true });
|
|
6741
6764
|
await fsCopyFile(srcPath, destPath);
|
|
6742
6765
|
created.push(file);
|
|
6743
6766
|
}
|
|
@@ -6864,7 +6887,7 @@ async function checkGovernancePlaceholders(targetDir) {
|
|
|
6864
6887
|
const name = "Governance placeholders (ARC-051)";
|
|
6865
6888
|
let scan;
|
|
6866
6889
|
try {
|
|
6867
|
-
scan = await scanGovernancePlaceholders(
|
|
6890
|
+
scan = await scanGovernancePlaceholders(join16(targetDir, ".arcane", "governance"));
|
|
6868
6891
|
} catch (err) {
|
|
6869
6892
|
return {
|
|
6870
6893
|
name,
|
|
@@ -6951,7 +6974,7 @@ async function checkNewlyAvailableComponents(targetDir) {
|
|
|
6951
6974
|
}
|
|
6952
6975
|
async function checkMcpConfig(targetDir) {
|
|
6953
6976
|
const name = "MCP config (per-server timeout, I12)";
|
|
6954
|
-
const filePath =
|
|
6977
|
+
const filePath = join16(targetDir, ".mcp.json");
|
|
6955
6978
|
let raw;
|
|
6956
6979
|
try {
|
|
6957
6980
|
raw = await readFile13(filePath, "utf8");
|
|
@@ -7034,7 +7057,7 @@ function isDelegationsFile(value) {
|
|
|
7034
7057
|
}
|
|
7035
7058
|
async function checkDelegations(targetDir) {
|
|
7036
7059
|
const name = "Standing delegations (T13)";
|
|
7037
|
-
const filePath =
|
|
7060
|
+
const filePath = join16(targetDir, ".arcane", "delegations.json");
|
|
7038
7061
|
let raw;
|
|
7039
7062
|
try {
|
|
7040
7063
|
raw = await readFile13(filePath, "utf8");
|
|
@@ -7147,7 +7170,7 @@ async function checkSpellScope(targetDir, homeDir = resolveHomeDir()) {
|
|
|
7147
7170
|
let probed = false;
|
|
7148
7171
|
const probe = "spell-status.md";
|
|
7149
7172
|
try {
|
|
7150
|
-
const entries = await readdir9(
|
|
7173
|
+
const entries = await readdir9(join16(storeRoot, USER_TIER_SPELLS_DIR));
|
|
7151
7174
|
const spells = entries.filter((e) => e.startsWith("spell-") && e.endsWith(".md"));
|
|
7152
7175
|
spellCount = spells.length;
|
|
7153
7176
|
probed = spells.includes(probe);
|
|
@@ -7261,7 +7284,7 @@ import { readFile as readFile15 } from "fs/promises";
|
|
|
7261
7284
|
|
|
7262
7285
|
// src/modules/ward.ts
|
|
7263
7286
|
import { readdir as readdir10, readFile as readFile14 } from "fs/promises";
|
|
7264
|
-
import { join as
|
|
7287
|
+
import { join as join17, relative as relative2 } from "path";
|
|
7265
7288
|
async function scanFileAllRules(absolutePath, displayPath, rules) {
|
|
7266
7289
|
const findings = [];
|
|
7267
7290
|
let content;
|
|
@@ -7324,7 +7347,7 @@ async function scanFilenames(root, rules, repoRoot = root) {
|
|
|
7324
7347
|
return findings;
|
|
7325
7348
|
}
|
|
7326
7349
|
for (const entry of entries) {
|
|
7327
|
-
const absolutePath =
|
|
7350
|
+
const absolutePath = join17(root, entry.name);
|
|
7328
7351
|
if (entry.isDirectory()) {
|
|
7329
7352
|
if (SKIP_DIRECTORIES.has(entry.name)) continue;
|
|
7330
7353
|
findings.push(...await scanFilenames(absolutePath, rules, repoRoot));
|
|
@@ -7439,7 +7462,7 @@ async function runWardCli(targetDir, options = {}, exclusions = []) {
|
|
|
7439
7462
|
}
|
|
7440
7463
|
|
|
7441
7464
|
// src/commands/unblock-push.ts
|
|
7442
|
-
import { basename as
|
|
7465
|
+
import { basename as basename3, resolve as resolve4 } from "path";
|
|
7443
7466
|
import { input as input4 } from "@inquirer/prompts";
|
|
7444
7467
|
async function runUnblockPush(targetDir) {
|
|
7445
7468
|
let manifest;
|
|
@@ -7471,7 +7494,7 @@ async function runUnblockPush(targetDir) {
|
|
|
7471
7494
|
process.exit(1);
|
|
7472
7495
|
return;
|
|
7473
7496
|
}
|
|
7474
|
-
const repoName =
|
|
7497
|
+
const repoName = basename3(resolve4(targetDir));
|
|
7475
7498
|
if (repoName === "") {
|
|
7476
7499
|
console.error(
|
|
7477
7500
|
"Could not determine a repository name to confirm against (is this repository at a filesystem root?). Refusing to unblock."
|
|
@@ -7760,10 +7783,10 @@ import * as client from "openid-client";
|
|
|
7760
7783
|
// src/modules/result-pages.ts
|
|
7761
7784
|
import { createHash as createHash3 } from "crypto";
|
|
7762
7785
|
import { readFile as readFile16 } from "fs/promises";
|
|
7763
|
-
import { join as
|
|
7786
|
+
import { join as join18 } from "path";
|
|
7764
7787
|
var RESULT_KINDS = ["success", "denied", "failure"];
|
|
7765
7788
|
function resultPagePath(kind) {
|
|
7766
|
-
return
|
|
7789
|
+
return join18("sign-in", `sign-in-callback-${kind}.html`);
|
|
7767
7790
|
}
|
|
7768
7791
|
function styleHashes(html) {
|
|
7769
7792
|
return [...html.matchAll(/<style\b[^>]*>([\s\S]*?)<\/style\s*>/gi)].map((match) => {
|
|
@@ -7811,7 +7834,7 @@ var PLAIN_RESULT_PAGES = {
|
|
|
7811
7834
|
async function loadResultPages(assetsDir) {
|
|
7812
7835
|
try {
|
|
7813
7836
|
const [success, denied, failure] = await Promise.all(
|
|
7814
|
-
RESULT_KINDS.map(async (kind) => resultPage(await readFile16(
|
|
7837
|
+
RESULT_KINDS.map(async (kind) => resultPage(await readFile16(join18(assetsDir, resultPagePath(kind)), "utf8")))
|
|
7815
7838
|
);
|
|
7816
7839
|
return { success, denied, failure };
|
|
7817
7840
|
} catch {
|
|
@@ -7889,9 +7912,9 @@ async function browserLogin(config, options) {
|
|
|
7889
7912
|
const state = client.randomState();
|
|
7890
7913
|
const nonce = client.randomNonce();
|
|
7891
7914
|
const server = createServer();
|
|
7892
|
-
await new Promise((
|
|
7915
|
+
await new Promise((resolve6, reject) => {
|
|
7893
7916
|
server.once("error", reject);
|
|
7894
|
-
server.listen(0, "127.0.0.1", () =>
|
|
7917
|
+
server.listen(0, "127.0.0.1", () => resolve6());
|
|
7895
7918
|
});
|
|
7896
7919
|
const address = server.address();
|
|
7897
7920
|
if (address === null || typeof address === "string") {
|
|
@@ -7917,7 +7940,7 @@ async function browserLogin(config, options) {
|
|
|
7917
7940
|
server.closeAllConnections?.();
|
|
7918
7941
|
server.close();
|
|
7919
7942
|
};
|
|
7920
|
-
const tokens = new Promise((
|
|
7943
|
+
const tokens = new Promise((resolve6, reject) => {
|
|
7921
7944
|
server.on("request", (req, res) => {
|
|
7922
7945
|
const url = new URL(req.url ?? "/", redirectUri);
|
|
7923
7946
|
if (RESULT_PATHS.has(url.pathname)) {
|
|
@@ -7928,7 +7951,7 @@ async function browserLogin(config, options) {
|
|
|
7928
7951
|
const served = browserResult;
|
|
7929
7952
|
const page = pages[served.kind];
|
|
7930
7953
|
const settle = () => {
|
|
7931
|
-
if (served.kind === "success")
|
|
7954
|
+
if (served.kind === "success") resolve6(served.result);
|
|
7932
7955
|
else reject(served.error);
|
|
7933
7956
|
};
|
|
7934
7957
|
res.once("finish", settle).once("close", settle);
|
|
@@ -7957,7 +7980,7 @@ async function browserLogin(config, options) {
|
|
|
7957
7980
|
}).end();
|
|
7958
7981
|
if (result.kind === "success") {
|
|
7959
7982
|
if (timer) clearTimeout(timer);
|
|
7960
|
-
timer = setTimeout(() =>
|
|
7983
|
+
timer = setTimeout(() => resolve6(result.result), Math.min(timeoutMs, 5e3));
|
|
7961
7984
|
}
|
|
7962
7985
|
};
|
|
7963
7986
|
const errorCode = url.searchParams.get("error");
|
|
@@ -8009,12 +8032,12 @@ async function refreshSession(config, refreshToken, scopes) {
|
|
|
8009
8032
|
}
|
|
8010
8033
|
function openInDefaultBrowser(url) {
|
|
8011
8034
|
const [command, args] = process.platform === "win32" ? ["rundll32", ["url.dll,FileProtocolHandler", url]] : process.platform === "darwin" ? ["open", [url]] : ["xdg-open", [url]];
|
|
8012
|
-
return new Promise((
|
|
8035
|
+
return new Promise((resolve6) => {
|
|
8013
8036
|
try {
|
|
8014
|
-
const child = execFile3(command, args, { windowsHide: true }, (error) =>
|
|
8015
|
-
child.on("error", () =>
|
|
8037
|
+
const child = execFile3(command, args, { windowsHide: true }, (error) => resolve6(!error));
|
|
8038
|
+
child.on("error", () => resolve6(false));
|
|
8016
8039
|
} catch {
|
|
8017
|
-
|
|
8040
|
+
resolve6(false);
|
|
8018
8041
|
}
|
|
8019
8042
|
});
|
|
8020
8043
|
}
|
|
@@ -8023,7 +8046,7 @@ function openInDefaultBrowser(url) {
|
|
|
8023
8046
|
import { execFileSync } from "child_process";
|
|
8024
8047
|
import { mkdirSync, readFileSync as readFileSync2, renameSync, rmSync, writeFileSync, existsSync as existsSync2 } from "fs";
|
|
8025
8048
|
import { userInfo } from "os";
|
|
8026
|
-
import { dirname as
|
|
8049
|
+
import { dirname as dirname11, join as join19 } from "path";
|
|
8027
8050
|
|
|
8028
8051
|
// src/modules/keychain-backend.ts
|
|
8029
8052
|
import { createRequire as createRequire2 } from "module";
|
|
@@ -8112,7 +8135,7 @@ var FileSecretBackend = class {
|
|
|
8112
8135
|
rmSync(this.filePath, { force: true });
|
|
8113
8136
|
return;
|
|
8114
8137
|
}
|
|
8115
|
-
mkdirSync(
|
|
8138
|
+
mkdirSync(dirname11(this.filePath), { recursive: true });
|
|
8116
8139
|
const tmp = `${this.filePath}.${process.pid}.tmp`;
|
|
8117
8140
|
writeFileSync(tmp, JSON.stringify(entries), { encoding: "utf8", mode: 384 });
|
|
8118
8141
|
try {
|
|
@@ -8157,7 +8180,7 @@ function restrictToCurrentUser(filePath) {
|
|
|
8157
8180
|
});
|
|
8158
8181
|
}
|
|
8159
8182
|
function insecureSessionFilePath(homeDir) {
|
|
8160
|
-
return
|
|
8183
|
+
return join19(userTierRoot(homeDir), INSECURE_SESSION_FILE_NAME);
|
|
8161
8184
|
}
|
|
8162
8185
|
function createSessionStorage(options = {}) {
|
|
8163
8186
|
const keychain = new ChunkedSecretStore(options.keychainBackend ?? createKeychainBackend());
|
|
@@ -8425,15 +8448,15 @@ async function runWhoami(options, deps = defaultWhoamiDeps()) {
|
|
|
8425
8448
|
|
|
8426
8449
|
// src/commands/report.ts
|
|
8427
8450
|
import { access as access5 } from "fs/promises";
|
|
8428
|
-
import { join as
|
|
8451
|
+
import { join as join22 } from "path";
|
|
8429
8452
|
|
|
8430
8453
|
// src/modules/show-report/generate.ts
|
|
8431
8454
|
import { mkdir as mkdir8, readdir as readdir11, readFile as readFile18, writeFile as writeFile12 } from "fs/promises";
|
|
8432
|
-
import { basename as
|
|
8455
|
+
import { basename as basename4, dirname as dirname12, isAbsolute, join as join21, relative as relative3, resolve as resolve5 } from "path";
|
|
8433
8456
|
|
|
8434
8457
|
// src/modules/show-report/model.ts
|
|
8435
8458
|
import { readFile as readFile17 } from "fs/promises";
|
|
8436
|
-
import { join as
|
|
8459
|
+
import { join as join20 } from "path";
|
|
8437
8460
|
|
|
8438
8461
|
// src/modules/show-report/plan-parser.ts
|
|
8439
8462
|
function parseFrontmatter(content) {
|
|
@@ -8802,11 +8825,11 @@ function buildCorrections(ledgerRows, programName) {
|
|
|
8802
8825
|
};
|
|
8803
8826
|
}
|
|
8804
8827
|
async function buildShowReportModel(options) {
|
|
8805
|
-
const planAbsPath =
|
|
8806
|
-
const queueAbsPath =
|
|
8828
|
+
const planAbsPath = join20(options.rootDir, options.planRelPath);
|
|
8829
|
+
const queueAbsPath = join20(options.rootDir, options.queueRelPath);
|
|
8807
8830
|
const ledgerRelPath = "docs/verification-ledger.md";
|
|
8808
|
-
const ledgerAbsPath =
|
|
8809
|
-
const decisionsAbsPath =
|
|
8831
|
+
const ledgerAbsPath = join20(options.rootDir, ledgerRelPath);
|
|
8832
|
+
const decisionsAbsPath = join20(options.rootDir, "DECISIONS.md");
|
|
8810
8833
|
const [planContent, queueContent, ledgerContent, decisionsContent] = await Promise.all([
|
|
8811
8834
|
readFile17(planAbsPath, "utf8"),
|
|
8812
8835
|
readOptional(queueAbsPath),
|
|
@@ -9004,7 +9027,7 @@ function toPosix(path4) {
|
|
|
9004
9027
|
async function programFromPlan(rootDir, slug, planRelPath) {
|
|
9005
9028
|
let planContent;
|
|
9006
9029
|
try {
|
|
9007
|
-
planContent = await readFile18(
|
|
9030
|
+
planContent = await readFile18(join21(rootDir, planRelPath), "utf8");
|
|
9008
9031
|
} catch {
|
|
9009
9032
|
return { skipped: { slug, reason: `no PLAN.md at ${planRelPath}` } };
|
|
9010
9033
|
}
|
|
@@ -9016,13 +9039,13 @@ async function programFromPlan(rootDir, slug, planRelPath) {
|
|
|
9016
9039
|
program: {
|
|
9017
9040
|
slug,
|
|
9018
9041
|
planRelPath,
|
|
9019
|
-
queueRelPath: toPosix(
|
|
9042
|
+
queueRelPath: toPosix(join21(dirname12(planRelPath), "OPERATOR-QUEUE.md")),
|
|
9020
9043
|
ledgerProgramName: programNameFromTitle(frontmatter.title ?? slug)
|
|
9021
9044
|
}
|
|
9022
9045
|
};
|
|
9023
9046
|
}
|
|
9024
9047
|
async function discoverPrograms(rootDir) {
|
|
9025
|
-
const plansDir =
|
|
9048
|
+
const plansDir = join21(rootDir, "docs", "plans");
|
|
9026
9049
|
let entries;
|
|
9027
9050
|
try {
|
|
9028
9051
|
entries = (await readdir11(plansDir, { withFileTypes: true })).filter((e) => e.isDirectory());
|
|
@@ -9040,9 +9063,9 @@ async function discoverPrograms(rootDir) {
|
|
|
9040
9063
|
return { programs, skipped };
|
|
9041
9064
|
}
|
|
9042
9065
|
async function programFromPlanPath(rootDir, planPath) {
|
|
9043
|
-
const planAbs = isAbsolute(planPath) ? planPath :
|
|
9066
|
+
const planAbs = isAbsolute(planPath) ? planPath : resolve5(rootDir, planPath);
|
|
9044
9067
|
const planRelPath = toPosix(relative3(rootDir, planAbs));
|
|
9045
|
-
return programFromPlan(rootDir,
|
|
9068
|
+
return programFromPlan(rootDir, basename4(dirname12(planAbs)), planRelPath);
|
|
9046
9069
|
}
|
|
9047
9070
|
function normalizeLineEndings2(content) {
|
|
9048
9071
|
return content.replace(/\r\n?/g, "\n");
|
|
@@ -9056,7 +9079,7 @@ async function readOptionalFile(path4) {
|
|
|
9056
9079
|
}
|
|
9057
9080
|
}
|
|
9058
9081
|
async function generateReport(rootDir, program2, templateHtml, theme = "auto") {
|
|
9059
|
-
const planContent = await readFile18(
|
|
9082
|
+
const planContent = await readFile18(join21(rootDir, program2.planRelPath), "utf8");
|
|
9060
9083
|
const frontmatter = parseFrontmatter(planContent);
|
|
9061
9084
|
const model = await buildShowReportModel({
|
|
9062
9085
|
rootDir,
|
|
@@ -9087,19 +9110,19 @@ async function runReportGeneration(options) {
|
|
|
9087
9110
|
if (!generated.themeApplied) themeSkipped.push(program2.slug);
|
|
9088
9111
|
const missing = JSON.parse(generated.json).sections.flatMap((section) => section.rows).filter((row) => row.descriptionState === "unwritten").map((row) => row.id);
|
|
9089
9112
|
if (missing.length > 0) unwritten.push({ slug: program2.slug, ids: missing });
|
|
9090
|
-
const outAbs = options.outDir ? isAbsolute(options.outDir) ? options.outDir :
|
|
9113
|
+
const outAbs = options.outDir ? isAbsolute(options.outDir) ? options.outDir : resolve5(options.rootDir, options.outDir) : resolve5(options.rootDir, dirname12(program2.planRelPath));
|
|
9091
9114
|
const outRel = toPosix(relative3(options.rootDir, outAbs));
|
|
9092
9115
|
const outputs = [
|
|
9093
9116
|
[`${outRel}/show-report.json`, generated.json],
|
|
9094
9117
|
[`${outRel}/show-report.html`, generated.html]
|
|
9095
9118
|
];
|
|
9096
9119
|
for (const [relPath, expected] of outputs) {
|
|
9097
|
-
const actual = await readOptionalFile(
|
|
9120
|
+
const actual = await readOptionalFile(join21(options.rootDir, relPath));
|
|
9098
9121
|
if (actual === null || normalizeLineEndings2(actual) !== normalizeLineEndings2(expected)) {
|
|
9099
9122
|
drifted.push(`${relPath} does not match a regeneration from its sources.`);
|
|
9100
9123
|
if (options.mode === "write") {
|
|
9101
9124
|
await mkdir8(outAbs, { recursive: true });
|
|
9102
|
-
await writeFile12(
|
|
9125
|
+
await writeFile12(join21(options.rootDir, relPath), expected, "utf8");
|
|
9103
9126
|
repaired.push(relPath);
|
|
9104
9127
|
}
|
|
9105
9128
|
}
|
|
@@ -9110,7 +9133,7 @@ async function runReportGeneration(options) {
|
|
|
9110
9133
|
|
|
9111
9134
|
// src/commands/report.ts
|
|
9112
9135
|
async function runReport(targetDir, options = {}, assetsDir) {
|
|
9113
|
-
const templatePath =
|
|
9136
|
+
const templatePath = join22(assetsDir, "report", "show-report.template.html");
|
|
9114
9137
|
try {
|
|
9115
9138
|
await access5(templatePath);
|
|
9116
9139
|
} catch {
|
|
@@ -9184,10 +9207,10 @@ async function runReport(targetDir, options = {}, assetsDir) {
|
|
|
9184
9207
|
}
|
|
9185
9208
|
|
|
9186
9209
|
// src/index.ts
|
|
9187
|
-
import { realpathSync } from "fs";
|
|
9210
|
+
import { realpathSync as realpathSync2 } from "fs";
|
|
9188
9211
|
var require3 = createRequire3(import.meta.url);
|
|
9189
9212
|
var pkg = require3("../package.json");
|
|
9190
|
-
var ASSETS_DIR = process.env["ARCANE_ASSETS_DIR"] ??
|
|
9213
|
+
var ASSETS_DIR = process.env["ARCANE_ASSETS_DIR"] ?? join23(dirname13(fileURLToPath(import.meta.url)), "assets");
|
|
9191
9214
|
var program = new Command();
|
|
9192
9215
|
var USER_FLAG_DESCRIPTION = "Operate on the per-user tier at ~/.arcane \u2014 spells shared by every repository on this machine, fanned out to ~/.agents/skills (Codex, Copilot) and ~/.claude/commands (Claude Code)";
|
|
9193
9216
|
var USER_TIER_AGENTS_DESCRIPTION = "Operate on the per-user tier at ~/.arcane \u2014 one agent roster shared by every repository on this machine, rendered to ~/.copilot/agents (VS Code agent modes)";
|
|
@@ -9397,7 +9420,7 @@ agentsCommand.command("list").description("Show the current agent roster with as
|
|
|
9397
9420
|
const scope = opts.user ? "user" : "repo";
|
|
9398
9421
|
await runAgentsList(agentsTargetDir(scope, process.cwd()), scope);
|
|
9399
9422
|
});
|
|
9400
|
-
if (
|
|
9423
|
+
if (realpathSync2(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
9401
9424
|
await program.parseAsync(process.argv);
|
|
9402
9425
|
}
|
|
9403
9426
|
export {
|