@krapht/ork 1.0.0-alpha.19 → 1.0.0-alpha.20

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/{__build_info__-dPf4cEGL.mjs → __build_info__-D6mXC4cD.mjs} +2 -2
  2. package/dist/argument-error-CihH46Le.mjs +27 -0
  3. package/dist/{attach-BQl8nQz2.mjs → attach-C1OnxyMh.mjs} +7 -5
  4. package/dist/{attach-DhZQ87Nz.mjs → attach-CEGbRg1D.mjs} +126 -69
  5. package/dist/bin.mjs +556 -119
  6. package/dist/{brief-BQbeoPMc.mjs → brief-DH66xmj8.mjs} +11 -6
  7. package/dist/{builtin-files-CWL3JEo-.mjs → builtin-files-Cf-NZDqM.mjs} +1 -1
  8. package/dist/{command-tree-B6uvMJq1.mjs → command-tree-CEkzWNy6.mjs} +1 -1
  9. package/dist/{completion-DKPuD_Rw.mjs → completion-DS0gCZRm.mjs} +19 -12
  10. package/dist/{context-Bw6R_EYq.mjs → context-CGPh12HV.mjs} +3 -3
  11. package/dist/{detached-BQlbmqR0.mjs → detached-Dakw5RZj.mjs} +3 -3
  12. package/dist/extra-arguments-BxQMXARO.mjs +13 -0
  13. package/dist/{find-B30-rd-u.mjs → find-BTikxc54.mjs} +1 -1
  14. package/dist/{flags-ByYBx-Xb.mjs → flags-C8yCq0j5.mjs} +7 -32
  15. package/dist/{follow-ups-Cxca1Jgg.mjs → follow-ups-CtlhHbwe.mjs} +87 -26
  16. package/dist/{format-C-nEZv-P.mjs → format-BB4E-T6x.mjs} +1 -1
  17. package/dist/{in-a-run-CZFzoT3R.mjs → in-a-run-CbnKjO7t.mjs} +2 -2
  18. package/dist/{install-CsqK1avz.mjs → install-CWcpX5g4.mjs} +1 -1
  19. package/dist/{limits-CXh052II.mjs → limits-VQqsBoQk.mjs} +1 -1
  20. package/dist/{mcp-B4wME9C4.mjs → mcp-wGfkMCVF.mjs} +6 -6
  21. package/dist/{once-BKn3pOtg.mjs → once-BHxSdZz_.mjs} +43 -7
  22. package/dist/{pause-B1grR1cl.mjs → pause-C_Wxh-KM.mjs} +6 -3
  23. package/dist/{print-Bd8MWVJk.mjs → print-DNYzq4Rr.mjs} +1 -1
  24. package/dist/{project-records-BOEVs69i.mjs → project-records-XY3ku95x.mjs} +4 -1
  25. package/dist/{relay-CeNiq3GT.mjs → relay-BA6tsnpc.mjs} +1 -1
  26. package/dist/{run-D6yU2rLf.mjs → run-Bvvg-fiF.mjs} +61 -19
  27. package/dist/{run-C0C54UiU.mjs → run-DBUAq-Uu.mjs} +29 -20
  28. package/dist/{send-DqgF58hb.mjs → send-nrKtak5F.mjs} +2 -2
  29. package/dist/{src-Ciy7i3se.mjs → src-8cOPOgrT.mjs} +155 -32
  30. package/dist/{src-n-u4TGev.mjs → src-CmEvHdju.mjs} +28 -28
  31. package/dist/{src-D2qpd-G7.mjs → src-D4JSymhK.mjs} +11 -2
  32. package/dist/{src-DP02RP9O.mjs → src-I_RyKu6_.mjs} +2 -2
  33. package/dist/{store-2KXzMCEN.mjs → store-CDpk8Wtz.mjs} +1 -1
  34. package/dist/ui/assets/app--FijEXiJ.css +1 -0
  35. package/dist/ui/assets/{app-ICEKpy_C.js → app-BowhjEAe.js} +12 -12
  36. package/dist/ui/assets/{index-BGaix5wh.js → index-CrMa2oHB.js} +2 -2
  37. package/dist/ui/index.html +1 -1
  38. package/dist/{ui-CAvygxTb.mjs → ui-DY3Dnpho.mjs} +4 -4
  39. package/dist/{usage-BFBco5OS.mjs → usage-1VwSg_Qt.mjs} +4 -4
  40. package/dist/{validate-UrmC5AG1.mjs → validate-BxayjjkV.mjs} +3 -3
  41. package/dist/{watch-DTxkgB8R.mjs → watch-CT0EtLPZ.mjs} +5 -5
  42. package/dist/{workflows-8GV-SeRm.mjs → workflows-DHHi-APg.mjs} +2 -2
  43. package/dist/{worktrees-CUdUvZlO.mjs → worktrees-CZvbHzbN.mjs} +4 -4
  44. package/package.json +1 -1
  45. package/dist/ui/assets/app-C5vO1M-l.css +0 -1
package/dist/bin.mjs CHANGED
@@ -1,26 +1,28 @@
1
1
  #!/usr/bin/env node
2
- import { Ai as driverChecks, Ar as applySteps, Fi as DRIVER_IDS, Fr as createAgent, G as listOrkWorktrees, Hn as nestItems, I as activeTree, In as runStatus, Ir as deleteAgent, Kn as loadProject, Kt as runListTable, Lr as AGENT_SOURCES, Mi as AGENT_TOOL_LAYOUTS, Mn as tellLiveRun, Mr as linkTools, Ni as ORK_LAYOUT, Nn as liveStatus, Nr as toolLinks, Pr as toolsInUse, Qt as planClean, R as openStore, Rr as catalogAgent, S as runningScript, T as isNewer, Ur as placeFiles, Ut as decisionListTable, Vr as inRepoPlace, W as createdWorktrees, Wn as PROJECT_FILE, Wr as placeFor, Wt as feedbackListTable, Yt as applyClean, _r as feedbackActions, ar as previewPost, at as answerDecision, b as releaseNotes, cr as FEEDBACK_KINDS, di as parseWorkflow, fi as serializeWorkflow, fr as planPost, gr as normalizeTitle, hi as WorkflowSchema, in as clearGitLocalEnv, ji as readyDriver, jn as requestStop, jr as planMoveInto, kr as planAgentMigration, lr as FEEDBACK_SEVERITIES, lt as detachResume, mr as sendPost, on as modelDriver, or as checkNote, ot as pendingDecisions, qr as shownPath, si as renameNodeTypes, sr as recordNote, ur as FEEDBACK_TARGETS, ut as resumable, vi as settingsOf, vr as openFixRun, x as runningRunIds, y as planUpgrade, zr as loadAgents } from "./src-Ciy7i3se.mjs";
3
- import { i as placeOf, n as acceptItem, t as runInstall } from "./install-CsqK1avz.mjs";
4
- import { _ as machineSlotLedger, a as onInterrupt, c as runWorkflowFile, g as sweepLocks, h as machineLocksDir, i as loadRuntime, m as resumeChat, n as flagValues, o as run, r as isDriver, s as terminalHuman, t as badDriver } from "./run-C0C54UiU.mjs";
2
+ import { $t as planClean, Ar as planAgentMigration, Br as loadAgents, Fr as toolsInUse, G as listOrkWorktrees, Gn as PROJECT_FILE, Gr as placeFor, Gt as feedbackListTable, Hr as inRepoPlace, I as activeTree, Ii as DRIVER_IDS, Ir as createAgent, Jr as shownPath, Ln as runStatus, Lr as deleteAgent, Mi as readyDriver, Mn as requestStop, Mr as planMoveInto, Ni as AGENT_TOOL_LAYOUTS, Nn as tellLiveRun, Nr as linkTools, Pi as ORK_LAYOUT, Pn as liveStatus, Pr as toolLinks, R as openStore, Rr as AGENT_SOURCES, S as runningScript, T as isNewer, Un as nestItems, W as createdWorktrees, Wr as placeFiles, Wt as decisionListTable, Xt as applyClean, _r as normalizeTitle, an as clearGitLocalEnv, at as followRunTree, b as releaseNotes, ci as renameNodeTypes, cr as recordNote, dr as FEEDBACK_TARGETS, dt as resumable, fi as parseWorkflow, gi as WorkflowSchema, hr as sendPost, it as followRuns, ji as driverChecks, jr as applySteps, lr as FEEDBACK_KINDS, or as previewPost, ot as answerDecision, pi as serializeWorkflow, pr as planPost, qn as loadProject, qt as runListTable, sn as modelDriver, sr as checkNote, st as pendingDecisions, ur as FEEDBACK_SEVERITIES, ut as detachResume, vr as feedbackActions, x as runningRunIds, y as planUpgrade, yi as settingsOf, yr as openFixRun, zr as catalogAgent } from "./src-8cOPOgrT.mjs";
3
+ import { i as placeOf, n as acceptItem, t as runInstall } from "./install-CWcpX5g4.mjs";
4
+ import { _ as machineSlotLedger, a as onInterrupt, c as runWorkflowFile, g as sweepLocks, h as machineLocksDir, i as loadRuntime, m as resumeChat, n as flagValues, o as run, r as isDriver, s as terminalHuman, t as badDriver } from "./run-DBUAq-Uu.mjs";
5
5
  import { t as applyFilters } from "./apply-Mb0fNpCX.mjs";
