@krapht/ork 1.0.0-alpha.15 → 1.0.0-alpha.16

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 (46) hide show
  1. package/README.md +10 -4
  2. package/dist/{__build_info__-Bsb1kjgF.mjs → __build_info__-DcRdK0Du.mjs} +25 -6
  3. package/dist/apply-Mb0fNpCX.mjs +17 -0
  4. package/dist/{attach-CPespxlQ.mjs → attach-7lB5_jpr.mjs} +5 -5
  5. package/dist/{attach-BHO3X3cf.mjs → attach-B-FeE1dJ.mjs} +46 -8
  6. package/dist/bin.mjs +238 -369
  7. package/dist/{brief-za0ozN_E.mjs → brief-BvMtIFio.mjs} +4 -4
  8. package/dist/{builtin-files-2bHf76x-.mjs → builtin-files-BRSOObAv.mjs} +1 -1
  9. package/dist/command-tree-BkSYWmYJ.mjs +63 -0
  10. package/dist/{completion-CQ2UYKuJ.mjs → completion-Cgb2qPPh.mjs} +45 -6
  11. package/dist/{context-BNqDw9z1.mjs → context-BN9A2Nnu.mjs} +3 -3
  12. package/dist/{find-BYLlszqj.mjs → find-mLYsR0uC.mjs} +2 -2
  13. package/dist/flags-CdgIH1L6.mjs +230 -0
  14. package/dist/{follow-ups-B4GJ3Vzw.mjs → follow-ups-DxCsOby1.mjs} +59 -24
  15. package/dist/{format-C8MsFj8_.mjs → format-hiDvtNGM.mjs} +17 -4
  16. package/dist/{help-groups-CQ98mLjF.mjs → help-groups-hB6kY34d.mjs} +1 -63
  17. package/dist/{in-a-run-Bp0k0J09.mjs → in-a-run-a447hxq8.mjs} +1 -1
  18. package/dist/{install-DWtXTq4y.mjs → install-23g_1ngs.mjs} +1 -1
  19. package/dist/{limits-B1qzS4cX.mjs → limits-C_Xz_t3C.mjs} +1 -1
  20. package/dist/{mcp-BOrVkyhN.mjs → mcp-CWng9I9n.mjs} +6 -6
  21. package/dist/{once-C1fL3GVo.mjs → once-lvwcWfVf.mjs} +3 -3
  22. package/dist/{pause-DOMK-R8V.mjs → pause-Cy11KrXK.mjs} +3 -3
  23. package/dist/{post-BzA6Qobq.mjs → post-C9soCx7i.mjs} +5 -3
  24. package/dist/print-BaWiWf4v.mjs +19 -0
  25. package/dist/project-records-BRiyQzUo.mjs +230 -0
  26. package/dist/{relay-OWuu4fib.mjs → relay-C0M1TWMB.mjs} +1 -1
  27. package/dist/{run--4-1xAF2.mjs → run-B-j3Qxqc.mjs} +26 -13
  28. package/dist/{run-CiPsW9Kf.mjs → run-D_u9V_Ve.mjs} +1 -1
  29. package/dist/{send-CnNNW-ot.mjs → send-qBxj0JbJ.mjs} +2 -2
  30. package/dist/{src-B9zWIVqo.mjs → src-CXBmaGwS.mjs} +93 -72
  31. package/dist/{src-DcrhgipG.mjs → src-DHDQ0itC.mjs} +91 -42
  32. package/dist/{src-Dr_fTSu-.mjs → src-DN-RPsil.mjs} +781 -502
  33. package/dist/{src-DQKz2eOH.mjs → src-nXLIwDrt.mjs} +9 -12
  34. package/dist/{store-oKJ-cCf3.mjs → store-Dyr1mP8K.mjs} +1 -1
  35. package/dist/ui/assets/app-BPSCxb_l.css +1 -0
  36. package/dist/ui/assets/{app-Drp8Dm9y.js → app-CnNRowFn.js} +40 -40
  37. package/dist/ui/assets/{index-BtwkftKe.js → index-CVzwYlz3.js} +2 -2
  38. package/dist/ui/index.html +1 -1
  39. package/dist/{ui-CzKlsCnm.mjs → ui-DLrSt5ny.mjs} +3 -3
  40. package/dist/{usage-DHGNk2JL.mjs → usage-CxmpYYO4.mjs} +61 -107
  41. package/dist/{validate-Bs_gg22g.mjs → validate-BVrT43GO.mjs} +35 -8
  42. package/dist/{watch-CCCfw3n7.mjs → watch-mkLm4t9D.mjs} +5 -5
  43. package/dist/{workflows-CW28ZU-z.mjs → workflows-Bvl2Fquu.mjs} +2 -2
  44. package/dist/{worktrees-CyE8aF6j.mjs → worktrees-DOOCoS2w.mjs} +49 -20
  45. package/package.json +8 -4
  46. package/dist/ui/assets/app-Bdc6OBeZ.css +0 -1
