svamp-cli 0.2.116 → 0.2.117

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.
@@ -58,7 +58,7 @@ async function serviceExpose(args) {
58
58
  process.exit(1);
59
59
  }
60
60
  if (foreground) {
61
- const { runFrpcTunnel } = await import('./frpc-9qgaimIN.mjs');
61
+ const { runFrpcTunnel } = await import('./frpc-63NvAmuT.mjs');
62
62
  await runFrpcTunnel(name, ports, void 0, {
63
63
  group,
64
64
  groupKey,
@@ -68,7 +68,7 @@ async function serviceExpose(args) {
68
68
  });
69
69
  return;
70
70
  }
71
- const { connectAndGetMachine } = await import('./commands-DdW5M7Le.mjs');
71
+ const { connectAndGetMachine } = await import('./commands-BxXUQlBD.mjs');
72
72
  const { server, machine } = await connectAndGetMachine();
73
73
  try {
74
74
  const status = await machine.tunnelStart({
@@ -123,7 +123,7 @@ async function serviceServe(args) {
123
123
  };
124
124
  process.on("SIGINT", cleanup);
125
125
  process.on("SIGTERM", cleanup);
126
- const { runFrpcTunnel } = await import('./frpc-9qgaimIN.mjs');
126
+ const { runFrpcTunnel } = await import('./frpc-63NvAmuT.mjs');
127
127
  await runFrpcTunnel(name, [caddyPort]);
128
128
  } catch (err) {
129
129
  console.error(`Error serving directory: ${err.message}`);
@@ -132,7 +132,7 @@ async function serviceServe(args) {
132
132
  }
133
133
  async function serviceList(_args) {
134
134
  try {
135
- const { connectAndGetMachine } = await import('./commands-DdW5M7Le.mjs');
135
+ const { connectAndGetMachine } = await import('./commands-BxXUQlBD.mjs');
136
136
  const { server, machine } = await connectAndGetMachine();
137
137
  try {
138
138
  const tunnels = await machine.tunnelList({});
@@ -161,7 +161,7 @@ async function serviceDelete(args) {
161
161
  process.exit(1);
162
162
  }
163
163
  try {
164
- const { connectAndGetMachine } = await import('./commands-DdW5M7Le.mjs');
164
+ const { connectAndGetMachine } = await import('./commands-BxXUQlBD.mjs');
165
165
  const { server, machine } = await connectAndGetMachine();
166
166
  try {
167
167
  await machine.tunnelStop({ name });
@@ -0,0 +1,504 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { connectAndGetMachine, resolveSessionId, createWorktree, connectAndResolveSession } from './commands-BxXUQlBD.mjs';
3
+ import { execSync } from 'node:child_process';
4
+ import { n as shortId } from './run-Ci6VToZR.mjs';
5
+ import 'node:path';
6
+ import 'node:os';
7
+ import 'os';
8
+ import 'fs/promises';
9
+ import 'fs';
10
+ import 'path';
11
+ import 'url';
12
+ import 'child_process';
13
+ import 'crypto';
14
+ import 'node:crypto';
15
+ import 'util';
16
+ import 'node:events';
17
+ import '@agentclientprotocol/sdk';
18
+ import '@modelcontextprotocol/sdk/client/index.js';
19
+ import '@modelcontextprotocol/sdk/client/stdio.js';
20
+ import '@modelcontextprotocol/sdk/types.js';
21
+ import 'zod';
22
+ import 'node:fs/promises';
23
+ import 'node:util';
24
+
25
+ function git(cwd, args) {
26
+ return execSync(`git ${args}`, { cwd, stdio: "pipe", encoding: "utf-8" }).toString();
27
+ }
28
+ function worktreeStatus(worktreePath) {
29
+ return git(worktreePath, "status --porcelain").trim();
30
+ }
31
+ function aheadBehind(worktreePath, baseBranch) {
32
+ try {
33
+ const out = git(worktreePath, `rev-list --left-right --count ${baseBranch}...HEAD`).trim();
34
+ const [behind, ahead] = out.split(/\s+/).map((n) => parseInt(n, 10) || 0);
35
+ return { ahead, behind };
36
+ } catch {
37
+ return { ahead: 0, behind: 0 };
38
+ }
39
+ }
40
+ function currentBranch(worktreePath) {
41
+ try {
42
+ return git(worktreePath, "branch --show-current").trim();
43
+ } catch {
44
+ return "";
45
+ }
46
+ }
47
+ function mergeBack(input) {
48
+ const baseBranch = input.baseBranch || "main";
49
+ const { projectRoot, branch, worktreePath } = input;
50
+ if (!existsSync(projectRoot)) {
51
+ return { ok: false, stage: "verify", detail: `project root not found: ${projectRoot}` };
52
+ }
53
+ if (!existsSync(worktreePath)) {
54
+ return { ok: false, stage: "verify", detail: `worktree not found: ${worktreePath}` };
55
+ }
56
+ let childStatus;
57
+ try {
58
+ childStatus = worktreeStatus(worktreePath);
59
+ } catch (e) {
60
+ return { ok: false, stage: "verify", detail: `not a git worktree: ${worktreePath} (${e.message})` };
61
+ }
62
+ if (childStatus) {
63
+ return {
64
+ ok: false,
65
+ stage: "verify",
66
+ rework: true,
67
+ detail: `feature worktree has uncommitted changes \u2014 commit (or run \`feature done\`) first:
68
+ ${childStatus}`
69
+ };
70
+ }
71
+ const leadBranch = currentBranch(projectRoot);
72
+ if (leadBranch !== baseBranch) {
73
+ return {
74
+ ok: false,
75
+ stage: "verify",
76
+ detail: `lead tree ${projectRoot} is on '${leadBranch || "detached"}', not base '${baseBranch}'. Check out ${baseBranch} there first.`
77
+ };
78
+ }
79
+ let leadStatus;
80
+ try {
81
+ leadStatus = git(projectRoot, "status --porcelain -uno").trim();
82
+ } catch (e) {
83
+ return { ok: false, stage: "verify", detail: `lead tree status failed: ${e.message}` };
84
+ }
85
+ if (leadStatus) {
86
+ return {
87
+ ok: false,
88
+ stage: "verify",
89
+ detail: `lead tree ${projectRoot} has uncommitted tracked changes; commit/stash before merging.`
90
+ };
91
+ }
92
+ try {
93
+ git(projectRoot, `merge --no-ff ${branch} -m "crew: merge ${branch} into ${baseBranch}"`);
94
+ } catch (e) {
95
+ const detail = (e.stdout?.toString() || "") + (e.stderr?.toString() || "") || e.message;
96
+ try {
97
+ git(projectRoot, "merge --abort");
98
+ } catch {
99
+ }
100
+ return { ok: false, stage: "merge", rework: true, detail: `merge conflict \u2014 aborted:
101
+ ${detail.trim()}` };
102
+ }
103
+ try {
104
+ git(projectRoot, `worktree remove ${worktreePath}`);
105
+ } catch (e) {
106
+ const detail = e.stderr?.toString() || "" || e.message;
107
+ return { ok: false, stage: "remove-worktree", detail: `merged OK but worktree removal failed: ${detail.trim()}` };
108
+ }
109
+ if (input.deleteBranch !== false) {
110
+ try {
111
+ git(projectRoot, `branch -d ${branch}`);
112
+ } catch {
113
+ }
114
+ }
115
+ return { ok: true, stage: "done", detail: `merged ${branch} into ${baseBranch}` };
116
+ }
117
+ function projectRootFromWorktree(worktreePath) {
118
+ const marker = "/.dev/worktree/";
119
+ const idx = worktreePath.indexOf(marker);
120
+ if (idx === -1) return null;
121
+ return worktreePath.slice(0, idx);
122
+ }
123
+
124
+ function requireLead(explicit) {
125
+ const id = explicit || process.env.SVAMP_SESSION_ID;
126
+ if (!id) {
127
+ console.error("No lead session. Run inside a Svamp session, or pass --lead <id>.");
128
+ process.exit(1);
129
+ }
130
+ return id;
131
+ }
132
+ async function rpc(machine, id, method, kwargs = {}) {
133
+ return machine.sessionRPC(id, method, kwargs);
134
+ }
135
+ async function inbox(machine, fromId, toId, body, subject) {
136
+ await rpc(machine, toId, "sendInboxMessage", {
137
+ message: {
138
+ messageId: shortId(),
139
+ body,
140
+ timestamp: Date.now(),
141
+ read: false,
142
+ from: `agent:${fromId}`,
143
+ fromSession: fromId,
144
+ to: toId,
145
+ subject,
146
+ urgency: "normal"
147
+ }
148
+ });
149
+ }
150
+ async function featureStart(brief, opts = {}) {
151
+ if (!brief?.trim()) {
152
+ console.error('A feature brief is required: svamp feature start "<brief>"');
153
+ process.exit(1);
154
+ }
155
+ const leadId = requireLead(opts.leadId);
156
+ const { server, machine } = await connectAndGetMachine(opts.machineId);
157
+ try {
158
+ const sessions = await machine.listSessions();
159
+ const lead = resolveSessionId(sessions, leadId);
160
+ const projectRoot = opts.directory || lead.directory;
161
+ if (!projectRoot) {
162
+ console.error("Could not determine the lead project directory.");
163
+ process.exit(1);
164
+ }
165
+ const base = opts.baseBranch || currentBranch(projectRoot) || "main";
166
+ let wt;
167
+ try {
168
+ wt = createWorktree(projectRoot);
169
+ } catch (e) {
170
+ console.error(`Failed to create worktree: ${e.message}`);
171
+ process.exit(1);
172
+ }
173
+ console.log(`Worktree: ${wt.branch} \u2192 ${wt.path}`);
174
+ const result = await machine.spawnSession({
175
+ directory: wt.path,
176
+ agent: "claude",
177
+ parentSessionId: lead.sessionId,
178
+ permissionMode: "bypassPermissions"
179
+ });
180
+ if (result.type !== "success" || !result.sessionId) {
181
+ console.error(`Failed to spawn feature child: ${result.errorMessage || result.type}`);
182
+ process.exit(1);
183
+ }
184
+ const childId = result.sessionId;
185
+ if (!lead.metadata?.crew) {
186
+ try {
187
+ await rpc(machine, lead.sessionId, "updateConfig", { patch: { crew: { role: "lead" } } });
188
+ } catch {
189
+ }
190
+ }
191
+ const oracle = opts.oracle?.trim();
192
+ const patch = {
193
+ crew: { role: "feature", feature: brief.trim(), branch: wt.branch, worktreePath: wt.path, baseBranch: base }
194
+ };
195
+ let judges = [];
196
+ if (oracle) {
197
+ judges = [{ type: "oracle", cmd: oracle, on_fail: "escalate" }, { type: "parent", parent: lead.sessionId }];
198
+ patch.supervisor = {
199
+ criteria: brief.trim(),
200
+ judges,
201
+ action: opts.action || "nudge",
202
+ max_rounds: opts.maxRounds ?? 20
203
+ };
204
+ }
205
+ try {
206
+ await rpc(machine, childId, "updateConfig", { patch });
207
+ } catch (e) {
208
+ console.warn(`Note: could not write crew/supervisor config yet (${e.message}).`);
209
+ }
210
+ try {
211
+ await rpc(machine, childId, "sendMessage", {
212
+ content: JSON.stringify({
213
+ role: "user",
214
+ content: { type: "text", text: `You are a crew feature child on branch \`${wt.branch}\` (base \`${base}\`), reporting to lead \`${lead.sessionId.slice(0, 8)}\`.
215
+
216
+ Feature brief:
217
+ ${brief.trim()}
218
+
219
+ Work in this worktree. Keep tests green, report milestones with \`svamp feature report\`, and when done run \`svamp feature done\` so your lead can review & merge. After merge you'll be closed.` },
220
+ meta: { sentFrom: "svamp-cli", crew: "feature-brief" }
221
+ })
222
+ });
223
+ } catch (e) {
224
+ console.warn(`Note: could not send the brief (${e.message}).`);
225
+ }
226
+ console.log(`Feature child started: ${childId}`);
227
+ console.log(` brief: ${brief.trim().slice(0, 80)}${brief.trim().length > 80 ? "\u2026" : ""}`);
228
+ console.log(` branch: ${wt.branch} (merges back to ${base})`);
229
+ console.log(judges.length ? ` gate: ${judges.map((j) => j.type).join(" \u2192 ")} (oracle auto-approves \u2192 lead merges)` : ` gate: none \u2014 child runs \`feature done\` when complete; lead reviews & merges`);
230
+ } finally {
231
+ await server.disconnect();
232
+ }
233
+ }
234
+ async function featureList(opts = {}) {
235
+ const leadId = requireLead(opts.leadId);
236
+ const { server, machine } = await connectAndGetMachine(opts.machineId);
237
+ try {
238
+ const sessions = await machine.listSessions();
239
+ const lead = resolveSessionId(sessions, leadId);
240
+ const children = sessions.filter((s) => s.metadata?.parentSessionId === lead.sessionId);
241
+ const rows = children.map((c) => {
242
+ const crew = c.metadata?.crew || {};
243
+ const dir = c.directory || crew.worktreePath || "";
244
+ const base = crew.baseBranch || "main";
245
+ const branch = crew.branch || (dir && existsSync(dir) ? currentBranch(dir) : "") || "";
246
+ let ahead = 0, behind = 0, dirty = false, gone = false;
247
+ if (dir && existsSync(dir)) {
248
+ const ab = aheadBehind(dir, base);
249
+ ahead = ab.ahead;
250
+ behind = ab.behind;
251
+ try {
252
+ dirty = worktreeStatus(dir) !== "";
253
+ } catch {
254
+ }
255
+ } else if (crew.worktreePath) {
256
+ gone = true;
257
+ }
258
+ return {
259
+ id: c.sessionId,
260
+ title: c.metadata?.summary?.text || c.metadata?.name || "",
261
+ role: crew.role || (crew.worktreePath ? "feature" : ""),
262
+ branch,
263
+ base,
264
+ ahead,
265
+ behind,
266
+ dirty,
267
+ gone,
268
+ active: c.active,
269
+ feature: crew.feature || ""
270
+ };
271
+ });
272
+ if (opts.json) {
273
+ console.log(JSON.stringify(rows, null, 2));
274
+ return;
275
+ }
276
+ if (rows.length === 0) {
277
+ console.log(`No feature children for lead ${lead.sessionId.slice(0, 8)}.`);
278
+ return;
279
+ }
280
+ console.log(`Features under lead ${lead.sessionId.slice(0, 8)}:
281
+ `);
282
+ for (const r of rows) {
283
+ const state = r.gone ? "worktree-gone" : r.active ? "active" : "idle";
284
+ const ab = r.gone ? "" : `+${r.ahead}/-${r.behind}${r.dirty ? " \u2717dirty" : ""}`;
285
+ const title = (r.feature || r.title || "").slice(0, 48);
286
+ console.log(` ${r.id.slice(0, 8)} ${state.padEnd(13)} ${(r.branch || "\u2014").padEnd(28)} ${ab.padEnd(14)} ${title}`);
287
+ }
288
+ console.log(`
289
+ Review a finished feature with: svamp feature merge <id>`);
290
+ } finally {
291
+ await server.disconnect();
292
+ }
293
+ }
294
+ async function featureReport(text, opts = {}) {
295
+ if (!text?.trim()) {
296
+ console.error("A report message is required.");
297
+ process.exit(1);
298
+ }
299
+ const selfId = requireLead(opts.selfId);
300
+ const { server, machine } = await connectAndGetMachine(opts.machineId);
301
+ try {
302
+ const meta = await rpc(machine, selfId, "getMetadata");
303
+ const parent = meta?.parentSessionId;
304
+ if (!parent) {
305
+ console.error("This session has no parent (lead) to report to.");
306
+ process.exit(1);
307
+ }
308
+ const kind = opts.blocker ? "blocker" : "progress";
309
+ await inbox(machine, selfId, parent, `<feature-${kind} from="${selfId}">
310
+ ${text.trim()}
311
+ </feature-${kind}>`, `feature ${kind}`);
312
+ console.log(`Reported ${kind} to lead ${parent.slice(0, 8)}.`);
313
+ } finally {
314
+ await server.disconnect();
315
+ }
316
+ }
317
+ async function featureDone(opts = {}) {
318
+ const selfId = requireLead(opts.selfId);
319
+ const { server, machine } = await connectAndGetMachine(opts.machineId);
320
+ try {
321
+ const meta = await rpc(machine, selfId, "getMetadata");
322
+ const parent = meta?.parentSessionId;
323
+ if (!parent) {
324
+ console.error("This session has no parent (lead) to report to.");
325
+ process.exit(1);
326
+ }
327
+ const crew = meta?.crew || {};
328
+ const dir = meta?.path || crew.worktreePath || "";
329
+ const base = crew.baseBranch || "main";
330
+ const branch = crew.branch || (dir && existsSync(dir) ? currentBranch(dir) : "") || "";
331
+ const ab = dir && existsSync(dir) ? aheadBehind(dir, base) : { ahead: 0, behind: 0 };
332
+ let dirty = false;
333
+ try {
334
+ dirty = dir && existsSync(dir) ? worktreeStatus(dir) !== "" : false;
335
+ } catch {
336
+ }
337
+ const body = [
338
+ `<merge-request child="${selfId}" branch="${branch}" base="${base}" commits="${ab.ahead}"${dirty ? ' dirty="true"' : ""}>`,
339
+ crew.feature ? `Feature: ${crew.feature}` : "",
340
+ opts.summary ? `Summary: ${opts.summary.trim()}` : "",
341
+ dirty ? "WARNING: worktree has uncommitted changes \u2014 commit before this can merge." : "",
342
+ `Review & merge with: svamp feature merge ${selfId.slice(0, 8)}`,
343
+ `</merge-request>`
344
+ ].filter(Boolean).join("\n");
345
+ await inbox(machine, selfId, parent, body, `merge-request (${branch || "feature"})`);
346
+ console.log(`Sent merge-request to lead ${parent.slice(0, 8)} (branch ${branch || "?"}, +${ab.ahead} commits).`);
347
+ if (dirty) console.log(" \u26A0 Worktree is dirty \u2014 commit your work or the merge will be rejected.");
348
+ } finally {
349
+ await server.disconnect();
350
+ }
351
+ }
352
+ async function featureMerge(childPartial, opts = {}) {
353
+ const leadId = opts.leadId || process.env.SVAMP_SESSION_ID || "";
354
+ const { server, machine, fullId: childId } = await connectAndResolveSession(childPartial, opts.machineId);
355
+ try {
356
+ const meta = await rpc(machine, childId, "getMetadata");
357
+ const crew = meta?.crew || {};
358
+ const worktreePath = crew.worktreePath || meta?.path || "";
359
+ if (!worktreePath) {
360
+ console.error("Could not determine the child worktree path.");
361
+ process.exit(1);
362
+ }
363
+ const base = crew.baseBranch || "main";
364
+ const branch = crew.branch || currentBranch(worktreePath) || "";
365
+ if (!branch) {
366
+ console.error("Could not determine the feature branch.");
367
+ process.exit(1);
368
+ }
369
+ const projectRoot = projectRootFromWorktree(worktreePath) || (leadId ? (await rpc(machine, leadId, "getMetadata").catch(() => null))?.path : null) || "";
370
+ if (!projectRoot) {
371
+ console.error("Could not determine the lead project root (base worktree).");
372
+ process.exit(1);
373
+ }
374
+ if (leadId) {
375
+ try {
376
+ await inbox(
377
+ machine,
378
+ leadId,
379
+ childId,
380
+ `<crew-freeze>Your lead is merging branch ${branch} into ${base}. Stop editing files now; do not commit until told.</crew-freeze>`,
381
+ "crew: freeze for merge"
382
+ );
383
+ } catch {
384
+ }
385
+ }
386
+ const r = mergeBack({ projectRoot, branch, worktreePath, baseBranch: base, deleteBranch: opts.deleteBranch });
387
+ if (!r.ok) {
388
+ if (r.rework && leadId) {
389
+ try {
390
+ await inbox(
391
+ machine,
392
+ leadId,
393
+ childId,
394
+ `<crew-rework branch="${branch}">
395
+ Merge was not applied: ${r.detail}
396
+ Resolve and run \`svamp feature done\` again.
397
+ </crew-rework>`,
398
+ "crew: rework needed"
399
+ );
400
+ } catch {
401
+ }
402
+ console.error(`Merge deferred (rework) at ${r.stage}: ${r.detail}`);
403
+ console.error(`Child ${childId.slice(0, 8)} re-woken with guidance; left running.`);
404
+ } else {
405
+ console.error(`Merge failed at ${r.stage}: ${r.detail}`);
406
+ }
407
+ process.exit(1);
408
+ }
409
+ if (!opts.keepChild) {
410
+ try {
411
+ await machine.archiveSession(childId);
412
+ } catch (e) {
413
+ console.warn(`Merged, but could not archive child: ${e.message}`);
414
+ }
415
+ }
416
+ console.log(`\u2705 Merged ${branch} \u2192 ${base}; worktree removed; child ${childId.slice(0, 8)} ${opts.keepChild ? "kept" : "archived"}.`);
417
+ } finally {
418
+ await server.disconnect();
419
+ }
420
+ }
421
+ function flag(args, ...names) {
422
+ for (const n of names) {
423
+ const i = args.indexOf(n);
424
+ if (i !== -1 && i + 1 < args.length) return args[i + 1];
425
+ }
426
+ return void 0;
427
+ }
428
+ function has(args, ...names) {
429
+ return names.some((n) => args.includes(n));
430
+ }
431
+ function positionals(args, valueFlags) {
432
+ const out = [];
433
+ for (let i = 0; i < args.length; i++) {
434
+ const a = args[i];
435
+ if (a.startsWith("-")) {
436
+ if (valueFlags.includes(a)) i++;
437
+ continue;
438
+ }
439
+ out.push(a);
440
+ }
441
+ return out;
442
+ }
443
+ const FEATURE_HELP = `Usage: svamp feature <command>
444
+
445
+ start "<brief>" [--oracle "<cmd>"] [--base <branch>] [--action nudge|block]
446
+ [--max N] [--dir <path>] [--lead <id>] [-m <machine>]
447
+ Spawn a managed worktree child of the lead + attach a supervisor (oracle\u2192parent).
448
+ list [--json] [--lead <id>] [-m <machine>]
449
+ The lead's feature children: branch, ahead/behind, status.
450
+ report "<text>" [--blocker] [-m <machine>] (run by a child) progress/blocker \u2192 lead
451
+ done [--summary "<text>"] [-m <machine>] (run by a child) send a merge-request \u2192 lead
452
+ merge <child-id> [--keep-child] [--no-delete-branch] [-m <machine>]
453
+ (run by the lead) verify \u2192 merge \u2192 remove worktree \u2192 archive child.`;
454
+ async function crewCommand(args) {
455
+ const sub = args[0];
456
+ const rest = args.slice(1);
457
+ const machineId = flag(rest, "-m", "--machine");
458
+ const lead = flag(rest, "--lead");
459
+ if (!sub || sub === "--help" || sub === "-h") {
460
+ console.log(FEATURE_HELP);
461
+ return;
462
+ }
463
+ if (sub === "start") {
464
+ const valueFlags = ["--oracle", "--base", "--action", "--max", "--dir", "-d", "--lead", "-m", "--machine"];
465
+ const brief = positionals(rest, valueFlags).join(" ");
466
+ const maxStr = flag(rest, "--max");
467
+ const action = flag(rest, "--action");
468
+ await featureStart(brief, {
469
+ leadId: lead,
470
+ machineId,
471
+ directory: flag(rest, "--dir", "-d"),
472
+ oracle: flag(rest, "--oracle"),
473
+ baseBranch: flag(rest, "--base"),
474
+ action: action === "block" ? "block" : action === "nudge" ? "nudge" : void 0,
475
+ maxRounds: maxStr ? parseInt(maxStr, 10) : void 0
476
+ });
477
+ } else if (sub === "list" || sub === "ls") {
478
+ await featureList({ leadId: lead, machineId, json: has(rest, "--json") });
479
+ } else if (sub === "report") {
480
+ const text = positionals(rest, ["-m", "--machine", "--lead"]).join(" ");
481
+ await featureReport(text, { machineId, blocker: has(rest, "--blocker") });
482
+ } else if (sub === "done") {
483
+ await featureDone({ machineId, summary: flag(rest, "--summary") });
484
+ } else if (sub === "merge") {
485
+ const child = positionals(rest, ["-m", "--machine", "--lead"])[0];
486
+ if (!child) {
487
+ console.error("Usage: svamp feature merge <child-id>");
488
+ process.exit(1);
489
+ }
490
+ await featureMerge(child, {
491
+ leadId: lead,
492
+ machineId,
493
+ keepChild: has(rest, "--keep-child"),
494
+ deleteBranch: has(rest, "--no-delete-branch") ? false : void 0
495
+ });
496
+ } else {
497
+ console.error(`Unknown feature command: ${sub}
498
+ `);
499
+ console.log(FEATURE_HELP);
500
+ process.exit(1);
501
+ }
502
+ }
503
+
504
+ export { crewCommand, featureDone, featureList, featureMerge, featureReport, featureStart };
@@ -1,11 +1,11 @@
1
1
  import { writeFileSync, readFileSync } from 'fs';
2
2
  import { resolve } from 'path';
3
- import { connectAndGetMachine } from './commands-DdW5M7Le.mjs';
3
+ import { connectAndGetMachine } from './commands-BxXUQlBD.mjs';
4
4
  import 'node:fs';
5
5
  import 'node:child_process';
6
6
  import 'node:path';
7
7
  import 'node:os';
8
- import './run-DHPCWQUq.mjs';
8
+ import './run-Ci6VToZR.mjs';
9
9
  import 'os';
10
10
  import 'fs/promises';
11
11
  import 'url';
@@ -1,7 +1,7 @@
1
1
  import os from 'os';
2
2
  import fs__default from 'fs';
3
3
  import { resolve, join, relative } from 'path';
4
- import { p as parseFrontmatter, o as getSkillsServer, q as getSkillsWorkspaceName, t as getSkillsCollectionName, u as fetchWithTimeout, v as searchSkills, w as SKILLS_DIR, x as getSkillInfo, y as downloadSkillFile, z as listSkillFiles } from './run-DHPCWQUq.mjs';
4
+ import { p as parseFrontmatter, o as getSkillsServer, q as getSkillsWorkspaceName, t as getSkillsCollectionName, u as fetchWithTimeout, v as searchSkills, w as SKILLS_DIR, x as getSkillInfo, y as downloadSkillFile, z as listSkillFiles } from './run-Ci6VToZR.mjs';
5
5
  import 'fs/promises';
6
6
  import 'url';
7
7
  import 'child_process';
@@ -1,7 +1,7 @@
1
1
  import { existsSync, readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import os from 'node:os';
4
- import { c as connectToHypha } from './run-DHPCWQUq.mjs';
4
+ import { c as connectToHypha } from './run-Ci6VToZR.mjs';
5
5
  import { PINNED_CLAUDE_CODE_VERSION } from './pinnedClaudeCode-HydRNEt7.mjs';
6
6
  import 'os';
7
7
  import 'fs/promises';
@@ -3,7 +3,7 @@ import { mkdirSync, writeFileSync, unlinkSync, existsSync, chmodSync, readFileSy
3
3
  import { join } from 'path';
4
4
  import { homedir, platform, arch } from 'os';
5
5
  import { createHash, randomUUID } from 'crypto';
6
- import { h as getFrpsSubdomainHost, i as getFrpsServerPort, j as getFrpsServerAddr } from './run-DHPCWQUq.mjs';
6
+ import { h as getFrpsSubdomainHost, i as getFrpsServerPort, j as getFrpsServerAddr } from './run-Ci6VToZR.mjs';
7
7
  import 'fs/promises';
8
8
  import 'url';
9
9
  import 'node:crypto';
@@ -1,5 +1,5 @@
1
- import { F as resolveModel, O as describeMisconfiguration, P as buildMachineDeps } from './run-DHPCWQUq.mjs';
2
- import { handleRealtimeEvent, initMachineVoiceSession } from './sideband-CYNK4foC.mjs';
1
+ import { F as resolveModel, O as describeMisconfiguration, P as buildMachineDeps } from './run-Ci6VToZR.mjs';
2
+ import { handleRealtimeEvent, initMachineVoiceSession } from './sideband-DE8X9tg3.mjs';
3
3
  import { WebSocket } from 'ws';
4
4
  import { execSync, spawn } from 'child_process';
5
5
  import 'os';
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export { c as connectToHypha, a as createSessionStore, d as daemonStatus, g as getHyphaServerUrl, r as registerMachineService, s as startDaemon, b as stopDaemon } from './run-DHPCWQUq.mjs';
1
+ export { c as connectToHypha, a as createSessionStore, d as daemonStatus, g as getHyphaServerUrl, r as registerMachineService, s as startDaemon, b as stopDaemon } from './run-Ci6VToZR.mjs';
2
2
  import 'os';
3
3
  import 'fs/promises';
4
4
  import 'fs';
@@ -1,5 +1,5 @@
1
1
  var name = "svamp-cli";
2
- var version = "0.2.116";
2
+ var version = "0.2.117";
3
3
  var description = "Svamp CLI — AI workspace daemon on Hypha Cloud";
4
4
  var author = "Amun AI AB";
5
5
  var license = "SEE LICENSE IN LICENSE";
@@ -17,9 +17,9 @@ var exports$1 = {
17
17
  "./cli": "./dist/cli.mjs"
18
18
  };
19
19
  var scripts = {
20
- build: "rm -rf dist bin/skills && mkdir -p bin/skills && cp -r ../../skills/artifact bin/skills/artifact && cp -r ../../skills/loop bin/skills/loop && tsc --noEmit && pkgroll",
20
+ build: "rm -rf dist bin/skills && mkdir -p bin/skills && cp -r ../../skills/artifact bin/skills/artifact && cp -r ../../skills/loop bin/skills/loop && cp -r ../../skills/crew bin/skills/crew && tsc --noEmit && pkgroll",
21
21
  typecheck: "tsc --noEmit",
22
- test: "npx tsx test/test-context-window.mjs && npx tsx test/test-instance-config.mjs && npx tsx test/test-authorize.mjs && npx tsx test/test-normalize-allowed-user.mjs && npx tsx test/test-share-url.mjs && npx tsx test/test-update-sharing-normalization.mjs && npx tsx test/test-staged-homes-sweep.mjs && npx tsx test/test-session-helpers.mjs && npx tsx test/test-cli-routing.mjs && npx tsx test/test-security-context.mjs && npx tsx test/test-isolation-decision.mjs && npx tsx test/test-loop-activation.mjs && npx tsx test/test-message-helpers.mjs && npx tsx test/test-agent-config.mjs && npx tsx test/test-wrap-command.mjs && npx tsx test/test-credential-staging.mjs && npx tsx test/test-claude-auth.mjs && npx tsx test/test-output-formatters.mjs && npx tsx test/test-inbox-guard.mjs && npx tsx test/test-auto-topic.mjs && npx tsx test/test-project-info.mjs && npx tsx test/test-agent-types.mjs && npx tsx test/test-transport.mjs && npx tsx test/test-session-update-handlers.mjs && npx tsx test/test-session-scanner.mjs && npx tsx test/test-hypha-client.mjs && npx tsx test/test-hook-settings.mjs && npx tsx test/test-session-service-logic.mjs && npx tsx test/test-daemon-persistence.mjs && npx tsx test/test-detect-isolation.mjs && npx tsx test/test-machine-service-logic.mjs && npx tsx test/test-interactive-helpers.mjs && npx tsx test/test-codex-backend.mjs && npx tsx test/test-acp-backend.mjs && npx tsx test/test-acp-bridge.mjs && npx tsx test/test-hook-server.mjs && npx tsx test/test-session-commands.mjs && npx tsx test/test-interactive-console.mjs && npx tsx test/test-session-messages.mjs && npx tsx test/test-session-send-query.mjs && npx tsx test/test-skills.mjs && npx tsx test/test-agent-grouping.mjs && npx tsx test/test-machine-list-directory.mjs && npx tsx test/test-service-commands.mjs && npx tsx test/test-supervisor.mjs && npx tsx test/test-supervisor-lock.mjs && node test/test-supervisor-restart.mjs && npx tsx test/test-clear-detection.mjs && npx tsx test/test-session-consolidation.mjs && npx tsx test/test-inbox.mjs && npx tsx test/test-short-id.mjs && npx tsx test/test-session-rpc-dispatch.mjs && npx tsx test/test-sandbox-cli.mjs && npx tsx test/test-serve-manager.mjs && npx tsx test/test-serve-stability.mjs && npx tsx test/test-frpc-e2e.mjs --unit-only && node test/pinnedClaudeCode.test.mjs && node test/fleet.test.mjs && npx tsx test/test-routine.mjs && npx tsx test/test-routine-rpc.mjs && npx tsx test/test-session-file.mjs && npx tsx test/test-channel-rpc.mjs && npx tsx test/test-wise-agent.mjs && npx tsx test/test-channel-agent.mjs && npx tsx test/test-channels-service.mjs && npx tsx test/test-channel-async-reply.mjs && npx tsx test/test-channel-binding.mjs && npx tsx test/test-channel-identity.mjs && npx tsx test/test-wise-agent-auth.mjs && npx tsx test/test-channel-http.mjs && npx tsx test/test-wise-voice.mjs && npx tsx test/test-wise-headless.mjs && npx tsx test/test-wise-machine.mjs",
22
+ test: "npx tsx test/test-context-window.mjs && npx tsx test/test-instance-config.mjs && npx tsx test/test-authorize.mjs && npx tsx test/test-normalize-allowed-user.mjs && npx tsx test/test-share-url.mjs && npx tsx test/test-update-sharing-normalization.mjs && npx tsx test/test-staged-homes-sweep.mjs && npx tsx test/test-session-helpers.mjs && npx tsx test/test-cli-routing.mjs && npx tsx test/test-security-context.mjs && npx tsx test/test-isolation-decision.mjs && npx tsx test/test-loop-activation.mjs && npx tsx test/test-message-helpers.mjs && npx tsx test/test-agent-config.mjs && npx tsx test/test-wrap-command.mjs && npx tsx test/test-credential-staging.mjs && npx tsx test/test-claude-auth.mjs && npx tsx test/test-output-formatters.mjs && npx tsx test/test-inbox-guard.mjs && npx tsx test/test-auto-topic.mjs && npx tsx test/test-project-info.mjs && npx tsx test/test-agent-types.mjs && npx tsx test/test-transport.mjs && npx tsx test/test-session-update-handlers.mjs && npx tsx test/test-session-scanner.mjs && npx tsx test/test-hypha-client.mjs && npx tsx test/test-hook-settings.mjs && npx tsx test/test-session-service-logic.mjs && npx tsx test/test-daemon-persistence.mjs && npx tsx test/test-detect-isolation.mjs && npx tsx test/test-machine-service-logic.mjs && npx tsx test/test-interactive-helpers.mjs && npx tsx test/test-codex-backend.mjs && npx tsx test/test-acp-backend.mjs && npx tsx test/test-acp-bridge.mjs && npx tsx test/test-hook-server.mjs && npx tsx test/test-session-commands.mjs && npx tsx test/test-interactive-console.mjs && npx tsx test/test-session-messages.mjs && npx tsx test/test-session-send-query.mjs && npx tsx test/test-skills.mjs && npx tsx test/test-agent-grouping.mjs && npx tsx test/test-machine-list-directory.mjs && npx tsx test/test-service-commands.mjs && npx tsx test/test-supervisor.mjs && npx tsx test/test-supervisor-lock.mjs && node test/test-supervisor-restart.mjs && npx tsx test/test-clear-detection.mjs && npx tsx test/test-session-consolidation.mjs && npx tsx test/test-inbox.mjs && npx tsx test/test-short-id.mjs && npx tsx test/test-session-rpc-dispatch.mjs && npx tsx test/test-sandbox-cli.mjs && npx tsx test/test-serve-manager.mjs && npx tsx test/test-serve-stability.mjs && npx tsx test/test-frpc-e2e.mjs --unit-only && node test/pinnedClaudeCode.test.mjs && node test/fleet.test.mjs && npx tsx test/test-routine.mjs && npx tsx test/test-routine-rpc.mjs && npx tsx test/test-session-file.mjs && npx tsx test/test-channel-rpc.mjs && npx tsx test/test-wise-agent.mjs && npx tsx test/test-channel-agent.mjs && npx tsx test/test-channels-service.mjs && npx tsx test/test-channel-async-reply.mjs && npx tsx test/test-channel-binding.mjs && npx tsx test/test-channel-identity.mjs && npx tsx test/test-wise-agent-auth.mjs && npx tsx test/test-channel-http.mjs && npx tsx test/test-wise-voice.mjs && npx tsx test/test-wise-headless.mjs && npx tsx test/test-wise-machine.mjs && npx tsx test/test-crew-merge.mjs",
23
23
  "test:hypha": "node --no-warnings test/test-hypha-service.mjs",
24
24
  dev: "tsx src/cli.ts",
25
25
  "dev:daemon": "tsx src/cli.ts daemon start-sync",
@@ -1,4 +1,4 @@
1
- import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import { n as shortId, c as connectToHypha, a as createSessionStore, r as registerMachineService, Q as generateHookSettings } from './run-DHPCWQUq.mjs';
1
+ import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import { n as shortId, c as connectToHypha, a as createSessionStore, r as registerMachineService, Q as generateHookSettings } from './run-Ci6VToZR.mjs';
2
2
  import os from 'node:os';
3
3
  import { resolve, join } from 'node:path';
4
4
  import { existsSync, readFileSync, watch } from 'node:fs';