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

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 (50) hide show
  1. package/README.md +10 -4
  2. package/dist/{__build_info__-Bsb1kjgF.mjs → __build_info__-C1Nm0hlJ.mjs} +25 -6
  3. package/dist/apply-Mb0fNpCX.mjs +17 -0
  4. package/dist/{attach-CPespxlQ.mjs → attach-EP374oMP.mjs} +5 -5
  5. package/dist/{attach-BHO3X3cf.mjs → attach-RQx__RzH.mjs} +46 -8
  6. package/dist/bin.mjs +238 -369
  7. package/dist/brief-Bs3cMQU0.mjs +100 -0
  8. package/dist/{builtin-files-2bHf76x-.mjs → builtin-files-yYaNTQRU.mjs} +1 -1
  9. package/dist/command-tree-BkSYWmYJ.mjs +63 -0
  10. package/dist/{completion-CQ2UYKuJ.mjs → completion-DIXWlnAl.mjs} +87 -6
  11. package/dist/{context-BNqDw9z1.mjs → context-Dq-VM_sq.mjs} +3 -3
  12. package/dist/{find-BYLlszqj.mjs → find-k489laBa.mjs} +2 -2
  13. package/dist/flags-B4ZZWspN.mjs +125 -0
  14. package/dist/{follow-ups-B4GJ3Vzw.mjs → follow-ups-DD8NTPe6.mjs} +59 -24
  15. package/dist/{format-C8MsFj8_.mjs → format-DQc9A_ei.mjs} +17 -4
  16. package/dist/{help-groups-CQ98mLjF.mjs → help-groups-hB6kY34d.mjs} +1 -63
  17. package/dist/in-a-run-D_9GlAlS.mjs +27 -0
  18. package/dist/{install-DWtXTq4y.mjs → install-PVlV2QPq.mjs} +1 -1
  19. package/dist/{limits-B1qzS4cX.mjs → limits-CHqBO7wb.mjs} +1 -1
  20. package/dist/{mcp-BOrVkyhN.mjs → mcp-D38Ajb0b.mjs} +6 -6
  21. package/dist/{once-C1fL3GVo.mjs → once-DuUzpzL2.mjs} +3 -3
  22. package/dist/parse-BzWpMN-a.mjs +107 -0
  23. package/dist/{pause-DOMK-R8V.mjs → pause-Bx3ePGrn.mjs} +3 -3
  24. package/dist/{post-BzA6Qobq.mjs → post-2CaJz_mV.mjs} +5 -3
  25. package/dist/print-n2qetUHc.mjs +19 -0
  26. package/dist/project-records-Cune1l6I.mjs +230 -0
  27. package/dist/{relay-OWuu4fib.mjs → relay-D7RN4m68.mjs} +1 -1
  28. package/dist/{run-CiPsW9Kf.mjs → run-BfxdsQJk.mjs} +1 -1
  29. package/dist/{run--4-1xAF2.mjs → run-OOWQIKMJ.mjs} +33 -20
  30. package/dist/{send-CnNNW-ot.mjs → send-CKth_vgn.mjs} +2 -2
  31. package/dist/{src-B9zWIVqo.mjs → src-BHod38UQ.mjs} +104 -72
  32. package/dist/{src-Dr_fTSu-.mjs → src-DLsFsmOD.mjs} +790 -502
  33. package/dist/{src-DQKz2eOH.mjs → src-DOjTY2ET.mjs} +238 -79
  34. package/dist/{src-DcrhgipG.mjs → src-DmmrX2_C.mjs} +162 -58
  35. package/dist/{store-oKJ-cCf3.mjs → store-DACH768X.mjs} +1 -1
  36. package/dist/ui/assets/app-9Q-62Ywz.css +1 -0
  37. package/dist/ui/assets/app-CN3acQ-1.js +111 -0
  38. package/dist/ui/assets/{index-BtwkftKe.js → index-B2uGxA4W.js} +2 -2
  39. package/dist/ui/index.html +1 -1
  40. package/dist/{ui-CzKlsCnm.mjs → ui-CJ-BX12n.mjs} +3 -3
  41. package/dist/{usage-DHGNk2JL.mjs → usage-DxZ6JGd4.mjs} +62 -107
  42. package/dist/{validate-Bs_gg22g.mjs → validate-BS0nCccW.mjs} +35 -8
  43. package/dist/{watch-CCCfw3n7.mjs → watch-Dp7IB4FR.mjs} +5 -5
  44. package/dist/{workflows-CW28ZU-z.mjs → workflows-Djo1edlK.mjs} +2 -2
  45. package/dist/{worktrees-CyE8aF6j.mjs → worktrees-Drof_Nyx.mjs} +49 -20
  46. package/package.json +8 -4
  47. package/dist/brief-za0ozN_E.mjs +0 -58
  48. package/dist/in-a-run-Bp0k0J09.mjs +0 -24
  49. package/dist/ui/assets/app-Bdc6OBeZ.css +0 -1
  50. package/dist/ui/assets/app-Drp8Dm9y.js +0 -111