@@ -19,7 +19,7 @@
19
19
  href="/favicon.svg"
20
20
  type="image/svg+xml"
21
21
  />
22
- <script type="module" crossorigin src="/assets/index-BtwkftKe.js"></script>
22
+ <script type="module" crossorigin src="/assets/index-CVzwYlz3.js"></script>
23
23
  <link rel="modulepreload" crossorigin href="/assets/theme-CdXvqTQ6.js">
24
24
  <link rel="stylesheet" crossorigin href="/assets/index-BPIbqRRb.css">
25
25
  </head>
@@ -1,7 +1,7 @@
1
- import { n as ORK_VERSION, t as ORK_NAME } from "./__build_info__-Bsb1kjgF.mjs";
1
+ import { n as ORK_VERSION, t as ORK_NAME } from "./__build_info__-DcRdK0Du.mjs";
2
2
  import { c as write, l as writeErr, s as style } from "./output-B1kwo0YU.mjs";
3
- import { o as mayShowSignIn } from "./in-a-run-Bp0k0J09.mjs";
4
- import { i as uiFilePath, l as newToken, n as startServer, r as runningServer, t as DEFAULT_PORT } from "./src-DQKz2eOH.mjs";
3
+ import { o as mayShowSignIn } from "./in-a-run-a447hxq8.mjs";
4
+ import { i as uiFilePath, l as newToken, n as startServer, r as runningServer, t as DEFAULT_PORT } from "./src-nXLIwDrt.mjs";
5
5
  import path from "node:path";
6
6
  import { constants } from "node:os";
7
7
  import { spawn } from "node:child_process";
@@ -1,57 +1,11 @@
1
- import { Dr as placeFor, G as applyPrune, K as planPrune, S as usageReport, St as usd, U as openStore, x as USAGE_DIMENSIONS } from "./src-Dr_fTSu-.mjs";
1
+ import { B as planPrune, L as openStore, Pr as placeFor, Ut as usageListTable, g as usageReport, h as USAGE_DIMENSIONS, z as applyPrune } from "./src-DN-RPsil.mjs";
2
+ import { i as readListArgs, n as filterArgs, o as allAlias, r as projectScope, s as agoTime } from "./flags-CdgIH1L6.mjs";
2
3
  import { C as termOf, c as write, l as writeErr, n as cost, o as shortCount, r as costOf, s as style, t as confirm } from "./output-B1kwo0YU.mjs";
3
4
  import { n as chip, t as CHIP_LABELS } from "./chips-BqcT58qr.mjs";
4
- import { n as limitsHere } from "./limits-B1qzS4cX.mjs";
5
- import { a as header, o as table } from "./format-C8MsFj8_.mjs";
5
+ import { n as limitsHere } from "./limits-C_Xz_t3C.mjs";
6
+ import { a as header, o as table } from "./format-hiDvtNGM.mjs";
6
7
  import { defineCommand } from "citty";
7
8
  // src/runs/usage.ts
8
- /**
9
- * @file How usage reads on the terminal (`ork usage`, specs/usage.md), and the
10
- * times its `--since`, `--until` and `ork prune --older-than` take.
11
- */
12
- const UNITS = {
13
- h: 36e5,
14
- d: 864e5,
15
- w: 6048e5
16
- };
17
- /** `30d`, `12w`, `24h` before `now`, as an ISO time; `null` when it is not one. */
18
- const agoTime = (text, now) => {
19
- const match = /^(\d+)([hdw])$/u.exec(text);
20
- if (match === null) return null;
21
- const [, amount = "0", unit = "d"] = match;
22
- if (Number(amount) === 0) return null;
23
- const date = new Date(now - Number(amount) * (UNITS[unit] ?? 0));
24
- return Number.isNaN(date.getTime()) ? null : date.toISOString();
25
- };
26
- /** A `YYYY-MM-DD` day's start in the machine's time zone, `after` days on. */
27
- const dayTime = (text, after) => {
28
- const match = /^(\d{4})-(\d{2})-(\d{2})$/u.exec(text);
29
- if (match === null) return null;
30
- const [, year = "", month = "", day = ""] = match;
31
- const start = new Date(Number(year), Number(month) - 1, Number(day));
32
- if (start.getMonth() !== Number(month) - 1) return null;
33
- if (start.getDate() !== Number(day)) return null;
34
- start.setDate(start.getDate() + after);
35
- return start.toISOString();
36
- };
37
- /** The start of the day `days` whole days ago, counting today as one. */
38
- const wholeDays = (days, now) => {
39
- const today = new Date(now);
40
- return new Date(today.getFullYear(), today.getMonth(), today.getDate() - (days - 1)).toISOString();
41
- };
42
- /**
43
- * `--since`: an age or the day it starts from. Days and weeks are whole days,
44
- * today among them, as the Usage panel counts them; hours are exact.
45
- */
46
- const sinceTime = (text, now) => {
47
- const ago = agoTime(text, now);
48
- if (ago === null) return dayTime(text, 0);
49
- if (text.endsWith("h")) return ago;
50
- const amount = Number(text.slice(0, -1));
51
- return wholeDays(text.endsWith("w") ? amount * 7 : amount, now);
52
- };
53
- /** `--until`: the last day it includes. */
54
- const untilTime = (text) => dayTime(text, 1);
55
9
  /** `950`, `12.3k`, `4.5M`. */
