toyon 0.8.0 → 0.8.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/cli.js +16 -1
- package/dist/daemon.js +201 -34
- package/dist/shell/assets/{Editor-CFH2QbSg.js → Editor-CrfFlcLA.js} +4 -4
- package/dist/shell/assets/{XTerm-C3aMkE_w.js → XTerm-CsuyFSMK.js} +1 -1
- package/dist/shell/assets/{cssMode-dc6AdHsZ.js → cssMode-CWkICf31.js} +1 -1
- package/dist/shell/assets/{freemarker2-CxtmNsLB.js → freemarker2-C2fP12sE.js} +1 -1
- package/dist/shell/assets/{grammar-B-X0FPRk.js → grammar-5S_Kl1e8.js} +1 -1
- package/dist/shell/assets/{handlebars-DVzmQ0KM.js → handlebars-CkRyqZGC.js} +1 -1
- package/dist/shell/assets/{html-CqqXL9ke.js → html-C9iJKWKA.js} +1 -1
- package/dist/shell/assets/{htmlMode-Bg-Em0rL.js → htmlMode-Dps-6NJd.js} +1 -1
- package/dist/shell/assets/index-BKyLPsvB.js +258 -0
- package/dist/shell/assets/index-DIDGdnCY.css +1 -0
- package/dist/shell/assets/{javascript-W-C4G7BB.js → javascript-DM5EiBY7.js} +1 -1
- package/dist/shell/assets/{jsonMode-De4ajMpV.js → jsonMode-ampRCTsW.js} +1 -1
- package/dist/shell/assets/{liquid-BSUg_7LQ.js → liquid-CEUat1ZS.js} +1 -1
- package/dist/shell/assets/{lspLanguageFeatures-BImXYajo.js → lspLanguageFeatures-ChupARNW.js} +1 -1
- package/dist/shell/assets/{mdx-B9A7RorP.js → mdx-DAglTq4w.js} +1 -1
- package/dist/shell/assets/{python-BD9sq3DF.js → python-DOPtAG-1.js} +1 -1
- package/dist/shell/assets/{razor-DGQVRh5-.js → razor-qRDOBjWc.js} +1 -1
- package/dist/shell/assets/{tsMode-DudQlgN_.js → tsMode-BD49NPBt.js} +1 -1
- package/dist/shell/assets/{xml-BI6-GyBn.js → xml-p5Js1H7t.js} +1 -1
- package/dist/shell/assets/{yaml-BEUvyYtP.js → yaml-CctH9qfh.js} +1 -1
- package/dist/shell/index.html +2 -2
- package/package.json +1 -1
- package/dist/shell/assets/index-BskWflhx.css +0 -1
- package/dist/shell/assets/index-C8QP4sgP.js +0 -255
package/dist/cli.js
CHANGED
|
@@ -5158,7 +5158,7 @@ function parseVersion(v) {
|
|
|
5158
5158
|
// package.json
|
|
5159
5159
|
var package_default = {
|
|
5160
5160
|
name: "toyon",
|
|
5161
|
-
version: "0.8.
|
|
5161
|
+
version: "0.8.1",
|
|
5162
5162
|
description: "Toyon is where coding agents work on your web app while you check each change by using it. Every chat gets its own running copy with a live preview.",
|
|
5163
5163
|
license: "MIT",
|
|
5164
5164
|
type: "module",
|
|
@@ -6181,6 +6181,19 @@ function policyLines(m, daemon) {
|
|
|
6181
6181
|
}
|
|
6182
6182
|
return lines;
|
|
6183
6183
|
}
|
|
6184
|
+
function agentLines(agents) {
|
|
6185
|
+
const level = agents.filter((a) => a.installed === a.pinned);
|
|
6186
|
+
const lines = [];
|
|
6187
|
+
if (level.length > 0)
|
|
6188
|
+
lines.push(line(true, "agents", level.map((a) => `${a.id} ${a.installed}`).join(", ")));
|
|
6189
|
+
for (const a of agents) {
|
|
6190
|
+
if (a.installed === a.pinned)
|
|
6191
|
+
continue;
|
|
6192
|
+
const why = a.error ? `: ${a.error}` : "; it may still be installing";
|
|
6193
|
+
lines.push(line(false, "agents", a.installed === null ? `${a.id} ${a.pinned} is not installed${why}` : `${a.id} runs ${a.installed}, not the pinned ${a.pinned}${why}. \`toyon restart\` tries again`));
|
|
6194
|
+
}
|
|
6195
|
+
return lines;
|
|
6196
|
+
}
|
|
6184
6197
|
async function doctor() {
|
|
6185
6198
|
const lines = [];
|
|
6186
6199
|
lines.push(line(true, "cli", `toyon ${package_default.version}, bun ${Bun.version}, ${process.platform} ${process.arch}`));
|
|
@@ -6209,6 +6222,8 @@ async function doctor() {
|
|
|
6209
6222
|
const { total, running } = h.worktrees;
|
|
6210
6223
|
lines.push(line(true, "running", `${running} of ${total} worktrees started; the rest start when opened`));
|
|
6211
6224
|
}
|
|
6225
|
+
if (h.agents)
|
|
6226
|
+
lines.push(...agentLines(h.agents));
|
|
6212
6227
|
if (version !== package_default.version) {
|
|
6213
6228
|
lines.push(line(false, "version", `cli is ${package_default.version} and the daemon is ${version}; \`toyon restart\` brings them level`));
|
|
6214
6229
|
}
|
package/dist/daemon.js
CHANGED
|
@@ -6469,7 +6469,7 @@ var PAIR_TTL_MS = 120000;
|
|
|
6469
6469
|
var pairLink = (host, code) => `https://${host}/#pair=${code}`;
|
|
6470
6470
|
|
|
6471
6471
|
// ../shared/src/protocol/limits.ts
|
|
6472
|
-
var PROTOCOL_VERSION =
|
|
6472
|
+
var PROTOCOL_VERSION = 83;
|
|
6473
6473
|
var FILE_MAX_CHARS = 5000000;
|
|
6474
6474
|
var IMAGE_MIME_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"];
|
|
6475
6475
|
var IMAGE_MAX_EDGE = 2576;
|
|
@@ -7882,15 +7882,6 @@ function installCommand(method, version) {
|
|
|
7882
7882
|
return ["bun", "add", "-g", `toyon@${version}`];
|
|
7883
7883
|
return null;
|
|
7884
7884
|
}
|
|
7885
|
-
function registryHost(registry) {
|
|
7886
|
-
if (registry === null || registry === "")
|
|
7887
|
-
return "the npm registry";
|
|
7888
|
-
try {
|
|
7889
|
-
return new URL(registry).host;
|
|
7890
|
-
} catch {
|
|
7891
|
-
return registry;
|
|
7892
|
-
}
|
|
7893
|
-
}
|
|
7894
7885
|
function newer(a, b) {
|
|
7895
7886
|
const pa = parseVersion(a);
|
|
7896
7887
|
const pb = parseVersion(b);
|
|
@@ -8163,7 +8154,7 @@ function safeUser() {
|
|
|
8163
8154
|
// ../daemon/package.json
|
|
8164
8155
|
var package_default = {
|
|
8165
8156
|
name: "@toyon/daemon",
|
|
8166
|
-
version: "0.8.
|
|
8157
|
+
version: "0.8.1",
|
|
8167
8158
|
private: true,
|
|
8168
8159
|
type: "module",
|
|
8169
8160
|
main: "src/index.ts",
|
|
@@ -13667,6 +13658,19 @@ class AgentRegistry {
|
|
|
13667
13658
|
await this.install(id);
|
|
13668
13659
|
}
|
|
13669
13660
|
}
|
|
13661
|
+
adapters() {
|
|
13662
|
+
const out = [];
|
|
13663
|
+
for (const spec of this.list()) {
|
|
13664
|
+
if (spec.run.kind === "command" || !this.packageName(spec))
|
|
13665
|
+
continue;
|
|
13666
|
+
const installed = this.installed(spec) ? this.installedVersion(spec) : null;
|
|
13667
|
+
const error = this.installErrors.get(spec.id);
|
|
13668
|
+
if (spec.onDemand && installed === null && !error)
|
|
13669
|
+
continue;
|
|
13670
|
+
out.push({ id: spec.id, installed, pinned: spec.run.version, ...error ? { error } : {} });
|
|
13671
|
+
}
|
|
13672
|
+
return out;
|
|
13673
|
+
}
|
|
13670
13674
|
infos() {
|
|
13671
13675
|
return this.list().map((spec) => {
|
|
13672
13676
|
const reason = this.unavailable(spec);
|
|
@@ -14145,6 +14149,9 @@ function backgroundStart(input, output) {
|
|
|
14145
14149
|
const m = STARTED.exec(output);
|
|
14146
14150
|
return m ? { taskId: m[1], file: m[2] } : null;
|
|
14147
14151
|
}
|
|
14152
|
+
function spawnDetached(output) {
|
|
14153
|
+
return output.includes("Async agent launched successfully");
|
|
14154
|
+
}
|
|
14148
14155
|
function claudeConfigDir() {
|
|
14149
14156
|
return process.env.CLAUDE_CONFIG_DIR || join7(homedir4(), ".claude");
|
|
14150
14157
|
}
|
|
@@ -14466,7 +14473,8 @@ function spawnOf(meta) {
|
|
|
14466
14473
|
const claude = asRecord(asRecord(meta).claudeCode);
|
|
14467
14474
|
const parentToolId = typeof claude.parentToolUseId === "string" ? claude.parentToolUseId : undefined;
|
|
14468
14475
|
const codex = asRecord(asRecord(meta).codex).subagent;
|
|
14469
|
-
const
|
|
14476
|
+
const spawns = claude.toolName === "Agent" || claude.toolName === "Task";
|
|
14477
|
+
const subagent = spawns || claude.subagent === true || Object.keys(asRecord(codex)).length > 0;
|
|
14470
14478
|
return { ...parentToolId ? { parentToolId } : {}, ...subagent ? { subagent: true } : {} };
|
|
14471
14479
|
}
|
|
14472
14480
|
function asRecord(v) {
|
|
@@ -14556,7 +14564,8 @@ function mapUpdate(update, memos, tag, sink, background) {
|
|
|
14556
14564
|
rawOutput: update.rawOutput,
|
|
14557
14565
|
ended: false,
|
|
14558
14566
|
writing: isWrittenKind(head.kind) && emptyInput(input),
|
|
14559
|
-
...spawn.parentToolId ? { parent: spawn.parentToolId } : {}
|
|
14567
|
+
...spawn.parentToolId ? { parent: spawn.parentToolId } : {},
|
|
14568
|
+
...spawn.subagent ? { spawns: true } : {}
|
|
14560
14569
|
};
|
|
14561
14570
|
memos.set(update.toolCallId, memo);
|
|
14562
14571
|
out.push({
|
|
@@ -14586,7 +14595,8 @@ function mapUpdate(update, memos, tag, sink, background) {
|
|
|
14586
14595
|
content: [],
|
|
14587
14596
|
ended: false,
|
|
14588
14597
|
writing: isWrittenKind(update.kind ?? undefined) && emptyInput(input),
|
|
14589
|
-
...spawn.parentToolId ? { parent: spawn.parentToolId } : {}
|
|
14598
|
+
...spawn.parentToolId ? { parent: spawn.parentToolId } : {},
|
|
14599
|
+
...spawn.subagent ? { spawns: true } : {}
|
|
14590
14600
|
};
|
|
14591
14601
|
memos.set(update.toolCallId, memo);
|
|
14592
14602
|
out.push({
|
|
@@ -14664,14 +14674,23 @@ function endOf(toolId, memo, status, sink, background) {
|
|
|
14664
14674
|
return { type: "tool-update", toolId, background: true };
|
|
14665
14675
|
}
|
|
14666
14676
|
const images = sink ? toolImages(memo.content, sink) : [];
|
|
14677
|
+
const detached = status === "completed" && !!memo.spawns && spawnDetached(wholeText(memo.content, memo.rawOutput));
|
|
14667
14678
|
return {
|
|
14668
14679
|
type: "tool-end",
|
|
14669
14680
|
toolId,
|
|
14670
14681
|
output,
|
|
14671
14682
|
isError: status === "failed" || exitCodeOf(memo.rawOutput) > 0,
|
|
14683
|
+
...detached ? { detached: true } : {},
|
|
14672
14684
|
...images.length ? { images } : {}
|
|
14673
14685
|
};
|
|
14674
14686
|
}
|
|
14687
|
+
function wholeText(content, rawOutput) {
|
|
14688
|
+
const parts = content.flatMap((c) => c.type === "content" && c.content.type === "text" ? [c.content.text] : []);
|
|
14689
|
+
if (typeof rawOutput === "string")
|
|
14690
|
+
parts.push(rawOutput);
|
|
14691
|
+
return parts.join(`
|
|
14692
|
+
`);
|
|
14693
|
+
}
|
|
14675
14694
|
function toolImages(content, sink) {
|
|
14676
14695
|
const out = [];
|
|
14677
14696
|
for (const c of content) {
|
|
@@ -15267,7 +15286,8 @@ class AcpSession {
|
|
|
15267
15286
|
sessionId,
|
|
15268
15287
|
cwd: this.d.cwd,
|
|
15269
15288
|
mcpServers: [],
|
|
15270
|
-
additionalDirectories
|
|
15289
|
+
additionalDirectories,
|
|
15290
|
+
...conn.spec.systemPrompt === "meta-append" ? { _meta: { systemPrompt: { append: SYSTEM_APPEND } } } : {}
|
|
15271
15291
|
});
|
|
15272
15292
|
modes = r.modes;
|
|
15273
15293
|
configOptions = r.configOptions;
|
|
@@ -26123,8 +26143,8 @@ var handlers = {
|
|
|
26123
26143
|
"update-now"(_msg, _ctx, s) {
|
|
26124
26144
|
return s.update.updateNow();
|
|
26125
26145
|
},
|
|
26126
|
-
"check-update"(_msg,
|
|
26127
|
-
|
|
26146
|
+
async "check-update"(_msg, ctx, s) {
|
|
26147
|
+
ctx.reply({ t: "update-checked", check: await s.update.checkNow() });
|
|
26128
26148
|
},
|
|
26129
26149
|
async commit(msg, ctx, s) {
|
|
26130
26150
|
const result = await s.worktrees.commit(msg.worktreeId, msg.message);
|
|
@@ -26695,6 +26715,7 @@ function startServer(opts) {
|
|
|
26695
26715
|
},
|
|
26696
26716
|
costs: s.idle.costs(),
|
|
26697
26717
|
updates: s.update.status(),
|
|
26718
|
+
agents: s.agents.adapters(),
|
|
26698
26719
|
sockets: [...sockets].map((ws) => ({ subs: [...ws.data.subs], sent: ws.data.sent, bytes: ws.data.bytes }))
|
|
26699
26720
|
});
|
|
26700
26721
|
let statusesInFlight = false;
|
|
@@ -27295,16 +27316,20 @@ class UpdateService {
|
|
|
27295
27316
|
async check() {
|
|
27296
27317
|
if (this.d.method === "none" || this.d.managedBy)
|
|
27297
27318
|
return;
|
|
27319
|
+
await this.ask();
|
|
27320
|
+
}
|
|
27321
|
+
async ask() {
|
|
27298
27322
|
const answer = await this.d.latest();
|
|
27299
27323
|
this.registryName = answer.registry;
|
|
27300
27324
|
if (answer.version === null) {
|
|
27301
27325
|
this.unreachable = answer.registry;
|
|
27302
|
-
return;
|
|
27326
|
+
return { registry: answer.registry, latest: null };
|
|
27303
27327
|
}
|
|
27304
27328
|
this.unreachable = null;
|
|
27305
27329
|
this.latest = newer(answer.version, this.d.running) ? answer.version : null;
|
|
27306
27330
|
this.announce();
|
|
27307
27331
|
await this.auto();
|
|
27332
|
+
return { registry: answer.registry, latest: answer.version };
|
|
27308
27333
|
}
|
|
27309
27334
|
async updateNow() {
|
|
27310
27335
|
this.refuseManaged();
|
|
@@ -27321,14 +27346,17 @@ class UpdateService {
|
|
|
27321
27346
|
async checkNow() {
|
|
27322
27347
|
this.refuseManaged();
|
|
27323
27348
|
if (this.d.method === "none") {
|
|
27324
|
-
|
|
27349
|
+
const answer = await this.d.latest();
|
|
27350
|
+
return { registry: answer.registry, latest: answer.version };
|
|
27325
27351
|
}
|
|
27326
|
-
await this.
|
|
27327
|
-
|
|
27328
|
-
|
|
27329
|
-
|
|
27330
|
-
|
|
27352
|
+
const answer = await this.ask();
|
|
27353
|
+
const target = this.target();
|
|
27354
|
+
if (target !== null && !this.wanted && !(this.needsInstall() && this.d.command(target) === null)) {
|
|
27355
|
+
this.failed = null;
|
|
27356
|
+
this.wanted = true;
|
|
27357
|
+
fireAndForget("update", this.proceed(), "update");
|
|
27331
27358
|
}
|
|
27359
|
+
return answer;
|
|
27332
27360
|
}
|
|
27333
27361
|
install() {
|
|
27334
27362
|
return this.d.method;
|
|
@@ -27983,9 +28011,14 @@ class LandingService {
|
|
|
27983
28011
|
d.hub.on("agentStatus", (id, status) => {
|
|
27984
28012
|
this.note(id, status);
|
|
27985
28013
|
if (status === "working")
|
|
27986
|
-
this.
|
|
28014
|
+
this.turnStarts(id);
|
|
27987
28015
|
});
|
|
27988
28016
|
}
|
|
28017
|
+
turnStarts(worktreeId) {
|
|
28018
|
+
if (!this.d.state.worktree(worktreeId)?.landing?.ready)
|
|
28019
|
+
return this.clear(worktreeId);
|
|
28020
|
+
this.judging.delete(worktreeId);
|
|
28021
|
+
}
|
|
27989
28022
|
note(worktreeId, status) {
|
|
27990
28023
|
if (status === "working" || status === "waiting")
|
|
27991
28024
|
this.busy.add(worktreeId);
|
|
@@ -28011,14 +28044,69 @@ class LandingService {
|
|
|
28011
28044
|
const wt = this.d.state.worktree(worktreeId);
|
|
28012
28045
|
if (!wt || !canLand(wt))
|
|
28013
28046
|
return;
|
|
28014
|
-
|
|
28015
|
-
|
|
28047
|
+
const held = await this.standing(wt);
|
|
28048
|
+
if (turn.end !== "done") {
|
|
28049
|
+
if (!held)
|
|
28050
|
+
this.clear(worktreeId);
|
|
28051
|
+
return;
|
|
28052
|
+
}
|
|
28016
28053
|
if (!await this.hasWork(wt)) {
|
|
28017
28054
|
this.clear(worktreeId);
|
|
28018
28055
|
return this.recapAnswer(wt, turn);
|
|
28019
28056
|
}
|
|
28057
|
+
if (held)
|
|
28058
|
+
return this.reword(wt, turn, held);
|
|
28020
28059
|
await this.run(wt, { at: turn.at, ask: true, quiet: false });
|
|
28021
28060
|
}
|
|
28061
|
+
async standing(wt) {
|
|
28062
|
+
const l = wt.landing;
|
|
28063
|
+
if (!l?.ready)
|
|
28064
|
+
return;
|
|
28065
|
+
return l.fingerprint === await treeFingerprint(wt.path) ? l : undefined;
|
|
28066
|
+
}
|
|
28067
|
+
async reword(wt, turn, held) {
|
|
28068
|
+
if (!this.d.judge)
|
|
28069
|
+
return;
|
|
28070
|
+
const worktreeId = wt.id;
|
|
28071
|
+
const repo = this.d.state.requireRepo(wt.repoId);
|
|
28072
|
+
this.judging.set(worktreeId, turn.at);
|
|
28073
|
+
const started = Date.now();
|
|
28074
|
+
const entries = this.d.transcript(worktreeId);
|
|
28075
|
+
const prompt = landPrompt({
|
|
28076
|
+
title: wt.title,
|
|
28077
|
+
firstAsk: firstAskOf(entries),
|
|
28078
|
+
turns: turnsSince(entries, 0),
|
|
28079
|
+
diffStat: await diffSummary(wt.path, baseOf(repo)),
|
|
28080
|
+
recentSubjects: await recentSubjects(wt.path, baseOf(repo))
|
|
28081
|
+
});
|
|
28082
|
+
let verdict = null;
|
|
28083
|
+
try {
|
|
28084
|
+
verdict = await this.d.judge(wt, prompt);
|
|
28085
|
+
} catch (e) {
|
|
28086
|
+
log.warn(worktreeId, "landing verdict kept its words: the question went unanswered", e);
|
|
28087
|
+
}
|
|
28088
|
+
if (this.stopping || this.judging.get(worktreeId) !== turn.at)
|
|
28089
|
+
return;
|
|
28090
|
+
this.judging.delete(worktreeId);
|
|
28091
|
+
const row = this.d.state.worktree(worktreeId);
|
|
28092
|
+
const now = row?.landing;
|
|
28093
|
+
if (!verdict || !row || !now?.ready || now.fingerprint !== held.fingerprint)
|
|
28094
|
+
return;
|
|
28095
|
+
const { why: _was, stale: _moved, ...rest } = now;
|
|
28096
|
+
const caveat = await this.sharedMigration(wt, repo);
|
|
28097
|
+
const doubt = [!verdict.ready && verdict.why ? verdict.why : "", caveat ?? ""].filter(Boolean).join(". ");
|
|
28098
|
+
const landing = { ...rest, at: turn.at, ...doubt ? { why: doubt } : {} };
|
|
28099
|
+
if (!landing.subject && verdict.subject) {
|
|
28100
|
+
landing.subject = verdict.subject;
|
|
28101
|
+
if (verdict.body)
|
|
28102
|
+
landing.body = verdict.body;
|
|
28103
|
+
delete landing.unanswered;
|
|
28104
|
+
}
|
|
28105
|
+
if (row.lastTurn?.at === turn.at && verdict.recap)
|
|
28106
|
+
row.lastTurn.recap = { at: Date.now(), text: verdict.recap };
|
|
28107
|
+
log.info(worktreeId, `landing: tree unchanged, check not run again${doubt ? ", doubted" : ""}, ${Date.now() - started}ms`);
|
|
28108
|
+
this.d.worktrees.setLanding(worktreeId, landing);
|
|
28109
|
+
}
|
|
28022
28110
|
async recapAnswer(wt, turn) {
|
|
28023
28111
|
if (!this.d.recap)
|
|
28024
28112
|
return;
|
|
@@ -28742,9 +28830,12 @@ function overwritten(text) {
|
|
|
28742
28830
|
}
|
|
28743
28831
|
return files;
|
|
28744
28832
|
}
|
|
28833
|
+
var listed = (files) => files.length > 3 ? `${files.slice(0, 3).join(", ")} and ${files.length - 3} more` : files.join(", ");
|
|
28745
28834
|
function inTheWay(defaultBr, what, files) {
|
|
28746
|
-
|
|
28747
|
-
|
|
28835
|
+
return `${defaultBr} has uncommitted files the ${what} would overwrite (${listed(files)}): commit or stash them there first`;
|
|
28836
|
+
}
|
|
28837
|
+
function untrackedInTheWay(defaultBr, files) {
|
|
28838
|
+
return `${defaultBr} has files that are untracked here (${listed(files)}): move or delete them, then sync`;
|
|
28748
28839
|
}
|
|
28749
28840
|
async function requireMainReady(repoPath, defaultBr, clean) {
|
|
28750
28841
|
const current = await git(repoPath, "branch", "--show-current");
|
|
@@ -28756,18 +28847,75 @@ async function requireMainReady(repoPath, defaultBr, clean) {
|
|
|
28756
28847
|
}
|
|
28757
28848
|
return null;
|
|
28758
28849
|
}
|
|
28759
|
-
|
|
28760
|
-
|
|
28761
|
-
|
|
28762
|
-
|
|
28850
|
+
var CARRIED = "refs/worktree/toyon/carried";
|
|
28851
|
+
async function setAside(worktreePath) {
|
|
28852
|
+
const head = await git(worktreePath, "rev-parse", "HEAD");
|
|
28853
|
+
const made = await git(worktreePath, "stash", "create");
|
|
28854
|
+
if (!head.ok || !made.ok) {
|
|
28855
|
+
return { ok: false, message: `could not set the uncommitted changes aside: ${made.err.slice(0, 200)}` };
|
|
28856
|
+
}
|
|
28857
|
+
if (!made.out)
|
|
28858
|
+
return null;
|
|
28859
|
+
const kept = await git(worktreePath, "update-ref", CARRIED, made.out);
|
|
28860
|
+
const cleared = kept.ok ? await git(worktreePath, "reset", "--hard", "-q") : kept;
|
|
28861
|
+
if (!cleared.ok) {
|
|
28862
|
+
return { ok: false, message: `could not set the uncommitted changes aside: ${cleared.err.slice(0, 200)}` };
|
|
28863
|
+
}
|
|
28864
|
+
return { sha: made.out, head: head.out };
|
|
28865
|
+
}
|
|
28866
|
+
async function putBack(worktreePath, defaultBr, aside, taken) {
|
|
28867
|
+
let clash = null;
|
|
28868
|
+
if (taken.ok) {
|
|
28869
|
+
const applied = await git(worktreePath, "stash", "apply", aside.sha);
|
|
28870
|
+
if (applied.ok) {
|
|
28871
|
+
await git(worktreePath, "update-ref", "-d", CARRIED);
|
|
28872
|
+
return taken;
|
|
28873
|
+
}
|
|
28874
|
+
const unmerged = await git(worktreePath, "diff", "--name-only", "--diff-filter=U");
|
|
28875
|
+
clash = unmerged.out.split(`
|
|
28876
|
+
`).filter(Boolean);
|
|
28877
|
+
await git(worktreePath, "reset", "--hard", "-q", aside.head);
|
|
28878
|
+
}
|
|
28879
|
+
const back = await git(worktreePath, "stash", "apply", "--index", aside.sha);
|
|
28880
|
+
if (!back.ok) {
|
|
28881
|
+
return {
|
|
28882
|
+
ok: false,
|
|
28883
|
+
message: `sync stopped with the uncommitted changes set aside: git stash apply ${CARRIED} in the worktree brings them back`
|
|
28884
|
+
};
|
|
28885
|
+
}
|
|
28886
|
+
await git(worktreePath, "update-ref", "-d", CARRIED);
|
|
28887
|
+
if (!clash)
|
|
28888
|
+
return taken;
|
|
28889
|
+
const named = clash.length ? ` (${listed(clash)})` : "";
|
|
28890
|
+
return {
|
|
28891
|
+
ok: false,
|
|
28892
|
+
message: `the uncommitted changes here conflict with ${defaultBr}${named}: commit them first, then sync`
|
|
28893
|
+
};
|
|
28894
|
+
}
|
|
28895
|
+
async function takeMainIn(worktreePath, defaultBr, own, w = UNWATCHED, carry = false) {
|
|
28896
|
+
if (!carry) {
|
|
28897
|
+
const cErr = await requireClean(worktreePath);
|
|
28898
|
+
if (cErr)
|
|
28899
|
+
return cErr;
|
|
28900
|
+
}
|
|
28763
28901
|
const { behind } = await aheadBehind(worktreePath, defaultBr);
|
|
28764
28902
|
if (behind === 0)
|
|
28765
28903
|
return { ok: true, message: `already up to date with ${defaultBr}` };
|
|
28904
|
+
const aside = carry ? await setAside(worktreePath) : null;
|
|
28905
|
+
if (aside && "message" in aside)
|
|
28906
|
+
return aside;
|
|
28907
|
+
const taken = await bringIn(worktreePath, defaultBr, own, behind, w);
|
|
28908
|
+
return aside ? putBack(worktreePath, defaultBr, aside, taken) : taken;
|
|
28909
|
+
}
|
|
28910
|
+
async function bringIn(worktreePath, defaultBr, own, behind, w) {
|
|
28766
28911
|
if (own) {
|
|
28767
28912
|
w.step(`rebasing onto ${defaultBr}`);
|
|
28768
28913
|
const r = await w.git(worktreePath, ["rebase", defaultBr]);
|
|
28769
28914
|
if (!r.ok) {
|
|
28770
28915
|
await git(worktreePath, "rebase", "--abort");
|
|
28916
|
+
const files = overwritten(r.text);
|
|
28917
|
+
if (files.length)
|
|
28918
|
+
return { ok: false, message: untrackedInTheWay(defaultBr, files) };
|
|
28771
28919
|
const ask = `rebase onto ${defaultBr} conflicts: ask the agent to bring ${defaultBr} in and resolve them`;
|
|
28772
28920
|
return {
|
|
28773
28921
|
ok: false,
|
|
@@ -28780,6 +28928,9 @@ async function takeMainIn(worktreePath, defaultBr, own, w = UNWATCHED) {
|
|
|
28780
28928
|
w.step(`merging ${defaultBr} in`);
|
|
28781
28929
|
const m = await w.git(worktreePath, ["merge", "--no-edit", defaultBr]);
|
|
28782
28930
|
if (!m.ok) {
|
|
28931
|
+
const files = overwritten(m.text);
|
|
28932
|
+
if (files.length)
|
|
28933
|
+
return { ok: false, message: untrackedInTheWay(defaultBr, files) };
|
|
28783
28934
|
return mergeFailure(worktreePath, m, `sync conflicts with ${defaultBr}: ask the agent to merge ${defaultBr} and resolve them`);
|
|
28784
28935
|
}
|
|
28785
28936
|
return { ok: true, message: `synced ${behind} commit(s) from ${defaultBr}` };
|
|
@@ -30706,12 +30857,24 @@ ${wt.landing.body}` : s;
|
|
|
30706
30857
|
const { wt, repo } = this.landable(worktreeId, "land");
|
|
30707
30858
|
const out = await this.ship(wt.id, "land", async () => {
|
|
30708
30859
|
await this.followBranch(wt);
|
|
30860
|
+
const moved = await this.movedMidTurn(wt);
|
|
30861
|
+
if (moved)
|
|
30862
|
+
return { result: moved };
|
|
30709
30863
|
return this.landOp(wt, repo, message);
|
|
30710
30864
|
});
|
|
30711
30865
|
if (out.result.ok && out.archiveIds)
|
|
30712
30866
|
this.noteLanded(wt, out.result.message, out.archiveIds);
|
|
30713
30867
|
return out;
|
|
30714
30868
|
}
|
|
30869
|
+
async movedMidTurn(wt) {
|
|
30870
|
+
const status = this.d.runtime.agentFor(wt.id)?.status;
|
|
30871
|
+
if (status !== "working" && status !== "waiting")
|
|
30872
|
+
return null;
|
|
30873
|
+
const l = wt.landing;
|
|
30874
|
+
if (!l || l.fingerprint === await treeFingerprint(wt.path))
|
|
30875
|
+
return null;
|
|
30876
|
+
return { ok: false, message: "the agent has changed files since the check: land once its turn ends" };
|
|
30877
|
+
}
|
|
30715
30878
|
noteLanded(wt, message, archiveIds) {
|
|
30716
30879
|
this.d.runtime.ensureAgent(wt).agent.note({ type: "landed", message, archiveIds, ts: Date.now() });
|
|
30717
30880
|
}
|
|
@@ -30983,6 +31146,10 @@ ${wt.landing.body}` : wt.landing.subject : undefined;
|
|
|
30983
31146
|
throw new UserError(`${r.name} is held by another tool`);
|
|
30984
31147
|
const repo = this.d.state.requireRepo(r.repoId);
|
|
30985
31148
|
const own = r.wt ? hasOwnBranch(r.wt) : false;
|
|
31149
|
+
const carry = !!r.wt;
|
|
31150
|
+
const status = this.d.runtime.agentFor(worktreeId)?.status;
|
|
31151
|
+
if (carry && (status === "working" || status === "waiting") && (await statusFiles(r.path)).length > 0)
|
|
31152
|
+
throw new UserError(`${r.name}'s agent is mid-turn with uncommitted changes: sync when it is done`);
|
|
30986
31153
|
const w = r.wt ? this.landWatch(r.wt) : UNWATCHED;
|
|
30987
31154
|
const result = await this.ship(worktreeId, "sync-main", async () => {
|
|
30988
31155
|
if (r.wt) {
|
|
@@ -30990,7 +31157,7 @@ ${wt.landing.body}` : wt.landing.subject : undefined;
|
|
|
30990
31157
|
if (!fetched.ok)
|
|
30991
31158
|
return fetched;
|
|
30992
31159
|
}
|
|
30993
|
-
const taken = await withRepoLock(repo.path, () => takeMainIn(r.path, r.base, own, w));
|
|
31160
|
+
const taken = await withRepoLock(repo.path, () => takeMainIn(r.path, r.base, own, w, carry));
|
|
30994
31161
|
if (taken.ok) {
|
|
30995
31162
|
this.headMoved(worktreeId);
|
|
30996
31163
|
if (r.wt)
|