@krapht/ork 1.0.0-alpha.0 → 1.0.0-alpha.10

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 (41) hide show
  1. package/README.md +5 -6
  2. package/dist/{__build_info__-DdTVd2p5.mjs → __build_info__-DFF18K8y.mjs} +72 -3
  3. package/dist/bin.mjs +603 -384
  4. package/dist/{clean-NnboOuz6.mjs → clean-DVaEH9e7.mjs} +5 -2
  5. package/dist/{completion-snnYGjgK.mjs → completion-Dv5kf598.mjs} +6 -6
  6. package/dist/context-CEaKOVmG.mjs +683 -0
  7. package/dist/{decide-B81wCZQg.mjs → decide-3l9q8UtX.mjs} +3 -3
  8. package/dist/{find-BKW-uGey.mjs → find-DEjjOlTG.mjs} +1 -1
  9. package/dist/format-Cb5_m-Qd.mjs +191 -0
  10. package/dist/{in-a-run-BPXX09Bs.mjs → in-a-run-BS9Nc-JW.mjs} +1 -1
  11. package/dist/{limits-JCve2QpI.mjs → limits-BSwEjQST.mjs} +1 -1
  12. package/dist/{list-C7UEgmtd.mjs → list-Qxr6rwsY.mjs} +11 -4
  13. package/dist/{mcp-CKPyNaKD.mjs → mcp-CslnG5wf.mjs} +6 -6
  14. package/dist/output-Bqljtmp_.mjs +40 -0
  15. package/dist/{pause-B_SL_yDz.mjs → pause-D7OVB5Ac.mjs} +4 -4
  16. package/dist/{relay-o-Luee1c.mjs → relay-EceRIebO.mjs} +2 -2
  17. package/dist/{remove-DAZ7ZCzt.mjs → remove-0IQZVWmI.mjs} +14 -7
  18. package/dist/{send-3B64fBNB.mjs → send-C0uwfUYi.mjs} +3 -3
  19. package/dist/{src-DHvWU4tX.mjs → src-Df6KOIDC.mjs} +5682 -3248
  20. package/dist/{src-C38BPzit.mjs → src-cmmXKHn1.mjs} +266 -203
  21. package/dist/{src-BJTegEzY.mjs → src-i5k2Y-kf.mjs} +40 -88
  22. package/dist/src-oaJYIddp.mjs +2255 -0
  23. package/dist/ui/THIRD_PARTY_LICENSES.txt +594 -0
  24. package/dist/ui/assets/app-C8uhHVYp.css +1 -0
  25. package/dist/ui/assets/app-qs8Lofv6.js +75 -0
  26. package/dist/ui/assets/{index-D83F2OKZ.js → index-DV1ZaOk-.js} +3 -3
  27. package/dist/ui/assets/theme-CdXvqTQ6.js +1 -0
  28. package/dist/ui/index.html +2 -2
  29. package/dist/{ui-CY4xxJUu.mjs → ui-CBkHJdqk.mjs} +4 -4
  30. package/dist/{usage-CJnytoe4.mjs → usage-Cd8pEYyK.mjs} +5 -5
  31. package/dist/{watch-BJB82ZJ7.mjs → watch-DW5R2OPO.mjs} +6 -6
  32. package/dist/{workflows-CiM7S3l0.mjs → workflows-BT1gDRIV.mjs} +4 -3
  33. package/dist/{worktrees-86ySXxR6.mjs → worktrees-g1MQMGie.mjs} +78 -19
  34. package/package.json +2 -4
  35. package/dist/context-DO3sBy9v.mjs +0 -274
  36. package/dist/format-CmTlTOjT.mjs +0 -427
  37. package/dist/output-B1V6ZJSx.mjs +0 -950
  38. package/dist/src-Dyz_Gy1q.mjs +0 -1183
  39. package/dist/ui/assets/app-C8nJ6NpT.js +0 -67
  40. package/dist/ui/assets/app-CH7YC3in.css +0 -1
  41. package/dist/ui/assets/theme-3xH5jxKs.js +0 -1
@@ -1,4 +1,4 @@
1
- import { An as guardedBase, Mn as writePlainFile, an as wakeRun, bn as placeFiles, ht as liveStatus, jn as plainPathInside, rn as socketPath } from "./src-DHvWU4tX.mjs";
1
+ import { Fn as plainPathInside, In as writePlainFile, Pn as guardedBase, an as socketPath, bt as liveStatus, sn as wakeRun, wn as placeFiles } from "./src-Df6KOIDC.mjs";
2
2
  import path from "node:path";
