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

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 (48) hide show
  1. package/dist/{__build_info__-C1Nm0hlJ.mjs → __build_info__-dPf4cEGL.mjs} +3 -134
  2. package/dist/{attach-EP374oMP.mjs → attach-BQl8nQz2.mjs} +5 -5
  3. package/dist/{attach-RQx__RzH.mjs → attach-DhZQ87Nz.mjs} +49 -224
  4. package/dist/bin.mjs +204 -154
  5. package/dist/{brief-Bs3cMQU0.mjs → brief-BQbeoPMc.mjs} +19 -9
  6. package/dist/{builtin-files-yYaNTQRU.mjs → builtin-files-CWL3JEo-.mjs} +1 -1
  7. package/dist/{command-tree-BkSYWmYJ.mjs → command-tree-B6uvMJq1.mjs} +1 -1
  8. package/dist/{completion-DIXWlnAl.mjs → completion-DKPuD_Rw.mjs} +47 -14
  9. package/dist/{context-Dq-VM_sq.mjs → context-Bw6R_EYq.mjs} +3 -3
  10. package/dist/detached-BQlbmqR0.mjs +38 -0
  11. package/dist/{find-k489laBa.mjs → find-B30-rd-u.mjs} +1 -1
  12. package/dist/flags-ByYBx-Xb.mjs +170 -0
  13. package/dist/{follow-ups-DD8NTPe6.mjs → follow-ups-Cxca1Jgg.mjs} +24 -18
  14. package/dist/{format-DQc9A_ei.mjs → format-C-nEZv-P.mjs} +27 -11
  15. package/dist/{help-groups-hB6kY34d.mjs → help-groups-BpRgBSTh.mjs} +13 -7
  16. package/dist/{in-a-run-D_9GlAlS.mjs → in-a-run-CZFzoT3R.mjs} +2 -2
  17. package/dist/{install-PVlV2QPq.mjs → install-CsqK1avz.mjs} +2 -183
  18. package/dist/{limits-CHqBO7wb.mjs → limits-CXh052II.mjs} +1 -1
  19. package/dist/{mcp-D38Ajb0b.mjs → mcp-B4wME9C4.mjs} +6 -6
  20. package/dist/{once-DuUzpzL2.mjs → once-BKn3pOtg.mjs} +3 -3
  21. package/dist/outputs-text-CNTJ7Qq_.mjs +19 -0
  22. package/dist/{pause-Bx3ePGrn.mjs → pause-B1grR1cl.mjs} +3 -3
  23. package/dist/{print-n2qetUHc.mjs → print-Bd8MWVJk.mjs} +1 -1
  24. package/dist/{project-records-Cune1l6I.mjs → project-records-BOEVs69i.mjs} +1 -1
  25. package/dist/{parse-BzWpMN-a.mjs → refusals-DQQX3AIb.mjs} +10 -1
  26. package/dist/{relay-D7RN4m68.mjs → relay-CeNiq3GT.mjs} +1 -1
  27. package/dist/{run-OOWQIKMJ.mjs → run-C0C54UiU.mjs} +116 -137
  28. package/dist/{run-BfxdsQJk.mjs → run-D6yU2rLf.mjs} +1 -1
  29. package/dist/{send-CKth_vgn.mjs → send-DqgF58hb.mjs} +2 -2
  30. package/dist/{src-DLsFsmOD.mjs → src-Ciy7i3se.mjs} +882 -71
  31. package/dist/{src-BHod38UQ.mjs → src-D2qpd-G7.mjs} +394 -247
  32. package/dist/{src-DmmrX2_C.mjs → src-DP02RP9O.mjs} +227 -180
  33. package/dist/{src-DOjTY2ET.mjs → src-n-u4TGev.mjs} +186 -43
  34. package/dist/{store-DACH768X.mjs → store-2KXzMCEN.mjs} +1 -1
  35. package/dist/ui/assets/app-C5vO1M-l.css +1 -0
  36. package/dist/ui/assets/{app-CN3acQ-1.js → app-ICEKpy_C.js} +21 -20
  37. package/dist/ui/assets/{index-B2uGxA4W.js → index-BGaix5wh.js} +2 -2
  38. package/dist/ui/index.html +1 -1
  39. package/dist/{ui-CJ-BX12n.mjs → ui-CAvygxTb.mjs} +53 -24
  40. package/dist/{usage-DxZ6JGd4.mjs → usage-BFBco5OS.mjs} +7 -7
  41. package/dist/{validate-BS0nCccW.mjs → validate-UrmC5AG1.mjs} +5 -5
  42. package/dist/{watch-Dp7IB4FR.mjs → watch-DTxkgB8R.mjs} +5 -5
  43. package/dist/{workflows-Djo1edlK.mjs → workflows-8GV-SeRm.mjs} +28 -4
  44. package/dist/{worktrees-Drof_Nyx.mjs → worktrees-CUdUvZlO.mjs} +9 -6
  45. package/package.json +1 -1
  46. package/dist/flags-B4ZZWspN.mjs +0 -125
  47. package/dist/post-2CaJz_mV.mjs +0 -46
  48. package/dist/ui/assets/app-9Q-62Ywz.css +0 -1
