volute 0.8.3 → 0.10.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.
Files changed (45) hide show
  1. package/dist/{agent-YORVRB6I.js → agent-ECRX44DB.js} +10 -10
  2. package/dist/{agent-manager-CMMH5KQQ.js → agent-manager-MRHHKAB6.js} +2 -2
  3. package/dist/api-client-YPKOZP2O.js +10 -0
  4. package/dist/{channel-RDGHBFSI.js → channel-2WHBRDTD.js} +66 -81
  5. package/dist/chunk-4RQBJWQX.js +17 -0
  6. package/dist/{chunk-YNNK4QN2.js → chunk-FYQGANL6.js} +40 -2
  7. package/dist/chunk-R3VB7NF5.js +205 -0
  8. package/dist/{chunk-23L3MKEV.js → chunk-STOEJOJO.js} +18 -4
  9. package/dist/cli.js +14 -14
  10. package/dist/{connector-ZP6MEFF4.js → connector-L2HBLZBW.js} +37 -20
  11. package/dist/create-VBZZNJOG.js +38 -0
  12. package/dist/{daemon-client-54J3EIZD.js → daemon-client-P44NU3KU.js} +1 -1
  13. package/dist/daemon-restart-QCLR6ZZV.js +61 -0
  14. package/dist/daemon.js +1732 -214
  15. package/dist/delete-BOTVU4YO.js +35 -0
  16. package/dist/{down-4DGRZRJU.js → down-4LIQG3CE.js} +3 -1
  17. package/dist/{env-KMNYGVZ2.js → env-CGORIKVF.js} +86 -37
  18. package/dist/{history-PXJVYLVY.js → history-NI5QP27M.js} +11 -8
  19. package/dist/import-2BZUWT23.js +21 -0
  20. package/dist/logs-APWVWGNX.js +77 -0
  21. package/dist/{package-2S7APQBC.js → package-ERGXEDAF.js} +1 -1
  22. package/dist/{restart-KVH3TK5N.js → restart-CCYM3MEC.js} +10 -4
  23. package/dist/{schedule-HCUCBNQI.js → schedule-E4MFGYSA.js} +24 -8
  24. package/dist/{send-BNC2S5BY.js → send-X6OQGSD6.js} +36 -28
  25. package/dist/{start-QU73YTJW.js → start-6YRS6FF6.js} +7 -2
  26. package/dist/{status-Q6ZQJXNI.js → status-SIMKH3ZE.js} +8 -3
  27. package/dist/{stop-N7U5N6A7.js → stop-UQSNF4CG.js} +7 -2
  28. package/dist/{up-RZJMSVQS.js → up-MNNPCMFF.js} +1 -1
  29. package/dist/upgrade-RSE4CZNE.js +55 -0
  30. package/dist/variant-7IZF6OWO.js +215 -0
  31. package/package.json +1 -1
  32. package/dist/chunk-ECPQXRLB.js +0 -264
  33. package/dist/chunk-NETNFBA5.js +0 -28
  34. package/dist/chunk-XUA3JUFK.js +0 -121
  35. package/dist/create-HGJHLABX.js +0 -96
  36. package/dist/daemon-restart-IMNCBWFV.js +0 -28
  37. package/dist/delete-45TGQC4N.js +0 -67
  38. package/dist/import-CNEDF3TD.js +0 -532
  39. package/dist/logs-TZB3MTLZ.js +0 -37
  40. package/dist/upgrade-CZF6PN7Y.js +0 -224
  41. package/dist/variant-RKXPN5DH.js +0 -476
  42. package/dist/{chunk-6RDCTVQK.js → chunk-4NAGJV3I.js} +0 -0
  43. package/dist/{chunk-W6TMWYU3.js → chunk-WV4W7BAT.js} +3 -3
  44. package/dist/{service-56CY4S6Z.js → service-OW35VZ5G.js} +3 -3
  45. package/dist/{setup-7SPMWF2O.js → setup-ABMZK6LS.js} +3 -3
