@krapht/ork 1.0.0-alpha.7 → 1.0.0-alpha.9

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 (33) hide show
  1. package/dist/{__build_info__-BGoaUwLd.mjs → __build_info__-y1TPvZvp.mjs} +2 -2
  2. package/dist/bin.mjs +598 -51
  3. package/dist/{clean-BPITM-6p.mjs → clean-BRUXyk87.mjs} +5 -2
  4. package/dist/{completion-CCn8xWmO.mjs → completion-cpZHtv50.mjs} +5 -5
  5. package/dist/{context-CZNM454F.mjs → context-Dbay8l7P.mjs} +8 -7
  6. package/dist/{decide-DsA6kOT7.mjs → decide-BphP3ONM.mjs} +1 -1
  7. package/dist/{find-ntyniv2E.mjs → find-BXZjliwr.mjs} +1 -1
  8. package/dist/format-DeT7vL-8.mjs +191 -0
  9. package/dist/{in-a-run-D_fnj1Mw.mjs → in-a-run-C4E9HfED.mjs} +1 -1
  10. package/dist/{limits-BKzVMwkt.mjs → limits-CBeoYrUH.mjs} +1 -1
  11. package/dist/{list-D9HPWLPH.mjs → list-BmxgNyd3.mjs} +11 -4
  12. package/dist/{mcp-BFl3cIDp.mjs → mcp-DTreb7Nq.mjs} +6 -6
  13. package/dist/{output-CwqlG1Le.mjs → output-BVkI6ZVv.mjs} +1 -1
  14. package/dist/{pause-DhgKQORW.mjs → pause-h66SC4rV.mjs} +4 -4
  15. package/dist/{relay-Cp8JGrdv.mjs → relay-BxRhxL6n.mjs} +2 -2
  16. package/dist/{remove-CSpT_nJH.mjs → remove-CYGd9Csj.mjs} +14 -7
  17. package/dist/{send-CHiYkM6X.mjs → send-BskOwN4p.mjs} +3 -3
  18. package/dist/{src-BsLHKQRc.mjs → src-BwFxTank.mjs} +335 -107
  19. package/dist/{src-CUeMUVwW.mjs → src-W1i5Bl6N.mjs} +23 -9
  20. package/dist/{src-DhGLtYR-.mjs → src-bOImrq43.mjs} +30 -11
  21. package/dist/{src-BwnitlJh.mjs → src-xd9DbQw8.mjs} +8 -8
  22. package/dist/ui/assets/{app-D4CoH-lh.js → app-BMg0OOv4.js} +19 -19
  23. package/dist/ui/assets/app-C8uhHVYp.css +1 -0
  24. package/dist/ui/assets/{index-DxDGdEwz.js → index-DY1q4SDl.js} +2 -2
  25. package/dist/ui/index.html +1 -1
  26. package/dist/{ui-BxE0L7V0.mjs → ui-B46Z3e9E.mjs} +4 -4
  27. package/dist/{usage-BuzDsEhq.mjs → usage-DpHXbQEu.mjs} +5 -5
  28. package/dist/{watch-CCj0vU3B.mjs → watch-CJO1ee6Y.mjs} +6 -6
  29. package/dist/{workflows-BNCDD6J-.mjs → workflows-BJdSHnkr.mjs} +4 -3
  30. package/dist/{worktrees-DBBcaimX.mjs → worktrees-Ccf2lUhE.mjs} +72 -19
  31. package/package.json +1 -1
  32. package/dist/format-DaROJUgG.mjs +0 -662
  33. package/dist/ui/assets/app-Dt_tjFj6.css +0 -1
@@ -1,4 +1,4 @@
1
- import { A as gitOk, O as removeWorktree } from "./src-BsLHKQRc.mjs";
1
+ import { A as gitOk, O as removeWorktree } from "./src-BwFxTank.mjs";
2
2
  // ../core/src/worktree/clean.ts
3
3
  /**
4
4
  * @file What `ork worktrees clean` and `ork worktrees remove` do with what
@@ -11,6 +11,7 @@ const keyOf = (item) => item.branch ?? item.path ?? `${item.runId}/${item.nodeId
11
11
  /** The reasons no `force` overrides: removing would break something. */
12
12
  const MAIN_CHECKOUT = "checked out in the main checkout";
13
13
  const RUN_GOING = "its run is still going";