@@ -1,10 +1,10 @@
1
- import { An as liveStatus, Fr as placeFor, L as openStore, Pr as placeFiles, R as storePath, Ti as AGENT_DRIVERS, mi as NodeIdSchema, rt as pendingDecisions } from "./src-DLsFsmOD.mjs";
2
- import { a as isBuiltinArg, i as builtinWorkflowText, r as builtinWorkflowNames } from "./builtin-files-yYaNTQRU.mjs";
3
- import { t as findRun } from "./find-k489laBa.mjs";
4
- import { a as SHELL_SCRIPTS, i as SHELLS, n as groupedEntries, r as COMPLETE_COMMAND, t as groupOf } from "./help-groups-hB6kY34d.mjs";
5
- import { i as findEntry, n as argInfoOf, s as subcommandsOf } from "./command-tree-BkSYWmYJ.mjs";
1
+ import { Ci as NodeIdSchema, Nn as liveStatus, Pi as AGENT_DRIVERS, R as openStore, Ur as placeFiles, Wr as placeFor, ot as pendingDecisions, z as storePath } from "./src-Ciy7i3se.mjs";
2
+ import { a as isBuiltinArg, i as builtinWorkflowText, r as builtinWorkflowNames } from "./builtin-files-CWL3JEo-.mjs";
3
+ import { t as findRun } from "./find-B30-rd-u.mjs";
4
+ import { a as SHELL_SCRIPTS, i as SHELLS, n as groupedEntries, r as COMPLETE_COMMAND, t as groupOf } from "./help-groups-BpRgBSTh.mjs";
5
+ import { o as subcommandsOf, r as findEntry, t as argInfoOf } from "./command-tree-B6uvMJq1.mjs";
6
6
  import { l as writeErr } from "./output-B1kwo0YU.mjs";
7
- import { a as resolveWorkflowArg, n as WORKFLOW_SUFFIX } from "./workflows-Djo1edlK.mjs";
7
+ import { n as WORKFLOW_SUFFIX, o as resolveWorkflowArg } from "./workflows-8GV-SeRm.mjs";
8
8
  import path from "node:path";
9
9
  import { parse } from "yaml";