3
3
  import { existsSync, readFileSync } from "node:fs";
4
4
  import { createHash } from "node:crypto";
@@ -69,7 +69,7 @@ const builtinWorkflowFile = (place, arg) => {
69
69
  * An answer goes to the store (the first one wins), then the run is woken on
70
70
  * its socket so it picks the answer up at once (specs/messaging.md).
71
71
  */
72
- /** Open decisions whose run is still live, oldest first. */
72
+ /** Open decisions whose run is still live, oldest first, ANDing the filter. */
73
73
  const pendingDecisions = (store, filter) => {
74
74
  const live = /* @__PURE__ */ new Map();
75
75
  return store.decisions({
@@ -78,7 +78,7 @@ const pendingDecisions = (store, filter) => {
78
78
  }).filter((decision) => {
79
79
  if (!live.has(decision.runId)) {
80
80
  const [run] = store.lookup(decision.runId);
81
- live.set(decision.runId, run !== void 0 && liveStatus(run) === "running");
81
+ live.set(decision.runId, run !== void 0 && liveStatus(run) === "running" && (filter.workflow === void 0 || run.workflow === filter.workflow));
82
82
  }
83
83
  return live.get(decision.runId) === true;
84
84
  });
@@ -1,4 +1,4 @@
1
- import { xn as placeFor } from "./src-DHvWU4tX.mjs";
1
+ import { Tn as placeFor } from "./src-Df6KOIDC.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  // ../core/src/remote/find.ts
4
4
  /**
@@ -0,0 +1,191 @@
1
+ import { Dt as isItem, bt as liveStatus } from "./src-Df6KOIDC.mjs";
2
+ import { i as style, n as cost } from "./output-Bqljtmp_.mjs";
3
+ // src/run/format-bus.ts
4
+ /** The first line of a message body, cut to fit one terminal line. */
5
+ const firstLine = (body) => {
6
+ const [line = ""] = body.trim().split("\n");
7
+ return line.length > 100 ? `${line.slice(0, 99)}…` : line;
8
+ };
9
+ /** Messages, owner questions and claims, without the node prefix. */
10
+ const formatBusEvent = (event) => {
11
+ if (event.type === "claim.taken") return [style("dim", `⚑ claimed ${event.resource}`)];
12
+ if (event.type === "claim.refused") {
13
+ const held = event.held === event.resource ? "" : ` (it holds ${event.held})`;
14
+ return [style("yellow", `⚑ ${event.resource} is ${event.holder}'s${held}`)];
15
+ }
16
+ if (event.type === "claim.released") return [style("dim", `⚑ released ${event.resource}`)];
17
+ if (event.type === "message.sent") {
18
+ const how = event.via === void 0 ? "" : ` (${event.via})`;
19
+ return [style("dim", `✉ to ${event.to}${how}: ${firstLine(event.body)}`)];
20
+ }
21
+ if (event.type === "message.received") {
22
+ const how = event.via === void 0 ? "" : ` (${event.via})`;
23
+ return [style("dim", `✉ from ${event.from}${how}: ${firstLine(event.body)}`)];
24
+ }
25
+ if (event.type === "call.started") return [style("dim", `↳ started ${event.workflow} as run ${event.run}`)];
26
+ if (event.type === "link.notice") return [style("dim", `↔ ${event.text}`)];
27
+ if (event.type === "message.read") return [];
28
+ if (event.type === "owner.asked") return [`${style(["yellow", "bold"], "?")} asked as the owner of ${event.about}: ${event.text} ${style("dim", `(${event.question})`)}`];
29
+ if (event.type === "owner.answered") return [style("dim", `→ ${event.answer} (owner)`)];
30
+ return [style("dim", "→ its session ended first; asking you")];
31
+ };
32
+ // src/run/items-ran.ts
33
+ /**
34
+ * @file How a fan-out's finished line counts its items: only the ones that
35
+ * started.
36
+ */
37
+ /** How many of a fan-out's items started; a skipped one never did. */
38
+ const itemsRan = (count, statuses) => {
39
+ const ran = Array.isArray(statuses) ? statuses.filter((status) => status !== "skipped").length : count;
40
+ return ran === count ? `${String(count)} ran` : `${String(ran)} of ${String(count)} ran`;
41
+ };
42
+ // src/run/verdict.ts
43
+ /**
44
+ * @file The one-line verdict of a finished node: its report's status, unless
45
+ * the node failed or its work did not merge.
46
+ */
47
+ /** Whether a finished node's work did not merge into its owner. */
48
+ const hasConflict = (output) => {
49
+ const { merge } = output;
50
+ return typeof merge === "object" && merge !== null && "ok" in merge && merge.ok === false;
51
+ };
52
+ /**
53
+ * A one-line verdict for a finished node, by what its type outputs. A node that
54
+ * failed reads `fail` even when its report said `pass` (a hook refused its
55
+ * commit); a child that did not merge says so.
56
+ */
57
+ const nodeVerdict = (output, ok = true, error) => {
58
+ const { status, summary, exitCode, choice, answeredBy, count } = output;
59
+ if (typeof status === "string") {
60
+ if (!ok && (status === "pass" || status === "done")) return error === void 0 ? "fail" : `fail: ${firstLine(error)}`;
61
+ const label = hasConflict(output) ? `${status}, merge conflict` : status;
62
+ return typeof summary === "string" && summary !== "" ? `${label}: ${summary}` : label;
63
+ }
64
+ if (typeof exitCode === "number") return `exit ${String(exitCode)}`;
65
+ if (typeof choice === "string") return `${choice} (${String(answeredBy)})`;
66
+ if (typeof count === "number") return itemsRan(count, output["statuses"]);
67
+ return "done";
68
+ };
69
+ // src/runs/format.ts
70
+ /**
71
+ * @file How stored runs read on the terminal: the `ork ps` table and the `ork
72
+ * tree` ownership tree with each node's state, tokens and cost, and what is
73
+ * paused (ADR-041).
74
+ */
75
+ const STATUS_STYLE = {
76
+ running: "cyan",
77
+ paused: "yellow",
78
+ succeeded: "green",
79
+ failed: "red",
80
+ crashed: "red",
81
+ budget_exhausted: "yellow",
82
+ quota_exhausted: "yellow",
83
+ timed_out: "yellow",
84
+ interrupted: "yellow"
85
+ };
86
+ /** `just now`, `5m ago`, `3h ago`, `2d ago`, then the date. */
87
+ const ago = (iso, now = Date.now()) => {
88
+ const minutes = Math.floor((now - Date.parse(iso)) / 6e4);
89
+ if (minutes < 1) return "just now";
90
+ if (minutes < 60) return `${String(minutes)}m ago`;
91
+ if (minutes < 1440) return `${String(Math.floor(minutes / 60))}h ago`;
92
+ if (minutes < 10080) return `${String(Math.floor(minutes / 1440))}d ago`;
93
+ return iso.slice(0, 10);
94
+ };
95
+ /** `42s`, `3m12s`, `1h05m`. */
96
+ const duration = (ms) => {
97
+ const s = Math.max(0, Math.round(ms / 1e3));
98
+ if (s < 60) return `${String(s)}s`;
99
+ if (s < 3600) return `${String(Math.floor(s / 60))}m${String(s % 60).padStart(2, "0")}s`;
100
+ return `${String(Math.floor(s / 3600))}h${String(Math.floor(s % 3600 / 60)).padStart(2, "0")}m`;
101
+ };
102
+ /**
103
+ * The `ork ps` table; `withProject` adds each run's project. A called run's
104
+ * cost is dimmed and says its caller's, whose cost already counts it.
105
+ */
106
+ const formatRuns = (runs, withProject, now = Date.now(), statusOf = liveStatus) => {
107
+ if (runs.length === 0) return ["No runs."];
108
+ const rows = runs.map((run) => {
109
+ const status = statusOf(run);
110
+ const end = run.finishedAt === void 0 ? now : Date.parse(run.finishedAt);
111
+ return {
112
+ id: run.id,
113
+ workflow: run.workflow,
114
+ status,
115
+ when: `${ago(run.startedAt, now)} · ${duration(end - Date.parse(run.startedAt))}`,
116
+ cost: run.parent === void 0 ? cost(run.costUsd) : `${cost(run.costUsd)} in ${run.parent}`,
117
+ called: run.parent !== void 0,
118
+ project: run.project
119
+ };
120
+ });
121
+ const width = (pick, title) => Math.max(title.length, ...rows.map((r) => pick(r).length));
122
+ const w = {
123
+ id: width((r) => r.id, "RUN"),
124
+ workflow: width((r) => r.workflow, "WORKFLOW"),
125
+ status: width((r) => r.status, "STATUS"),
126
+ when: width((r) => r.when, "STARTED"),
127
+ cost: width((r) => r.cost, "COST")
128
+ };
129
+ const header = [
130
+ "RUN".padEnd(w.id),
131
+ "WORKFLOW".padEnd(w.workflow),
132
+ "STATUS".padEnd(w.status),
133
+ "STARTED".padEnd(w.when),
134
+ "COST".padStart(w.cost),
135
+ ...withProject ? ["PROJECT"] : []
136
+ ].join(" ");
137
+ return [style("dim", header), ...rows.map((r) => [
138
+ style("bold", r.id.padEnd(w.id)),
139
+ r.workflow.padEnd(w.workflow),
140
+ style(STATUS_STYLE[r.status] ?? "reset", r.status.padEnd(w.status)),
141
+ style("dim", r.when.padEnd(w.when)),
142
+ r.called ? style("dim", r.cost.padStart(w.cost)) : r.cost.padStart(w.cost),
143
+ ...withProject ? [style("dim", r.project)] : []
144
+ ].join(" "))];
145
+ };
146
+ const STATE_MARK = {
147
+ waiting: style("dim", "·"),
148
+ running: style("cyan", "●"),
149
+ ok: style("green", "✓"),
150
+ failed: style("red", "✗"),
151
+ skipped: style("dim", "-")
152
+ };
153
+ /** `950`, `12.3k`, `1.2M`. */
154
+ const count = (n) => {
155
+ if (n < 1e3) return String(n);
156
+ if (n < 1e6) return `${(n / 1e3).toFixed(1)}k`;
157
+ return `${(n / 1e6).toFixed(1)}M`;
158
+ };
159
+ const stateText = (node) => {
160
+ if (node.state === "running") return node.runs > 1 ? `running (round ${String(node.runs)})` : "running";
161
+ if (node.state === "ok" || node.state === "failed") {
162
+ const verdict = nodeVerdict(node.output ?? {}, node.state === "ok");
163
+ const line = verdict.length > 60 ? `${verdict.slice(0, 59)}…` : verdict;
164
+ return node.runs > 1 ? `${line} (${String(node.runs)} runs)` : line;
165
+ }
166
+ return node.state;
167
+ };
168
+ /** The ownership tree, one line per node, children under their owner. */
169
+ const formatTree = (roots, now = Date.now()) => {
170
+ const lines = [];
171
+ const walk = (node, lead, branch) => {
172
+ lines.push({
173
+ label: `${lead}${branch}${node.id}`,
174
+ node
175
+ });
176
+ const next = lead + (branch === "├─ " ? "│ " : branch === "" ? "" : " ");
177
+ node.children.forEach((child, index) => {
178
+ walk(child, next, index === node.children.length - 1 ? "└─ " : "├─ ");
179
+ });
180
+ };
181
+ for (const root of roots) walk(root, "", "");
182
+ const label = Math.max(...lines.map((l) => l.label.length));
183
+ const type = Math.max(...lines.map((l) => l.node.type.length));
184
+ return lines.map(({ label: text, node }) => {
185
+ const usage = node.inputTokens + node.outputTokens === 0 ? "" : style("dim", ` ${count(node.inputTokens)} in / ${count(node.outputTokens)} out · ${cost(node.costUsd)}`);
186
+ const state = node.held === true ? `${style("yellow", "‖")} paused` : `${STATE_MARK[node.state]} ${stateText(node)}`;
187
+ const spent = isItem(node) && node.startedAt !== void 0 ? style("dim", ` ${duration((node.finishedAt === void 0 ? now : Date.parse(node.finishedAt)) - Date.parse(node.startedAt))}`) : "";
188
+ return `${text.padEnd(label)} ${style("dim", node.type.padEnd(type))} ${state}${spent}${usage}`;
189
+ });
190
+ };
191
+ export { nodeVerdict as a, hasConflict as i, formatRuns as n, firstLine as o, formatTree as r, formatBusEvent as s, ago as t };
@@ -1,4 +1,4 @@
1
- import { p as runMarkerIn } from "./src-C38BPzit.mjs";
1
+ import { p as runMarkerIn } from "./src-cmmXKHn1.mjs";
2
2
  // src/commands/in-a-run.ts
3
3
  /**
4
4
  * @file The commands that act as the user refuse a program or an agent in a run
@@ -1,4 +1,4 @@
1
- import { a as limitsNow, i as NOT_STARTED, o as limitsReport, r as capsHere } from "./src-DHvWU4tX.mjs";
1
+ import { a as limitsNow, i as NOT_STARTED, o as limitsReport, r as capsHere } from "./src-Df6KOIDC.mjs";
2
2
  // src/run/limits.ts
3
3
  /**
4
4
  * @file The limits across runs that apply here (RFC-007): the user's settings
@@ -1,4 +1,4 @@
1
- import { A as gitOk, j as lines, k as git } from "./src-DHvWU4tX.mjs";
1
+ import { M as git, N as gitOk, P as lines } from "./src-Df6KOIDC.mjs";
2
2
  import { existsSync } from "node:fs";
3
3
  // ../core/src/worktree/list.ts
4
4
  /**
@@ -56,7 +56,7 @@ const onNoBranch = async (entry) => entry === void 0 ? "0" : (await git(entry.pa
56
56
  "--remotes"
57
57
  ])).stdout;
58
58
  /** One created worktree or branch, with its state read from git. */
59
- const stateOf = async (root, entries, item) => {
59
+ const stateOf = async (root, entries, item, recorded) => {
60
60
  const { branch } = item;
61
61
  const entry = branch === void 0 || item.path !== "" ? entries.find((e) => e.path === item.path) : entries.find((e) => e.branch === branch);
62
62
  const holder = branch === void 0 ? void 0 : entries.find((e) => e.branch === branch && e.path !== entry?.path);
@@ -76,6 +76,7 @@ const stateOf = async (root, entries, item) => {
76
76
  ...checkedOut === void 0 ? {} : { checkedOut },
77
77
  runId: item.runId,
78
78
  nodeId: item.nodeId,
79
+ recorded,
79
80
  path: entry?.path ?? null,
80
81
  missing,
81
82
  dirty: changes > 0,
@@ -104,7 +105,13 @@ const listOrkWorktrees = async (root, created = []) => {
104
105
  "refs/heads/"
105
106
  ]));
106
107
  const recordedBranches = new Set(created.flatMap((c) => c.branch ?? []));
107
- 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))];
108
- return Promise.all(items.map((item) => stateOf(root, entries, item)));
108
+ const items = [...created.filter((c) => c.branch === void 0 ? entries.some((e) => e.path === c.path) : branches.includes(c.branch)).map((item) => ({
109
+ item,
110
+ recorded: true
111
+ })), ...branches.filter((b) => b.startsWith("ork/") && !recordedBranches.has(b)).map((b) => ({
112
+ item: prefixed(b),
113
+ recorded: false
114
+ }))];
115
+ return Promise.all(items.map(({ item, recorded }) => stateOf(root, entries, item, recorded)));
109
116
  };
