@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
package/dist/bin.mjs CHANGED
@@ -1,18 +1,18 @@
1
1
  #!/usr/bin/env node
2
- import { $ as adoptedCalls, $n as isClaudeNode, An as projectRootOf, B as peerOf, Cn as placeFiles, Ct as runTree, D as inspectProject, Dn as shownPath, En as runDirOf, F as createCalls, Ft as PROJECT_FILE, Gn as serializeWorkflow, Gt as planPost, Ht as FEEDBACK_SEVERITIES, I as addressOf, It as gitRulesOf, Jn as WorkflowSchema, K as requestStop, L as endLink, Lt as loadProject, M as clearGitLocalEnv, N as parseInputPairs, P as resolveInputs, R as endLinksOf, Sn as newProjectId, St as runStatus, Tn as projectsHome, U as flattenTree, Ut as FEEDBACK_TARGETS, V as tellNode, Vn as beforeOf, Vt as FEEDBACK_KINDS, Xt as fingerprint, Yt as createFeedback, Zn as settingsOf, Zt as normalizeTitle, _ as lateTriggers, _n as catalogAgent, an as socketRoom, at as followEvents, bn as hasInRepoFiles, bt as liveStatus, cr as CLAUDE_DRIVERS, ct as planNodeSession, d as runningRunIds, dn as planMoveInto, dr as experimentsOn, et as detachResume, f as runningScript, fn as linkTools, fr as knownExperiments, gn as deleteAgent, gt as startRun, h as isNewer, hn as createAgent, ht as walkSummary, i as NOT_STARTED, in as socketPath, it as detachRun, kn as inside, l as planUpgrade, ln as planAgentMigration, lr as firstLine, lt as namingNoDriver, mn as toolsInUse, n as resolveBase, nt as listKey, on as wakeRun, or as AGENT_TOOL_LAYOUTS, pn as toolLinks, q as tellLiveRun, qt as sendPost, rn as serveRun, rr as INTEGRATIONS, sn as maskReplacer, sr as ORK_LAYOUT, t as createWorkspace, tt as resumable, u as releaseNotes, un as applySteps, ur as EXPERIMENTS, ut as sessionDriver, v as triggersReport, vn as loadAgents, vt as userMessageText, wn as placeFor, wt as templateOf, xn as inRepoPlace } from "./src-BsLHKQRc.mjs";
3
- import { a as formatEvent, c as createDecisionQueue, i as nestItems, n as formatRuns, o as formatRunEvent, r as formatTree, s as createTerminalHuman, t as ago } from "./format-DaROJUgG.mjs";
4
- import { C as placeOf, T as openFixRun, _ as nodeSession, b as recordNote, c as checkDue, d as pendingNotice, f as readUpdateCache, g as activeTree, h as writeUpdateCache, i as registryOf, l as fetchLatest, m as updateCachePath, n as ORK_VERSION, o as updateSwitchOff, p as refreshUpdateCache, r as isSwitchedOn, s as runInstall, t as ORK_NAME, u as newerVersion$1, v as previewPost, w as feedbackActions, x as acceptItem, y as checkNote } from "./__build_info__-BGoaUwLd.mjs";
5
- import { i as builtinWorkflowFile, n as pendingDecisions, r as builtinProblem, t as answerDecision } from "./decide-DsA6kOT7.mjs";
6
- import { n as newRunId, t as findRun } from "./find-ntyniv2E.mjs";
7
- import { a as write, c as openStore, i as style, l as storePath, n as cost, o as writeErr, s as isNodeEvent, t as confirm } from "./output-CwqlG1Le.mjs";
8
- import { n as listOrkWorktrees, t as createdWorktrees } from "./list-D9HPWLPH.mjs";
9
- import { a as planClean, t as applyClean } from "./clean-BPITM-6p.mjs";
2
+ import { $ as adoptedCalls, $n as settingsOf, $t as normalizeTitle, B as peerOf, Cn as inRepoPlace, Ct as runTree, D as inspectProject, Dn as projectsHome, En as placeFor, F as createCalls, Gt as FEEDBACK_TARGETS, I as addressOf, K as requestStop, L as endLink, Lt as PROJECT_FILE, M as clearGitLocalEnv, Mn as projectRootOf, N as parseInputPairs, On as runDirOf, P as resolveInputs, Qt as fingerprint, R as endLinksOf, Rt as gitRulesOf, Sn as hasInRepoFiles, St as runStatus, Tn as placeFiles, U as flattenTree, Un as beforeOf, Ut as FEEDBACK_KINDS, V as tellNode, Wt as FEEDBACK_SEVERITIES, Xn as WorkflowSchema, Yt as sendPost, Zt as createFeedback, _ as lateTriggers, _n as createAgent, an as serveRun, ar as INTEGRATIONS, at as followEvents, bn as loadAgents, bt as liveStatus, cn as wakeRun, cr as AGENT_TOOL_LAYOUTS, ct as planNodeSession, d as runningRunIds, dn as planAgentMigration, dr as firstLine, et as detachResume, f as runningScript, fn as applySteps, fr as EXPERIMENTS, gn as toolsInUse, gt as startRun, h as isNewer, hn as toolLinks, ht as walkSummary, i as NOT_STARTED, it as detachRun, jn as inside, kn as shownPath, kt as nestItems, l as planUpgrade, ln as maskReplacer, lr as ORK_LAYOUT, lt as namingNoDriver, mn as linkTools, mr as knownExperiments, n as resolveBase, nt as listKey, on as socketPath, pn as planMoveInto, pr as experimentsOn, q as tellLiveRun, qn as serializeWorkflow, qt as planPost, rt as DETACHED_ENV, sn as socketRoom, t as createWorkspace, tr as isClaudeNode, tt as resumable, u as releaseNotes, un as maskSecrets, ur as CLAUDE_DRIVERS, ut as sessionDriver, v as triggersReport, vn as deleteAgent, vt as userMessageText, wn as newProjectId, wt as templateOf, yn as catalogAgent, zt as loadProject } from "./src-BwFxTank.mjs";
3
+ import { C as placeOf, T as openFixRun, _ as nodeSession, b as recordNote, c as checkDue, d as pendingNotice, f as readUpdateCache, g as activeTree, h as writeUpdateCache, i as registryOf, l as fetchLatest, m as updateCachePath, n as ORK_VERSION, o as updateSwitchOff, p as refreshUpdateCache, r as isSwitchedOn, s as runInstall, t as ORK_NAME, u as newerVersion$1, v as previewPost, w as feedbackActions, x as acceptItem, y as checkNote } from "./__build_info__-y1TPvZvp.mjs";
4
+ import { i as builtinWorkflowFile, n as pendingDecisions, r as builtinProblem, t as answerDecision } from "./decide-BphP3ONM.mjs";
5
+ import { n as newRunId, t as findRun } from "./find-BXZjliwr.mjs";
6
+ import { a as write, c as openStore, i as style, l as storePath, n as cost, o as writeErr, s as isNodeEvent, t as confirm } from "./output-BVkI6ZVv.mjs";
7
+ import { n as listOrkWorktrees, t as createdWorktrees } from "./list-BmxgNyd3.mjs";
8
+ import { a as planClean, t as applyClean } from "./clean-BRUXyk87.mjs";
10
9
  import { c as subcommandsOf, i as argsOf, n as groupedEntries, o as publicArgs, s as resolved, t as groupOf } from "./help-groups-BwYoAWsh.mjs";
11
- import { _ as namedMcpEntries, a as builtinExecutors, d as maskedEnv, f as maskedNamesOfRun, g as clearOutputFiles, h as sessionEnv, i as onPath, l as clearStateFiles, m as runScrubber, n as missingProgramMessage, r as missingPrograms, t as envProblems, v as resultOf } from "./src-CUeMUVwW.mjs";
12
- import { c as registeredProjects, i as refusedBuiltin, n as WORKFLOW_SUFFIX, o as workflowArg, s as projectAt, t as WORKFLOW_DIR } from "./workflows-BNCDD6J-.mjs";
13
- import { a as validate, c as warningLines, i as loadWorkflow, o as validateFile, r as formatReport, s as walkLoaded, t as context } from "./context-CZNM454F.mjs";
14
- import { i as runLimits, r as limitsRefusal, t as chatNotice } from "./limits-BKzVMwkt.mjs";
15
- import { i as decideRefusal, n as chatRefusal } from "./in-a-run-D_fnj1Mw.mjs";
10
+ import { _ as namedMcpEntries, a as builtinExecutors, d as maskedEnv, f as maskedNamesOfRun, g as clearOutputFiles, h as sessionEnv, i as onPath, l as clearStateFiles, m as runScrubber, n as missingProgramMessage, r as missingPrograms, t as envProblems, v as resultOf } from "./src-W1i5Bl6N.mjs";
11
+ import { c as registeredProjects, i as refusedBuiltin, n as WORKFLOW_SUFFIX, o as workflowArg, s as projectAt, t as WORKFLOW_DIR } from "./workflows-BJdSHnkr.mjs";
12
+ import { a as validate, c as warningLines, i as loadWorkflow, o as validateFile, r as formatReport, s as walkLoaded, t as context } from "./context-Dbay8l7P.mjs";
13
+ import { a as nodeVerdict, i as hasConflict, n as formatRuns, o as firstLine$1, r as formatTree, s as formatBusEvent, t as ago } from "./format-DeT7vL-8.mjs";
14
+ import { i as runLimits, r as limitsRefusal, t as chatNotice } from "./limits-CBeoYrUH.mjs";
15
+ import { i as decideRefusal, n as chatRefusal } from "./in-a-run-C4E9HfED.mjs";
16
16
  import path from "node:path";