6
- import { n as builtinWorkflowFile, t as builtinProblem } from "./builtin-files-CWL3JEo-.mjs";
7
- import { p as scrubberOfRun } from "./src-DP02RP9O.mjs";
8
- import { t as findRun } from "./find-B30-rd-u.mjs";
9
- import { c as fetchLatest, d as readUpdateCache, h as nodeSession, i as registryOf, m as writeUpdateCache, n as ORK_VERSION, p as updateCachePath, t as ORK_NAME } from "./__build_info__-dPf4cEGL.mjs";
6
+ import { n as builtinWorkflowFile, t as builtinProblem } from "./builtin-files-Cf-NZDqM.mjs";
7
+ import { p as scrubberOfRun } from "./src-I_RyKu6_.mjs";
8
+ import { t as findRun } from "./find-BTikxc54.mjs";
9
+ import { c as fetchLatest, d as readUpdateCache, h as nodeSession, i as registryOf, m as writeUpdateCache, n as ORK_VERSION, p as updateCachePath, t as ORK_NAME } from "./__build_info__-D6mXC4cD.mjs";
10
10
  import { n as groupedEntries, t as groupOf } from "./help-groups-BpRgBSTh.mjs";
11
- import { a as resolved, i as publicArgs, n as argsOf, o as subcommandsOf } from "./command-tree-B6uvMJq1.mjs";
11
+ import { a as resolved, i as publicArgs, n as argsOf, o as subcommandsOf } from "./command-tree-CEkzWNy6.mjs";
12
12
  import { C as termOf, D as wrap, E as widthOf, S as takePlainFlag, T as hang, a as plain, b as canKey, c as write, h as paint, l as writeErr, m as bold, s as style, t as confirm, w as cut } from "./output-B1kwo0YU.mjs";
13
13
  import { n as chip, r as nodeChip, t as CHIP_LABELS } from "./chips-BqcT58qr.mjs";
14
- import { a as unknownNode, i as logListTable, l as createTerminalHuman, n as askStored, r as followRun, u as readText } from "./attach-DhZQ87Nz.mjs";
15
- import { c as projectAt, l as registeredProjects, n as WORKFLOW_SUFFIX, t as WORKFLOW_DIR } from "./workflows-8GV-SeRm.mjs";
16
- import { C as startUpdateCheck, S as showUpdateNotice, _ as runChecks, a as formatReport, c as checksHere, g as orkCheck, h as headWarning, m as experimentsCheck, n as validate, p as exec, r as validateFile, v as socketCheck, w as updateCheckOff, x as runUpdateChild, y as triggerChecks } from "./validate-UrmC5AG1.mjs";
17
- import { a as decideRefusal, r as chatRefusal } from "./in-a-run-CZFzoT3R.mjs";
18
- import { t as context } from "./context-Bw6R_EYq.mjs";
19
- import { i as readListArgs, n as filterArgs, o as resolveRun, r as projectScope, s as refuseUnknownFlags, t as fail$1 } from "./flags-ByYBx-Xb.mjs";
20
- import { i as listedStatus, n as formatRuns, r as formatTreeView, t as ago } from "./format-C-nEZv-P.mjs";
21
- import { t as printList } from "./print-Bd8MWVJk.mjs";
22
- import { a as formatRegistered, i as formatProjects, n as move, o as recordInRepo, r as withStore, s as registerProject, t as forget } from "./project-records-BOEVs69i.mjs";
23
- import { t as withStore$1 } from "./store-2KXzMCEN.mjs";
14
+ import { a as printerFor, c as logListTable, h as readText, i as printUnder, l as matcher, m as createTerminalHuman, n as createPrompts, o as createLogPrinter, r as askStored, s as followRun, u as unknownNode } from "./attach-CEGbRg1D.mjs";
15
+ import { c as projectAt, l as registeredProjects, n as WORKFLOW_SUFFIX, t as WORKFLOW_DIR } from "./workflows-DHHi-APg.mjs";
16
+ import { C as startUpdateCheck, S as showUpdateNotice, _ as runChecks, a as formatReport, c as checksHere, g as orkCheck, h as headWarning, m as experimentsCheck, n as validate, p as exec, r as validateFile, v as socketCheck, w as updateCheckOff, x as runUpdateChild, y as triggerChecks } from "./validate-BxayjjkV.mjs";
17
+ import { a as createView, o as styler } from "./verdict-CMvPCpWC.mjs";
18
+ import { a as decideRefusal, r as chatRefusal } from "./in-a-run-CbnKjO7t.mjs";
19
+ import { t as context } from "./context-CGPh12HV.mjs";
20
+ import { t as refuseExtraArguments } from "./extra-arguments-BxQMXARO.mjs";
21
+ import { c as refuseUnknownFlags, i as readListArgs, n as filterArgs, o as resolveRun, r as projectScope, s as rootOf, t as fail$1 } from "./flags-C8yCq0j5.mjs";
22
+ import { i as listedStatus, n as formatRuns, r as formatTreeView, t as ago } from "./format-BB4E-T6x.mjs";
23
+ import { t as printList } from "./print-DNYzq4Rr.mjs";
24
+ import { a as formatRegistered, i as formatProjects, n as move, o as recordInRepo, r as withStore, s as registerProject, t as forget } from "./project-records-XY3ku95x.mjs";
25
+ import { t as withStore$1 } from "./store-CDpk8Wtz.mjs";
24
26
  import path from "node:path";