110
117
  export { listOrkWorktrees as n, createdWorktrees as t };
@@ -1,8 +1,8 @@
1
- import { h as isNewer } from "./src-DHvWU4tX.mjs";
2
- import { n as ORK_VERSION } from "./__build_info__-DdTVd2p5.mjs";
3
- import { a as openMcp, c as refused, o as tool, s as answer } from "./src-BJTegEzY.mjs";
4
- import { n as explainNode } from "./context-DO3sBy9v.mjs";
5
- import { p as runMarkerIn } from "./src-C38BPzit.mjs";
1
+ import { h as isNewer } from "./src-Df6KOIDC.mjs";
2
+ import { n as ORK_VERSION } from "./__build_info__-DFF18K8y.mjs";
3
+ import { p as runMarkerIn } from "./src-cmmXKHn1.mjs";
4
+ import { n as explainNode } from "./context-CEaKOVmG.mjs";
5
+ import { a as openMcp, c as refused, o as tool, s as answer } from "./src-i5k2Y-kf.mjs";
6
6
  import { z } from "zod";
7
7
  import { defineCommand } from "citty";
8
8
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -43,7 +43,7 @@ const orkMcpServer = (options = {}) => {
43
43
  ...options.script === void 0 ? {} : { script: options.script }
44
44
  });
