svamp-cli 0.2.342 → 0.2.356

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 (67) hide show
  1. package/bin/skills/artifact/SKILL.md +19 -5
  2. package/bin/skills/crew/SKILL.md +62 -13
  3. package/dist/DefaultTransport-vfp78fug.mjs +47 -0
  4. package/dist/GeminiTransport-3OkhtFRO.mjs +130 -0
  5. package/dist/KimiTransport-D8n009Tr.mjs +141 -0
  6. package/dist/acpAgentConfig-CObOPlA8.mjs +48 -0
  7. package/dist/acpBackend-UaBD4zL3.mjs +925 -0
  8. package/dist/{adminCommands-CRTBDZan.mjs → adminCommands-BvsYnRDA.mjs} +2 -18
  9. package/dist/{agentCommands-BuO988tJ.mjs → agentCommands-D91RcjqQ.mjs} +6 -23
  10. package/dist/api-BHjyC0Ph.mjs +157 -0
  11. package/dist/{auth--WS0wezs.mjs → auth-CRz8TsEl.mjs} +1 -22
  12. package/dist/bootStatus-DqtNgJch.mjs +143 -0
  13. package/dist/claudeAuth-C0wc1GTM.mjs +291 -0
  14. package/dist/cli.mjs +2948 -5
  15. package/dist/codexAppServerBackend-VGsYtbqg.mjs +786 -0
  16. package/dist/codexProvider-BtxYc5Rj.mjs +68 -0
  17. package/dist/{commands-DDuGBSsz.mjs → commands-B-yczgz_.mjs} +115 -49
  18. package/dist/{commands-DU4B-4Gh.mjs → commands-BMeDrpSv.mjs} +55 -25
  19. package/dist/{commands-DXLOKJOj.mjs → commands-BbmypSgx.mjs} +62 -27
  20. package/dist/{commands-C0HQiu-S.mjs → commands-BkdbelFo.mjs} +78 -38
  21. package/dist/{commands-DXM17M6n.mjs → commands-C13-yE2R.mjs} +42 -19
  22. package/dist/{commands-BKb1g8Gq.mjs → commands-CoOc4qiw.mjs} +20 -30
  23. package/dist/{commands-BsKgZ5sQ.mjs → commands-DW5RnHZw.mjs} +2 -19
  24. package/dist/{commands-Cwxy-C5N.mjs → commands-sPvRcG3J.mjs} +10 -32
  25. package/dist/cron-hHGdb5kU.mjs +132 -0
  26. package/dist/detectIsolation-CrV8IFDv.mjs +339 -0
  27. package/dist/envFlag-COOuJ3AH.mjs +11 -0
  28. package/dist/fileLock-BMPwCrB6.mjs +80 -0
  29. package/dist/{fleet-BHzlAvnu.mjs → fleet-Ca-UjJNL.mjs} +122 -36
  30. package/dist/flushExit-C-i6SGjj.mjs +37 -0
  31. package/dist/friendlyName-1UZcyDhu.mjs +586 -0
  32. package/dist/{headlessCli-C2gvOpOt.mjs → headlessCli-C8HzQvis.mjs} +3 -21
  33. package/dist/hookSettings-FFc1pi8j.mjs +10546 -0
  34. package/dist/{httpServer-Be1Hcddr.mjs → httpServer-BzEDbOGn.mjs} +14 -5
  35. package/dist/hyphaClient-ybivOjZo.mjs +49 -0
  36. package/dist/index.mjs +40 -8
  37. package/dist/instance-CeHz5C8v.mjs +203 -0
  38. package/dist/kimiProvider-DbxODebR.mjs +69 -0
  39. package/dist/kimiSetup-NGsoYB5h.mjs +48 -0
  40. package/dist/loopVerify-DALtTk2V.mjs +444 -0
  41. package/dist/machineTools-DygM1jXW.mjs +580 -0
  42. package/dist/model-CUHojegn.mjs +66 -0
  43. package/dist/{notifyCommands-0aZUwKQK.mjs → notifyCommands-CCUE18Q-.mjs} +3 -18
  44. package/dist/package-BrsiNutS.mjs +64 -0
  45. package/dist/pinnedCodex-XW4qvOAe.mjs +11 -0
  46. package/dist/pinnedNono-5ZHb6328.mjs +15 -0
  47. package/dist/{rpc-rAQQ1q8t.mjs → rpc-CBkSkzsI.mjs} +38 -6
  48. package/dist/{rpc-B2Lc2BAB.mjs → rpc-ccY7xvlo.mjs} +33 -22
  49. package/dist/{run-C7QVfyw4.mjs → run-DCq3aEiF.mjs} +17 -9
  50. package/dist/run-DDmeV9K3.mjs +9826 -0
  51. package/dist/sandboxDetect-Cdzsj5TC.mjs +19 -0
  52. package/dist/{scheduler-CzF4lOe_.mjs → scheduler-BkVJ27Ki.mjs} +38 -6
  53. package/dist/securityContext-Cm5UTCuD.mjs +218 -0
  54. package/dist/serveAuth-4M0vwMyj.mjs +460 -0
  55. package/dist/{serveCommands-EIEqdaVu.mjs → serveCommands-CLhkccJM.mjs} +13 -34
  56. package/dist/sharingNotificationSync-qGIQ-3yf.mjs +359 -0
  57. package/dist/{sideband-BoutiRJi.mjs → sideband-68HhGT0i.mjs} +1 -21
  58. package/dist/store-B4E6NzG6.mjs +356 -0
  59. package/dist/store-fi0f-ff1.mjs +184 -0
  60. package/dist/types-57J3Re0n.mjs +17 -0
  61. package/dist/wantsHelp-6bMOeNUz.mjs +5 -0
  62. package/dist/wrapCommand-CdNawSaa.mjs +117 -0
  63. package/package.json +3 -3
  64. package/dist/cli-B24CmIli.mjs +0 -2780
  65. package/dist/package-CTbGYaJG.mjs +0 -64
  66. package/dist/run-CF4kAVPW.mjs +0 -26508
  67. package/dist/sandboxDetect-DNTcbgWD.mjs +0 -12