14
+ const NOT_ORKS = "not created by Ork";
14
15
  const kept = (item, skip) => ({
15
16
  item,
16
17
  removeWorktree: false,
@@ -19,6 +20,7 @@ const kept = (item, skip) => ({
19
20
  });
20
21
  /** What clean does with one item. */
21
22
  const cleanStep = (item, options) => {
23
+ if (!item.recorded) return kept(item, NOT_ORKS);
22
24
  if (item.inMainCheckout) return kept(item, MAIN_CHECKOUT);
23
25
  if (item.heldBy !== void 0) return kept(item, `checked out in ${item.heldBy}`);
24
26
  if (options.live?.has(item.runId) === true) return kept(item, RUN_GOING);
@@ -45,7 +47,8 @@ const stepActs = (step) => step.removeWorktree || step.deleteBranch || stepPrune
45
47
  * What `ork worktrees clean` would do. A worktree with uncommitted changes, an
46
48
  * unmerged branch, or a detached worktree holding commits no branch has, is
47
49
  * kept unless `force`; one checked out in the main checkout or in a worktree
48
- * Ork didn't create, or whose run is still going, is kept even then.
50
+ * Ork didn't create, whose run is still going, or an `ork/` branch no run
51
+ * recorded creating, is kept even then.
49
52
  */
50
53
  const planClean = (items, filter, options) => items.filter((item) => filter.runId === void 0 || item.runId === filter.runId).filter((item) => filter.key === void 0 || keyOf(item) === filter.key).filter((item) => !filter.merged || item.merged).map((item) => cleanStep(item, options));
51
54
  /**
@@ -1,9 +1,9 @@
1
- import { Cn as placeFiles, bt as liveStatus, cr as CLAUDE_DRIVERS, tr as NodeIdSchema, wn as placeFor } from "./src-BsLHKQRc.mjs";
2
- import { a as builtinWorkflowNames, n as pendingDecisions, o as builtinWorkflowText, s as isBuiltinArg } from "./decide-DsA6kOT7.mjs";
3
- import { t as findRun } from "./find-ntyniv2E.mjs";
4
- import { c as openStore, l as storePath, o as writeErr } from "./output-CwqlG1Le.mjs";
1
+ import { En as placeFor, Tn as placeFiles, bt as liveStatus, rr as NodeIdSchema, ur as CLAUDE_DRIVERS } from "./src-BwFxTank.mjs";
2
+ import { a as builtinWorkflowNames, n as pendingDecisions, o as builtinWorkflowText, s as isBuiltinArg } from "./decide-BphP3ONM.mjs";
3
+ import { t as findRun } from "./find-BXZjliwr.mjs";
4
+ import { c as openStore, l as storePath, o as writeErr } from "./output-BVkI6ZVv.mjs";
5
5
  import { a as findEntry, c as subcommandsOf, d as SHELL_SCRIPTS, l as COMPLETE_COMMAND, n as groupedEntries, r as argInfoOf, t as groupOf, u as SHELLS } from "./help-groups-BwYoAWsh.mjs";
6
- import { a as resolveWorkflowArg, n as WORKFLOW_SUFFIX } from "./workflows-BNCDD6J-.mjs";
6
+ import { a as resolveWorkflowArg, n as WORKFLOW_SUFFIX } from "./workflows-BJdSHnkr.mjs";
7
7
  import path from "node:path";
8
8
  import { parse } from "yaml";
9
9
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
@@ -1,6 +1,6 @@
1
- import { C as workflowTextHash, Cn as placeFiles, On as workflowDirOf, S as readProjectTriggers, ht as walkSummary, jn as LESSON_CAP_TOKENS, mt as walkChecks, pt as checkWorkflowText, st as explainSession, wn as placeFor } from "./src-BsLHKQRc.mjs";
2
- import { a as write, i as style, r as plain } from "./output-CwqlG1Le.mjs";
3
- import { c as registeredProjects, i as refusedBuiltin, o as workflowArg, r as findWorkflowFiles, s as projectAt } from "./workflows-BNCDD6J-.mjs";
1
+ import { An as workflowDirOf, C as workflowTextHash, En as placeFor, Nn as LESSON_CAP_TOKENS, S as readProjectTriggers, Tn as placeFiles, ht as walkSummary, mt as walkChecks, pt as checkWorkflowText, st as explainSession } from "./src-BwFxTank.mjs";
2
+ import { a as write, i as style, r as plain } from "./output-BVkI6ZVv.mjs";
3
+ import { c as registeredProjects, i as refusedBuiltin, o as workflowArg, r as findWorkflowFiles, s as projectAt } from "./workflows-BJdSHnkr.mjs";
4
4
  import path from "node:path";
5
5
  import { readFileSync } from "node:fs";
6
6
  import { defineCommand } from "citty";
@@ -10,7 +10,8 @@ import { defineCommand } from "citty";
10
10
  * and with `--paths`, each way a run can end. A gate's answer that ends the
11
11
  * run shows here as an ending, not as a problem.
12
12
  */
13
- const walkLine = (walk) => walk === void 0 ? [] : [style("dim", walkSummary(walk))];
13
+ /** The walk's one line; unless `--paths` is on, it points there for each ending. */
14
+ const walkLine = (walk, showsPaths = false) => walk === void 0 ? [] : [style("dim", showsPaths ? walkSummary(walk) : `${walkSummary(walk)} See each with \`ork validate --paths\`.`)];
14
15
  const endingLine = ({ status, notRun, paths }) => {
15
16
  const ways = `${String(paths)} ${paths === 1 ? "way" : "ways"}`;
16
17
  const word = status === "succeeded" ? style("green", status) : style("red", status);
@@ -109,7 +110,7 @@ const diagnosticLine = (file, d) => {
109
110
  /** The warnings alone, for a run that goes ahead despite them. */
110
111
  const warningLines = (report) => report.diagnostics.filter((d) => d.severity === "warning").map((d) => diagnosticLine(report.file, d));
111
112
  /** Human-readable report, one line per diagnostic, compiler style. */
112
- const formatReport = (report) => {
113
+ const formatReport = (report, showsPaths = false) => {
113
114
  const lines = report.diagnostics.map((d) => diagnosticLine(report.file, d));
114
115
  const errors = report.diagnostics.filter((d) => d.severity === "error").length;
115
116
  const warnings = report.diagnostics.length - errors;
@@ -119,7 +120,7 @@ const formatReport = (report) => {
119
120
  const extra = warnings > 0 ? `, ${plural(warnings, "warning")}` : "";
120
121
  lines.push(`${style("green", "✓")} ${report.file} ${style("dim", `(${plural(nodes, "node")}, ${plural(edges, "edge")}, starts at ${entries.join(", ")}${extra})`)}`);
121
122
  } else lines.push(`${style("red", "✗")} ${report.file} ${style("dim", `(${plural(errors, "error")}, ${plural(warnings, "warning")})`)}`);
122
- return [...lines, ...walkLine(report.walk)];
123
+ return [...lines, ...walkLine(report.walk, showsPaths)];
123
124
  };
124
125
  const validate = defineCommand({
125
126
  meta: {
@@ -165,7 +166,7 @@ const validate = defineCommand({
165
166
  }] : []];
166
167
  if (args.json) write(JSON.stringify(reports, void 0, 2));
167
168
  else for (const report of reports) {
168
- for (const line of formatReport(report)) write(line);
169
+ for (const line of formatReport(report, args.paths)) write(line);
169
170
  if (args.paths) for (const line of pathLines(report.walk)) write(line);
170
171
  }
171
172
  if (reports.some((r) => hasErrors(r.diagnostics))) process.exitCode = 1;
@@ -1,4 +1,4 @@
1
- import { Cn as placeFiles, Fn as writePlainFile, Nn as guardedBase, Pn as plainPathInside, bt as liveStatus, in as socketPath, on as wakeRun } from "./src-BsLHKQRc.mjs";
1
+ import { Fn as guardedBase, In as plainPathInside, Ln as writePlainFile, Tn as placeFiles, bt as liveStatus, cn as wakeRun, on as socketPath } from "./src-BwFxTank.mjs";
2
2
  import path from "node:path";
3
3
  import { existsSync, readFileSync } from "node:fs";
4
4
  import { createHash } from "node:crypto";
@@ -1,4 +1,4 @@
1
- import { wn as placeFor } from "./src-BsLHKQRc.mjs";
1
+ import { En as placeFor } from "./src-BwFxTank.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  // ../core/src/remote/find.ts
4
4
  /**
@@ -0,0 +1,191 @@
1
+ import { Ot as isItem, bt as liveStatus } from "./src-BwFxTank.mjs";
2
+ import { i as style, n as cost } from "./output-BVkI6ZVv.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-CUeMUVwW.mjs";
1
+ import { p as runMarkerIn } from "./src-W1i5Bl6N.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-BsLHKQRc.mjs";
1
+ import { a as limitsNow, i as NOT_STARTED, o as limitsReport, r as capsHere } from "./src-BwFxTank.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-BsLHKQRc.mjs";
1
+ import { A as gitOk, j as lines, k as git } from "./src-BwFxTank.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-BsLHKQRc.mjs";
2
- import { n as ORK_VERSION } from "./__build_info__-BGoaUwLd.mjs";
3
- import { p as runMarkerIn } from "./src-CUeMUVwW.mjs";
4
- import { n as explainNode } from "./context-CZNM454F.mjs";
5
- import { a as openMcp, c as refused, o as tool, s as answer } from "./src-BwnitlJh.mjs";
1
+ import { h as isNewer } from "./src-BwFxTank.mjs";
2
+ import { n as ORK_VERSION } from "./__build_info__-y1TPvZvp.mjs";
3
+ import { p as runMarkerIn } from "./src-W1i5Bl6N.mjs";
4
+ import { n as explainNode } from "./context-Dbay8l7P.mjs";
5
+ import { a as openMcp, c as refused, o as tool, s as answer } from "./src-xd9DbQw8.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.7") ? tooOld(all, options.expects) : all;
46
+ const tools = options.expects !== void 0 && isNewer(options.expects, "1.0.0-alpha.9") ? tooOld(all, options.expects) : all;
47
47
  const server = new McpServer({
48
48
  name: "ork",
49
49
  version: ORK_VERSION
@@ -1,4 +1,4 @@
1
- import { Ht as FEEDBACK_SEVERITIES, J as backfillUsage, Q as text, Tt as ownProcessStart, Ut as FEEDBACK_TARGETS, Vt as FEEDBACK_KINDS, X as eventOf, Y as usageQueries, Z as inTransaction, cn as maskSecrets, sn as maskReplacer, yn as PROJECT_MODES } from "./src-BsLHKQRc.mjs";
1
+ import { Gt as FEEDBACK_TARGETS, J as backfillUsage, Q as text, Tt as ownProcessStart, Ut as FEEDBACK_KINDS, Wt as FEEDBACK_SEVERITIES, X as eventOf, Y as usageQueries, Z as inTransaction, ln as maskReplacer, un as maskSecrets, xn as PROJECT_MODES } from "./src-BwFxTank.mjs";
2
2
  import path from "node:path";
3
3
  import { mkdirSync } from "node:fs";
4
4
  import { homedir } from "node:os";
@@ -1,7 +1,7 @@
1
- import { G as requestPause, W as requestContinue } from "./src-BsLHKQRc.mjs";
2
- import { t as findRun } from "./find-ntyniv2E.mjs";
3
- import { a as write, c as openStore, i as style, o as writeErr } from "./output-CwqlG1Le.mjs";
4
- import { r as continueRefusal } from "./in-a-run-D_fnj1Mw.mjs";
1
+ import { G as requestPause, W as requestContinue } from "./src-BwFxTank.mjs";
2
+ import { t as findRun } from "./find-BXZjliwr.mjs";
3
+ import { a as write, c as openStore, i as style, o as writeErr } from "./output-BVkI6ZVv.mjs";
4
+ import { r as continueRefusal } from "./in-a-run-C4E9HfED.mjs";
5
5
  import { defineCommand } from "citty";
6
6
  // src/commands/pause.ts
7
7
  /**
@@ -1,5 +1,5 @@
1
- import { H as unlinkNodes, I as addressOf, z as linkNodes } from "./src-BsLHKQRc.mjs";
2
- import { a as write, c as openStore, i as style, o as writeErr } from "./output-CwqlG1Le.mjs";
1
+ import { H as unlinkNodes, I as addressOf, z as linkNodes } from "./src-BwFxTank.mjs";
2
+ import { a as write, c as openStore, i as style, o as writeErr } from "./output-BVkI6ZVv.mjs";
3
3
  import { defineCommand } from "citty";
4
4
  // src/commands/relay.ts
5
5
  /**
@@ -1,6 +1,6 @@
1
- import { bt as liveStatus, j as lines, k as git } from "./src-BsLHKQRc.mjs";
2
- import { n as listOrkWorktrees, t as createdWorktrees } from "./list-D9HPWLPH.mjs";
3
- import { i as keyOf, n as applyStep, r as cleanStep, s as stepPrunes } from "./clean-BPITM-6p.mjs";
1
+ import { bt as liveStatus, j as lines, k as git } from "./src-BwFxTank.mjs";
2
+ import { n as listOrkWorktrees, t as createdWorktrees } from "./list-BmxgNyd3.mjs";
3
+ import { i as keyOf, n as applyStep, r as cleanStep, s as stepPrunes } from "./clean-BRUXyk87.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 { bt as liveStatus, in as socketPath, kt as scriptContactsOf, on as wakeRun } from "./src-BsLHKQRc.mjs";
2
- import { c as openStore, o as writeErr } from "./output-CwqlG1Le.mjs";
3
- import { u as inheritedScrubber } from "./src-CUeMUVwW.mjs";
1
+ import { bt as liveStatus, cn as wakeRun, jt as scriptContactsOf, on as socketPath } from "./src-BwFxTank.mjs";
2
+ import { c as openStore, o as writeErr } from "./output-BVkI6ZVv.mjs";
3
+ import { u as inheritedScrubber } from "./src-W1i5Bl6N.mjs";
4
4
  import { defineCommand } from "citty";
5
5
  // ../core/src/remote/send.ts
6
6
  /**