@krapht/ork 1.0.0-alpha.18 → 1.0.0-alpha.19

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 (48) hide show
  1. package/dist/{__build_info__-B8TNbFtN.mjs → __build_info__-dPf4cEGL.mjs} +3 -134
  2. package/dist/{attach-CU4Pk42S.mjs → attach-BQl8nQz2.mjs} +5 -5
  3. package/dist/{attach-qvfyaQXC.mjs → attach-DhZQ87Nz.mjs} +49 -224
  4. package/dist/bin.mjs +204 -154
  5. package/dist/{brief-Bh1FnAHv.mjs → brief-BQbeoPMc.mjs} +19 -9
  6. package/dist/{builtin-files-dnTP2GaX.mjs → builtin-files-CWL3JEo-.mjs} +1 -1
  7. package/dist/{command-tree-BkSYWmYJ.mjs → command-tree-B6uvMJq1.mjs} +1 -1
  8. package/dist/{completion-CzXUGSVn.mjs → completion-DKPuD_Rw.mjs} +47 -14
  9. package/dist/{context-b4_gPlv3.mjs → context-Bw6R_EYq.mjs} +3 -3
  10. package/dist/detached-BQlbmqR0.mjs +38 -0
  11. package/dist/{find-P-VV4B47.mjs → find-B30-rd-u.mjs} +1 -1
  12. package/dist/flags-ByYBx-Xb.mjs +170 -0
  13. package/dist/{follow-ups-B_c1DSpA.mjs → follow-ups-Cxca1Jgg.mjs} +24 -18
  14. package/dist/{format-BS1qzpkf.mjs → format-C-nEZv-P.mjs} +27 -11
  15. package/dist/{help-groups-hB6kY34d.mjs → help-groups-BpRgBSTh.mjs} +13 -7
  16. package/dist/{in-a-run-DKoMGTNE.mjs → in-a-run-CZFzoT3R.mjs} +2 -2
  17. package/dist/{install-B743Bwzz.mjs → install-CsqK1avz.mjs} +2 -183
  18. package/dist/{limits-DrsKCqaN.mjs → limits-CXh052II.mjs} +1 -1
  19. package/dist/{mcp-BMwekw38.mjs → mcp-B4wME9C4.mjs} +6 -6
  20. package/dist/{once-6L8gciJV.mjs → once-BKn3pOtg.mjs} +3 -3
  21. package/dist/outputs-text-CNTJ7Qq_.mjs +19 -0
  22. package/dist/{pause-8gnVgICR.mjs → pause-B1grR1cl.mjs} +3 -3
  23. package/dist/{print-DLe4MHn-.mjs → print-Bd8MWVJk.mjs} +1 -1
  24. package/dist/{project-records-CsILxwfI.mjs → project-records-BOEVs69i.mjs} +1 -1
  25. package/dist/{parse-BzWpMN-a.mjs → refusals-DQQX3AIb.mjs} +10 -1
  26. package/dist/{relay-Bvm9JEoN.mjs → relay-CeNiq3GT.mjs} +1 -1
  27. package/dist/{run-D4yuTmmJ.mjs → run-C0C54UiU.mjs} +116 -137
  28. package/dist/{run-y8jI7bAI.mjs → run-D6yU2rLf.mjs} +1 -1
  29. package/dist/{send-BV9eIxQ7.mjs → send-DqgF58hb.mjs} +2 -2
  30. package/dist/{src-Frwpx3WF.mjs → src-Ciy7i3se.mjs} +843 -66
  31. package/dist/{src-ygFMh2eP.mjs → src-D2qpd-G7.mjs} +4 -4
  32. package/dist/{src-CVeA4tAw.mjs → src-DP02RP9O.mjs} +227 -180
  33. package/dist/{src-DWAcd3Sy.mjs → src-n-u4TGev.mjs} +186 -43
  34. package/dist/{store-B0nGqnPq.mjs → store-2KXzMCEN.mjs} +1 -1
  35. package/dist/ui/assets/app-C5vO1M-l.css +1 -0
  36. package/dist/ui/assets/{app-C4yxmYQR.js → app-ICEKpy_C.js} +21 -20
  37. package/dist/ui/assets/{index-CcbJWo5C.js → index-BGaix5wh.js} +2 -2
  38. package/dist/ui/index.html +1 -1
  39. package/dist/{ui-BZ9BhFw8.mjs → ui-CAvygxTb.mjs} +53 -24
  40. package/dist/{usage-BIs4j5Rw.mjs → usage-BFBco5OS.mjs} +7 -7
  41. package/dist/{validate-Xn2ZbJQC.mjs → validate-UrmC5AG1.mjs} +5 -5
  42. package/dist/{watch-9VM3DFTp.mjs → watch-DTxkgB8R.mjs} +5 -5
  43. package/dist/{workflows-CBYtycIy.mjs → workflows-8GV-SeRm.mjs} +28 -4
  44. package/dist/{worktrees-C7vfh7y5.mjs → worktrees-CUdUvZlO.mjs} +9 -6
  45. package/package.json +1 -1
  46. package/dist/flags-CqjJ44uW.mjs +0 -125
  47. package/dist/post-y1qL1Fnj.mjs +0 -46
  48. package/dist/ui/assets/app-9Q-62Ywz.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-CcbJWo5C.js"></script>