@@ -1,27 +1,10 @@
1
1
  import { existsSync, readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
- import { f as flushAndExit } from './cli-B24CmIli.mjs';
4
- import { f as resolveProjectRoot, o as searchIssues, m as listIssues, n as isPendingIssue, j as resumeIssue, p as pauseIssue, k as addComment, u as updateIssue, h as getIssue, J as checkVerifyCommand, q as isVisibleTo, K as summarize, l as addIssue } from './run-CF4kAVPW.mjs';
5
- import './serviceManager-hlOVxkhW.mjs';
6
- import 'os';
7
- import 'fs/promises';
8
- import 'fs';
9
- import 'path';
10
- import 'url';
11
- import 'child_process';
12
- import 'crypto';
3
+ import { a as flushAndExit } from './flushExit-C-i6SGjj.mjs';
4
+ import { r as resolveProjectRoot, s as searchIssues, l as listIssues, j as isPendingIssue, e as resumeIssue, f as pauseIssue, h as addComment, a as applyTriageStatus, u as updateIssue, p as parseScopeArg, I as ISSUE_SCOPES, b as parseStatusArg, d as ISSUE_STATUSES, g as getIssue, m as checkVerifyCommand, k as isVisibleTo, n as summarize, i as addIssue } from './store-B4E6NzG6.mjs';
13
5
  import 'node:crypto';
14
6
  import 'node:child_process';
15
- import 'util';
16
- import 'http';
17
- import 'net';
18
- import 'node:os';
19
- import 'node:events';
20
- import '@agentclientprotocol/sdk';
21
- import 'node:util';
22
- import 'node:readline';
23
- import 'node:fs/promises';
24
- import 'yaml';
7
+ import './fileLock-BMPwCrB6.mjs';
25
8
 
26
9
  const STATUS_GLYPH = {
27
10
  ready: "\u25C9",
@@ -64,12 +47,27 @@ function loopActiveForCwd(cwd = process.cwd(), sid = process.env.SVAMP_SESSION_I
64
47
  }
65
48
  return false;
66
49
  }
50
+ const VALUE_FLAGS = /* @__PURE__ */ new Set([
51
+ "--answer",
52
+ "--blocked",
53
+ "--body",
54
+ "--branch",
55
+ "--label",
56
+ "--option",
57
+ "--question",
58
+ "--scope",
59
+ "--session",
60
+ "--status",
61
+ "--title",
62
+ "--verify",
63
+ "--verify-cmd"
64
+ ]);
67
65
  function positional(args) {
68
66
  const out = [];
69
67
  for (let i = 0; i < args.length; i++) {
70
68
  const a = args[i];
71
69
  if (a.startsWith("--")) {
72
- if (a !== "--json" && a !== "--ready") i++;
70
+ if (!a.includes("=") && VALUE_FLAGS.has(a)) i++;
73
71
  continue;
74
72
  }
75
73
  out.push(a);
@@ -272,11 +270,44 @@ ${res.output || ""}
272
270
  if (labels.length) patch.labels = labels;
273
271
  const v = buildVerify(rest);
274
272
  if (v || has(rest, "--no-verify")) patch.verify = has(rest, "--no-verify") ? null : v;
275
- if (flag(rest, "--scope")) patch.scope = flag(rest, "--scope");
273
+ if (flag(rest, "--scope")) {
274
+ const nextScope = parseScopeArg(flag(rest, "--scope"));
275
+ if (!nextScope) {
276
+ console.error(`Invalid --scope "${flag(rest, "--scope")}". Must be one of: ${ISSUE_SCOPES.join(", ")}.`);
277
+ process.exit(1);
278
+ }
279
+ patch.scope = nextScope;
280
+ }
276
281
  if (has(rest, "--unclaim")) patch.session = null;
277
282
  else if (flag(rest, "--session")) patch.session = flag(rest, "--session");
278
283
  if (flag(rest, "--body")) patch.body = flag(rest, "--body");
279
- if (flag(rest, "--status")) patch.status = flag(rest, "--status");
284
+ if (flag(rest, "--status")) {
285
+ const nextStatus = parseStatusArg(flag(rest, "--status"));
286
+ if (!nextStatus) {
287
+ console.error(`Invalid --status "${flag(rest, "--status")}". Must be one of: ${ISSUE_STATUSES.join(", ")}.`);
288
+ console.error(`(To close an issue, use \`svamp issue close ${id}\` \u2014 it runs the verify-cmd.)`);
289
+ process.exit(1);
290
+ }
291
+ if (nextStatus === "archived") {
292
+ const current = getIssue(root, id);
293
+ const vc = current?.verify?.type === "command" ? current.verify.text?.trim() : "";
294
+ if (vc && current?.status !== "archived") {
295
+ if (has(rest, "--force")) {
296
+ console.error(`\u26A0 #${id}: archiving WITHOUT running verify-cmd (--force): \`${vc}\``);
297
+ } else {
298
+ const res = checkVerifyCommand(root, current);
299
+ if (!res.ok) {
300
+ console.error(`Refusing to archive #${id} via edit: its verify-cmd failed \u2014 the fix is not verified.
301
+ $ ${res.cmd}
302
+ ${res.output || ""}
303
+ (Use \`svamp issue archive\` for an obsolete/duplicate item, or pass --force to override.)`);
304
+ process.exit(1);
305
+ }
306
+ }
307
+ }
308
+ }
309
+ patch.status = nextStatus;
310
+ }
280
311
  if (has(rest, "--crew")) patch.disposition = "crew";
281
312
  else if (has(rest, "--inline")) patch.disposition = "inline";
282
313
  if (has(rest, "--triaged")) patch.triaged = true;
@@ -291,7 +322,7 @@ ${res.output || ""}
291
322
  case "triage": {
292
323
  const id = positional(rest)[0];
293
324
  if (!id) {
294
- console.error('usage: svamp issue triage <id> [--title "\u2026"] [--label x \u2026] [--crew|--inline] [--verify-cmd "cmd" | --verify "criterion" | --manual] [--body "rewritten"] [--backlog]');
325
+ console.error('usage: svamp issue triage <id> [--title "\u2026"] [--label x \u2026] [--crew|--inline] [--verify-cmd "cmd" | --verify "criterion" | --manual] [--body "rewritten"]');
295
326
  process.exit(1);
296
327
  }
297
328
  const patch = { triaged: true };
@@ -303,7 +334,10 @@ ${res.output || ""}
303
334
  const tv = buildVerify(rest);
304
335
  if (tv) patch.verify = tv;
305
336
  if (flag(rest, "--body")) patch.body = flag(rest, "--body");
306
- patch.status = "ready";
337
+ applyTriageStatus(root, id, patch, (m) => {
338
+ console.error(m);
339
+ process.exit(1);
340
+ });
307
341
  const updated = updateIssue(root, id, patch);
308
342
  if (!updated) {
309
343
  console.error(`Issue not found: ${id}`);
@@ -497,7 +531,8 @@ the user explicitly reserved): re-run with \`--blocked "<the specific blocker>"\
497
531
  " # A paused issue drops out of `pending`, so a loop converges cleanly instead",
498
532
  ' # of spinning on it. NOT for "please check my work" \u2014 close finished work.',
499
533
  ' resume <id> [--answer "\u2026"] # un-pause (the user answering a --question resumes it automatically)',
500
- " pending [--session <id>] [--mine] # exit 0 if no actionable (ready/in_progress) issues \u2014 the loop oracle",
534
+ " pending [--session <id>] [--mine] # exit 0 when NO issue is open \u2014 i.e. every issue is",
535
+ " # archived or paused. Counted by EXCLUSION, not an allow-list (#107). The loop oracle.",
501
536
  ' search "<query>" [--json]',
502
537
  " guide <triage|work> # the triage / work-loop procedure (so triggers can stay short)"
503
538
  ].join("\n"));
@@ -508,4 +543,4 @@ the user explicitly reserved): re-run with \`--blocked "<the specific blocker>"\
508
543
  }
509
544
  }
510
545
 
511
- export { issueCommand, loopActiveForCwd };
546
+ export { VALUE_FLAGS, issueCommand, loopActiveForCwd, positional };
@@ -1,28 +1,22 @@
1
1
  import { existsSync } from 'node:fs';
2
2
  import { join, resolve } from 'node:path';
3
- import { connectAndGetMachine, resolveSessionId, createWorktree, connectAndResolveSession } from './commands-DDuGBSsz.mjs';
3
+ import { connectAndGetMachine, resolveSessionId, createWorktree, connectAndResolveSession } from './commands-B-yczgz_.mjs';
4
4
  import { execFileSync } from 'node:child_process';
5
- import { ak as withFileLock, u as updateIssue, k as addComment, l as addIssue, e as shortId } from './run-CF4kAVPW.mjs';
6
- import { w as wantsHelp } from './cli-B24CmIli.mjs';
5
+ import { w as withFileLock } from './fileLock-BMPwCrB6.mjs';
6
+ import { u as updateIssue, h as addComment, i as addIssue, o as resolveCrewIssueOnMerge } from './store-B4E6NzG6.mjs';
7
+ import { s as shortId } from './friendlyName-1UZcyDhu.mjs';
8
+ import { w as wantsHelp } from './wantsHelp-6bMOeNUz.mjs';
7
9
  import 'node:os';
8
- import 'os';
9
- import 'fs/promises';
10
+ import './hyphaClient-ybivOjZo.mjs';
11
+ import './types-57J3Re0n.mjs';
12
+ import './securityContext-Cm5UTCuD.mjs';
13
+ import './loopVerify-DALtTk2V.mjs';
14
+ import './instance-CeHz5C8v.mjs';
10
15
  import 'fs';
11
16
  import 'path';
12
- import 'url';
13
- import 'child_process';
14
- import 'crypto';
17
+ import 'os';
18
+ import './flushExit-C-i6SGjj.mjs';
15
19
  import 'node:crypto';
16
- import 'util';
17
- import 'http';
18
- import 'net';
19
- import 'node:events';
20
- import '@agentclientprotocol/sdk';
21
- import 'node:util';
22
- import 'node:readline';
23
- import 'node:fs/promises';
24
- import 'yaml';
25
- import './serviceManager-hlOVxkhW.mjs';
26
20
 
27
21
  function git(cwd, args) {
28
22
  return execFileSync("git", args, { cwd, stdio: "pipe", encoding: "utf-8" }).toString();
@@ -187,6 +181,9 @@ function projectRootFromWorktree(worktreePath) {
187
181
  if (idx === -1) return null;
188
182
  return worktreePath.slice(0, idx);
189
183
  }
184
+ function canonicalProjectRoot(anyPathInProject) {
185
+ return projectRootFromWorktree(anyPathInProject) ?? anyPathInProject;
186
+ }
190
187
  function removeWorktree(worktreePath) {
191
188
  const projectRoot = projectRootFromWorktree(worktreePath);
192
189
  if (!projectRoot) return;
@@ -214,14 +211,17 @@ function resolveNotifyLead(leadId, childParentSessionId, childId) {
214
211
  return leadId || childParentSessionId || childId;
215
212
  }
216
213
 
214
+ function crewReportUrgency(crew) {
215
+ return crew?.action === "block" ? "urgent" : "normal";
216
+ }
217
217
  function isStandaloneChild(crew) {
218
218
  return !!crew && crew.standalone === true;
219
219
  }
220
- function worktreeCrewMeta(feature, branch, worktreePath, baseBranch) {
221
- return { role: "feature", feature, branch, worktreePath, baseBranch };
220
+ function worktreeCrewMeta(feature, branch, worktreePath, baseBranch, action) {
221
+ return { role: "feature", feature, branch, worktreePath, baseBranch, ...action ? { action } : {} };
222
222
  }
223
- function standaloneCrewMeta(feature, directory) {
224
- return { role: "feature", feature, standalone: true, directory };
223
+ function standaloneCrewMeta(feature, directory, action) {
224
+ return { role: "feature", feature, standalone: true, directory, ...action ? { action } : {} };
225
225
  }
226
226
  function childDirectory(crew, sessionDir) {
227
227
  return crew?.worktreePath || crew?.directory || sessionDir || "";
@@ -277,7 +277,7 @@ async function waitForChildIdle(machine, childId, timeoutMs = 2e4) {
277
277
  }
278
278
  return false;
279
279
  }
280
- async function inbox(machine, fromId, toId, body, subject) {
280
+ async function inbox(machine, fromId, toId, body, subject, urgency = "normal") {
281
281
  await rpc(machine, toId, "sendInboxMessage", {
282
282
  message: {
283
283
  messageId: shortId(),
@@ -288,7 +288,7 @@ async function inbox(machine, fromId, toId, body, subject) {
288
288
  fromSession: fromId,
289
289
  to: toId,
290
290
  subject,
291
- urgency: "normal"
291
+ urgency
292
292
  }
293
293
  });
294
294
  }
@@ -306,6 +306,7 @@ async function featureStart(brief, opts = {}) {
306
306
  const standalone = !!opts.noWorktree;
307
307
  let childDir;
308
308
  let base = "";
309
+ const leadDir = opts.directory || lead.directory;
309
310
  if (standalone) {
310
311
  const dir = opts.directory;
311
312
  if (!dir) {
@@ -319,14 +320,14 @@ async function featureStart(brief, opts = {}) {
319
320
  }
320
321
  console.log(`Standalone child folder: ${childDir} (no worktree)`);
321
322
  } else {
322
- const projectRoot2 = opts.directory || lead.directory;
323
- if (!projectRoot2) {
323
+ if (!leadDir) {
324
324
  console.error("Could not determine the lead project directory.");
325
325
  process.exit(1);
326
326
  }
327
- base = opts.baseBranch || currentBranch(projectRoot2) || "main";
327
+ const worktreeRoot = canonicalProjectRoot(leadDir);
328
+ base = opts.baseBranch || currentBranch(worktreeRoot) || "main";
328
329
  try {
329
- wt = createWorktree(projectRoot2);
330
+ wt = createWorktree(worktreeRoot);
330
331
  } catch (e) {
331
332
  console.error(`Failed to create worktree: ${e.message}`);
332
333
  process.exit(1);
@@ -334,7 +335,7 @@ async function featureStart(brief, opts = {}) {
334
335
  childDir = wt.path;
335
336
  console.log(`Worktree: ${wt.branch} \u2192 ${wt.path}`);
336
337
  }
337
- const projectRoot = lead.directory || opts.directory || childDir;
338
+ const projectRoot = canonicalProjectRoot(leadDir || childDir);
338
339
  const result = await machine.spawnSession({
339
340
  directory: childDir,
340
341
  agent: "claude",
@@ -395,8 +396,9 @@ async function featureStart(brief, opts = {}) {
395
396
  }
396
397
  const oracle = opts.oracle?.trim();
397
398
  const patch = {
398
- crew: standalone ? standaloneCrewMeta(brief.trim(), childDir) : worktreeCrewMeta(brief.trim(), wt.branch, wt.path, base)
399
+ crew: standalone ? standaloneCrewMeta(brief.trim(), childDir, opts.action) : worktreeCrewMeta(brief.trim(), wt.branch, wt.path, base, opts.action)
399
400
  };
401
+ const childMaxIterations = opts.maxRounds ?? 20;
400
402
  let judges = [];
401
403
  if (oracle) {
402
404
  judges = [{ type: "oracle", cmd: oracle, on_fail: "escalate" }, { type: "parent", parent: lead.sessionId }];
@@ -404,8 +406,10 @@ async function featureStart(brief, opts = {}) {
404
406
  until: brief.trim(),
405
407
  oracle,
406
408
  parent: lead.sessionId,
407
- max_iterations: opts.maxRounds ?? 20
409
+ max_iterations: childMaxIterations
408
410
  };
411
+ } else if (opts.maxRounds != null) {
412
+ patch.loop = { max_iterations: childMaxIterations };
409
413
  }
410
414
  try {
411
415
  await rpc(machine, childId, "updateConfig", { patch });
@@ -527,9 +531,10 @@ async function featureReport(text, opts = {}) {
527
531
  process.exit(1);
528
532
  }
529
533
  const kind = opts.blocker ? "blocker" : "progress";
534
+ const urgency = opts.blocker ? "urgent" : crewReportUrgency(meta?.crew);
530
535
  await inbox(machine, selfId, parent, `<feature-${kind} from="${selfId}">
531
536
  ${text.trim()}
532
- </feature-${kind}>`, `feature ${kind}`);
537
+ </feature-${kind}>`, `feature ${kind}`, urgency);
533
538
  console.log(`Reported ${kind} to lead ${parent.slice(0, 8)}.`);
534
539
  } finally {
535
540
  await server.disconnect();
@@ -555,7 +560,7 @@ async function featureDone(opts = {}) {
555
560
  `No branch to merge (standalone folder). Detach & close with: svamp feature merge ${selfId.slice(0, 8)}`,
556
561
  `</done-request>`
557
562
  ].filter(Boolean).join("\n");
558
- await inbox(machine, selfId, parent, body2, `done-request (standalone)`);
563
+ await inbox(machine, selfId, parent, body2, `done-request (standalone)`, crewReportUrgency(crew));
559
564
  console.log(`Sent done-request to lead ${parent.slice(0, 8)} (standalone folder ${dir2 || "?"}; no branch to merge).`);
560
565
  return;
561
566
  }
@@ -576,7 +581,7 @@ async function featureDone(opts = {}) {
576
581
  `Review & merge with: svamp feature merge ${selfId.slice(0, 8)}`,
577
582
  `</merge-request>`
578
583
  ].filter(Boolean).join("\n");
579
- await inbox(machine, selfId, parent, body, `merge-request (${branch || "feature"})`);
584
+ await inbox(machine, selfId, parent, body, `merge-request (${branch || "feature"})`, crewReportUrgency(crew));
580
585
  console.log(`Sent merge-request to lead ${parent.slice(0, 8)} (branch ${branch || "?"}, +${ab.ahead} commits).`);
581
586
  if (dirty) console.log(" \u26A0 Worktree is dirty \u2014 commit your work or the merge will be rejected.");
582
587
  } finally {
@@ -624,7 +629,14 @@ async function featureMerge(childPartial, opts = {}) {
624
629
  notifyLead,
625
630
  childId,
626
631
  `<crew-freeze>Your lead is merging branch ${branch} into ${base}. Stop editing files now; do not commit until told.</crew-freeze>`,
627
- "crew: freeze for merge"
632
+ "crew: freeze for merge",
633
+ // #1619: URGENT. This is a pre-emption notice, and the state it must interrupt —
634
+ // a child mid-work — is by definition NOT idle, so at 'normal' urgency
635
+ // classifyInbound QUEUES it until the child stops on its own. waitForChildIdle
636
+ // below is supposed to observe the child halting BECAUSE of this message; a queued
637
+ // message causes no halt, so the wait could only ever time out, and mergeBack can
638
+ // then reach `git worktree remove --force` on a tree the child is still writing.
639
+ "urgent"
628
640
  );
629
641
  } catch {
630
642
  }
@@ -644,7 +656,10 @@ async function featureMerge(childPartial, opts = {}) {
644
656
  Merge was not applied: ${r.detail}
645
657
  Resolve and run \`svamp feature done\` again.
646
658
  </crew-rework>`,
647
- "crew: rework needed"
659
+ "crew: rework needed",
660
+ // #1619: URGENT — the comment above says 're-wake the child', which at
661
+ // 'normal' urgency is exactly what did not happen.
662
+ "urgent"
648
663
  );
649
664
  } catch {
650
665
  }
