webmux 0.22.1 → 0.24.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/backend/dist/server.js +203 -119
- package/bin/webmux.js +195 -112
- package/frontend/dist/assets/index-BRoz1T_W.js +151 -0
- package/frontend/dist/assets/{index-Dxe_PLr0.css → index-D9R5ycW2.css} +1 -1
- package/frontend/dist/index.html +2 -2
- package/package.json +1 -1
- package/frontend/dist/assets/index-BukbebAA.js +0 -150
package/backend/dist/server.js
CHANGED
|
@@ -8630,7 +8630,7 @@ function buildAgentInvocation(input) {
|
|
|
8630
8630
|
if (input.launchMode === "resume") {
|
|
8631
8631
|
return `codex${yoloFlag2} resume --last`;
|
|
8632
8632
|
}
|
|
8633
|
-
const promptSuffix2 = input.prompt ? ` ${quoteShell(input.prompt)}` : "";
|
|
8633
|
+
const promptSuffix2 = input.prompt ? ` -- ${quoteShell(input.prompt)}` : "";
|
|
8634
8634
|
if (input.systemPrompt) {
|
|
8635
8635
|
return `codex${yoloFlag2} -c ${quoteShell(`developer_instructions=${input.systemPrompt}`)}${promptSuffix2}`;
|
|
8636
8636
|
}
|
|
@@ -8640,7 +8640,7 @@ function buildAgentInvocation(input) {
|
|
|
8640
8640
|
if (input.launchMode === "resume") {
|
|
8641
8641
|
return `claude${yoloFlag} --continue`;
|
|
8642
8642
|
}
|
|
8643
|
-
const promptSuffix = input.prompt ? ` ${quoteShell(input.prompt)}` : "";
|
|
8643
|
+
const promptSuffix = input.prompt ? ` -- ${quoteShell(input.prompt)}` : "";
|
|
8644
8644
|
if (input.systemPrompt) {
|
|
8645
8645
|
return `claude${yoloFlag} --append-system-prompt ${quoteShell(input.systemPrompt)}${promptSuffix}`;
|
|
8646
8646
|
}
|
|
@@ -8968,6 +8968,9 @@ class BunGitGateway {
|
|
|
8968
8968
|
readWorktreeStatus(cwd) {
|
|
8969
8969
|
return readGitWorktreeStatus(cwd);
|
|
8970
8970
|
}
|
|
8971
|
+
readStatus(cwd) {
|
|
8972
|
+
return runGit(["status", "--short", "--untracked-files=all"], cwd);
|
|
8973
|
+
}
|
|
8971
8974
|
createWorktree(opts) {
|
|
8972
8975
|
const args = ["worktree", "add"];
|
|
8973
8976
|
if (opts.mode === "new") {
|
|
@@ -9211,6 +9214,18 @@ function toErrorMessage2(error) {
|
|
|
9211
9214
|
function stringifyStartupEnvValue(value) {
|
|
9212
9215
|
return typeof value === "boolean" ? String(value) : value;
|
|
9213
9216
|
}
|
|
9217
|
+
function prefixAgentBranch(agent, branch) {
|
|
9218
|
+
return `${agent}-${branch}`;
|
|
9219
|
+
}
|
|
9220
|
+
function buildCreateWorktreeTargets(branch, agentSelection) {
|
|
9221
|
+
if (agentSelection === "both") {
|
|
9222
|
+
return [
|
|
9223
|
+
{ branch: prefixAgentBranch("claude", branch), agent: "claude" },
|
|
9224
|
+
{ branch: prefixAgentBranch("codex", branch), agent: "codex" }
|
|
9225
|
+
];
|
|
9226
|
+
}
|
|
9227
|
+
return [{ branch, agent: agentSelection }];
|
|
9228
|
+
}
|
|
9214
9229
|
|
|
9215
9230
|
class LifecycleError extends Error {
|
|
9216
9231
|
status;
|
|
@@ -9225,114 +9240,47 @@ class LifecycleService {
|
|
|
9225
9240
|
constructor(deps) {
|
|
9226
9241
|
this.deps = deps;
|
|
9227
9242
|
}
|
|
9228
|
-
async
|
|
9243
|
+
async createWorktrees(input) {
|
|
9229
9244
|
const mode = input.mode ?? "new";
|
|
9230
|
-
const
|
|
9231
|
-
if (
|
|
9232
|
-
throw new LifecycleError("
|
|
9233
|
-
}
|
|
9234
|
-
if (requestedBaseBranch && mode === "existing") {
|
|
9235
|
-
throw new LifecycleError("Base branch is only supported for new worktrees", 400);
|
|
9245
|
+
const agentSelection = input.agent ?? this.deps.config.workspace.defaultAgent;
|
|
9246
|
+
if (agentSelection === "both" && mode === "existing") {
|
|
9247
|
+
throw new LifecycleError("Creating both agents is only supported for new worktrees", 400);
|
|
9236
9248
|
}
|
|
9237
9249
|
const branch = await this.resolveBranch(input.branch, input.prompt, mode);
|
|
9238
|
-
|
|
9239
|
-
|
|
9240
|
-
}
|
|
9241
|
-
const baseBranch = mode === "new" ? requestedBaseBranch || this.deps.config.workspace.mainBranch : undefined;
|
|
9242
|
-
const branchAvailability = this.resolveBranchAvailability(branch, mode);
|
|
9243
|
-
const { profileName, profile } = this.resolveProfile(input.profile);
|
|
9244
|
-
const agent = this.resolveAgent(input.agent);
|
|
9245
|
-
const worktreePath = this.resolveWorktreePath(branch);
|
|
9246
|
-
const createProgressBase = {
|
|
9247
|
-
branch,
|
|
9248
|
-
...baseBranch ? { baseBranch } : {},
|
|
9249
|
-
path: worktreePath,
|
|
9250
|
-
profile: profileName,
|
|
9251
|
-
agent
|
|
9252
|
-
};
|
|
9253
|
-
const deleteBranchOnRollback = mode === "new" || branchAvailability.deleteBranchOnRollback;
|
|
9254
|
-
let initialized = null;
|
|
9250
|
+
const targets = buildCreateWorktreeTargets(branch, agentSelection);
|
|
9251
|
+
const createdBranches = [];
|
|
9255
9252
|
try {
|
|
9256
|
-
|
|
9257
|
-
|
|
9258
|
-
|
|
9259
|
-
|
|
9260
|
-
|
|
9261
|
-
|
|
9262
|
-
|
|
9263
|
-
|
|
9264
|
-
|
|
9265
|
-
mode,
|
|
9266
|
-
...baseBranch ? { baseBranch } : {},
|
|
9267
|
-
...branchAvailability.startPoint ? { startPoint: branchAvailability.startPoint } : {},
|
|
9268
|
-
profile: profileName,
|
|
9269
|
-
agent,
|
|
9270
|
-
runtime: profile.runtime,
|
|
9271
|
-
startupEnvValues: await this.buildStartupEnvValues(input.envOverrides),
|
|
9272
|
-
allocatedPorts: await this.allocatePorts(),
|
|
9273
|
-
runtimeEnvExtras: { WEBMUX_WORKTREE_PATH: worktreePath },
|
|
9274
|
-
controlUrl: this.controlUrl(),
|
|
9275
|
-
controlToken: await this.deps.getControlToken(),
|
|
9276
|
-
deleteBranchOnRollback
|
|
9277
|
-
}, {
|
|
9278
|
-
git: this.deps.git
|
|
9279
|
-
});
|
|
9280
|
-
await this.reportCreateProgress({
|
|
9281
|
-
...createProgressBase,
|
|
9282
|
-
phase: "running_post_create_hook"
|
|
9283
|
-
});
|
|
9284
|
-
await this.runLifecycleHook({
|
|
9285
|
-
name: "postCreate",
|
|
9286
|
-
command: this.deps.config.lifecycleHooks.postCreate,
|
|
9287
|
-
meta: initialized.meta,
|
|
9288
|
-
worktreePath
|
|
9289
|
-
});
|
|
9290
|
-
initialized = await this.refreshManagedArtifactsFromMeta({
|
|
9291
|
-
gitDir: initialized.paths.gitDir,
|
|
9292
|
-
meta: initialized.meta,
|
|
9293
|
-
worktreePath
|
|
9294
|
-
});
|
|
9295
|
-
await this.reportCreateProgress({
|
|
9296
|
-
...createProgressBase,
|
|
9297
|
-
phase: "preparing_runtime"
|
|
9298
|
-
});
|
|
9299
|
-
await ensureAgentRuntimeArtifacts({
|
|
9300
|
-
gitDir: initialized.paths.gitDir,
|
|
9301
|
-
worktreePath
|
|
9302
|
-
});
|
|
9303
|
-
await this.reportCreateProgress({
|
|
9304
|
-
...createProgressBase,
|
|
9305
|
-
phase: "starting_session"
|
|
9306
|
-
});
|
|
9307
|
-
await this.materializeRuntimeSession({
|
|
9308
|
-
branch,
|
|
9309
|
-
profile,
|
|
9310
|
-
agent,
|
|
9311
|
-
initialized,
|
|
9312
|
-
worktreePath,
|
|
9313
|
-
prompt: input.prompt,
|
|
9314
|
-
launchMode: "fresh"
|
|
9315
|
-
});
|
|
9316
|
-
await this.reportCreateProgress({
|
|
9317
|
-
...createProgressBase,
|
|
9318
|
-
phase: "reconciling"
|
|
9319
|
-
});
|
|
9320
|
-
await this.deps.reconciliation.reconcile(this.deps.projectRoot, { force: true });
|
|
9321
|
-
return {
|
|
9322
|
-
branch,
|
|
9323
|
-
worktreeId: initialized.meta.worktreeId
|
|
9324
|
-
};
|
|
9253
|
+
for (const target of targets) {
|
|
9254
|
+
const created = await this.createResolvedWorktree({
|
|
9255
|
+
...input,
|
|
9256
|
+
mode,
|
|
9257
|
+
branch: target.branch,
|
|
9258
|
+
agent: target.agent
|
|
9259
|
+
});
|
|
9260
|
+
createdBranches.push(created.branch);
|
|
9261
|
+
}
|
|
9325
9262
|
} catch (error) {
|
|
9326
|
-
|
|
9327
|
-
|
|
9328
|
-
|
|
9329
|
-
throw this.wrapOperationError(new Error(`${toErrorMessage2(error)}; ${cleanupError}`));
|
|
9330
|
-
}
|
|
9263
|
+
const rollbackError = await this.rollbackCreatedWorktrees(createdBranches);
|
|
9264
|
+
if (rollbackError) {
|
|
9265
|
+
throw this.wrapOperationError(new Error(`${toErrorMessage2(error)}; ${rollbackError}`));
|
|
9331
9266
|
}
|
|
9332
9267
|
throw this.wrapOperationError(error);
|
|
9333
|
-
} finally {
|
|
9334
|
-
await this.finishCreateProgress(branch);
|
|
9335
9268
|
}
|
|
9269
|
+
return {
|
|
9270
|
+
primaryBranch: createdBranches[0],
|
|
9271
|
+
branches: createdBranches
|
|
9272
|
+
};
|
|
9273
|
+
}
|
|
9274
|
+
async createWorktree(input) {
|
|
9275
|
+
const mode = input.mode ?? "new";
|
|
9276
|
+
const branch = await this.resolveBranch(input.branch, input.prompt, mode);
|
|
9277
|
+
const agent = this.resolveAgent(input.agent);
|
|
9278
|
+
return await this.createResolvedWorktree({
|
|
9279
|
+
...input,
|
|
9280
|
+
mode,
|
|
9281
|
+
branch,
|
|
9282
|
+
agent
|
|
9283
|
+
});
|
|
9336
9284
|
}
|
|
9337
9285
|
async openWorktree(branch) {
|
|
9338
9286
|
try {
|
|
@@ -9733,6 +9681,123 @@ class LifecycleService {
|
|
|
9733
9681
|
async finishCreateProgress(branch) {
|
|
9734
9682
|
await this.deps.onCreateFinished?.(branch);
|
|
9735
9683
|
}
|
|
9684
|
+
async rollbackCreatedWorktrees(branches) {
|
|
9685
|
+
const cleanupErrors = [];
|
|
9686
|
+
for (const branch of [...branches].reverse()) {
|
|
9687
|
+
try {
|
|
9688
|
+
await this.removeWorktree(branch);
|
|
9689
|
+
} catch (error) {
|
|
9690
|
+
cleanupErrors.push(`rollback failed for ${branch}: ${toErrorMessage2(error)}`);
|
|
9691
|
+
}
|
|
9692
|
+
}
|
|
9693
|
+
return cleanupErrors.length > 0 ? cleanupErrors.join("; ") : null;
|
|
9694
|
+
}
|
|
9695
|
+
async createResolvedWorktree(input) {
|
|
9696
|
+
const requestedBaseBranch = input.baseBranch?.trim();
|
|
9697
|
+
if (requestedBaseBranch && !isValidBranchName(requestedBaseBranch)) {
|
|
9698
|
+
throw new LifecycleError("Invalid base branch name", 400);
|
|
9699
|
+
}
|
|
9700
|
+
if (requestedBaseBranch && input.mode === "existing") {
|
|
9701
|
+
throw new LifecycleError("Base branch is only supported for new worktrees", 400);
|
|
9702
|
+
}
|
|
9703
|
+
if (requestedBaseBranch && requestedBaseBranch === input.branch) {
|
|
9704
|
+
throw new LifecycleError("Base branch must differ from branch name", 400);
|
|
9705
|
+
}
|
|
9706
|
+
const baseBranch = input.mode === "new" ? requestedBaseBranch || this.deps.config.workspace.mainBranch : undefined;
|
|
9707
|
+
const branchAvailability = this.resolveBranchAvailability(input.branch, input.mode);
|
|
9708
|
+
const { profileName, profile } = this.resolveProfile(input.profile);
|
|
9709
|
+
const worktreePath = this.resolveWorktreePath(input.branch);
|
|
9710
|
+
const createProgressBase = {
|
|
9711
|
+
branch: input.branch,
|
|
9712
|
+
...baseBranch ? { baseBranch } : {},
|
|
9713
|
+
path: worktreePath,
|
|
9714
|
+
profile: profileName,
|
|
9715
|
+
agent: input.agent
|
|
9716
|
+
};
|
|
9717
|
+
const deleteBranchOnRollback = input.mode === "new" || branchAvailability.deleteBranchOnRollback;
|
|
9718
|
+
let initialized = null;
|
|
9719
|
+
try {
|
|
9720
|
+
await this.reportCreateProgress({
|
|
9721
|
+
...createProgressBase,
|
|
9722
|
+
phase: "creating_worktree"
|
|
9723
|
+
});
|
|
9724
|
+
await mkdir4(dirname4(worktreePath), { recursive: true });
|
|
9725
|
+
initialized = await createManagedWorktree({
|
|
9726
|
+
repoRoot: this.deps.projectRoot,
|
|
9727
|
+
worktreePath,
|
|
9728
|
+
branch: input.branch,
|
|
9729
|
+
mode: input.mode,
|
|
9730
|
+
...baseBranch ? { baseBranch } : {},
|
|
9731
|
+
...branchAvailability.startPoint ? { startPoint: branchAvailability.startPoint } : {},
|
|
9732
|
+
profile: profileName,
|
|
9733
|
+
agent: input.agent,
|
|
9734
|
+
runtime: profile.runtime,
|
|
9735
|
+
startupEnvValues: await this.buildStartupEnvValues(input.envOverrides),
|
|
9736
|
+
allocatedPorts: await this.allocatePorts(),
|
|
9737
|
+
runtimeEnvExtras: { WEBMUX_WORKTREE_PATH: worktreePath },
|
|
9738
|
+
controlUrl: this.controlUrl(),
|
|
9739
|
+
controlToken: await this.deps.getControlToken(),
|
|
9740
|
+
deleteBranchOnRollback
|
|
9741
|
+
}, {
|
|
9742
|
+
git: this.deps.git
|
|
9743
|
+
});
|
|
9744
|
+
await this.reportCreateProgress({
|
|
9745
|
+
...createProgressBase,
|
|
9746
|
+
phase: "running_post_create_hook"
|
|
9747
|
+
});
|
|
9748
|
+
await this.runLifecycleHook({
|
|
9749
|
+
name: "postCreate",
|
|
9750
|
+
command: this.deps.config.lifecycleHooks.postCreate,
|
|
9751
|
+
meta: initialized.meta,
|
|
9752
|
+
worktreePath
|
|
9753
|
+
});
|
|
9754
|
+
initialized = await this.refreshManagedArtifactsFromMeta({
|
|
9755
|
+
gitDir: initialized.paths.gitDir,
|
|
9756
|
+
meta: initialized.meta,
|
|
9757
|
+
worktreePath
|
|
9758
|
+
});
|
|
9759
|
+
await this.reportCreateProgress({
|
|
9760
|
+
...createProgressBase,
|
|
9761
|
+
phase: "preparing_runtime"
|
|
9762
|
+
});
|
|
9763
|
+
await ensureAgentRuntimeArtifacts({
|
|
9764
|
+
gitDir: initialized.paths.gitDir,
|
|
9765
|
+
worktreePath
|
|
9766
|
+
});
|
|
9767
|
+
await this.reportCreateProgress({
|
|
9768
|
+
...createProgressBase,
|
|
9769
|
+
phase: "starting_session"
|
|
9770
|
+
});
|
|
9771
|
+
await this.materializeRuntimeSession({
|
|
9772
|
+
branch: input.branch,
|
|
9773
|
+
profile,
|
|
9774
|
+
agent: input.agent,
|
|
9775
|
+
initialized,
|
|
9776
|
+
worktreePath,
|
|
9777
|
+
prompt: input.prompt,
|
|
9778
|
+
launchMode: "fresh"
|
|
9779
|
+
});
|
|
9780
|
+
await this.reportCreateProgress({
|
|
9781
|
+
...createProgressBase,
|
|
9782
|
+
phase: "reconciling"
|
|
9783
|
+
});
|
|
9784
|
+
await this.deps.reconciliation.reconcile(this.deps.projectRoot, { force: true });
|
|
9785
|
+
return {
|
|
9786
|
+
branch: input.branch,
|
|
9787
|
+
worktreeId: initialized.meta.worktreeId
|
|
9788
|
+
};
|
|
9789
|
+
} catch (error) {
|
|
9790
|
+
if (initialized) {
|
|
9791
|
+
const cleanupError = await this.cleanupFailedCreate(input.branch, worktreePath, profile.runtime, deleteBranchOnRollback);
|
|
9792
|
+
if (cleanupError) {
|
|
9793
|
+
throw this.wrapOperationError(new Error(`${toErrorMessage2(error)}; ${cleanupError}`));
|
|
9794
|
+
}
|
|
9795
|
+
}
|
|
9796
|
+
throw this.wrapOperationError(error);
|
|
9797
|
+
} finally {
|
|
9798
|
+
await this.finishCreateProgress(input.branch);
|
|
9799
|
+
}
|
|
9800
|
+
}
|
|
9736
9801
|
wrapOperationError(error) {
|
|
9737
9802
|
if (error instanceof LifecycleError) {
|
|
9738
9803
|
return error;
|
|
@@ -10849,6 +10914,8 @@ class BunPortProbe {
|
|
|
10849
10914
|
|
|
10850
10915
|
// backend/src/services/auto-name-service.ts
|
|
10851
10916
|
var MAX_BRANCH_LENGTH = 40;
|
|
10917
|
+
var DEFAULT_AUTO_NAME_MODEL = "claude-haiku-4-5-20251001";
|
|
10918
|
+
var AUTO_NAME_MAX_TOKENS = 50;
|
|
10852
10919
|
var DEFAULT_SYSTEM_PROMPT = [
|
|
10853
10920
|
"Generate a concise git branch name from the task description.",
|
|
10854
10921
|
"Return only the branch name.",
|
|
@@ -10899,11 +10966,12 @@ function buildClaudeArgs(model, systemPrompt, prompt) {
|
|
|
10899
10966
|
systemPrompt,
|
|
10900
10967
|
"--output-format",
|
|
10901
10968
|
"text",
|
|
10902
|
-
"--no-session-persistence"
|
|
10969
|
+
"--no-session-persistence",
|
|
10970
|
+
"--model",
|
|
10971
|
+
model || DEFAULT_AUTO_NAME_MODEL,
|
|
10972
|
+
"--max-tokens",
|
|
10973
|
+
String(AUTO_NAME_MAX_TOKENS)
|
|
10903
10974
|
];
|
|
10904
|
-
if (model) {
|
|
10905
|
-
args.push("--model", model);
|
|
10906
|
-
}
|
|
10907
10975
|
args.push(prompt);
|
|
10908
10976
|
return args;
|
|
10909
10977
|
}
|
|
@@ -10949,8 +11017,11 @@ class AutoNameService {
|
|
|
10949
11017
|
throw new Error(`'${cli}' CLI not found. Install it or check your PATH.`);
|
|
10950
11018
|
}
|
|
10951
11019
|
if (result.exitCode !== 0) {
|
|
10952
|
-
const
|
|
10953
|
-
|
|
11020
|
+
const stderr = result.stderr.trim();
|
|
11021
|
+
const stdout = result.stdout.trim();
|
|
11022
|
+
const output2 = stderr || stdout || `exit ${result.exitCode}`;
|
|
11023
|
+
const command = args.join(" ");
|
|
11024
|
+
throw new Error(`${cli} failed (command: ${command}): ${output2}`);
|
|
10954
11025
|
}
|
|
10955
11026
|
const output = result.stdout.trim();
|
|
10956
11027
|
if (!output) {
|
|
@@ -11433,6 +11504,7 @@ function createWebmuxRuntime(options = {}) {
|
|
|
11433
11504
|
autoName,
|
|
11434
11505
|
onCreateProgress: (progress) => {
|
|
11435
11506
|
worktreeCreationTracker.set(progress);
|
|
11507
|
+
options.onCreateProgress?.(progress);
|
|
11436
11508
|
},
|
|
11437
11509
|
onCreateFinished: (branch) => {
|
|
11438
11510
|
worktreeCreationTracker.clear(branch);
|
|
@@ -11769,13 +11841,17 @@ async function apiCreateWorktree(req) {
|
|
|
11769
11841
|
const baseBranch = typeof body.baseBranch === "string" && body.baseBranch.trim() ? body.baseBranch.trim() : undefined;
|
|
11770
11842
|
const prompt = typeof body.prompt === "string" && body.prompt.trim() ? body.prompt.trim() : undefined;
|
|
11771
11843
|
const profile = typeof body.profile === "string" ? body.profile : undefined;
|
|
11772
|
-
const agent = body.agent === "claude" || body.agent === "codex" ? body.agent : undefined;
|
|
11844
|
+
const agent = body.agent === "claude" || body.agent === "codex" || body.agent === "both" ? body.agent : undefined;
|
|
11773
11845
|
const createLinearTicket = body.createLinearTicket === true;
|
|
11774
11846
|
const linearTitle = typeof body.linearTitle === "string" && body.linearTitle.trim() ? body.linearTitle.trim() : undefined;
|
|
11775
11847
|
const mode = body.mode === "new" || body.mode === "existing" ? body.mode : undefined;
|
|
11848
|
+
const agentSelection = agent ?? config.workspace.defaultAgent;
|
|
11776
11849
|
if (body.mode !== undefined && body.mode !== "new" && body.mode !== "existing") {
|
|
11777
11850
|
return errorResponse("Invalid worktree create mode", 400);
|
|
11778
11851
|
}
|
|
11852
|
+
if (body.agent !== undefined && body.agent !== "claude" && body.agent !== "codex" && body.agent !== "both") {
|
|
11853
|
+
return errorResponse("Invalid agent selection", 400);
|
|
11854
|
+
}
|
|
11779
11855
|
if (baseBranch && !isValidBranchName(baseBranch)) {
|
|
11780
11856
|
return errorResponse("Invalid base branch name", 400);
|
|
11781
11857
|
}
|
|
@@ -11813,26 +11889,32 @@ async function apiCreateWorktree(req) {
|
|
|
11813
11889
|
return errorResponse(linearResult.error, 502);
|
|
11814
11890
|
}
|
|
11815
11891
|
resolvedBranch = linearResult.data.branchName;
|
|
11816
|
-
ensureBranchNotCreating(resolvedBranch);
|
|
11817
11892
|
log.info(`[linear] created ticket ${linearResult.data.identifier} branch=${linearResult.data.branchName} title="${linearResult.data.title.slice(0, 80)}"`);
|
|
11818
|
-
} else if (resolvedBranch) {
|
|
11819
|
-
ensureBranchNotCreating(resolvedBranch);
|
|
11820
11893
|
}
|
|
11821
|
-
if (resolvedBranch
|
|
11822
|
-
|
|
11894
|
+
if (resolvedBranch) {
|
|
11895
|
+
const targetBranches = buildCreateWorktreeTargets(resolvedBranch, agentSelection).map((target) => target.branch);
|
|
11896
|
+
for (const targetBranch of targetBranches) {
|
|
11897
|
+
ensureBranchNotBusy(targetBranch);
|
|
11898
|
+
}
|
|
11899
|
+
if (baseBranch && targetBranches.some((targetBranch) => targetBranch === baseBranch)) {
|
|
11900
|
+
return errorResponse("Base branch must differ from branch name", 400);
|
|
11901
|
+
}
|
|
11823
11902
|
}
|
|
11824
|
-
log.info(`[worktree:add] mode=${mode ?? "new"}${resolvedBranch ? ` branch=${resolvedBranch}` : ""}${baseBranch ? ` base=${baseBranch}` : ""}${profile ? ` profile=${profile}` : ""}
|
|
11825
|
-
const result = await lifecycleService.
|
|
11903
|
+
log.info(`[worktree:add] mode=${mode ?? "new"}${resolvedBranch ? ` branch=${resolvedBranch}` : ""}${baseBranch ? ` base=${baseBranch}` : ""}${profile ? ` profile=${profile}` : ""} agent=${agentSelection}${createLinearTicket ? " linearTicket=true" : ""}${prompt ? ` prompt="${prompt.slice(0, 80)}"` : ""}`);
|
|
11904
|
+
const result = await lifecycleService.createWorktrees({
|
|
11826
11905
|
mode,
|
|
11827
11906
|
branch: resolvedBranch,
|
|
11828
11907
|
baseBranch,
|
|
11829
11908
|
prompt,
|
|
11830
11909
|
profile,
|
|
11831
|
-
agent,
|
|
11910
|
+
agent: agentSelection,
|
|
11832
11911
|
envOverrides
|
|
11833
11912
|
});
|
|
11834
|
-
log.debug(`[worktree:add] done
|
|
11835
|
-
return jsonResponse({
|
|
11913
|
+
log.debug(`[worktree:add] done branches=${result.branches.join(",")}`);
|
|
11914
|
+
return jsonResponse({
|
|
11915
|
+
primaryBranch: result.primaryBranch,
|
|
11916
|
+
branches: result.branches
|
|
11917
|
+
}, 201);
|
|
11836
11918
|
}
|
|
11837
11919
|
async function apiDeleteWorktree(name) {
|
|
11838
11920
|
return withRemovingBranch(name, async () => {
|
|
@@ -11958,11 +12040,13 @@ async function apiGetWorktreeDiff(name) {
|
|
|
11958
12040
|
if (!state)
|
|
11959
12041
|
return errorResponse(`Worktree not found: ${name}`, 404);
|
|
11960
12042
|
const uncommitted = git.readDiff(state.path);
|
|
12043
|
+
const gitStatus = git.readStatus(state.path);
|
|
11961
12044
|
const unpushedCommits = git.listUnpushedCommits(state.path);
|
|
11962
12045
|
const truncated = uncommitted.length > MAX_DIFF_BYTES;
|
|
11963
12046
|
return jsonResponse({
|
|
11964
12047
|
uncommitted: truncated ? uncommitted.slice(0, MAX_DIFF_BYTES) : uncommitted,
|
|
11965
12048
|
uncommittedTruncated: truncated,
|
|
12049
|
+
gitStatus,
|
|
11966
12050
|
unpushedCommits
|
|
11967
12051
|
});
|
|
11968
12052
|
}
|