17
17
  import { stringify } from "yaml";
18
18
  import { z } from "zod";
@@ -53,6 +53,221 @@ const readyDriver = (checks) => {
53
53
  ready: ready.length
54
54
  };
55
55
  };
56
+ // ../core/src/control/queue.ts
57
+ const PERMISSION_OPTIONS = [{
58
+ id: "allow",
59
+ label: "Allow"
60
+ }, {
61
+ id: "deny",
62
+ label: "Deny"
63
+ }];
64
+ const DENIED = "The user did not allow this tool.";
65
+ /** The deny message a session gets, with the person's words when given. */
66
+ const deniedMessage = (note) => note === void 0 ? DENIED : `${DENIED} They said: ${note}`;
67
+ /** The input fields that say what a tool will do, the first one present. */
68
+ const TOOL_INPUT_KEYS = [
69
+ "command",
70
+ "file_path",
71
+ "path",
72
+ "url",
73
+ "pattern"
74
+ ];
75
+ /** A tool's input for the decision's context: as written, cut at `max`. */
76
+ const inputContext = (input, max = 4e3) => {
77
+ const key = TOOL_INPUT_KEYS.find((k) => typeof input[k] === "string");
78
+ const text = key === void 0 ? JSON.stringify(input, void 0, 2) : String(input[key]);
79
+ return text.length > max ? `${text.slice(0, max - 1)}…` : text;
80
+ };
81
+ const createDecisionQueue = (options) => {
82
+ const { store, runId, emit } = options;
83
+ const earlier = store.decisions({ runId });
84
+ let count = Math.max(0, ...earlier.map(({ id }) => Number(id.slice(runId.length + 1)) || 0));
85
+ for (const { id, node, status } of earlier) {
86
+ if (status !== "open") continue;
87
+ store.withdrawDecision(id);
88
+ emit({
89
+ type: "decision.withdrawn",
90
+ node,
91
+ decision: id
92
+ });
93
+ }
94
+ /** Opens a decision and settles it with the first answer. */
95
+ const decide = (open, local, withdrawn) => {
96
+ count += 1;
97
+ const id = `${runId}-${String(count)}`;
98
+ const record = store.openDecision({
99
+ ...open,
100
+ id,
101
+ runId
102
+ });
103
+ const event = {
104
+ type: "decision.opened",
105
+ node: record.node,
106
+ decision: id,
107
+ kind: record.kind,
108
+ question: record.question,
109
+ options: record.options
110
+ };
111
+ if (record.recommended !== void 0) event.recommended = record.recommended;
112
+ if (record.owner !== void 0) event.owner = record.owner;
113
+ emit(event);
114
+ const { promise, resolve, reject } = Promise.withResolvers();
115
+ const settled = new AbortController();
116
+ /** `note` is the stored one: masked, and absent when blank. */
117
+ const finish = (answer, by, note) => {
118
+ if (settled.signal.aborted) return;
119
+ settled.abort();
120
+ const answered = {
121
+ type: "decision.answered",
122
+ node: open.node,
123
+ decision: id,
124
+ answer,
125
+ by
126
+ };
127
+ if (note !== void 0) answered.note = note;
128
+ emit(answered);
129
+ resolve(note === void 0 ? {
130
+ answer,
131
+ by
132
+ } : {
133
+ answer,
134
+ by,
135
+ note
136
+ });
137
+ };
138
+ const withdraw = () => {
139
+ if (settled.signal.aborted) return;
140
+ settled.abort();
141
+ store.withdrawDecision(id);
142
+ emit({
143
+ type: "decision.withdrawn",
144
+ node: open.node,
145
+ decision: id
146
+ });
147
+ reject(/* @__PURE__ */ new Error("the question was withdrawn"));
148
+ };
149
+ if (withdrawn?.aborted === true) withdraw();
150
+ withdrawn?.addEventListener("abort", withdraw, { once: true });
151
+ const fromStore = async () => {
152
+ try {
153
+ const closed = await options.waitFor(id, settled.signal);
154
+ if (closed.status === "answered" && closed.answer !== void 0) finish(closed.answer, closed.answeredBy ?? "decide", closed.note);
155
+ } catch {}
156
+ };
157
+ const fromTerminal = async (ask) => {
158
+ try {
159
+ const { answer, note } = await ask(settled.signal);
160
+ if (store.answerDecision(id, answer, "terminal", note)) finish(answer, "terminal", store.decision(id)?.note);
161
+ } catch {}
162
+ };
163
+ fromStore();
164
+ if (local !== void 0) fromTerminal(local);
165
+ return promise;
166
+ };
167
+ return {
168
+ ask: (question) => {
169
+ const open = {
170
+ node: question.nodeId,
171
+ kind: "question",
172
+ question: question.question,
173
+ options: question.options
174
+ };
175
+ if (question.recommended !== void 0) open.recommended = question.recommended;
176
+ if (question.context !== void 0) open.context = question.context;
177
+ if (question.document !== void 0) open.document = question.document;
178
+ if (question.owner !== void 0) open.owner = question.owner;
179
+ const { local } = options;
180
+ return decide(open, local === void 0 ? void 0 : (signal) => {
181
+ let note;
182
+ return local.ask({
183
+ ...question,
184
+ question: maskSecrets(question.question),
185
+ ...question.context === void 0 ? {} : { context: maskSecrets(question.context) },
186
+ ...question.document === void 0 ? {} : { document: maskSecrets(question.document) },
187
+ signal,
188
+ onNote: (given) => {
189
+ note = given;
190
+ }
191
+ }).then((answer) => note === void 0 ? { answer } : {
192
+ answer,
193
+ note
194
+ });
195
+ }, question.signal).then(({ answer, note }) => {
196
+ if (note !== void 0) question.onNote?.(note);
197
+ return answer;
198
+ });
199
+ },
200
+ settled: (question) => {
201
+ count += 1;
202
+ const id = `${runId}-${String(count)}`;
203
+ const { owner } = question;
204
+ const record = store.openDecision({
205
+ id,
206
+ runId,
207
+ node: question.nodeId,
208
+ kind: "question",
209
+ question: question.question,
210
+ options: question.options,
211
+ owner,
212
+ ...question.recommended === void 0 ? {} : { recommended: question.recommended },
213
+ ...question.context === void 0 ? {} : { context: question.context }
214
+ });
215
+ emit({
216
+ type: "decision.opened",
217
+ node: record.node,
218
+ decision: id,
219
+ kind: "question",
220
+ question: record.question,
221
+ options: record.options,
222
+ owner,
223
+ ...record.recommended === void 0 ? {} : { recommended: record.recommended }
224
+ });
225
+ store.answerDecision(id, question.answer, "owner", question.note);
226
+ const note = store.decision(id)?.note;
227
+ emit({
228
+ type: "decision.answered",
229
+ node: record.node,
230
+ decision: id,
231
+ answer: question.answer,
232
+ by: "owner",
233
+ owner,
234
+ ...note === void 0 ? {} : { note }
235
+ });
236
+ },
237
+ permit: async (request) => {
238
+ const { local } = options;
239
+ let localMessage = DENIED;
240
+ const { answer, note } = await decide({
241
+ node: request.nodeId,
242
+ kind: "permission",
243
+ question: `Allow ${request.toolName}?`,
244
+ options: PERMISSION_OPTIONS,
245
+ context: inputContext(request.input)
246
+ }, local === void 0 ? void 0 : (signal) => {
247
+ let said;
248
+ return local.permit({
249
+ ...request,
250
+ signal,
251
+ onNote: (given) => {
252
+ said = given;
253
+ }
254
+ }).then((result) => {
255
+ if (!result.allow) localMessage = result.message;
256
+ const choice = result.allow ? "allow" : "deny";
257
+ return said === void 0 ? { answer: choice } : {
258
+ answer: choice,
259
+ note: said
260
+ };
261
+ });
262
+ }, request.signal);
263
+ if (note !== void 0) request.onNote?.(note);
264
+ return answer === "allow" ? { allow: true } : {
265
+ allow: false,
266
+ message: note === void 0 ? localMessage : deniedMessage(note)
267
+ };
268
+ }
269
+ };
270
+ };
56
271
  // ../core/src/control/control.ts