@@ -660,7 +675,12 @@ Resolve and run \`svamp feature done\` again.
660
675
  Merge could not be applied (${r.stage}): ${r.detail}
661
676
  Stop editing and hold \u2014 do not keep working. Once the blocker is resolved, the lead can retry \`svamp feature merge\`.
662
677
  </crew-blocked>`,
663
- "crew: merge blocked"
678
+ "crew: merge blocked",
679
+ // #1619: URGENT. #0353's whole point is to STOP a child that is 'silently
680
+ // LOOPING against an un-merged branch, burning tokens toward the cost
681
+ // ceiling'. A looping child is never idle, so a queued STOP notice arrived
682
+ // only once it was no longer needed.
683
+ "urgent"
664
684
  );
665
685
  } catch {
666
686
  }
@@ -676,6 +696,12 @@ Stop editing and hold \u2014 do not keep working. Once the blocker is resolved,
676
696
  console.warn(`Merged, but could not archive child: ${e.message}`);
677
697
  }
678
698
  }
699
+ try {
700
+ const resolved = resolveCrewIssueOnMerge(projectRoot, childId, { branch, base });
701
+ if (resolved) console.log(` issue: #${resolved.id} closed (merged).`);
702
+ } catch (e) {
703
+ console.warn(`Merged, but could not close the crew tracking issue: ${e.message}`);
704
+ }
679
705
  const cleanup = r.cleanupWarning ? "worktree NOT removed (see warning)" : isMainWorkingTree(projectRoot, worktreePath) ? "ran in the lead tree (no worktree to remove)" : "worktree removed";