@@ -1,224 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- resolveAgentName
4
- } from "./chunk-AZEL2IEK.js";
5
- import {
6
- composeTemplate,
7
- copyTemplateToDir,
8
- findTemplatesRoot
9
- } from "./chunk-XUA3JUFK.js";
10
- import {
11
- parseArgs
12
- } from "./chunk-D424ZQGI.js";
13
- import {
14
- exec,
15
- execInherit
16
- } from "./chunk-5C5JWR2L.js";
17
- import {
18
- daemonFetch
19
- } from "./chunk-23L3MKEV.js";
20
- import {
21
- addVariant,
22
- nextPort,
23
- resolveAgent
24
- } from "./chunk-DP2DX4WV.js";
25
- import "./chunk-K3NQKI34.js";
26
-
27
- // src/commands/upgrade.ts
28
- import { existsSync, mkdirSync, rmSync } from "fs";
29
- import { resolve } from "path";
30
- var TEMPLATE_BRANCH = "volute/template";
31
- var VARIANT_NAME = "upgrade";
32
- async function run(args) {
33
- const { positional, flags } = parseArgs(args, {
34
- template: { type: "string" },
35
- continue: { type: "boolean" }
36
- });
37
- const agentName = resolveAgentName({ agent: positional[0] });
38
- const { dir: projectRoot } = resolveAgent(agentName);
39
- const template = flags.template ?? "agent-sdk";
40
- if (flags.continue) {
41
- await continueUpgrade(agentName, projectRoot);
42
- return;
43
- }
44
- const worktreeDir = resolve(projectRoot, ".variants", VARIANT_NAME);
45
- if (existsSync(worktreeDir)) {
46
- console.error(
47
- `Upgrade variant already exists: ${worktreeDir}
48
- If a previous upgrade is in progress, use --continue to finish it.
49
- Otherwise, remove it with: volute variant delete ${VARIANT_NAME} --agent ${agentName}`
50
- );
51
- process.exit(1);
52
- }
53
- await exec("git", ["worktree", "prune"], { cwd: projectRoot });
54
- try {
55
- await exec("git", ["branch", "-D", VARIANT_NAME], { cwd: projectRoot });
56
- } catch {
57
- }
58
- console.log("Updating template branch...");
59
- await updateTemplateBranch(projectRoot, template, agentName);
60
- console.log("Creating upgrade variant...");
61
- const parentDir = resolve(projectRoot, ".variants");
62
- if (!existsSync(parentDir)) {
63
- mkdirSync(parentDir, { recursive: true });
64
- }
65
- await exec("git", ["worktree", "add", "-b", VARIANT_NAME, worktreeDir], {
66
- cwd: projectRoot
67
- });
68
- console.log("Merging template changes...");
69
- const hasConflicts = await mergeTemplateBranch(worktreeDir);
70
- if (hasConflicts) {
71
- console.log("\nMerge conflicts detected. Resolve them in:");
72
- console.log(` ${worktreeDir}`);
73
- console.log(`
74
- Then run:`);
75
- console.log(` volute agent upgrade ${agentName} --continue`);
76
- return;
77
- }
78
- await installAndVerify(agentName, worktreeDir);
79
- }
80
- async function updateTemplateBranch(projectRoot, template, agentName) {
81
- const tempWorktree = resolve(projectRoot, ".variants", "_template_update");
82
- let branchExists = false;
83
- try {
84
- await exec("git", ["rev-parse", "--verify", TEMPLATE_BRANCH], {
85
- cwd: projectRoot
86
- });
87
- branchExists = true;
88
- } catch {
89
- }
90
- try {
91
- await exec("git", ["worktree", "remove", "--force", tempWorktree], { cwd: projectRoot });
92
- } catch {
93
- }
94
- if (existsSync(tempWorktree)) {
95
- rmSync(tempWorktree, { recursive: true, force: true });
96
- }
97
- const templatesRoot = findTemplatesRoot();
98
- const { composedDir, manifest } = composeTemplate(templatesRoot, template);
99
- try {
100
- if (branchExists) {
101
- await exec("git", ["worktree", "add", tempWorktree, TEMPLATE_BRANCH], {
102
- cwd: projectRoot
103
- });
104
- } else {
105
- await exec("git", ["worktree", "add", "--detach", tempWorktree], {
106
- cwd: projectRoot
107
- });
108
- await exec("git", ["checkout", "--orphan", TEMPLATE_BRANCH], {
109
- cwd: tempWorktree
110
- });
111
- await exec("git", ["rm", "-rf", "--cached", "."], { cwd: tempWorktree });
112
- await exec("git", ["clean", "-fd"], { cwd: tempWorktree });
113
- }
114
- if (branchExists) {
115
- await exec("git", ["rm", "-rf", "."], { cwd: tempWorktree }).catch(() => {
116
- });
117
- }
118
- copyTemplateToDir(composedDir, tempWorktree, agentName, manifest);
119
- const initDir = resolve(tempWorktree, ".init");
120
- if (existsSync(initDir)) {
121
- rmSync(initDir, { recursive: true, force: true });
122
- }
123
- await exec("git", ["add", "-A"], { cwd: tempWorktree });
124
- try {
125
- await exec("git", ["diff", "--cached", "--quiet"], { cwd: tempWorktree });
126
- console.log("Template branch is already up to date.");
127
- } catch {
128
- await exec("git", ["commit", "-m", "template update"], {
129
- cwd: tempWorktree
130
- });
131
- }
132
- } finally {
133
- try {
134
- await exec("git", ["worktree", "remove", "--force", tempWorktree], { cwd: projectRoot });
135
- } catch {
136
- }
137
- if (existsSync(tempWorktree)) {
138
- rmSync(tempWorktree, { recursive: true, force: true });
139
- }
140
- rmSync(composedDir, { recursive: true, force: true });
141
- }
142
- }
143
- async function mergeTemplateBranch(worktreeDir) {
144
- try {
145
- await exec(
146
- "git",
147
- ["merge", TEMPLATE_BRANCH, "--allow-unrelated-histories", "-m", "merge template update"],
148
- { cwd: worktreeDir }
149
- );
150
- return false;
151
- } catch (e) {
152
- try {
153
- const status = await exec("git", ["status", "--porcelain"], {
154
- cwd: worktreeDir
155
- });
156
- const hasConflictMarkers = status.split("\n").some((line) => line.startsWith("UU") || line.startsWith("AA"));
157
- if (hasConflictMarkers) return true;
158
- } catch {
159
- }
160
- throw e;
161
- }
162
- }
163
- async function continueUpgrade(agentName, projectRoot) {
164
- const worktreeDir = resolve(projectRoot, ".variants", VARIANT_NAME);
165
- if (!existsSync(worktreeDir)) {
166
- console.error("No upgrade in progress. Run `volute agent upgrade` first.");
167
- process.exit(1);
168
- }
169
- const status = await exec("git", ["status", "--porcelain"], {
170
- cwd: worktreeDir
171
- });
172
- const hasConflicts = status.split("\n").some((line) => line.startsWith("UU") || line.startsWith("AA"));
173
- if (hasConflicts) {
174
- console.error("There are still unresolved conflicts. Resolve them first.");
175
- process.exit(1);
176
- }
177
- try {
178
- await exec("git", ["add", "-A"], { cwd: worktreeDir });
179
- await exec("git", ["commit", "--no-edit"], { cwd: worktreeDir });
180
- } catch {
181
- }
182
- await installAndVerify(agentName, worktreeDir);
183
- }
184
- async function installAndVerify(agentName, worktreeDir) {
185
- console.log("Installing dependencies...");
186
- await execInherit("npm", ["install"], { cwd: worktreeDir });
187
- const variantPort = nextPort();
188
- addVariant(agentName, {
189
- name: VARIANT_NAME,
190
- branch: VARIANT_NAME,
191
- path: worktreeDir,
192
- port: variantPort,
193
- created: (/* @__PURE__ */ new Date()).toISOString()
194
- });
195
- console.log("Starting upgrade variant...");
196
- try {
197
- const res = await daemonFetch(
198
- `/api/agents/${encodeURIComponent(`${agentName}@${VARIANT_NAME}`)}/start`,
199
- { method: "POST" }
200
- );
201
- if (!res.ok) {
202
- const data = await res.json();
203
- console.error(data.error ?? "Failed to start variant");
204
- process.exit(1);
205
- }
206
- } catch {
207
- console.error("Failed to start variant. Is the daemon running? (volute up)");
208
- console.error(
209
- `The variant was created but not started. Use: volute agent start ${agentName}@${VARIANT_NAME}`
210
- );
211
- process.exit(1);
212
- }
213
- console.log(`
214
- Upgrade variant running on port ${variantPort}`);
215
- console.log(`
216
- Next steps:`);
217
- console.log(
218
- ` volute send @${agentName}@${VARIANT_NAME} "hello" # chat with upgraded variant`
219
- );
220
- console.log(` volute variant merge ${VARIANT_NAME} # merge back when satisfied`);
221
- }
222
- export {
223
- run
224
- };
@@ -1,476 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- resolveAgentName
4
- } from "./chunk-AZEL2IEK.js";
5
- import {
6
- parseArgs
7
- } from "./chunk-D424ZQGI.js";
8
- import {
9
- exec,
10
- execInherit
11
- } from "./chunk-5C5JWR2L.js";
12
- import {
13
- daemonFetch
14
- } from "./chunk-23L3MKEV.js";
15
- import {
16
- addVariant,
17
- checkHealth,
18
- findVariant,
19
- nextPort,
20
- readVariants,
21
- removeVariant,
22
- resolveAgent,
23
- validateBranchName,
24
- writeVariants
25
- } from "./chunk-DP2DX4WV.js";
26
- import "./chunk-K3NQKI34.js";
27
-
28
- // src/commands/variant.ts
29
- import { existsSync, mkdirSync as mkdirSync2, writeFileSync } from "fs";
30
- import { resolve as resolve2 } from "path";
31
-
32
- // src/lib/spawn-server.ts
33
- import { spawn } from "child_process";
34
- import { closeSync, mkdirSync, openSync, readFileSync } from "fs";
35
- import { resolve } from "path";
36
- function tsxBin(cwd) {
37
- return resolve(cwd, "node_modules", ".bin", "tsx");
38
- }
39
- function spawnServer(cwd, port, options) {
40
- if (options?.detached) {
41
- return spawnDetached(cwd, port, options.logDir);
42
- }
43
- return spawnAttached(cwd, port);
44
- }
45
- function spawnAttached(cwd, port) {
46
- const child = spawn(tsxBin(cwd), ["src/server.ts", "--port", String(port)], {
47
- cwd,
48
- stdio: ["ignore", "pipe", "pipe"]
49
- });
50
- return new Promise((resolve3) => {
51
- const timeout = setTimeout(() => resolve3(null), 3e4);
52
- function checkOutput(data) {
53
- const match = data.toString().match(/listening on :(\d+)/);
54
- if (match) {
55
- clearTimeout(timeout);
56
- resolve3({ child, actualPort: parseInt(match[1], 10) });
57
- }
58
- }
59
- child.stdout?.on("data", checkOutput);
60
- child.stderr?.on("data", checkOutput);
61
- child.on("error", () => {
62
- clearTimeout(timeout);
63
- resolve3(null);
64
- });
65
- child.on("exit", () => {
66
- clearTimeout(timeout);
67
- resolve3(null);
68
- });
69
- });
70
- }
71
- function spawnDetached(cwd, port, logDir) {
72
- const logsDir = logDir ?? resolve(cwd, ".volute", "logs");
73
- mkdirSync(logsDir, { recursive: true });
74
- const logPath = resolve(logsDir, "agent.log");
75
- const logFd = openSync(logPath, "a");
76
- const child = spawn(tsxBin(cwd), ["src/server.ts", "--port", String(port)], {
77
- cwd,
78
- stdio: ["ignore", logFd, logFd],
79
- detached: true
80
- });
81
- child.unref();
82
- closeSync(logFd);
83
- return new Promise((res) => {
84
- let done = false;
85
- function finish(result) {
86
- if (done) return;
87
- done = true;
88
- clearInterval(interval);
89
- clearTimeout(timeout);
90
- res(result);
91
- }
92
- const interval = setInterval(() => {
93
- try {
94
- const content = readFileSync(logPath, "utf-8");
95
- const match = content.match(/listening on :(\d+)/);
96
- if (match) {
97
- finish({ child, actualPort: parseInt(match[1], 10) });
98
- }
99
- } catch {
100
- }
101
- }, 100);
102
- const timeout = setTimeout(() => finish(null), 3e4);
103
- child.on("error", () => finish(null));
104
- child.on("exit", () => finish(null));
105
- });
106
- }
107
-
108
- // src/lib/verify.ts
109
- async function verify(port) {
110
- const health = await checkHealth(port);
111
- if (!health.ok) {
112
- console.error(" Health check: failed");
113
- return false;
114
- }
115
- console.log(" Health check: OK");
116
- try {
117
- const res = await fetch(`http://127.0.0.1:${port}/message`, {
118
- method: "POST",
119
- headers: { "Content-Type": "application/json" },
120
- body: JSON.stringify({
121
- content: [{ type: "text", text: "ping" }],
122
- channel: "system"
123
- }),
124
- signal: AbortSignal.timeout(6e4)
125
- });
126
- if (!res.ok) {
127
- console.error(" Test message: failed to send");
128
- return false;
129
- }
130
- const result = await res.json();
131
- if (result.ok) {
132
- console.log(" Test message: OK");
133
- return true;
134
- } else {
135
- console.error(" Test message: unexpected response");
136
- return false;
137
- }
138
- } catch (e) {
139
- const msg = e instanceof Error ? e.message : String(e);
140
- console.error(` Test message: ${msg}`);
141
- return false;
142
- }
143
- }
144
-
145
- // src/commands/variant.ts
146
- async function run(args) {
147
- const subcommand = args[0];
148
- switch (subcommand) {
149
- case "create":
150
- await createVariant(args.slice(1));
151
- break;
152
- case "list":
153
- await listVariants(args.slice(1));
154
- break;
155
- case "merge":
156
- await mergeVariant(args.slice(1));
157
- break;
158
- case "delete":
159
- await deleteVariant(args.slice(1));
160
- break;
161
- case "--help":
162
- case "-h":
163
- case void 0:
164
- printUsage();
165
- break;
166
- default:
167
- printUsage();
168
- process.exit(1);
169
- }
170
- }
171
- function printUsage() {
172
- console.log(`Usage:
173
- volute variant create <variant> [--agent <name>] [--soul "..."] [--port N] [--no-start] [--json]
174
- volute variant list [--agent <name>] [--json]
175
- volute variant merge <variant> [--agent <name>] [--summary "..." --memory "..."] [--skip-verify]
176
- volute variant delete <variant> [--agent <name>]`);
177
- }
178
- async function createVariant(args) {
179
- const { positional, flags } = parseArgs(args, {
180
- agent: { type: "string" },
181
- soul: { type: "string" },
182
- port: { type: "number" },
183
- "no-start": { type: "boolean" },
184
- json: { type: "boolean" }
185
- });
186
- const agentName = resolveAgentName(flags);
187
- const variantName = positional[0];
188
- const { soul, port, json } = flags;
189
- const noStart = flags["no-start"];
190
- if (!variantName) {
191
- console.error(
192
- 'Usage: volute variant create <variant> [--agent <name>] [--soul "..."] [--port N] [--no-start] [--json]'
193
- );
194
- process.exit(1);
195
- }
196
- const err = validateBranchName(variantName);
197
- if (err) {
198
- console.error(err);
199
- process.exit(1);
200
- }
201
- const { dir: projectRoot } = resolveAgent(agentName);
202
- const variantDir = resolve2(projectRoot, ".variants", variantName);
203
- if (existsSync(variantDir)) {
204
- console.error(`Variant directory already exists: ${variantDir}`);
205
- process.exit(1);
206
- }
207
- mkdirSync2(resolve2(projectRoot, ".variants"), { recursive: true });
208
- try {
209
- await exec("git", ["worktree", "add", "-b", variantName, variantDir], { cwd: projectRoot });
210
- } catch (e) {
211
- const msg = e instanceof Error ? e.message : String(e);
212
- console.error(`Failed to create worktree: ${msg}`);
213
- process.exit(1);
214
- }
215
- if (!json) console.log("Installing dependencies...");
216
- try {
217
- if (json) {
218
- await exec("npm", ["install"], { cwd: variantDir });
219
- } else {
220
- await execInherit("npm", ["install"], { cwd: variantDir });
221
- }
222
- } catch (e) {
223
- const msg = e instanceof Error ? e.message : String(e);
224
- console.error(`npm install failed: ${msg}`);
225
- process.exit(1);
226
- }
227
- if (soul) {
228
- writeFileSync(resolve2(variantDir, "home/SOUL.md"), soul);
229
- }
230
- const variantPort = port ?? nextPort();
231
- const variant = {
232
- name: variantName,
233
- branch: variantName,
234
- path: variantDir,
235
- port: variantPort,
236
- created: (/* @__PURE__ */ new Date()).toISOString()
237
- };
238
- addVariant(agentName, variant);
239
- if (!noStart) {
240
- if (!json) console.log("Starting variant via daemon...");
241
- try {
242
- const res = await daemonFetch(
243
- `/api/agents/${encodeURIComponent(`${agentName}@${variantName}`)}/start`,
244
- {
245
- method: "POST"
246
- }
247
- );
248
- if (!res.ok) {
249
- const data = await res.json();
250
- console.error(data.error ?? "Failed to start variant");
251
- process.exit(1);
252
- }
253
- } catch {
254
- console.error("Failed to start variant. Is the daemon running? (volute up)");
255
- console.error(
256
- `The variant was created but not started. Use: volute agent start ${agentName}@${variantName}`
257
- );
258
- process.exit(1);
259
- }
260
- }
261
- if (json) {
262
- console.log(JSON.stringify(variant, null, 2));
263
- } else {
264
- console.log(`
265
- Variant created: ${variantName}`);
266
- console.log(` Branch: ${variant.branch}`);
267
- console.log(` Path: ${variant.path}`);
268
- console.log(` Port: ${variantPort}`);
269
- }
270
- }
271
- async function listVariants(args) {
272
- const { flags } = parseArgs(args, {
273
- agent: { type: "string" },
274
- json: { type: "boolean" }
275
- });
276
- const agentName = resolveAgentName(flags);
277
- const { json } = flags;
278
- resolveAgent(agentName);
279
- const variants = readVariants(agentName);
280
- if (variants.length === 0) {
281
- if (json) {
282
- console.log("[]");
283
- } else {
284
- console.log("No variants.");
285
- }
286
- return;
287
- }
288
- const results = await Promise.all(
289
- variants.map(async (v) => {
290
- if (!v.port) return { ...v, status: "no-server" };
291
- const health = await checkHealth(v.port);
292
- return { ...v, status: health.ok ? "running" : "dead" };
293
- })
294
- );
295
- const updated = results.map(({ status, ...v }) => ({
296
- ...v,
297
- running: status === "running"
298
- }));
299
- writeVariants(agentName, updated);
300
- if (json) {
301
- console.log(JSON.stringify(results, null, 2));
302
- return;
303
- }
304
- const nameW = Math.max(4, ...results.map((r) => r.name.length));
305
- const portW = Math.max(4, ...results.map((r) => String(r.port || "-").length));
306
- console.log(`${"NAME".padEnd(nameW)} ${"PORT".padEnd(portW)} ${"STATUS".padEnd(10)} BRANCH`);
307
- for (const r of results) {
308
- console.log(
309
- `${r.name.padEnd(nameW)} ${String(r.port || "-").padEnd(portW)} ${r.status.padEnd(10)} ${r.branch}`
310
- );
311
- }
312
- }
313
- async function mergeVariant(args) {
314
- const { positional, flags } = parseArgs(args, {
315
- agent: { type: "string" },
316
- summary: { type: "string" },
317
- justification: { type: "string" },
318
- memory: { type: "string" },
319
- "skip-verify": { type: "boolean" }
320
- });
321
- const agentName = resolveAgentName(flags);
322
- const variantName = positional[0];
323
- if (!variantName) {
324
- console.error(
325
- "Usage: volute variant merge <variant> [--agent <name>] [--summary '...'] [--justification '...'] [--memory '...'] [--skip-verify]"
326
- );
327
- process.exit(1);
328
- }
329
- const { dir: projectRoot } = resolveAgent(agentName);
330
- const variant = findVariant(agentName, variantName);
331
- if (!variant) {
332
- console.error(`Unknown variant: ${variantName}`);
333
- process.exit(1);
334
- }
335
- const branchErr = validateBranchName(variant.branch);
336
- if (branchErr) {
337
- console.error(branchErr);
338
- process.exit(1);
339
- }
340
- if (existsSync(variant.path)) {
341
- const status = (await exec("git", ["status", "--porcelain"], { cwd: variant.path })).trim();
342
- if (status) {
343
- console.log("Committing uncommitted changes in variant...");
344
- try {
345
- await exec("git", ["add", "-A"], { cwd: variant.path });
346
- await exec("git", ["commit", "-m", "Auto-commit uncommitted changes before merge"], {
347
- cwd: variant.path
348
- });
349
- } catch (err) {
350
- console.error("Failed to auto-commit variant changes:", err);
351
- console.error("Please commit or stash changes in the variant manually before merging.");
352
- process.exit(1);
353
- }
354
- }
355
- }
356
- if (!flags["skip-verify"]) {
357
- console.log("Verifying variant...");
358
- console.log("Starting temporary server for verification...");
359
- const result = await spawnServer(variant.path, 0, { detached: true });
360
- if (!result) {
361
- console.error("Failed to start server for verification. Use --skip-verify to skip.");
362
- process.exit(1);
363
- }
364
- const verified = await verify(result.actualPort);
365
- try {
366
- process.kill(result.child.pid);
367
- } catch {
368
- }
369
- if (!verified) {
370
- console.error("Verification failed. Fix issues or use --skip-verify to proceed anyway.");
371
- process.exit(1);
372
- }
373
- console.log("Verification passed.");
374
- }
375
- const mainStatus = (await exec("git", ["status", "--porcelain"], { cwd: projectRoot })).trim();
376
- if (mainStatus) {
377
- console.log("Committing uncommitted changes in main...");
378
- try {
379
- await exec("git", ["add", "-A"], { cwd: projectRoot });
380
- await exec("git", ["commit", "-m", "Auto-commit uncommitted changes before merge"], {
381
- cwd: projectRoot
382
- });
383
- } catch (err) {
384
- console.error("Failed to auto-commit main changes:", err);
385
- console.error("Please commit or stash your changes manually before merging.");
386
- process.exit(1);
387
- }
388
- }
389
- console.log(`Merging branch: ${variant.branch}`);
390
- try {
391
- await execInherit("git", ["merge", variant.branch], { cwd: projectRoot });
392
- } catch (e) {
393
- console.error("Merge failed:", e);
394
- process.exit(1);
395
- }
396
- if (existsSync(variant.path)) {
397
- try {
398
- await exec("git", ["worktree", "remove", "--force", variant.path], { cwd: projectRoot });
399
- } catch {
400
- }
401
- }
402
- try {
403
- await exec("git", ["branch", "-D", variant.branch], { cwd: projectRoot });
404
- } catch {
405
- }
406
- removeVariant(agentName, variantName);
407
- console.log("Reinstalling dependencies...");
408
- try {
409
- await execInherit("npm", ["install"], { cwd: projectRoot });
410
- } catch (e) {
411
- console.error("npm install failed:", e);
412
- }
413
- console.log(`Variant ${variantName} merged and cleaned up.`);
414
- if (process.env.VOLUTE_SUPERVISOR) return;
415
- const context = {
416
- type: "merged",
417
- name: variantName,
418
- ...flags.summary && { summary: flags.summary },
419
- ...flags.justification && { justification: flags.justification },
420
- ...flags.memory && { memory: flags.memory }
421
- };
422
- try {
423
- console.log("Restarting agent via daemon...");
424
- const res = await daemonFetch(`/api/agents/${encodeURIComponent(agentName)}/restart`, {
425
- method: "POST",
426
- headers: { "Content-Type": "application/json" },
427
- body: JSON.stringify({ context })
428
- });
429
- if (res.ok) {
430
- console.log(`${agentName} restarted.`);
431
- } else {
432
- const data = await res.json();
433
- console.error(`Failed to restart: ${data.error ?? "unknown error"}`);
434
- }
435
- } catch {
436
- console.log(`Daemon not running. Start the agent manually: volute agent start ${agentName}`);
437
- }
438
- }
439
- async function deleteVariant(args) {
440
- const { positional, flags } = parseArgs(args, {
441
- agent: { type: "string" }
442
- });
443
- const agentName = resolveAgentName(flags);
444
- const variantName = positional[0];
445
- if (!variantName) {
446
- console.error("Usage: volute variant delete <variant> [--agent <name>]");
447
- process.exit(1);
448
- }
449
- const { dir: projectRoot } = resolveAgent(agentName);
450
- const variant = findVariant(agentName, variantName);
451
- if (!variant) {
452
- console.error(`Unknown variant: ${variantName}`);
453
- process.exit(1);
454
- }
455
- try {
456
- await daemonFetch(`/api/agents/${encodeURIComponent(`${agentName}@${variantName}`)}/stop`, {
457
- method: "POST"
458
- });
459
- } catch {
460
- }
461
- if (existsSync(variant.path)) {
462
- try {
463
- await exec("git", ["worktree", "remove", "--force", variant.path], { cwd: projectRoot });
464
- } catch {
465
- }
466
- }
467
- try {
468
- await exec("git", ["branch", "-D", variant.branch], { cwd: projectRoot });
469
- } catch {
470
- }
471
- removeVariant(agentName, variantName);
472
- console.log(`Variant ${variantName} deleted.`);
473
- }
474
- export {
475
- run
476
- };
File without changes