@krapht/ork 0.3.0 → 1.0.0-alpha.0

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 (39) hide show
  1. package/README.md +16 -12
  2. package/dist/__build_info__-DdTVd2p5.mjs +615 -0
  3. package/dist/bin.mjs +1572 -909
  4. package/dist/clean-NnboOuz6.mjs +88 -0
  5. package/dist/completion-snnYGjgK.mjs +405 -0
  6. package/dist/{context-BFGC_C02.mjs → context-DO3sBy9v.mjs} +30 -191
  7. package/dist/decide-B81wCZQg.mjs +113 -0
  8. package/dist/{find-BcvhT6UN.mjs → find-BKW-uGey.mjs} +1 -1
  9. package/dist/{format-DqpObyMl.mjs → format-CmTlTOjT.mjs} +72 -198
  10. package/dist/help-groups-BwYoAWsh.mjs +354 -0
  11. package/dist/{in-a-run-BbLhZOFW.mjs → in-a-run-BPXX09Bs.mjs} +1 -1
  12. package/dist/{limits-BKYAmqCR.mjs → limits-JCve2QpI.mjs} +1 -1
  13. package/dist/list-C7UEgmtd.mjs +110 -0
  14. package/dist/{mcp-BWsdQyri.mjs → mcp-CKPyNaKD.mjs} +7 -7
  15. package/dist/{output-Q2EdeVUt.mjs → output-B1V6ZJSx.mjs} +130 -36
  16. package/dist/{pause-DKACDfV1.mjs → pause-B_SL_yDz.mjs} +4 -4
  17. package/dist/{relay-CbK-Wme9.mjs → relay-o-Luee1c.mjs} +2 -2
  18. package/dist/remove-DAZ7ZCzt.mjs +194 -0
  19. package/dist/{send-C6yr04Fe.mjs → send-3B64fBNB.mjs} +3 -3
  20. package/dist/{src-D5ruZFgD.mjs → src-BJTegEzY.mjs} +1212 -57
  21. package/dist/{src-DtN1k-B3.mjs → src-C38BPzit.mjs} +251 -18
  22. package/dist/{src-Cz1yOUTg.mjs → src-DHvWU4tX.mjs} +2742 -1659
  23. package/dist/{src-CZOVF2yU.mjs → src-Dyz_Gy1q.mjs} +35 -8
  24. package/dist/ui/THIRD_PARTY_LICENSES.txt +2 -2
  25. package/dist/ui/assets/app-C8nJ6NpT.js +67 -0
  26. package/dist/ui/assets/app-CH7YC3in.css +1 -0
  27. package/dist/ui/assets/{index-BiO54L90.css → index-BPIbqRRb.css} +1 -1
  28. package/dist/ui/assets/{index-BoG6O-Lp.js → index-D83F2OKZ.js} +2 -2
  29. package/dist/ui/index.html +2 -3
  30. package/dist/ui-CY4xxJUu.mjs +472 -0
  31. package/dist/{usage-BIjf9Xdt.mjs → usage-CJnytoe4.mjs} +6 -5
  32. package/dist/{watch-B8o4Szd_.mjs → watch-BJB82ZJ7.mjs} +7 -7
  33. package/dist/workflows-CiM7S3l0.mjs +88 -0
  34. package/dist/worktrees-86ySXxR6.mjs +212 -0
  35. package/package.json +6 -6
  36. package/dist/__build_info__-CXXMLvjt.mjs +0 -104
  37. package/dist/ui/assets/app-BlDdP396.js +0 -61
  38. package/dist/ui/assets/app-C722xNZ3.css +0 -1
  39. package/dist/ui-CZS1XKla.mjs +0 -160
@@ -1,175 +1,17 @@
1
- import { C as gitOk, S as git, rt as liveStatus, w as lines, x as removeWorktree } from "./src-Cz1yOUTg.mjs";
2
- import { i as style, n as cost } from "./output-Q2EdeVUt.mjs";
1
+ import { ht as liveStatus } from "./src-DHvWU4tX.mjs";
2
+ import { i as style, n as cost } from "./output-B1V6ZJSx.mjs";
3
3
  import { createInterface } from "node:readline/promises";