package/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  # @krapht/ork
2
2
 
3
- The `ork` command: orchestrate Claude agents on your machine with hierarchical,
4
- serializable workflows.
3
+ The `ork` command: orchestrate coding agents (Claude Code, OpenCode, Command
4
+ Code) on your machine with hierarchical, serializable workflows.
5
5
 
6
- > **Alpha.** Workflows of Claude agents and tools, run from the terminal or the
6
+ > **Alpha.** Workflows of coding agents and tools, run from the terminal or the
7
7
  > browser (`ork ui`). Expect changes until 1.0.0. See the
8
8
  > [project README](https://github.com/mimshins/ork#readme), the
9
9
  > [guides](https://github.com/mimshins/ork/tree/main/docs#readme) and
@@ -28,7 +28,13 @@ ork upgrade # a newer version? upgrade the way you installed Ork
28
28
 
29
29
  A workflow that names no agent driver runs on the one that's ready. Today that
30
30
  is `claude-code`: your own `claude`, logged in or with `ANTHROPIC_API_KEY` or a
31
- cloud provider set. `--driver claude-code` names it.
31
+ cloud provider set. Your own `opencode` (`open-code`) and Command Code's `cmd`
32
+ (`command-code`) work too: `--driver <id>`, or a driver per node
33
+ ([Drivers](https://github.com/mimshins/ork/blob/main/docs/guides/drivers.md)).
34
+
35
+ Ork is built with Ork: its own workflows plan, build, review and document its
36
+ features, and each run is measured against a manual session in
37
+ [Benchmarks: Ork building Ork](https://github.com/mimshins/ork/blob/main/benchmarks/README.md).
32
38
 
33
39
  Ork never stores or asks for credentials: it uses the tools you already have,
34
40
  signed in with your own accounts. Once a day it asks the npm registry whether a
@@ -1,4 +1,4 @@
1
- import { A as isNewer, j as parseVersion, k as compareVersions } from "./src-Dr_fTSu-.mjs";
1
+ import { C as compareVersions, T as parseVersion, w as isNewer } from "./src-DLsFsmOD.mjs";
2
2
  import path from "node:path";
3
3
  import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
4
4
  import { homedir } from "node:os";
@@ -57,19 +57,38 @@ const check = (f, action) => {
57
57
  const error = refusal(f, action);
58
58
  return error === void 0 ? { ok: true } : {
59
59
  ok: false,
60
+ kind: "status",
60
61
  why: `it is ${f.status}`,
61
62
  error
62
63
  };
63
64
  };
64
- const postCheck = (f) => {
65
- const open = f.status === "waiting" || f.status === "due";
65
+ /** Whether `to` names one GitHub issue or pull request by its URL. */
66
+ const isGithubIssueUrl = (to) => /^https:\/\/github\.com\/[^/\s]+\/[^/\s]+\/(?:issues|pull)\/\d+$/u.test(to);
67
+ /**
68
+ * Why `f`'s draft can't be posted to GitHub whatever its status, or
69
+ * `undefined`.
70
+ */
71
+ const githubDraftRefusal = (f) => {
66
72
  if (f.kind !== "message" || f.draft?.channel !== "github") return {
67
- ok: false,
68
73
  why: "not drafted for the github channel",
69
74
  error: "only a message drafted for the github channel is posted; send the others yourself"
70
75
  };
76
+ return isGithubIssueUrl(f.draft.to) ? void 0 : {
77
+ why: "its `to` isn't a GitHub issue or pull request URL",
78
+ error: `${f.id}'s \`to\` (${f.draft.to}) isn't a GitHub issue or pull request URL, so it is not posted; copy the text and send it yourself.`
79
+ };
80
+ };
81
+ const postCheck = (f) => {
82
+ const open = f.status === "waiting" || f.status === "due";
83
+ const refused = githubDraftRefusal(f);
84
+ if (refused !== void 0) return {
85
+ ok: false,
86
+ kind: "draft",
87
+ ...refused
88
+ };
71
89
  return open ? { ok: true } : {
72
90
  ok: false,
91
+ kind: "status",
73
92
  why: `it is ${f.status}`,
74
93
  error: `${f.id} is ${f.status}, so it is not posted.`
75
94
  };
@@ -336,5 +355,5 @@ const registryShown = (registry) => {
336
355
  * fails when it is stale.
337
356
  */
338
357
  const ORK_NAME = "@krapht/ork";
339
- const ORK_VERSION = "1.0.0-alpha.15";
340
- export { dueCause as _, registryShown as a, fetchLatest as c, readUpdateCache as d, refreshUpdateCache as f, changeFollowUp as g, nodeSession as h, registryOf as i, newerVersion as l, writeUpdateCache as m, ORK_VERSION as n, updateSwitchOff as o, updateCachePath as p, isSwitchedOn as r, checkDue as s, ORK_NAME as t, pendingNotice as u, findFollowUp as v, followUpActions as y };
358
+ const ORK_VERSION = "1.0.0-alpha.17";
359
+ export { dueCause as _, registryShown as a, githubDraftRefusal as b, fetchLatest as c, readUpdateCache as d, refreshUpdateCache as f, changeFollowUp as g, nodeSession as h, registryOf as i, newerVersion as l, writeUpdateCache as m, ORK_VERSION as n, updateSwitchOff as o, updateCachePath as p, isSwitchedOn as r, checkDue as s, ORK_NAME as t, pendingNotice as u, findFollowUp as v, isGithubIssueUrl as x, followUpActions as y };
@@ -0,0 +1,17 @@
1
+ // ../core/src/list-filters/apply.ts
2
+ /**
3
+ * @file Keeps the rows a list's filter matches (RFC-017). The list's own order
4
+ * and `--limit` stay the caller's: filter, then order, then limit.
5
+ */
6
+ const fieldMatches = (field, row, wanted) => {
7
+ if (wanted === void 0 || wanted === "all" || field.get === void 0) return true;
8
+ const got = field.get(row);
9
+ if (got === void 0) return false;
10
+ const values = typeof got === "string" ? [got] : got;
11
+ if (field.type === "since") return values.some((value) => Date.parse(value) >= Date.parse(wanted));
12
+ if (field.type === "until") return values.some((value) => Date.parse(value) < Date.parse(wanted));
13
+ return values.includes(wanted);
14
+ };
15
+ /** The rows of `rows` every field of `filter` matches, in their order. */
16
+ const applyFilters = (table, rows, filter) => rows.filter((row) => table.fields.every((field) => fieldMatches(field, row, filter[field.name])));
17
+ export { applyFilters as t };
@@ -1,9 +1,9 @@
1
- import "./src-Dr_fTSu-.mjs";
2
- import { c as createTerminalHuman, t as attachRun } from "./attach-BHO3X3cf.mjs";
3
- import { t as findRun } from "./find-BYLlszqj.mjs";
1
+ import "./src-DLsFsmOD.mjs";
2
+ import { l as createTerminalHuman, t as attachRun } from "./attach-RQx__RzH.mjs";
3
+ import { t as findRun } from "./find-k489laBa.mjs";
4
4
  import { c as write, l as writeErr, s as style } from "./output-B1kwo0YU.mjs";
5
- import { n as attachRefusal } from "./in-a-run-Bp0k0J09.mjs";
6
- import { t as withStore } from "./store-oKJ-cCf3.mjs";
5
+ import { n as attachRefusal } from "./in-a-run-D_9GlAlS.mjs";
6
+ import { t as withStore } from "./store-DACH768X.mjs";
7
7
  import { defineCommand } from "citty";
8
8
  // src/commands/attach.ts
9
9
  /**
@@ -1,4 +1,5 @@
1
- import { Bn as outcomeEnd, H as isNodeEvent, Hn as END_KINDS, Lt as lockWaitLine, Ut as slotWaitLine, _t as followEvents, ct as followRunTree, fr as maskSecrets, lt as answerDecision, ut as pendingDecisions, zn as endOf } from "./src-Dr_fTSu-.mjs";
1
+ import { Ft as slotWaitLine, I as isNodeEvent, Kn as endOf, Yn as END_KINDS, kt as lockWaitLine, nt as answerDecision, qn as outcomeEnd, rt as pendingDecisions, tt as followRunTree, ut as followEvents, yr as maskSecrets } from "./src-DLsFsmOD.mjs";
2
+ import { t as applyFilters } from "./apply-Mb0fNpCX.mjs";
2
3
  import { C as termOf, D as wrap, E as widthOf, O as readKeys, _ as interrupt, a as plain, b as canKey, g as createRegion, h as paint, m as bold, n as cost, p as apply, r as costOf, s as style, u as confirmOrStop, v as readLine, w as cut, x as plainEnv, y as withdrawn } from "./output-B1kwo0YU.mjs";
3
4
  import { n as chip, r as nodeChip, t as CHIP_LABELS } from "./chips-BqcT58qr.mjs";
4
5
  import { a as createView, i as formatBusEvent, n as nodeVerdict, o as styler, r as firstLine, t as hasConflict } from "./verdict-CMvPCpWC.mjs";
@@ -1038,7 +1039,8 @@ const formatRunEvent = (event, width, tools, terminal = false, verbose = false,
1038
1039
  if (event.type === "node.started") {
1039
1040
  const round = event.round > 1 ? ` (round ${String(event.round)})` : "";
1040
1041
  const owner = event.owner === void 0 ? "" : `, for ${event.owner}`;
1041
- return [`${tag(event.id)} ${style("dim", `${event.nodeType}${round}${owner}`)}`];
1042
+ const named = event.description === void 0 ? "" : ` · ${event.description}`;
1043
+ return [`${tag(event.id)} ${style("dim", `${event.nodeType}${round}${owner}${named}`)}`];
1042
1044
  }
1043
1045
  if (event.type === "node.event") {
1044
1046
  const inner = event.event;
@@ -1121,7 +1123,7 @@ const formatRunEvent = (event, width, tools, terminal = false, verbose = false,
1121
1123
  /** Turns stored events into lines, keeping the state a transcript needs. */
1122
1124
  const createLogPrinter = (options) => {
1123
1125
  const tools = /* @__PURE__ */ new Map();
1124
- const view = createView();
1126
+ const view = options.view ?? createView();
1125
1127
  let width = 8;
1126
1128
  return (stored) => {
1127
1129
  const { event } = stored;
@@ -1141,6 +1143,19 @@ const unknownNode = (stored, node, live) => {
1141
1143
  const declared = stored.some(({ event }) => event.type === "run.started" && event.nodes.some((n) => n.id === node));
1142
1144
  return named || declared || live ? null : `Run has no node ${node}.`;
1143
1145
  };
1146
+ /** `ork logs`'s filter flags (RFC-017): an event's time; no ids. */
1147
+ const logListTable = { fields: [{
1148
+ name: "since",
1149
+ description: "Only events since an age (24h, 30d, 12w), a day (2026-11-01), or all",
1150
+ type: "since",
1151
+ all: true,
1152
+ get: (stored) => stored.at
1153
+ }, {
1154
+ name: "until",
1155
+ description: "Only events on or before this day (2026-11-01)",
1156
+ type: "until",
1157
+ get: (stored) => stored.at
1158
+ }] };
1144
1159
  /**
1145
1160
  * Prints a run's events; with `follow`, then each new one as it is stored:
1146
1161
  * pushed over the run's socket, or polled from the store when there is none.
@@ -1149,8 +1164,13 @@ const unknownNode = (stored, node, live) => {
1149
1164
  */
1150
1165
  const followRun = async (store, run, out, options) => {
1151
1166
  const print = createLogPrinter(options);
1167
+ const matches = (stored) => applyFilters(logListTable, [stored], options.filter ?? {}).length > 0 && (options.node === void 0 || isNodeEvent(stored.event, options.node));
1168
+ const replayed = options.limit === void 0 ? [] : store.events(run.id, options.after ?? 0).filter((stored) => matches(stored)).slice(0, -options.limit);
1169
+ const dropped = new Set(replayed.map((stored) => stored.seq));
1152
1170
  if (await followEvents(store, run.id, (stored) => {
1153
- for (const line of print(stored)) out(line);
1171
+ const lines = print(stored);
1172
+ if (!matches(stored) || dropped.has(stored.seq)) return;
1173
+ for (const line of lines) out(line);
1154
1174
  }, {
1155
1175
  follow: options.follow,
1156
1176
  ...options.after === void 0 ? {} : { after: options.after },
@@ -1208,13 +1228,15 @@ const askStored = async (store, human, decision, { place, by, signal, home }) =>
1208
1228
  * asked through one `TerminalHuman`, withdrawn when it closes elsewhere.
1209
1229
  */
1210
1230
  const where = (path) => path.join(" › ");
1231
+ /** The gutter before a called run's lines: its call path, then ` › `. */
1232
+ const gutterOf = (path) => path.length === 0 ? "" : `${where(path)} › `;
1211
1233
  /**
1212
1234
  * Prints the events of `root` and of the runs it called until the root ends or
1213
1235
  * `signal` aborts, and asks each open decision with `options.human`.
1214
1236
  */
1215
1237
  const attachRun = async (store, root, out, options = {}) => {
1216
1238
  const { human, basePath = [] } = options;
1217
- const { term } = createView();
1239
+ const { term } = options.view ?? createView();
1218
1240
  const say = styler(term);
1219
1241
  const printers = /* @__PURE__ */ new Map();
1220
1242
  const prompts = /* @__PURE__ */ new Map();
@@ -1226,9 +1248,25 @@ const attachRun = async (store, root, out, options = {}) => {
1226
1248
  succeeded: false
1227
1249
  };
1228
1250
  const show = (path, lines) => {
1229
- const prefix = path.length === 0 ? "" : paint(term, "muted", `${where(path)} › `);
1251
+ const gutter = gutterOf(path);
1252
+ const prefix = gutter === "" ? "" : paint(term, "muted", gutter);
1230
1253
  for (const line of lines) out(line === "" ? line : `${prefix}${line}`);
1231
1254
  };
1255
+ /**
1256
+ * A run's printer, laid out in the columns its gutter leaves, so a line fits
1257
+ * the terminal with the gutter before it and a wrapped line goes on under its
1258
+ * own text, not at the terminal's first column.
1259
+ */
1260
+ const printerFor = (path) => createLogPrinter({
1261
+ json: false,
1262
+ view: {
1263
+ term: {
1264
+ ...term,
1265
+ width: Math.max(20, term.width - widthOf(gutterOf(path)))
1266
+ },
1267
+ roots: /* @__PURE__ */ new Map()
1268
+ }
1269
+ });
1232
1270
  const ask = (run, path, id) => {
1233
1271
  const decision = store.decision(id);
1234
1272
  if (human === void 0 || decision === void 0) return;
@@ -1288,7 +1326,7 @@ const attachRun = async (store, root, out, options = {}) => {
1288
1326
  }
1289
1327
  let print = printers.get(run);
1290
1328
  if (print === void 0) {
1291
- print = createLogPrinter({ json: false });
1329
+ print = printerFor(path);
1292
1330
  printers.set(run, print);
1293
1331
  }
1294
1332
  show(path, print(stored));
@@ -1307,4 +1345,4 @@ const attachRun = async (store, root, out, options = {}) => {
1307
1345
  exitCode: end === "aborted" || seen.succeeded ? 0 : 1
1308
1346
  };
1309
1347
  };
1310
- export { formatEvent as a, createTerminalHuman as c, unknownNode as i, readText as l, askStored as n, formatRunEvent as o, followRun as r, callsShown as s, attachRun as t, createDecisionQueue as u };
1348
+ export { unknownNode as a, callsShown as c, createDecisionQueue as d, logListTable as i, createTerminalHuman as l, askStored as n, formatEvent as o, followRun as r, formatRunEvent as s, attachRun as t, readText as u };