57
272
  const startControl = async (options) => {
58
273
  const { store, runId } = options;
@@ -482,6 +697,7 @@ const createReplay = (history, cutOff = /* @__PURE__ */ new Map(), mail, earlier
482
697
  return null;
483
698
  },
484
699
  previousSession: ({ id, item, round = 0 }) => item === void 0 || renewed.has(item.of) ? void 0 : [...fanOuts.items.get(id) ?? []].findLast(([before]) => before < round)?.[1].sessionId,
700
+ lastOutput: (id) => history.get(id)?.at(-1)?.output,
485
701
  kept: () => [...history.keys(), ...fanOuts.items.keys()],
486
702
  ...mail === void 0 ? {} : { mail },
487
703
  ...earlier === void 0 ? {} : { earlier }
@@ -589,6 +805,285 @@ const renderHelp = async (cmd, parent) => {
589
805
  const showHelp = async (cmd, parent) => {
590
806
  process.stdout.write(`${await renderHelp(cmd, parent)}\n\n`);
591
807
  };
808
+ // src/human.ts
809
+ /**
810
+ * @file The terminal `HumanChannel`: answers `ask_user` questions, gates and
811
+ * permission prompts on the terminal, one at a time, and denies or defers
812
+ * when no one is there. After a choice it offers one optional line for a
813
+ * note, which Enter skips. In a run it is the decision queue's local
814
+ * answerer, racing `ork decide`.
815
+ */
816
+ const PREVIEW = 160;
817
+ /** A document longer than this is offered (`d`) rather than printed. */
818
+ const SHORT_DOCUMENT_LINES = 40;
819
+ const NO_ONE_TO_PERMIT = "No one is at the terminal to approve this tool, so it is not allowed in this run.";
820
+ const NOTE_PROMPT = " Note for the run (optional, Enter to skip): ";
821
+ const NO_ONE_TO_ASK = "no one (nobody is at the terminal; use your best judgment and say so in your report)";
822
+ /** One line that shows what a tool is about to do. */
823
+ const previewInput = (toolName, input) => {
824
+ const key = TOOL_INPUT_KEYS.find((k) => typeof input[k] === "string");
825
+ const line = (key === void 0 ? JSON.stringify(input) : `${key}: ${String(input[key])}`).replaceAll(/\s+/gu, " ").trim();
826
+ return line.length > PREVIEW ? `${toolName} ${line.slice(0, 159)}…` : `${toolName} ${line}`;
827
+ };
828
+ /** The option an answer names: its number, or its id. */
829
+ const pickOption = (answer, options, recommended) => {
830
+ const value = answer.trim();
831
+ if (value === "") return recommended;
832
+ const index = Number(value);
833
+ if (Number.isInteger(index) && index >= 1 && index <= options.length) return options[index - 1]?.id;
834
+ return options.find((o) => o.id === value)?.id;
835
+ };
836
+ const optionLine = (option, index, recommended) => {
837
+ const label = option.label === void 0 ? "" : ` ${option.label}`;
838
+ const description = option.description === void 0 ? "" : style("dim", ` - ${option.description}`);
839
+ const mark = option.id === recommended ? style("green", " (recommended)") : "";
840
+ return ` ${String(index + 1)}) ${style("bold", option.id)}${label}${description}${mark}`;
841
+ };
842
+ /**
843
+ * Answers permission prompts and `ask_user` questions on the terminal, one at a
844
+ * time. Without a terminal it denies tools and leaves questions to the node. A
845
+ * withdrawn prompt (answered elsewhere) rejects, which frees the turn.
846
+ */
847
+ const createTerminalHuman = (io) => {
848
+ let rl;
849
+ let turn = Promise.resolve();
850
+ const say = (text) => {
851
+ io.output.write(`${text}\n`);
852
+ };
853
+ const prompt = (text, signal) => {
854
+ if (rl === void 0) {
855
+ rl = createInterface({
856
+ input: io.input,
857
+ output: io.output
858
+ });
859
+ if (io.interrupt !== void 0) rl.on("SIGINT", io.interrupt);
860
+ }
861
+ return signal === void 0 ? rl.question(text) : rl.question(text, { signal });
862
+ };
863
+ /** The optional line after a choice, when the asker takes a note. */
864
+ const takeNote = async (onNote, signal) => {
865
+ if (onNote === void 0) return;
866
+ const note = (await prompt(NOTE_PROMPT, signal)).trim();
867
+ if (note !== "") onNote(note);
868
+ };
869
+ const choose = async (hint, options, recommended, signal, document) => {
870
+ const answer = await prompt(` Choose${hint}: `, signal);
871
+ const picked = pickOption(answer, options, recommended);
872
+ if (picked !== void 0) return picked;
873
+ if (document !== void 0 && answer.trim() === "d") say(document);
874
+ else say(style("dim", " Type an option's number or id."));
875
+ return choose(hint, options, recommended, signal, document);
876
+ };
877
+ const oneAtATime = (work) => {
878
+ const result = turn.then(work);
879
+ turn = result.catch(() => null);
880
+ return result;
881
+ };
882
+ return {
883
+ permit: ({ nodeId, toolName, input, signal, onNote }) => oneAtATime(async () => {
884
+ if (signal?.aborted === true) throw new Error("the prompt was withdrawn");
885
+ say(`${style(["yellow", "bold"], "?")} ${style("bold", nodeId)} wants to run ${previewInput(toolName, input)}`);
886
+ if (!io.interactive) {
887
+ say(style("dim", ` denied: ${NO_ONE_TO_PERMIT}`));
888
+ return {
889
+ allow: false,
890
+ message: NO_ONE_TO_PERMIT
891
+ };
892
+ }
893
+ const answer = (await prompt(" Allow? [y/N] ", signal)).trim().toLowerCase();
894
+ if (answer === "y" || answer === "yes") return { allow: true };
895
+ await takeNote(onNote, signal);
896
+ return {
897
+ allow: false,
898
+ message: "The user did not allow this tool."
899
+ };
900
+ }),
901
+ ask: ({ nodeId, question, options, recommended, context, document, signal, onNote }) => oneAtATime(async () => {
902
+ if (signal?.aborted === true) throw new Error("the question was withdrawn");
903
+ const documentLines = document?.trimEnd().split("\n").length ?? 0;
904
+ if (document !== void 0 && documentLines <= SHORT_DOCUMENT_LINES) say(document);
905
+ say(`${style(["yellow", "bold"], "?")} ${style("bold", nodeId)} asks: ${question}`);
906
+ if (context !== void 0) say(style("dim", ` ${context.replaceAll("\n", "\n ")}`));
907
+ for (const [index, option] of options.entries()) say(optionLine(option, index, recommended));
908
+ if (!io.interactive) {
909
+ const answer = recommended ?? NO_ONE_TO_ASK;
910
+ say(style("dim", ` no terminal; answered ${answer}`));
911
+ return answer;
912
+ }
913
+ const fallback = options.findIndex((o) => o.id === recommended);
914
+ const hint = fallback === -1 ? "" : ` [${String(fallback + 1)}]`;
915
+ if (documentLines > SHORT_DOCUMENT_LINES) say(` Document: ${String(documentLines)} lines. Type d to read it.`);
916
+ const picked = await choose(hint, options, recommended, signal, document);
917
+ await takeNote(onNote, signal);
918
+ return picked;
919
+ }),
920
+ close: () => {
921
+ rl?.close();
922
+ }
923
+ };
924
+ };
925
+ // src/run/format-result.ts
926
+ const RUN_MARK = {
927
+ succeeded: style("green", "✓"),
928
+ failed: style("red", "✗"),
929
+ budget_exhausted: style("yellow", "!"),
930
+ quota_exhausted: style("yellow", "!"),
931
+ timed_out: style("yellow", "!"),
932
+ interrupted: style("yellow", "!")
933
+ };
934
+ /** `1 failure handled`, `2 failures handled`. */
935
+ const failuresHandled = (count) => `${String(count)} ${count === 1 ? "failure" : "failures"} handled`;
936
+ /**
937
+ * The run summary: status, cost, what ran, what was skipped and which failures
938
+ * an edge handled, branches to look at, why, and mail to nodes that never ran.
939
+ */
940
+ const formatRunResult = (result) => {
941
+ const ran = result.nodes.filter((n) => n.runs > 0).length;
942
+ const skipped = result.skipped ?? result.nodes.length - ran;
943
+ const handled = result.failuresHandled ?? 0;
944
+ const lines = ["", `${RUN_MARK[result.status]} run ${result.runId} ${result.status.replace("_", " ")} ${style("dim", `· ${cost(result.costUsd)} · ${String(ran)} ran, ${String(skipped)} skipped${handled === 0 ? "" : `, ${failuresHandled(handled)}`}`)}`];
945
+ for (const kept of result.worktrees) lines.push(` ${kept.id}: ${kept.branch ?? "detached"} ${style("dim", `at ${kept.path} (${kept.reason})`)}`);
946
+ for (const reason of result.reasons) lines.push(` ${style("red", reason)}`);
947
+ for (const { node, messages } of result.unread ?? []) lines.push(` ${style(["yellow", "bold"], "warning")} ${node} never ran, so ${messages === 1 ? "1 message" : `${String(messages)} messages`} to it went unread`);
948
+ return lines;
949
+ };
950
+ // src/run/format.ts
951
+ /** Who answered a decision, as the log says it. */
952
+ const ANSWERED_BY = {
953
+ terminal: "terminal",
954
+ decide: "ork decide",
955
+ ui: "ork ui",
956
+ owner: "its owner"
957
+ };
958
+ const STATUS_MARK = {
959
+ pass: style("green", "✓"),
960
+ done: style("green", "✓"),
961
+ changes_requested: style("yellow", "!"),
962
+ fail: style("red", "✗")
963
+ };
964
+ const seconds = (ms) => `${(ms / 1e3).toFixed(1)}s`;
965
+ const indent = (text) => text.trimEnd().split("\n").map((line) => ` ${line}`);
966
+ /** The usage line: cost, tokens, turns and time. */
967
+ const formatUsage = ({ usage }) => style("dim", [
968
+ cost(usage.costUsd),
969
+ `${String(usage.inputTokens)} in / ${String(usage.outputTokens)} out (${String(usage.cacheReadTokens)} cached)`,
970
+ `${String(usage.turns)} turns`,
971
+ seconds(usage.durationMs)
972
+ ].join(" · "));
973
+ /** How a finished session reads on the terminal. */
974
+ const formatOutcome = (nodeId, outcome) => {
975
+ const { report } = outcome;
976
+ if (report === null) return [
977
+ `${style("red", "✗")} ${style("bold", nodeId)} ended without a report (${outcome.subtype})`,
978
+ ...outcome.errors.map((e) => ` ${style("red", e)}`),
979
+ ` ${formatUsage(outcome)}`
980
+ ];
981
+ const lines = [`${STATUS_MARK[report.status] ?? "-"} ${style("bold", nodeId)} ${report.status}: ${report.summary}`];
982
+ if (report.branch !== void 0) lines.push(` branch: ${report.branch}`);
983
+ for (const [name, file] of Object.entries(report.artifacts)) lines.push(` ${name}: ${file}`);
984
+ for (const [name, value] of Object.entries(report.fields)) lines.push(` ${name}: ${JSON.stringify(value)}`);
985
+ lines.push(` ${formatUsage(outcome)}`);
986
+ return lines;
987
+ };
988
+ /** The transcript lines for one event; tool names come from `tools`. */
989
+ const formatEvent = (nodeId, event, tools) => {
990
+ if (event.type === "session.started") {
991
+ const fork = event.fork === true ? ", a fork to answer a question" : "";
992
+ return [style("dim", `▸ ${nodeId} (${event.model}, session ${event.sessionId}${fork})`)];
993
+ }
994
+ if (event.type === "assistant.text") return indent(event.text);
995
+ if (event.type === "tool.called") {
996
+ tools.set(event.id, event.name);
997
+ return [style("dim", ` · ${previewInput(event.name, event.input)}`)];
998
+ }
999
+ if (event.type === "tool.result") return event.isError ? [style("red", ` ✗ ${tools.get(event.id) ?? "tool"} failed`)] : [];
1000
+ if (event.type === "plan.limit") return event.status === "allowed" ? [] : [style("yellow", ` ! plan: ${event.window.replaceAll("_", " ")} limit ${event.status === "rejected" ? "reached" : "is close"}`)];
1001
+ return formatOutcome(nodeId, event.outcome);
1002
+ };
1003
+ const items = (count) => `${String(count)} ${count === 1 ? "item" : "items"}`;
1004
+ /** Transcript lines for one run event, prefixed with the node they are from. */
1005
+ const formatRunEvent = (event, width, tools, terminal = false) => {
1006
+ const tag = (id) => style("cyan", `${id.padEnd(width)} │`);
1007
+ if (event.type === "run.started") return [style("dim", `▸ run ${event.runId} of ${event.workflow} (starts at ${event.entries.join(", ")})`), ...event.settings?.trigger === void 0 ? [] : [style("dim", ` started by trigger ${event.settings.trigger}; untrusted inputs: ${(event.settings.untrusted ?? []).join(", ") || "none"}`)]];
1008
+ if (event.type === "node.started") {
1009
+ const round = event.round > 1 ? ` (round ${String(event.round)})` : "";
1010
+ const owner = event.owner === void 0 ? "" : `, for ${event.owner}`;
1011
+ return [`${tag(event.id)} ${style("dim", `${event.nodeType}${round}${owner}`)}`];
1012
+ }
1013
+ if (event.type === "node.event") {
1014
+ const inner = event.event;
1015
+ if (inner.type === "session.finished") return [];
1016
+ const fork = inner.fork === true ? style("dim", "fork · ") : "";
1017
+ if (inner.type === "session.started") {
1018
+ const why = inner.fork === true ? ", to answer a question" : "";
1019
+ return [`${tag(event.id)} ${fork}${style("dim", `${inner.model}, session ${inner.sessionId}${why}`)}`];
1020
+ }
1021
+ return formatEvent(event.id, event.event, tools).map((line) => `${tag(event.id)} ${fork}${line.replace(/^ {2}/u, "")}`);
1022
+ }
1023
+ if (event.type === "node.finished") {
1024
+ const conflict = hasConflict(event.output);
1025
+ const mark = event.ok ? style(conflict ? "yellow" : "green", conflict ? "!" : "✓") : style("red", "✗");
1026
+ const spent = event.usage === void 0 ? "" : style("dim", ` · ${cost(event.usage.costUsd)}`);
1027
+ return [`${tag(event.id)} ${mark} ${nodeVerdict(event.output, event.ok, event.error)}${spent}`];
1028
+ }
1029
+ if (event.type === "node.skipped") return [`${tag(event.id)} ${style("dim", "- skipped")}`];
1030
+ if (event.type === "loop.exhausted") return [`${style(["yellow", "bold"], "!")} the loop ${event.from} -> ${event.to} used all ${String(event.maxRounds)} rounds`];
1031
+ if (event.type === "watch.started") return [`${tag(event.id)} ${style("dim", `watching until ${event.until}, every ${event.every}`)}`];
1032
+ if (event.type === "watch.changed") return [`${tag(event.id)} ${style("dim", `look ${String(event.looks)}:`)} ${event.said}`];
1033
+ if (event.type === "watch.ended") return [`${tag(event.id)} ${style("dim", `${event.outcome} after ${String(event.looks)} looks`)}`];
1034
+ if (event.type === "edge.taken") return [];
1035
+ if (event.type === "worktree") {
1036
+ const detail = event.detail === void 0 ? "" : ` (${event.detail})`;
1037
+ const what = event.action === "removed" && event.branch !== void 0 ? `worktree of ${event.branch} (branch kept)` : `${event.branch ?? event.path}${detail}`;
1038
+ const line = `${event.action} ${what}`;
1039
+ return [`${tag(event.id)} ${event.action === "conflict" ? style("yellow", `! ${line}`) : style("dim", `⎇ ${line}`)}`];
1040
+ }
1041
+ if (event.type === "decision.opened") {
1042
+ const what = event.kind === "permission" ? "asks to" : "asks:";
1043
+ const question = event.kind === "permission" ? event.question.replace(/^Allow /u, "run ").replace(/\?$/u, "") : event.question;
1044
+ const where = event.owner === void 0 ? `decision ${event.decision}; ${terminal ? "answer here or with ork decide" : "ork decide"}` : `decision ${event.decision}, via ${event.owner}`;
1045
+ return [`${tag(event.node)} ${style(["yellow", "bold"], "?")} ${what} ${question} ${style("dim", `(${where})`)}`];
1046
+ }
1047
+ if (event.type === "decision.answered") {
1048
+ const by = event.by === "owner" && event.owner !== void 0 ? event.owner : ANSWERED_BY[event.by];
1049
+ const said = event.note === void 0 ? "" : ` · said: ${event.note.replaceAll(/\s+/gu, " ")}`;
1050
+ return [`${tag(event.node)} ${style("dim", `→ ${event.answer} (${by})${said}`)}`];
1051
+ }
1052
+ if (event.type === "decision.withdrawn") return [`${tag(event.node)} ${style("dim", "→ question withdrawn")}`];
1053
+ if (event.type === "run.command") {
1054
+ const node = event.node === void 0 ? "" : ` --node ${event.node}`;
1055
+ return [style(["yellow", "bold"], `! ork ${event.command}${node} received`)];
1056
+ }
1057
+ if (event.type === "node.held") {
1058
+ const at = event.at === "tool" ? "its tool call waits for ork continue" : "it starts after ork continue";
1059
+ return [`${tag(event.id)} ${style("yellow", `‖ paused: ${at}`)}`];
1060
+ }
1061
+ if (event.type === "node.released") return [`${tag(event.id)} ${style("dim", "▸ continues")}`];
1062
+ if (event.type === "each.listed") {
1063
+ const round = event.round > 1 ? ` (round ${String(event.round)})` : "";
1064
+ return [`${tag(event.node)} ${style("dim", `${items(event.count)}${round}`)}`];
1065
+ }
1066
+ if (event.type === "each.exceeded") {
1067
+ const over = `${items(event.count)}, over maxItems ${String(event.maxItems)}`;
1068
+ return [`${tag(event.node)} ${style("yellow", `! ${over}`)}`];
1069
+ }
1070
+ if (event.type === "run.finished") return formatRunResult(event.result);
1071
+ if (event.type === "run.resumed") {
1072
+ const [one] = event.kept;
1073
+ const kept = one === void 0 ? "every node runs again" : event.kept.length === 1 ? `${one} keeps its result` : `${event.kept.join(", ")} keep their results`;
1074
+ return ["", style("dim", `▸ resumed: ${kept}`)];
1075
+ }
1076
+ if (event.type === "feedback.recorded") {
1077
+ const { target, kind, title } = event.feedback;
1078
+ const line = style("dim", `✎ feedback (${target}, ${kind}): ${title}`);
1079
+ return [event.node === void 0 ? line : `${tag(event.node)} ${line}`];
1080
+ }
1081
+ if (event.type === "user.message") {
1082
+ const note = event.delivered === "queued" ? " (for its next session)" : "";
1083
+ return [`${tag(event.node)} ${style(["magenta", "bold"], "you:")} ${firstLine$1(event.body)}${style("dim", note)}`];
1084
+ }
1085
+ return formatBusEvent(event).map((line) => `${tag(event.node)} ${line}`);
1086
+ };
592
1087
  // src/update/notify.ts
593
1088
  /**
594
1089
  * @file The new-version notice (ADR-038). At most once a day a command starts a
@@ -684,7 +1179,7 @@ const versionCheck = (current, cache, checksOff) => {
684
1179
  return {
685
1180
  name: "ork",
686
1181
  status: "ok",
687
- detail: `${current} (${cache?.latest === void 0 ? checksOff ? "update check off" : "not checked for updates yet" : `the latest as of ${cache.checkedAt.slice(0, 10)}`})`
1182
+ detail: `${current} (${cache?.latest === void 0 ? checksOff === null ? "not checked for updates yet" : `the daily update check is off here: ${checksOff}` : `the latest as of ${cache.checkedAt.slice(0, 10)}`})`
688
1183
  };
689
1184
  };
690
1185
  // src/doctor/checks.ts
@@ -708,7 +1203,7 @@ const exec = (program, args) => new Promise((resolve) => {
708
1203
  });
709
1204
  });
710
1205
  /** Ork's own version, against the last update check (no network). */
711
- const orkCheck = () => versionCheck(ORK_VERSION, readUpdateCache(updateCachePath()), updateCheckOff(process.env, [], true) !== null);
1206
+ const orkCheck = () => versionCheck(ORK_VERSION, readUpdateCache(updateCachePath()), updateCheckOff(process.env, [], process.stderr.isTTY));
712
1207
  const MIN_NODE = [24, 20];
713
1208
  const checkNode = (version) => {
714
1209
  const [major = 0, minor = 0] = version.split(".").map(Number);
@@ -724,6 +1219,23 @@ const checkNode = (version) => {
724
1219
  hint: `Ork needs Node ${MIN_NODE.join(".")} or later.`
725
1220
  };
726
1221
  };
1222
+ /** A work tree with no commit has no `HEAD` for a worktree to branch from. */
1223
+ const checkHead = async (run) => {
1224
+ return (await run("git", [
1225
+ "rev-parse",
1226
+ "--verify",
1227
+ "HEAD"
1228
+ ])).ok ? {
1229
+ name: "git commits",
1230
+ status: "ok",
1231
+ detail: "HEAD exists"
1232
+ } : {
1233
+ name: "git commits",
1234
+ status: "warn",
1235
+ detail: "no commit yet",
1236
+ hint: "Make a first commit: worktree runs branch from HEAD."
1237
+ };
1238
+ };
727
1239
  const checkGit = async (run) => {
728
1240
  const version = await run("git", ["--version"]);
729
1241
  if (!version.ok) return [{
@@ -733,20 +1245,30 @@ const checkGit = async (run) => {
733
1245
  hint: "Install git; Ork gives each writing node its own worktree."
734
1246
  }];
735
1247
  const inside = await run("git", ["rev-parse", "--is-inside-work-tree"]);
736
- return [{
737
- name: "git",
738
- status: "ok",
739
- detail: firstLine(version.stdout)
740
- }, inside.ok ? {
741
- name: "git repo",
742
- status: "ok",
743
- detail: "inside a work tree"
744
- } : {
745
- name: "git repo",
746
- status: "warn",
747
- detail: "not a git repository",
748
- hint: "Run Ork inside a git repository so nodes can use worktrees."
749
- }];
1248
+ const head = inside.ok ? await checkHead(run) : null;
1249
+ return [
1250
+ {
1251
+ name: "git",
1252
+ status: "ok",
1253
+ detail: firstLine(version.stdout)
1254
+ },
1255
+ inside.ok ? {
1256
+ name: "git repo",
1257
+ status: "ok",
1258
+ detail: "inside a work tree"
1259
+ } : {
1260
+ name: "git repo",
1261
+ status: "warn",
1262
+ detail: "not a git repository",
1263
+ hint: "Run Ork inside a git repository so nodes can use worktrees."
1264
+ },
1265
+ ...head === null ? [] : [head]
1266
+ ];
1267
+ };
1268
+ /** What `ork init` warns of: a work tree with no commit, else null. */
1269
+ const headWarning = async (run) => {
1270
+ const head = (await checkGit(run)).find((r) => r.name === "git commits");
1271
+ return head?.status === "warn" ? `${head.detail}. ${head.hint ?? ""}`.trim() : null;
750
1272
  };
751
1273
  /**
752
1274
  * Runs every check: Node and git, then every registered Claude driver and
@@ -1011,6 +1533,16 @@ const createLogPrinter = (options) => {
1011
1533
  };
1012
1534
  };
1013
1535
  /**
1536
+ * Why `--node` names nothing in this run, or `null`: no event names it and the
1537
+ * run didn't declare it. A live run may still start it, so that is not
1538
+ * refused.
1539
+ */
1540
+ const unknownNode = (stored, node, live) => {
1541
+ const named = stored.some(({ event }) => isNodeEvent(event, node));
1542
+ const declared = stored.some(({ event }) => event.type === "run.started" && event.nodes.some((n) => n.id === node));
1543
+ return named || declared || live ? null : `Run has no node ${node}.`;
1544
+ };
1545
+ /**
1014
1546
  * Prints a run's events; with `follow`, then each new one as it is stored:
1015
1547
  * pushed over the run's socket, or polled from the store when there is none.
1016
1548
  * Returns when `run.finished` is stored, the run's process is gone, or `signal`
@@ -1537,7 +2069,7 @@ const runWorkflowFile = async (options) => {
1537
2069
  for (const event of signals(raw)) {
1538
2070
  control.publish(record(event));
1539
2071
  if (options.json) options.out(JSON.stringify(event));
1540
- else for (const line of formatRunEvent(event, width, tools)) options.out(line);
2072
+ else for (const line of formatRunEvent(event, width, tools, options.human !== void 0)) options.out(line);
1541
2073
  }
1542
2074
  };
1543
2075
  const workspace = root === void 0 ? void 0 : createWorkspace({
@@ -1634,7 +2166,7 @@ const flagValues = (rawArgs, flag) => {
1634
2166
  };
1635
2167
  const isDriver = (value) => CLAUDE_DRIVERS.some((d) => d.id === value);
1636
2168
  /** Loads the drivers only when a run starts, so other commands stay fast. */
1637
- const loadRuntime = async (driver) => (await import("./src-DhGLtYR-.mjs")).createRuntime(driver, sessionEnv(process.env));
2169
+ const loadRuntime = async (driver) => (await import("./src-bOImrq43.mjs")).createRuntime(driver, sessionEnv(process.env));
1638
2170
  /**
1639
2171
  * Ctrl-C asks the run to stop, and so does a terminal closing or a polite kill,
1640
2172
  * so that the run ends tidily and leaves no result unscrubbed.
@@ -1652,11 +2184,18 @@ const onInterrupt = (stop) => {
1652
2184
  process.once("SIGHUP", interrupt);
1653
2185
  };
1654
2186
  /** The terminal that answers a run's questions; none in a detached run. */
1655
- const terminalHuman = () => process.env["ORK_DETACHED"] === "1" ? void 0 : createTerminalHuman({
1656
- input: process.stdin,
1657
- output: process.stderr,
1658
- interactive: process.stdin.isTTY
1659
- });
2187
+ const terminalHuman = () => {
2188
+ const detached = process.env[DETACHED_ENV] === "1";
2189
+ if (detached) writeErr(style("dim", "detached run: no terminal asks; answer with ork decide"));
2190
+ return detached ? void 0 : createTerminalHuman({
2191
+ input: process.stdin,
2192
+ output: process.stderr,
2193
+ interactive: process.stdin.isTTY,
2194
+ interrupt: () => {
2195
+ process.kill(process.pid, "SIGINT");
2196
+ }
2197
+ });
2198
+ };
1660
2199
  const run = defineCommand({
1661
2200
  meta: {
1662
2201
  name: "run",
@@ -2321,7 +2860,7 @@ const ORK_SKILLS = {
2321
2860
  principles: "---\nname: principles\ndescription: Use when deciding how to split work into an Ork workflow - which steps are programs, which are Claude sessions, where a person decides - so it costs less than doing it by hand and can't go wrong quietly.\n---\n\n# How Ork splits work\n\nOrk exists to do long jobs for less than doing them by hand, and safely. Four\nrules decide the shape of a workflow.\n\n## 1. A program for what a program can do\n\nA model turn is for judgment. Anything a program can decide, it should: run the\ntests, read a pull request's state, count what changed, wait for CI.\n\n- **A tool node** (`tool.shell`, `tool.git`, `tool.gh`, `tool.aws`) or an\n **`mcp.call`** does it with no session. With `output: { format: json }`, its\n answer routes the run: `when: \"json.state == 'OPEN'\"`.\n- **`verify:`** on a session runs a check before its report counts, so a session\n can't claim success past failing tests. **`before:`** hands it what a program\n found, so it doesn't spend turns finding it.\n- **`watch:`** waits on CI or a review with a program that looks every few\n seconds; hours of waiting cost nothing.\n- **`notify:`** lets a program tell a running session what it found, with no\n model turn to carry the news.\n\n## 2. Spend where it pays\n\n- A small model and low effort for a narrow task (`model: haiku`,\n `effort: low`); the large ones where thinking pays.\n- `budgetUsd` on each session and on the run (`limits.budgetUsd`). A loop of\n sessions without a budget or `limits.timeout` gets a warning, for good reason.\n- Give a session what it needs in its `prompt` and `before:`, rather than\n letting it search; and `isolation: readonly` for a reviewer that only reads.\n\n## 3. Make a mistake impossible before checking for it\n\n- Declare what a node says: `output.schema` lists on tool JSON and Claude\n reports (`verdict: [approve, escalate]`). Conditions then compare against a\n closed list, and a run fails a value off it rather than matching nothing.\n- A verdict is a declared field, never words to search a summary for.\n- Give each condition its opposite, and each failure a handler (`when: \"!ok\"`).\n- Check every draft with Ork's `check` tool until it's clean.\n\n## 4. A person decides what can't be undone\n\nMerging, deploying, publishing, spending a lot: put a `human.gate` before it,\nwith the safe option `recommended`. For a run nobody watches, give it a\n`timeout` and the safe `default`. You never answer a gate yourself.\n\n## An example\n\nA review that a program checks first, and a person signs off:\n\n```yaml\nschemaVersion: 1\nname: careful-review\ndescription: Lint with a program, review with a small model, and ask before merging.\ninputs:\n pr:\n type: string\n required: true\nlimits:\n budgetUsd: 3\nnodes:\n lint:\n type: tool.shell\n command: pnpm lint\n allow: [pnpm lint]\n review:\n type: claude.worker\n prompt: \"Review pull request {{inputs.pr}}. The linter passed.\"\n model: haiku\n effort: low\n isolation: readonly\n budgetUsd: 1\n output:\n schema:\n verdict: [approve, escalate]\n ask:\n type: human.gate\n question: \"Merge pull request {{inputs.pr}}? The review says {{nodes.review.output.verdict}}.\"\n options: [merge, wait]\n recommended: wait\n merge:\n type: tool.gh\n command: pr merge {{inputs.pr}} --squash\n allow: [pr merge]\nedges:\n - lint -> review\n - review -> ask\n - from: ask\n to: merge\n when: \"choice == 'merge'\"\n```\n\n`lint` costs nothing, `review` is a small model with a cap, and nothing merges\nwithout a person.\n",
2322
2861
  run: "---\nname: run\ndescription: Use when starting, following or explaining an Ork run - what it's doing, why it failed, what it cost, and what waits on the person - and when the person asks about their Ork runs.\n---\n\n# Running and following Ork workflows\n\nOrk runs workflows on the person's machine with their accounts, so a run spends\ntheir money and can push, merge or deploy. Start one only when they ask, and\nnever answer a decision for them.\n\n## Starting\n\n1. Find it: Ork's `workflows` tool gives each workflow's name and inputs.\n2. Check it first with `check` (`workflow: <name>`). Don't start one with an\n error.\n3. Start it with `start_run`, giving every required input. Claude Code asks the\n person first, unless they've allowed it. It answers with the run's id; the run goes on in the\n background, whatever happens to this conversation.\n\nWithout Ork's tools: `ork run <workflow> --detach --input name=value`.\n\n## Following\n\n- `run` gives a run's status, what it waits on, its cost so far, and each\n node's state. `runs` lists the newest runs.\n- `run_events` gives its newest events; `node` narrows them, and `last` or\n `after` widens them. A `node.finished` event with `ok: false` says why a node\n failed.\n- `explain_context` says what a node's session was given: its agent,\n instructions, skills and files. Use it when a node didn't know something.\n- `usage` says what runs spent, by workflow and model, and how close the limits\n are.\n\nSay what you found plainly: which node, what it said, and what it cost. Quote a\nnode's own words rather than retelling them.\n\n## What only the person does\n\n- **Decisions.** `decisions` lists the questions waiting on them: a gate, a loop\n that ran out of rounds, a permission. Tell them what each asks, the options,\n and the recommended one, and that they answer in `ork ui` or with\n `ork decide` in their own terminal. Don't answer one: never run\n `ork decide --id … --choose …`; with Ork's plugin, a hook stops it.\n- **Stopping, pausing, resuming.** Tell them the command: `ork stop <run>`,\n `ork pause <run>`, `ork continue <run>`, `ork resume <run>`. A node can be\n stopped alone with `--node <id>`.\n\n## Without Ork's tools\n\n| Want | Command |\n| ---------------------- | ------------------------------ |\n| Runs, newest first | `ork ps` |\n| A run's transcript | `ork logs <run>` (`--follow`) |\n| Its nodes and cost | `ork tree <run>` |\n| What runs spent | `ork usage` |\n| What waits on a person | `ork decide --list` |\n| A node's context | `ork context explain <wf> <n>` |\n",
2323
2862
  setup: "---\nname: setup\ndescription: Use when the person asks you to set up Ork, install it, or get started with it in a repository - checking it's installed, setting up the project, making Ork's tools available to you, and a first workflow.\n---\n\n# Setting up Ork\n\nOrk runs workflows on the person's machine with their own accounts. Setting it\nup changes their machine and their repository, so say what each step does and\nask before it. Work through these in order, and stop where they say no.\n\n## 1. Check\n\n```sh\nork --version\nork doctor\n```\n\n- **No `ork`:** it needs Node 24.20 or later (`node --version`). The install is\n `npm install --global @krapht/ork`; show it and run it only when they say\n yes. The guide:\n https://github.com/mimshins/ork/blob/main/docs/getting-started/installation.md\n- **Read `ork doctor` to them.** It needs git, a git repository with a commit,\n and one Claude driver. Don't run `git init` or a commit without asking.\n- **A driver not ready:** they log in themselves, with `claude` (the CLI\n driver), or by setting `ANTHROPIC_API_KEY` or a cloud provider in their own\n shell (the SDK driver). Never ask for a key, never take one pasted to you, and\n never write one into a file or a command. `gh` and `aws` are optional, and\n their logins are their own too (`gh auth login`, `aws sso login`).\n\n## 2. The project\n\n- **`ork init`** keeps Ork's files in the repository, in `.ork/`, committed with\n the code. It adds `.ork/runs/` to `.gitignore`, links `.claude/` and similar\n folders to `.ork/`, and writes a starter workflow, `hello`.\n- **`ork register`** keeps them beside it, in `~/.ork/projects/<id>/`, and\n writes nothing into the repository. For a repository whose process isn't\n theirs to change, or to try Ork first.\n\nExplain both and ask which. Skip the question when the repository decides it: a\n`.ork/` folder means `ork init` has run (`ork projects` lists what's\nregistered). Neither overwrites a file.\n\n## 3. Make Ork available to you\n\nIf Ork's tools (`workflows`, `check`) already answer you, skip this. Otherwise,\nby where you run, and asking before each change to a config:\n\n- **Claude Code:** Ork's plugin brings the skills and the tools. The person\n types these themselves:\n\n ```\n /plugin marketplace add mimshins/ork\n /plugin install ork@ork\n ```\n\n Or, in a project set up with `ork init`, the skills in the repository for\n everyone who clones it, then the tools:\n\n ```sh\n ork init --skills\n claude mcp add ork -- ork mcp\n ```\n\n New tools start when they restart Claude Code or reconnect `ork` in `/mcp`;\n so does the plugin's, once `ork` is installed.\n\n- **Another MCP client:** add a server named `ork` with command `ork` and args\n `[\"mcp\"]` to its MCP config, where its own docs say. Its tools find the\n project by its id, from `ork projects`.\n- **A shell only:** use the CLI. `ork --help` and each command's `--help` say\n what it takes.\n\n## 4. A first workflow\n\nOffer to write one for a task they name. Follow `/ork:write`, check each draft\n(the `check` tool, or `ork validate <file>`) until it has no error, then save\nit. Don't start it: a run spends their money. Start it only when they say so,\nin the background with its inputs (`start_run`, or\n`ork run <name> --detach --input name=value`), and follow it with `/ork:run`.\n\n## 5. Show them `ork ui`\n\nTell them to run `ork ui` in their own terminal: their runs, graphs and logs in\nthe browser, and where they answer what a run asks.\n\n## Never\n\n- Ask for, store or pass on a password, key or token.\n- Answer a run's decision: they answer in `ork ui` or with `ork decide` in\n their own terminal.\n- Install, edit a config, or start a run without a yes.\n",
2324
- write: "---\nname: write\ndescription: Use when writing or changing an Ork workflow (a `.ork/workflows/*.ork.yaml` file) - its nodes, edges, conditions and loops - and checking each draft until Ork accepts it.\n---\n\n# Writing an Ork workflow\n\nAn Ork workflow is one YAML file in the project, `.ork/workflows/<name>.ork.yaml`.\nIt names the nodes (Claude sessions, programs, a person's decisions) and the\nedges that carry work between them. Ork runs it on the person's machine with\ntheir own accounts.\n\n## How to work\n\n1. **Look first.** Ork's `workflows` tool lists the project's workflows; read\n one that's close. `agents` lists the agents a node can use. Before you split\n the work, read `/ork:principles`: a program where a program will do, a\n session only for judgment.\n2. **Don't guess a field.** `workflow_format` gives the format; ask it for one\n node type (`nodeType: \"human.gate\"`) to see that type's fields.\n3. **Check every draft** with Ork's `check` tool (`text`, or `workflow` for a\n file), and fix what it says until it has no error. Read its warnings: they\n mean something. Without Ork's tools, run `ork validate <file>`.\n4. **Then save the file**, beginning with the schema line below, so editors can\n check it too.\n\n## The shape of a file\n\n```yaml\n# yaml-language-server: $schema=https://raw.githubusercontent.com/mimshins/ork/main/packages/schema/workflow.schema.json\nschemaVersion: 1\nname: fix-and-ship\ndescription: Fix a failing test, check it with the test suite, and ask before opening a pull request.\ninputs:\n issue:\n type: string\n required: true\n description: What fails.\ndefaults:\n model: sonnet\n budgetUsd: 2\nlimits:\n budgetUsd: 10\nnodes:\n fix:\n type: claude.worker\n prompt: \"Fix this, and keep the change small: {{inputs.issue}}\"\n tests:\n type: tool.shell\n command: pnpm test\n allow: [pnpm test]\n ask:\n type: human.gate\n question: \"Open a pull request for {{nodes.fix.output.branch}}?\"\n options: [open, hold]\n recommended: hold\n pr:\n type: tool.gh\n command: pr create --fill --head {{nodes.fix.output.branch}}\n allow: [pr create]\nedges:\n - fix -> tests\n - from: tests\n to: fix\n when: \"!ok\"\n maxRounds: 2\n - from: tests\n to: ask\n when: ok\n - from: ask\n to: pr\n when: \"choice == 'open'\"\n```\n\nNode ids are lowercase: letters, digits, `-` and `_`. Secrets never go in the\nfile: a tool reads one with `env:` and `{{env.NAME}}`.\n\n## Nodes\n\n| Type | Does |\n| --------------------------------- | ------------------------------------------------------------------- |\n| `claude.worker` | A Claude session that does one task and reports |\n| `claude.leader` | A session that plans and delegates to the nodes it owns (`parent:`) |\n| `claude.subagent` | A subagent inside its parent's session; it takes no edges |\n| `tool.shell` | Any program, run without a shell; `allow:` lists command prefixes |\n| `tool.git`, `tool.gh`, `tool.aws` | The person's own `git`, `gh`, `aws`, with an allow list |\n| `mcp.call` | One tool call on an MCP server, with no session |\n| `human.gate` | A question with options and a recommended one |\n| `control.join` | Waits for its inputs: `all` (default), `any`, or `n` of them |\n| `control.router`, `control.split` | Pass their input on; a router takes its first matching edge |\n| `workflow.call` | Starts another workflow as its own run |\n\n- **A Claude node reports** `status` (`pass`, `changes_requested`, `done` or\n `fail`), `summary`, `artifacts`, `branch`, and any field its `output.schema`\n declares, such as `verdict: [approve, escalate]`. For another verdict, declare\n a field: `status` can't take other values.\n- **A tool node reports** `exitCode`, `stdout`, `stderr`, and with\n `output: { format: json, schema: { state: [OPEN, CLOSED] } }` its JSON as\n `json`. Declare the fields you read: a run then checks them, and a typo can't\n pass.\n- **A gate reports** `choice` and `answeredBy`. A `timeout` needs a `default`.\n\n## Edges and conditions\n\n- `a -> b`, `a -> b, c`, `b, c -> j` are plain edges, taken when the source\n succeeded. A failed node takes no plain edge: handle a failure with\n `when: \"!ok\"`, or `status == 'fail'` on a Claude node.\n- `{ from: a, to: b, when: \"...\" }` is taken when the condition holds. A bare\n name reads the source's output: `when: \"verdict == 'approve'\"`. Quote any\n condition that starts with `!`, or YAML reads it as a tag.\n- Conditions have `==`, `!=`, `<`, `>`, `&&`, `||`, `!`, and `any`, `all`,\n `count`, `len`, `contains`. A join's `statuses` suits `any(statuses, 'fail')`.\n- A node runs when any edge into it is taken; only a join waits. A node with no\n way in taken is skipped, and so is what follows it.\n- Templates, `{{inputs.x}}`, `{{nodes.a.output.field}}`, `{{run.id}}`, go in\n `prompt`, `instructions`, `command`, `cwd` and `question`. Each value stays\n one argument of a command, so it can't inject one.\n\n## Loops\n\nEvery cycle needs an edge with `maxRounds`. Give the way back a condition and\nthe way out its opposite, as `!ok` and `ok` above; a plain edge beside a loop\nfires every round. When a loop runs out, Ork asks a person what to do.\n\n## What `check` refuses, and why\n\n- **A value the source can't have** (`when.value`): `status == 'failed'` (it's\n `fail`), a gate's `choice == 'aprove'` for `approve`, a value outside a\n declared list. The message names the values it can have.\n- **A loop no condition ends**, a plain edge beside a loop, a loop of sessions\n with no `limits.budgetUsd` or `limits.timeout`: warnings, each worth fixing.\n- **`no way the walk tried reaches x`**: Ork tried every way through, and none\n got to `x`. A join waiting on a branch a first-match router never takes is\n the usual cause.\n"
2863
+ write: "---\nname: write\ndescription: Use when writing or changing an Ork workflow (a `.ork/workflows/*.ork.yaml` file) - its nodes, edges, conditions and loops - and checking each draft until Ork accepts it.\n---\n\n# Writing an Ork workflow\n\nAn Ork workflow is one YAML file in the project, `.ork/workflows/<name>.ork.yaml`.\nIt names the nodes (Claude sessions, programs, a person's decisions) and the\nedges that carry work between them. Ork runs it on the person's machine with\ntheir own accounts.\n\n## How to work\n\n1. **Look first.** Ork's `workflows` tool lists the project's workflows; read\n one that's close. `agents` lists the agents a node can use. Before you split\n the work, read `/ork:principles`: a program where a program will do, a\n session only for judgment.\n2. **Don't guess a field.** `workflow_format` gives the format; ask it for one\n node type (`nodeType: \"human.gate\"`) to see that type's fields.\n3. **Check every draft** with Ork's `check` tool (`text`, or `workflow` for a\n file), and fix what it says until it has no error. Read its warnings: they\n mean something. Without Ork's tools, run `ork validate <file>`.\n4. **Then save the file**, beginning with the schema line below, so editors can\n check it too.\n\n## The shape of a file\n\n```yaml\n# yaml-language-server: $schema=https://raw.githubusercontent.com/mimshins/ork/main/packages/schema/workflow.schema.json\nschemaVersion: 1\nname: fix-and-ship\ndescription: Fix a failing test, check it with the test suite, and ask before opening a pull request.\ninputs:\n issue:\n type: string\n required: true\n description: What fails.\ndefaults:\n model: sonnet\n budgetUsd: 2\nlimits:\n budgetUsd: 10\nnodes:\n fix:\n type: claude.worker\n prompt: \"Fix this, and keep the change small: {{inputs.issue}}\"\n tests:\n type: tool.shell\n command: pnpm test\n allow: [pnpm test]\n ask:\n type: human.gate\n question: \"Open a pull request for {{nodes.fix.output.branch}}?\"\n options: [open, hold]\n recommended: hold\n pr:\n type: tool.gh\n command: pr create --fill --head {{nodes.fix.output.branch}}\n allow: [pr create]\nedges:\n - fix -> tests\n - from: tests\n to: fix\n when: \"!ok\"\n maxRounds: 2\n - from: tests\n to: ask\n when: ok\n - from: ask\n to: pr\n when: \"choice == 'open'\"\n```\n\nNode ids are lowercase: letters, digits, `-` and `_`. Secrets never go in the\nfile: a tool reads one with `env:` and `{{env.NAME}}`.\n\n## Nodes\n\n| Type | Does |\n| --------------------------------- | ------------------------------------------------------------------- |\n| `claude.worker` | A Claude session that does one task and reports |\n| `claude.leader` | A session that plans and delegates to the nodes it owns (`parent:`) |\n| `claude.subagent` | A subagent inside its parent's session; it takes no edges |\n| `tool.shell` | Any program, run without a shell; `allow:` lists command prefixes |\n| `tool.git`, `tool.gh`, `tool.aws` | The person's own `git`, `gh`, `aws`, with an allow list |\n| `mcp.call` | One tool call on an MCP server, with no session |\n| `human.gate` | A question with options and a recommended one |\n| `control.join` | Waits for its inputs: `all` (default), `any`, or `n` of them |\n| `control.router`, `control.split` | Pass their input on; a router takes its first matching edge |\n| `workflow.call` | Starts another workflow as its own run |\n\nA worker, tool node, `mcp.call` or `workflow.call` can run once per entry of a\nlist: see [One node per item](#one-node-per-item).\n\n- **A Claude node reports** `status` (`pass`, `changes_requested`, `done` or\n `fail`), `summary`, `artifacts`, `branch`, and any field its `output.schema`\n declares, such as `verdict: [approve, escalate]`. For another verdict, declare\n a field: `status` can't take other values.\n- **A tool node reports** `exitCode`, `stdout`, `stderr`, and with\n `output: { format: json, schema: { state: [OPEN, CLOSED] } }` its JSON as\n `json`. Declare the fields you read: a run then checks them, and a typo can't\n pass.\n- **A gate reports** `choice` and `answeredBy`. A `timeout` needs a `default`.\n\n## Edges and conditions\n\n- `a -> b`, `a -> b, c`, `b, c -> j` are plain edges, taken when the source\n succeeded. A failed node takes no plain edge: handle a failure with\n `when: \"!ok\"`, or `status == 'fail'` on a Claude node.\n- `{ from: a, to: b, when: \"...\" }` is taken when the condition holds. A bare\n name reads the source's output: `when: \"verdict == 'approve'\"`. Quote any\n condition that starts with `!`, or YAML reads it as a tag.\n- Conditions have `==`, `!=`, `<`, `>`, `&&`, `||`, `!`, and `any`, `all`,\n `count`, `len`, `contains`. A join's `statuses` suits `any(statuses, 'fail')`.\n- A node runs when any edge into it is taken; only a join waits. A node with no\n way in taken is skipped, and so is what follows it.\n- Templates, `{{inputs.x}}`, `{{nodes.a.output.field}}`, `{{run.id}}`, go in\n `prompt`, `instructions`, `command`, `cwd` and `question`. Each value stays\n one argument of a command, so it can't inject one.\n\n## One node per item\n\n`forEach` runs a worker, a tool node, an `mcp.call` or a `workflow.call` once\nper entry of a list, with no model turn to fan out or gather. Not a leader, a\ngate, a subagent or a control node.\n\n```yaml\nplan:\n type: claude.worker\n prompt: \"Split this into at most 6 independent tasks: {{inputs.goal}}\"\n isolation: readonly\n output:\n schema:\n tasks:\n list: { title: string, detail: string } # declare the list and its shape\nbuild:\n type: claude.worker\n prompt: \"Task {{index}}: {{item.title}}. {{item.detail}}\"\n isolation: worktree\n forEach:\n from: \"{{nodes.plan.output.tasks}}\" # exactly one path to a declared list\n maxItems: 6 # required with `from`: the cap on sessions started\n maxParallel: 3\n```\n\n- `forEach: { items: [a, b] }` takes a list written in the file instead.\n- `{{item}}` and `{{index}}` work only in a node with `forEach`, in `prompt`,\n `command`, `arguments`, `inputs`, `env` and `ref`. A program also gets\n `ORK_ITEM` and `ORK_INDEX`.\n- The next node reads `items`, `results` (each item's output, `summary`,\n `branch`), `statuses` and `count`: `{{nodes.build.output.results}}`,\n `when: \"all(statuses, 'pass')\"`. Items follow no edges; the node does.\n- `onFailure: finish` (default) runs every item; `stop` starts no more after a\n failure. A list over `maxItems` makes Ork ask a person.\n- Declare the source's `list`, or `check` warns `forEach.undeclared`. Fix\n `forEach.not-list`, `forEach.order` and `item.field` errors; they name the\n field or node.\n\n## Loops\n\nEvery cycle needs an edge with `maxRounds`. Give the way back a condition and\nthe way out its opposite, as `!ok` and `ok` above; a plain edge beside a loop\nfires every round. When a loop runs out, Ork asks a person what to do.\n\n## What `check` refuses, and why\n\n- **A value the source can't have** (`when.value`): `status == 'failed'` (it's\n `fail`), a gate's `choice == 'aprove'` for `approve`, a value outside a\n declared list. The message names the values it can have.\n- **A loop no condition ends**, a plain edge beside a loop, a loop of sessions\n with no `limits.budgetUsd` or `limits.timeout`: warnings, each worth fixing.\n- **`no way the walk tried reaches x`**: Ork tried every way through, and none\n got to `x`. A join waiting on a branch a first-match router never takes is\n the usual cause.\n"
2325
2864
  };
2326
2865
  // src/project/init-skills.ts
2327
2866
  /**
@@ -4188,6 +4727,12 @@ const logs = defineCommand({
4188
4727
  process.exitCode = 1;
4189
4728
  return;
4190
4729
  }
4730
+ const problem = args.node === void 0 ? null : unknownNode(store.events(found.run.id), args.node, args.follow && found.run.status === "running");
4731
+ if (problem !== null) {
4732
+ writeErr(problem);
4733
+ process.exitCode = 1;
4734
+ return;
4735
+ }
4191
4736
  const abort = new AbortController();
4192
4737
  process.once("SIGINT", () => {
4193
4738
  abort.abort();
@@ -4352,6 +4897,8 @@ const init = defineCommand({
4352
4897
  skills: args.skills,
4353
4898
  link: args.link
4354
4899
  });
4900
+ const noCommit = await headWarning(exec);
4901
+ if (noCommit !== null) write(style("yellow", `\n! git: ${noCommit}`));
4355
4902
  write(`\nNext: edit ${WORKFLOW_DIR}/, then run ${style("bold", "ork validate")}.`);
4356
4903
  }
4357
4904
  });
@@ -4550,27 +5097,27 @@ const main = defineCommand({
4550
5097
  ps,
4551
5098
  logs,
4552
5099
  tree,
4553
- usage: () => import("./usage-BuzDsEhq.mjs").then((module) => module.usage),
4554
- prune: () => import("./usage-BuzDsEhq.mjs").then((module) => module.prune),
5100
+ usage: () => import("./usage-DpHXbQEu.mjs").then((module) => module.usage),
5101
+ prune: () => import("./usage-DpHXbQEu.mjs").then((module) => module.prune),
4555
5102
  decide,
4556
- relay: () => import("./relay-Cp8JGrdv.mjs").then((module) => module.relay),
4557
- pause: () => import("./pause-DhgKQORW.mjs").then((module) => module.pause),
4558
- continue: () => import("./pause-DhgKQORW.mjs").then((module) => module.continueRun),
5103
+ relay: () => import("./relay-BxRhxL6n.mjs").then((module) => module.relay),
5104
+ pause: () => import("./pause-h66SC4rV.mjs").then((module) => module.pause),
5105
+ continue: () => import("./pause-h66SC4rV.mjs").then((module) => module.continueRun),
4559
5106
  stop,
4560
5107
  chat,
4561
- send: () => import("./send-CHiYkM6X.mjs").then((module) => module.send),
5108
+ send: () => import("./send-BskOwN4p.mjs").then((module) => module.send),
4562
5109
  resume,
4563
5110
  feedback,
4564
5111
  context,
4565
- worktrees: () => import("./worktrees-DBBcaimX.mjs").then((module) => module.worktrees),
4566
- wt: () => import("./worktrees-DBBcaimX.mjs").then((module) => module.worktrees),
4567
- watch: () => import("./watch-CCj0vU3B.mjs").then((module) => module.watch),
4568
- mcp: () => import("./mcp-BFl3cIDp.mjs").then((module) => module.mcp),
4569
- ui: () => import("./ui-BxE0L7V0.mjs").then((module) => module.ui),
5112
+ worktrees: () => import("./worktrees-Ccf2lUhE.mjs").then((module) => module.worktrees),
5113
+ wt: () => import("./worktrees-Ccf2lUhE.mjs").then((module) => module.worktrees),
5114
+ watch: () => import("./watch-CJO1ee6Y.mjs").then((module) => module.watch),
5115
+ mcp: () => import("./mcp-DTreb7Nq.mjs").then((module) => module.mcp),
5116
+ ui: () => import("./ui-B46Z3e9E.mjs").then((module) => module.ui),
4570
5117
  doctor,
4571
5118
  upgrade,
4572
- completion: () => import("./completion-CCn8xWmO.mjs").then((module) => module.completion),
4573
- __complete: () => import("./completion-CCn8xWmO.mjs").then((module) => module.completeCommand(main))
5119
+ completion: () => import("./completion-cpZHtv50.mjs").then((module) => module.completion),
5120
+ __complete: () => import("./completion-cpZHtv50.mjs").then((module) => module.completeCommand(main))
4574
5121
  }
4575
5122
  });
4576
5123
  // src/bin.ts
@@ -4588,7 +5135,7 @@ const main = defineCommand({
4588
5135
  clearGitLocalEnv();
4589
5136
  const argv = process.argv.slice(2);
4590
5137
  if (argv[0] === "__complete") {
4591
- const { printCompletions } = await import("./completion-CCn8xWmO.mjs");
5138
+ const { printCompletions } = await import("./completion-cpZHtv50.mjs");
4592
5139
  await printCompletions(main, argv.slice(1));
4593
5140
  } else if (process.env["ORK_UPDATE_CHECK_CHILD"] === "1") await runUpdateChild();
4594
5141
  else {