arcane-cli 0.16.1 → 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.
|
@@ -220,6 +220,13 @@ Create the session branch as the **first action** of any session, before any fil
|
|
|
220
220
|
1. `git branch -m <old> <new>`
|
|
221
221
|
2. `git push -u origin <new>`
|
|
222
222
|
3. `git push origin --delete <old>` (skip this if an active PR still depends on `<old>`).
|
|
223
|
+
- **EF-20 hazard:** on some Windows/Git-for-Windows filesystems, a rename or delete can trigger
|
|
224
|
+
an interactive retry prompt (e.g. a file lock from an editor or antivirus scan) that blocks
|
|
225
|
+
indefinitely on a terminal with no non-interactive fallback. When running these commands
|
|
226
|
+
directly through a tool that supports a per-call timeout (an agent's shell tool, a CI step),
|
|
227
|
+
set one rather than assuming the command will return. Arcane's own TypeScript Git helper
|
|
228
|
+
(`src/modules/git.ts`) applies this as a standing contract — closed stdin plus a
|
|
229
|
+
command-class timeout — for every `git` invocation the CLI itself makes.
|
|
223
230
|
|
|
224
231
|
**Agents:**
|
|
225
232
|
|
package/dist/index.js
CHANGED
|
@@ -1599,15 +1599,140 @@ async function runAgentsList(targetDir) {
|
|
|
1599
1599
|
|
|
1600
1600
|
// src/modules/git.ts
|
|
1601
1601
|
import { execFile } from "child_process";
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
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(
|
|
1607
1695
|
"git",
|
|
1608
|
-
|
|
1609
|
-
{
|
|
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
|
+
}
|
|
1610
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"]);
|
|
1611
1736
|
if (stdout2.trim() !== "true") {
|
|
1612
1737
|
return { status: "not-repository" };
|
|
1613
1738
|
}
|
|
@@ -1615,15 +1740,11 @@ async function inspectGitRepository(cwd) {
|
|
|
1615
1740
|
return { status: "not-repository" };
|
|
1616
1741
|
}
|
|
1617
1742
|
try {
|
|
1618
|
-
await
|
|
1743
|
+
await runGit(cwd, ["rev-parse", "--verify", "HEAD"]);
|
|
1619
1744
|
} catch {
|
|
1620
1745
|
return { status: "no-commits" };
|
|
1621
1746
|
}
|
|
1622
|
-
const { stdout } = await
|
|
1623
|
-
"git",
|
|
1624
|
-
["status", "--porcelain", "-uall"],
|
|
1625
|
-
{ cwd }
|
|
1626
|
-
);
|
|
1747
|
+
const { stdout } = await runGit(cwd, ["status", "--porcelain", "-uall"]);
|
|
1627
1748
|
const lines = stdout.trim();
|
|
1628
1749
|
return {
|
|
1629
1750
|
status: "ready",
|
|
@@ -1632,7 +1753,7 @@ async function inspectGitRepository(cwd) {
|
|
|
1632
1753
|
}
|
|
1633
1754
|
async function countUncommittedChanges(cwd) {
|
|
1634
1755
|
try {
|
|
1635
|
-
const { stdout } = await
|
|
1756
|
+
const { stdout } = await runGit(cwd, ["status", "--porcelain", "-uall"]);
|
|
1636
1757
|
const lines = stdout.trim();
|
|
1637
1758
|
return lines.length === 0 ? 0 : lines.split("\n").length;
|
|
1638
1759
|
} catch {
|
|
@@ -2391,7 +2512,7 @@ async function runUninstall(options, targetDir) {
|
|
|
2391
2512
|
import { access as access3, readFile as readFile5, mkdir as mkdir5, copyFile as fsCopyFile2 } from "fs/promises";
|
|
2392
2513
|
import { join as join10, dirname as dirname2 } from "path";
|
|
2393
2514
|
import { execFile as execFile2 } from "child_process";
|
|
2394
|
-
import { promisify
|
|
2515
|
+
import { promisify } from "util";
|
|
2395
2516
|
|
|
2396
2517
|
// src/config/incidents.ts
|
|
2397
2518
|
var INCIDENT_QUEUE = [
|
|
@@ -2459,7 +2580,7 @@ function evaluateIncidentGate(records) {
|
|
|
2459
2580
|
}
|
|
2460
2581
|
|
|
2461
2582
|
// src/commands/doctor.ts
|
|
2462
|
-
var
|
|
2583
|
+
var execFileAsync = promisify(execFile2);
|
|
2463
2584
|
async function checkNodeVersion() {
|
|
2464
2585
|
const name = "Node.js version";
|
|
2465
2586
|
const raw = process.version;
|
|
@@ -2497,7 +2618,7 @@ async function checkVSCodeExtension(id, label) {
|
|
|
2497
2618
|
let installBin = "code";
|
|
2498
2619
|
for (const bin of ["code", "code-insiders"]) {
|
|
2499
2620
|
try {
|
|
2500
|
-
await
|
|
2621
|
+
await execFileAsync(bin, ["--version"]);
|
|
2501
2622
|
installBin = bin;
|
|
2502
2623
|
break;
|
|
2503
2624
|
} catch {
|