baro-ai 0.51.3 → 0.51.4
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/cli.mjs +458 -78
- package/dist/cli.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -13750,8 +13750,8 @@ var require_jwa = __commonJS({
|
|
|
13750
13750
|
}
|
|
13751
13751
|
function typeError(template) {
|
|
13752
13752
|
var args = [].slice.call(arguments, 1);
|
|
13753
|
-
var
|
|
13754
|
-
return new TypeError(
|
|
13753
|
+
var errMsg2 = util.format.bind(util, template).apply(null, args);
|
|
13754
|
+
return new TypeError(errMsg2);
|
|
13755
13755
|
}
|
|
13756
13756
|
function bufferOrString(obj) {
|
|
13757
13757
|
return Buffer4.isBuffer(obj) || typeof obj === "string";
|
|
@@ -22243,9 +22243,9 @@ var require_websocket_server = __commonJS({
|
|
|
22243
22243
|
|
|
22244
22244
|
// ../baro-memory/dist/vectra-store.js
|
|
22245
22245
|
import { LocalIndex } from "vectra";
|
|
22246
|
-
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as
|
|
22247
|
-
import { join as
|
|
22248
|
-
import { tmpdir, homedir } from "os";
|
|
22246
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync3, renameSync, rmSync as rmSync2, readdirSync, statSync as statSync2, lstatSync } from "fs";
|
|
22247
|
+
import { join as join3 } from "path";
|
|
22248
|
+
import { tmpdir as tmpdir2, homedir } from "os";
|
|
22249
22249
|
async function createMemoryStore(config) {
|
|
22250
22250
|
const cfg = { ...DEFAULTS, ...config };
|
|
22251
22251
|
if (cfg.defaultMinSimilarity < 0 || cfg.defaultMinSimilarity > 1) {
|
|
@@ -22257,23 +22257,23 @@ async function createMemoryStore(config) {
|
|
|
22257
22257
|
if (cfg.disabled) {
|
|
22258
22258
|
return new NoOpMemoryStore();
|
|
22259
22259
|
}
|
|
22260
|
-
const sessionPath = cfg.sessionPath ||
|
|
22260
|
+
const sessionPath = cfg.sessionPath || join3(tmpdir2(), `baro-memory-${process.pid}-${Date.now()}`);
|
|
22261
22261
|
validateSessionPath(sessionPath);
|
|
22262
22262
|
mkdirSync2(sessionPath, { recursive: true });
|
|
22263
|
-
const indexPath =
|
|
22263
|
+
const indexPath = join3(sessionPath, "index");
|
|
22264
22264
|
mkdirSync2(indexPath, { recursive: true });
|
|
22265
22265
|
const index = new LocalIndex(indexPath);
|
|
22266
22266
|
if (!await index.isIndexCreated()) {
|
|
22267
22267
|
await index.createIndex({ version: 1 });
|
|
22268
22268
|
}
|
|
22269
22269
|
const transformers = await import("@xenova/transformers");
|
|
22270
|
-
transformers.env.cacheDir = process.env.TRANSFORMERS_CACHE ||
|
|
22270
|
+
transformers.env.cacheDir = process.env.TRANSFORMERS_CACHE || join3(homedir(), ".baro", "models");
|
|
22271
22271
|
const { pipeline: pipeline2 } = transformers;
|
|
22272
22272
|
const extractor = await pipeline2("feature-extraction", cfg.embeddingModel);
|
|
22273
22273
|
return new VectraMemoryStore(index, extractor, sessionPath, cfg);
|
|
22274
22274
|
}
|
|
22275
22275
|
function validateSessionPath(sessionPath) {
|
|
22276
|
-
const resolved =
|
|
22276
|
+
const resolved = join3(sessionPath);
|
|
22277
22277
|
if (resolved.includes("..")) {
|
|
22278
22278
|
throw new Error(`Invalid session path (contains ..): ${resolved}`);
|
|
22279
22279
|
}
|
|
@@ -22284,26 +22284,26 @@ function validateSessionPath(sessionPath) {
|
|
|
22284
22284
|
}
|
|
22285
22285
|
}
|
|
22286
22286
|
const normalizedPath = resolved.toLowerCase();
|
|
22287
|
-
const isSafe = ALLOWED_SESSION_PARENTS.some((p) => normalizedPath.includes(p)) || normalizedPath.startsWith(
|
|
22287
|
+
const isSafe = ALLOWED_SESSION_PARENTS.some((p) => normalizedPath.includes(p)) || normalizedPath.startsWith(tmpdir2().toLowerCase());
|
|
22288
22288
|
if (!isSafe) {
|
|
22289
22289
|
throw new Error(`Invalid session path (must be under ~/.baro, tmpdir, or contain 'baro-memory'): ${resolved}`);
|
|
22290
22290
|
}
|
|
22291
22291
|
}
|
|
22292
22292
|
function pruneOldSessions(sessionsDir) {
|
|
22293
22293
|
try {
|
|
22294
|
-
if (!
|
|
22294
|
+
if (!existsSync3(sessionsDir))
|
|
22295
22295
|
return;
|
|
22296
22296
|
const now = Date.now();
|
|
22297
22297
|
for (const entry of readdirSync(sessionsDir)) {
|
|
22298
22298
|
if (!entry.startsWith("run-"))
|
|
22299
22299
|
continue;
|
|
22300
|
-
const entryPath =
|
|
22300
|
+
const entryPath = join3(sessionsDir, entry);
|
|
22301
22301
|
try {
|
|
22302
22302
|
const stat2 = lstatSync(entryPath);
|
|
22303
22303
|
if (stat2.isSymbolicLink())
|
|
22304
22304
|
continue;
|
|
22305
22305
|
if (stat2.isDirectory() && now - stat2.mtimeMs > SESSION_TTL_MS) {
|
|
22306
|
-
|
|
22306
|
+
rmSync2(entryPath, { recursive: true, force: true });
|
|
22307
22307
|
}
|
|
22308
22308
|
} catch {
|
|
22309
22309
|
}
|
|
@@ -22354,10 +22354,10 @@ var init_vectra_store = __esm({
|
|
|
22354
22354
|
this.index = index;
|
|
22355
22355
|
this.extractor = extractor;
|
|
22356
22356
|
this.sessionPath = sessionPath;
|
|
22357
|
-
this.indexPath =
|
|
22358
|
-
this.indexFilePath =
|
|
22359
|
-
this.cachePath =
|
|
22360
|
-
this.lockPath =
|
|
22357
|
+
this.indexPath = join3(sessionPath, "index");
|
|
22358
|
+
this.indexFilePath = join3(this.indexPath, "index.json");
|
|
22359
|
+
this.cachePath = join3(sessionPath, "cache.json");
|
|
22360
|
+
this.lockPath = join3(sessionPath, "cache.lock");
|
|
22361
22361
|
this.config = config;
|
|
22362
22362
|
}
|
|
22363
22363
|
/**
|
|
@@ -22381,7 +22381,7 @@ var init_vectra_store = __esm({
|
|
|
22381
22381
|
*/
|
|
22382
22382
|
refreshIndexIfChanged() {
|
|
22383
22383
|
try {
|
|
22384
|
-
if (!
|
|
22384
|
+
if (!existsSync3(this.indexFilePath))
|
|
22385
22385
|
return;
|
|
22386
22386
|
const mtimeMs = statSync2(this.indexFilePath).mtimeMs;
|
|
22387
22387
|
if (mtimeMs === this.lastIndexMtimeMs)
|
|
@@ -22556,7 +22556,7 @@ ${result.content}
|
|
|
22556
22556
|
}
|
|
22557
22557
|
async close() {
|
|
22558
22558
|
try {
|
|
22559
|
-
|
|
22559
|
+
rmSync2(this.lockPath, { force: true });
|
|
22560
22560
|
} catch {
|
|
22561
22561
|
}
|
|
22562
22562
|
}
|
|
@@ -22605,7 +22605,7 @@ ${result.content}
|
|
|
22605
22605
|
*/
|
|
22606
22606
|
loadCache() {
|
|
22607
22607
|
try {
|
|
22608
|
-
if (
|
|
22608
|
+
if (existsSync3(this.cachePath)) {
|
|
22609
22609
|
const raw = readFileSync3(this.cachePath, "utf-8");
|
|
22610
22610
|
if (raw.trim()) {
|
|
22611
22611
|
return JSON.parse(raw);
|
|
@@ -22633,8 +22633,8 @@ ${result.content}
|
|
|
22633
22633
|
}
|
|
22634
22634
|
} finally {
|
|
22635
22635
|
try {
|
|
22636
|
-
if (
|
|
22637
|
-
|
|
22636
|
+
if (existsSync3(this.lockPath))
|
|
22637
|
+
rmSync2(this.lockPath);
|
|
22638
22638
|
} catch {
|
|
22639
22639
|
}
|
|
22640
22640
|
}
|
|
@@ -22692,12 +22692,12 @@ var init_dist2 = __esm({
|
|
|
22692
22692
|
});
|
|
22693
22693
|
|
|
22694
22694
|
// ../baro-orchestrator/scripts/cli.ts
|
|
22695
|
-
import { existsSync as
|
|
22695
|
+
import { existsSync as existsSync6 } from "fs";
|
|
22696
22696
|
import { resolve as resolve3 } from "path";
|
|
22697
22697
|
|
|
22698
22698
|
// ../baro-orchestrator/src/orchestrate.ts
|
|
22699
22699
|
import { mkdirSync as mkdirSync5 } from "fs";
|
|
22700
|
-
import { dirname as dirname3, join as
|
|
22700
|
+
import { dirname as dirname3, join as join6 } from "path";
|
|
22701
22701
|
|
|
22702
22702
|
// ../../node_modules/openai/internal/tslib.mjs
|
|
22703
22703
|
function __classPrivateFieldSet(receiver, state, value, kind, f3) {
|
|
@@ -39880,7 +39880,7 @@ async function safePullRebase(cwd, onLog, gate) {
|
|
|
39880
39880
|
} catch {
|
|
39881
39881
|
}
|
|
39882
39882
|
try {
|
|
39883
|
-
await exec("git", ["pull", "--rebase", "origin", branch], { cwd });
|
|
39883
|
+
await exec("git", ["pull", "--rebase=merges", "origin", branch], { cwd });
|
|
39884
39884
|
onLog?.("[git] pull ok");
|
|
39885
39885
|
} catch {
|
|
39886
39886
|
onLog?.("[git] pull conflict, continuing without pull");
|
|
@@ -39923,7 +39923,7 @@ async function gitPushWithRetry(gate, options) {
|
|
|
39923
39923
|
`[git] push rejected (attempt ${attempt}/${max}), pulling and retrying...`
|
|
39924
39924
|
);
|
|
39925
39925
|
try {
|
|
39926
|
-
await exec("git", ["pull", "--rebase", "origin", branch], {
|
|
39926
|
+
await exec("git", ["pull", "--rebase=merges", "origin", branch], {
|
|
39927
39927
|
cwd: options.cwd
|
|
39928
39928
|
});
|
|
39929
39929
|
} catch {
|
|
@@ -39991,6 +39991,316 @@ function extractStderr(e2) {
|
|
|
39991
39991
|
return e2 instanceof Error ? e2.message : String(e2);
|
|
39992
39992
|
}
|
|
39993
39993
|
|
|
39994
|
+
// ../baro-orchestrator/src/worktree.ts
|
|
39995
|
+
import { execFile as execFile2 } from "child_process";
|
|
39996
|
+
import { existsSync, rmSync, symlinkSync } from "fs";
|
|
39997
|
+
import { tmpdir } from "os";
|
|
39998
|
+
import { join } from "path";
|
|
39999
|
+
import { promisify as promisify3 } from "util";
|
|
40000
|
+
var exec2 = promisify3(execFile2);
|
|
40001
|
+
var LINKED_DEP_DIRS = ["node_modules", ".venv", "vendor"];
|
|
40002
|
+
var WorktreeManager = class {
|
|
40003
|
+
constructor(repoRoot, gate, runId, opts = {}) {
|
|
40004
|
+
this.repoRoot = repoRoot;
|
|
40005
|
+
this.gate = gate;
|
|
40006
|
+
this.runId = runId;
|
|
40007
|
+
this.baseDir = join(tmpdir(), "baro-worktrees", runId);
|
|
40008
|
+
this.linkDepDirs = opts.linkDepDirs ?? true;
|
|
40009
|
+
this.log = opts.onLog ?? ((line) => process.stderr.write(`[worktree] ${line}
|
|
40010
|
+
`));
|
|
40011
|
+
}
|
|
40012
|
+
paths = /* @__PURE__ */ new Map();
|
|
40013
|
+
/** Stories whose merge-back failed: their branch is kept for recovery. */
|
|
40014
|
+
preserved = /* @__PURE__ */ new Set();
|
|
40015
|
+
baseDir;
|
|
40016
|
+
linkDepDirs;
|
|
40017
|
+
log;
|
|
40018
|
+
branchOf(storyId) {
|
|
40019
|
+
return `baro-wt/${this.runId}/${sanitize(storyId)}`;
|
|
40020
|
+
}
|
|
40021
|
+
pathOf(storyId) {
|
|
40022
|
+
return join(this.baseDir, sanitize(storyId));
|
|
40023
|
+
}
|
|
40024
|
+
/**
|
|
40025
|
+
* Create an isolated worktree for a story, branched off the current
|
|
40026
|
+
* run-branch HEAD. Returns the worktree path, or null on any failure so
|
|
40027
|
+
* the caller can fall back to the shared repo cwd (preserving the old
|
|
40028
|
+
* behavior rather than failing the story).
|
|
40029
|
+
*/
|
|
40030
|
+
async create(storyId) {
|
|
40031
|
+
const release = await this.gate.acquire();
|
|
40032
|
+
try {
|
|
40033
|
+
const branch = this.branchOf(storyId);
|
|
40034
|
+
const path6 = this.pathOf(storyId);
|
|
40035
|
+
await this.removeWorktreeQuiet(path6);
|
|
40036
|
+
await this.deleteBranchQuiet(branch);
|
|
40037
|
+
await exec2(
|
|
40038
|
+
"git",
|
|
40039
|
+
["worktree", "add", "-b", branch, path6, "HEAD"],
|
|
40040
|
+
{ cwd: this.repoRoot }
|
|
40041
|
+
);
|
|
40042
|
+
this.paths.set(storyId, path6);
|
|
40043
|
+
if (this.linkDepDirs) this.symlinkDepDirs(path6);
|
|
40044
|
+
this.log(`created ${branch} at ${path6}`);
|
|
40045
|
+
return path6;
|
|
40046
|
+
} catch (e2) {
|
|
40047
|
+
this.log(
|
|
40048
|
+
`could not create worktree for ${storyId} (${errMsg(e2)}); falling back to shared tree`
|
|
40049
|
+
);
|
|
40050
|
+
return null;
|
|
40051
|
+
} finally {
|
|
40052
|
+
release();
|
|
40053
|
+
}
|
|
40054
|
+
}
|
|
40055
|
+
/**
|
|
40056
|
+
* Merge a passed story's branch onto the run branch. Returns true if a
|
|
40057
|
+
* worktree merge happened, false if the story had no worktree (create()
|
|
40058
|
+
* fell back to the shared tree). On any merge failure, retries once with
|
|
40059
|
+
* `-X theirs` (the merging story wins). Throws — leaving the run branch
|
|
40060
|
+
* clean (`merge --abort`) — only when even that can't resolve it; the
|
|
40061
|
+
* caller must then preserve the branch rather than discard the work.
|
|
40062
|
+
*/
|
|
40063
|
+
async mergeBack(storyId) {
|
|
40064
|
+
const path6 = this.paths.get(storyId);
|
|
40065
|
+
if (!path6) return false;
|
|
40066
|
+
const branch = this.branchOf(storyId);
|
|
40067
|
+
const release = await this.gate.acquire();
|
|
40068
|
+
try {
|
|
40069
|
+
await this.autoCommitLeftovers(storyId, path6);
|
|
40070
|
+
const msg = `baro: merge story ${storyId}`;
|
|
40071
|
+
try {
|
|
40072
|
+
await exec2("git", ["merge", "--no-ff", "-m", msg, branch], {
|
|
40073
|
+
cwd: this.repoRoot
|
|
40074
|
+
});
|
|
40075
|
+
return true;
|
|
40076
|
+
} catch {
|
|
40077
|
+
const conflicts = await this.conflictedPaths();
|
|
40078
|
+
await this.abortMerge(storyId);
|
|
40079
|
+
this.log(
|
|
40080
|
+
`WARNING: story ${storyId} conflicts with already-merged work` + (conflicts.length ? ` on [${conflicts.join(", ")}]` : "") + `; auto-resolving with -X theirs (this story wins)`
|
|
40081
|
+
);
|
|
40082
|
+
try {
|
|
40083
|
+
await exec2(
|
|
40084
|
+
"git",
|
|
40085
|
+
["merge", "--no-ff", "-X", "theirs", "-m", msg, branch],
|
|
40086
|
+
{ cwd: this.repoRoot }
|
|
40087
|
+
);
|
|
40088
|
+
return true;
|
|
40089
|
+
} catch (e2) {
|
|
40090
|
+
await this.abortMerge(storyId);
|
|
40091
|
+
this.preserved.add(storyId);
|
|
40092
|
+
throw new Error(
|
|
40093
|
+
`could not merge story ${storyId} even with -X theirs: ${errMsg(e2)}`
|
|
40094
|
+
);
|
|
40095
|
+
}
|
|
40096
|
+
}
|
|
40097
|
+
} finally {
|
|
40098
|
+
release();
|
|
40099
|
+
}
|
|
40100
|
+
}
|
|
40101
|
+
/**
|
|
40102
|
+
* Abort an in-progress merge, logging if the abort itself fails. A
|
|
40103
|
+
* lingering MERGE_HEAD (e.g. a held index.lock) would otherwise make the
|
|
40104
|
+
* NEXT story's merge fail and be misdiagnosed against the wrong story.
|
|
40105
|
+
*/
|
|
40106
|
+
async abortMerge(storyId) {
|
|
40107
|
+
try {
|
|
40108
|
+
await exec2("git", ["merge", "--abort"], { cwd: this.repoRoot });
|
|
40109
|
+
} catch (e2) {
|
|
40110
|
+
this.log(
|
|
40111
|
+
`WARNING: 'git merge --abort' failed after story ${storyId} (${errMsg(e2)}); run branch may have a lingering MERGE_HEAD`
|
|
40112
|
+
);
|
|
40113
|
+
}
|
|
40114
|
+
}
|
|
40115
|
+
/** Remove a story's worktree + branch (after merge-back, or on failure). */
|
|
40116
|
+
async cleanup(storyId) {
|
|
40117
|
+
const path6 = this.paths.get(storyId);
|
|
40118
|
+
const branch = this.branchOf(storyId);
|
|
40119
|
+
const release = await this.gate.acquire();
|
|
40120
|
+
try {
|
|
40121
|
+
if (path6) {
|
|
40122
|
+
await this.removeWorktreeQuiet(path6);
|
|
40123
|
+
this.paths.delete(storyId);
|
|
40124
|
+
}
|
|
40125
|
+
await this.deleteBranchQuiet(branch);
|
|
40126
|
+
} finally {
|
|
40127
|
+
release();
|
|
40128
|
+
}
|
|
40129
|
+
}
|
|
40130
|
+
/**
|
|
40131
|
+
* Remove every worktree this manager created, plus its temp dir. Branches
|
|
40132
|
+
* are deleted too — EXCEPT those marked preserved (an unresolvable
|
|
40133
|
+
* merge-back): their worktree dir is freed but the branch ref is kept so
|
|
40134
|
+
* the commits stay recoverable after the run.
|
|
40135
|
+
*/
|
|
40136
|
+
async cleanupAll() {
|
|
40137
|
+
const release = await this.gate.acquire();
|
|
40138
|
+
try {
|
|
40139
|
+
for (const [storyId, path6] of this.paths) {
|
|
40140
|
+
await this.removeWorktreeQuiet(path6);
|
|
40141
|
+
if (this.preserved.has(storyId)) {
|
|
40142
|
+
this.log(
|
|
40143
|
+
`kept branch ${this.branchOf(storyId)} for recovery (merge-back failed); inspect with: git log ${this.branchOf(storyId)}`
|
|
40144
|
+
);
|
|
40145
|
+
} else {
|
|
40146
|
+
await this.deleteBranchQuiet(this.branchOf(storyId));
|
|
40147
|
+
}
|
|
40148
|
+
}
|
|
40149
|
+
this.paths.clear();
|
|
40150
|
+
await execQuiet("git", ["worktree", "prune"], this.repoRoot);
|
|
40151
|
+
rmSyncQuiet(this.baseDir);
|
|
40152
|
+
} finally {
|
|
40153
|
+
release();
|
|
40154
|
+
}
|
|
40155
|
+
}
|
|
40156
|
+
/**
|
|
40157
|
+
* Reclaim worktree admin entries whose dirs are already gone
|
|
40158
|
+
* (`git worktree prune`) and delete any branches under THIS run id
|
|
40159
|
+
* (defensive re-entrancy guard — ids are unique per process, so this
|
|
40160
|
+
* does not touch a concurrent run's worktrees).
|
|
40161
|
+
*/
|
|
40162
|
+
async cleanupStaleOnStart() {
|
|
40163
|
+
const release = await this.gate.acquire();
|
|
40164
|
+
try {
|
|
40165
|
+
await execQuiet("git", ["worktree", "prune"], this.repoRoot);
|
|
40166
|
+
const prefix = `baro-wt/${this.runId}/`;
|
|
40167
|
+
let branches = [];
|
|
40168
|
+
try {
|
|
40169
|
+
const { stdout } = await exec2(
|
|
40170
|
+
"git",
|
|
40171
|
+
["branch", "--list", `${prefix}*`, "--format=%(refname:short)"],
|
|
40172
|
+
{ cwd: this.repoRoot }
|
|
40173
|
+
);
|
|
40174
|
+
branches = stdout.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
40175
|
+
} catch {
|
|
40176
|
+
}
|
|
40177
|
+
for (const branch of branches) {
|
|
40178
|
+
await this.deleteBranchQuiet(branch);
|
|
40179
|
+
}
|
|
40180
|
+
} finally {
|
|
40181
|
+
release();
|
|
40182
|
+
}
|
|
40183
|
+
}
|
|
40184
|
+
// ── internals ────────────────────────────────────────────────────
|
|
40185
|
+
symlinkDepDirs(worktreePath) {
|
|
40186
|
+
for (const dir of LINKED_DEP_DIRS) {
|
|
40187
|
+
const src = join(this.repoRoot, dir);
|
|
40188
|
+
const dest = join(worktreePath, dir);
|
|
40189
|
+
if (!existsSync(src) || existsSync(dest)) continue;
|
|
40190
|
+
try {
|
|
40191
|
+
symlinkSync(src, dest, "dir");
|
|
40192
|
+
} catch (e2) {
|
|
40193
|
+
this.log(`could not symlink ${dir} into worktree (${errMsg(e2)})`);
|
|
40194
|
+
}
|
|
40195
|
+
}
|
|
40196
|
+
}
|
|
40197
|
+
/**
|
|
40198
|
+
* Commit work the agent edited but didn't commit, so it isn't lost on
|
|
40199
|
+
* cleanup (passing is signalled by the agent, not by a commit existing).
|
|
40200
|
+
* Never commits the symlinked dep dirs, and surfaces a warning rather than
|
|
40201
|
+
* silently dropping work if any git step fails.
|
|
40202
|
+
*/
|
|
40203
|
+
async autoCommitLeftovers(storyId, worktreePath) {
|
|
40204
|
+
let dirty = false;
|
|
40205
|
+
try {
|
|
40206
|
+
const { stdout } = await exec2("git", ["status", "--porcelain"], {
|
|
40207
|
+
cwd: worktreePath
|
|
40208
|
+
});
|
|
40209
|
+
dirty = stdout.trim().length > 0;
|
|
40210
|
+
} catch {
|
|
40211
|
+
return;
|
|
40212
|
+
}
|
|
40213
|
+
if (!dirty) return;
|
|
40214
|
+
try {
|
|
40215
|
+
await exec2("git", ["add", "-A"], { cwd: worktreePath });
|
|
40216
|
+
} catch (e2) {
|
|
40217
|
+
this.log(
|
|
40218
|
+
`WARNING: failed to stage story ${storyId}'s leftover work (${errMsg(e2)}); it will not be included in the merge`
|
|
40219
|
+
);
|
|
40220
|
+
return;
|
|
40221
|
+
}
|
|
40222
|
+
await execQuiet("git", ["reset", "-q", "--", ...LINKED_DEP_DIRS], worktreePath);
|
|
40223
|
+
let staged = [];
|
|
40224
|
+
let diffFailed = false;
|
|
40225
|
+
try {
|
|
40226
|
+
const { stdout } = await exec2("git", ["diff", "--cached", "--name-only"], {
|
|
40227
|
+
cwd: worktreePath
|
|
40228
|
+
});
|
|
40229
|
+
staged = stdout.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
40230
|
+
} catch (e2) {
|
|
40231
|
+
diffFailed = true;
|
|
40232
|
+
this.log(
|
|
40233
|
+
`WARNING: could not inspect staged changes for story ${storyId} (${errMsg(e2)}); committing whatever is staged`
|
|
40234
|
+
);
|
|
40235
|
+
}
|
|
40236
|
+
const depStaged = staged.filter(
|
|
40237
|
+
(p) => LINKED_DEP_DIRS.some((d) => p === d || p.startsWith(`${d}/`))
|
|
40238
|
+
);
|
|
40239
|
+
if (depStaged.length > 0) {
|
|
40240
|
+
this.log(
|
|
40241
|
+
`WARNING: could not keep dep dirs [${depStaged.join(", ")}] out of story ${storyId}'s auto-commit; skipping it to avoid committing symlinks`
|
|
40242
|
+
);
|
|
40243
|
+
await execQuiet("git", ["reset", "-q"], worktreePath);
|
|
40244
|
+
return;
|
|
40245
|
+
}
|
|
40246
|
+
if (!diffFailed && staged.length === 0) return;
|
|
40247
|
+
this.log(
|
|
40248
|
+
`WARNING: story ${storyId} passed with uncommitted changes; auto-committing ${diffFailed ? "" : `${staged.length} path(s) `}before merge`
|
|
40249
|
+
);
|
|
40250
|
+
try {
|
|
40251
|
+
await exec2(
|
|
40252
|
+
"git",
|
|
40253
|
+
["commit", "-m", `baro: auto-commit uncommitted work for story ${storyId}`],
|
|
40254
|
+
{ cwd: worktreePath }
|
|
40255
|
+
);
|
|
40256
|
+
} catch (e2) {
|
|
40257
|
+
this.log(
|
|
40258
|
+
`WARNING: failed to auto-commit story ${storyId}'s leftover work (${errMsg(e2)}); it will not be included in the merge`
|
|
40259
|
+
);
|
|
40260
|
+
}
|
|
40261
|
+
}
|
|
40262
|
+
async conflictedPaths() {
|
|
40263
|
+
try {
|
|
40264
|
+
const { stdout } = await exec2(
|
|
40265
|
+
"git",
|
|
40266
|
+
["diff", "--name-only", "--diff-filter=U"],
|
|
40267
|
+
{ cwd: this.repoRoot }
|
|
40268
|
+
);
|
|
40269
|
+
return stdout.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
40270
|
+
} catch {
|
|
40271
|
+
return [];
|
|
40272
|
+
}
|
|
40273
|
+
}
|
|
40274
|
+
async removeWorktreeQuiet(path6) {
|
|
40275
|
+
await execQuiet("git", ["worktree", "remove", "--force", path6], this.repoRoot);
|
|
40276
|
+
if (existsSync(path6)) {
|
|
40277
|
+
rmSyncQuiet(path6);
|
|
40278
|
+
await execQuiet("git", ["worktree", "prune"], this.repoRoot);
|
|
40279
|
+
}
|
|
40280
|
+
}
|
|
40281
|
+
async deleteBranchQuiet(branch) {
|
|
40282
|
+
await execQuiet("git", ["branch", "-D", branch], this.repoRoot);
|
|
40283
|
+
}
|
|
40284
|
+
};
|
|
40285
|
+
function sanitize(storyId) {
|
|
40286
|
+
return storyId.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
40287
|
+
}
|
|
40288
|
+
async function execQuiet(cmd, args, cwd) {
|
|
40289
|
+
try {
|
|
40290
|
+
await exec2(cmd, args, { cwd });
|
|
40291
|
+
} catch {
|
|
40292
|
+
}
|
|
40293
|
+
}
|
|
40294
|
+
function rmSyncQuiet(path6) {
|
|
40295
|
+
try {
|
|
40296
|
+
rmSync(path6, { recursive: true, force: true });
|
|
40297
|
+
} catch {
|
|
40298
|
+
}
|
|
40299
|
+
}
|
|
40300
|
+
function errMsg(e2) {
|
|
40301
|
+
return e2?.message ?? String(e2);
|
|
40302
|
+
}
|
|
40303
|
+
|
|
39994
40304
|
// ../baro-orchestrator/src/dag.ts
|
|
39995
40305
|
function buildDag(stories, options = {}) {
|
|
39996
40306
|
const onlyIncomplete = options.onlyIncomplete ?? false;
|
|
@@ -40159,8 +40469,8 @@ var Auditor = class extends BaseObserver {
|
|
|
40159
40469
|
};
|
|
40160
40470
|
|
|
40161
40471
|
// ../baro-orchestrator/src/participants/conductor.ts
|
|
40162
|
-
import { existsSync, readFileSync as readFileSync2 } from "fs";
|
|
40163
|
-
import { join } from "path";
|
|
40472
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
40473
|
+
import { join as join2 } from "path";
|
|
40164
40474
|
|
|
40165
40475
|
// ../baro-orchestrator/src/prd.ts
|
|
40166
40476
|
import { readFileSync, writeFileSync } from "fs";
|
|
@@ -40345,6 +40655,14 @@ var Conductor = class extends BaseObserver {
|
|
|
40345
40655
|
/** Resolved when the run terminates, exposed for callers that need it. */
|
|
40346
40656
|
done;
|
|
40347
40657
|
resolveDone;
|
|
40658
|
+
/**
|
|
40659
|
+
* Serializes event handling. Mozaik delivers events without awaiting the
|
|
40660
|
+
* handler, so two StoryResults settling together could interleave — one
|
|
40661
|
+
* spawning the next DAG level while another's `await onStoryPassed`
|
|
40662
|
+
* (worktree merge-back, #50) is still in flight, leaving the dependent
|
|
40663
|
+
* level without the merged work. Chaining handlers keeps them sequential.
|
|
40664
|
+
*/
|
|
40665
|
+
handleChain = Promise.resolve();
|
|
40348
40666
|
constructor(opts) {
|
|
40349
40667
|
super();
|
|
40350
40668
|
this.opts = {
|
|
@@ -40381,7 +40699,13 @@ var Conductor = class extends BaseObserver {
|
|
|
40381
40699
|
async onExternalEvent(_source, event) {
|
|
40382
40700
|
await this.handle(event);
|
|
40383
40701
|
}
|
|
40384
|
-
|
|
40702
|
+
handle(event) {
|
|
40703
|
+
const run = this.handleChain.then(() => this.handleEvent(event));
|
|
40704
|
+
this.handleChain = run.catch(() => {
|
|
40705
|
+
});
|
|
40706
|
+
return run;
|
|
40707
|
+
}
|
|
40708
|
+
async handleEvent(event) {
|
|
40385
40709
|
if (RunStartRequest.is(event)) {
|
|
40386
40710
|
await this.handleRunStart();
|
|
40387
40711
|
return;
|
|
@@ -40521,6 +40845,20 @@ var Conductor = class extends BaseObserver {
|
|
|
40521
40845
|
} else {
|
|
40522
40846
|
this.currentLevel.failed.push(item.storyId);
|
|
40523
40847
|
this.addGlobalFailed(item.storyId);
|
|
40848
|
+
if (this.opts.onStoryFailed) {
|
|
40849
|
+
try {
|
|
40850
|
+
await this.opts.onStoryFailed(item.storyId);
|
|
40851
|
+
} catch (e2) {
|
|
40852
|
+
this.emit(
|
|
40853
|
+
ConductorState.create({
|
|
40854
|
+
phase: "running_level",
|
|
40855
|
+
detail: `onStoryFailed hook for ${item.storyId} failed: ${e2?.message ?? String(e2)}`,
|
|
40856
|
+
currentLevel: this.currentLevel.ordinal,
|
|
40857
|
+
totalLevels: this.currentLevel.totalLevelsHint
|
|
40858
|
+
})
|
|
40859
|
+
);
|
|
40860
|
+
}
|
|
40861
|
+
}
|
|
40524
40862
|
}
|
|
40525
40863
|
await this.fillSpawnSlots();
|
|
40526
40864
|
if (this.currentLevel.pending.size === 0) {
|
|
@@ -40790,9 +41128,9 @@ ${prompt}`;
|
|
|
40790
41128
|
return true;
|
|
40791
41129
|
}
|
|
40792
41130
|
resolvePrompt(story) {
|
|
40793
|
-
const candidatePath = this.opts.promptTemplatePath ??
|
|
41131
|
+
const candidatePath = this.opts.promptTemplatePath ?? join2(this.opts.cwd, "prompt.md");
|
|
40794
41132
|
let prompt;
|
|
40795
|
-
if (
|
|
41133
|
+
if (existsSync2(candidatePath)) {
|
|
40796
41134
|
const tpl = readFileSyncSafe(candidatePath);
|
|
40797
41135
|
prompt = tpl ? applyTemplate(tpl, story) : buildDefaultStoryPrompt(story);
|
|
40798
41136
|
} else {
|
|
@@ -40871,9 +41209,9 @@ function applyTemplate(tpl, story) {
|
|
|
40871
41209
|
}
|
|
40872
41210
|
|
|
40873
41211
|
// ../baro-orchestrator/src/participants/critic.ts
|
|
40874
|
-
import { execFile as
|
|
40875
|
-
import { promisify as
|
|
40876
|
-
var execFileAsync =
|
|
41212
|
+
import { execFile as execFile3 } from "child_process";
|
|
41213
|
+
import { promisify as promisify4 } from "util";
|
|
41214
|
+
var execFileAsync = promisify4(execFile3);
|
|
40877
41215
|
var VERDICT_SYSTEM_PROMPT = `You are a strict acceptance-criteria evaluator. You will receive:
|
|
40878
41216
|
1. A list of acceptance criteria that must ALL be satisfied.
|
|
40879
41217
|
2. The output text produced by an agent.
|
|
@@ -42009,9 +42347,9 @@ ${userPrompt}`;
|
|
|
42009
42347
|
};
|
|
42010
42348
|
|
|
42011
42349
|
// ../baro-orchestrator/src/participants/finalizer.ts
|
|
42012
|
-
import { execFile as
|
|
42013
|
-
import { promisify as
|
|
42014
|
-
var execFileAsync2 =
|
|
42350
|
+
import { execFile as execFile4 } from "child_process";
|
|
42351
|
+
import { promisify as promisify5 } from "util";
|
|
42352
|
+
var execFileAsync2 = promisify5(execFile4);
|
|
42015
42353
|
var Finalizer = class extends BaseObserver {
|
|
42016
42354
|
opts;
|
|
42017
42355
|
envRef = null;
|
|
@@ -42923,10 +43261,10 @@ function tokenizeHints(prompt) {
|
|
|
42923
43261
|
|
|
42924
43262
|
// ../baro-orchestrator/src/participants/memory-librarian.ts
|
|
42925
43263
|
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync3 } from "fs";
|
|
42926
|
-
import { join as
|
|
43264
|
+
import { join as join4 } from "path";
|
|
42927
43265
|
var DEBUG = process.env.BARO_DEBUG?.includes("memory") ?? false;
|
|
42928
|
-
var LOG_DIR =
|
|
42929
|
-
var LOG_FILE =
|
|
43266
|
+
var LOG_DIR = join4(process.env.HOME || "/tmp", ".baro", "runs");
|
|
43267
|
+
var LOG_FILE = join4(LOG_DIR, `memory-${Date.now()}.log`);
|
|
42930
43268
|
try {
|
|
42931
43269
|
mkdirSync3(LOG_DIR, { recursive: true });
|
|
42932
43270
|
} catch {
|
|
@@ -46831,6 +47169,8 @@ var StoryFactory = class extends BaseObserver {
|
|
|
46831
47169
|
// narrows to vanilla AgenticEnvironment.
|
|
46832
47170
|
envRef = null;
|
|
46833
47171
|
active = /* @__PURE__ */ new Map();
|
|
47172
|
+
/** Story ids whose spawn is in progress (closes the await-create window). */
|
|
47173
|
+
spawning = /* @__PURE__ */ new Set();
|
|
46834
47174
|
setEnvironment(env) {
|
|
46835
47175
|
this.envRef = env;
|
|
46836
47176
|
}
|
|
@@ -46849,7 +47189,16 @@ var StoryFactory = class extends BaseObserver {
|
|
|
46849
47189
|
}
|
|
46850
47190
|
async spawn(req) {
|
|
46851
47191
|
if (!this.envRef) return;
|
|
46852
|
-
if (this.active.has(req.storyId)) return;
|
|
47192
|
+
if (this.active.has(req.storyId) || this.spawning.has(req.storyId)) return;
|
|
47193
|
+
this.spawning.add(req.storyId);
|
|
47194
|
+
try {
|
|
47195
|
+
await this.buildAndLaunch(req);
|
|
47196
|
+
} finally {
|
|
47197
|
+
this.spawning.delete(req.storyId);
|
|
47198
|
+
}
|
|
47199
|
+
}
|
|
47200
|
+
async buildAndLaunch(req) {
|
|
47201
|
+
if (!this.envRef) return;
|
|
46853
47202
|
const route = resolveStoryRoute(req.model, {
|
|
46854
47203
|
tierMap: this.opts.tierMap,
|
|
46855
47204
|
fallbackBackend: this.opts.llm ?? "claude",
|
|
@@ -46861,24 +47210,25 @@ var StoryFactory = class extends BaseObserver {
|
|
|
46861
47210
|
process.stderr.write(
|
|
46862
47211
|
`[story-factory] ${req.storyId} \u2192 ${formatRoute(route)}` + (req.model ? ` (model="${req.model}")` : "") + "\n"
|
|
46863
47212
|
);
|
|
47213
|
+
const storyCwd = this.opts.worktrees ? await this.opts.worktrees.create(req.storyId) ?? this.opts.cwd : this.opts.cwd;
|
|
46864
47214
|
const agent = route.backend === "pi" ? new PiStoryAgent({
|
|
46865
47215
|
id: req.storyId,
|
|
46866
47216
|
prompt: req.prompt,
|
|
46867
|
-
cwd:
|
|
47217
|
+
cwd: storyCwd,
|
|
46868
47218
|
model: route.model,
|
|
46869
47219
|
retries: req.retries,
|
|
46870
47220
|
timeoutSecs: req.timeoutSecs
|
|
46871
47221
|
}) : route.backend === "opencode" ? new OpenCodeStoryAgent({
|
|
46872
47222
|
id: req.storyId,
|
|
46873
47223
|
prompt: req.prompt,
|
|
46874
|
-
cwd:
|
|
47224
|
+
cwd: storyCwd,
|
|
46875
47225
|
model: route.model,
|
|
46876
47226
|
retries: req.retries,
|
|
46877
47227
|
timeoutSecs: req.timeoutSecs
|
|
46878
47228
|
}) : route.backend === "codex" ? new CodexStoryAgent({
|
|
46879
47229
|
id: req.storyId,
|
|
46880
47230
|
prompt: req.prompt,
|
|
46881
|
-
cwd:
|
|
47231
|
+
cwd: storyCwd,
|
|
46882
47232
|
// undefined → let Codex pick its account default.
|
|
46883
47233
|
model: route.model,
|
|
46884
47234
|
retries: req.retries,
|
|
@@ -46887,7 +47237,7 @@ var StoryFactory = class extends BaseObserver {
|
|
|
46887
47237
|
{
|
|
46888
47238
|
id: req.storyId,
|
|
46889
47239
|
prompt: req.prompt,
|
|
46890
|
-
cwd:
|
|
47240
|
+
cwd: storyCwd,
|
|
46891
47241
|
model: route.model,
|
|
46892
47242
|
retries: req.retries,
|
|
46893
47243
|
timeoutSecs: req.timeoutSecs
|
|
@@ -46900,7 +47250,7 @@ var StoryFactory = class extends BaseObserver {
|
|
|
46900
47250
|
) : new StoryAgent({
|
|
46901
47251
|
id: req.storyId,
|
|
46902
47252
|
prompt: req.prompt,
|
|
46903
|
-
cwd:
|
|
47253
|
+
cwd: storyCwd,
|
|
46904
47254
|
// undefined → StoryAgent applies its own default.
|
|
46905
47255
|
model: route.model,
|
|
46906
47256
|
effort: this.opts.effort,
|
|
@@ -46918,9 +47268,9 @@ var StoryFactory = class extends BaseObserver {
|
|
|
46918
47268
|
};
|
|
46919
47269
|
|
|
46920
47270
|
// ../baro-orchestrator/src/participants/surgeon.ts
|
|
46921
|
-
import { execFile as
|
|
46922
|
-
import { promisify as
|
|
46923
|
-
var execFileAsync3 =
|
|
47271
|
+
import { execFile as execFile5 } from "child_process";
|
|
47272
|
+
import { promisify as promisify6 } from "util";
|
|
47273
|
+
var execFileAsync3 = promisify6(execFile5);
|
|
46924
47274
|
var SURGEON_SYSTEM_PROMPT = `You are the Surgeon \u2014 an autonomous planner that adapts a software-project
|
|
46925
47275
|
DAG when stories fail. Given:
|
|
46926
47276
|
1. A snapshot of the current PRD (project, story list with dependencies +
|
|
@@ -47574,11 +47924,19 @@ async function orchestrate(config) {
|
|
|
47574
47924
|
const useGit = config.withGit ?? await isInsideGitRepo(config.cwd);
|
|
47575
47925
|
const gitGate = new GitGate();
|
|
47576
47926
|
let baseSha = null;
|
|
47927
|
+
const runId = `run-${Date.now()}-${process.pid}`;
|
|
47928
|
+
const worktreesEnabled = config.withWorktrees ?? !("BARO_NO_WORKTREES" in process.env);
|
|
47929
|
+
const worktrees = useGit && worktreesEnabled ? new WorktreeManager(config.cwd, gitGate, runId, {
|
|
47930
|
+
linkDepDirs: config.worktreeLinkDepDirs ?? true,
|
|
47931
|
+
onLog: (line) => emitTui && emit({ type: "story_log", id: "_git", line })
|
|
47932
|
+
}) : null;
|
|
47933
|
+
const storyPushes = [];
|
|
47934
|
+
let worktreePushNeeded = false;
|
|
47577
47935
|
const useLibrarian = config.withLibrarian ?? true;
|
|
47578
47936
|
const useSentry = config.withSentry ?? true;
|
|
47579
47937
|
const useMemory = config.withMemory ?? true;
|
|
47580
|
-
const sessionsDir =
|
|
47581
|
-
const memorySessionPath = useMemory ?
|
|
47938
|
+
const sessionsDir = join6(process.env.HOME || "/tmp", ".baro", "sessions");
|
|
47939
|
+
const memorySessionPath = useMemory ? join6(sessionsDir, runId, "memory") : void 0;
|
|
47582
47940
|
if (useMemory) {
|
|
47583
47941
|
try {
|
|
47584
47942
|
const { pruneOldSessions: pruneOldSessions2 } = await Promise.resolve().then(() => (init_dist2(), dist_exports));
|
|
@@ -47702,6 +48060,7 @@ async function orchestrate(config) {
|
|
|
47702
48060
|
(line) => emitTui && emit({ type: "story_log", id: "_git", line })
|
|
47703
48061
|
);
|
|
47704
48062
|
}
|
|
48063
|
+
await worktrees?.cleanupStaleOnStart();
|
|
47705
48064
|
} : void 0,
|
|
47706
48065
|
onBeforeStoryLaunch: librarian ? (storyId, story) => {
|
|
47707
48066
|
const hints = [
|
|
@@ -47711,40 +48070,50 @@ async function orchestrate(config) {
|
|
|
47711
48070
|
return librarian.gatherContext(storyId, hints);
|
|
47712
48071
|
} : void 0,
|
|
47713
48072
|
onStoryPassed: useGit ? async (storyId) => {
|
|
47714
|
-
|
|
47715
|
-
|
|
47716
|
-
|
|
47717
|
-
|
|
47718
|
-
|
|
47719
|
-
|
|
47720
|
-
|
|
47721
|
-
|
|
47722
|
-
|
|
47723
|
-
|
|
47724
|
-
|
|
47725
|
-
emit({
|
|
47726
|
-
type: "push_status",
|
|
47727
|
-
id: storyId,
|
|
47728
|
-
success: true,
|
|
47729
|
-
error: null
|
|
47730
|
-
});
|
|
48073
|
+
const log2 = (line) => emitTui && emit({ type: "story_log", id: storyId, line });
|
|
48074
|
+
if (worktrees) {
|
|
48075
|
+
let merged = false;
|
|
48076
|
+
try {
|
|
48077
|
+
merged = await worktrees.mergeBack(storyId);
|
|
48078
|
+
} catch (e2) {
|
|
48079
|
+
log2(`[git] merge-back failed; worktree preserved for recovery: ${e2?.message ?? String(e2)}`);
|
|
48080
|
+
if (emitTui) {
|
|
48081
|
+
emit({ type: "push_status", id: storyId, success: false, error: e2?.message ?? String(e2) });
|
|
48082
|
+
}
|
|
48083
|
+
return;
|
|
47731
48084
|
}
|
|
47732
|
-
|
|
47733
|
-
|
|
47734
|
-
|
|
47735
|
-
|
|
47736
|
-
id: storyId,
|
|
47737
|
-
|
|
47738
|
-
|
|
47739
|
-
});
|
|
48085
|
+
if (merged) {
|
|
48086
|
+
await worktrees.cleanup(storyId);
|
|
48087
|
+
worktreePushNeeded = true;
|
|
48088
|
+
if (emitTui) {
|
|
48089
|
+
emit({ type: "push_status", id: storyId, success: true, error: null });
|
|
48090
|
+
}
|
|
48091
|
+
return;
|
|
47740
48092
|
}
|
|
47741
48093
|
}
|
|
47742
|
-
|
|
48094
|
+
await safePullRebase(config.cwd, log2, gitGate);
|
|
48095
|
+
storyPushes.push(
|
|
48096
|
+
(async () => {
|
|
48097
|
+
try {
|
|
48098
|
+
await gitPushWithRetry(gitGate, { cwd: config.cwd, onLog: log2 });
|
|
48099
|
+
if (emitTui) {
|
|
48100
|
+
emit({ type: "push_status", id: storyId, success: true, error: null });
|
|
48101
|
+
}
|
|
48102
|
+
} catch (e2) {
|
|
48103
|
+
if (emitTui) {
|
|
48104
|
+
emit({ type: "push_status", id: storyId, success: false, error: e2?.message ?? String(e2) });
|
|
48105
|
+
}
|
|
48106
|
+
}
|
|
48107
|
+
})()
|
|
48108
|
+
);
|
|
48109
|
+
} : void 0,
|
|
48110
|
+
onStoryFailed: worktrees ? (storyId) => worktrees.cleanup(storyId) : void 0
|
|
47743
48111
|
});
|
|
47744
48112
|
conductor.setEnvironment(env);
|
|
47745
48113
|
conductor.join(env);
|
|
47746
48114
|
const storyFactory = new StoryFactory({
|
|
47747
48115
|
cwd: config.cwd,
|
|
48116
|
+
worktrees: worktrees ?? void 0,
|
|
47748
48117
|
llm: storyLlm,
|
|
47749
48118
|
openaiModel: config.storyModel ?? "gpt-5.5",
|
|
47750
48119
|
storyModelOverride: config.storyModel,
|
|
@@ -47776,6 +48145,17 @@ async function orchestrate(config) {
|
|
|
47776
48145
|
RunStartRequest.create({ reason: "orchestrate" })
|
|
47777
48146
|
);
|
|
47778
48147
|
const summary = await conductor.done;
|
|
48148
|
+
await Promise.allSettled(storyPushes);
|
|
48149
|
+
if (worktreePushNeeded) {
|
|
48150
|
+
const log2 = (line) => emitTui && emit({ type: "story_log", id: "_git", line });
|
|
48151
|
+
try {
|
|
48152
|
+
await gitPushWithRetry(gitGate, { cwd: config.cwd, onLog: log2 });
|
|
48153
|
+
if (emitTui) emit({ type: "push_status", id: "_git", success: true, error: null });
|
|
48154
|
+
} catch (e2) {
|
|
48155
|
+
if (emitTui) emit({ type: "push_status", id: "_git", success: false, error: e2?.message ?? String(e2) });
|
|
48156
|
+
}
|
|
48157
|
+
}
|
|
48158
|
+
await worktrees?.cleanupAll();
|
|
47779
48159
|
if (critic) await critic.idle();
|
|
47780
48160
|
if (surgeon) await surgeon.idle();
|
|
47781
48161
|
if (finalizer) await finalizer.complete();
|
|
@@ -48054,7 +48434,7 @@ async function main() {
|
|
|
48054
48434
|
}
|
|
48055
48435
|
const cwd = resolve3(args.cwd);
|
|
48056
48436
|
const prdPath = resolve3(cwd, args.prd);
|
|
48057
|
-
if (!
|
|
48437
|
+
if (!existsSync6(prdPath)) {
|
|
48058
48438
|
process.stderr.write(`[cli] PRD not found: ${prdPath}
|
|
48059
48439
|
`);
|
|
48060
48440
|
process.exit(2);
|