25
27
  import { stringify } from "yaml";
26
28
  import { z } from "zod";
@@ -358,6 +360,97 @@ const noteRun = (store, cwd, args) => {
358
360
  * file names (by pattern only when the file is gone or no longer parses).
359
361
  */
360
362
  const noteOn = (store, run, ask) => recordNote(store, run, ask, "--", scrubberOfRun(run, store.projects()));
363
+ // src/runs/follow-decisions.ts
364
+ /**
365
+ * @file `ork decide --follow`: ask the open decisions of the live runs in scope
366
+ * on this terminal, then each one a run opens later, one prompt at a time
367
+ * (specs/cli.md). Runs are followed with core's `followRuns`, so a run
368
+ * started after this one, and a called run, are found; a decision answered
369
+ * elsewhere withdraws its prompt. Ctrl-C and `--until-idle` end it; no
370
+ * decision is closed by either.
371
+ */
372
+ const scopeOf$1 = ({ project, runId, workflow }) => ({
373
+ ...project === void 0 ? {} : { project },
374
+ ...runId === void 0 ? {} : { run: runId },
375
+ ...workflow === void 0 ? {} : { workflow }
376
+ });
377
+ /**
378
+ * Asks `open` (the decisions open now, in the store's order) and then each
379
+ * decision the runs in `filter` open, until `session.signal` aborts or, with
380
+ * `untilIdle`, nothing is live and nothing is being asked. Says why it stopped
381
+ * and returns.
382
+ */
383
+ const followDecisions = async (store, open, filter, session) => {
384
+ const stop = new AbortController();
385
+ session.signal?.addEventListener("abort", () => {
386
+ stop.abort();
387
+ });
388
+ const human = createTerminalHuman({
389
+ ...session.terminal,
390
+ interrupt: () => {
391
+ stop.abort();
392
+ }
393
+ });
394
+ const term = termOf(session.terminal.output, session.terminal.env);
395
+ const say = styler(term);
396
+ const prompts = createPrompts(store, human, {
397
+ by: "decide",
398
+ out: session.out,
399
+ term,
400
+ ...session.home === void 0 ? {} : { home: session.home }
401
+ });
402
+ stop.signal.addEventListener("abort", () => {
403
+ prompts.abort();
404
+ });
405
+ /**
406
+ * Every decision put to the person: one that was left (`q`) isn't asked
407
+ * again.
408
+ */
409
+ const asked = /* @__PURE__ */ new Set();
410
+ const workflowOf = (run) => store.lookup(run).find((r) => r.id === run)?.workflow;
411
+ const ask = (run, decision, path) => {
412
+ if (asked.has(decision)) return;
413
+ asked.add(decision);
414
+ const workflow = workflowOf(run);
415
+ prompts.ask({
416
+ run,
417
+ ...workflow === void 0 ? {} : { workflow },
418
+ ...path === void 0 ? {} : { path }
419
+ }, decision);
420
+ };
421
+ try {
422
+ for (const d of open) ask(d.runId, d.id);
423
+ let end;
424
+ for (;;) {
425
+ end = await followRuns(store, scopeOf$1(filter), ({ run, workflow, path, stored }) => {
426
+ const { event } = stored;
427
+ if (prompts.seen(event)) return;
428
+ if (event.type === "decision.opened" && (filter.runId === void 0 || run === filter.runId) && (filter.workflow === void 0 || workflow === filter.workflow) && pendingDecisions(store, { runId: run }).some((d) => d.id === event.decision)) ask(run, event.decision, path);
429
+ }, {
430
+ untilIdle: session.untilIdle === true,
431
+ ...session.home === void 0 ? {} : { home: session.home },
432
+ ...session.intervalMs === void 0 ? {} : { intervalMs: session.intervalMs },
433
+ signal: stop.signal
434
+ });
435
+ if (end === "aborted" || prompts.open() === 0) break;
436
+ await prompts.settled();
437
+ if (stop.signal.aborted) {
438
+ end = "aborted";
439
+ break;
440
+ }
441
+ }
442
+ prompts.abort();
443
+ await prompts.settled();
444
+ if (end === "idle") session.out(say("dim", "no live runs"));
445
+ else {
446
+ const left = (/* @__PURE__ */ new Set([...pendingDecisions(store, filter).map((d) => d.id), ...[...asked].filter((id) => store.decision(id)?.status === "open")])).size;
447
+ session.out(say("dim", `stopped following; ${left} decision(s) still open`));
448
+ }
449
+ return end;
450
+ } finally {
451
+ human.close();
452
+ }
453
+ };
361
454
  // src/commands/decide-list.ts
362
455
  /**
363
456
  * @file The list `ork decide --list` prints: one line per open decision, and
@@ -426,7 +519,8 @@ const selectDecisions = (store, args) => {
426
519
  const pending = pendingDecisions(store, filter);
427
520
  return {
428
521
  open: request.limit === void 0 ? pending : pending.slice(0, request.limit),
429
- workflow
522
+ workflow,
523
+ filter
430
524
  };
431
525
  };
432
526
  /** Prints `open` as `--ids`, `--json` or the lines of `formatDecisions`. */
@@ -445,6 +539,95 @@ const decisionHint = (store, arg) => {
445
539
  if (decision === void 0 || decision.status !== "open") return void 0;
446
540
  return `${arg} is a decision id, not a run: answer it with \`ork decide --id ${arg} --choose ${decision.recommended ?? decision.options[0]?.id ?? "<option>"}\``;
447
541
  };
542
+ /** The flags that list or answer one decision, which `--follow` can't take. */
543
+ const NOT_WITH_FOLLOW = [
544
+ "list",
545
+ "json",
546
+ "ids",
547
+ "limit",
548
+ "id",
549
+ "choose",
550
+ "document",
551
+ "say",
552
+ "note",
553
+ "rating"
554
+ ];
555
+ /** Why the flags can't follow decisions, or `undefined` when they can. */
556
+ const followRefusal = (args) => {
557
+ if (args["follow"] !== true) return args["until-idle"] === true ? "--until-idle goes with --follow." : void 0;
558
+ const given = NOT_WITH_FOLLOW.filter((name) => args[name] !== void 0 && args[name] !== false);
559
+ return given.length === 0 ? void 0 : `--follow asks decisions as they open; it can't go with ${given.map((name) => `--${name}`).join(", ")}.`;
560
+ };
561
+ // src/commands/decide-ask.ts
562
+ /**
563
+ * @file `ork decide` answering on a terminal: the open decisions one at a time
564
+ * (`askOpen`), or those and every one a live run opens later (`--follow`,
565
+ * `followCommand`), with the line a plain terminal gets after an answer.
566
+ */
567
+ /** What the answer said, as stored (masked), after the answer's line. */
568
+ const saidLine = (store, id) => {
569
+ const note = store.decision(id)?.note;
570
+ return note === void 0 ? "" : style("dim", ` · said: ${note}`);
571
+ };
572
+ /** The workflow a card names for a run, when the store knows it. */
573
+ const workflowOf = (store, run) => {
574
+ const workflow = store.lookup(run).find((r) => r.id === run)?.workflow;
575
+ return workflow === void 0 ? {} : { workflow };
576
+ };
577
+ /** Asks `open` on `human`, one question at a time, in order. */
578
+ const askOpen = async (store, human, open) => {
579
+ const collapses = canKey(process.stdin, process.stdout);
580
+ for (const [index, d] of open.entries()) {
581
+ const asked = await askStored(store, human, d, {
582
+ by: "decide",
583
+ place: {
584
+ run: d.runId,
585
+ ...workflowOf(store, d.runId),
586
+ decision: d.id,
587
+ index: index + 1,
588
+ total: open.length
589
+ }
590
+ });
591
+ if (asked.kind === "refused") write(`${style("yellow", "!")} ${asked.message}\n`);
592
+ else if (asked.kind === "answered" && !collapses) write(`${style("green", "✓")} ${d.id}: ${asked.choice}${saidLine(store, d.id)}\n`);
593
+ }
594
+ };
595
+ /** `ork decide --follow`: the person at this terminal answers as decisions open. */
596
+ const followCommand = async (args) => {
597
+ const refusal = decideRefusal();
598
+ if (refusal !== null) {
599
+ fail$1(refusal);
600
+ return;
601
+ }
602
+ if (!process.stdin.isTTY) {
603
+ fail$1("--follow needs a terminal to answer on; `ork decide --list` shows what is open.");
604
+ return;
605
+ }
606
+ const store = openStore();
607
+ const stop = new AbortController();
608
+ const onSigint = () => {
609
+ stop.abort();
610
+ };
611
+ try {
612
+ const selected = selectDecisions(store, args);
613
+ if (selected === void 0) return;
614
+ process.once("SIGINT", onSigint);
615
+ await followDecisions(store, selected.open, selected.filter, {
616
+ out: write,
617
+ signal: stop.signal,
618
+ untilIdle: args["until-idle"] === true,
619
+ terminal: {
620
+ input: process.stdin,
621
+ output: process.stdout,
622
+ interactive: true,
623
+ verbose: args["verbose"] === true
624
+ }
625
+ });
626
+ } finally {
627
+ process.off("SIGINT", onSigint);
628
+ store.close();
629
+ }
630
+ };
448
631
  // src/commands/decide.ts
449
632
  /**
450
633
  * @file `ork decide`: answer a live run's open decisions from any terminal (it
@@ -485,11 +668,6 @@ const noteProblem = (note, rating) => {
485
668
  }, "--");
486
669
  return checked.ok ? void 0 : checked.error;
487
670
  };
488
- /** What the answer said, as stored (masked), after the answer's line. */
489
- const saidLine = (store, id) => {
490
- const note = store.decision(id)?.note;
491
- return note === void 0 ? "" : style("dim", ` · said: ${note}`);
492
- };
493
671
  const decide = defineCommand({
494
672
  meta: {
495
673
  name: "decide",
@@ -532,9 +710,29 @@ const decide = defineCommand({
532
710
  type: "boolean",
533
711
  description: "Show each question's whole context, not its first lines",
534
712
  default: false
713
+ },
714
+ follow: {
715
+ type: "boolean",
716
+ alias: ["watch"],
717
+ description: "Ask the open decisions, then each new one of any live run (Ctrl-C stops)",
718
+ default: false
719
+ },
720
+ "until-idle": {
721
+ type: "boolean",
722
+ description: "With --follow, stop once no live run is left",
723
+ default: false
535
724
  }
536
725
  },
537
726
  run: async ({ args }) => {
727
+ const conflict = followRefusal(args);
728
+ if (conflict !== void 0) {
729
+ failed$2(conflict);
730
+ return;
731
+ }
732
+ if (args.follow) {
733
+ await followCommand(args);
734
+ return;
735
+ }
538
736
  const listing = asksForList(args);
539
737
  const refusal = !args.document && (args.id !== void 0 || args.choose !== void 0 || process.stdin.isTTY && !listing) ? decideRefusal() : null;
540
738
  if (refusal !== null) {
@@ -596,20 +794,7 @@ const decide = defineCommand({
596
794
  printDecisions(open, args, workflow);
597
795
  return;
598
796
  }
599
- const collapses = canKey(process.stdin, process.stdout);
600
- for (const [index, d] of open.entries()) {
601
- const asked = await askStored(store, human, d, {
602
- by: "decide",
603
- place: {
604
- run: d.runId,
605
- decision: d.id,
606
- index: index + 1,
607
- total: open.length
608
- }
609
- });
610
- if (asked.kind === "refused") write(`${style("yellow", "!")} ${asked.message}\n`);
611
- else if (asked.kind === "answered" && !collapses) write(`${style("green", "✓")} ${d.id}: ${asked.choice}${saidLine(store, d.id)}\n`);
612
- }
797
+ await askOpen(store, human, open);
613
798
  } finally {
614
799
  human?.close();
615
800
  store.close();
@@ -634,6 +819,7 @@ const stop = defineCommand({
634
819
  name: "stop",
635
820
  description: "Stop a live run, or one of its nodes"
636
821
  },
822
+ plugins: [refuseExtraArguments],
637
823
  args: {
638
824
  run: {
639
825
  type: "positional",
@@ -683,6 +869,7 @@ const resume = defineCommand({
683
869
  name: "resume",
684
870
  description: "Continue a run that didn't succeed (stopped, failed or crashed): finished nodes keep their results"
685
871
  },
872
+ plugins: [refuseExtraArguments],
686
873
  args: {
687
874
  run: {
688
875
  type: "positional",
@@ -822,7 +1009,7 @@ const formatCheck = (result, term = termOf(process.stdout)) => {
822
1009
  * folder. Says nothing when there were none. `bin` is the `cmd` program.
823
1010
  */
824
1011
  const leaseChecks = async (place, env = process.env, bin = "cmd") => {
825
- const { cleanStaleLeases, leaseLedger } = await import("./src-D2qpd-G7.mjs");
1012
+ const { cleanStaleLeases, leaseLedger } = await import("./src-D4JSymhK.mjs");
826
1013
  const { cleaned, failed } = await cleanStaleLeases({
827
1014
  bin,
828
1015
  ledger: leaseLedger(placeFiles(place).runs),
@@ -1935,12 +2122,57 @@ const triageSummary = ({ merged, declined, triaged }) => {
1935
2122
  `${String(triaged)} new item(s) triaged`
1936
2123
  ].join("; ");
1937
2124
  };
2125
+ // src/commands/feedback-resolve.ts
2126
+ /**
2127
+ * @file `ork feedback resolve`: marks items resolved that were fixed by hand,
2128
+ * outside `ork feedback apply`, so they leave the list (specs/feedback.md).
2129
+ * Takes several ids, space or comma separated, and changes none when one is
2130
+ * unknown or can't be resolved (dismissed, or resolved or published already).
2131
+ * An item seen again after it was resolved opens again.
2132
+ */
2133
+ const resolve = defineCommand({
2134
+ meta: {
2135
+ name: "resolve",
2136
+ description: "Mark items fixed by hand resolved: they leave the list, and open again if they recur"
2137
+ },
2138
+ args: { item: {
2139
+ type: "positional",
2140
+ description: "A feedback item's id, or a prefix of one; several allowed, space or comma separated",
2141
+ required: true
2142
+ } },
2143
+ run: ({ args }) => withStore$1((store) => {
2144
+ const named = [...new Set(args._.flatMap((word) => word.split(",")).map((word) => word.trim()).filter((word) => word !== ""))];
2145
+ const items = /* @__PURE__ */ new Map();
2146
+ const refusals = [];
2147
+ for (const prefix of named) {
2148
+ const found = findFeedbackItem(store, prefix);
2149
+ if (!found.ok) {
2150
+ refusals.push(found.error);
2151
+ continue;
2152
+ }
2153
+ const allowed = feedbackActions(found.item).resolve;
2154
+ if (allowed.ok) items.set(found.item.id, found.item);
2155
+ else refusals.push(allowed.error);
2156
+ }
2157
+ if (refusals.length > 0 || items.size === 0) {
2158
+ for (const refusal of refusals) writeErr(refusal);
2159
+ if (items.size === 0 && refusals.length === 0) writeErr("Give a feedback item's id.");
2160
+ process.exitCode = 1;
2161
+ return;
2162
+ }
2163
+ for (const item of items.values()) {
2164
+ store.setFeedbackStatus(item.id, "resolved");
2165
+ write(`${style("green", "✓")} resolved ${item.id.slice(0, 12)}: ${item.title}`);
2166
+ }
2167
+ })
2168
+ });
1938
2169
  // src/commands/feedback-loop.ts
1939
2170
  /**
1940
2171
  * @file The `ork feedback` subcommands that close the loop (ADR-059): `apply`
1941
2172
  * runs Ork's own workflow to propose a fix for an item, `triage` merges items
1942
2173
  * about the same problem by rules after asking, and `split` takes a merge
1943
- * apart.
2174
+ * apart. `resolve` (marking items fixed by hand) is passed on from its own
2175
+ * file.
1944
2176
  */
1945
2177
  const itemArg$1 = {
1946
2178
  type: "positional",
@@ -2276,10 +2508,10 @@ const importFeedback = defineCommand({
2276
2508
  /**
2277
2509
  * @file `ork feedback`: the feedback every run recorded, grouped into items
2278
2510
  * (specs/feedback.md): list and show them, note a run, dismiss and restore an
2279
- * item, and accept one, as a lesson its workflow reads from then on. What
2280
- * closes the loop further (apply, triage, split) is in `feedback-loop.ts`,
2281
- * and what moves feedback off the machine (publish, export, import) in
2282
- * `feedback-share.ts`.
2511
+ * item, accept one, as a lesson its workflow reads from then on, and resolve
2512
+ * ones fixed by hand (`feedback-resolve.ts`). What closes the loop further
2513
+ * (apply, triage, split) is in `feedback-loop.ts`, and what moves feedback
2514
+ * off the machine (publish, export, import) in `feedback-share.ts`.
2283
2515
  */
2284
2516
  /** Runs `body` on the item `prefix` names, or says why there's none. */
2285
2517
  const withItem = (prefix, body) => withStore$1((store) => {
@@ -2299,7 +2531,7 @@ const itemArg = {
2299
2531
  const feedback = defineCommand({
2300
2532
  meta: {
2301
2533
  name: "feedback",
2302
- description: "What got in the way in your runs, grouped: list, show, note, dismiss, restore, accept, apply, triage, split, publish, export, import"
2534
+ description: "What got in the way in your runs, grouped: list, show, note, dismiss, restore, accept, resolve, apply, triage, split, publish, export, import"
2303
2535
  },
2304
2536
  subCommands: {
2305
2537
  list,
@@ -2308,6 +2540,7 @@ const feedback = defineCommand({
2308
2540
  name: "show",
2309
2541
  description: "Show an item and its records"
2310
2542
  },
2543
+ plugins: [refuseExtraArguments],
2311
2544
  args: {
2312
2545
  item: itemArg,
2313
2546
  json: {
@@ -2333,6 +2566,7 @@ const feedback = defineCommand({
2333
2566
  name: "note",
2334
2567
  description: "Note how a run or one of its nodes went; it joins the feedback"
2335
2568
  },
2569
+ plugins: [refuseExtraArguments],
2336
2570
  args: {
2337
2571
  run: {
2338
2572
  type: "positional",
@@ -2381,6 +2615,7 @@ const feedback = defineCommand({
2381
2615
  name: "accept",
2382
2616
  description: "Accept an item; with --lesson, every later session of its workflow reads it"
2383
2617
  },
2618
+ plugins: [refuseExtraArguments],
2384
2619
  args: {
2385
2620
  item: itemArg,
2386
2621
  lesson: {
@@ -2414,17 +2649,31 @@ const feedback = defineCommand({
2414
2649
  for (const line of rest) write(` ${line}`);
2415
2650
  })
2416
2651
  }),
2417
- apply,
2652
+ resolve,
2653
+ apply: {
2654
+ ...apply,
2655
+ plugins: [refuseExtraArguments]
2656
+ },
2418
2657
  triage,
2419
- split,
2420
- publish,
2658
+ split: {
2659
+ ...split,
2660
+ plugins: [refuseExtraArguments]
2661
+ },
2662
+ publish: {
2663
+ ...publish,
2664
+ plugins: [refuseExtraArguments]
2665
+ },
2421
2666
  export: exportFeedback,
2422
- import: importFeedback,
2667
+ import: {
2668
+ ...importFeedback,
2669
+ plugins: [refuseExtraArguments]
2670
+ },
2423
2671
  dismiss: defineCommand({
2424
2672
  meta: {
2425
2673
  name: "dismiss",
2426
2674
  description: "Dismiss an item: it leaves the list, and stays dismissed"
2427
2675
  },
2676
+ plugins: [refuseExtraArguments],
2428
2677
  args: { item: itemArg },
2429
2678
  run: ({ args }) => withItem(args.item, (store, item) => {
2430
2679
  store.setFeedbackStatus(item.id, "dismissed");
@@ -2436,6 +2685,7 @@ const feedback = defineCommand({
2436
2685
  name: "restore",
2437
2686
  description: "Bring a dismissed item back to the list, as new"
2438
2687
  },
2688
+ plugins: [refuseExtraArguments],
2439
2689
  args: { item: itemArg },
2440
2690
  run: ({ args }) => withItem(args.item, (store, item) => {
2441
2691
  store.setFeedbackStatus(item.id, "new");
@@ -2444,57 +2694,144 @@ const feedback = defineCommand({
2444
2694
  })
2445
2695
  }
2446
2696
  });
2447
- // src/commands/runs.ts
2697
+ // src/runs/logs-many.ts
2448
2698
  /**
2449
- * @file `ork ps`, `ork logs` and `ork tree`: read what every project's runs
2450
- * recorded in `~/.ork/state.db` (specs/event-store.md).
2699
+ * @file `ork logs` over several runs: a run with the runs it called inline
2700
+ * under their call paths, and every live run of a project interleaved behind
2701
+ * a `<id> <workflow> › ` prefix (`logs -f --all`; specs/cli.md,
2702
+ * specs/scheduler/calls.md).
2451
2703
  */
2452
- /** How many runs `ork ps` shows without `--limit`. */
2453
- const PS_LIMIT = 20;
2454
- /** The project root a scope other than `all` names. */
2455
- const rootOf = (scope, projects) => scope === "here" ? placeFor(process.cwd(), projects).root : scope.root;
2456
- const ps = defineCommand({
2457
- meta: {
2458
- name: "ps",
2459
- description: "List this project's runs, newest first"
2460
- },
2461
- plugins: [refuseUnknownFlags],
2462
- args: { ...filterArgs(runListTable, { extra: { limit: {
2463
- type: "string",
2464
- description: `Show at most this many, a positive whole number (default ${String(PS_LIMIT)})`
2465
- } } }) },
2466
- run: ({ args }) => withStore$1((store) => {
2467
- const req = readListArgs(runListTable, args);
2468
- if (req === void 0) return;
2469
- const scope = projectScope(req.filter, store.projects());
2470
- if (scope === void 0) return;
2471
- const stored = store.runs(scope === "all" ? {} : { project: rootOf(scope, store.projects()) });
2472
- const rows = req.filter["status"] === void 0 ? stored : stored.map((run) => run.status === "running" ? {
2473
- ...run,
2474
- listed: runStatus(run, store.events(run.id).map((s) => s.event))
2475
- } : run);
2476
- const shown = applyFilters(runListTable, rows, req.filter).slice(0, req.limit ?? PS_LIMIT);
2477
- const byId = new Map(stored.map((run) => [run.id, run]));
2478
- const statusOf = (run) => listedStatus(run, run.status === "running" ? store.events(run.id).map((s) => s.event) : []);
2479
- printList(shown, runListTable, {
2480
- ids: args["ids"],
2481
- json: args["json"]
2482
- }, (runs) => {
2483
- for (const line of formatRuns(runs, scope === "all", Date.now(), statusOf)) write(line);
2484
- }, (runs) => runs.map((run) => {
2485
- const record = byId.get(run.id) ?? run;
2486
- return {
2487
- ...record,
2488
- live: statusOf(record)
2489
- };
2490
- }));
2491
- })
2704
+ /**
2705
+ * The stored events of `roots` and of the runs they called, at any depth, in
2706
+ * the order they print: a called run's right after its `call.started`.
2707
+ */
2708
+ const treeEvents = (store, roots) => {
2709
+ const seen = new Set(roots);
2710
+ const walk = (id, into) => {
2711
+ for (const stored of store.events(id)) {
2712
+ into.push(stored);
2713
+ const { event } = stored;
2714
+ if (event.type === "call.started" && !seen.has(event.run)) {
2715
+ seen.add(event.run);
2716
+ walk(event.run, into);
2717
+ }
2718
+ }
2719
+ return into;
2720
+ };
2721
+ return roots.flatMap((id) => walk(id, []));
2722
+ };
2723
+ /** `run:seq` of an event, to name it in a set. */
2724
+ const keyOf = (stored) => `${stored.runId}:${stored.seq}`;
2725
+ /**
2726
+ * The stored events (in print order, over all of `roots`' runs) that `--limit`
2727
+ * cuts: all but the last `limit` that pass the filters. Events stored after
2728
+ * this read are not in it, so they always print.
2729
+ */
2730
+ const droppedBy = (store, roots, options) => {
2731
+ if (options.limit === void 0) return /* @__PURE__ */ new Set();
2732
+ const matches = matcher(options);
2733
+ const kept = treeEvents(store, roots).filter((stored) => matches(stored));
2734
+ return new Set(kept.slice(0, -options.limit).map((stored) => keyOf(stored)));
2735
+ };
2736
+ /** A run's printer: JSON lines as stored, or text laid out behind `path`. */
2737
+ const printerOf = (term, options, path) => options.json ? createLogPrinter({ json: true }) : printerFor(term, path, {
2738
+ ...options.verbose === void 0 ? {} : { verbose: options.verbose },
2739
+ ...options.node === void 0 ? {} : { node: options.node }
2740
+ });
2741
+ /** Prints a run's `lines` behind `path`'s gutter; JSON lines have none. */
2742
+ const show = (out, term, options, path, lines) => {
2743
+ if (options.json) for (const line of lines) out(line);
2744
+ else printUnder(out, term, path, lines);
2745
+ };
2746
+ /** What the clock-free options of a follower are, as `followRuns` takes them. */
2747
+ const pollOf = (options) => ({
2748
+ ...options.home === void 0 ? {} : { home: options.home },
2749
+ ...options.intervalMs === void 0 ? {} : { intervalMs: options.intervalMs },
2750
+ ...options.signal === void 0 ? {} : { signal: options.signal }
2492
2751
  });
2493
- const runArg = {
2752
+ /**
2753
+ * Whether `--node` names nothing in `root` or the runs it called (none of them
2754
+ * has such an event or declares it), as `unknownNode` says for one run.
2755
+ */
2756
+ const unknownCallNode = (store, root, node, live) => unknownNode(treeEvents(store, [root]), node, live);
2757
+ /**
2758
+ * Prints `root`'s events and those of the runs it called, each called run's
2759
+ * right after the `call.started` that started it, under its call path. With
2760
+ * `follow`, goes on until the root ends (or `signal` aborts), then names the
2761
+ * called runs still running.
2762
+ */
2763
+ const printCalls = async (store, root, out, options) => {
2764
+ const { term } = options.view ?? createView();
2765
+ const say = styler(term);
2766
+ const matches = matcher(options);
2767
+ const dropped = droppedBy(store, [root.id], options);
2768
+ const printers = /* @__PURE__ */ new Map();
2769
+ const { end, stillRunning } = await followRunTree(store, root.id, ({ run, path, stored }) => {
2770
+ let print = printers.get(run);
2771
+ if (print === void 0) {
2772
+ print = printerOf(term, options, path);
2773
+ printers.set(run, print);
2774
+ }
2775
+ const lines = print(stored);
2776
+ if (matches(stored) && !dropped.has(keyOf(stored))) show(out, term, options, path, lines);
2777
+ }, {
2778
+ follow: options.follow,
2779
+ ...pollOf(options)
2780
+ });
2781
+ if (end === "crashed") out(style("red", `run ${root.id} stopped without finishing (its process is gone)`));
2782
+ if (!options.follow || options.json) return;
2783
+ for (const id of stillRunning) out(say("dim", `↳ run ${id} still running · ork attach ${id}`));
2784
+ };
2785
+ /**
2786
+ * Prints the events of every live run in `scope` (a called run too, once), each
2787
+ * line behind `<4-char id> <workflow> › `: the stored events first, then new
2788
+ * ones, runs that start later included. Ends when `signal` aborts, or with
2789
+ * `untilIdle` once no run is live.
2790
+ */
2791
+ const printAll = async (store, scope, out, options) => {
2792
+ const { term } = options.view ?? createView();
2793
+ const matches = matcher(options);
2794
+ const { project } = scope;
2795
+ const live = store.runs(project === void 0 || project === "all" ? {} : { project }).filter((run) => liveStatus(run) === "running");
2796
+ const dropped = droppedBy(store, live.map((run) => run.id), options);
2797
+ const printers = /* @__PURE__ */ new Map();
2798
+ await followRuns(store, scope, ({ run, workflow, stored }) => {
2799
+ const path = [`${run.slice(0, 4)} ${workflow}`];
2800
+ let print = printers.get(run);
2801
+ if (print === void 0) {
2802
+ print = printerOf(term, options, path);
2803
+ printers.set(run, print);
2804
+ }
2805
+ const lines = print(stored);
2806
+ if (matches(stored) && !dropped.has(keyOf(stored))) show(out, term, options, path, lines);
2807
+ }, {
2808
+ ...options.untilIdle === true ? { untilIdle: true } : {},
2809
+ ...pollOf(options)
2810
+ });
2811
+ };
2812
+ // src/commands/logs.ts
2813
+ /**
2814
+ * @file `ork logs`: prints a run's transcript (with the runs it called inline),
2815
+ * or follows every live run of a project (`--all`), from `~/.ork/state.db`
2816
+ * (specs/event-store.md, specs/cli.md).
2817
+ */
2818
+ const runArg$1 = {
2494
2819
  type: "positional",
2495
2820
  description: "A run id or a prefix of one (default: this project's newest)",
2496
2821
  required: false
2497
2822
  };
2823
+ /** Why `ork logs`'s flags can't go together, or `null`. */
2824
+ const logsRefusal = (args) => {
2825
+ if (args.all) {
2826
+ if (!args.follow) return "--all needs -f.";
2827
+ if (args.run !== void 0) return "--all can't be used with a run.";
2828
+ if (args.calls !== void 0) return "--calls and --no-calls don't apply to --all (a called run prints as its own).";
2829
+ return null;
2830
+ }
2831
+ if (args.project !== void 0) return "--project needs --all.";
2832
+ if (args["until-idle"]) return "--until-idle needs --all.";
2833
+ return null;
2834
+ };
2498
2835
  const logs = defineCommand({
2499
2836
  meta: {
2500
2837
  name: "logs",
@@ -2502,7 +2839,7 @@ const logs = defineCommand({
2502
2839
  },
2503
2840
  plugins: [refuseUnknownFlags],
2504
2841
  args: {
2505
- run: runArg,
2842
+ run: runArg$1,
2506
2843
  ...filterArgs(logListTable, {
2507
2844
  ids: false,
2508
2845
  json: false,
@@ -2510,7 +2847,7 @@ const logs = defineCommand({
2510
2847
  }),
2511
2848
  limit: {
2512
2849
  type: "string",
2513
- description: "Print only the last this many stored events, a positive whole number (a live follow goes on past it)"
2850
+ description: "Print only the last this many stored events, a positive whole number, counted overall in print order, not per run (a live follow goes on past it)"
2514
2851
  },
2515
2852
  node: {
2516
2853
  type: "string",
@@ -2531,27 +2868,79 @@ const logs = defineCommand({
2531
2868
  type: "boolean",
2532
2869
  description: "Show each tool call whole, and why each failed node stopped, with its scrubbed details (always on with --node)",
2533
2870
  default: false
2871
+ },
2872
+ all: {
2873
+ type: "boolean",
2874
+ description: "With -f: every live run of the project, interleaved, each line behind its run id and workflow (a called run too, once; runs that start later join)",
2875
+ default: false
2876
+ },
2877
+ project: {
2878
+ type: "string",
2879
+ description: "With --all: a registered project (id or root), or all (default: this one)"
2880
+ },
2881
+ "until-idle": {
2882
+ type: "boolean",
2883
+ description: "With --all: end once no live run is left",
2884
+ default: false
2885
+ },
2886
+ calls: {
2887
+ type: "boolean",
2888
+ description: "Print the runs a run called inline under their call paths (default in text; --no-calls prints the run alone; --json needs --calls to include them)",
2889
+ negativeDescription: "Print the run alone, without the runs it called"
2534
2890
  }
2535
2891
  },
2536
2892
  run: ({ args }) => withStore$1(async (store) => {
2893
+ const refusal = logsRefusal(args);
2894
+ if (refusal !== null) {
2895
+ fail$1(refusal);
2896
+ return;
2897
+ }
2537
2898
  const req = readListArgs(logListTable, args);
2538
2899
  if (req === void 0) return;
2900
+ const abort = new AbortController();
2901
+ process.once("SIGINT", () => {
2902
+ abort.abort();
2903
+ });
2904
+ if (args.all) {
2905
+ const scope = projectScope(args.project === void 0 ? {} : { project: args.project }, store.projects());
2906
+ if (scope === void 0) return;
2907
+ await printAll(store, scope === "all" ? {} : { project: rootOf(scope, store.projects()) }, write, {
2908
+ json: args.json,
2909
+ verbose: args.verbose,
2910
+ filter: req.filter,
2911
+ ...req.limit === void 0 ? {} : { limit: req.limit },
2912
+ ...args.node === void 0 ? {} : { node: args.node },
2913
+ ...args["until-idle"] ? { untilIdle: true } : {},
2914
+ signal: abort.signal
2915
+ });
2916
+ return;
2917
+ }
2539
2918
  const found = findRun(store, args.run, process.cwd());
2540
2919
  if (!found.ok) {
2541
2920
  writeErr(found.error);
2542
2921
  process.exitCode = 1;
2543
2922
  return;
2544
2923
  }
2545
- const problem = args.node === void 0 ? null : unknownNode(store.events(found.run.id), args.node, args.follow && found.run.status === "running");
2924
+ const calls = args.calls ?? !args.json;
2925
+ const live = args.follow && found.run.status === "running";
2926
+ const problem = args.node === void 0 ? null : calls ? unknownCallNode(store, found.run.id, args.node, live) : unknownNode(store.events(found.run.id), args.node, live);
2546
2927
  if (problem !== null) {
2547
2928
  writeErr(problem);
2548
2929
  process.exitCode = 1;
2549
2930
  return;
2550
2931
  }
2551
- const abort = new AbortController();
2552
- process.once("SIGINT", () => {
2553
- abort.abort();
2554
- });
2932
+ if (calls) {
2933
+ await printCalls(store, found.run, write, {
2934
+ follow: args.follow,
2935
+ json: args.json,
2936
+ verbose: args.verbose,
2937
+ filter: req.filter,
2938
+ ...req.limit === void 0 ? {} : { limit: req.limit },
2939
+ signal: abort.signal,
2940
+ ...args.node === void 0 ? {} : { node: args.node }
2941
+ });
2942
+ return;
2943
+ }
2555
2944
  await followRun(store, found.run, write, {
2556
2945
  follow: args.follow,
2557
2946
  json: args.json,
@@ -2563,13 +2952,61 @@ const logs = defineCommand({
2563
2952
  });
2564
2953
  })
2565
2954
  });
2955
+ // src/commands/runs.ts
2956
+ /**
2957
+ * @file `ork ps`, `ork logs` and `ork tree`: read what every project's runs
2958
+ * recorded in `~/.ork/state.db` (specs/event-store.md).
2959
+ */
2960
+ /** How many runs `ork ps` shows without `--limit`. */
2961
+ const PS_LIMIT = 20;
2962
+ const ps = defineCommand({
2963
+ meta: {
2964
+ name: "ps",
2965
+ description: "List this project's runs, newest first"
2966
+ },
2967
+ plugins: [refuseUnknownFlags],
2968
+ args: { ...filterArgs(runListTable, { extra: { limit: {
2969
+ type: "string",
2970
+ description: `Show at most this many, a positive whole number (default ${String(PS_LIMIT)})`
2971
+ } } }) },
2972
+ run: ({ args }) => withStore$1((store) => {
2973
+ const req = readListArgs(runListTable, args);
2974
+ if (req === void 0) return;
2975
+ const scope = projectScope(req.filter, store.projects());
2976
+ if (scope === void 0) return;
2977
+ const stored = store.runs(scope === "all" ? {} : { project: rootOf(scope, store.projects()) });
2978
+ const rows = req.filter["status"] === void 0 ? stored : stored.map((run) => run.status === "running" ? {
2979
+ ...run,
2980
+ listed: runStatus(run, store.events(run.id).map((s) => s.event))
2981
+ } : run);
2982
+ const shown = applyFilters(runListTable, rows, req.filter).slice(0, req.limit ?? PS_LIMIT);
2983
+ const byId = new Map(stored.map((run) => [run.id, run]));
2984
+ const statusOf = (run) => listedStatus(run, run.status === "running" ? store.events(run.id).map((s) => s.event) : []);
2985
+ printList(shown, runListTable, {
2986
+ ids: args["ids"],
2987
+ json: args["json"]
2988
+ }, (runs) => {
2989
+ for (const line of formatRuns(runs, scope === "all", Date.now(), statusOf)) write(line);
2990
+ }, (runs) => runs.map((run) => {
2991
+ const record = byId.get(run.id) ?? run;
2992
+ return {
2993
+ ...record,
2994
+ live: statusOf(record)
2995
+ };
2996
+ }));
2997
+ })
2998
+ });
2566
2999
  const tree = defineCommand({
2567
3000
  meta: {
2568
3001
  name: "tree",
2569
3002
  description: "Show a run's ownership tree with status, tokens and cost. A delegated task shows its task label: the lead's short description, whitespace folded, clipped to 80 characters, secrets masked"
2570
3003
  },
2571
3004
  args: {
2572
- run: runArg,
3005
+ run: {
3006
+ type: "positional",
3007
+ description: "A run id or a prefix of one (default: this project's newest)",
3008
+ required: false
3009
+ },
2573
3010
  json: {
2574
3011
  type: "boolean",
2575
3012
  description: "Print JSON",
@@ -3035,31 +3472,31 @@ const main = defineCommand({
3035
3472
  run,
3036
3473
  ps,
3037
3474
  logs,
3038
- attach: () => import("./attach-BQl8nQz2.mjs").then((module) => module.attach),
3475
+ attach: () => import("./attach-C1OnxyMh.mjs").then((module) => module.attach),
3039
3476
  tree,
3040
- usage: () => import("./usage-BFBco5OS.mjs").then((module) => module.usage),
3041
- prune: () => import("./usage-BFBco5OS.mjs").then((module) => module.prune),
3477
+ usage: () => import("./usage-1VwSg_Qt.mjs").then((module) => module.usage),
3478
+ prune: () => import("./usage-1VwSg_Qt.mjs").then((module) => module.prune),
3042
3479
  decide,
3043
- relay: () => import("./relay-CeNiq3GT.mjs").then((module) => module.relay),
3044
- pause: () => import("./pause-B1grR1cl.mjs").then((module) => module.pause),
3045
- continue: () => import("./pause-B1grR1cl.mjs").then((module) => module.continueRun),
3480
+ relay: () => import("./relay-BA6tsnpc.mjs").then((module) => module.relay),
3481
+ pause: () => import("./pause-C_Wxh-KM.mjs").then((module) => module.pause),
3482
+ continue: () => import("./pause-C_Wxh-KM.mjs").then((module) => module.continueRun),
3046
3483
  stop,
3047
3484
  chat,
3048
- once: () => import("./once-BKn3pOtg.mjs").then((module) => module.once),
3049
- send: () => import("./send-DqgF58hb.mjs").then((module) => module.send),
3485
+ once: () => import("./once-BHxSdZz_.mjs").then((module) => module.once),
3486
+ send: () => import("./send-nrKtak5F.mjs").then((module) => module.send),
3050
3487
  resume,
3051
3488
  feedback,
3052
- "follow-ups": () => import("./follow-ups-Cxca1Jgg.mjs").then((module) => module.followUps),
3489
+ "follow-ups": () => import("./follow-ups-CtlhHbwe.mjs").then((module) => module.followUps),
3053
3490
  context,
3054
- worktrees: () => import("./worktrees-CUdUvZlO.mjs").then((module) => module.worktrees),
3055
- wt: () => import("./worktrees-CUdUvZlO.mjs").then((module) => module.worktrees),
3056
- watch: () => import("./watch-DTxkgB8R.mjs").then((module) => module.watch),
3057
- mcp: () => import("./mcp-B4wME9C4.mjs").then((module) => module.mcp),
3058
- ui: () => import("./ui-CAvygxTb.mjs").then((module) => module.ui),
3491
+ worktrees: () => import("./worktrees-CZvbHzbN.mjs").then((module) => module.worktrees),
3492
+ wt: () => import("./worktrees-CZvbHzbN.mjs").then((module) => module.worktrees),
3493
+ watch: () => import("./watch-CT0EtLPZ.mjs").then((module) => module.watch),
3494
+ mcp: () => import("./mcp-wGfkMCVF.mjs").then((module) => module.mcp),
3495
+ ui: () => import("./ui-DY3Dnpho.mjs").then((module) => module.ui),
3059
3496
  doctor,
3060
3497
  upgrade,
3061
- completion: () => import("./completion-DKPuD_Rw.mjs").then((module) => module.completion),
3062
- __complete: () => import("./completion-DKPuD_Rw.mjs").then((module) => module.completeCommand(main))
3498
+ completion: () => import("./completion-DS0gCZRm.mjs").then((module) => module.completion),
3499
+ __complete: () => import("./completion-DS0gCZRm.mjs").then((module) => module.completeCommand(main))
3063
3500
  }
3064
3501
  });
3065
3502
  // src/bin.ts
@@ -3081,7 +3518,7 @@ const main = defineCommand({
3081
3518
  clearGitLocalEnv();
3082
3519
  const argv = takePlainFlag(process.argv.slice(2));
3083
3520
  if (argv[0] === "__complete") {
3084
- const { printCompletions } = await import("./completion-DKPuD_Rw.mjs");
3521
+ const { printCompletions } = await import("./completion-DS0gCZRm.mjs");
3085
3522
  await printCompletions(main, argv.slice(1));
3086
3523
  } else if (process.env["ORK_UPDATE_CHECK_CHILD"] === "1") await runUpdateChild();
3087
3524
  else {
@@ -3094,7 +3531,7 @@ else {
3094
3531
  const dash = argv.indexOf("--");
3095
3532
  const askedHelp = argv.slice(1, dash === -1 ? 0 : dash).some((arg) => arg === "--help" || arg === "-h");
3096
3533
  if (argv[0] === "once" && dash !== -1 && !askedHelp) try {
3097
- const { once } = await import("./once-BKn3pOtg.mjs");
3534
+ const { once } = await import("./once-BHxSdZz_.mjs");
3098
3535
  await runCommand(once, { rawArgs: argv.slice(1) });
3099
3536
  } catch (error) {
3100
3537
  console.error(error);