4
- import { existsSync } from "node:fs";
5
- // ../core/src/worktree/list.ts
6
- /**
7
- * @file What `ork worktrees` shows and cleans: every worktree and branch Ork's
8
- * runs recorded creating, whatever the project's git rules named them, and
9
- * any `ork/*` branch besides, with their state read from git itself.
10
- */
11
- /**
12
- * The worktrees the project's runs recorded creating, oldest run first; one
13
- * entry per branch or folder, from the latest run that created it.
14
- */
15
- const createdWorktrees = (store, project) => {
16
- const byName = /* @__PURE__ */ new Map();
17
- for (const run of store.runs({ project }).toReversed()) for (const { event } of store.events(run.id)) {
18
- if (event.type !== "worktree" || event.action !== "created") continue;
19
- byName.set(event.branch ?? event.path, {
20
- runId: run.id,
21
- nodeId: event.id,
22
- path: event.path,
23
- ...event.branch === void 0 ? {} : { branch: event.branch }
24
- });
25
- }
26
- return [...byName.values()];
27
- };
28
- const parsePorcelain = (text) => text.split("\n\n").map((block) => lines(block)).map((fields) => {
29
- const value = (key) => fields.find((f) => f.startsWith(`${key} `))?.slice(key.length + 1);
30
- return {
31
- path: value("worktree") ?? "",
32
- branch: (value("branch") ?? "").replace(/^refs\/heads\//u, ""),
33
- prunable: fields.some((f) => f.startsWith("prunable"))
34
- };
35
- }).filter((entry) => entry.path !== "");
36
- /** The `ork/<run>/<node>` branches of runs that recorded nothing. */
37
- const prefixed = (branch) => {
38
- const [, runId = "", ...rest] = branch.split("/");
39
- return {
40
- runId,
41
- nodeId: rest.join("/"),
42
- path: "",
43
- branch
44
- };
45
- };
46
- /** How many commits a detached worktree holds that no branch, tag or remote has. */
47
- const onNoBranch = async (entry) => entry === void 0 ? "0" : (await git(entry.path, [
48
- "rev-list",
49
- "--count",
50
- "HEAD",
51
- "--not",
52
- "--branches",
53
- "--tags",
54
- "--remotes"
55
- ])).stdout;
56
- /** One created worktree or branch, with its state read from git. */
57
- const stateOf = async (root, entries, item) => {
58
- const { branch } = item;
59
- const entry = branch === void 0 ? entries.find((e) => e.path === item.path) : entries.find((e) => e.branch === branch);
60
- const missing = entry !== void 0 && (entry.prunable || !existsSync(entry.path));
61
- const status = entry === void 0 || missing ? "" : (await git(entry.path, ["status", "--porcelain"])).stdout;
62
- const checkedOut = branch === void 0 && entry !== void 0 && entry.branch !== "" ? entry.branch : void 0;
63
- const on = branch ?? checkedOut;
64
- const counted = on === void 0 ? await onNoBranch(entry === void 0 || missing ? void 0 : entry) : (await git(root, [
65
- "rev-list",
66
- "--count",
67
- `HEAD..${on}`
68
- ])).stdout;
69
- const ahead = Number(counted);
70
- return {
71
- ...branch === void 0 ? {} : { branch },
72
- ...checkedOut === void 0 ? {} : { checkedOut },
73
- runId: item.runId,
74
- nodeId: item.nodeId,
75
- path: entry?.path ?? null,
76
- missing,
77
- dirty: lines(status).length > 0,
78
- ahead: Number.isNaN(ahead) ? 0 : ahead,
79
- merged: on !== void 0 && ahead === 0,
80
- inMainCheckout: entry?.path === root
81
- };
82
- };
83
- /**
84
- * Every worktree and branch Ork's runs created in the repository at `root`,
85
- * with its state: those the runs recorded (`created`, whatever the project's
86
- * git rules named them), and any `ork/` branch besides. A recorded one that git
87
- * no longer has is left out.
88
- */
89
- const listOrkWorktrees = async (root, created = []) => {
90
- const entries = parsePorcelain(await gitOk(root, [
91
- "worktree",
92
- "list",
93
- "--porcelain"
94
- ]));
95
- const branches = lines(await gitOk(root, [
96
- "for-each-ref",
97
- "--format=%(refname:short)",
98
- "refs/heads/"
99
- ]));
100
- const recordedBranches = new Set(created.flatMap((c) => c.branch ?? []));
101
- const items = [...created.filter((c) => c.branch === void 0 ? entries.some((e) => e.path === c.path) : branches.includes(c.branch)), ...branches.filter((b) => b.startsWith("ork/") && !recordedBranches.has(b)).map((b) => prefixed(b))];
102
- return Promise.all(items.map((item) => stateOf(root, entries, item)));
103
- };
104
- const cleanStep = (item, options) => {
105
- if (item.inMainCheckout) return {
106
- item,
107
- removeWorktree: false,
108
- deleteBranch: false,
109
- skip: "checked out in the main checkout"
110
- };
111
- if (item.dirty && !options.force) return {
112
- item,
113
- removeWorktree: false,
114
- deleteBranch: false,
115
- skip: "uncommitted changes"
116
- };
117
- if (item.branch === void 0 && item.checkedOut === void 0 && item.ahead > 0 && !options.force) return {
118
- item,
119
- removeWorktree: false,
120
- deleteBranch: false,
121
- skip: "commits on no branch"
122
- };
123
- const removable = item.path !== null && !item.missing;
124
- if (options.branches && item.branch !== void 0 && !item.merged && !options.force) return {
125
- item,
126
- removeWorktree: removable,
127
- deleteBranch: false,
128
- skip: "branch not merged"
129
- };
130
- return {
131
- item,
132
- removeWorktree: removable,
133
- deleteBranch: options.branches && item.branch !== void 0
134
- };
135
- };
136
- /**
137
- * What `ork worktrees clean` would do. A worktree with uncommitted changes, an
138
- * unmerged branch, or a detached worktree holding commits no branch has, is
139
- * kept unless `force`.
140
- */
141
- const planClean = (items, filter, options) => items.filter((item) => filter.runId === void 0 || item.runId === filter.runId).filter((item) => !filter.merged || item.merged).map((item) => cleanStep(item, options));
142
- /** Carries out a clean plan, then prunes stale worktree entries. */
143
- const applyClean = async (root, steps, force) => {
144
- const done = [];
145
- await gitOk(root, ["worktree", "prune"]);
146
- for (const step of steps) {
147
- const { item } = step;
148
- if (step.removeWorktree && item.path !== null) {
149
- await removeWorktree(root, item.path, force);
150
- done.push(`removed ${item.path}`);
151
- }
152
- if (step.deleteBranch && item.branch !== void 0) {
153
- await gitOk(root, [
154
- "branch",
155
- force ? "-D" : "-d",
156
- item.branch
157
- ]);
158
- done.push(`deleted ${item.branch}`);
159
- }
160
- }
161
- await gitOk(root, ["worktree", "prune"]);
162
- return done;
163
- };
164
4
  // src/human.ts
165
5
  /**
166
6
  * @file The terminal `HumanChannel`: answers `ask_user` questions, gates and
167
7
  * permission prompts on the terminal, one at a time, and denies or defers
168
- * when no one is there. In a run it is the decision queue's local answerer,
169
- * racing `ork decide`.
8
+ * when no one is there. After a choice it offers one optional line for a
9
+ * note, which Enter skips. In a run it is the decision queue's local
10
+ * answerer, racing `ork decide`.
170
11
  */
171
12
  const PREVIEW = 160;
172
13
  const NO_ONE_TO_PERMIT = "No one is at the terminal to approve this tool, so it is not allowed in this run.";
14
+ const NOTE_PROMPT = " Note for the run (optional, Enter to skip): ";
173
15
  const NO_ONE_TO_ASK = "no one (nobody is at the terminal; use your best judgment and say so in your report)";
174
16
  /** One line that shows what a tool is about to do. */
175
17
  const previewInput = (toolName, input) => {
@@ -215,6 +57,12 @@ const createTerminalHuman = (io) => {
215
57
  });
216
58
  return signal === void 0 ? rl.question(text) : rl.question(text, { signal });
217
59
  };
