@krapht/ork 1.0.0-alpha.14 → 1.0.0-alpha.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -4
- package/dist/__build_info__-DcRdK0Du.mjs +359 -0
- package/dist/apply-Mb0fNpCX.mjs +17 -0
- package/dist/{attach-CdtGX9se.mjs → attach-7lB5_jpr.mjs} +5 -4
- package/dist/{store-CleF7KeS.mjs → attach-B-FeE1dJ.mjs} +60 -25
- package/dist/bin.mjs +328 -1734
- package/dist/brief-BvMtIFio.mjs +58 -0
- package/dist/{builtin-files-Bdq23IIi.mjs → builtin-files-BRSOObAv.mjs} +1 -1
- package/dist/command-tree-BkSYWmYJ.mjs +63 -0
- package/dist/{completion--ywde-56.mjs → completion-Cgb2qPPh.mjs} +45 -6
- package/dist/context-BN9A2Nnu.mjs +106 -0
- package/dist/{find-BVX-bL4u.mjs → find-mLYsR0uC.mjs} +2 -2
- package/dist/flags-CdgIH1L6.mjs +230 -0
- package/dist/follow-ups-DxCsOby1.mjs +496 -0
- package/dist/{format-CvTE5CZQ.mjs → format-hiDvtNGM.mjs} +35 -8
- package/dist/{help-groups-CpR2UzSn.mjs → help-groups-hB6kY34d.mjs} +5 -63
- package/dist/{in-a-run-cL5N9gpH.mjs → in-a-run-a447hxq8.mjs} +1 -1
- package/dist/{__build_info__-B82-dGJY.mjs → install-23g_1ngs.mjs} +3 -227
- package/dist/{limits-Dhe--wmX.mjs → limits-C_Xz_t3C.mjs} +1 -1
- package/dist/{mcp-CsmboyXv.mjs → mcp-CWng9I9n.mjs} +6 -6
- package/dist/{once-CrwI-ZdN.mjs → once-lvwcWfVf.mjs} +3 -3
- package/dist/orphan-guard-BFKg8VSd.mjs +58 -0
- package/dist/{pause-DssrYcda.mjs → pause-Cy11KrXK.mjs} +3 -3
- package/dist/post-C9soCx7i.mjs +46 -0
- package/dist/print-BaWiWf4v.mjs +19 -0
- package/dist/project-records-BRiyQzUo.mjs +230 -0
- package/dist/{relay-DASRgsTQ.mjs → relay-C0M1TWMB.mjs} +1 -1
- package/dist/run-B-j3Qxqc.mjs +1464 -0
- package/dist/{run-D8divGCs.mjs → run-D_u9V_Ve.mjs} +1 -1
- package/dist/{send-CXv8bx0i.mjs → send-qBxj0JbJ.mjs} +2 -2
- package/dist/{src-njdtejgZ.mjs → src-CXBmaGwS.mjs} +213 -75
- package/dist/{src-C7z_6fhn.mjs → src-DHDQ0itC.mjs} +106 -50
- package/dist/{src-tsuZRu0H.mjs → src-DN-RPsil.mjs} +5423 -4467
- package/dist/{src-8Mul0695.mjs → src-nXLIwDrt.mjs} +325 -81
- package/dist/store-Dyr1mP8K.mjs +16 -0
- package/dist/ui/assets/app-BPSCxb_l.css +1 -0
- package/dist/ui/assets/app-CnNRowFn.js +111 -0
- package/dist/ui/assets/{index-D-_uPI2v.js → index-CVzwYlz3.js} +2 -2
- package/dist/ui/index.html +1 -1
- package/dist/{ui-CNO1M0qT.mjs → ui-DLrSt5ny.mjs} +3 -3
- package/dist/{usage-BVXi0iFY.mjs → usage-CxmpYYO4.mjs} +61 -107
- package/dist/{context-Y_I4Mofq.mjs → validate-BVrT43GO.mjs} +35 -108
- package/dist/{watch-DVNIciHp.mjs → watch-mkLm4t9D.mjs} +19 -5
- package/dist/{workflows-B30QkwTk.mjs → workflows-Bvl2Fquu.mjs} +2 -2
- package/dist/{worktrees-DD4_TpkH.mjs → worktrees-DOOCoS2w.mjs} +49 -20
- package/package.json +8 -4
- package/dist/ui/assets/app-D_nAPnHM.css +0 -1
- package/dist/ui/assets/app-DqBAtXSZ.js +0 -111
package/dist/ui/index.html
CHANGED
|
@@ -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-
|
|
22
|
+
<script type="module" crossorigin src="/assets/index-CVzwYlz3.js"></script>
|
|
23
23
|
<link rel="modulepreload" crossorigin href="/assets/theme-CdXvqTQ6.js">
|
|
24
24
|
<link rel="stylesheet" crossorigin href="/assets/index-BPIbqRRb.css">
|
|
25
25
|
</head>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { n as ORK_VERSION, t as ORK_NAME } from "./__build_info__-
|
|
1
|
+
import { n as ORK_VERSION, t as ORK_NAME } from "./__build_info__-DcRdK0Du.mjs";
|
|
2
2
|
import { c as write, l as writeErr, s as style } from "./output-B1kwo0YU.mjs";
|
|
3
|
-
import { o as mayShowSignIn } from "./in-a-run-
|
|
4
|
-
import { i as uiFilePath, l as newToken, n as startServer, r as runningServer, t as DEFAULT_PORT } from "./src-
|
|
3
|
+
import { o as mayShowSignIn } from "./in-a-run-a447hxq8.mjs";
|
|
4
|
+
import { i as uiFilePath, l as newToken, n as startServer, r as runningServer, t as DEFAULT_PORT } from "./src-nXLIwDrt.mjs";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { constants } from "node:os";
|
|
7
7
|
import { spawn } from "node:child_process";
|
|
@@ -1,57 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { B as planPrune, L as openStore, Pr as placeFor, Ut as usageListTable, g as usageReport, h as USAGE_DIMENSIONS, z as applyPrune } from "./src-DN-RPsil.mjs";
|
|
2
|
+
import { i as readListArgs, n as filterArgs, o as allAlias, r as projectScope, s as agoTime } from "./flags-CdgIH1L6.mjs";
|
|
2
3
|
import { C as termOf, c as write, l as writeErr, n as cost, o as shortCount, r as costOf, s as style, t as confirm } from "./output-B1kwo0YU.mjs";
|
|
3
4
|
import { n as chip, t as CHIP_LABELS } from "./chips-BqcT58qr.mjs";
|
|
4
|
-
import { n as limitsHere } from "./limits-
|
|
5
|
-
import { a as header, o as table } from "./format-
|
|
5
|
+
import { n as limitsHere } from "./limits-C_Xz_t3C.mjs";
|
|
6
|
+
import { a as header, o as table } from "./format-hiDvtNGM.mjs";
|
|
6
7
|
import { defineCommand } from "citty";
|
|
7
8
|
// src/runs/usage.ts
|
|
8
|
-
/**
|
|
9
|
-
* @file How usage reads on the terminal (`ork usage`, specs/usage.md), and the
|
|
10
|
-
* times its `--since`, `--until` and `ork prune --older-than` take.
|
|
11
|
-
*/
|
|
12
|
-
const UNITS = {
|
|
13
|
-
h: 36e5,
|
|
14
|
-
d: 864e5,
|
|
15
|
-
w: 6048e5
|
|
16
|
-
};
|
|
17
|
-
/** `30d`, `12w`, `24h` before `now`, as an ISO time; `null` when it is not one. */
|
|
18
|
-
const agoTime = (text, now) => {
|
|
19
|
-
const match = /^(\d+)([hdw])$/u.exec(text);
|
|
20
|
-
if (match === null) return null;
|
|
21
|
-
const [, amount = "0", unit = "d"] = match;
|
|
22
|
-
if (Number(amount) === 0) return null;
|
|
23
|
-
const date = new Date(now - Number(amount) * (UNITS[unit] ?? 0));
|
|
24
|
-
return Number.isNaN(date.getTime()) ? null : date.toISOString();
|
|
25
|
-
};
|
|
26
|
-
/** A `YYYY-MM-DD` day's start in the machine's time zone, `after` days on. */
|
|
27
|
-
const dayTime = (text, after) => {
|
|
28
|
-
const match = /^(\d{4})-(\d{2})-(\d{2})$/u.exec(text);
|
|
29
|
-
if (match === null) return null;
|
|
30
|
-
const [, year = "", month = "", day = ""] = match;
|
|
31
|
-
const start = new Date(Number(year), Number(month) - 1, Number(day));
|
|
32
|
-
if (start.getMonth() !== Number(month) - 1) return null;
|
|
33
|
-
if (start.getDate() !== Number(day)) return null;
|
|
34
|
-
start.setDate(start.getDate() + after);
|
|
35
|
-
return start.toISOString();
|
|
36
|
-
};
|
|
37
|
-
/** The start of the day `days` whole days ago, counting today as one. */
|
|
38
|
-
const wholeDays = (days, now) => {
|
|
39
|
-
const today = new Date(now);
|
|
40
|
-
return new Date(today.getFullYear(), today.getMonth(), today.getDate() - (days - 1)).toISOString();
|
|
41
|
-
};
|
|
42
|
-
/**
|
|
43
|
-
* `--since`: an age or the day it starts from. Days and weeks are whole days,
|
|
44
|
-
* today among them, as the Usage panel counts them; hours are exact.
|
|
45
|
-
*/
|
|
46
|
-
const sinceTime = (text, now) => {
|
|
47
|
-
const ago = agoTime(text, now);
|
|
48
|
-
if (ago === null) return dayTime(text, 0);
|
|
49
|
-
if (text.endsWith("h")) return ago;
|
|
50
|
-
const amount = Number(text.slice(0, -1));
|
|
51
|
-
return wholeDays(text.endsWith("w") ? amount * 7 : amount, now);
|
|
52
|
-
};
|
|
53
|
-
/** `--until`: the last day it includes. */
|
|
54
|
-
const untilTime = (text) => dayTime(text, 1);
|
|
55
9
|
/** `950`, `12.3k`, `4.5M`. */
|
|
56
10
|
const tokens = shortCount;
|
|
57
11
|
const percent = (share) => `${String(Math.round(share * 100))}%`;
|
|
@@ -87,7 +41,7 @@ const usageTable = (term, groups, cpu = false) => table(term, [
|
|
|
87
41
|
{ text: group.key },
|
|
88
42
|
{ text: cost(group.costUsd) },
|
|
89
43
|
{ text: String(group.sessions) },
|
|
90
|
-
{ text: `${tokens(group.inputTokens + group.cacheReadTokens + group.cacheWriteTokens)}
|
|
44
|
+
{ text: `${tokens(group.inputTokens + group.cacheReadTokens + group.cacheWriteTokens)} prompt / ${tokens(group.outputTokens)} out` },
|
|
91
45
|
{ text: percent(group.cacheHitRate) },
|
|
92
46
|
...cpu ? [{ text: cpuText(group.cpuMs) }] : []
|
|
93
47
|
])], " ");
|
|
@@ -106,6 +60,15 @@ const shortTime = (iso) => new Date(iso).toLocaleString(void 0, {
|
|
|
106
60
|
});
|
|
107
61
|
/** A warning line: a `WARN` chip when rich, else the line in yellow. */
|
|
108
62
|
const warned = (term, text) => term.rich ? ` ${chip(term, CHIP_LABELS.warn, "warn")} ${text}` : style("yellow", ` ${text}`);
|
|
63
|
+
/**
|
|
64
|
+
* `$0.0000 of $0.0100`: both sides of a dollar cap with the same digits, four
|
|
65
|
+
* when either is under a dollar, else cents (`usd` alone gives `$0.00 of
|
|
66
|
+
* $0.0100`).
|
|
67
|
+
*/
|
|
68
|
+
const capAmounts = (spent, cap) => {
|
|
69
|
+
const places = cap < 1 || spent > 0 && spent < 1 ? 4 : 2;
|
|
70
|
+
return `$${spent.toFixed(places)} of $${cap.toFixed(places)}`;
|
|
71
|
+
};
|
|
109
72
|
/** The limits across runs, above a report; nothing with none set. */
|
|
110
73
|
const formatLimits = ({ problems, caps, tokenCaps = [], plan }, term = termOf(process.stdout)) => {
|
|
111
74
|
if (problems.length === 0 && caps.length === 0 && tokenCaps.length === 0 && plan.length === 0) return [];
|
|
@@ -113,7 +76,7 @@ const formatLimits = ({ problems, caps, tokenCaps = [], plan }, term = termOf(pr
|
|
|
113
76
|
style("bold", "Limits"),
|
|
114
77
|
...problems.map((problem) => warned(term, problem)),
|
|
115
78
|
...caps.map((cap) => {
|
|
116
|
-
const line = ` ${cap.project === void 0 ? "all projects" : "this project"}, this ${cap.window}: ${
|
|
79
|
+
const line = ` ${cap.project === void 0 ? "all projects" : "this project"}, this ${cap.window}: ${capAmounts(cap.spentUsd, cap.capUsd)}, resets ${shortTime(cap.resetsAt)}`;
|
|
117
80
|
return cap.leftUsd <= 0 ? warned(term, line.trimStart()) : line;
|
|
118
81
|
}),
|
|
119
82
|
...tokenCaps.map((cap) => {
|
|
@@ -128,7 +91,7 @@ const formatLimits = ({ problems, caps, tokenCaps = [], plan }, term = termOf(pr
|
|
|
128
91
|
const formatUsage = (report, scope, by, term = termOf(process.stdout)) => {
|
|
129
92
|
const { totals } = report;
|
|
130
93
|
if (totals.sessions === 0) return [`No Claude sessions in ${scope}.`];
|
|
131
|
-
const lines = [`${style("bold", costOf(totals.costUsd, totals.partial, totals.tokens))} in ${scope}`, `${counted(totals.runs, "run")} · ${counted(totals.sessions, "session")} · ${counted(totals.turns, "turn")} · ${tokens(totals.inputTokens + totals.cacheReadTokens + totals.cacheWriteTokens)}
|
|
94
|
+
const lines = [`${style("bold", costOf(totals.costUsd, totals.partial, totals.tokens))} in ${scope}`, `${counted(totals.runs, "run")} · ${counted(totals.sessions, "session")} · ${counted(totals.turns, "turn")} · ${tokens(totals.inputTokens + totals.cacheReadTokens + totals.cacheWriteTokens)} prompt / ${tokens(totals.outputTokens)} out${totals.tokens === void 0 ? "" : ` (${tokens(totals.tokens)} counted)`} · ${percent(totals.cacheHitRate)} from cache`];
|
|
132
95
|
for (const dimension of by === void 0 ? OVERVIEW : [by]) {
|
|
133
96
|
const groups = report.groups[dimension];
|
|
134
97
|
const shown = by === void 0 ? dimension === "day" ? groups.slice(-10) : groups.slice(0, OVERVIEW_ROWS) : groups;
|
|
@@ -163,67 +126,58 @@ const usage = defineCommand({
|
|
|
163
126
|
name: "usage",
|
|
164
127
|
description: "Show what this project's runs spent over time"
|
|
165
128
|
},
|
|
166
|
-
args: {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
default: "30d"
|
|
181
|
-
},
|
|
182
|
-
until: {
|
|
183
|
-
type: "string",
|
|
184
|
-
description: "The last day to include"
|
|
185
|
-
},
|
|
186
|
-
by: {
|
|
187
|
-
type: "string",
|
|
188
|
-
description: USAGE_DIMENSIONS.join(", ")
|
|
189
|
-
},
|
|
190
|
-
workflow: {
|
|
191
|
-
type: "string",
|
|
192
|
-
description: "Only this workflow"
|
|
193
|
-
},
|
|
194
|
-
json: {
|
|
195
|
-
type: "boolean",
|
|
196
|
-
description: "Print JSON",
|
|
197
|
-
default: false
|
|
129
|
+
args: filterArgs(usageListTable, {
|
|
130
|
+
ids: false,
|
|
131
|
+
limit: false,
|
|
132
|
+
extra: {
|
|
133
|
+
...allAlias,
|
|
134
|
+
"clear-plan-limits": {
|
|
135
|
+
type: "boolean",
|
|
136
|
+
description: "Forget what Claude Code last said of your plan's limits, for one that has lifted",
|
|
137
|
+
default: false
|
|
138
|
+
},
|
|
139
|
+
by: {
|
|
140
|
+
type: "string",
|
|
141
|
+
description: USAGE_DIMENSIONS.join(", ")
|
|
142
|
+
}
|
|
198
143
|
}
|
|
199
|
-
},
|
|
144
|
+
}),
|
|
200
145
|
run: ({ args }) => {
|
|
201
|
-
const
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
207
|
-
if (
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
refuse(`--by takes one of ${USAGE_DIMENSIONS.join(", ")}, not \`${args.by}\`.`);
|
|
146
|
+
const asked = args["since"];
|
|
147
|
+
const since = typeof asked === "string" ? asked : "30d";
|
|
148
|
+
const request = readListArgs(usageListTable, {
|
|
149
|
+
...args,
|
|
150
|
+
since
|
|
151
|
+
}, { all: "project" });
|
|
152
|
+
if (request === void 0) return;
|
|
153
|
+
const rawBy = args["by"];
|
|
154
|
+
const given = typeof rawBy === "string" ? rawBy : void 0;
|
|
155
|
+
if (given !== void 0 && !isDimension(given)) {
|
|
156
|
+
refuse(`--by takes one of ${USAGE_DIMENSIONS.join(", ")}, not \`${given}\`.`);
|
|
213
157
|
return;
|
|
214
158
|
}
|
|
159
|
+
const by = given;
|
|
215
160
|
const store = openStore();
|
|
216
161
|
try {
|
|
217
|
-
|
|
162
|
+
const scope = projectScope(request.filter, store.projects());
|
|
163
|
+
if (scope === void 0) return;
|
|
164
|
+
const named = typeof scope === "object" ? scope.root : void 0;
|
|
165
|
+
const place = scope === "here" ? placeFor(process.cwd(), store.projects()) : store.projects().find((p) => p.root === named);
|
|
166
|
+
const { workflow, until, since: from } = request.filter;
|
|
167
|
+
if (args["clear-plan-limits"] === true) {
|
|
168
|
+
const cleared = store.clearPlanLimits();
|
|
169
|
+
const said = cleared === 0 ? "No plan limit was recorded." : `Cleared ${String(cleared)} plan limit(s).`;
|
|
170
|
+
(args["json"] === true ? writeErr : write)(said);
|
|
171
|
+
}
|
|
218
172
|
const filter = {
|
|
219
|
-
...
|
|
220
|
-
...
|
|
221
|
-
...
|
|
173
|
+
...place === void 0 ? {} : { project: place.root },
|
|
174
|
+
...workflow === void 0 ? {} : { workflow },
|
|
175
|
+
...from === void 0 || from === "all" ? {} : { since: from },
|
|
222
176
|
...until === void 0 ? {} : { until }
|
|
223
177
|
};
|
|
224
178
|
const report = usageReport(store.usage(filter), store.toolCpu(filter));
|
|
225
|
-
const limits = limitsHere(store,
|
|
226
|
-
if (args
|
|
179
|
+
const limits = limitsHere(store, place);
|
|
180
|
+
if (args["json"] === true) {
|
|
227
181
|
write(JSON.stringify({
|
|
228
182
|
...report,
|
|
229
183
|
limits
|
|
@@ -231,9 +185,9 @@ const usage = defineCommand({
|
|
|
231
185
|
return;
|
|
232
186
|
}
|
|
233
187
|
for (const line of formatLimits(limits)) write(line);
|
|
234
|
-
const
|
|
235
|
-
const period =
|
|
236
|
-
for (const line of formatUsage(report, `${
|
|
188
|
+
const where = scope === "here" ? "this project" : scope === "all" ? "every project" : `project ${String(request.filter["project"])}`;
|
|
189
|
+
const period = since === "all" ? "" : `, since ${since}`;
|
|
190
|
+
for (const line of formatUsage(report, `${where}${period}`, by)) write(line);
|
|
237
191
|
} finally {
|
|
238
192
|
store.close();
|
|
239
193
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { D as triggersReport, Di as EXPERIMENTS, E as lateTriggers, Ei as firstLine, Hn as loadProject, J as driverRefusals, L as openStore, M as workflowTextHash, Nr as placeFiles, Oi as experimentsOn, Pr as placeFor, R as storePath, Rr as workflowDirOf, St as walkSummary, Ti as DRIVER_IDS, X as driverQueues, bi as driverChecks, bt as checkWorkflowText, fi as DriverSchema, gi as INTEGRATIONS, hr as socketRoom, j as readProjectTriggers, ki as knownExperiments, mi as retiredDriverHint, q as budgetWarnings, rn as namingNoDriver, wi as AGENT_DRIVERS, xi as readyDriver, xt as walkChecks } from "./src-DN-RPsil.mjs";
|
|
2
|
+
import { d as readUpdateCache, f as refreshUpdateCache, i as registryOf, l as newerVersion, m as writeUpdateCache, n as ORK_VERSION, o as updateSwitchOff, p as updateCachePath, r as isSwitchedOn, s as checkDue, t as ORK_NAME, u as pendingNotice } from "./__build_info__-DcRdK0Du.mjs";
|
|
3
3
|
import { C as termOf, D as wrap, E as widthOf, a as plain, c as write, h as paint, l as writeErr, s as style, w as cut, x as plainEnv } from "./output-B1kwo0YU.mjs";
|
|
4
4
|
import { n as chip, t as CHIP_LABELS } from "./chips-BqcT58qr.mjs";
|
|
5
|
-
import { c as registeredProjects, i as refusedBuiltin,
|
|
5
|
+
import { c as registeredProjects, i as refusedBuiltin, r as findWorkflowFiles, s as projectAt } from "./workflows-Bvl2Fquu.mjs";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { readFileSync } from "node:fs";
|
|
8
8
|
import { execFile, spawn } from "node:child_process";
|
|
@@ -304,10 +304,30 @@ const experimentsCheck = (place, env = process.env, registry = EXPERIMENTS) => {
|
|
|
304
304
|
* `driver.ts` has the whole rule): the one `ork doctor`'s own checks find
|
|
305
305
|
* ready on this machine, said in one line. With none ready, the run stops
|
|
306
306
|
* before any session starts, with the hints `ork doctor` gives. A resumed run
|
|
307
|
-
* keeps the driver it recorded.
|
|
307
|
+
* keeps the driver it recorded. `--driver <id>` skips that look but checks
|
|
308
|
+
* that one driver's tool, and stops the run the same way when it isn't
|
|
309
|
+
* ready.
|
|
308
310
|
*/
|
|
309
311
|
/** The real checks: the tools on this machine and its environment. */
|
|
310
|
-
const checksHere = () => driverChecks(exec, process.env)
|
|
312
|
+
const checksHere = (only) => only === void 0 ? driverChecks(exec, process.env) : Promise.all(AGENT_DRIVERS.filter(({ id }) => id === only).map(async (driver) => ({
|
|
313
|
+
id: driver.id,
|
|
314
|
+
result: await driver.doctor(exec, process.env)
|
|
315
|
+
})));
|
|
316
|
+
/**
|
|
317
|
+
* What stops a run on `--driver <id>`: its own doctor check isn't `ok`, so its
|
|
318
|
+
* tool isn't found or isn't signed in. Empty when it is ready. Drivers named on
|
|
319
|
+
* nodes aren't looked at here; they can sit on a branch that never runs.
|
|
320
|
+
*/
|
|
321
|
+
const overrideRefusal = async (id, checks) => {
|
|
322
|
+
const found = (await (checks ?? checksHere)(id)).find((c) => c.id === id);
|
|
323
|
+
if (found === void 0 || found.result.status === "ok") return [];
|
|
324
|
+
const { detail, hint } = found.result;
|
|
325
|
+
return [
|
|
326
|
+
`--driver ${id}: ${id} isn't ready here (${detail}).`,
|
|
327
|
+
...hint === void 0 ? [] : [hint],
|
|
328
|
+
"See `ork doctor`."
|
|
329
|
+
];
|
|
330
|
+
};
|
|
311
331
|
const subject = (names, all) => {
|
|
312
332
|
if (all) return "No driver is named, so the run uses";
|
|
313
333
|
const [only] = names;
|
|
@@ -340,7 +360,13 @@ const chooseReadyDriver = (names, all, checks) => {
|
|
|
340
360
|
* recorded; otherwise the one ready here.
|
|
341
361
|
*/
|
|
342
362
|
const runReadyDriver = async (workflow, options) => {
|
|
343
|
-
if (options.override !== void 0)
|
|
363
|
+
if (options.override !== void 0) {
|
|
364
|
+
const lines = await overrideRefusal(options.override, options.checks);
|
|
365
|
+
return lines.length === 0 ? { ok: true } : {
|
|
366
|
+
ok: false,
|
|
367
|
+
lines
|
|
368
|
+
};
|
|
369
|
+
}
|
|
344
370
|
const names = namingNoDriver(workflow);
|
|
345
371
|
if (names.length === 0) return { ok: true };
|
|
346
372
|
if (options.recorded !== void 0) return {
|
|
@@ -431,7 +457,8 @@ const pathLines = (walk) => {
|
|
|
431
457
|
// src/commands/validate-report.ts
|
|
432
458
|
const plural = (count, word) => `${count} ${word}${count === 1 ? "" : "s"}`;
|
|
433
459
|
const diagnosticLine = (file, d, term) => {
|
|
434
|
-
const
|
|
460
|
+
const at = d.file ?? file;
|
|
461
|
+
const where = d.line === void 0 ? at : `${at}:${d.line}:${d.column ?? 1}`;
|
|
435
462
|
if (term.rich) return [
|
|
436
463
|
`${d.severity === "error" ? chip(term, CHIP_LABELS.error, "danger") : chip(term, CHIP_LABELS.warn, "warn")} ${paint(term, "muted", cut(where, term.width - 9))}`,
|
|
437
464
|
...wrap(d.message, term.width - 2).map((line) => ` ${line}`),
|
|
@@ -602,104 +629,4 @@ const validate = defineCommand({
|
|
|
602
629
|
if (reports.some((r) => hasErrors(r.diagnostics))) process.exitCode = 1;
|
|
603
630
|
}
|
|
604
631
|
});
|
|
605
|
-
|
|
606
|
-
/**
|
|
607
|
-
* @file `ork context explain <wf> <node>`: what one node's session would load,
|
|
608
|
-
* layer by layer, as core's `explainSession` plans it without writing
|
|
609
|
-
* anything (one of Ork's own `ork:` workflows is written into the project's
|
|
610
|
-
* run folder first, as `ork run` does): its project instructions, every skill
|
|
611
|
-
* with the layer it came from and whether it is live, shadowed or switched
|
|
612
|
-
* off, its context files, its practices, and estimated tokens.
|
|
613
|
-
*/
|
|
614
|
-
/** Loads a workflow file and plans one node's session in it, in place. */
|
|
615
|
-
const explainNode = (file, nodeId, cwd, home) => {
|
|
616
|
-
const { report, workflow, graph, place, workflowDir } = loadWorkflow(file, cwd, home);
|
|
617
|
-
if (workflow === null) return {
|
|
618
|
-
ok: false,
|
|
619
|
-
lines: formatReport(report)
|
|
620
|
-
};
|
|
621
|
-
const result = explainSession(workflow, graph, nodeId, {
|
|
622
|
-
root: place.root,
|
|
623
|
-
orkDir: place.dir,
|
|
624
|
-
workflowDir,
|
|
625
|
-
...home === void 0 ? {} : { home }
|
|
626
|
-
});
|
|
627
|
-
return result.ok ? result : {
|
|
628
|
-
ok: false,
|
|
629
|
-
lines: [result.error]
|
|
630
|
-
};
|
|
631
|
-
};
|
|
632
|
-
const approx = (count) => style("dim", `≈${count.toLocaleString("en")} tokens`);
|
|
633
|
-
const formatExplanation = (e) => {
|
|
634
|
-
const width = Math.max(8, ...e.skills.map((s) => s.name.length));
|
|
635
|
-
const sourceWidth = Math.max(0, ...e.skills.map((s) => s.source.length));
|
|
636
|
-
const lines = [
|
|
637
|
-
`${style("bold", e.node)} ${style("dim", `(${e.type}) in ${e.workflow}`)}${e.template === void 0 ? "" : style("dim", `, planned as ${e.template}, the node it was spawned from`)}`,
|
|
638
|
-
"",
|
|
639
|
-
...e.agent === void 0 ? [] : [`${style("bold", "Agent")} ${e.agent.name} ${style("dim", `(${e.agent.where})`)} ${approx(e.agent.tokens)}`],
|
|
640
|
-
`${style("bold", "Instructions")} ${e.instructions}`,
|
|
641
|
-
"",
|
|
642
|
-
style("bold", "Skills")
|
|
643
|
-
];
|
|
644
|
-
if (e.skills.length === 0) lines.push(style("dim", " none"));
|
|
645
|
-
for (const s of e.skills) {
|
|
646
|
-
const mark = s.state === "live" ? style("green", "✓") : style("dim", "−");
|
|
647
|
-
const state = s.why === void 0 ? approx(s.tokens).replace("tokens", "tokens when used") : style("yellow", `off: ${s.why}`);
|
|
648
|
-
lines.push(` ${mark} ${s.name.padEnd(width)} ${s.layer.padEnd(8)} ${style("dim", s.source.padEnd(sourceWidth))} ${state}`);
|
|
649
|
-
}
|
|
650
|
-
lines.push("", style("bold", "Context files"));
|
|
651
|
-
if (e.files.length === 0) lines.push(style("dim", " none"));
|
|
652
|
-
for (const f of e.files) {
|
|
653
|
-
const cut = f.cutFrom === void 0 ? "" : ` ${style("yellow", `cut at the ${LESSON_CAP_TOKENS.toLocaleString("en")}-token cap; the whole file is ${approx(f.cutFrom)}`)}`;
|
|
654
|
-
lines.push(` ${f.layer.padEnd(8)} ${f.name} ${approx(f.tokens)}${cut}`);
|
|
655
|
-
}
|
|
656
|
-
lines.push("", style("bold", "Practices"));
|
|
657
|
-
for (const p of e.practices) lines.push(` ${p.name.padEnd(8)} ${approx(p.tokens)}`);
|
|
658
|
-
for (const name of e.skippedPractices) lines.push(` ${name.padEnd(8)} ${style("dim", "the project's own skill wins")}`);
|
|
659
|
-
if (e.practices.length === 0 && e.skippedPractices.length === 0) lines.push(style("dim", " off"));
|
|
660
|
-
lines.push("", `Appended to Claude Code's system prompt: ${approx(e.appendedTokens)}`);
|
|
661
|
-
return lines;
|
|
662
|
-
};
|
|
663
|
-
const explain = defineCommand({
|
|
664
|
-
meta: {
|
|
665
|
-
name: "explain",
|
|
666
|
-
description: "Show what one node's session loads, layer by layer"
|
|
667
|
-
},
|
|
668
|
-
args: {
|
|
669
|
-
workflow: {
|
|
670
|
-
type: "positional",
|
|
671
|
-
description: "A workflow file, the name of one in the project's workflows (.ork/workflows/), or one of Ork's own (ork:feedback-apply)",
|
|
672
|
-
required: true
|
|
673
|
-
},
|
|
674
|
-
node: {
|
|
675
|
-
type: "positional",
|
|
676
|
-
description: "Node id",
|
|
677
|
-
required: true
|
|
678
|
-
},
|
|
679
|
-
json: {
|
|
680
|
-
type: "boolean",
|
|
681
|
-
description: "Print JSON",
|
|
682
|
-
default: false
|
|
683
|
-
}
|
|
684
|
-
},
|
|
685
|
-
run: ({ args }) => {
|
|
686
|
-
const cwd = process.cwd();
|
|
687
|
-
if (refusedBuiltin([args.workflow], cwd)) return;
|
|
688
|
-
const result = explainNode(workflowArg(args.workflow, cwd).file, args.node, cwd);
|
|
689
|
-
if (!result.ok) {
|
|
690
|
-
for (const line of result.lines) process.stderr.write(`${line}\n`);
|
|
691
|
-
process.exitCode = 1;
|
|
692
|
-
return;
|
|
693
|
-
}
|
|
694
|
-
if (args.json) write(JSON.stringify(result.explanation, void 0, 2));
|
|
695
|
-
else for (const line of formatExplanation(result.explanation)) write(line);
|
|
696
|
-
}
|
|
697
|
-
});
|
|
698
|
-
const context = defineCommand({
|
|
699
|
-
meta: {
|
|
700
|
-
name: "context",
|
|
701
|
-
description: "Inspect the context Ork compiles for each node"
|
|
702
|
-
},
|
|
703
|
-
subCommands: { explain }
|
|
704
|
-
});
|
|
705
|
-
export { showUpdateNotice as C, runUpdateChild as S, updateCheckOff as T, orkCheck as _, validateFile as a, triggerChecks as b, warningLines as c, chooseReadyDriver as d, runReadyDriver as f, headWarning as g, experimentsCheck as h, validate as i, budgetWarningLines as l, exec as m, explainNode as n, walkLoaded as o, unfitLines as p, loadWorkflow as r, formatReport as s, context as t, checksHere as u, runChecks as v, startUpdateCheck as w, UPDATE_CHILD_ENV as x, socketCheck as y };
|
|
632
|
+
export { startUpdateCheck as C, showUpdateNotice as S, runChecks as _, formatReport as a, UPDATE_CHILD_ENV as b, checksHere as c, runReadyDriver as d, unfitLines as f, orkCheck as g, headWarning as h, walkLoaded as i, chooseReadyDriver as l, experimentsCheck as m, validate as n, warningLines as o, exec as p, validateFile as r, budgetWarningLines as s, loadWorkflow as t, overrideRefusal as u, socketCheck as v, updateCheckOff as w, runUpdateChild as x, triggerChecks as y };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as newRunId } from "./find-
|
|
1
|
+
import { A as declaredTriggers, D as triggersReport, In as stillTheSameProcess, L as openStore, N as takeWatchLock, Nr as placeFiles, O as createWatcher, P as watchingPid, di as isToolNode, hi as timeoutMs, hn as startFollowUpChecks, j as readProjectTriggers, k as triggerStanding, kn as liveStatus, lt as detachRun, oi as CheckReportSchema, tr as runGh } from "./src-DN-RPsil.mjs";
|
|
2
|
+
import { n as newRunId } from "./find-mLYsR0uC.mjs";
|
|
3
3
|
import { C as termOf, E as widthOf, a as plain, c as write, l as writeErr, s as style, t as confirm, w as cut } from "./output-B1kwo0YU.mjs";
|
|
4
4
|
import { n as chip, t as CHIP_LABELS } from "./chips-BqcT58qr.mjs";
|
|
5
|
-
import { c as toolExecutor, o as spawnTool, s as mcpCallExecutor } from "./src-
|
|
6
|
-
import { i as runLimits, r as limitsRefusal } from "./limits-
|
|
7
|
-
import { t as acceptRefusal } from "./in-a-run-
|
|
5
|
+
import { c as toolExecutor, o as spawnTool, s as mcpCallExecutor } from "./src-DHDQ0itC.mjs";
|
|
6
|
+
import { i as runLimits, r as limitsRefusal } from "./limits-C_Xz_t3C.mjs";
|
|
7
|
+
import { t as acceptRefusal } from "./in-a-run-a447hxq8.mjs";
|
|
8
8
|
import path from "node:path";
|
|
9
9
|
import { mkdirSync } from "node:fs";
|
|
10
10
|
import { homedir } from "node:os";
|
|
@@ -150,6 +150,8 @@ const acceptLines = (item, standing, term = termOf(process.stdout)) => [
|
|
|
150
150
|
];
|
|
151
151
|
/** The triggers `--accept` can't offer, so they aren't silently left out. */
|
|
152
152
|
const unacceptableLines = (item, term = termOf(process.stdout)) => [`${style("bold", plain(item.id))} ${style("dim", plain(item.place.root))}`, ...item.diagnostics.filter(({ severity }) => severity === "error").map(({ message }) => ` ${failMark(term)} ${plain(message)}`)];
|
|
153
|
+
/** What `ork watch` says when follow-ups come due. */
|
|
154
|
+
const dueLines = (due) => due.map((f) => `▸ follow-up due (${plain(f.for)}): ${plain(f.title)}${f.note === void 0 ? "" : ` (${plain(f.note)})`}, \`ork follow-ups show ${f.id}\``);
|
|
153
155
|
// src/watch/start.ts
|
|
154
156
|
/**
|
|
155
157
|
* @file Starting a trigger's run (RFC-011): an ordinary run in the background,
|
|
@@ -273,7 +275,19 @@ const watchLoop = async (store) => {
|
|
|
273
275
|
const timer = setInterval(() => {
|
|
274
276
|
watcher.tick();
|
|
275
277
|
}, TICK_MS);
|
|
278
|
+
const stopChecks = startFollowUpChecks({
|
|
279
|
+
rows: store,
|
|
280
|
+
gh: runGh,
|
|
281
|
+
projects: () => store.projects().map((project) => project.root),
|
|
282
|
+
onDue: (due) => {
|
|
283
|
+
for (const line of dueLines(due)) write(line);
|
|
284
|
+
},
|
|
285
|
+
onError: (error) => {
|
|
286
|
+
writeErr(`follow-ups: ${error}`);
|
|
287
|
+
}
|
|
288
|
+
});
|
|
276
289
|
await stopped;
|
|
290
|
+
stopChecks();
|
|
277
291
|
clearInterval(timer);
|
|
278
292
|
stopping.abort();
|
|
279
293
|
await watcher.settled();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as isBuiltinArg, n as builtinWorkflowFile, t as builtinProblem } from "./builtin-files-
|
|
1
|
+
import { Ir as runDirOf, L as openStore, Nr as placeFiles, Pr as placeFor, R as storePath, zr as inside } from "./src-DN-RPsil.mjs";
|
|
2
|
+
import { a as isBuiltinArg, n as builtinWorkflowFile, t as builtinProblem } from "./builtin-files-BRSOObAv.mjs";
|
|
3
3
|
import { l as writeErr } from "./output-B1kwo0YU.mjs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Gt as applyClean, Jt as planClean, L as openStore, Wt as worktreeListTable, Yt as stepActs, Zt as inspectProject, a as planRemove, c as repoRoot, d as planLine, i as lostWork, l as labelOf, p as stateParts, qt as keyOf, r as applyRemove, s as readWorktrees, u as mergeStateOf } from "./src-DN-RPsil.mjs";
|
|
2
|
+
import { t as applyFilters } from "./apply-Mb0fNpCX.mjs";
|
|
3
|
+
import { i as readListArgs, n as filterArgs, r as projectScope } from "./flags-CdgIH1L6.mjs";
|
|
2
4
|
import { C as termOf, c as write, h as paint, l as writeErr, s as style, t as confirm, w as cut } from "./output-B1kwo0YU.mjs";
|
|
3
5
|
import { n as chip } from "./chips-BqcT58qr.mjs";
|
|
6
|
+
import { t as printList } from "./print-BaWiWf4v.mjs";
|
|
7
|
+
import { r as withStore } from "./project-records-BRiyQzUo.mjs";
|
|
4
8
|
import path from "node:path";
|
|
5
|
-
import { realpathSync } from "node:fs";
|
|
9
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
6
10
|
import { defineCommand } from "citty";
|
|
7
11
|
// src/project/go-ahead.ts
|
|
8
12
|
/**
|
|
@@ -24,11 +28,11 @@ const goAhead = async (yes, verb, terminal = process.stdin.isTTY, ask = confirm)
|
|
|
24
28
|
};
|
|
25
29
|
// src/project/worktrees.ts
|
|
26
30
|
/**
|
|
27
|
-
* @file What `ork worktrees` prints: each worktree
|
|
28
|
-
* its state read from git, the plan `ork worktrees
|
|
29
|
-
* carry out, and what a forced remove would lose.
|
|
30
|
-
* (worktree/words.ts), which the Worktrees panel says
|
|
31
|
-
* them.
|
|
31
|
+
* @file What `ork worktrees` reads across projects, and prints: each worktree
|
|
32
|
+
* or branch a run left, with its state read from git, the plan `ork worktrees
|
|
33
|
+
* clean` or `remove` would carry out, and what a forced remove would lose.
|
|
34
|
+
* The words are core's (worktree/words.ts), which the Worktrees panel says
|
|
35
|
+
* too; this only colors them.
|
|
32
36
|
*/
|
|
33
37
|
/** One line of state: its merge state, then what clean would care about. */
|
|
34
38
|
const worktreeState = (item, term = termOf(process.stdout)) => {
|
|
@@ -55,6 +59,22 @@ const formatLosses = (losses) => losses.flatMap(({ item, lost }) => {
|
|
|
55
59
|
const lines = formatLost(lost);
|
|
56
60
|
return lines.length === 0 ? [] : [`Loses in ${labelOf(item)}:`, ...lines];
|
|
57
61
|
});
|
|
62
|
+
/** What Ork created in each registered project's repository still on disk. */
|
|
63
|
+
const inProjects = async (roots) => {
|
|
64
|
+
const store = openStore();
|
|
65
|
+
try {
|
|
66
|
+
return (await Promise.all([...new Set(roots)].filter((root) => existsSync(root)).map(async (root) => {
|
|
67
|
+
try {
|
|
68
|
+
return (await readWorktrees(store, root)).items;
|
|
69
|
+
} catch (error) {
|
|
70
|
+
writeErr(`Skipped ${root}: ${error instanceof Error ? error.message : String(error)}`);
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
}))).flat();
|
|
74
|
+
} finally {
|
|
75
|
+
store.close();
|
|
76
|
+
}
|
|
77
|
+
};
|
|
58
78
|
// src/commands/worktrees.ts
|
|
59
79
|
/**
|
|
60
80
|
* @file `ork worktrees` (or `ork wt`): lists the worktrees and branches runs
|
|
@@ -105,23 +125,31 @@ const realOr = (target) => {
|
|
|
105
125
|
const done = (lines) => {
|
|
106
126
|
for (const line of lines) write(`${style("green", "✓")} ${line}`);
|
|
107
127
|
};
|
|
108
|
-
const show = async (
|
|
109
|
-
const
|
|
110
|
-
if (
|
|
111
|
-
|
|
112
|
-
|
|
128
|
+
const show = async (args) => {
|
|
129
|
+
const request = readListArgs(worktreeListTable, args);
|
|
130
|
+
if (request === void 0) return;
|
|
131
|
+
const projects = withStore((store) => store.projects());
|
|
132
|
+
const scope = projectScope(request.filter, projects);
|
|
133
|
+
if (scope === void 0) return;
|
|
134
|
+
let items;
|
|
135
|
+
if (scope === "here") {
|
|
136
|
+
const found = await here(true);
|
|
137
|
+
if (found === null) return;
|
|
138
|
+
items = found.items;
|
|
139
|
+
} else items = await inProjects(scope === "all" ? projects.map((p) => p.root) : [scope.root]);
|
|
140
|
+
const shown = applyFilters(worktreeListTable, items, request.filter).slice(0, request.limit);
|
|
141
|
+
printList(shown, worktreeListTable, args, (rows) => {
|
|
142
|
+
for (const line of formatWorktrees(rows)) write(line);
|
|
143
|
+
});
|
|
113
144
|
};
|
|
145
|
+
const listArgs = filterArgs(worktreeListTable);
|
|
114
146
|
const list = defineCommand({
|
|
115
147
|
meta: {
|
|
116
148
|
name: "list",
|
|
117
149
|
description: "List Ork's worktrees and branches (the default)"
|
|
118
150
|
},
|
|
119
|
-
args:
|
|
120
|
-
|
|
121
|
-
description: "Print JSON",
|
|
122
|
-
default: false
|
|
123
|
-
} },
|
|
124
|
-
run: ({ args }) => show(args.json)
|
|
151
|
+
args: listArgs,
|
|
152
|
+
run: ({ args }) => show(args)
|
|
125
153
|
});
|
|
126
154
|
const yes = {
|
|
127
155
|
type: "boolean",
|
|
@@ -256,15 +284,16 @@ const worktrees = defineCommand({
|
|
|
256
284
|
name: "worktrees",
|
|
257
285
|
description: "Track and clean up the git worktrees Ork creates for runs"
|
|
258
286
|
},
|
|
287
|
+
args: listArgs,
|
|
259
288
|
subCommands: {
|
|
260
289
|
list,
|
|
261
290
|
clean,
|
|
262
291
|
remove
|
|
263
292
|
},
|
|
264
|
-
run: async ({ rawArgs }) => {
|
|
293
|
+
run: async ({ args, rawArgs }) => {
|
|
265
294
|
const [first] = rawArgs;
|
|
266
295
|
if (first === "list" || first === "clean" || first === "remove") return;
|
|
267
|
-
await show(
|
|
296
|
+
await show(args);
|
|
268
297
|
}
|
|
269
298
|
});
|
|
270
299
|
export { worktrees };
|
package/package.json
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@krapht/ork",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
4
|
-
"description": "Orchestrate
|
|
3
|
+
"version": "1.0.0-alpha.16",
|
|
4
|
+
"description": "Orchestrate coding agents (Claude Code, OpenCode, Command Code) on your machine: hierarchical, serializable workflows with a CLI and a localhost UI.",
|
|
5
5
|
"keywords": [
|
|
6
|
-
"claude",
|
|
7
6
|
"agents",
|
|
7
|
+
"coding-agents",
|
|
8
8
|
"orchestration",
|
|
9
9
|
"workflow",
|
|
10
10
|
"cli",
|
|
11
|
-
"ai-engineering"
|
|
11
|
+
"ai-engineering",
|
|
12
|
+
"claude",
|
|
13
|
+
"claude-code",
|
|
14
|
+
"opencode",
|
|
15
|
+
"command-code"
|
|
12
16
|
],
|
|
13
17
|
"license": "MIT",
|
|
14
18
|
"repository": {
|