56
10
  const tokens = shortCount;
57
11
  const percent = (share) => `${String(Math.round(share * 100))}%`;
@@ -87,7 +41,7 @@ const usageTable = (term, groups, cpu = false) => table(term, [
87
41
  { text: group.key },
88
42
  { text: cost(group.costUsd) },
89
43
  { text: String(group.sessions) },
90
- { text: `${tokens(group.inputTokens + group.cacheReadTokens + group.cacheWriteTokens)} in / ${tokens(group.outputTokens)} out` },
44
+ { text: `${tokens(group.inputTokens + group.cacheReadTokens + group.cacheWriteTokens)} prompt / ${tokens(group.outputTokens)} out` },
91
45
  { text: percent(group.cacheHitRate) },
92
46
  ...cpu ? [{ text: cpuText(group.cpuMs) }] : []
93
47
  ])], " ");
@@ -106,6 +60,15 @@ const shortTime = (iso) => new Date(iso).toLocaleString(void 0, {
106
60
  });
107
61
  /** A warning line: a `WARN` chip when rich, else the line in yellow. */
108
62
  const warned = (term, text) => term.rich ? ` ${chip(term, CHIP_LABELS.warn, "warn")} ${text}` : style("yellow", ` ${text}`);
63
+ /**
64
+ * `$0.0000 of $0.0100`: both sides of a dollar cap with the same digits, four
65
+ * when either is under a dollar, else cents (`usd` alone gives `$0.00 of
66
+ * $0.0100`).
67
+ */
68
+ const capAmounts = (spent, cap) => {
69
+ const places = cap < 1 || spent > 0 && spent < 1 ? 4 : 2;
70
+ return `$${spent.toFixed(places)} of $${cap.toFixed(places)}`;
71
+ };
109
72
  /** The limits across runs, above a report; nothing with none set. */