45
45
  const all = [...opened.tools, explainTool(opened, options.home)];
46
- const tools = options.expects !== void 0 && isNewer(options.expects, "1.0.0-alpha.0") ? tooOld(all, options.expects) : all;
46
+ const tools = options.expects !== void 0 && isNewer(options.expects, "1.0.0-alpha.10") ? tooOld(all, options.expects) : all;
47
47
  const server = new McpServer({
48
48
  name: "ork",
49
49
  version: ORK_VERSION
@@ -0,0 +1,40 @@
1
+ import { createInterface } from "node:readline/promises";
2
+ import { stripVTControlCharacters, styleText } from "node:util";
3
+ // src/output.ts
4
+ /**
5
+ * @file Terminal output helpers: styled text that turns plain when stdout is
6
+ * not a TTY or `NO_COLOR` is set, and a yes-or-no question on the terminal.
7
+ */
8
+ /** Styles text for stdout; plain when stdout is not a TTY or NO_COLOR is set. */
9
+ const style = (format, text) => styleText(format, text, { stream: process.stdout });
10
+ /**
11
+ * Text from outside, fit for a terminal: escape sequences could hide a line on
12
+ * the very screen a person judges from, and a line break could forge one.
13
+ */
14
+ const plain = (text) => stripVTControlCharacters(text).replaceAll(/[\p{Cc}\p{Bidi_Control}]/gu, " ");
15
+ const write = (text) => {
16
+ process.stdout.write(`${text}\n`);
17
+ };
18
+ const writeErr = (text) => {
19
+ process.stderr.write(`${text}\n`);
20
+ };
21
+ /** What a run spent: `$0.0312`, or `$0.00` for nothing. */
22
+ const cost = (usd) => usd === 0 ? "$0.00" : `$${usd.toFixed(4)}`;
23
+ /** Asks a yes-or-no question on the terminal; anything but yes is no. */
24
+ const confirm = async (question) => {
25
+ const rl = createInterface({
26
+ input: process.stdin,
27
+ output: process.stderr
28
+ });
29
+ const { promise: ended, resolve: end } = Promise.withResolvers();
30
+ rl.once("close", () => {
31
+ end("");
32
+ });
33
+ try {
34
+ const answer = await Promise.race([rl.question(question), ended]);
35
+ return ["y", "yes"].includes(answer.trim().toLowerCase());
36
+ } finally {
37
+ rl.close();
38
+ }
39
+ };
40
+ export { write as a, style as i, cost as n, writeErr as o, plain as r, confirm as t };
@@ -1,7 +1,7 @@
1
- import { G as requestContinue, K as requestPause } from "./src-DHvWU4tX.mjs";
2
- import { t as findRun } from "./find-BKW-uGey.mjs";
3
- import { a as write, c as openStore, i as style, o as writeErr } from "./output-B1V6ZJSx.mjs";
4
- import { r as continueRefusal } from "./in-a-run-BPXX09Bs.mjs";
1
+ import { D as openStore, X as requestPause, Y as requestContinue } from "./src-Df6KOIDC.mjs";
2
+ import { t as findRun } from "./find-DEjjOlTG.mjs";
3
+ import { a as write, i as style, o as writeErr } from "./output-Bqljtmp_.mjs";
4
+ import { r as continueRefusal } from "./in-a-run-BS9Nc-JW.mjs";
5
5
  import { defineCommand } from "citty";
6
6
  // src/commands/pause.ts
7
7
  /**
@@ -1,5 +1,5 @@
1
- import { B as linkNodes, L as addressOf, U as unlinkNodes } from "./src-DHvWU4tX.mjs";
2
- import { a as write, c as openStore, i as style, o as writeErr } from "./output-B1V6ZJSx.mjs";
1
+ import { D as openStore, V as addressOf, W as linkNodes, q as unlinkNodes } from "./src-Df6KOIDC.mjs";
2
+ import { a as write, i as style, o as writeErr } from "./output-Bqljtmp_.mjs";
3
3
  import { defineCommand } from "citty";
4
4
  // src/commands/relay.ts
5
5
  /**
@@ -1,6 +1,6 @@
1
- import { ht as liveStatus, j as lines, k as git } from "./src-DHvWU4tX.mjs";
2
- import { n as listOrkWorktrees, t as createdWorktrees } from "./list-C7UEgmtd.mjs";
3
- import { i as keyOf, n as applyStep, r as cleanStep, s as stepPrunes } from "./clean-NnboOuz6.mjs";
1
+ import { M as git, P as lines, bt as liveStatus } from "./src-Df6KOIDC.mjs";
2
+ import { n as listOrkWorktrees, t as createdWorktrees } from "./list-Qxr6rwsY.mjs";
3
+ import { i as keyOf, n as applyStep, r as cleanStep, s as stepPrunes } from "./clean-DVaEH9e7.mjs";
4
4
  // ../core/src/worktree/words.ts
5
5
  /**
6
6
  * @file The plain words for what `ork worktrees` shows, in one place, so the
@@ -45,6 +45,7 @@ const stateParts = (item, into) => {
45
45
  if (item.ahead > 0) part(`${counted(item.ahead, "commit")} on no branch`, true);
46
46
  else if (item.path !== null && !item.missing) part("nothing committed");
47
47
  }
48
+ if (!item.recorded) part("not created by Ork");
48
49
  if (item.inMainCheckout) part("checked out in the main checkout");
49
50
  if (item.heldBy !== void 0) part(`checked out in ${item.heldBy}`);
50
51
  return parts;
@@ -67,11 +68,12 @@ const planLine = (step) => {
67
68
  };
68
69
  };
69
70
  /**
70
- * Why an item can't be removed at all, even forced, if it can't: the main
71
- * checkout or a worktree Ork didn't create holds it, or its run is still
72
- * going.
71
+ * Why an item can't be removed at all, even forced, if it can't: no run
72
+ * recorded creating it, the main checkout or a worktree Ork didn't create holds
73
+ * it, or its run is still going.
73
74
  */
74
75
  const removeRefusal = (item, live) => {
76
+ if (!item.recorded) return "not created by Ork (no run recorded it); delete it with git if it's yours to delete";
75
77
  if (item.inMainCheckout) return "checked out in the main checkout; switch it to another branch first";
76
78
  if (item.heldBy !== void 0) return `checked out in ${item.heldBy}, a worktree Ork didn't create; switch it to another branch first`;
77
79
  return live ? `run ${item.runId} is still going; stop it first` : void 0;
@@ -101,6 +103,11 @@ const projectHead = async (root) => {
101
103
  branch: answer.code === 0 ? answer.stdout.trim() : null
102
104
  };
103
105
  };
106
+ /** The top of the repository `cwd` is in, even one with no commit yet. */
107
+ const repoRoot = async (cwd) => {
108
+ const top = await git(cwd, ["rev-parse", "--show-toplevel"]);
109
+ return top.code === 0 ? top.stdout.trim() : null;
110
+ };
104
111
  /** What Ork created in the repo at `root`, from its runs' records and git. */
105
112
  const readWorktrees = async (store, root) => ({
106
113
  items: await listOrkWorktrees(root, createdWorktrees(store, root)),
@@ -191,4 +198,4 @@ const applyRemove = async (root, { step, force }, done = []) => {
191
198
  await applyStep(root, step, force, done);
192
199
  return done;
193
200
  };
194
- export { readWorktrees as a, planLine as c, projectHead as i, removeRefusal as l, lostWork as n, labelOf as o, planRemove as r, mergeStateOf as s, applyRemove as t, stateParts as u };
201
+ export { readWorktrees as a, mergeStateOf as c, stateParts as d, projectHead as i, planLine as l, lostWork as n, repoRoot as o, planRemove as r, labelOf as s, applyRemove as t, removeRefusal as u };
@@ -1,6 +1,6 @@
1
- import { an as wakeRun, ht as liveStatus, kt as scriptContactsOf, rn as socketPath } from "./src-DHvWU4tX.mjs";
2
- import { c as openStore, o as writeErr } from "./output-B1V6ZJSx.mjs";
3
- import { u as inheritedScrubber } from "./src-C38BPzit.mjs";
1
+ import { At as scriptContactsOf, D as openStore, an as socketPath, bt as liveStatus, sn as wakeRun } from "./src-Df6KOIDC.mjs";
2
+ import { o as writeErr } from "./output-Bqljtmp_.mjs";
3
+ import { u as inheritedScrubber } from "./src-cmmXKHn1.mjs";
4
4
  import { defineCommand } from "citty";
5
5
  // ../core/src/remote/send.ts
6
6
  /**