680
706
  if (r.cleanupWarning) console.warn(`\u26A0\uFE0F ${r.cleanupWarning}`);
681
707
  console.log(`\u2705 Merged ${branch} \u2192 ${base}; ${cleanup}; child ${childId.slice(0, 8)} ${opts.keepChild ? "kept" : "archived"}.`);
@@ -711,6 +737,10 @@ const FEATURE_HELP = `Usage: svamp feature <command>
711
737
  [--max N] [--dir <path>] [--no-worktree] [--model <id>] [--lead <id>] [-m <machine>]
712
738
  Spawn a managed child of the lead + attach a loop gate (oracle\u2192parent).
713
739
  --model sets the child's model (e.g. claude-fable-5, claude-opus-4-8).
740
+ --action nudge|block controls how the lead is notified when the child reports or
741
+ finishes: 'nudge' (default) sends at normal urgency, which the lead picks up on its
742
+ next idle turn; 'block' sends urgent, which WAKES the lead. An explicit
743
+ 'crew report --blocker' is always urgent regardless.
714
744
  --issue <id> links the child to an existing backlog issue: the child's verdict
715
745
  routes ONTO that issue \u2014 the seam that joins crew to the backlog that
716
746
  'svamp issue pending' (the loop oracle) reads.
@@ -737,6 +767,16 @@ async function crewCommand(args) {
737
767
  const valueFlags = ["--oracle", "--base", "--action", "--max", "--dir", "-d", "--lead", "-m", "--machine", "--issue", "--model"];
738
768
  const brief = positionals(rest, valueFlags).join(" ");
739
769
  const maxStr = flag(rest, "--max");
770
+ let maxRounds;
771
+ if (maxStr !== void 0) {
772
+ const n = Number(maxStr);
773
+ if (!Number.isInteger(n) || n <= 0) {
774
+ console.error(`Error: --max must be a positive integer (got "${maxStr}").`);
775
+ console.error(" Omit it to use the default round cap; it is a HARD stop for the child.");
776
+ process.exit(1);
777
+ }
778
+ maxRounds = n;
779
+ }
740
780
  const action = flag(rest, "--action");
741
781
  await featureStart(brief, {
742
782
  leadId: lead,
@@ -748,7 +788,7 @@ async function crewCommand(args) {
748
788
  issueId: flag(rest, "--issue"),
749
789
  model: flag(rest, "--model"),
750
790
  action: action === "block" ? "block" : action === "nudge" ? "nudge" : void 0,
751
- maxRounds: maxStr ? parseInt(maxStr, 10) : void 0
791
+ maxRounds
752
792
  });
753
793
  } else if (sub === "list" || sub === "ls") {
754
794
  await featureList({ leadId: lead, machineId, json: has(rest, "--json") });
@@ -1,28 +1,22 @@
1
- import { connectAndResolveSession } from './commands-DDuGBSsz.mjs';
2
- import { w as wantsHelp } from './cli-B24CmIli.mjs';
1
+ import { connectAndResolveSession } from './commands-B-yczgz_.mjs';
2
+ import { w as wantsHelp } from './wantsHelp-6bMOeNUz.mjs';
3
3
  import 'node:fs';
4
4
  import 'node:child_process';
5
5
  import 'node:path';
6
6
  import 'node:os';
7
- import './run-CF4kAVPW.mjs';
8
- import 'os';
9
- import 'fs/promises';
7
+ import './hyphaClient-ybivOjZo.mjs';
8
+ import './friendlyName-1UZcyDhu.mjs';
9
+ import 'node:crypto';
10
+ import './types-57J3Re0n.mjs';
11
+ import './securityContext-Cm5UTCuD.mjs';
12
+ import './loopVerify-DALtTk2V.mjs';
13
+ import './instance-CeHz5C8v.mjs';
10
14
  import 'fs';
11
15
  import 'path';
12
- import 'url';
13
- import 'child_process';
14
- import 'crypto';
15
- import 'node:crypto';
16
- import 'util';
17
- import 'http';
18
- import 'net';
19
- import 'node:events';
20
- import '@agentclientprotocol/sdk';
21
- import 'node:util';
22
- import 'node:readline';
23
- import 'node:fs/promises';
24
- import 'yaml';
25
- import './serviceManager-hlOVxkhW.mjs';
16
+ import 'os';
17
+ import './store-B4E6NzG6.mjs';
18
+ import './fileLock-BMPwCrB6.mjs';
19
+ import './flushExit-C-i6SGjj.mjs';
26
20
 
27
21
  function argVal(args, name) {
28
22
  const i = args.indexOf(name);
@@ -117,6 +111,27 @@ async function outpostInstall(args) {
117
111
  if (info.downloadBase) console.log("Direct download: " + info.downloadBase);
118
112
  }
119
113
  }
114
+ async function outpostRotate(args) {
115
+ const dl = argVal(args, "--download-base");
116
+ if (!args.includes("--yes") && !args.includes("-y")) {
117
+ console.log("This REVOKES this session's outpost token and mints a new one.");
118
+ console.log("Every currently-connected outpost will stop working until it re-runs the new");
119
+ console.log("install command. Re-run with --yes to proceed.");
120
+ process.exit(1);
121
+ }
122
+ const info = await withSession((m, id) => m.sessionRPC(id, "outpostRotateToken", dl ? { params: { downloadBase: dl } } : {}));
123
+ if (!info || info.error) {
124
+ console.error(`Could not rotate the outpost token: ${info?.error || "unknown error"}`);
125
+ process.exit(1);
126
+ }
127
+ console.log("\u2713 outpost token rotated \u2014 the previous token no longer works.\n");
128
+ console.log("Any machine that was connected must re-run the install command below:\n");
129
+ const gen = info.install || {};
130
+ if (gen.unix) console.log(" macOS / Linux:\n " + gen.unix + "\n");
131
+ if (gen.windows) console.log(" Windows (PowerShell):\n " + gen.windows + "\n");
132
+ if (info.pairingCode) console.log(` \u2026or the short pairing code: ${info.pairingCode}
133
+ `);
134
+ }
120
135
  function usage() {
121
136
  console.log(`svamp outpost \u2014 drive a connected remote machine (FDE outpost)