110
73
  const formatLimits = ({ problems, caps, tokenCaps = [], plan }, term = termOf(process.stdout)) => {
111
74
  if (problems.length === 0 && caps.length === 0 && tokenCaps.length === 0 && plan.length === 0) return [];
@@ -113,7 +76,7 @@ const formatLimits = ({ problems, caps, tokenCaps = [], plan }, term = termOf(pr
113
76
  style("bold", "Limits"),
114
77
  ...problems.map((problem) => warned(term, problem)),
115
78
  ...caps.map((cap) => {
116
- const line = ` ${cap.project === void 0 ? "all projects" : "this project"}, this ${cap.window}: ${usd(cap.spentUsd)} of ${usd(cap.capUsd)}, resets ${shortTime(cap.resetsAt)}`;
79
+ const line = ` ${cap.project === void 0 ? "all projects" : "this project"}, this ${cap.window}: ${capAmounts(cap.spentUsd, cap.capUsd)}, resets ${shortTime(cap.resetsAt)}`;
117
80
  return cap.leftUsd <= 0 ? warned(term, line.trimStart()) : line;
118
81
  }),
119
82
  ...tokenCaps.map((cap) => {
@@ -128,7 +91,7 @@ const formatLimits = ({ problems, caps, tokenCaps = [], plan }, term = termOf(pr
128
91
  const formatUsage = (report, scope, by, term = termOf(process.stdout)) => {
129
92
  const { totals } = report;
130
93
  if (totals.sessions === 0) return [`No Claude sessions in ${scope}.`];
131
- const lines = [`${style("bold", costOf(totals.costUsd, totals.partial, totals.tokens))} in ${scope}`, `${counted(totals.runs, "run")} · ${counted(totals.sessions, "session")} · ${counted(totals.turns, "turn")} · ${tokens(totals.inputTokens + totals.cacheReadTokens + totals.cacheWriteTokens)} in / ${tokens(totals.outputTokens)} out · ${percent(totals.cacheHitRate)} from cache`];
94
+ const lines = [`${style("bold", costOf(totals.costUsd, totals.partial, totals.tokens))} in ${scope}`, `${counted(totals.runs, "run")} · ${counted(totals.sessions, "session")} · ${counted(totals.turns, "turn")} · ${tokens(totals.inputTokens + totals.cacheReadTokens + totals.cacheWriteTokens)} prompt / ${tokens(totals.outputTokens)} out${totals.tokens === void 0 ? "" : ` (${tokens(totals.tokens)} counted)`} · ${percent(totals.cacheHitRate)} from cache`];
132
95
  for (const dimension of by === void 0 ? OVERVIEW : [by]) {
133
96
  const groups = report.groups[dimension];
134
97
  const shown = by === void 0 ? dimension === "day" ? groups.slice(-10) : groups.slice(0, OVERVIEW_ROWS) : groups;
@@ -163,67 +126,58 @@ const usage = defineCommand({
163
126
  name: "usage",
164
127
  description: "Show what this project's runs spent over time"
165
128
  },
166
- args: {
167
- all: {
168
- type: "boolean",
169
- description: "Every project's runs",
170
- default: false
171
- },
172
- "clear-plan-limits": {
173
- type: "boolean",
174
- description: "Forget what Claude Code last said of your plan's limits, for one that has lifted",
175
- default: false
176
- },
177
- since: {
178
- type: "string",
179
- description: "Whole days counting today (30d, 12w), exact hours (24h), a day (2026-11-01), or all",
180
- default: "30d"
181
- },
182
- until: {
183
- type: "string",
184
- description: "The last day to include"
185
- },
186
- by: {
187
- type: "string",
188
- description: USAGE_DIMENSIONS.join(", ")
189
- },
190
- workflow: {
191
- type: "string",
192
- description: "Only this workflow"
193
- },
194
- json: {
195
- type: "boolean",
196
- description: "Print JSON",
197
- default: false
129
+ args: filterArgs(usageListTable, {
130
+ ids: false,
131
+ limit: false,
132
+ extra: {
133
+ ...allAlias,
134
+ "clear-plan-limits": {
135
+ type: "boolean",
136
+ description: "Forget what Claude Code last said of your plan's limits, for one that has lifted",
137
+ default: false
138
+ },
139
+ by: {
140
+ type: "string",
141
+ description: USAGE_DIMENSIONS.join(", ")
142
+ }
198
143
  }
199
- },
144
+ }),
200
145
  run: ({ args }) => {
201
- const since = args.since === "all" ? void 0 : sinceTime(args.since, Date.now());
202
- const until = args.until === void 0 ? void 0 : untilTime(args.until);
203
- if (since === null) {
204
- refuse(`--since takes an age (30d), a day (2026-11-01) or all, not \`${args.since}\`.`);
205
- return;
206
- }
207
- if (until === null) {
208
- refuse(`--until takes a day (2026-11-01), not \`${String(args.until)}\`.`);
209
- return;
210
- }
211
- if (args.by !== void 0 && !isDimension(args.by)) {
212
- refuse(`--by takes one of ${USAGE_DIMENSIONS.join(", ")}, not \`${args.by}\`.`);
146
+ const asked = args["since"];
147
+ const since = typeof asked === "string" ? asked : "30d";
148
+ const request = readListArgs(usageListTable, {
149
+ ...args,
150
+ since
151
+ }, { all: "project" });
152
+ if (request === void 0) return;
153
+ const rawBy = args["by"];
154
+ const given = typeof rawBy === "string" ? rawBy : void 0;
155
+ if (given !== void 0 && !isDimension(given)) {
156
+ refuse(`--by takes one of ${USAGE_DIMENSIONS.join(", ")}, not \`${given}\`.`);
213
157
  return;
214
158
  }
159
+ const by = given;
215
160
  const store = openStore();
216
161
  try {
217
- if (args["clear-plan-limits"]) store.clearPlanLimits();
162
+ const scope = projectScope(request.filter, store.projects());
163
+ if (scope === void 0) return;
164
+ const named = typeof scope === "object" ? scope.root : void 0;
165
+ const place = scope === "here" ? placeFor(process.cwd(), store.projects()) : store.projects().find((p) => p.root === named);
166
+ const { workflow, until, since: from } = request.filter;
167
+ if (args["clear-plan-limits"] === true) {
168
+ const cleared = store.clearPlanLimits();
169
+ const said = cleared === 0 ? "No plan limit was recorded." : `Cleared ${String(cleared)} plan limit(s).`;
170
+ (args["json"] === true ? writeErr : write)(said);
171
+ }
218
172
  const filter = {
219
- ...args.all ? {} : { project: placeFor(process.cwd(), store.projects()).root },
220
- ...args.workflow === void 0 ? {} : { workflow: args.workflow },
221
- ...since === void 0 ? {} : { since },
173
+ ...place === void 0 ? {} : { project: place.root },
174
+ ...workflow === void 0 ? {} : { workflow },
175
+ ...from === void 0 || from === "all" ? {} : { since: from },
222
176
  ...until === void 0 ? {} : { until }
223
177
  };
224
178
  const report = usageReport(store.usage(filter), store.toolCpu(filter));
225
- const limits = limitsHere(store, args.all ? void 0 : placeFor(process.cwd(), store.projects()));
226
- if (args.json) {
179
+ const limits = limitsHere(store, place);
180
+ if (args["json"] === true) {
227
181
  write(JSON.stringify({
228
182
  ...report,
229
183
  limits
@@ -231,9 +185,9 @@ const usage = defineCommand({
231
185
  return;
232
186
  }
233
187
  for (const line of formatLimits(limits)) write(line);
234
- const place = args.all ? "every project" : "this project";
235
- const period = args.since === "all" ? "" : `, since ${args.since}`;
236
- for (const line of formatUsage(report, `${place}${period}`, args.by)) write(line);
188
+ const where = scope === "here" ? "this project" : scope === "all" ? "every project" : `project ${String(request.filter["project"])}`;
189
+ const period = since === "all" ? "" : `, since ${since}`;
190
+ for (const line of formatUsage(report, `${where}${period}`, by)) write(line);
237
191
  } finally {
238
192
  store.close();
239
193
  }
@@ -1,8 +1,8 @@
1
- import { At as walkSummary, Dr as placeFor, Er as placeFiles, In as loadProject, L as readProjectTriggers, M as lateTriggers, N as triggersReport, Ot as checkWorkflowText, Qt as namingNoDriver, R as workflowTextHash, Si as knownExperiments, U as openStore, W as storePath, _i as AGENT_DRIVERS, ai as DriverSchema, bi as EXPERIMENTS, et as budgetWarnings, jr as workflowDirOf, kt as walkChecks, li as INTEGRATIONS, lr as socketRoom, mi as readyDriver, pi as driverChecks, rt as driverQueues, si as retiredDriverHint, tt as driverRefusals, vi as DRIVER_IDS, xi as experimentsOn, yi as firstLine } from "./src-Dr_fTSu-.mjs";
2
- import { d as readUpdateCache, f as refreshUpdateCache, i as registryOf, l as newerVersion, m as writeUpdateCache, n as ORK_VERSION, o as updateSwitchOff, p as updateCachePath, r as isSwitchedOn, s as checkDue, t as ORK_NAME, u as pendingNotice } from "./__build_info__-Bsb1kjgF.mjs";
1
+ import { D as triggersReport, Di as EXPERIMENTS, E as lateTriggers, Ei as firstLine, Hn as loadProject, J as driverRefusals, L as openStore, M as workflowTextHash, Nr as placeFiles, Oi as experimentsOn, Pr as placeFor, R as storePath, Rr as workflowDirOf, St as walkSummary, Ti as DRIVER_IDS, X as driverQueues, bi as driverChecks, bt as checkWorkflowText, fi as DriverSchema, gi as INTEGRATIONS, hr as socketRoom, j as readProjectTriggers, ki as knownExperiments, mi as retiredDriverHint, q as budgetWarnings, rn as namingNoDriver, wi as AGENT_DRIVERS, xi as readyDriver, xt as walkChecks } from "./src-DN-RPsil.mjs";
2
+ import { d as readUpdateCache, f as refreshUpdateCache, i as registryOf, l as newerVersion, m as writeUpdateCache, n as ORK_VERSION, o as updateSwitchOff, p as updateCachePath, r as isSwitchedOn, s as checkDue, t as ORK_NAME, u as pendingNotice } from "./__build_info__-DcRdK0Du.mjs";
3
3
  import { C as termOf, D as wrap, E as widthOf, a as plain, c as write, h as paint, l as writeErr, s as style, w as cut, x as plainEnv } from "./output-B1kwo0YU.mjs";
4
4
  import { n as chip, t as CHIP_LABELS } from "./chips-BqcT58qr.mjs";
5
- import { c as registeredProjects, i as refusedBuiltin, r as findWorkflowFiles, s as projectAt } from "./workflows-CW28ZU-z.mjs";
5
+ import { c as registeredProjects, i as refusedBuiltin, r as findWorkflowFiles, s as projectAt } from "./workflows-Bvl2Fquu.mjs";
6
6
  import path from "node:path";
7
7
  import { readFileSync } from "node:fs";
8
8
  import { execFile, spawn } from "node:child_process";
@@ -304,10 +304,30 @@ const experimentsCheck = (place, env = process.env, registry = EXPERIMENTS) => {
304
304
  * `driver.ts` has the whole rule): the one `ork doctor`'s own checks find
305
305
  * ready on this machine, said in one line. With none ready, the run stops
306
306
  * before any session starts, with the hints `ork doctor` gives. A resumed run
307
- * keeps the driver it recorded.
307
+ * keeps the driver it recorded. `--driver <id>` skips that look but checks
308
+ * that one driver's tool, and stops the run the same way when it isn't
309
+ * ready.
308
310
  */
309
311
  /** The real checks: the tools on this machine and its environment. */
310
- const checksHere = () => driverChecks(exec, process.env);
312
+ const checksHere = (only) => only === void 0 ? driverChecks(exec, process.env) : Promise.all(AGENT_DRIVERS.filter(({ id }) => id === only).map(async (driver) => ({
313
+ id: driver.id,
314
+ result: await driver.doctor(exec, process.env)
315
+ })));
316
+ /**
317
+ * What stops a run on `--driver <id>`: its own doctor check isn't `ok`, so its
318
+ * tool isn't found or isn't signed in. Empty when it is ready. Drivers named on
319
+ * nodes aren't looked at here; they can sit on a branch that never runs.
320
+ */
321
+ const overrideRefusal = async (id, checks) => {
322
+ const found = (await (checks ?? checksHere)(id)).find((c) => c.id === id);
323
+ if (found === void 0 || found.result.status === "ok") return [];
324
+ const { detail, hint } = found.result;
325
+ return [
326
+ `--driver ${id}: ${id} isn't ready here (${detail}).`,
327
+ ...hint === void 0 ? [] : [hint],
328
+ "See `ork doctor`."
329
+ ];
330
+ };
311
331
  const subject = (names, all) => {
312
332
  if (all) return "No driver is named, so the run uses";
313
333
  const [only] = names;
@@ -340,7 +360,13 @@ const chooseReadyDriver = (names, all, checks) => {
340
360
  * recorded; otherwise the one ready here.
341
361
  */
342
362
  const runReadyDriver = async (workflow, options) => {
343
- if (options.override !== void 0) return { ok: true };
363
+ if (options.override !== void 0) {
364
+ const lines = await overrideRefusal(options.override, options.checks);
365
+ return lines.length === 0 ? { ok: true } : {
366
+ ok: false,
367
+ lines
368
+ };
369
+ }
344
370
  const names = namingNoDriver(workflow);
345
371
  if (names.length === 0) return { ok: true };
346
372
  if (options.recorded !== void 0) return {
@@ -431,7 +457,8 @@ const pathLines = (walk) => {
431
457
  // src/commands/validate-report.ts
432
458
  const plural = (count, word) => `${count} ${word}${count === 1 ? "" : "s"}`;
433
459
  const diagnosticLine = (file, d, term) => {
434
- const where = d.line === void 0 ? file : `${file}:${d.line}:${d.column ?? 1}`;
460
+ const at = d.file ?? file;
461
+ const where = d.line === void 0 ? at : `${at}:${d.line}:${d.column ?? 1}`;
435
462
  if (term.rich) return [
436
463
  `${d.severity === "error" ? chip(term, CHIP_LABELS.error, "danger") : chip(term, CHIP_LABELS.warn, "warn")} ${paint(term, "muted", cut(where, term.width - 9))}`,
437
464
  ...wrap(d.message, term.width - 2).map((line) => ` ${line}`),
@@ -602,4 +629,4 @@ const validate = defineCommand({
602
629
  if (reports.some((r) => hasErrors(r.diagnostics))) process.exitCode = 1;
603
630
  }
604
631
  });
605
- export { updateCheckOff as C, startUpdateCheck as S, socketCheck as _, formatReport as a, runUpdateChild as b, checksHere as c, unfitLines as d, exec as f, runChecks as g, orkCheck as h, walkLoaded as i, chooseReadyDriver as l, headWarning as m, validate as n, warningLines as o, experimentsCheck as p, validateFile as r, budgetWarningLines as s, loadWorkflow as t, runReadyDriver as u, triggerChecks as v, showUpdateNotice as x, UPDATE_CHILD_ENV as y };
632
+ export { startUpdateCheck as C, showUpdateNotice as S, runChecks as _, formatReport as a, UPDATE_CHILD_ENV as b, checksHere as c, runReadyDriver as d, unfitLines as f, orkCheck as g, headWarning as h, walkLoaded as i, chooseReadyDriver as l, experimentsCheck as m, validate as n, warningLines as o, exec as p, validateFile as r, budgetWarningLines as s, loadWorkflow as t, overrideRefusal as u, socketCheck as v, updateCheckOff as w, runUpdateChild as x, triggerChecks as y };
@@ -1,10 +1,10 @@
1
- import { $r as CheckReportSchema, An as stillTheSameProcess, B as watchingPid, Cn as liveStatus, Er as placeFiles, F as triggerStanding, I as declaredTriggers, L as readProjectTriggers, N as triggersReport, P as createWatcher, U as openStore, Yn as runGh, ci as timeoutMs, gt as detachRun, ii as isToolNode, ln as startFollowUpChecks, z as takeWatchLock } from "./src-Dr_fTSu-.mjs";
2
- import { n as newRunId } from "./find-BYLlszqj.mjs";
1
+ import { A as declaredTriggers, D as triggersReport, In as stillTheSameProcess, L as openStore, N as takeWatchLock, Nr as placeFiles, O as createWatcher, P as watchingPid, di as isToolNode, hi as timeoutMs, hn as startFollowUpChecks, j as readProjectTriggers, k as triggerStanding, kn as liveStatus, lt as detachRun, oi as CheckReportSchema, tr as runGh } from "./src-DN-RPsil.mjs";
2
+ import { n as newRunId } from "./find-mLYsR0uC.mjs";
3
3
  import { C as termOf, E as widthOf, a as plain, c as write, l as writeErr, s as style, t as confirm, w as cut } from "./output-B1kwo0YU.mjs";
4
4
  import { n as chip, t as CHIP_LABELS } from "./chips-BqcT58qr.mjs";
5
- import { c as toolExecutor, o as spawnTool, s as mcpCallExecutor } from "./src-DcrhgipG.mjs";
6
- import { i as runLimits, r as limitsRefusal } from "./limits-B1qzS4cX.mjs";
7
- import { t as acceptRefusal } from "./in-a-run-Bp0k0J09.mjs";
5
+ import { c as toolExecutor, o as spawnTool, s as mcpCallExecutor } from "./src-DHDQ0itC.mjs";
6
+ import { i as runLimits, r as limitsRefusal } from "./limits-C_Xz_t3C.mjs";
7
+ import { t as acceptRefusal } from "./in-a-run-a447hxq8.mjs";
8
8
  import path from "node:path";
9
9
  import { mkdirSync } from "node:fs";
10
10
  import { homedir } from "node:os";
@@ -1,5 +1,5 @@
1
- import { Dr as placeFor, Er as placeFiles, Mr as inside, U as openStore, W as storePath, kr as runDirOf } from "./src-Dr_fTSu-.mjs";
2
- import { a as isBuiltinArg, n as builtinWorkflowFile, t as builtinProblem } from "./builtin-files-2bHf76x-.mjs";
1
+ import { Ir as runDirOf, L as openStore, Nr as placeFiles, Pr as placeFor, R as storePath, zr as inside } from "./src-DN-RPsil.mjs";
2
+ import { a as isBuiltinArg, n as builtinWorkflowFile, t as builtinProblem } from "./builtin-files-BRSOObAv.mjs";
3
3
  import { l as writeErr } from "./output-B1kwo0YU.mjs";
4
4
  import path from "node:path";
5
5
  import { existsSync, readdirSync, statSync } from "node:fs";
@@ -1,8 +1,12 @@
1
- import { U as openStore, Y as inspectProject, _ as keyOf, a as planRemove, c as repoRoot, d as planLine, h as applyClean, i as lostWork, l as labelOf, p as stateParts, r as applyRemove, s as readWorktrees, u as mergeStateOf, v as planClean, y as stepActs } from "./src-Dr_fTSu-.mjs";
1
+ import { Gt as applyClean, Jt as planClean, L as openStore, Wt as worktreeListTable, Yt as stepActs, Zt as inspectProject, a as planRemove, c as repoRoot, d as planLine, i as lostWork, l as labelOf, p as stateParts, qt as keyOf, r as applyRemove, s as readWorktrees, u as mergeStateOf } from "./src-DN-RPsil.mjs";
2
+ import { t as applyFilters } from "./apply-Mb0fNpCX.mjs";
3
+ import { i as readListArgs, n as filterArgs, r as projectScope } from "./flags-CdgIH1L6.mjs";
2
4
  import { C as termOf, c as write, h as paint, l as writeErr, s as style, t as confirm, w as cut } from "./output-B1kwo0YU.mjs";
3
5
  import { n as chip } from "./chips-BqcT58qr.mjs";
6
+ import { t as printList } from "./print-BaWiWf4v.mjs";
7
+ import { r as withStore } from "./project-records-BRiyQzUo.mjs";
4
8
  import path from "node:path";
5
- import { realpathSync } from "node:fs";
9
+ import { existsSync, realpathSync } from "node:fs";
6
10
  import { defineCommand } from "citty";
7
11
  // src/project/go-ahead.ts
8
12
  /**
@@ -24,11 +28,11 @@ const goAhead = async (yes, verb, terminal = process.stdin.isTTY, ask = confirm)
24
28
  };
25
29
  // src/project/worktrees.ts
26
30
  /**
27
- * @file What `ork worktrees` prints: each worktree or branch a run left, with
28
- * its state read from git, the plan `ork worktrees clean` or `remove` would
29
- * carry out, and what a forced remove would lose. The words are core's
30
- * (worktree/words.ts), which the Worktrees panel says too; this only colors
31
- * them.
31
+ * @file What `ork worktrees` reads across projects, and prints: each worktree
32
+ * or branch a run left, with its state read from git, the plan `ork worktrees
33
+ * clean` or `remove` would carry out, and what a forced remove would lose.
34
+ * The words are core's (worktree/words.ts), which the Worktrees panel says
35
+ * too; this only colors them.
32
36
  */
33
37
  /** One line of state: its merge state, then what clean would care about. */
34
38
  const worktreeState = (item, term = termOf(process.stdout)) => {
@@ -55,6 +59,22 @@ const formatLosses = (losses) => losses.flatMap(({ item, lost }) => {
55
59
  const lines = formatLost(lost);
56
60
  return lines.length === 0 ? [] : [`Loses in ${labelOf(item)}:`, ...lines];
57
61
  });
62
+ /** What Ork created in each registered project's repository still on disk. */
63
+ const inProjects = async (roots) => {
64
+ const store = openStore();
65
+ try {
66
+ return (await Promise.all([...new Set(roots)].filter((root) => existsSync(root)).map(async (root) => {
67
+ try {
68
+ return (await readWorktrees(store, root)).items;
69
+ } catch (error) {
70
+ writeErr(`Skipped ${root}: ${error instanceof Error ? error.message : String(error)}`);
71
+ return [];
72
+ }
73
+ }))).flat();
74
+ } finally {
75
+ store.close();
76
+ }
77
+ };
58
78
  // src/commands/worktrees.ts
59
79
  /**
60
80
  * @file `ork worktrees` (or `ork wt`): lists the worktrees and branches runs
@@ -105,23 +125,31 @@ const realOr = (target) => {
105
125
  const done = (lines) => {
106
126
  for (const line of lines) write(`${style("green", "✓")} ${line}`);
107
127
  };
108
- const show = async (json) => {
109
- const found = await here(true);
110
- if (found === null) return;
111
- if (json) write(JSON.stringify(found.items, void 0, 2));
112
- else for (const line of formatWorktrees(found.items)) write(line);
128
+ const show = async (args) => {
129
+ const request = readListArgs(worktreeListTable, args);
130
+ if (request === void 0) return;
131
+ const projects = withStore((store) => store.projects());
132
+ const scope = projectScope(request.filter, projects);
133
+ if (scope === void 0) return;
134
+ let items;
135
+ if (scope === "here") {
136
+ const found = await here(true);
137
+ if (found === null) return;
138
+ items = found.items;
139
+ } else items = await inProjects(scope === "all" ? projects.map((p) => p.root) : [scope.root]);
140
+ const shown = applyFilters(worktreeListTable, items, request.filter).slice(0, request.limit);
141
+ printList(shown, worktreeListTable, args, (rows) => {
142
+ for (const line of formatWorktrees(rows)) write(line);
143
+ });
113
144
  };
145
+ const listArgs = filterArgs(worktreeListTable);
114
146
  const list = defineCommand({
115
147
  meta: {
116
148
  name: "list",
117
149
  description: "List Ork's worktrees and branches (the default)"
118
150
  },
119
- args: { json: {
120
- type: "boolean",
121
- description: "Print JSON",
122
- default: false
123
- } },
124
- run: ({ args }) => show(args.json)
151
+ args: listArgs,
152
+ run: ({ args }) => show(args)
125
153
  });
126
154
  const yes = {
127
155
  type: "boolean",
@@ -256,15 +284,16 @@ const worktrees = defineCommand({
256
284
  name: "worktrees",
257
285
  description: "Track and clean up the git worktrees Ork creates for runs"
258
286
  },
287
+ args: listArgs,
259
288
  subCommands: {
260
289
  list,
261
290
  clean,
262
291
  remove
263
292
  },
264
- run: async ({ rawArgs }) => {
293
+ run: async ({ args, rawArgs }) => {
265
294
  const [first] = rawArgs;
266
295
  if (first === "list" || first === "clean" || first === "remove") return;
267
- await show(rawArgs.includes("--json"));
296
+ await show(args);
268
297
  }
269
298
  });
270
299
  export { worktrees };
package/package.json CHANGED
@@ -1,14 +1,18 @@
1
1
  {
2
2
  "name": "@krapht/ork",
3
- "version": "1.0.0-alpha.15",
4
- "description": "Orchestrate Claude agents on your machine: hierarchical, serializable workflows with a CLI and a localhost UI.",
3
+ "version": "1.0.0-alpha.16",
4
+ "description": "Orchestrate coding agents (Claude Code, OpenCode, Command Code) on your machine: hierarchical, serializable workflows with a CLI and a localhost UI.",
5
5
  "keywords": [
6
- "claude",
7
6
  "agents",
7
+ "coding-agents",
8
8
  "orchestration",
9
9
  "workflow",
10
10
  "cli",
11
- "ai-engineering"
11
+ "ai-engineering",
12
+ "claude",
13
+ "claude-code",
14
+ "opencode",
15
+ "command-code"
12
16
  ],
13
17
  "license": "MIT",
14
18
  "repository": {