@oratis/lisa 0.5.0 → 0.8.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 +5 -0
- package/dist/autostart/install.d.ts +28 -0
- package/dist/autostart/install.d.ts.map +1 -0
- package/dist/autostart/install.js +180 -0
- package/dist/autostart/install.js.map +1 -0
- package/dist/cli.js +49 -1
- package/dist/cli.js.map +1 -1
- package/dist/heartbeat/install.d.ts.map +1 -1
- package/dist/heartbeat/install.js +1 -43
- package/dist/heartbeat/install.js.map +1 -1
- package/dist/heartbeat/runner.d.ts.map +1 -1
- package/dist/heartbeat/runner.js +11 -0
- package/dist/heartbeat/runner.js.map +1 -1
- package/dist/idle/runner.d.ts +12 -0
- package/dist/idle/runner.d.ts.map +1 -1
- package/dist/idle/runner.js +33 -2
- package/dist/idle/runner.js.map +1 -1
- package/dist/integrations/aider/observer.d.ts +48 -0
- package/dist/integrations/aider/observer.d.ts.map +1 -0
- package/dist/integrations/aider/observer.js +196 -0
- package/dist/integrations/aider/observer.js.map +1 -0
- package/dist/integrations/comparisons.d.ts +23 -0
- package/dist/integrations/comparisons.d.ts.map +1 -0
- package/dist/integrations/comparisons.js +47 -0
- package/dist/integrations/comparisons.js.map +1 -0
- package/dist/integrations/dispatch-ledger.d.ts +51 -0
- package/dist/integrations/dispatch-ledger.d.ts.map +1 -0
- package/dist/integrations/dispatch-ledger.js +106 -0
- package/dist/integrations/dispatch-ledger.js.map +1 -0
- package/dist/integrations/github-pr/observer.d.ts +85 -0
- package/dist/integrations/github-pr/observer.d.ts.map +1 -0
- package/dist/integrations/github-pr/observer.js +261 -0
- package/dist/integrations/github-pr/observer.js.map +1 -0
- package/dist/integrations/hub.d.ts.map +1 -1
- package/dist/integrations/hub.js +10 -0
- package/dist/integrations/hub.js.map +1 -1
- package/dist/integrations/opencode/observer.d.ts +74 -0
- package/dist/integrations/opencode/observer.d.ts.map +1 -0
- package/dist/integrations/opencode/observer.js +207 -0
- package/dist/integrations/opencode/observer.js.map +1 -0
- package/dist/integrations/registry.d.ts.map +1 -1
- package/dist/integrations/registry.js +3 -1
- package/dist/integrations/registry.js.map +1 -1
- package/dist/integrations/scheduled-dispatch.d.ts +42 -0
- package/dist/integrations/scheduled-dispatch.d.ts.map +1 -0
- package/dist/integrations/scheduled-dispatch.js +140 -0
- package/dist/integrations/scheduled-dispatch.js.map +1 -0
- package/dist/launchd.d.ts +18 -0
- package/dist/launchd.d.ts.map +1 -0
- package/dist/launchd.js +60 -0
- package/dist/launchd.js.map +1 -0
- package/dist/orchestrator/journal.d.ts +46 -0
- package/dist/orchestrator/journal.d.ts.map +1 -0
- package/dist/orchestrator/journal.js +82 -0
- package/dist/orchestrator/journal.js.map +1 -0
- package/dist/orchestrator/recap.d.ts +38 -0
- package/dist/orchestrator/recap.d.ts.map +1 -0
- package/dist/orchestrator/recap.js +134 -0
- package/dist/orchestrator/recap.js.map +1 -0
- package/dist/screen_advisor/engine.d.ts +79 -0
- package/dist/screen_advisor/engine.d.ts.map +1 -0
- package/dist/screen_advisor/engine.js +128 -0
- package/dist/screen_advisor/engine.js.map +1 -0
- package/dist/soul/birth.d.ts.map +1 -1
- package/dist/soul/birth.js +8 -0
- package/dist/soul/birth.js.map +1 -1
- package/dist/soul/lock.d.ts +7 -4
- package/dist/soul/lock.d.ts.map +1 -1
- package/dist/soul/lock.js +33 -9
- package/dist/soul/lock.js.map +1 -1
- package/dist/tools/agent_recap.d.ts +21 -0
- package/dist/tools/agent_recap.d.ts.map +1 -0
- package/dist/tools/agent_recap.js +43 -0
- package/dist/tools/agent_recap.js.map +1 -0
- package/dist/tools/compare_agents.d.ts +16 -0
- package/dist/tools/compare_agents.d.ts.map +1 -0
- package/dist/tools/compare_agents.js +145 -0
- package/dist/tools/compare_agents.js.map +1 -0
- package/dist/tools/dispatch_agent.d.ts +13 -0
- package/dist/tools/dispatch_agent.d.ts.map +1 -1
- package/dist/tools/dispatch_agent.js +49 -37
- package/dist/tools/dispatch_agent.js.map +1 -1
- package/dist/tools/exec-util.d.ts +18 -0
- package/dist/tools/exec-util.d.ts.map +1 -0
- package/dist/tools/exec-util.js +72 -0
- package/dist/tools/exec-util.js.map +1 -0
- package/dist/tools/inspect_agent.d.ts +21 -0
- package/dist/tools/inspect_agent.d.ts.map +1 -0
- package/dist/tools/inspect_agent.js +87 -0
- package/dist/tools/inspect_agent.js.map +1 -0
- package/dist/tools/list_agents.d.ts +21 -0
- package/dist/tools/list_agents.d.ts.map +1 -0
- package/dist/tools/list_agents.js +88 -0
- package/dist/tools/list_agents.js.map +1 -0
- package/dist/tools/pr_status.d.ts +31 -0
- package/dist/tools/pr_status.d.ts.map +1 -0
- package/dist/tools/pr_status.js +90 -0
- package/dist/tools/pr_status.js.map +1 -0
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.js +26 -0
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/repo_digest.d.ts +33 -0
- package/dist/tools/repo_digest.d.ts.map +1 -0
- package/dist/tools/repo_digest.js +113 -0
- package/dist/tools/repo_digest.js.map +1 -0
- package/dist/tools/review_diff.d.ts +28 -0
- package/dist/tools/review_diff.d.ts.map +1 -0
- package/dist/tools/review_diff.js +68 -0
- package/dist/tools/review_diff.js.map +1 -0
- package/dist/tools/run_checks.d.ts +15 -0
- package/dist/tools/run_checks.d.ts.map +1 -0
- package/dist/tools/run_checks.js +117 -0
- package/dist/tools/run_checks.js.map +1 -0
- package/dist/tools/scheduled_dispatch.d.ts +27 -0
- package/dist/tools/scheduled_dispatch.d.ts.map +1 -0
- package/dist/tools/scheduled_dispatch.js +57 -0
- package/dist/tools/scheduled_dispatch.js.map +1 -0
- package/dist/tools/signal_agent.d.ts +36 -0
- package/dist/tools/signal_agent.d.ts.map +1 -0
- package/dist/tools/signal_agent.js +140 -0
- package/dist/tools/signal_agent.js.map +1 -0
- package/dist/web/island.d.ts +1 -1
- package/dist/web/island.d.ts.map +1 -1
- package/dist/web/island.js +139 -27
- package/dist/web/island.js.map +1 -1
- package/dist/web/lisa-html.d.ts +1 -1
- package/dist/web/lisa-html.d.ts.map +1 -1
- package/dist/web/lisa-html.js +132 -4
- package/dist/web/lisa-html.js.map +1 -1
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +190 -0
- package/dist/web/server.js.map +1 -1
- package/package.json +1 -1
package/dist/soul/lock.js
CHANGED
|
@@ -9,10 +9,13 @@
|
|
|
9
9
|
* - lose a desire-progress append (read-modify-write race), or
|
|
10
10
|
* - collide on .git/index.lock (one commit fails).
|
|
11
11
|
*
|
|
12
|
-
* withFileLock implements a portable advisory mutex
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
12
|
+
* withFileLock implements a portable advisory mutex by writing the lock body to
|
|
13
|
+
* a private temp file and then `link()`-ing it into place — link() fails if the
|
|
14
|
+
* target exists, so the lock is created exclusively AND already holds its full
|
|
15
|
+
* content (no window where a contender can read a half-written file and mistake
|
|
16
|
+
* it for stale). It self-heals from a crashed holder via a staleness timeout +
|
|
17
|
+
* a best-effort liveness check on the recorded pid, stealing atomically by
|
|
18
|
+
* renaming the stale file away so racing contenders can't clobber each other.
|
|
16
19
|
*/
|
|
17
20
|
import fsp from "node:fs/promises";
|
|
18
21
|
import path from "node:path";
|
|
@@ -28,7 +31,13 @@ async function isStale(lockPath, staleMs) {
|
|
|
28
31
|
try {
|
|
29
32
|
body = JSON.parse(await fsp.readFile(lockPath, "utf8"));
|
|
30
33
|
}
|
|
31
|
-
catch {
|
|
34
|
+
catch (e) {
|
|
35
|
+
// The file vanished between the failed create and our read — the holder
|
|
36
|
+
// released it. There's nothing to steal; let the caller retry the create
|
|
37
|
+
// race. (Treating this as "stale" would race a concurrent acquirer into an
|
|
38
|
+
// unconditional rm that could delete a *different* holder's fresh lock.)
|
|
39
|
+
if (e.code === "ENOENT")
|
|
40
|
+
return false;
|
|
32
41
|
// Unreadable / malformed lock file → treat as stale so we can recover.
|
|
33
42
|
return true;
|
|
34
43
|
}
|
|
@@ -58,22 +67,37 @@ export async function withFileLock(lockPath, fn, opts = {}) {
|
|
|
58
67
|
const deadline = Date.now() + timeoutMs;
|
|
59
68
|
// ── acquire ──
|
|
60
69
|
for (;;) {
|
|
70
|
+
// Write the lock body to a private temp file first, then atomically link it
|
|
71
|
+
// into place. link() fails with EEXIST if the lock already exists, so
|
|
72
|
+
// exclusive creation and "the lock file always has complete content" hold
|
|
73
|
+
// simultaneously — a contender can never observe a half-written lock and
|
|
74
|
+
// mistake it for malformed/stale. The temp name is unique per attempt so
|
|
75
|
+
// concurrent acquirers in the *same* process don't clobber each other.
|
|
76
|
+
const tmp = `${lockPath}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
61
77
|
try {
|
|
62
|
-
|
|
78
|
+
await fsp.writeFile(tmp, JSON.stringify({ pid: process.pid, ts: Date.now() }));
|
|
63
79
|
try {
|
|
64
|
-
await
|
|
80
|
+
await fsp.link(tmp, lockPath);
|
|
65
81
|
}
|
|
66
82
|
finally {
|
|
67
|
-
await
|
|
83
|
+
await fsp.rm(tmp, { force: true }).catch(() => { });
|
|
68
84
|
}
|
|
69
85
|
break; // acquired
|
|
70
86
|
}
|
|
71
87
|
catch (e) {
|
|
88
|
+
await fsp.rm(tmp, { force: true }).catch(() => { });
|
|
72
89
|
if (e.code !== "EEXIST")
|
|
73
90
|
throw e;
|
|
74
91
|
// Held by someone. Steal if stale, else wait.
|
|
75
92
|
if (await isStale(lockPath, staleMs)) {
|
|
76
|
-
|
|
93
|
+
// Steal atomically: rename the stale file to a unique name so only one
|
|
94
|
+
// contender can claim it. Losers get ENOENT and just retry the link
|
|
95
|
+
// race — nobody unconditionally rm's a path that may already hold a
|
|
96
|
+
// fresh lock from a concurrent acquirer.
|
|
97
|
+
await fsp
|
|
98
|
+
.rename(lockPath, tmp)
|
|
99
|
+
.then(() => fsp.rm(tmp, { force: true }))
|
|
100
|
+
.catch(() => { });
|
|
77
101
|
continue; // retry immediately
|
|
78
102
|
}
|
|
79
103
|
if (Date.now() >= deadline) {
|
package/dist/soul/lock.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/soul/lock.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/soul/lock.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agent_recap (L6) — the cross-agent "while you were away" digest.
|
|
3
|
+
*
|
|
4
|
+
* Answers "what happened across all my agents since I left?" by synthesizing
|
|
5
|
+
* the orchestrator journal (every observed session's state transitions over
|
|
6
|
+
* time) into a readable, project-grouped recap: who ran where, what finished,
|
|
7
|
+
* what errored, what's still going. Complements advise_now (relevance-gated
|
|
8
|
+
* alerts, now) and list_agents (the current roster) with the temporal story —
|
|
9
|
+
* including sessions that have since ended and dropped out of the live list.
|
|
10
|
+
*
|
|
11
|
+
* Structural metadata only (agent, project, state, tool/file names, errors) —
|
|
12
|
+
* never prompts, replies, or file contents. Read-only.
|
|
13
|
+
*/
|
|
14
|
+
import type { ToolDefinition } from "../types.js";
|
|
15
|
+
interface RecapInput {
|
|
16
|
+
/** Look back this many minutes (default 120, clamped 1–1440). */
|
|
17
|
+
sinceMinutes?: number;
|
|
18
|
+
}
|
|
19
|
+
export declare const agentRecapTool: ToolDefinition<RecapInput, string>;
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=agent_recap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent_recap.d.ts","sourceRoot":"","sources":["../../src/tools/agent_recap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAIlD,UAAU,UAAU;IAClB,iEAAiE;IACjE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,cAAc,EAAE,cAAc,CAAC,UAAU,EAAE,MAAM,CA2B7D,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agent_recap (L6) — the cross-agent "while you were away" digest.
|
|
3
|
+
*
|
|
4
|
+
* Answers "what happened across all my agents since I left?" by synthesizing
|
|
5
|
+
* the orchestrator journal (every observed session's state transitions over
|
|
6
|
+
* time) into a readable, project-grouped recap: who ran where, what finished,
|
|
7
|
+
* what errored, what's still going. Complements advise_now (relevance-gated
|
|
8
|
+
* alerts, now) and list_agents (the current roster) with the temporal story —
|
|
9
|
+
* including sessions that have since ended and dropped out of the live list.
|
|
10
|
+
*
|
|
11
|
+
* Structural metadata only (agent, project, state, tool/file names, errors) —
|
|
12
|
+
* never prompts, replies, or file contents. Read-only.
|
|
13
|
+
*/
|
|
14
|
+
import { eventsSince } from "../orchestrator/journal.js";
|
|
15
|
+
import { buildRecap, formatRecap } from "../orchestrator/recap.js";
|
|
16
|
+
export const agentRecapTool = {
|
|
17
|
+
name: "agent_recap",
|
|
18
|
+
description: "Summarize what happened across ALL the coding agents LISA observes (Claude Code, " +
|
|
19
|
+
"Codex, OpenCode, Aider, GitHub PRs) over a recent time window — the cross-agent " +
|
|
20
|
+
'"while you were away" recap. Use when the user asks what their agents did, what ' +
|
|
21
|
+
"happened since they were gone, or for an end-of-session wrap-up. Groups by project " +
|
|
22
|
+
"and reports what finished, errored, or is still running. Structural metadata only — " +
|
|
23
|
+
"never conversation content. Read-only.",
|
|
24
|
+
inputSchema: {
|
|
25
|
+
type: "object",
|
|
26
|
+
properties: {
|
|
27
|
+
sinceMinutes: {
|
|
28
|
+
type: "number",
|
|
29
|
+
description: "Window to look back over, in minutes (default 120; max 1440).",
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
additionalProperties: false,
|
|
33
|
+
},
|
|
34
|
+
async execute(input) {
|
|
35
|
+
const mins = Math.max(1, Math.min(1440, Math.round(input.sinceMinutes ?? 120)));
|
|
36
|
+
const now = Date.now();
|
|
37
|
+
const sinceMs = now - mins * 60_000;
|
|
38
|
+
const recap = buildRecap(eventsSince(sinceMs), sinceMs, now);
|
|
39
|
+
const window = mins >= 60 ? `${Math.round(mins / 60)}h` : `${mins}m`;
|
|
40
|
+
return `Cross-agent recap (last ${window}):\n${formatRecap(recap)}`;
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=agent_recap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent_recap.js","sourceRoot":"","sources":["../../src/tools/agent_recap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAOnE,MAAM,CAAC,MAAM,cAAc,GAAuC;IAChE,IAAI,EAAE,aAAa;IACnB,WAAW,EACT,mFAAmF;QACnF,kFAAkF;QAClF,kFAAkF;QAClF,qFAAqF;QACrF,sFAAsF;QACtF,wCAAwC;IAC1C,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,YAAY,EAAE;gBACZ,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,+DAA+D;aAC7E;SACF;QACD,oBAAoB,EAAE,KAAK;KAC5B;IACD,KAAK,CAAC,OAAO,CAAC,KAAK;QACjB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,YAAY,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC;QAChF,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,OAAO,GAAG,GAAG,GAAG,IAAI,GAAG,MAAM,CAAC;QACpC,MAAM,KAAK,GAAG,UAAU,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;QAC7D,MAAM,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC;QACrE,OAAO,2BAA2B,MAAM,OAAO,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;IACtE,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ToolDefinition } from "../types.js";
|
|
2
|
+
import { type ComparisonJob, type ComparisonEntry, type CompareAgentKind } from "../integrations/comparisons.js";
|
|
3
|
+
interface CompareInput {
|
|
4
|
+
action?: "start" | "status" | "collect" | "cleanup" | "list";
|
|
5
|
+
task?: string;
|
|
6
|
+
agents?: CompareAgentKind[];
|
|
7
|
+
cwd?: string;
|
|
8
|
+
id?: string;
|
|
9
|
+
}
|
|
10
|
+
/** Pure: one line summarising an entry's live status. Exported for tests. */
|
|
11
|
+
export declare function formatStatusLine(e: ComparisonEntry, state: string | null, changed: number): string;
|
|
12
|
+
/** Pure: header line for a job. Exported for tests. */
|
|
13
|
+
export declare function formatJobHeader(j: ComparisonJob): string;
|
|
14
|
+
export declare const compareAgentsTool: ToolDefinition<CompareInput, string>;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=compare_agents.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compare_agents.d.ts","sourceRoot":"","sources":["../../src/tools/compare_agents.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAIlD,OAAO,EAOL,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACtB,MAAM,gCAAgC,CAAC;AAExC,UAAU,YAAY;IACpB,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;IAC7D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,6EAA6E;AAC7E,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAIlG;AAED,uDAAuD;AACvD,wBAAgB,eAAe,CAAC,CAAC,EAAE,aAAa,GAAG,MAAM,CAGxD;AAOD,eAAO,MAAM,iBAAiB,EAAE,cAAc,CAAC,YAAY,EAAE,MAAM,CA8GlE,CAAC"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* compare_agents (vibe-coding, workflow form) — run the SAME task across two or
|
|
3
|
+
* more agents and compare what each produced, so you can pick the best result.
|
|
4
|
+
*
|
|
5
|
+
* It's a workflow, not a one-shot, because agents run for minutes:
|
|
6
|
+
* start → make an isolated git worktree per agent (off the repo, so they
|
|
7
|
+
* don't clobber each other), launch each agent on the task, record
|
|
8
|
+
* the job. Returns immediately.
|
|
9
|
+
* status → for each agent: live session state + how many files it has changed.
|
|
10
|
+
* collect → diff stat per agent's worktree, side by side, so you choose.
|
|
11
|
+
* cleanup → remove the worktrees + scratch branches once you've picked.
|
|
12
|
+
* list → existing comparison jobs.
|
|
13
|
+
*/
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
import { mkdir } from "node:fs/promises";
|
|
16
|
+
import { getCurrentHub } from "../integrations/current-hub.js";
|
|
17
|
+
import { launchAgent } from "./dispatch_agent.js";
|
|
18
|
+
import { runIn, gitRoot } from "./exec-util.js";
|
|
19
|
+
import { loadComparisons, addComparison, getComparison, removeComparison, newJobId, COMPARE_ROOT, } from "../integrations/comparisons.js";
|
|
20
|
+
/** Pure: one line summarising an entry's live status. Exported for tests. */
|
|
21
|
+
export function formatStatusLine(e, state, changed) {
|
|
22
|
+
if (e.launchError)
|
|
23
|
+
return ` ${e.agent}: failed to launch — ${e.launchError}`;
|
|
24
|
+
const st = state ?? "no session yet";
|
|
25
|
+
return ` ${e.agent}: ${st} · ${changed} file(s) changed [${e.branch}]`;
|
|
26
|
+
}
|
|
27
|
+
/** Pure: header line for a job. Exported for tests. */
|
|
28
|
+
export function formatJobHeader(j) {
|
|
29
|
+
const task = j.task.length > 70 ? j.task.slice(0, 67) + "…" : j.task;
|
|
30
|
+
return `compare ${j.id} (${j.entries.length} agents) in ${j.repo}\n task: "${task}"`;
|
|
31
|
+
}
|
|
32
|
+
async function changedCount(worktree, signal) {
|
|
33
|
+
const r = await runIn(worktree, "git", ["-C", worktree, "status", "--porcelain"], { timeoutMs: 8000, signal });
|
|
34
|
+
return r.code === 0 ? r.stdout.split("\n").filter((l) => l.trim()).length : 0;
|
|
35
|
+
}
|
|
36
|
+
export const compareAgentsTool = {
|
|
37
|
+
name: "compare_agents",
|
|
38
|
+
description: "Run the same task across multiple agents in isolated git worktrees and compare results — a " +
|
|
39
|
+
"workflow (agents run for minutes): action:'start' (task + agents, default [claude,codex]) launches " +
|
|
40
|
+
"them; 'status' (id) shows each one's live state + files changed; 'collect' (id) diffs each result " +
|
|
41
|
+
"side by side so you pick a winner; 'cleanup' (id) removes the worktrees; 'list' shows jobs. Use when " +
|
|
42
|
+
"the user wants to try an approach two ways, or isn't sure which agent does better. Spawns agents.",
|
|
43
|
+
inputSchema: {
|
|
44
|
+
type: "object",
|
|
45
|
+
properties: {
|
|
46
|
+
action: { type: "string", enum: ["start", "status", "collect", "cleanup", "list"] },
|
|
47
|
+
task: { type: "string", description: "The shared task to give every agent (for start).", minLength: 1 },
|
|
48
|
+
agents: { type: "array", items: { type: "string", enum: ["claude", "codex", "opencode", "aider"] }, description: "Agents to compare (for start). Default [claude, codex]." },
|
|
49
|
+
cwd: { type: "string", description: "Absolute path in the repo to branch worktrees from (for start). Defaults to current dir." },
|
|
50
|
+
id: { type: "string", description: "Comparison job id (for status/collect/cleanup)." },
|
|
51
|
+
},
|
|
52
|
+
additionalProperties: false,
|
|
53
|
+
},
|
|
54
|
+
async execute(input, ctx) {
|
|
55
|
+
const action = input.action ?? "start";
|
|
56
|
+
if (action === "list") {
|
|
57
|
+
const jobs = await loadComparisons();
|
|
58
|
+
if (jobs.length === 0)
|
|
59
|
+
return "(no comparison jobs)";
|
|
60
|
+
return jobs.map(formatJobHeader).join("\n\n");
|
|
61
|
+
}
|
|
62
|
+
if (action === "start") {
|
|
63
|
+
const task = input.task?.trim();
|
|
64
|
+
if (!task)
|
|
65
|
+
return "(start needs a task)";
|
|
66
|
+
const agents = input.agents && input.agents.length ? Array.from(new Set(input.agents)) : ["claude", "codex"];
|
|
67
|
+
if (agents.length < 2)
|
|
68
|
+
return "(comparing needs at least 2 agents)";
|
|
69
|
+
const cwd = input.cwd && input.cwd.startsWith("/") ? input.cwd : ctx.cwd;
|
|
70
|
+
const repo = await gitRoot(cwd, ctx.signal);
|
|
71
|
+
if (!repo)
|
|
72
|
+
return `(not a git repo: ${cwd} — compare_agents isolates each agent in a git worktree)`;
|
|
73
|
+
const id = newJobId();
|
|
74
|
+
await mkdir(path.join(COMPARE_ROOT, id), { recursive: true });
|
|
75
|
+
const entries = [];
|
|
76
|
+
for (const agent of agents) {
|
|
77
|
+
const branch = `lisa-compare/${id}-${agent}`;
|
|
78
|
+
const worktree = path.join(COMPARE_ROOT, id, agent);
|
|
79
|
+
const wt = await runIn(repo, "git", ["-C", repo, "worktree", "add", "-b", branch, worktree, "HEAD"], { timeoutMs: 20000, signal: ctx.signal });
|
|
80
|
+
if (wt.code !== 0) {
|
|
81
|
+
entries.push({ agent, worktree, branch, launchError: `worktree add failed: ${wt.stderr.trim().slice(0, 120)}` });
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const r = await launchAgent(agent, task, worktree, ctx.log);
|
|
85
|
+
entries.push({ agent, worktree, branch, pid: r.pid, launchError: r.error });
|
|
86
|
+
}
|
|
87
|
+
const job = { id, task, repo, createdAt: Date.now(), entries };
|
|
88
|
+
await addComparison(job);
|
|
89
|
+
const launched = entries.filter((e) => !e.launchError).map((e) => e.agent);
|
|
90
|
+
const failed = entries.filter((e) => e.launchError);
|
|
91
|
+
return (`Started comparison ${id}: ${launched.join(" vs ")} on the same task in ${repo}.\n` +
|
|
92
|
+
entries.map((e) => ` ${e.agent} → ${e.launchError ? "FAILED: " + e.launchError : "pid " + e.pid + " in " + e.worktree}`).join("\n") +
|
|
93
|
+
(failed.length ? "" : "") +
|
|
94
|
+
`\n\nThey run in the background. Check progress with compare_agents action:'status' id:'${id}', ` +
|
|
95
|
+
`then action:'collect' id:'${id}' to diff the results, and 'cleanup' when done.`);
|
|
96
|
+
}
|
|
97
|
+
// status / collect / cleanup need a job
|
|
98
|
+
if (!input.id)
|
|
99
|
+
return `(${action} needs an id — from compare_agents action:'list')`;
|
|
100
|
+
const job = await getComparison(input.id);
|
|
101
|
+
if (!job)
|
|
102
|
+
return `(no comparison job matches "${input.id}")`;
|
|
103
|
+
if (action === "status") {
|
|
104
|
+
const hub = getCurrentHub();
|
|
105
|
+
const lines = [formatJobHeader(job)];
|
|
106
|
+
for (const e of job.entries) {
|
|
107
|
+
const session = hub?.list().find((s) => s.cwd === e.worktree);
|
|
108
|
+
const changed = await changedCount(e.worktree, ctx.signal);
|
|
109
|
+
lines.push(formatStatusLine(e, session ? session.state : null, changed));
|
|
110
|
+
}
|
|
111
|
+
return lines.join("\n");
|
|
112
|
+
}
|
|
113
|
+
if (action === "collect") {
|
|
114
|
+
const lines = [formatJobHeader(job), ""];
|
|
115
|
+
for (const e of job.entries) {
|
|
116
|
+
if (e.launchError) {
|
|
117
|
+
lines.push(`── ${e.agent}: (didn't launch: ${e.launchError})`);
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
const stat = await runIn(e.worktree, "git", ["-C", e.worktree, "diff", "HEAD", "--stat"], { timeoutMs: 10000, signal: ctx.signal });
|
|
121
|
+
// diff HEAD misses untracked new files — list those separately so a
|
|
122
|
+
// brand-new file an agent created isn't invisible in the comparison.
|
|
123
|
+
const st = await runIn(e.worktree, "git", ["-C", e.worktree, "status", "--porcelain"], { timeoutMs: 8000, signal: ctx.signal });
|
|
124
|
+
const untracked = st.code === 0 ? st.stdout.split("\n").filter((l) => l.startsWith("??")).map((l) => l.slice(3).trim()) : [];
|
|
125
|
+
let body = stat.code === 0 && stat.stdout.trim() ? stat.stdout.trim() : "";
|
|
126
|
+
if (untracked.length)
|
|
127
|
+
body += `\n new files: ${untracked.slice(0, 10).join(", ")}${untracked.length > 10 ? ` (+${untracked.length - 10})` : ""}`;
|
|
128
|
+
lines.push(`── ${e.agent} [${e.branch}] ──\n${body.trim() || "(no changes)"}`);
|
|
129
|
+
}
|
|
130
|
+
lines.push("", "Review one in full with review_diff cwd:'<worktree>' target:'head', then cleanup.");
|
|
131
|
+
return lines.join("\n");
|
|
132
|
+
}
|
|
133
|
+
// cleanup
|
|
134
|
+
const errs = [];
|
|
135
|
+
for (const e of job.entries) {
|
|
136
|
+
const rm = await runIn(job.repo, "git", ["-C", job.repo, "worktree", "remove", "--force", e.worktree], { timeoutMs: 15000, signal: ctx.signal });
|
|
137
|
+
if (rm.code !== 0 && !/not a working tree|No such file/i.test(rm.stderr))
|
|
138
|
+
errs.push(`${e.agent}: ${rm.stderr.trim().slice(0, 80)}`);
|
|
139
|
+
await runIn(job.repo, "git", ["-C", job.repo, "branch", "-D", e.branch], { timeoutMs: 8000, signal: ctx.signal });
|
|
140
|
+
}
|
|
141
|
+
await removeComparison(job.id);
|
|
142
|
+
return errs.length ? `Cleaned up ${job.id} with warnings:\n${errs.join("\n")}` : `Cleaned up comparison ${job.id} (worktrees + scratch branches removed).`;
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
//# sourceMappingURL=compare_agents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compare_agents.js","sourceRoot":"","sources":["../../src/tools/compare_agents.ts"],"names":[],"mappings":"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"}
|
|
@@ -33,6 +33,19 @@ export declare function buildDispatchArgv(agent: DispatchInput["agent"], task: s
|
|
|
33
33
|
cmd: string;
|
|
34
34
|
args: string[];
|
|
35
35
|
};
|
|
36
|
+
export type DispatchAgentKind = DispatchInput["agent"];
|
|
37
|
+
export interface LaunchResult {
|
|
38
|
+
pid?: number;
|
|
39
|
+
/** Present when the launch failed (binary missing, spawn error). */
|
|
40
|
+
error?: string;
|
|
41
|
+
cmd: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Spawn a CLI agent headlessly, detached, and record it in the dispatch ledger.
|
|
45
|
+
* Shared by dispatch_agent (interactive), scheduled_dispatch (heartbeat-timed),
|
|
46
|
+
* and compare_agents (parallel). Never throws — returns {pid} or {error}.
|
|
47
|
+
*/
|
|
48
|
+
export declare function launchAgent(agent: DispatchAgentKind, task: string, cwd: string, log?: (m: string) => void): Promise<LaunchResult>;
|
|
36
49
|
export declare const dispatchAgentTool: ToolDefinition<DispatchInput, string>;
|
|
37
50
|
export {};
|
|
38
51
|
//# sourceMappingURL=dispatch_agent.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatch_agent.d.ts","sourceRoot":"","sources":["../../src/tools/dispatch_agent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"dispatch_agent.d.ts","sourceRoot":"","sources":["../../src/tools/dispatch_agent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAIlD,UAAU,aAAa;IACrB,KAAK,EAAE,QAAQ,GAAG,OAAO,GAAG,UAAU,GAAG,OAAO,CAAC;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,+DAA+D;IAC/D,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAoBD,wEAAwE;AACxE,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,EAC7B,IAAI,EAAE,MAAM,GACX;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAgBjC;AAED,MAAM,MAAM,iBAAiB,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;AAEvD,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oEAAoE;IACpE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,iBAAiB,EACxB,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,EACX,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,GACxB,OAAO,CAAC,YAAY,CAAC,CAkCvB;AAED,eAAO,MAAM,iBAAiB,EAAE,cAAc,CAAC,aAAa,EAAE,MAAM,CA6DnE,CAAC"}
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
*/
|
|
23
23
|
import { spawn } from "node:child_process";
|
|
24
24
|
import { getCurrentHub } from "../integrations/current-hub.js";
|
|
25
|
+
import { recordDispatch } from "../integrations/dispatch-ledger.js";
|
|
25
26
|
/**
|
|
26
27
|
* L4 COORDINATE: is another agent already actively working in this cwd?
|
|
27
28
|
* Launching a second agent into the same directory is the #1 way multi-agent
|
|
@@ -55,6 +56,49 @@ export function buildDispatchArgv(agent, task) {
|
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Spawn a CLI agent headlessly, detached, and record it in the dispatch ledger.
|
|
61
|
+
* Shared by dispatch_agent (interactive), scheduled_dispatch (heartbeat-timed),
|
|
62
|
+
* and compare_agents (parallel). Never throws — returns {pid} or {error}.
|
|
63
|
+
*/
|
|
64
|
+
export async function launchAgent(agent, task, cwd, log) {
|
|
65
|
+
const { cmd, args } = buildDispatchArgv(agent, task);
|
|
66
|
+
let child;
|
|
67
|
+
try {
|
|
68
|
+
child = spawn(cmd, args, { cwd, detached: true, stdio: "ignore" });
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
return { error: `Failed to launch ${agent}: ${err.message}. Is "${cmd}" installed and on PATH?`, cmd };
|
|
72
|
+
}
|
|
73
|
+
const launchError = await new Promise((resolve) => {
|
|
74
|
+
let settled = false;
|
|
75
|
+
child.once("error", (e) => {
|
|
76
|
+
if (settled)
|
|
77
|
+
return;
|
|
78
|
+
settled = true;
|
|
79
|
+
resolve(e.code === "ENOENT" ? `"${cmd}" not found on PATH — is ${agent} installed?` : `launch error: ${e.message}`);
|
|
80
|
+
});
|
|
81
|
+
setTimeout(() => {
|
|
82
|
+
if (settled)
|
|
83
|
+
return;
|
|
84
|
+
settled = true;
|
|
85
|
+
resolve(null);
|
|
86
|
+
}, 150);
|
|
87
|
+
});
|
|
88
|
+
if (launchError)
|
|
89
|
+
return { error: launchError, cmd };
|
|
90
|
+
const pid = child.pid;
|
|
91
|
+
child.unref();
|
|
92
|
+
if (typeof pid === "number") {
|
|
93
|
+
try {
|
|
94
|
+
recordDispatch({ agent, pid, cwd, task });
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
log?.(`[dispatch] ledger write failed (non-fatal): ${err.message}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return { pid, cmd };
|
|
101
|
+
}
|
|
58
102
|
export const dispatchAgentTool = {
|
|
59
103
|
name: "dispatch_agent",
|
|
60
104
|
description: "Launch another CLI coding agent (claude, codex, opencode, aider) to work on a task " +
|
|
@@ -100,46 +144,14 @@ export const dispatchAgentTool = {
|
|
|
100
144
|
`Wait for it to finish, pick a different directory, or pass force:true to override.`);
|
|
101
145
|
}
|
|
102
146
|
}
|
|
103
|
-
const {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
child = spawn(cmd, args, {
|
|
107
|
-
cwd,
|
|
108
|
-
detached: true, // survive LISA; the agent runs on its own
|
|
109
|
-
stdio: "ignore",
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
catch (err) {
|
|
113
|
-
return `Failed to launch ${input.agent}: ${err.message}. Is "${cmd}" installed and on PATH?`;
|
|
114
|
-
}
|
|
115
|
-
// If the binary is missing, spawn emits 'error' asynchronously. Surface a
|
|
116
|
-
// useful message in that case rather than silently "succeeding".
|
|
117
|
-
const launchError = await new Promise((resolve) => {
|
|
118
|
-
let settled = false;
|
|
119
|
-
child.once("error", (e) => {
|
|
120
|
-
if (settled)
|
|
121
|
-
return;
|
|
122
|
-
settled = true;
|
|
123
|
-
resolve(e.code === "ENOENT"
|
|
124
|
-
? `"${cmd}" not found on PATH — is ${input.agent} installed?`
|
|
125
|
-
: `launch error: ${e.message}`);
|
|
126
|
-
});
|
|
127
|
-
// Give the spawn a tick to fail fast; if it doesn't, assume it started.
|
|
128
|
-
setTimeout(() => {
|
|
129
|
-
if (settled)
|
|
130
|
-
return;
|
|
131
|
-
settled = true;
|
|
132
|
-
resolve(null);
|
|
133
|
-
}, 150);
|
|
134
|
-
});
|
|
135
|
-
if (launchError)
|
|
136
|
-
return launchError;
|
|
137
|
-
const pid = child.pid;
|
|
138
|
-
child.unref(); // don't keep LISA's event loop alive for it
|
|
147
|
+
const { pid, error } = await launchAgent(input.agent, input.task, cwd, ctx.log);
|
|
148
|
+
if (error)
|
|
149
|
+
return error;
|
|
139
150
|
ctx.log(`[dispatch] launched ${input.agent} (pid ${pid}) in ${cwd}: ${input.task.slice(0, 80)}`);
|
|
140
151
|
return (`Launched ${input.agent} (pid ${pid}) in ${cwd}.\n` +
|
|
141
152
|
`It's running autonomously and will appear in the agent session monitor. ` +
|
|
142
|
-
`I won't block on it — ask me "what's running?" (advise_now) to check progress
|
|
153
|
+
`I won't block on it — ask me "what's running?" (advise_now) to check progress, ` +
|
|
154
|
+
`or signal_agent to list/cancel it.`);
|
|
143
155
|
},
|
|
144
156
|
};
|
|
145
157
|
//# sourceMappingURL=dispatch_agent.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatch_agent.js","sourceRoot":"","sources":["../../src/tools/dispatch_agent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"dispatch_agent.js","sourceRoot":"","sources":["../../src/tools/dispatch_agent.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface ExecResult {
|
|
2
|
+
code: number | null;
|
|
3
|
+
stdout: string;
|
|
4
|
+
stderr: string;
|
|
5
|
+
timedOut: boolean;
|
|
6
|
+
/** True when the binary itself couldn't be spawned (e.g. not installed). */
|
|
7
|
+
spawnError?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function runIn(cwd: string, cmd: string, args: string[], opts?: {
|
|
10
|
+
timeoutMs?: number;
|
|
11
|
+
signal?: AbortSignal;
|
|
12
|
+
maxBytes?: number;
|
|
13
|
+
}): Promise<ExecResult>;
|
|
14
|
+
/** Validate that `p` is an absolute path to an existing directory. */
|
|
15
|
+
export declare function isDir(p: string): Promise<boolean>;
|
|
16
|
+
/** `git -C <cwd> rev-parse --show-toplevel` → repo root, or null if not a repo. */
|
|
17
|
+
export declare function gitRoot(cwd: string, signal?: AbortSignal): Promise<string | null>;
|
|
18
|
+
//# sourceMappingURL=exec-util.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec-util.d.ts","sourceRoot":"","sources":["../../src/tools/exec-util.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,4EAA4E;IAC5E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,KAAK,CACnB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EAAE,EACd,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GACzE,OAAO,CAAC,UAAU,CAAC,CAoCrB;AAED,sEAAsE;AACtE,wBAAsB,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOvD;AAED,mFAAmF;AACnF,wBAAsB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAOvF"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Small process-exec helper shared by the repo/PR/check tools. Runs a command
|
|
3
|
+
* in a given directory, captures (capped) output, honours an AbortSignal and a
|
|
4
|
+
* timeout. Never throws — returns a result with the exit code so callers branch
|
|
5
|
+
* on it. Dependency-free (node:child_process only).
|
|
6
|
+
*/
|
|
7
|
+
import { spawn } from "node:child_process";
|
|
8
|
+
import { stat } from "node:fs/promises";
|
|
9
|
+
export function runIn(cwd, cmd, args, opts = {}) {
|
|
10
|
+
const cap = opts.maxBytes ?? 64 * 1024;
|
|
11
|
+
return new Promise((resolve) => {
|
|
12
|
+
let child;
|
|
13
|
+
try {
|
|
14
|
+
child = spawn(cmd, args, { cwd, signal: opts.signal });
|
|
15
|
+
}
|
|
16
|
+
catch (e) {
|
|
17
|
+
resolve({ code: null, stdout: "", stderr: "", timedOut: false, spawnError: String(e.message) });
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
let stdout = "";
|
|
21
|
+
let stderr = "";
|
|
22
|
+
let timedOut = false;
|
|
23
|
+
const timer = opts.timeoutMs
|
|
24
|
+
? setTimeout(() => {
|
|
25
|
+
timedOut = true;
|
|
26
|
+
try {
|
|
27
|
+
child.kill("SIGKILL");
|
|
28
|
+
}
|
|
29
|
+
catch { }
|
|
30
|
+
}, opts.timeoutMs)
|
|
31
|
+
: null;
|
|
32
|
+
child.stdout?.on("data", (b) => {
|
|
33
|
+
if (stdout.length < cap)
|
|
34
|
+
stdout += b.toString("utf8");
|
|
35
|
+
});
|
|
36
|
+
child.stderr?.on("data", (b) => {
|
|
37
|
+
if (stderr.length < cap)
|
|
38
|
+
stderr += b.toString("utf8");
|
|
39
|
+
});
|
|
40
|
+
child.on("error", (e) => {
|
|
41
|
+
if (timer)
|
|
42
|
+
clearTimeout(timer);
|
|
43
|
+
resolve({ code: null, stdout, stderr, timedOut, spawnError: String(e.message) });
|
|
44
|
+
});
|
|
45
|
+
child.on("close", (code) => {
|
|
46
|
+
if (timer)
|
|
47
|
+
clearTimeout(timer);
|
|
48
|
+
resolve({ code, stdout, stderr, timedOut });
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/** Validate that `p` is an absolute path to an existing directory. */
|
|
53
|
+
export async function isDir(p) {
|
|
54
|
+
if (!p || !p.startsWith("/"))
|
|
55
|
+
return false;
|
|
56
|
+
try {
|
|
57
|
+
return (await stat(p)).isDirectory();
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/** `git -C <cwd> rev-parse --show-toplevel` → repo root, or null if not a repo. */
|
|
64
|
+
export async function gitRoot(cwd, signal) {
|
|
65
|
+
const r = await runIn(cwd, "git", ["-C", cwd, "rev-parse", "--show-toplevel"], { timeoutMs: 5000, signal });
|
|
66
|
+
if (r.code === 0) {
|
|
67
|
+
const root = r.stdout.trim();
|
|
68
|
+
return root || null;
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=exec-util.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec-util.js","sourceRoot":"","sources":["../../src/tools/exec-util.ts"],"names":[],"mappings":"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"}
|