@krapht/ork 1.0.0-alpha.2 → 1.0.0-alpha.21

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 (75) hide show
  1. package/README.md +12 -7
  2. package/dist/__build_info__-0wqQ-rtf.mjs +180 -0
  3. package/dist/apply-Mb0fNpCX.mjs +17 -0
  4. package/dist/argument-error-CihH46Le.mjs +27 -0
  5. package/dist/attach-C0-YyNoY.mjs +92 -0
  6. package/dist/attach-CAKNqnUK.mjs +1259 -0
  7. package/dist/bin.mjs +1885 -1609
  8. package/dist/brief-C2hew8cS.mjs +116 -0
  9. package/dist/{decide-CGyx9rv7.mjs → builtin-files-YYAnbZGD.mjs} +3 -51
  10. package/dist/chips-CP_pgKlj.mjs +48 -0
  11. package/dist/command-tree-CEkzWNy6.mjs +63 -0
  12. package/dist/{completion-CSU5vkEt.mjs → completion-K4rAVsrf.mjs} +200 -30
  13. package/dist/context-B-fjPJky.mjs +106 -0
  14. package/dist/context-DMK0U1w0.mjs +160 -0
  15. package/dist/detached-q0AYG0Bs.mjs +39 -0
  16. package/dist/extra-arguments-BxQMXARO.mjs +13 -0
  17. package/dist/{find-D6Gj1sxg.mjs → find-Ct5CNl1J.mjs} +2 -2
  18. package/dist/flags-C90N4J1i.mjs +145 -0
  19. package/dist/follow-ups-D3HPyNYM.mjs +568 -0
  20. package/dist/format-CiPhQqbX.mjs +260 -0
  21. package/dist/{help-groups-BwYoAWsh.mjs → help-groups-BQvcKQ9H.mjs} +49 -78
  22. package/dist/in-a-run-D_wg4iJi.mjs +27 -0
  23. package/dist/install-Hji6LIei.mjs +259 -0
  24. package/dist/{limits-B1-DYc3d.mjs → limits-BD6RZRCS.mjs} +2 -2
  25. package/dist/{mcp-DE_EmyVm.mjs → mcp-CpbDmBLG.mjs} +6 -6
  26. package/dist/once-J8Qn5fTG.mjs +112 -0
  27. package/dist/orphan-guard-BFKg8VSd.mjs +58 -0
  28. package/dist/output-DXZbnFt_.mjs +458 -0
  29. package/dist/outputs-text-CNTJ7Qq_.mjs +19 -0
  30. package/dist/{pause-CL6N_Xrj.mjs → pause-Cl4ZYA35.mjs} +7 -4
  31. package/dist/print-DzugGZQB.mjs +19 -0
  32. package/dist/project-records-CtkmlsHN.mjs +233 -0
  33. package/dist/refusals-DQQX3AIb.mjs +116 -0
  34. package/dist/{relay-0SD0erwf.mjs → relay-CiP77vga.mjs} +2 -2
  35. package/dist/run-CtuNY3mE.mjs +234 -0
  36. package/dist/send-DRYGks8K.mjs +104 -0
  37. package/dist/short-flags-DkJLWVqh.mjs +1 -0
  38. package/dist/short-flags-Qf21as0E.mjs +83 -0
  39. package/dist/src-BMFQKHz4.mjs +3122 -0
  40. package/dist/{src-BbyEoMn4.mjs → src-BrFfDKux.mjs} +800 -235
  41. package/dist/{src-Dujmvv4s.mjs → src-BuF9Lbow.mjs} +12580 -5505
  42. package/dist/{src-DOcK8Jfp.mjs → src-k0A_QpST.mjs} +1024 -590
  43. package/dist/store-rTgRu8xy.mjs +25 -0
  44. package/dist/ui/THIRD_PARTY_LICENSES.txt +612 -0
  45. package/dist/ui/assets/app-BYxpKJRh.css +1 -0
  46. package/dist/ui/assets/app-SEFEJLkF.js +112 -0
  47. package/dist/ui/assets/{index-9dFha9Qn.js → index-Cb6Voq_W.js} +3 -3
  48. package/dist/ui/assets/theme-CdXvqTQ6.js +1 -0
  49. package/dist/ui/index.html +2 -2
  50. package/dist/{ui-CTmO2Xzu.mjs → ui-C2f37TSb.mjs} +57 -25
  51. package/dist/usage-BuvGcQSk.mjs +256 -0
  52. package/dist/validate-ObP5lt29.mjs +1 -0
  53. package/dist/validate-_HjACtk9.mjs +632 -0
  54. package/dist/verdict-B592j1W3.mjs +84 -0
  55. package/dist/{watch-CtbgDAcz.mjs → watch-DV3ixCYj.mjs} +45 -17
  56. package/dist/{workflows-D1_WVukk.mjs → workflows-QLjBhGNS.mjs} +30 -5
  57. package/dist/worktrees-D3ubG_Qw.mjs +302 -0
  58. package/dist/yolo-BSH1E_kE.mjs +33 -0
  59. package/package.json +10 -8
  60. package/dist/__build_info__-fy6L1Mrd.mjs +0 -615
  61. package/dist/clean-DaHjs__2.mjs +0 -88
  62. package/dist/context-DT8MfEIR.mjs +0 -274
  63. package/dist/format-Dnfv8zMT.mjs +0 -427
  64. package/dist/in-a-run-pLAZeNys.mjs +0 -22
  65. package/dist/list-BrH8KRSJ.mjs +0 -110
  66. package/dist/output-BLQWD6MJ.mjs +0 -950
  67. package/dist/remove-BeGdqroy.mjs +0 -194
  68. package/dist/report-DK4LGqwF.mjs +0 -129
  69. package/dist/send-C8tunWhv.mjs +0 -163
  70. package/dist/src-CCUKRX51.mjs +0 -1204
  71. package/dist/ui/assets/app-BNT0SuIz.css +0 -1
  72. package/dist/ui/assets/app-T7wVAhgx.js +0 -67
  73. package/dist/ui/assets/theme-3xH5jxKs.js +0 -1
  74. package/dist/usage-DYUmP0i4.mjs +0 -419
  75. package/dist/worktrees-DdMYoNOC.mjs +0 -212