10
10
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
@@ -279,7 +279,8 @@ const providerFor = (providers, path, name) => providers[[...path, name].join("
279
279
  /**
280
280
  * @file What `ork __complete` offers at a position: subcommands (the root's in
281
281
  * `ork --help`'s groups), a command's public flags, an argument's values from
282
- * its provider, or an enum flag's values; filtered by what was typed, and
282
+ * its provider, or an enum flag's values (also when written `--flag=value`,
283
+ * each candidate then keeping `--flag=`); filtered by what was typed, and
283
284
  * printed one per line as `value<TAB>description`, with `:group <title>`
284
285
  * before each group and `:files` when file names fit too.
285
286
  */
@@ -365,10 +366,14 @@ const offerAt = (position, providers, context) => {
365
366
  /** What fits at `position`, keeping what starts with the typed word. */
366
367
  const offerFor = (position, providers, context) => {
367
368
  const offer = offerAt(position, providers, context);
369
+ const prefix = position.slot.kind === "value" ? position.slot.prefix ?? "" : "";
368
370
  return {
369
371
  sections: offer.sections.map((section) => ({
370
372
  ...section,
371
- candidates: section.candidates.filter((c) => c.value.startsWith(position.current))
373
+ candidates: section.candidates.filter((c) => c.value.startsWith(position.current)).map((c) => ({
374
+ value: `${prefix}${c.value}`,
375
+ description: c.description
376
+ }))
372
377
  })).filter((section) => section.candidates.length > 0),
373
378
  files: offer.files
374
379
  };
@@ -436,12 +441,26 @@ const step = async (walk, word) => {
436
441
  index: walk.index + 1
437
442
  };
438
443
  };
439
- const slotOf = (walk, current) => {
444
+ /** `--flag=` of a typed `--flag=text` whose flag takes a value, else nothing. */
445
+ const valuePrefixOf = (walk, typed) => {
446
+ if (walk.pending !== void 0 || walk.rest || !typed.startsWith("-")) return;
447
+ const at = typed.indexOf("=");
448
+ return (at < 0 ? void 0 : flagNamed(walk.flags, typed))?.takesValue === true ? typed.slice(0, at + 1) : void 0;
449
+ };
450
+ const slotOf = (walk, typed, prefix) => {
440
451
  if (walk.pending !== void 0) return {
441
452
  kind: "value",
442
453
  flag: walk.pending
443
454
  };
444
- if (!walk.rest && current.startsWith("-")) return { kind: "flag" };
455
+ if (prefix !== void 0) {
456
+ const flag = flagNamed(walk.flags, prefix);
457
+ if (flag !== void 0) return {
458
+ kind: "value",
459
+ flag,
460
+ prefix
461
+ };
462
+ }
463
+ if (!walk.rest && typed.startsWith("-")) return { kind: "flag" };
445
464
  const positional = walk.positionals[walk.index];
446
465
  return positional === void 0 ? { kind: "word" } : {
447
466
  kind: "word",
@@ -454,7 +473,7 @@ const slotOf = (walk, current) => {
454
473
  */
455
474
  const positionOf = async (root, words) => {
456
475
  const before = words.slice(0, -1);
457
- const current = words.at(-1) ?? "";
476
+ const typed = words.at(-1) ?? "";
458
477
  const start = {
459
478
  ...await argInfoOf(root),
460
479
  path: [],
@@ -465,10 +484,12 @@ const positionOf = async (root, words) => {
465
484
  };
466
485
  const walk = await before.reduce(async (previous, word) => step(await previous, word), Promise.resolve(start));
467
486
  const { pending: _pending, rest: _rest, ...reached } = walk;
487
+ const prefix = valuePrefixOf(walk, typed);
488
+ const current = prefix === void 0 ? typed : typed.slice(prefix.length);
468
489
  return {
469
490
  ...reached,
470
491
  current,
471
- slot: slotOf(walk, current)
492
+ slot: slotOf(walk, typed, prefix)
472
493
  };
473
494
  };
474
495
  // src/commands/completion.ts
@@ -495,12 +516,24 @@ const completionLines = async (root, words, cwd, storeFile) => {
495
516
  opened.store?.close();
496
517
  }
497
518
  };
519
+ const guard = (error) => {
520
+ if (error.code !== "EPIPE") throw error;
521
+ };
522
+ /**
523
+ * Writes `text` to stdout. A reader that quits early (`ork completion zsh |
524
+ * head -1`) closes the pipe: that EPIPE is ignored, any other error is not.
525
+ */
526
+ const writeOut = (text) => {
527
+ process.stdout.off("error", guard);
528
+ process.stdout.on("error", guard);
529
+ process.stdout.write(text);
530
+ };
498
531
  /** `ork __complete`'s whole job: print the candidates, or nothing. */
499
532
  const printCompletions = async (root, args, cwd = process.cwd()) => {
500
533
  try {
501
534
  const words = args[0] === "--" ? args.slice(1) : args;
502
535
  const lines = await completionLines(root, words, cwd);
503
- process.stdout.write(lines.map((line) => `${line}\n`).join(""));
536
+ writeOut(lines.map((line) => `${line}\n`).join(""));
504
537
  } catch {}
505
538
  };
506
539
  /** The hidden `ork __complete` of the command `root`. */
@@ -529,7 +562,7 @@ const completion = defineCommand({
529
562
  process.exitCode = 1;
530
563
  return;
531
564
  }
532
- process.stdout.write(script);
565
+ writeOut(script);
533
566
  }
534
567
  });
535
568
  export { completeCommand, completion, printCompletions };
@@ -1,7 +1,7 @@
1
- import { Hr as LESSON_CAP_TOKENS, gt as explainSession } from "./src-DLsFsmOD.mjs";
1
+ import { Zr as LESSON_CAP_TOKENS, yt as explainSession } from "./src-Ciy7i3se.mjs";
2
2
  import { c as write, s as style } from "./output-B1kwo0YU.mjs";
3
- import { i as refusedBuiltin, o as workflowArg } from "./workflows-Djo1edlK.mjs";
4
- import { a as formatReport, t as loadWorkflow } from "./validate-BS0nCccW.mjs";
3
+ import { a as refusedBuiltin, s as workflowArg } from "./workflows-8GV-SeRm.mjs";
4
+ import { a as formatReport, t as loadWorkflow } from "./validate-UrmC5AG1.mjs";
5
5
  import { defineCommand } from "citty";
6
6
  // src/commands/context.ts
7
7
  /**
@@ -0,0 +1,38 @@
1
+ import { ft as detachRun } from "./src-Ciy7i3se.mjs";
2
+ import { n as newRunId } from "./find-B30-rd-u.mjs";
3
+ import { c as write, i as detachHint, l as writeErr, s as style } from "./output-B1kwo0YU.mjs";
4
+ // src/run/detached.ts
5
+ /**
6
+ * @file `ork run --detach`: checks what the run would refuse (a `--follow-ups`
7
+ * that can't be taken) before any run folder is made, then starts the run in
8
+ * the background and says how to follow it.
9
+ */
10
+ /** Starts the run in the background; `rawArgs` are `ork run`'s own, as typed. */
11
+ const startDetached = async (store, options) => {
12
+ const { rawArgs, cwd, target } = options;
13
+ const { prepareFollowUps } = await import("./brief-BQbeoPMc.mjs");
14
+ if (await prepareFollowUps(store, rawArgs, void 0, target, void 0, false) === void 0) return;
15
+ const single = options.node !== void 0 && options.node !== "";
16
+ const runId = newRunId(store, single ? "node-" : "");
17
+ const detached = await detachRun({
18
+ store,
19
+ runId,
20
+ args: [
21
+ "run",
22
+ ...rawArgs.filter((arg) => arg !== "--detach"),
23
+ "--run-id",
24
+ runId
25
+ ],
26
+ cwd,
27
+ runDir: target.runDir(runId)
28
+ });
29
+ if (detached.ok) {
30
+ write(`▸ run ${detached.runId} started in the background`);
31
+ write(style("dim", ` ${detachHint(detached.runId)}`));
32
+ } else {
33
+ writeErr(detached.error);
34
+ writeErr(style("dim", `(output: ${detached.log})`));
35
+ process.exitCode = 1;
36
+ }
37
+ };
38
+ export { startDetached };
@@ -1,4 +1,4 @@
1
- import { Fr as placeFor } from "./src-DLsFsmOD.mjs";
1
+ import { Wr as placeFor } from "./src-Ciy7i3se.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  // ../core/src/remote/find.ts
4
4
  /**
@@ -0,0 +1,170 @@
1
+ import "./src-Ciy7i3se.mjs";
2
+ import { i as parseLimit, n as unknownProjectRefusal, r as parseFilters, t as limitRefusal } from "./refusals-DQQX3AIb.mjs";
3
+ import { t as findRun } from "./find-B30-rd-u.mjs";
4
+ import { a as resolved, n as argsOf } from "./command-tree-B6uvMJq1.mjs";
5
+ import { l as writeErr } from "./output-B1kwo0YU.mjs";
6
+ import { parseArgs } from "citty";
7
+ // src/list/known-flags.ts
8
+ /**
9
+ * @file The citty plugin that refuses a flag a list command does not have (`ork
10
+ * ps --bogus`, `ork decide --list --since 24h`). citty's parser is not
11
+ * strict: it puts an unknown `--since 24h` into the arguments as `since:
12
+ * true` plus a positional, which `decide` would read as a run id. The plugin
13
+ * runs before the command, on the nine list commands only (specs/cli/
14
+ * list-filters.md), and refuses with citty's own argument error, so the
15
+ * one-line message and exit code 1 are the same as for a wrong enum value.
16
+ */
17
+ /** Names citty reads without the command declaring them. */
18
+ const BUILT_IN = /* @__PURE__ */ new Set([
19
+ "_",
20
+ "help",
21
+ "h"
22
+ ]);
23
+ /** A flag's name without its spelling: `run-id`, `runId` and `run_id` agree. */
24
+ const spelling = (name) => name.replaceAll(/[-_]/gu, "").toLowerCase();
25
+ /**
26
+ * An error citty's `runMain` takes for an argument error (its `CLIError`, which
27
+ * it does not export): a failed parse gives one, and it takes `message`.
28
+ */
29
+ const argumentError = (message) => {
30
+ try {
31
+ parseArgs([], { x: {
32
+ type: "string",
33
+ required: true
34
+ } });
35
+ } catch (error) {
36
+ if (error instanceof Error) {
37
+ error.message = message;
38
+ return error;
39
+ }
40
+ }
41
+ return new Error(message);
42
+ };
43
+ /** How the flag is typed: `--since`, or `-x` for a one-letter name. */
44
+ const written = (name) => name.length === 1 ? `-${name}` : `--${name}`;
45
+ /** Refuses the first flag the command does not declare. */
46
+ const refuseUnknownFlags = {
47
+ name: "refuse-unknown-flags",
48
+ setup: async ({ cmd, rawArgs, args }) => {
49
+ const [first] = rawArgs;
50
+ const subs = await resolved(cmd.subCommands ?? {});
51
+ if (first !== void 0 && first in subs) return;
52
+ const known = /* @__PURE__ */ new Set();
53
+ for (const [name, def] of Object.entries(await argsOf(cmd))) {
54
+ known.add(spelling(name));
55
+ if ("alias" in def && def.alias !== void 0) for (const alias of [def.alias].flat()) known.add(spelling(alias));
56
+ }
57
+ const unknown = Object.keys(args).find((key) => !BUILT_IN.has(key) && !known.has(spelling(key)));
58
+ if (unknown !== void 0) throw argumentError(`Unknown flag \`${written(unknown)}\`.`);
59
+ }
60
+ };
61
+ // src/list/flags.ts
62
+ /**
63
+ * @file The flags every list command shares (RFC-017), made from its field
64
+ * table: one flag per field (an enum field's values as citty `options`, so
65
+ * `--help` and Tab completion read them), `--ids`, `--limit` and `--json`;
66
+ * and reading them back: each value checked by core's `parseFilters`,
67
+ * `--limit` a positive whole number, `--project` a registered one. A refusal
68
+ * says why on stderr and sets the exit code; the command then returns.
69
+ */
70
+ const fieldArg = (field) => {
71
+ if (field.type !== "enum") return {
72
+ type: "string",
73
+ description: field.description
74
+ };
75
+ return {
76
+ type: "enum",
77
+ options: [...field.values ?? [], ...field.all === true ? ["all"] : []],
78
+ description: field.description
79
+ };
80
+ };
81
+ /** The citty flags of `table`, then `--ids`, `--limit`, `--json`, `extra`. */
82
+ const filterArgs = (table, options = {}) => ({
83
+ ...Object.fromEntries(table.fields.map((field) => [field.name, fieldArg(field)])),
84
+ ...options.ids ?? table.id !== void 0 ? { ids: {
85
+ type: "boolean",
86
+ description: "Print only the ids, comma-separated, on one line",
87
+ default: false
88
+ } } : {},
89
+ ...options.limit ?? true ? { limit: {
90
+ type: "string",
91
+ description: "Show at most this many (a positive whole number)"
92
+ } } : {},
93
+ ...options.json ?? true ? { json: {
94
+ type: "boolean",
95
+ description: "Print JSON",
96
+ default: false
97
+ } } : {},
98
+ ...options.extra
99
+ });
100
+ /** Says why, and makes the command exit 1. */
101
+ const fail = (message) => {
102
+ writeErr(message);
103
+ process.exitCode = 1;
104
+ };
105
+ /** The `--ids` with `--json` refusal; `true` when `args` has both. */
106
+ const refuseIdsWithJson = (args) => {
107
+ if (args.ids !== true || args.json !== true) return false;
108
+ fail("--ids and --json can't be used together.");
109
+ return true;
110
+ };
111
+ /**
112
+ * `args` (a command's parsed flags) read against `table`: the filter, and the
113
+ * limit. `undefined` when something was refused (already said, exit code set).
114
+ */
115
+ const readListArgs = (table, args, options = {}) => {
116
+ if (refuseIdsWithJson(args)) return void 0;
117
+ const parsed = parseFilters(table, args, options.now ?? Date.now());
118
+ if (!parsed.ok) {
119
+ fail(parsed.error);
120
+ return;
121
+ }
122
+ const text = args["limit"];
123
+ if (typeof text !== "string") return {
124
+ filter: parsed.filter,
125
+ limit: void 0
126
+ };
127
+ const limit = parseLimit(text);
128
+ if (limit === null) {
129
+ fail(limitRefusal(text));
130
+ return;
131
+ }
132
+ return {
133
+ filter: parsed.filter,
134
+ limit
135
+ };
136
+ };
137
+ /**
138
+ * The scope the filter's `project` names, checked against the registered
139
+ * projects (core does not know them): an id or a root, or `all`; none given is
140
+ * `here`. A project not registered is refused, listing the registered ones
141
+ * (said, exit code set, `undefined`).
142
+ */
143
+ const projectScope = (filter, projects) => {
144
+ const key = filter["project"];
145
+ if (key === void 0) return "here";
146
+ if (key === "all") return "all";
147
+ const found = projects.find((p) => p.id === key || p.root === key);
148
+ if (found !== void 0) return { root: found.root };
149
+ fail(unknownProjectRefusal(key, projects.map((p) => p.id)));
150
+ };
151
+ /**
152
+ * `filter` with its `run` (a prefix) made the full id of the run it names, as
153
+ * `ork decide --run` reads it; an unknown or ambiguous run is refused with
154
+ * `findRun`'s message (said, exit code set, `undefined`). No `run` (or an empty
155
+ * one) leaves `filter` as it is.
156
+ */
157
+ const resolveRun = (store, filter) => {
158
+ const run = filter["run"];
159
+ if (run === void 0 || run === "") return filter;
160
+ const found = findRun(store, run, process.cwd());
161
+ if (!found.ok) {
162
+ fail(found.error);
163
+ return;
164
+ }
165
+ return {
166
+ ...filter,
167
+ run: found.run.id
168
+ };
169
+ };
170
+ export { refuseIdsWithJson as a, readListArgs as i, filterArgs as n, resolveRun as o, projectScope as r, refuseUnknownFlags as s, fail as t };
@@ -1,13 +1,12 @@
1
- import { Fr as placeFor, Ht as followUpListTable, hn as recordPublished, mn as markDue, nr as runGh, vn as deliverFollowUp, yn as followUpProblem } from "./src-DLsFsmOD.mjs";
2
- import { d as followUpsRule, f as refreshDue, n as flagValues } from "./run-OOWQIKMJ.mjs";
3
- import { g as changeFollowUp, v as findFollowUp, x as isGithubIssueUrl, y as followUpActions } from "./__build_info__-C1Nm0hlJ.mjs";
4
- import { t as postFollowUpMessage } from "./post-2CaJz_mV.mjs";
1
+ import { Gt as followUpListTable, Jn as changeFollowUp, Qn as isGithubIssueUrl, Sn as followUpProblem, Wr as placeFor, Xn as findFollowUp, Zn as followUpActions, gn as postFollowUpMessage, pr as runGh, vn as markDue, xn as deliverFollowUp, yn as recordPublished } from "./src-Ciy7i3se.mjs";
2
+ import { d as followUpsRule, f as refreshDue, n as flagValues } from "./run-C0C54UiU.mjs";
5
3
  import { t as applyFilters } from "./apply-Mb0fNpCX.mjs";
6
- import { t as findRun } from "./find-k489laBa.mjs";
4
+ import { n as unknownProjectRefusal } from "./refusals-DQQX3AIb.mjs";
5
+ import { t as findRun } from "./find-B30-rd-u.mjs";
7
6
  import { a as plain, c as write, l as writeErr, s as style } from "./output-B1kwo0YU.mjs";
8
- import { i as readListArgs, n as filterArgs, o as allAlias } from "./flags-B4ZZWspN.mjs";
9
- import { t as printList } from "./print-n2qetUHc.mjs";
10
- import { t as withStore } from "./store-DACH768X.mjs";
7
+ import { i as readListArgs, n as filterArgs, s as refuseUnknownFlags } from "./flags-ByYBx-Xb.mjs";
8
+ import { t as printList } from "./print-Bd8MWVJk.mjs";
9
+ import { t as withStore } from "./store-2KXzMCEN.mjs";
11
10
  import { homedir } from "node:os";
12
11
  import { defineCommand } from "citty";
13
12
  // src/follow-ups/command.ts
@@ -41,8 +40,7 @@ const placeOf = (store, key) => {
41
40
  if (key === void 0) return placeFor(process.cwd(), projects);
42
41
  const found = projects.find((p) => p.id === key || p.root === key);
43
42
  if (found === void 0) {
44
- const names = projects.map((p) => p.id);
45
- fail(`No registered project \`${key}\`; registered: ${names.length === 0 ? "none" : names.join(", ")}.`);
43
+ fail(unknownProjectRefusal(key, projects.map((p) => p.id)));
46
44
  return;
47
45
  }
48
46
  return placeFor(found.root, projects);
@@ -112,8 +110,10 @@ const byFor = (a, b) => forOrder(a.for) - forOrder(b.for) || a.for.localeCompare
112
110
  /** A multi-line text, each line safe to print. */
113
111
  const linesOf = (text) => text.split("\n").map((line) => plain(line));
114
112
  const when = (f) => f.ref === void 0 ? f.due : `${f.due} ${f.ref}`;
115
- /** One follow-up on one line. */
116
- const followUpLine = (f) => `${style("dim", f.id)} ${f.kind.padEnd(7)} ${plain(f.title)} ${style("dim", `(${plain(when(f))})`)}`;
113
+ /** One follow-up on one line; a short note when a run has taken it. */
114
+ const followUpLine = (f) => `${style("dim", f.id)} ${f.kind.padEnd(7)} ${plain(f.title)} ${style("dim", `(${plain(when(f))})`)}${f.takenBy === void 0 ? "" : style("dim", ` taken by ${plain(f.takenBy)}`)}`;
115
+ /** What to do with a draft: copy and send it, unless it is closed already. */
116
+ const draftHeading = (f, to) => f.status === "done" || f.status === "dropped" ? `Draft to ${to} (${f.status})` : `Draft to ${to}: copy it, send it, then \`ork follow-ups done ${f.id}\``;
117
117
  /**
118
118
  * The follow-ups grouped by status, then by `for`; the closed ones only with
119
119
  * `all`. A line each, under a heading each.
@@ -139,13 +139,14 @@ const formatFollowUp = (f) => [
139
139
  `${style("bold", plain(f.title))} ${style("dim", f.id)}`,
140
140
  `status ${f.status}${f.note === void 0 ? "" : ` (${plain(f.note)})`}, for ${plain(f.for)}, ${f.kind}, due ${plain(f.due)}`,
141
141
  ...f.ref === void 0 ? [] : [`waits on ${plain(f.ref)}`],
142
+ ...f.takenBy === void 0 ? [] : [`taken by run ${plain(f.takenBy)}`],
142
143
  ...fromLine(f),
143
144
  ...f.where.length === 0 ? [] : [`files ${f.where.map((file) => plain(file)).join(", ")}`],
144
145
  ...f.url === void 0 ? [] : [`kept at ${plain(f.url)}`],
145
146
  ...f.body === "" ? [] : ["", ...linesOf(f.body)],
146
147
  ...f.draft === void 0 ? [] : [
147
148
  "",
148
- style("bold", `Draft to ${plain(f.draft.to)}${f.draft.channel === void 0 ? "" : ` (${plain(f.draft.channel)})`}: copy it, send it, then \`ork follow-ups done ${f.id}\``),
149
+ style("bold", draftHeading(f, `${plain(f.draft.to)}${f.draft.channel === void 0 ? "" : ` (${plain(f.draft.channel)})`}`)),
149
150
  ...linesOf(f.draft.text).map((line) => ` ${line}`)
150
151
  ],
151
152
  ...f.steps === void 0 ? [] : [
@@ -419,6 +420,7 @@ const add = defineCommand({
419
420
  const { followUp } = added;
420
421
  write(`${style("green", "✓")} ${added.merged ? "merged into" : "added"} ${followUp.id}: ${plain(followUp.title)}`);
421
422
  if (added.note !== void 0) writeErr(style("dim", added.note));
423
+ if (added.merged && input.draft !== void 0 && followUp.draft !== void 0 && input.draft.text !== followUp.draft.text) write(style("dim", ` the new --text was not kept: ${followUp.id} keeps its first draft (see \`ork follow-ups show ${followUp.id}\`)`));
422
424
  if (relayed !== void 0) write(style("dim", ` sent to ${relayed.node} of run ${relayed.run}, which is live in that project`));
423
425
  })
424
426
  });
@@ -450,17 +452,21 @@ const followUps = defineCommand({
450
452
  name: "list",
451
453
  description: "List the follow-ups, due first, then by who they are for"
452
454
  },
453
- args: {
454
- ...filterArgs(followUpListTable),
455
- ...allAlias
456
- },
455
+ plugins: [refuseUnknownFlags],
456
+ args: filterArgs(followUpListTable),
457
457
  run: async ({ args }) => {
458
458
  const given = args;
459
- const asked = readListArgs(followUpListTable, given, { all: "status" });
459
+ const asked = readListArgs(followUpListTable, given);
460
460
  if (asked === void 0) return;
461
461
  const { filter, limit } = asked;
462
462
  const status = filter["status"];
463
463
  await inProjects(filter["project"], (store, places) => {
464
+ const target = filter["for"]?.match(/^project:(.*)$/u)?.[1];
465
+ const ids = store.projects().map((p) => p.id);
466
+ if (target !== void 0 && !ids.includes(target)) {
467
+ fail(unknownProjectRefusal(target, ids));
468
+ return;
469
+ }
464
470
  const stored = places.flatMap((place) => store.followUps(place.root));
465
471
  const shown = applyFilters(followUpListTable, stored, filter).filter((f) => status !== void 0 || OPEN.has(f.status)).toSorted((a, b) => RANK[a.status] - RANK[b.status] || byFor(a, b)).slice(0, limit);
466
472
  printList(shown, followUpListTable, given, (rows) => {
@@ -1,4 +1,4 @@
1
- import { An as liveStatus, Nn as runStatus, Pn as slotWaitsOf, Rn as isItem, jn as lockWaitsOf, kt as lockWaitLine } from "./src-DLsFsmOD.mjs";
1
+ import { In as runStatus, Ln as slotWaitsOf, Mt as lockWaitLine, Nn as liveStatus, Or as maskSecrets, Pn as lockWaitsOf, Vn as isItem } from "./src-Ciy7i3se.mjs";
2
2
  import { C as termOf, E as widthOf, n as cost, o as shortCount, r as costOf, s as style, w as cut } from "./output-B1kwo0YU.mjs";
3
3
  import { i as statusChip, n as chip } from "./chips-BqcT58qr.mjs";
4
4
  import { n as nodeVerdict } from "./verdict-CMvPCpWC.mjs";
@@ -193,13 +193,27 @@ const worth = (node) => {
193
193
  return style("dim", ` ${node.unpriced === true ? tok : `${tok} · ${cost(node.costUsd)}`}`);
194
194
  };
195
195
  /**
196
+ * A crashed run's node that never ended: cut off when it had started, not run
197
+ * when it had not; `null` for one that ended.
198
+ */
199
+ const crashedState = (node) => {
200
+ if (node.state === "running") return `${STATE_MARK.failed()} cut off`;
201
+ if (node.state === "waiting") return `${STATE_MARK.skipped()} not run`;
202
+ return null;
203
+ };
204
+ /**
196
205
  * The ownership tree, one line per node, children under their owner. A node in
197
- * `waits` (a live run's `lockWaitsOf`) reads `waiting for <lock>`, an item
198
- * too.
206
+ * `waits` (a live run's `lockWaitsOf`) reads `waiting for <lock>`, an item too.
207
+ * In a `crashed` run, a node started but not ended reads `cut off` and one not
208
+ * started `not run`: nothing will end them.
199
209
  */
200
- const formatTree = (roots, now = Date.now(), waits = []) => {
210
+ const formatTree = (roots, now = Date.now(), waits = [], crashed = false) => {
201
211
  const lines = [];
202
- const walk = (node, lead, branch) => {
212
+ const walk = (raw, lead, branch) => {
213
+ const node = raw.description === void 0 ? raw : {
214
+ ...raw,
215
+ description: maskSecrets(raw.description)
216
+ };
203
217
  const named = node.description === void 0 ? "" : ` ${node.description}`;
204
218
  lines.push({
205
219
  label: `${lead}${branch}${node.id}${named}`,
@@ -211,14 +225,15 @@ const formatTree = (roots, now = Date.now(), waits = []) => {
211
225
  });
212
226
  };
213
227
  for (const root of roots) walk(root, "", "");
214
- const label = Math.max(...lines.map((l) => l.label.length));
228
+ const label = Math.max(...lines.map((l) => widthOf(l.label)));
215
229
  const type = Math.max(...lines.map((l) => l.node.type.length));
216
230
  return lines.map(({ label: text, node }) => {
217
231
  const usage = worth(node);
218
232
  const lock = waits.find((wait) => wait.id === node.id)?.lock;
219
- const state = node.held === true ? `${style("yellow", "‖")} paused` : lock === void 0 ? `${STATE_MARK[node.state]()} ${stateText(node)}` : `${STATE_MARK.waiting()} waiting for ${lock}`;
220
- const spent = isItem(node) && node.startedAt !== void 0 ? style("dim", ` ${duration((node.finishedAt === void 0 ? now : Date.parse(node.finishedAt)) - Date.parse(node.startedAt))}`) : "";
221
- return `${node.description === void 0 ? text : `${text.slice(0, text.length - node.description.length)}${dim(node.description)}`}${" ".repeat(label - text.length)} ${style("dim", node.type.padEnd(type))} ${state}${spent}${usage}`;
233
+ const cut = crashed ? crashedState(node) : null;
234
+ const state = cut ?? (node.held === true ? `${style("yellow", "‖")} paused` : lock === void 0 ? `${STATE_MARK[node.state]()} ${stateText(node)}` : `${STATE_MARK.waiting()} waiting for ${lock}`);
235
+ const spent = isItem(node) && node.startedAt !== void 0 && cut === null ? style("dim", ` ${duration((node.finishedAt === void 0 ? now : Date.parse(node.finishedAt)) - Date.parse(node.startedAt))}`) : "";
236
+ return `${node.description === void 0 ? text : `${text.slice(0, text.length - node.description.length)}${dim(node.description)}`}${" ".repeat(label - widthOf(text))} ${style("dim", node.type.padEnd(type))} ${state}${spent}${usage}`;
222
237
  });
223
238
  };
224
239
  /**
@@ -227,7 +242,8 @@ const formatTree = (roots, now = Date.now(), waits = []) => {
227
242
  * show; `events` are the run's, `roots` its nested tree.
228
243
  */
229
244
  const formatTreeView = (run, events, roots, now = Date.now()) => {
230
- const waits = runStatus(run, events) === "running" ? lockWaitsOf(events) : [];
231
- return [`${style("bold", `run ${run.id}`)} ${dim(`of ${run.workflow} · ${listedStatus(run, events)} · ${cost(run.costUsd)}`)}`, ...formatTree(roots, now, waits)];
245
+ const status = runStatus(run, events);
246
+ const waits = status === "running" ? lockWaitsOf(events) : [];
247
+ return [`${style("bold", `run ${run.id}`)} ${dim(`of ${run.workflow} · ${listedStatus(run, events)} · ${cost(run.costUsd)}`)}`, ...formatTree(roots, now, waits, status === "crashed")];
232
248
  };
233
249
  export { header as a, listedStatus as i, formatRuns as n, table as o, formatTreeView as r, ago as t };
@@ -54,9 +54,10 @@ fi
54
54
  `,
55
55
  bash: `# Tab completion for ork, in bash. Add to your ~/.bashrc:
56
56
  # eval "$(ork completion bash)"
57
- # (macOS's bash 3.2 can't source <(…); saving it to a file and sourcing that
58
- # works too.) Every expansion is quoted and lines are read with read -r, so a
59
- # candidate is never split or glob-expanded.
57
+ # (macOS's bash 3.2 loads nothing from source <(…), and prints no error;
58
+ # saving it to a file and sourcing that works in every version.) Every
59
+ # expansion is quoted and lines are read with read -r, so a candidate is never
60
+ # split or glob-expanded.
60
61
 
61
62
  _ork() {
62
63
  local line="\${COMP_LINE:0:COMP_POINT}" cur files=0 strip="" out
@@ -65,10 +66,15 @@ _ork() {
65
66
  if [[ "$line" == *[[:space:]] ]]; then words+=(""); fi
66
67
  cur="\${words[\${#words[@]}-1]}"
67
68
  COMPREPLY=()
68
- # bash splits a word at ":" (ork:feature); offer only what follows it.
69
- if [[ "$cur" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then
70
- strip="\${cur%"\${cur##*:}"}"
71
- fi
69
+ # bash splits a word at ":" and "=" (ork:feature, --input=plan=); offer only
70
+ # what follows the last of them that is a word break.
71
+ local brk pre
72
+ for brk in : =; do
73
+ if [[ "$cur" == *"$brk"* && "$COMP_WORDBREAKS" == *"$brk"* ]]; then
74
+ pre="\${cur%"\${cur##*"$brk"}"}"
75
+ if (( \${#pre} > \${#strip} )); then strip="$pre"; fi
76
+ fi
77
+ done
72
78
  out="$("\${words[0]}" ${COMPLETE_COMMAND} -- "\${words[@]:1}" 2>/dev/null)"
73
79
  while IFS= read -r line; do
74
80
  case "$line" in
@@ -1,5 +1,5 @@
1
- import { R as storePath } from "./src-DLsFsmOD.mjs";
2
- import { g as storeGuarded, h as runMarkerIn } from "./src-DmmrX2_C.mjs";
1
+ import { z as storePath } from "./src-Ciy7i3se.mjs";
2
+ import { g as storeGuarded, h as runMarkerIn } from "./src-DP02RP9O.mjs";
3
3
  // src/commands/in-a-run.ts
4
4
  /**
5
5
  * @file The commands that act as the user refuse a program or an agent in a run