60
+ /** The optional line after a choice, when the asker takes a note. */
61
+ const takeNote = async (onNote, signal) => {
62
+ if (onNote === void 0) return;
63
+ const note = (await prompt(NOTE_PROMPT, signal)).trim();
64
+ if (note !== "") onNote(note);
65
+ };
218
66
  const choose = async (hint, options, recommended, signal) => {
219
67
  const picked = pickOption(await prompt(` Choose${hint}: `, signal), options, recommended);
220
68
  if (picked !== void 0) return picked;
@@ -227,7 +75,7 @@ const createTerminalHuman = (io) => {
227
75
  return result;
228
76
  };
229
77
  return {
230
- permit: ({ nodeId, toolName, input, signal }) => oneAtATime(async () => {
78
+ permit: ({ nodeId, toolName, input, signal, onNote }) => oneAtATime(async () => {
231
79
  if (signal?.aborted === true) throw new Error("the prompt was withdrawn");
232
80
  say(`${style(["yellow", "bold"], "?")} ${style("bold", nodeId)} wants to run ${previewInput(toolName, input)}`);
233
81
  if (!io.interactive) {
@@ -238,24 +86,28 @@ const createTerminalHuman = (io) => {
238
86
  };
239
87
  }
240
88
  const answer = (await prompt(" Allow? [y/N] ", signal)).trim().toLowerCase();
241
- return answer === "y" || answer === "yes" ? { allow: true } : {
89
+ if (answer === "y" || answer === "yes") return { allow: true };
90
+ await takeNote(onNote, signal);
91
+ return {
242
92
  allow: false,
243
93
  message: "The user did not allow this tool."
244
94
  };
245
95
  }),
246
- ask: ({ nodeId, question, options, recommended, context, signal }) => oneAtATime(() => {
247
- if (signal?.aborted === true) return Promise.reject(/* @__PURE__ */ new Error("the question was withdrawn"));
96
+ ask: ({ nodeId, question, options, recommended, context, signal, onNote }) => oneAtATime(async () => {
97
+ if (signal?.aborted === true) throw new Error("the question was withdrawn");
248
98
  say(`${style(["yellow", "bold"], "?")} ${style("bold", nodeId)} asks: ${question}`);
249
99
  if (context !== void 0) say(style("dim", ` ${context}`));
250
100
  for (const [index, option] of options.entries()) say(optionLine(option, index, recommended));
251
101
  if (!io.interactive) {
252
102
  const answer = recommended ?? NO_ONE_TO_ASK;
253
103
  say(style("dim", ` no terminal; answered ${answer}`));
254
- return Promise.resolve(answer);
104
+ return answer;
255
105
  }
256
106
  const fallback = options.findIndex((o) => o.id === recommended);
257
107
  const hint = fallback === -1 ? "" : ` [${String(fallback + 1)}]`;
258
- return choose(hint, options, recommended, signal);
108
+ const picked = await choose(hint, options, recommended, signal);
109
+ await takeNote(onNote, signal);
110
+ return picked;
259
111
  }),
260
112
  close: () => {
261
113
  rl?.close();
@@ -291,12 +143,38 @@ const formatBusEvent = (event) => {
291
143
  if (event.type === "owner.answered") return [style("dim", `→ ${event.answer} (owner)`)];
292
144
  return [style("dim", "→ its session ended first; asking you")];
293
145
  };
146
+ // src/run/format-result.ts
147
+ const RUN_MARK = {
148
+ succeeded: style("green", "✓"),
149
+ failed: style("red", "✗"),
150
+ budget_exhausted: style("yellow", "!"),
151
+ quota_exhausted: style("yellow", "!"),
152
+ timed_out: style("yellow", "!"),
153
+ interrupted: style("yellow", "!")
154
+ };
155
+ /** `1 failure handled`, `2 failures handled`. */
156
+ const failuresHandled = (count) => `${String(count)} ${count === 1 ? "failure" : "failures"} handled`;
157
+ /**
158
+ * The run summary: status, cost, what ran, what was skipped and which failures
159
+ * an edge handled, branches to look at, why, and mail to nodes that never ran.
160
+ */
161
+ const formatRunResult = (result) => {
162
+ const ran = result.nodes.filter((n) => n.runs > 0).length;
163
+ const skipped = result.skipped ?? result.nodes.length - ran;
164
+ const handled = result.failuresHandled ?? 0;
165
+ const lines = ["", `${RUN_MARK[result.status]} run ${result.runId} ${result.status.replace("_", " ")} ${style("dim", `· ${cost(result.costUsd)} · ${String(ran)} ran, ${String(skipped)} skipped${handled === 0 ? "" : `, ${failuresHandled(handled)}`}`)}`];
166
+ for (const kept of result.worktrees) lines.push(` ${kept.id}: ${kept.branch ?? "detached"} ${style("dim", `at ${kept.path} (${kept.reason})`)}`);
167
+ for (const reason of result.reasons) lines.push(` ${style("red", reason)}`);
168
+ for (const { node, messages } of result.unread ?? []) lines.push(` ${style(["yellow", "bold"], "warning")} ${node} never ran, so ${messages === 1 ? "1 message" : `${String(messages)} messages`} to it went unread`);
169
+ return lines;
170
+ };
294
171
  // src/run/format.ts
295
172
  /** Who answered a decision, as the log says it. */
296
173
  const ANSWERED_BY = {
297
174
  terminal: "terminal",
298
175
  decide: "ork decide",
299
- ui: "ork ui"
176
+ ui: "ork ui",
177
+ owner: "its owner"
300
178
  };
301
179
  const STATUS_MARK = {
302
180
  pass: style("green", "✓"),
@@ -330,7 +208,10 @@ const formatOutcome = (nodeId, outcome) => {
330
208
  };
331
209
  /** The transcript lines for one event; tool names come from `tools`. */
332
210
  const formatEvent = (nodeId, event, tools) => {
333
- if (event.type === "session.started") return [style("dim", `▸ ${nodeId} (${event.model}, session ${event.sessionId})`)];
211
+ if (event.type === "session.started") {
212
+ const fork = event.fork === true ? ", a fork to answer a question" : "";
213
+ return [style("dim", `▸ ${nodeId} (${event.model}, session ${event.sessionId}${fork})`)];
214
+ }
334
215
  if (event.type === "assistant.text") return indent(event.text);
335
216
  if (event.type === "tool.called") {
336
217
  tools.set(event.id, event.name);
@@ -361,8 +242,12 @@ const formatRunEvent = (event, width, tools) => {
361
242
  if (event.type === "node.event") {
362
243
  const inner = event.event;
363
244
  if (inner.type === "session.finished") return [];
364
- if (inner.type === "session.started") return [`${tag(event.id)} ${style("dim", `${inner.model}, session ${inner.sessionId}`)}`];
365
- return formatEvent(event.id, event.event, tools).map((line) => `${tag(event.id)} ${line.replace(/^ {2}/u, "")}`);
245
+ const fork = inner.fork === true ? style("dim", "fork · ") : "";
246
+ if (inner.type === "session.started") {
247
+ const why = inner.fork === true ? ", to answer a question" : "";
248
+ return [`${tag(event.id)} ${fork}${style("dim", `${inner.model}, session ${inner.sessionId}${why}`)}`];
249
+ }
250
+ return formatEvent(event.id, event.event, tools).map((line) => `${tag(event.id)} ${fork}${line.replace(/^ {2}/u, "")}`);
366
251
  }
367
252
  if (event.type === "node.finished") {
368
253
  const mark = event.ok ? style("green", "✓") : style("red", "✗");
@@ -383,11 +268,13 @@ const formatRunEvent = (event, width, tools) => {
383
268
  if (event.type === "decision.opened") {
384
269
  const what = event.kind === "permission" ? "asks to" : "asks:";
385
270
  const question = event.kind === "permission" ? event.question.replace(/^Allow /u, "run ").replace(/\?$/u, "") : event.question;
386
- return [`${tag(event.node)} ${style(["yellow", "bold"], "?")} ${what} ${question} ${style("dim", `(decision ${event.decision}; ork decide)`)}`];
271
+ const where = event.owner === void 0 ? `decision ${event.decision}; ork decide` : `decision ${event.decision}, via ${event.owner}`;
272
+ return [`${tag(event.node)} ${style(["yellow", "bold"], "?")} ${what} ${question} ${style("dim", `(${where})`)}`];
387
273
  }
388
274
  if (event.type === "decision.answered") {
389
- const by = ANSWERED_BY[event.by];
390
- return [`${tag(event.node)} ${style("dim", `→ ${event.answer} (${by})`)}`];
275
+ const by = event.by === "owner" && event.owner !== void 0 ? event.owner : ANSWERED_BY[event.by];
276
+ const said = event.note === void 0 ? "" : ` · said: ${event.note.replaceAll(/\s+/gu, " ")}`;
277
+ return [`${tag(event.node)} ${style("dim", `→ ${event.answer} (${by})${said}`)}`];
391
278
  }
392
279
  if (event.type === "decision.withdrawn") return [`${tag(event.node)} ${style("dim", "→ question withdrawn")}`];
393
280
  if (event.type === "run.command") {
@@ -416,23 +303,6 @@ const formatRunEvent = (event, width, tools) => {
416
303
  }
417
304
  return formatBusEvent(event).map((line) => `${tag(event.node)} ${line}`);
418
305
  };
419
- const RUN_MARK = {
420
- succeeded: style("green", "✓"),
421
- failed: style("red", "✗"),
422
- budget_exhausted: style("yellow", "!"),
423
- quota_exhausted: style("yellow", "!"),
424
- timed_out: style("yellow", "!"),
425
- interrupted: style("yellow", "!")
426
- };
427
- /** The run summary: status, cost, what ran, branches to look at, and why. */
428
- const formatRunResult = (result) => {
429
- const ran = result.nodes.filter((n) => n.runs > 0).length;
430
- const skipped = result.nodes.length - ran;
431
- const lines = ["", `${RUN_MARK[result.status]} run ${result.runId} ${result.status.replace("_", " ")} ${style("dim", `· ${cost(result.costUsd)} · ${String(ran)} ran, ${String(skipped)} skipped`)}`];
432
- for (const kept of result.worktrees) lines.push(` ${kept.id}: ${kept.branch ?? "detached"} ${style("dim", `at ${kept.path} (${kept.reason})`)}`);
433
- for (const reason of result.reasons) lines.push(` ${style("red", reason)}`);
434
- return lines;
435
- };
436
306
  // src/runs/format.ts
437
307
  /**
438
308
  * @file How stored runs read on the terminal: the `ork ps` table and the `ork
@@ -466,7 +336,10 @@ const duration = (ms) => {
466
336
  if (s < 3600) return `${String(Math.floor(s / 60))}m${String(s % 60).padStart(2, "0")}s`;
467
337
  return `${String(Math.floor(s / 3600))}h${String(Math.floor(s % 3600 / 60)).padStart(2, "0")}m`;
468
338
  };
469
- /** The `ork ps` table; `withProject` adds each run's project. */
339
+ /**
340
+ * The `ork ps` table; `withProject` adds each run's project. A called run's
341
+ * cost is dimmed and says its caller's, whose cost already counts it.
342
+ */
470
343
  const formatRuns = (runs, withProject, now = Date.now(), statusOf = liveStatus) => {
471
344
  if (runs.length === 0) return ["No runs."];
472
345
  const rows = runs.map((run) => {
@@ -477,7 +350,8 @@ const formatRuns = (runs, withProject, now = Date.now(), statusOf = liveStatus)
477
350
  workflow: run.workflow,
478
351
  status,
479
352
  when: `${ago(run.startedAt, now)} · ${duration(end - Date.parse(run.startedAt))}`,
480
- cost: cost(run.costUsd),
353
+ cost: run.parent === void 0 ? cost(run.costUsd) : `${cost(run.costUsd)} in ${run.parent}`,
354
+ called: run.parent !== void 0,
481
355
  project: run.project
482
356
  };
483
357
  });
@@ -502,7 +376,7 @@ const formatRuns = (runs, withProject, now = Date.now(), statusOf = liveStatus)
502
376
  r.workflow.padEnd(w.workflow),
503
377
  style(STATUS_STYLE[r.status] ?? "reset", r.status.padEnd(w.status)),
504
378
  style("dim", r.when.padEnd(w.when)),
505
- r.cost.padStart(w.cost),
379
+ r.called ? style("dim", r.cost.padStart(w.cost)) : r.cost.padStart(w.cost),
506
380
  ...withProject ? [style("dim", r.project)] : []
507
381
  ].join(" "))];
508
382
  };
@@ -550,4 +424,4 @@ const formatTree = (roots) => {
550
424
  return `${text.padEnd(label)} ${style("dim", node.type.padEnd(type))} ${state}${usage}`;
551
425
  });
552
426
  };
553
- export { formatRunEvent as a, createdWorktrees as c, formatEvent as i, listOrkWorktrees as l, formatRuns as n, createTerminalHuman as o, formatTree as r, applyClean as s, ago as t, planClean as u };
427
+ export { formatRunEvent as a, formatEvent as i, formatRuns as n, createTerminalHuman as o, formatTree as r, ago as t };