@@ -0,0 +1,260 @@
1
+ import { Fn as lockWaitsOf, Ln as runStatus, Nt as lockWaitLine, Pn as liveStatus, Rn as slotWaitingOf, Vn as yoloOf, Wn as isItem, jr as maskSecrets, zn as slotWaitsOf, zt as slotWaitLine } from "./src-BuF9Lbow.mjs";
2
+ import { n as cost, o as shortCount, r as costOf, s as style, v as termOf, x as widthOf, y as cut } from "./output-DXZbnFt_.mjs";
3
+ import { a as statusChip, r as chip } from "./chips-CP_pgKlj.mjs";
4
+ import { n as nodeVerdict } from "./verdict-B592j1W3.mjs";
5
+ // src/term/table.ts
6
+ /**
7
+ * @file Aligned columns for the listing commands: each column as wide as its
8
+ * widest cell, a two-space gap, the last column left unpadded. Rich, a cell
9
+ * may be a chip and a table wider than the screen cuts its flexible columns
10
+ * with `…`; plain, nothing is cut or styled, as the listings always printed.
11
+ */
12
+ /** A dimmed header cell. */
13
+ const header = (text) => ({
14
+ text,
15
+ look: (s) => style("dim", s)
16
+ });
17
+ const GAP = " ";
18
+ /** A flexible column is never cut under this many columns. */
19
+ const MIN_FLEX = 8;
20
+ /** The text a cell shows, and its columns. */
21
+ const shown = (term, cell) => term.rich && cell.chip !== void 0 ? cell.chip.label : cell.text;
22
+ const widthOfCell = (term, cell) => widthOf(shown(term, cell)) + (term.rich && cell.chip !== void 0 ? 2 : 0);
23
+ /** The lines of a table, one per row (a header is a row the caller passes). */
24
+ const table = (term, columns, rows, indent = "") => {
25
+ const widths = columns.map((_, c) => Math.max(0, ...rows.map((row) => widthOfCell(term, row[c] ?? { text: "" }))));
26
+ if (term.rich) {
27
+ const gaps = 2 * (columns.length - 1);
28
+ let over = indent.length + widths.reduce((a, b) => a + b, 0) + gaps - term.width;
29
+ columns.forEach((column, c) => {
30
+ const width = widths[c] ?? 0;
31
+ if (over <= 0 || column.flex !== true || width <= MIN_FLEX) return;
32
+ const next = Math.max(MIN_FLEX, width - over);
33
+ over -= width - next;
34
+ widths[c] = next;
35
+ });
36
+ }
37
+ const last = columns.length - 1;
38
+ return rows.map((row) => indent + columns.map((column, c) => {
39
+ const cell = row[c] ?? { text: "" };
40
+ const width = widths[c] ?? 0;
41
+ const look = cell.look ?? ((s) => s);
42
+ if (term.rich && cell.chip !== void 0) {
43
+ const used = widthOfCell(term, cell);
44
+ const pad = " ".repeat(Math.max(0, width - used));
45
+ return column.align === "right" ? pad + chip(term, cell.chip.label, cell.chip.role) : chip(term, cell.chip.label, cell.chip.role) + (c === last ? "" : pad);
46
+ }
47
+ const text = cut(shown(term, cell), width);
48
+ const pad = " ".repeat(Math.max(0, width - widthOf(text)));
49
+ if (column.align === "right") return look(pad + text);
50
+ return c === last ? look(text) : look(text + pad);
51
+ }).join(GAP));
52
+ };
53
+ // src/runs/format.ts
54
+ /**
55
+ * @file How stored runs read on the terminal: the `ork ps` table and the `ork
56
+ * tree` ownership tree with each node's state, tokens and cost, and what is
57
+ * paused (ADR-041).
58
+ */
59
+ const STATUS_STYLE = {
60
+ running: "cyan",
61
+ paused: "yellow",
62
+ succeeded: "green",
63
+ failed: "red",
64
+ crashed: "red",
65
+ budget_exhausted: "yellow",
66
+ quota_exhausted: "yellow",
67
+ timed_out: "yellow",
68
+ interrupted: "yellow"
69
+ };
70
+ /** `just now`, `5m ago`, `3h ago`, `2d ago`, then the date. */
71
+ const ago = (iso, now = Date.now()) => {
72
+ const minutes = Math.floor((now - Date.parse(iso)) / 6e4);
73
+ if (minutes < 1) return "just now";
74
+ if (minutes < 60) return `${String(minutes)}m ago`;
75
+ if (minutes < 1440) return `${String(Math.floor(minutes / 60))}h ago`;
76
+ if (minutes < 10080) return `${String(Math.floor(minutes / 1440))}d ago`;
77
+ return iso.slice(0, 10);
78
+ };
79
+ /** `42s`, `3m12s`, `1h05m`. */
80
+ const duration = (ms) => {
81
+ const s = Math.max(0, Math.round(ms / 1e3));
82
+ if (s < 60) return `${String(s)}s`;
83
+ if (s < 3600) return `${String(Math.floor(s / 60))}m${String(s % 60).padStart(2, "0")}s`;
84
+ return `${String(Math.floor(s / 3600))}h${String(Math.floor(s % 3600 / 60)).padStart(2, "0")}m`;
85
+ };
86
+ const dim = (s) => style("dim", s);
87
+ /**
88
+ * A run's status as `ork ps` reads it: for a running run, `waiting for <lock>,
89
+ * held by <run>/<node>` with a node waiting for a lock, or `waiting for slot`
90
+ * with a command waiting for a machine-wide slot; `events` are a running
91
+ * run's.
92
+ */
93
+ const listedStatus = (run, events) => {
94
+ const status = runStatus(run, events);
95
+ if (status !== "running") return status;
96
+ const [lock] = lockWaitsOf(events);
97
+ if (lock !== void 0) return lockWaitLine(lock.lock, lock.holders);
98
+ return slotWaitsOf(events).length > 0 ? "waiting for slot" : status;
99
+ };
100
+ /**
101
+ * The `ork ps` table; `withProject` adds each run's project. A called run's
102
+ * cost is dimmed and says its caller's, whose cost already counts it.
103
+ */
104
+ const formatRuns = (runs, withProject, now = Date.now(), statusOf = liveStatus, term = termOf(process.stdout), yolo = () => false) => {
105
+ if (runs.length === 0) return ["No runs."];
106
+ const rows = runs.map((run) => {
107
+ const status = statusOf(run);
108
+ const end = run.finishedAt === void 0 ? now : Date.parse(run.finishedAt);
109
+ return {
110
+ id: run.id,
111
+ workflow: yolo(run) ? `${run.workflow} · yolo` : run.workflow,
112
+ yolo: yolo(run),
113
+ status,
114
+ when: `${ago(run.startedAt, now)} · ${duration(end - Date.parse(run.startedAt))}`,
115
+ cost: run.parent === void 0 ? costOf(run.costUsd, run.partial, run.tokens) : `${costOf(run.costUsd, run.partial, run.tokens)} in ${run.parent}`,
116
+ called: run.parent !== void 0,
117
+ project: run.project
118
+ };
119
+ });
120
+ const rowOf = (r) => {
121
+ const cells = [
122
+ {
123
+ text: r.id,
124
+ look: (s) => style("bold", s)
125
+ },
126
+ r.yolo ? {
127
+ text: r.workflow,
128
+ look: (s) => style("yellow", s)
129
+ } : { text: r.workflow },
130
+ {
131
+ text: r.status,
132
+ chip: statusChip(r.status),
133
+ look: (s) => style(STATUS_STYLE[r.status] ?? "reset", s)
134
+ },
135
+ {
136
+ text: r.when,
137
+ look: dim
138
+ },
139
+ r.called ? {
140
+ text: r.cost,
141
+ look: dim
142
+ } : { text: r.cost }
143
+ ];
144
+ if (withProject) cells.push({
145
+ text: r.project,
146
+ look: dim
147
+ });
148
+ return cells;
149
+ };
150
+ const heads = [
151
+ "RUN",
152
+ "WORKFLOW",
153
+ "STATUS",
154
+ "STARTED",
155
+ "COST"
156
+ ].map((text) => header(text));
157
+ if (withProject) heads.push(header("PROJECT"));
158
+ const columns = [
159
+ {},
160
+ { flex: true },
161
+ {},
162
+ {},
163
+ { align: "right" }
164
+ ];
165
+ if (withProject) columns.push({ flex: true });
166
+ return table(term, columns, [heads, ...rows.map((row) => rowOf(row))]);
167
+ };
168
+ /** Marks are made when used, so `--plain` (set after load) is read. */
169
+ const STATE_MARK = {
170
+ waiting: () => style("dim", "·"),
171
+ running: () => style("cyan", "●"),
172
+ ok: () => style("green", "✓"),
173
+ failed: () => style("red", "✗"),
174
+ skipped: () => style("dim", "-")
175
+ };
176
+ /** `950`, `12.3k`, `1.2M`. */
177
+ const count = shortCount;
178
+ const stateText = (node) => {
179
+ if (node.state === "running") return node.runs > 1 ? `running (round ${String(node.runs)})` : "running";
180
+ if (node.state === "ok" || node.state === "failed") {
181
+ const verdict = nodeVerdict(node.output ?? {}, node.state === "ok");
182
+ const line = verdict.length > 60 ? `${verdict.slice(0, 59)}…` : verdict;
183
+ return node.runs > 1 ? `${line} (${String(node.runs)} runs)` : line;
184
+ }
185
+ return node.state;
186
+ };
187
+ /** A node's counted tokens: input, output and cache write. */
188
+ const countedOf = (node) => node.inputTokens + node.outputTokens + (node.cacheWriteTokens ?? 0);
189
+ /**
190
+ * What a node spent: its counted tokens, then dollars unless its driver reports
191
+ * none, where `$0.00` would be false. Blank with no tokens.
192
+ */
193
+ const worth = (node) => {
194
+ const counted = countedOf(node);
195
+ if (counted === 0) return "";
196
+ const tok = `${count(counted)} tok`;
197
+ return style("dim", ` ${node.unpriced === true ? tok : `${tok} · ${cost(node.costUsd)}`}`);
198
+ };
199
+ /**
200
+ * A crashed run's node that never ended: cut off when it had started, not run
201
+ * when it had not; `null` for one that ended.
202
+ */
203
+ const crashedState = (node) => {
204
+ if (node.state === "running") return `${STATE_MARK.failed()} cut off`;
205
+ if (node.state === "waiting") return `${STATE_MARK.skipped()} not run`;
206
+ return null;
207
+ };
208
+ /**
209
+ * The ownership tree, one line per node, children under their owner. A node in
210
+ * `waits` (a live run's lock and slot waits) reads its line, an item too. In a
211
+ * `crashed` run, a node started but not ended reads `cut off` and one not
212
+ * started `not run`: nothing will end them.
213
+ */
214
+ const formatTree = (roots, now = Date.now(), waits = [], crashed = false) => {
215
+ const lines = [];
216
+ const walk = (raw, lead, branch) => {
217
+ const node = raw.description === void 0 ? raw : {
218
+ ...raw,
219
+ description: maskSecrets(raw.description)
220
+ };
221
+ const named = node.description === void 0 ? "" : ` ${node.description}`;
222
+ lines.push({
223
+ label: `${lead}${branch}${node.id}${named}`,
224
+ node
225
+ });
226
+ const next = lead + (branch === "├─ " ? "│ " : branch === "" ? "" : " ");
227
+ node.children.forEach((child, index) => {
228
+ walk(child, next, index === node.children.length - 1 ? "└─ " : "├─ ");
229
+ });
230
+ };
231
+ for (const root of roots) walk(root, "", "");
232
+ const label = Math.max(...lines.map((l) => widthOf(l.label)));
233
+ const type = Math.max(...lines.map((l) => l.node.type.length));
234
+ return lines.map(({ label: text, node }) => {
235
+ const usage = worth(node);
236
+ const wait = waits.find((each) => each.id === node.id)?.line;
237
+ const cut = crashed ? crashedState(node) : null;
238
+ const state = cut ?? (node.held === true ? `${style("yellow", "‖")} paused` : wait === void 0 ? `${STATE_MARK[node.state]()} ${stateText(node)}` : `${STATE_MARK.waiting()} ${wait}`);
239
+ 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))}`) : "";
240
+ 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}`;
241
+ });
242
+ };
243
+ /**
244
+ * What `ork tree` prints: the run's header (its status as `ork ps` reads it)
245
+ * and the tree. A finished run's lock waits are stale, so only a live run's
246
+ * show; `events` are the run's, `roots` its nested tree.
247
+ */
248
+ const formatTreeView = (run, events, roots, now = Date.now()) => {
249
+ const status = runStatus(run, events);
250
+ const waits = status === "running" ? [...lockWaitsOf(events).map(({ id, lock }) => ({
251
+ id,
252
+ line: `waiting for ${lock}`
253
+ })), ...slotWaitingOf(events).map(({ id, holders }) => ({
254
+ id,
255
+ line: slotWaitLine(holders)
256
+ }))] : [];
257
+ const yolo = status === "running" && yoloOf(events) ? style("yellow", " · yolo") : "";
258
+ return [`${style("bold", `run ${run.id}`)} ${dim(`of ${run.workflow} · ${listedStatus(run, events)} · ${cost(run.costUsd)}`)}${yolo}`, ...formatTree(roots, now, waits, status === "crashed")];
259
+ };
260
+ export { header as a, listedStatus as i, formatRuns as n, table as o, formatTreeView as r, ago as t };
@@ -4,7 +4,9 @@
4
4
  * shell. Each is thin: it runs `ork __complete -- <words…>` with the word