122
137
 
@@ -127,6 +142,10 @@ function usage() {
127
142
  stdout/stderr/exit code synchronously (like a local command).
128
143
  With one outpost connected, --machine is optional.
129
144
  svamp outpost install [--os <os>] Reprint the install one-liner(s) for this session's outpost
145
+ svamp outpost rotate [--yes] REVOKE this session's outpost token and mint a new one.
146
+ Use after the install one-liner has been shared somewhere it
147
+ should not have been. Every currently-connected outpost stops
148
+ working and must re-run the new install command.
130
149
 
131
150
  With several machines connected, target each by id, e.g.:
132
151
  svamp outpost exec --machine build-mac "uname -a"
@@ -155,6 +174,10 @@ async function outpostCommand(args) {
155
174
  await outpostInstall(args.slice(1));
156
175
  return;
157
176
  }
177
+ if (sub === "rotate" || sub === "revoke") {
178
+ await outpostRotate(args.slice(1));
179
+ return;
180
+ }
158
181
  console.error(`unknown outpost command: ${sub}`);
159
182
  usage();
160
183
  process.exit(1);
@@ -1,27 +1,13 @@
1
- import { spawnSync } from 'node:child_process';
2
- import { f as resolveProjectRoot, L as workflowSteps, y as setWorkflowEnabled, F as isWorkflowEnabled, z as removeWorkflow, w as getWorkflow, E as listWorkflows, A as validateCronExpr, C as saveWorkflow, D as rawWorkflow, M as describeNextFire } from './run-CF4kAVPW.mjs';
3
- import { w as wantsHelp } from './cli-B24CmIli.mjs';
4
- import 'os';
5
- import 'fs/promises';
6
- import 'fs';
7
- import 'path';
8
- import 'url';
9
- import 'child_process';
10
- import 'crypto';
1
+ import { r as resolveProjectRoot } from './store-B4E6NzG6.mjs';
2
+ import { w as wantsHelp } from './wantsHelp-6bMOeNUz.mjs';
3
+ import { s as setWorkflowEnabled, i as isWorkflowEnabled, r as removeWorkflow, g as getWorkflow, l as listWorkflows, c as workflowSteps, a as saveWorkflow, b as rawWorkflow } from './store-fi0f-ff1.mjs';
4
+ import { v as validateCronExpr, d as describeNextFire } from './cron-hHGdb5kU.mjs';
11
5
  import 'node:fs';
12
6
  import 'node:path';
13
7
  import 'node:crypto';
14
- import 'util';
15
- import 'http';
16
- import 'net';
17
- import 'node:os';
18
- import 'node:events';
19
- import '@agentclientprotocol/sdk';
20
- import 'node:util';
21
- import 'node:readline';
22
- import 'node:fs/promises';
8
+ import 'node:child_process';
9
+ import './fileLock-BMPwCrB6.mjs';
23
10
  import 'yaml';
24
- import './serviceManager-hlOVxkhW.mjs';
25
11
 
26
12
  function flag(args, name) {
27
13
  const i = args.indexOf(name);
@@ -161,16 +147,20 @@ async function workflowCommand(args) {
161
147
  console.error(`Workflow not found: ${name}`);
162
148
  process.exit(1);
163
149
  }
164
- const steps = workflowSteps(wf);
165
- console.log(`\u25B6 running workflow "${wf.name}" (${steps.length} step${steps.length === 1 ? "" : "s"})`);
166
- for (let i = 0; i < steps.length; i++) {
167
- const step = steps[i];
168
- console.log(` [${i + 1}/${steps.length}] $ ${step.run}`);
169
- const r = spawnSync("sh", ["-c", step.run], { cwd: root, stdio: "inherit" });
170
- if (r.status !== 0) {
171
- console.error(` \u2717 step ${i + 1} failed (exit ${r.status ?? "signal"}). Stopping.`);
172
- process.exit(r.status ?? 1);
173
- }
150
+ const { runWorkflow } = await import('./run-DDmeV9K3.mjs').then(function (n) { return n.e; });
151
+ const run = await runWorkflow(root, wf, { trigger: "manual", actor: process.env.SVAMP_SESSION_ID || null });
152
+ console.log(`\u25B6 ran workflow "${wf.name}" (${run.steps.length} step${run.steps.length === 1 ? "" : "s"}) \u2192 ${run.status}`);
153
+ for (const st of run.steps) {
154
+ console.log(` [${st.index + 1}] $ ${st.cmd}`);
155
+ if (st.stdout) console.log(st.stdout.trimEnd());
156
+ if (st.stderr) console.error(st.stderr.trimEnd());
157
+ if (st.timedOut) console.error(` \u2717 step ${st.index + 1} timed out.`);
158
+ else if (st.exitCode !== 0) console.error(` \u2717 step ${st.index + 1} failed (exit ${st.exitCode ?? "signal"}).`);
159
+ }
160
+ if (run.escalatedIssue) console.log(` \u21B3 escalated to issue #${run.escalatedIssue} (agent will correct the workflow).`);
161
+ if (run.status !== "success") {
162
+ console.error(`\u2717 workflow "${wf.name}" failed.`);
163
+ process.exit(1);
174
164
  }
175
165
  console.log(`\u2713 workflow "${wf.name}" completed.`);
176
166
  break;
@@ -1,25 +1,8 @@
1
1
  import os from 'os';
2
2
  import fs__default from 'fs';
3
3
  import { resolve, join, relative } from 'path';
4
- import { W as parseFrontmatter, X as getSkillsServer, Y as getSkillsWorkspaceName, Z as getSkillsCollectionName, _ as fetchWithTimeout, $ as searchSkills, a0 as SKILLS_DIR, a1 as getSkillInfo, a2 as downloadSkillFile, a3 as listSkillFiles } from './run-CF4kAVPW.mjs';
5
- import 'fs/promises';
6
- import 'url';
7
- import 'child_process';
8
- import 'crypto';
9
- import 'node:fs';
10
- import 'node:path';
11
- import 'node:crypto';
12
- import 'node:child_process';
13
- import 'util';
14
- import 'http';
15
- import 'net';
16
- import 'node:os';
17
- import 'node:events';
18
- import '@agentclientprotocol/sdk';
19
- import 'node:util';
20
- import 'node:readline';
21
- import 'node:fs/promises';
22
- import 'yaml';
4
+ import { parseFrontmatter, getSkillsServer, getSkillsWorkspaceName, getSkillsCollectionName, fetchWithTimeout, searchSkills, SKILLS_DIR, getSkillInfo, downloadSkillFile, listSkillFiles } from './api-BHjyC0Ph.mjs';
5
+ import './instance-CeHz5C8v.mjs';
23
6
 
24
7
  const SVAMP_HOME = process.env.SVAMP_HOME || join(os.homedir(), ".svamp");
25
8
  const ENV_FILE = join(SVAMP_HOME, ".env");