22
+ <script type="module" crossorigin src="/assets/index-BGaix5wh.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,8 @@
1
- import { n as ORK_VERSION, t as ORK_NAME } from "./__build_info__-B8TNbFtN.mjs";
1
+ import { p as scrubberOfRun } from "./src-DP02RP9O.mjs";
2
+ import { n as ORK_VERSION, t as ORK_NAME } from "./__build_info__-dPf4cEGL.mjs";
2
3
  import { c as write, l as writeErr, s as style } from "./output-B1kwo0YU.mjs";
3
- import { o as mayShowSignIn } from "./in-a-run-DKoMGTNE.mjs";
4
- import { i as uiFilePath, l as newToken, n as startServer, r as runningServer, t as DEFAULT_PORT } from "./src-DWAcd3Sy.mjs";
4
+ import { o as mayShowSignIn } from "./in-a-run-CZFzoT3R.mjs";
5
+ import { i as uiFilePath, l as newToken, n as startServer, r as runningServer, t as DEFAULT_PORT, u as createClient } from "./src-n-u4TGev.mjs";
5
6
  import path from "node:path";
6
7
  import { constants } from "node:os";
7
8
  import { spawn } from "node:child_process";
@@ -105,7 +106,8 @@ const childMessage = (value) => {
105
106
  const startMessage = (value) => isRecord(value) && value["type"] === "ork-ui-start" && typeof value["port"] === "number" && typeof value["token"] === "string" ? {
106
107
  type: "ork-ui-start",
107
108
  port: value["port"],
108
- token: value["token"]
109
+ token: value["token"],
110
+ ...typeof value["session"] === "string" ? { session: value["session"] } : {}
109
111
  } : null;
110
112
  // src/browser/serve.ts
111
113
  /**
@@ -190,11 +192,13 @@ const serve = async (start, end) => {
190
192
  version: ORK_VERSION,
191
193
  port: start.port,
192
194
  token: start.token,
195
+ ...start.session === void 0 ? {} : { sessionSecret: start.session },
193
196
  ui: uiFolders(),
194
197
  update: { name: ORK_NAME },
195
198
  restart: () => {
196
199
  send({ type: "ork-ui-restart" }).then(end);
197
- }
200
+ },
201
+ noteScrub: (run, registered) => scrubberOfRun(run, registered)
198
202
  });
199
203
  } catch (error) {
200
204
  await send({
@@ -231,13 +235,14 @@ const serveForSupervisor = async () => {
231
235
  * @file `ork ui`'s process model (ADR-060): the command in the terminal only
232
236
  * supervises; its server runs in a child, the launcher the user ran
233
237
  * (`process.argv[1]` as given, so after an upgrade it is the new install's)
234
- * with `ui --serve`. The port and the token go over the child's IPC channel.
235
- * When the page asks for a restart, the child says so and exits, and the
236
- * supervisor starts the launcher again on the same port with the same token,
237
- * so the open tab stays signed in. There is only ever one child; Ctrl-C (or
238
- * SIGTERM) stops it and then the supervisor, which exits with its code. The
239
- * child may wait for an upgrade to finish first; a second Ctrl-C (or SIGTERM)
240
- * kills it and exits at once.
238
+ * with `ui --serve`. The port, the token and the session secret (made here,
239
+ * once) go over the child's IPC channel. When the page asks for a restart,
240
+ * the child says so and exits, and the supervisor starts the launcher again
241
+ * on the same port with the same token and session secret, so the open tab
242
+ * stays signed in. There is only ever one child; Ctrl-C (or SIGTERM) stops it
243
+ * and then the supervisor, which exits with its code. The child may wait for
244
+ * an upgrade to finish first; a second Ctrl-C (or SIGTERM) kills it and exits
245
+ * at once.
241
246
  */
242
247
  /**
243
248
  * How long an exited child's channel may take to close before the supervisor
@@ -250,6 +255,7 @@ const signalled = (signal) => 128 + constants.signals[signal];
250
255
  /** Supervises the server until it stops; resolves the exit code. */
251
256
  const supervise = (options) => new Promise((resolve) => {
252
257
  const { token, say, sayErr } = options;
258
+ const session = newToken();
253
259
  let port = options.port;
254
260
  let stopping = false;
255
261
  let ended = false;
@@ -288,7 +294,8 @@ const supervise = (options) => new Promise((resolve) => {
288
294
  if (message?.type === "ork-ui-hello") child.send({
289
295
  type: "ork-ui-start",
290
296
  port,
291
- token
297
+ token,
298
+ session
292
299
  });
293
300
  else if (message?.type === "ork-ui-ready") {
294
301
  ready = true;
@@ -384,26 +391,48 @@ const onStopSignals = (stop) => {
384
391
  // src/commands/ui.ts
385
392
  /**
386
393
  * @file `ork ui`: starts the local server (ADR-039) on loopback and opens it in
387
- * the browser, signed in with the server's token; or, when one is already
388
- * running (`~/.ork/ui.json`), opens that one. The command supervises the
389
- * server in a child process, so the page can restart it onto a newly
390
- * installed `ork` (ADR-060). The server runs nothing: runs it starts keep
391
- * running when it stops, and the CLI works without it.
394
+ * the browser, signed in with a single-use code the server mints (RFC-019);
395
+ * or, when one is already running (`~/.ork/ui.json`), opens that one. The
396
+ * command supervises the server in a child process, so the page can restart
397
+ * it onto a newly installed `ork` (ADR-060). The server runs nothing: runs it
398
+ * starts keep running when it stops, and the CLI works without it.
399
+ */
400
+ /** The link that signs the browser in; the server swaps its code for a cookie. */
401
+ const signInLink = (port, code) => `http://127.0.0.1:${String(port)}/?code=${code}`;
402
+ /** What `ork ui` says when it can't get a sign-in code from the server. */
403
+ const MINT_FAILED_OLDER = "The running UI is from an older Ork; stop it and run `ork ui`.";
404
+ /**
405
+ * Gets a single-use sign-in code from the server with the API token, and
406
+ * returns the link for it, or the reason there is none. The token only goes in
407
+ * this request's header, never in a link or an argument.
392
408
  */
393
- /** The link that signs the browser in; the server swaps it for a cookie. */
394
- const signInLink = (port, token) => `http://127.0.0.1:${String(port)}/?token=${token}`;
409
+ const mintSignInLink = async (port, token, fetchFn) => {
410
+ const answer = await createClient({
411
+ base: `http://127.0.0.1:${String(port)}`,
412
+ token,
413
+ ...fetchFn === void 0 ? {} : { fetch: fetchFn }
414
+ }).call("signIn");
415
+ if (answer.ok) return { link: signInLink(port, answer.data.code) };
416
+ return { failure: answer.status === 404 || answer.status === 0 ? MINT_FAILED_OLDER : `Could not get a sign-in code: ${answer.error}` };
417
+ };
395
418
  /** `--port`: 0 to 65535 (0 picks a free port); `null` when invalid. */
396
419
  const portArg = (value) => {
397
420
  if (value === void 0) return DEFAULT_PORT;
398
421
  const port = Number(value);
399
422
  return Number.isInteger(port) && port >= 0 && port <= 65535 ? port : null;
400
423
  };
401
- /** Opens the link, or prints it when there is no browser to open. */
402
- const show = async (link, open) => {
424
+ /** Opens the sign-in link, or prints it when there is no browser to open. */
425
+ const show = async (port, token, open) => {
403
426
  if (!mayShowSignIn()) {
404
427
  writeErr("A program or an agent in a run does not get the sign-in link, which acts as you. Run `ork ui` in your own terminal.");
405
428
  return;
406
429
  }
430
+ const minted = await mintSignInLink(port, token);
431
+ if ("failure" in minted) {
432
+ writeErr(minted.failure);
433
+ return;
434
+ }
435
+ const { link } = minted;
407
436
  if (!(open && process.stdout.isTTY && await openInBrowser(link))) write(`Open it with this link, which signs you in: ${link}`);
408
437
  };
409
438
  /** What `ork ui` says once its server serves, the first time or again. */
@@ -415,7 +444,7 @@ const ready = async ({ port, version, restarted }, token, open) => {
415
444
  }
416
445
  write(`Ork's UI is at ${url}`);
417
446
  write(style("dim", "Runs keep running when it stops, and every ork command works without it. Ctrl-C stops it."));
418
- await show(signInLink(port, token), open);
447
+ await show(port, token, open);
419
448
  };
420
449
  const ui = defineCommand({
421
450
  meta: {
@@ -452,7 +481,7 @@ const ui = defineCommand({
452
481
  const running = runningServer(uiFilePath());
453
482
  if (running !== null) {
454
483
  write(`Ork's UI is already running at http://127.0.0.1:${String(running.port)} (process ${String(running.pid)}).`);
455
- await show(signInLink(running.port, running.token), args.open);
484
+ await show(running.port, running.token, args.open);
456
485
  return;
457
486
  }
458
487
  const token = newToken();
@@ -1,10 +1,10 @@
1
- import { B as planPrune, Fr as placeFor, L as openStore, Wt as usageListTable, g as usageReport, h as USAGE_DIMENSIONS, z as applyPrune } from "./src-Frwpx3WF.mjs";
2
- import { r as agoTime } from "./parse-BzWpMN-a.mjs";
1
+ import { H as applyPrune, R as openStore, U as planPrune, Wr as placeFor, _ as usageReport, g as USAGE_DIMENSIONS, qt as usageListTable } from "./src-Ciy7i3se.mjs";
2
+ import { a as agoTime } from "./refusals-DQQX3AIb.mjs";
3
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";
4
4
  import { n as chip, t as CHIP_LABELS } from "./chips-BqcT58qr.mjs";
5
- import { n as limitsHere } from "./limits-DrsKCqaN.mjs";
6
- import { i as readListArgs, n as filterArgs, o as allAlias, r as projectScope } from "./flags-CqjJ44uW.mjs";
7
- import { a as header, o as table } from "./format-BS1qzpkf.mjs";
5
+ import { n as limitsHere } from "./limits-CXh052II.mjs";
6
+ import { i as readListArgs, n as filterArgs, r as projectScope, s as refuseUnknownFlags } from "./flags-ByYBx-Xb.mjs";
7
+ import { a as header, o as table } from "./format-C-nEZv-P.mjs";
8
8
  import { defineCommand } from "citty";
9
9
  // src/runs/usage.ts
10
10
  /** `950`, `12.3k`, `4.5M`. */
@@ -127,11 +127,11 @@ const usage = defineCommand({
127
127
  name: "usage",
128
128
  description: "Show what this project's runs spent over time"
129
129
  },
130
+ plugins: [refuseUnknownFlags],
130
131
  args: filterArgs(usageListTable, {
131
132
  ids: false,
132
133
  limit: false,
133
134
  extra: {
134
- ...allAlias,
135
135
  "clear-plan-limits": {
136
136
  type: "boolean",
137
137
  description: "Forget what Claude Code last said of your plan's limits, for one that has lifted",
@@ -149,7 +149,7 @@ const usage = defineCommand({
149
149
  const request = readListArgs(usageListTable, {
150
150
  ...args,
151
151
  since
152
- }, { all: "project" });
152
+ });
153
153
  if (request === void 0) return;
154
154
  const rawBy = args["by"];
155
155
  const given = typeof rawBy === "string" ? rawBy : void 0;
@@ -1,13 +1,13 @@
1
- import { Ai as knownExperiments, D as triggersReport, Di as firstLine, E as lateTriggers, Ei as DRIVER_IDS, Fr as placeFor, J as driverRefusals, L as openStore, M as workflowTextHash, Oi as EXPERIMENTS, Pr as placeFiles, R as storePath, Si as readyDriver, St as walkSummary, Ti as AGENT_DRIVERS, Un as loadProject, X as driverQueues, _i as INTEGRATIONS, bt as checkWorkflowText, gr as socketRoom, hi as retiredDriverHint, in as namingNoDriver, j as readProjectTriggers, ki as experimentsOn, pi as DriverSchema, q as budgetWarnings, xi as driverChecks, xt as walkChecks, zr as workflowDirOf } from "./src-Frwpx3WF.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__-B8TNbFtN.mjs";
1
+ import { $ as driverQueues, Ai as driverChecks, Ct as checkWorkflowText, D as lateTriggers, Ei as INTEGRATIONS, Fi as DRIVER_IDS, Ii as firstLine, Jr as workflowDirOf, Kn as loadProject, Li as EXPERIMENTS, M as readProjectTriggers, N as workflowTextHash, O as triggersReport, Pi as AGENT_DRIVERS, R as openStore, Ri as experimentsOn, Si as DriverSchema, Tt as walkSummary, Ur as placeFiles, Wr as placeFor, X as budgetWarnings, Z as driverRefusals, ji as readyDriver, sn as namingNoDriver, wi as retiredDriverHint, wr as socketRoom, wt as walkChecks, z as storePath, zi as knownExperiments } from "./src-Ciy7i3se.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__-dPf4cEGL.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-CBYtycIy.mjs";
5
+ import { a as refusedBuiltin, c as projectAt, i as missingWorkflowText, l as registeredProjects, r as findWorkflowFiles } from "./workflows-8GV-SeRm.mjs";
6
6
  import path from "node:path";
7
7
  import { readFileSync } from "node:fs";
8
8
  import { execFile, spawn } from "node:child_process";
9
- import { defineCommand } from "citty";
10
9
  import { styleText } from "node:util";
10
+ import { defineCommand } from "citty";
11
11
  // src/update/notify.ts
12
12
  /**
13
13
  * @file The new-version notice (ADR-038). At most once a day a command starts a
@@ -520,7 +520,7 @@ const loadWorkflow = (file, cwd, home, registered = registeredProjects(home)) =>
520
520
  diagnostics: [{
521
521
  severity: "error",
522
522
  code: "file.read",
523
- message: error instanceof Error && "code" in error && error.code === "ENOENT" ? "file not found" : "cannot read file",
523
+ message: error instanceof Error && "code" in error && error.code === "ENOENT" ? missingWorkflowText(file, placeFiles(place).workflows) : "cannot read file",
524
524
  path: []
525
525
  }]
526
526
  },
@@ -1,10 +1,10 @@
1
- import { A as declaredTriggers, An as liveStatus, D as triggersReport, L as openStore, Ln as stillTheSameProcess, N as takeWatchLock, O as createWatcher, P as watchingPid, Pr as placeFiles, fi as isToolNode, gi as timeoutMs, gn as startFollowUpChecks, j as readProjectTriggers, k as triggerStanding, lt as detachRun, nr as runGh, si as CheckReportSchema } from "./src-Frwpx3WF.mjs";
2
- import { n as newRunId } from "./find-P-VV4B47.mjs";
1
+ import { A as triggerStanding, Bn as stillTheSameProcess, F as watchingPid, M as readProjectTriggers, Nn as liveStatus, O as triggersReport, P as takeWatchLock, R as openStore, Ti as timeoutMs, Ur as placeFiles, bn as startFollowUpChecks, ft as detachRun, gi as CheckReportSchema, j as declaredTriggers, k as createWatcher, pr as runGh, xi as isToolNode } from "./src-Ciy7i3se.mjs";
2
+ import { c as toolExecutor, o as spawnTool, s as mcpCallExecutor } from "./src-DP02RP9O.mjs";
3
+ import { n as newRunId } from "./find-B30-rd-u.mjs";
3
4
  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
5
  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-CVeA4tAw.mjs";
6
- import { i as runLimits, r as limitsRefusal } from "./limits-DrsKCqaN.mjs";
7
- import { t as acceptRefusal } from "./in-a-run-DKoMGTNE.mjs";
6
+ import { i as runLimits, r as limitsRefusal } from "./limits-CXh052II.mjs";
7
+ import { t as acceptRefusal } from "./in-a-run-CZFzoT3R.mjs";
8
8
  import path from "node:path";
9
9
  import { mkdirSync } from "node:fs";
10
10
  import { homedir } from "node:os";
@@ -1,8 +1,8 @@
1
- import { Br as inside, Fr as placeFor, L as openStore, Lr as runDirOf, Pr as placeFiles, R as storePath } from "./src-Frwpx3WF.mjs";
2
- import { a as isBuiltinArg, n as builtinWorkflowFile, t as builtinProblem } from "./builtin-files-dnTP2GaX.mjs";
1
+ import { Kr as runDirOf, R as openStore, Ur as placeFiles, Wr as placeFor, Yr as inside, z as storePath } from "./src-Ciy7i3se.mjs";
2
+ import { a as isBuiltinArg, n as builtinWorkflowFile, t as builtinProblem } from "./builtin-files-CWL3JEo-.mjs";
3
3
  import { l as writeErr } from "./output-B1kwo0YU.mjs";
4
4
  import path from "node:path";
5
- import { existsSync, readdirSync, statSync } from "node:fs";
5
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
6
6
  // src/project/place.ts
7
7
  /**
8
8
  * @file Which project a folder belongs to, from the projects the store has
@@ -86,4 +86,28 @@ const workflowArg = (arg, cwd) => {
86
86
  runDir: (runId) => runDirOf(place, runId)
87
87
  };
88
88
  };
89
- export { resolveWorkflowArg as a, registeredProjects as c, refusedBuiltin as i, WORKFLOW_SUFFIX as n, workflowArg as o, findWorkflowFiles as r, projectAt as s, WORKFLOW_DIR as t };
89
+ /**
90
+ * The `name:` line of a workflow file, read without parsing it (it may be
91
+ * broken).
92
+ */
93
+ const nameOf = (file) => {
94
+ try {
95
+ return /^name:\s*["']?([^\s"'#]+)/mu.exec(readFileSync(file, "utf8"))?.[1];
96
+ } catch {
97
+ return;
98
+ }
99
+ };
100
+ /**
101
+ * The text for a workflow argument that is no file: the workflows the project
102
+ * has, by file name (and `name:` when it differs), and that a file name or path
103
+ * is what the command takes. Empty-handed when the folder is missing.
104
+ */
105
+ const missingWorkflowText = (arg, workflows) => {
106
+ const listed = (existsSync(workflows) ? readdirSync(workflows).filter((name) => name.endsWith(WORKFLOW_SUFFIX)).toSorted() : []).map((file) => {
107
+ const name = nameOf(path.join(workflows, file));
108
+ const stem = file.slice(0, -9);
109
+ return name === void 0 || name === stem ? file : `${file} (${name})`;
110
+ });
111
+ return `no workflow file ${arg}; a file name or path is meant, not a workflow name (${listed.length === 0 ? "this project has no workflows" : `this project's workflows: ${listed.join(", ")}`})`;
112
+ };
113
+ export { refusedBuiltin as a, projectAt as c, missingWorkflowText as i, registeredProjects as l, WORKFLOW_SUFFIX as n, resolveWorkflowArg as o, findWorkflowFiles as r, workflowArg as s, WORKFLOW_DIR as t };
@@ -1,10 +1,10 @@
1
- import { Gt as worktreeListTable, Jt as keyOf, Kt as applyClean, L as openStore, Qt as inspectProject, Xt as stepActs, Yt as planClean, a as planRemove, c as repoRoot, d as planLine, i as lostWork, l as labelOf, p as stateParts, r as applyRemove, s as readWorktrees, u as mergeStateOf } from "./src-Frwpx3WF.mjs";
1
+ import { $t as stepActs, Jt as worktreeListTable, Qt as planClean, R as openStore, Yt as applyClean, Zt as keyOf, a as planRemove, c as repoRoot, d as mergeStateOf, f as planLine, i as lostWork, l as isDetached, m as stateParts, r as applyRemove, s as readWorktrees, tn as inspectProject, u as labelOf } from "./src-Ciy7i3se.mjs";
2
2
  import { t as applyFilters } from "./apply-Mb0fNpCX.mjs";
3
3
  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";
4
4
  import { n as chip } from "./chips-BqcT58qr.mjs";
5
- import { i as readListArgs, n as filterArgs, r as projectScope } from "./flags-CqjJ44uW.mjs";
6
- import { t as printList } from "./print-DLe4MHn-.mjs";
7
- import { r as withStore } from "./project-records-CsILxwfI.mjs";
5
+ import { i as readListArgs, n as filterArgs, o as resolveRun, r as projectScope, s as refuseUnknownFlags } from "./flags-ByYBx-Xb.mjs";
6
+ import { t as printList } from "./print-Bd8MWVJk.mjs";
7
+ import { r as withStore } from "./project-records-BOEVs69i.mjs";
8
8
  import path from "node:path";
9
9
  import { existsSync, realpathSync } from "node:fs";
10
10
  import { defineCommand } from "citty";
@@ -42,7 +42,7 @@ const worktreeState = (item, term = termOf(process.stdout)) => {
42
42
  return [...head, ...parts].join(" · ");
43
43
  };
44
44
  /** A name as the list shows it, saying when it's detached. */
45
- const nameOf = (item) => mergeStateOf(item) === "detached" ? `${labelOf(item)} (detached)` : labelOf(item);
45
+ const nameOf = (item) => isDetached(item) ? `${labelOf(item)} (detached)` : labelOf(item);
46
46
  const formatWorktrees = (items, term = termOf(process.stdout)) => {
47
47
  if (items.length === 0) return ["No Ork worktrees or branches."];
48
48
  const width = Math.max(...items.map((i) => nameOf(i).length));
@@ -131,13 +131,15 @@ const show = async (args) => {
131
131
  const projects = withStore((store) => store.projects());
132
132
  const scope = projectScope(request.filter, projects);
133
133
  if (scope === void 0) return;
134
+ const filter = withStore((store) => resolveRun(store, request.filter));
135
+ if (filter === void 0) return;
134
136
  let items;
135
137
  if (scope === "here") {
136
138
  const found = await here(true);
137
139
  if (found === null) return;
138
140
  items = found.items;
139
141
  } 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);
142
+ const shown = applyFilters(worktreeListTable, items, filter).slice(0, request.limit);
141
143
  printList(shown, worktreeListTable, args, (rows) => {
142
144
  for (const line of formatWorktrees(rows)) write(line);
143
145
  });
@@ -148,6 +150,7 @@ const list = defineCommand({
148
150
  name: "list",
149
151
  description: "List Ork's worktrees and branches (the default)"
150
152
  },
153
+ plugins: [refuseUnknownFlags],
151
154
  args: listArgs,
152
155
  run: ({ args }) => show(args)
153
156
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@krapht/ork",
3
- "version": "1.0.0-alpha.18",
3
+ "version": "1.0.0-alpha.19",
4
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
6
  "agents",
@@ -1,125 +0,0 @@
1
- import "./src-Frwpx3WF.mjs";
2
- import { n as parseLimit, t as parseFilters } from "./parse-BzWpMN-a.mjs";
3
- import { t as INTERNAL } from "./command-tree-BkSYWmYJ.mjs";
4
- import { l as writeErr } from "./output-B1kwo0YU.mjs";
5
- // src/list/all-alias.ts
6
- /**
7
- * @file The deprecated `--all` of `ps`, `usage`, `decide`, `follow-ups list`
8
- * and `feedback list` (RFC-017): it stands for `--project all` or `--status
9
- * all`, is left out of `--help` and Tab completion (an `Internal:` flag), and
10
- * warns once on stderr. `prune --all` is not this one.
11
- */
12
- /** The hidden `--all` flag, for a command's `args`. */
13
- const allAlias = { all: {
14
- type: "boolean",
15
- description: `${INTERNAL} deprecated, use --project all or --status all`,
16
- default: false
17
- } };
18
- /**
19
- * `args` with `all` in `--all`'s place when `--all` was given and that flag was
20
- * not (a given one wins); the warning prints either way. `args` as is when
21
- * `--all` was not given.
22
- */
23
- const warnAll = (args, flag) => {
24
- if (args["all"] !== true) return args;
25
- writeErr(`--all is deprecated; use --${flag} all.`);
26
- return args[flag] === void 0 ? {
27
- ...args,
28
- [flag]: "all"
29
- } : args;
30
- };
31
- // src/list/flags.ts
32
- /**
33
- * @file The flags every list command shares (RFC-017), made from its field
34
- * table: one flag per field (an enum field's values as citty `options`, so
35
- * `--help` and Tab completion read them), `--ids`, `--limit` and `--json`;
36
- * and reading them back: each value checked by core's `parseFilters`,
37
- * `--limit` a positive whole number, `--project` a registered one. A refusal
38
- * says why on stderr and sets the exit code; the command then returns.
39
- */
40
- const fieldArg = (field) => {
41
- if (field.type !== "enum") return {
42
- type: "string",
43
- description: field.description
44
- };
45
- return {
46
- type: "enum",
47
- options: [...field.values ?? [], ...field.all === true ? ["all"] : []],
48
- description: field.description
49
- };
50
- };
51
- /** The citty flags of `table`, then `--ids`, `--limit`, `--json`, `extra`. */
52
- const filterArgs = (table, options = {}) => ({
53
- ...Object.fromEntries(table.fields.map((field) => [field.name, fieldArg(field)])),
54
- ...options.ids ?? table.id !== void 0 ? { ids: {
55
- type: "boolean",
56
- description: "Print only the ids, comma-separated, on one line",
57
- default: false
58
- } } : {},
59
- ...options.limit ?? true ? { limit: {
60
- type: "string",
61
- description: "Show at most this many (a positive whole number)"
62
- } } : {},
63
- ...options.json ?? true ? { json: {
64
- type: "boolean",
65
- description: "Print JSON",
66
- default: false
67
- } } : {},
68
- ...options.extra
69
- });
70
- /** Says why, and makes the command exit 1. */
71
- const fail = (message) => {
72
- writeErr(message);
73
- process.exitCode = 1;
74
- };
75
- /** The `--ids` with `--json` refusal; `true` when `args` has both. */
76
- const refuseIdsWithJson = (args) => {
77
- if (args.ids !== true || args.json !== true) return false;
78
- fail("--ids and --json can't be used together.");
79
- return true;
80
- };
81
- /**
82
- * `args` (a command's parsed flags) read against `table`: the filter, and the
83
- * limit. `all` names the flag `--all` stands for (`project` or `status`):
84
- * `--all` fills it when it is not given, with one warning. `undefined` when
85
- * something was refused (already said, exit code set).
86
- */
87
- const readListArgs = (table, args, options = {}) => {
88
- if (refuseIdsWithJson(args)) return void 0;
89
- const given = options.all === void 0 ? args : warnAll(args, options.all);
90
- const parsed = parseFilters(table, given, options.now ?? Date.now());
91
- if (!parsed.ok) {
92
- fail(parsed.error);
93
- return;
94
- }
95
- const text = args["limit"];
96
- if (typeof text !== "string") return {
97
- filter: parsed.filter,
98
- limit: void 0
99
- };
100
- const limit = parseLimit(text);
101
- if (limit === null) {
102
- fail(`--limit takes a positive whole number, not \`${text}\`.`);
103
- return;
104
- }
105
- return {
106
- filter: parsed.filter,
107
- limit
108
- };
109
- };
110
- /**
111
- * The scope the filter's `project` names, checked against the registered
112
- * projects (core does not know them): an id or a root, or `all`; none given is
113
- * `here`. A project not registered is refused, listing the registered ones
114
- * (said, exit code set, `undefined`).
115
- */
116
- const projectScope = (filter, projects) => {
117
- const key = filter["project"];
118
- if (key === void 0) return "here";
119
- if (key === "all") return "all";
120
- const found = projects.find((p) => p.id === key || p.root === key);
121
- if (found !== void 0) return { root: found.root };
122
- const names = projects.map((p) => p.id);
123
- fail(`No registered project \`${key}\`; registered: ${names.length === 0 ? "none" : names.join(", ")}.`);
124
- };
125
- export { refuseIdsWithJson as a, readListArgs as i, filterArgs as n, allAlias as o, projectScope as r, fail as t };
@@ -1,46 +0,0 @@
1
- import { ar as redact } from "./src-Frwpx3WF.mjs";
2
- import { b as githubDraftRefusal } from "./__build_info__-B8TNbFtN.mjs";
3
- // ../core/src/follow-ups/post.ts
4
- /** Posts `f`'s draft as a GitHub comment once `confirmed`, and marks it done. */
5
- const postFollowUpMessage = async (f, options) => {
6
- const draft = f.draft;
7
- const refused = githubDraftRefusal(f);
8
- if (refused !== void 0 || draft === void 0) return {
9
- ok: false,
10
- error: refused?.error ?? "it has no draft"
11
- };
12
- if (!options.confirmed) return {
13
- ok: false,
14
- error: "posting to GitHub needs the maintainer's confirmation"
15
- };
16
- const body = redact(draft.text, {
17
- roots: [f.project],
18
- home: options.home
19
- });
20
- const on = /\/pull\/\d+/u.test(draft.to) ? "pr" : "issue";
21
- const result = await options.gh([
22
- on,
23
- "comment",
24
- draft.to,
25
- "--body",
26
- body
27
- ], { cwd: f.project });
28
- if (!result.ok) return {
29
- ok: false,
30
- error: `gh failed: ${result.stderr.trim() || result.stdout.trim() || "no output"}`
31
- };
32
- const url = result.stdout.trim().split("\n").findLast((line) => line.startsWith("https://"));
33
- const done = {
34
- ...f,
35
- status: "done",
36
- note: `posted${url === void 0 ? "" : ` as ${url}`}`,
37
- updatedAt: options.now?.() ?? (/* @__PURE__ */ new Date()).toISOString()
38
- };
39
- options.rows.putFollowUp(done);
40
- return {
41
- ok: true,
42
- followUp: done,
43
- ...url === void 0 ? {} : { url }
44
- };
45
- };
46
- export { postFollowUpMessage as t };