5
5
  * under the cursor last, and offers the lines it prints, `value<TAB>
6
6
  * description`. A line `:group <title>` starts a group (zsh labels the menu
7
- * with it); `:files` asks for file names too. A new shell is one more entry.
7
+ * with it); `:files` asks for file names too. A value ending in `=` (an
8
+ * input's `name=`) takes no space after it, so its value is typed next. A new
9
+ * shell is one more entry.
8
10
  */
9
11
  /** The hidden command the scripts run. */
10
12
  const COMPLETE_COMMAND = "__complete";
@@ -15,9 +17,11 @@ const SHELL_SCRIPTS = {
15
17
  # (ork completion zsh > ~/.zsh/completions/_ork), or source it.
16
18
 
17
19
  _ork() {
18
- local -a lines cands
19
- local line group=commands files=0 n=0 ret=1
20
+ local -a lines cands opens
21
+ local line value said group=commands files=0 n=0 ret=1
20
22
  lines=("\${(@f)$("\${words[1]}" ${COMPLETE_COMMAND} -- "\${(@)words[2,CURRENT]}" 2>/dev/null)}")
23
+ # A last group line offers what the last group gathered.
24
+ lines+=(":group end")
21
25
  for line in "\${lines[@]}"; do
22
26
  case $line in
23
27
  (:files) files=1 ;;
@@ -25,15 +29,22 @@ _ork() {
25
29
  if (( \${#cands} )); then
26
30
  _describe -V -t "ork-$(( ++n ))" "$group" cands && ret=0
27
31
  fi
28
- cands=()
32
+ # name= is followed by its value, not a space.
33
+ if (( \${#opens} )); then
34
+ _describe -V -t "ork-$(( ++n ))" "$group" opens -S '' && ret=0
35
+ fi
36
+ cands=() opens=()
29
37
  group=\${line#:group }
30
38
  ;;
31
- (?*) cands+=("\${\${line%%$'\\t'*}//:/\\\\:}:\${line#*$'\\t'}") ;;
39
+ (?*)
40
+ value=\${\${line%%$'\\t'*}//:/\\\\:}
41
+ said=\${line#*$'\\t'}
42
+ # No description: the value alone, or zsh shows an empty one as --.
43
+ [[ $said == "$line" || -z $said ]] || value+=":$said"
44
+ if [[ $line == *=$'\\t'* ]]; then opens+=("$value"); else cands+=("$value"); fi
45
+ ;;
32
46
  esac
33
47
  done
34
- if (( \${#cands} )); then
35
- _describe -V -t "ork-$(( ++n ))" "$group" cands && ret=0
36
- fi
37
48
  if (( files )); then
38
49
  _files && ret=0
39
50
  fi
@@ -48,9 +59,10 @@ fi
48
59
  `,
49
60
  bash: `# Tab completion for ork, in bash. Add to your ~/.bashrc:
50
61
  # eval "$(ork completion bash)"
51
- # (macOS's bash 3.2 can't source <(…); saving it to a file and sourcing that
52
- # works too.) Every expansion is quoted and lines are read with read -r, so a
53
- # candidate is never split or glob-expanded.
62
+ # (macOS's bash 3.2 loads nothing from source <(…), and prints no error;
63
+ # saving it to a file and sourcing that works in every version.) Every
64
+ # expansion is quoted and lines are read with read -r, so a candidate is never
65
+ # split or glob-expanded.
54
66
 
55
67
  _ork() {
56
68
  local line="\${COMP_LINE:0:COMP_POINT}" cur files=0 strip="" out
@@ -59,10 +71,15 @@ _ork() {
59
71
  if [[ "$line" == *[[:space:]] ]]; then words+=(""); fi
60
72
  cur="\${words[\${#words[@]}-1]}"
61
73
  COMPREPLY=()
62
- # bash splits a word at ":" (ork:feature); offer only what follows it.
63
- if [[ "$cur" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then
64
- strip="\${cur%"\${cur##*:}"}"
65
- fi
74
+ # bash splits a word at ":" and "=" (ork:feature, --input=plan=); offer only
75
+ # what follows the last of them that is a word break.
76
+ local brk pre
77
+ for brk in : =; do
78
+ if [[ "$cur" == *"$brk"* && "$COMP_WORDBREAKS" == *"$brk"* ]]; then
79
+ pre="\${cur%"\${cur##*"$brk"}"}"
80
+ if (( \${#pre} > \${#strip} )); then strip="$pre"; fi
81
+ fi
82
+ done
66
83
  out="$("\${words[0]}" ${COMPLETE_COMMAND} -- "\${words[@]:1}" 2>/dev/null)"
67
84
  while IFS= read -r line; do
68
85
  case "$line" in
@@ -71,6 +88,10 @@ _ork() {
71
88
  *)
72
89
  line="\${line%%$'\\t'*}"
73
90
  COMPREPLY+=("\${line#"$strip"}")
91
+ # name= is followed by its value, not a space (bash 4 and later).
92
+ if [[ "$line" == *= ]] && type compopt &>/dev/null; then
93
+ compopt -o nospace
94
+ fi
74
95
  ;;
75
96
  esac
76
97
  done <<< "$out"
@@ -103,68 +124,6 @@ complete -c ork -f -a '(__ork_complete)'
103
124
  };
104
125
  /** The shells there is a script for. */
105
126
  const SHELLS = Object.keys(SHELL_SCRIPTS);
106
- // src/command-tree.ts
107
- /** How a flag that only Ork passes to itself starts its description. */
108
- const INTERNAL = "Internal:";
109
- /** Whether citty was given a function that gives the value. */
110
- const isThunk = (value) => typeof value === "function";
111
- /** A value citty lets be given as is, as a promise, or as a function. */
112
- const resolved = (value) => Promise.resolve(isThunk(value) ? value() : value);
113
- /** `args` without the flags only Ork passes to itself. */
114
- const publicArgs = (args) => Object.fromEntries(Object.entries(args).filter(([, def]) => !(def.description ?? "").startsWith(INTERNAL)));
115
- /** A command's flags and positionals, which citty lets be given lazily. */
116
- const argsOf = (cmd) => cmd.args === void 0 ? Promise.resolve({}) : resolved(cmd.args);
117
- const listOf = (value) => value === void 0 ? [] : [value].flat();
118
- const flagOf = (name, def) => ({
119
- name,
120
- description: def.description ?? "",
121
- takesValue: def.type === "string" || def.type === "enum",
122
- aliases: "alias" in def ? listOf(def.alias) : [],
123
- options: def.type === "enum" ? def.options ?? [] : [],
124
- negatable: def.type === "boolean" && def.default === true
125
- });
126
- /** A command's public flags and positionals, in the order it defines them. */
127
- const argInfoOf = async (cmd) => {
128
- const entries = Object.entries(publicArgs(await argsOf(cmd)));
129
- return {
130
- flags: entries.filter(([, def]) => def.type !== "positional").map(([name, def]) => flagOf(name, def)),
131
- positionals: entries.filter(([, def]) => def.type === "positional").map(([name, def]) => ({
132
- name,
133
- description: def.description ?? ""
134
- }))
135
- };
136
- };
137
- /**
138
- * A command's subcommands, in order, loaded ones resolved. A name whose
139
- * definition is one listed before it (`wt`, `worktrees`'s) is an alias of that
140
- * one, not an entry of its own.
141
- */
142
- const subcommandsOf = async (cmd) => {
143
- const subs = cmd.subCommands === void 0 ? {} : await resolved(cmd.subCommands);
144
- const loaded = await Promise.all(Object.entries(subs).map(async ([name, sub]) => {
145
- const def = await resolved(sub);
146
- return {
147
- name,
148
- def,
149
- meta: def.meta === void 0 ? {} : await resolved(def.meta)
150
- };
151
- }));
152
- const entries = [];
153
- for (const { name, def, meta } of loaded) {
154
- const same = entries.find((entry) => entry.def === def);
155
- if (same === void 0) entries.push({
156
- name,
157
- aliases: listOf(meta.alias),
158
- description: meta.description ?? "",
159
- hidden: meta.hidden === true,
160
- def
161
- });
162
- else same.aliases.push(name);
163
- }
164
- return entries;
165
- };
166
- /** The subcommand `name` (or one of its aliases) of `entries`. */
167
- const findEntry = (entries, name) => entries.find((entry) => entry.name === name || entry.aliases.includes(name));
168
127
  // src/help-groups.ts
169
128
  const HELP_GROUPS = [
170
129
  {
@@ -220,6 +179,10 @@ const HELP_GROUPS = [
220
179
  name: "logs",
221
180
  short: "Print a run's transcript"
222
181
  },
182
+ {
183
+ name: "attach",
184
+ short: "Follow a run here and answer its questions"
185
+ },
223
186
  {
224
187
  name: "tree",
225
188
  short: "Show a run's tree, tokens and cost"
@@ -270,6 +233,10 @@ const HELP_GROUPS = [
270
233
  name: "feedback",
271
234
  short: "What got in the way in your runs"
272
235
  },
236
+ {
237
+ name: "follow-ups",
238
+ short: "What shipped changes left for later"
239
+ },
273
240
  {
274
241
  name: "worktrees",
275
242
  short: "Track and clean up Ork's worktrees"
@@ -316,6 +283,10 @@ const HELP_GROUPS = [
316
283
  name: "send",
317
284
  short: "Write to a session from a tool node's program"
318
285
  },
286
+ {
287
+ name: "once",
288
+ short: "Run a check unless it passed on these files"
289
+ },
319
290
  {
320
291
  name: "completion",
321
292
  short: "Print a tab-completion script"
@@ -351,4 +322,4 @@ const groupedEntries = (entries) => {
351
322
  entries: other
352
323
  }].filter((group) => group.entries.length > 0);
353
324
  };
354
- export { findEntry as a, subcommandsOf as c, SHELL_SCRIPTS as d, argsOf as i, COMPLETE_COMMAND as l, groupedEntries as n, publicArgs as o, argInfoOf as r, resolved as s, groupOf as t, SHELLS as u };
325
+ export { SHELL_SCRIPTS as a, SHELLS as i, groupedEntries as n, COMPLETE_COMMAND as r, groupOf as t };
@@ -0,0 +1,27 @@
1
+ import { z as storePath } from "./src-BuF9Lbow.mjs";
2
+ import { g as storeGuarded, h as runMarkerIn } from "./src-k0A_QpST.mjs";
3
+ // src/commands/in-a-run.ts
4
+ /**
5
+ * @file The commands that act as the user refuse a program or an agent in a run
6
+ * (ADR-047), in the stores that run guards: the store of a nested run under a
7
+ * temp HOME is not guarded unless an enclosing run lists it. The refusal
8
+ * names the variable it saw, for someone at their own terminal who inherited
9
+ * it. It stops accidents; it is not a boundary.
10
+ */
11
+ const refusal = (why, env, store) => {
12
+ const marker = runMarkerIn(env);
13
+ return marker === null || !storeGuarded(env, store) ? null : `${why} ${marker} is set here. If this is your own terminal, it inherited that from a program in a run (a tmux server started by one, say); open a shell without it.`;
14
+ };
15
+ /** Why `ork watch --accept` refuses here, or `null` when it may run. */
16
+ const acceptRefusal = (env = process.env, store = storePath()) => refusal("`ork watch --accept` approves code to run with nobody there, so a program or an agent in a run cannot use it.", env, store);
17
+ /** Why `ork chat` refuses here, or `null` when it may run. */
18
+ const chatRefusal = (env = process.env, store = storePath()) => refusal("`ork chat` speaks as you, so a program or an agent in a run cannot use it. A program writes to a session with `ork send`.", env, store);
19
+ /** Why `ork decide` will not answer here, or `null` when it may. */
20
+ const decideRefusal = (env = process.env, store = storePath()) => refusal("`ork decide` answers as you, so a program or an agent in a run cannot answer with it; `ork decide --list` still shows what is open.", env, store);
21
+ /** Why `ork continue` refuses here, or `null` when it may run. */
22
+ const continueRefusal = (env = process.env, store = storePath()) => refusal("`ork continue` undoes a pause you set, so a program or an agent in a run cannot use it.", env, store);
23
+ /** Whether `ork ui` may print its sign-in link, which acts as the user. */
24
+ const mayShowSignIn = (env = process.env, store = storePath()) => !storeGuarded(env, store);
25
+ /** Why `ork attach` will not answer here, or `null` when it may. */
26
+ const attachRefusal = (env = process.env, store = storePath()) => refusal("`ork attach` answers as you, so a program or an agent in a run cannot use it; `ork logs` still shows the run.", env, store);
27
+ export { decideRefusal as a, continueRefusal as i, attachRefusal as n, mayShowSignIn as o, chatRefusal as r, acceptRefusal as t };