@krapht/ork 0.1.0 → 0.2.0
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
CHANGED
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
The `ork` command: orchestrate Claude agents on your machine with hierarchical,
|
|
4
4
|
serializable workflows.
|
|
5
5
|
|
|
6
|
-
> **Pre-alpha.**
|
|
7
|
-
>
|
|
8
|
-
>
|
|
9
|
-
>
|
|
6
|
+
> **Pre-alpha.** The workflow format, `ork run` with delegation and worktrees,
|
|
7
|
+
> the context compiler, agents, run logs, decisions from any terminal, messages
|
|
8
|
+
> between nodes, chat, resume, claims and local feedback work today; a web UI
|
|
9
|
+
> comes next. Expect changes between 0.x releases. See the
|
|
10
|
+
> [project README](https://github.com/mimshins/ork#readme) and the
|
|
11
|
+
> [guides](https://github.com/mimshins/ork/tree/main/docs#readme).
|
|
10
12
|
|
|
11
13
|
```sh
|
|
12
14
|
npm install --global @krapht/ork
|
|
@@ -19,12 +21,14 @@ ork run hello --input task="add a README"
|
|
|
19
21
|
ork worktrees # the worktrees and branches runs left; `clean` tidies up
|
|
20
22
|
ork run hello --node lead --input task="add a README" --in-place
|
|
21
23
|
# run one Claude node on its own
|
|
24
|
+
ork upgrade # a newer version? upgrade the way you installed Ork
|
|
22
25
|
```
|
|
23
26
|
|
|
24
27
|
`--driver sdk` (the default) needs `ANTHROPIC_API_KEY` or a cloud provider;
|
|
25
28
|
`--driver cli` uses your own logged-in `claude`.
|
|
26
29
|
|
|
27
30
|
Ork never stores or asks for credentials: it uses the tools you already have,
|
|
28
|
-
signed in with your own accounts.
|
|
31
|
+
signed in with your own accounts. Once a day it asks the npm registry whether a
|
|
32
|
+
newer version is out (`ORK_NO_UPDATE_CHECK=1` turns that off).
|
|
29
33
|
|
|
30
34
|
Requires Node.js 24.20 or later. MIT licensed.
|
package/dist/bin.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { A as contextDiagnostics, B as loadAgents, C as createFeedback, D as maskSecrets, E as maskReplacer, F as toolLinks, G as placeFiles, H as hasInRepoFiles, I as toolsInUse, J as runDirOf, K as placeFor, L as createAgent, M as applySteps, N as planMoveInto, O as parseTemplate, P as linkTools, Q as projectRootOf, R as deleteAgent, S as FEEDBACK_TARGETS, T as targetOf, U as inRepoPlace, V as PROJECT_MODES, W as newProjectId, X as workflowDirOf, Y as shownPath, Z as inside, _ as compileWorkflow, a as removeWorktree, at as isToolNode, b as FEEDBACK_KINDS, c as lines, ct as integrationFor, d as planNodeSession, dt as CLAUDE_DRIVERS, et as parseWorkflow, ft as firstLine$1, g as resolvePractices, h as practiceInstructions, i as inspectProject, it as isClaudeNode, j as planAgentMigration, k as renderTemplate, l as detectInstaller, lt as AGENT_TOOL_LAYOUTS, m as runScope, n as resolveBase, nt as WorkflowSchema, o as git, ot as INTEGRATIONS, p as userMessageText, q as projectsHome, rt as settingsOf, s as gitOk, st as TOOL_PREFIX, t as createWorkspace, tt as serializeWorkflow, u as startRun, ut as ORK_LAYOUT, v as gitRulesOf, w as fingerprint, x as FEEDBACK_SEVERITIES, y as loadProject, z as catalogAgent } from "./src-CJ_zF0Zj.mjs";
|
|
3
3
|
import { defineCommand, runMain } from "citty";
|
|
4
4
|
import { createInterface } from "node:readline/promises";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { stringify } from "yaml";
|
|
7
|
-
import { accessSync, appendFileSync, constants, existsSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
7
|
+
import { accessSync, appendFileSync, constants, existsSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
8
8
|
import { createHash, randomUUID } from "node:crypto";
|
|
9
9
|
import { homedir } from "node:os";
|
|
10
10
|
import { createConnection, createServer } from "node:net";
|
|
@@ -13,8 +13,6 @@ import { DatabaseSync } from "node:sqlite";
|
|
|
13
13
|
import { execFile, spawn } from "node:child_process";
|
|
14
14
|
import { styleText } from "node:util";
|
|
15
15
|
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
16
|
-
// src/__build_info__.ts
|
|
17
|
-
const ORK_VERSION = "0.1.0";
|
|
18
16
|
// ../core/src/control/queue.ts
|
|
19
17
|
const PERMISSION_OPTIONS = [{
|
|
20
18
|
id: "allow",
|
|
@@ -881,7 +879,7 @@ const MIGRATIONS = [
|
|
|
881
879
|
);`
|
|
882
880
|
];
|
|
883
881
|
/** A text column, or `""` when it is null. */
|
|
884
|
-
const text = (row, key) => {
|
|
882
|
+
const text$1 = (row, key) => {
|
|
885
883
|
const value = row?.[key];
|
|
886
884
|
return typeof value === "string" ? value : "";
|
|
887
885
|
};
|
|
@@ -914,23 +912,23 @@ const kindOf = (value) => value === "permission" ? "permission" : "question";
|
|
|
914
912
|
const statusOf$1 = (value) => value === "answered" || value === "withdrawn" ? value : "open";
|
|
915
913
|
const decisionOf = (row) => {
|
|
916
914
|
const record = {
|
|
917
|
-
id: text(row, "id"),
|
|
918
|
-
runId: text(row, "run_id"),
|
|
919
|
-
node: text(row, "node"),
|
|
920
|
-
kind: kindOf(text(row, "kind")),
|
|
921
|
-
question: text(row, "question"),
|
|
922
|
-
options: choicesOf(text(row, "options") || "[]"),
|
|
923
|
-
openedAt: text(row, "opened_at"),
|
|
924
|
-
status: statusOf$1(text(row, "status"))
|
|
915
|
+
id: text$1(row, "id"),
|
|
916
|
+
runId: text$1(row, "run_id"),
|
|
917
|
+
node: text$1(row, "node"),
|
|
918
|
+
kind: kindOf(text$1(row, "kind")),
|
|
919
|
+
question: text$1(row, "question"),
|
|
920
|
+
options: choicesOf(text$1(row, "options") || "[]"),
|
|
921
|
+
openedAt: text$1(row, "opened_at"),
|
|
922
|
+
status: statusOf$1(text$1(row, "status"))
|
|
925
923
|
};
|
|
926
924
|
const optional = {
|
|
927
|
-
recommended: text(row, "recommended"),
|
|
928
|
-
context: text(row, "context"),
|
|
929
|
-
answer: text(row, "answer"),
|
|
930
|
-
answeredAt: text(row, "answered_at")
|
|
925
|
+
recommended: text$1(row, "recommended"),
|
|
926
|
+
context: text$1(row, "context"),
|
|
927
|
+
answer: text$1(row, "answer"),
|
|
928
|
+
answeredAt: text$1(row, "answered_at")
|
|
931
929
|
};
|
|
932
930
|
for (const [key, value] of Object.entries(optional)) if (value !== "") Object.assign(record, { [key]: value });
|
|
933
|
-
const by = text(row, "answered_by");
|
|
931
|
+
const by = text$1(row, "answered_by");
|
|
934
932
|
if (by === "terminal" || by === "decide") record.answeredBy = by;
|
|
935
933
|
return record;
|
|
936
934
|
};
|
|
@@ -982,12 +980,12 @@ const decisionQueries = (db) => {
|
|
|
982
980
|
const at = (/* @__PURE__ */ new Date()).toISOString();
|
|
983
981
|
return pending.all(runId).flatMap((row) => {
|
|
984
982
|
handled.run(at, Number(row["id"]));
|
|
985
|
-
const kind = text(row, "kind");
|
|
983
|
+
const kind = text$1(row, "kind");
|
|
986
984
|
if (kind === "stop") return [{ kind }];
|
|
987
985
|
if (kind === "message") return [{
|
|
988
986
|
kind,
|
|
989
|
-
node: text(row, "node"),
|
|
990
|
-
body: text(row, "body")
|
|
987
|
+
node: text$1(row, "node"),
|
|
988
|
+
body: text$1(row, "body")
|
|
991
989
|
}];
|
|
992
990
|
return [];
|
|
993
991
|
});
|
|
@@ -1012,7 +1010,7 @@ const SOURCES = [
|
|
|
1012
1010
|
const oneOf$1 = (values, value, fallback) => values.find((v) => v === value) ?? fallback;
|
|
1013
1011
|
/** A JSON array column of strings, without nulls. */
|
|
1014
1012
|
const strings = (row, key) => {
|
|
1015
|
-
const parsed = JSON.parse(text(row, key) || "[]");
|
|
1013
|
+
const parsed = JSON.parse(text$1(row, key) || "[]");
|
|
1016
1014
|
return Array.isArray(parsed) ? parsed.filter((v) => typeof v === "string" && v !== "") : [];
|
|
1017
1015
|
};
|
|
1018
1016
|
const ORIGIN_COLUMNS = [
|
|
@@ -1026,46 +1024,46 @@ const ORIGIN_COLUMNS = [
|
|
|
1026
1024
|
const recordOf$2 = (row) => {
|
|
1027
1025
|
const origin = {};
|
|
1028
1026
|
for (const [key, column] of ORIGIN_COLUMNS) {
|
|
1029
|
-
const value = text(row, column);
|
|
1027
|
+
const value = text$1(row, column);
|
|
1030
1028
|
if (value !== "") origin[key] = value;
|
|
1031
1029
|
}
|
|
1032
1030
|
const feedback = {
|
|
1033
|
-
source: oneOf$1(SOURCES, text(row, "source"), "system"),
|
|
1034
|
-
kind: oneOf$1(FEEDBACK_KINDS, text(row, "kind"), "idea"),
|
|
1035
|
-
target: oneOf$1(FEEDBACK_TARGETS, text(row, "target"), "workflow"),
|
|
1036
|
-
severity: oneOf$1(FEEDBACK_SEVERITIES, text(row, "severity"), "medium"),
|
|
1037
|
-
title: text(row, "title"),
|
|
1038
|
-
body: text(row, "body"),
|
|
1031
|
+
source: oneOf$1(SOURCES, text$1(row, "source"), "system"),
|
|
1032
|
+
kind: oneOf$1(FEEDBACK_KINDS, text$1(row, "kind"), "idea"),
|
|
1033
|
+
target: oneOf$1(FEEDBACK_TARGETS, text$1(row, "target"), "workflow"),
|
|
1034
|
+
severity: oneOf$1(FEEDBACK_SEVERITIES, text$1(row, "severity"), "medium"),
|
|
1035
|
+
title: text$1(row, "title"),
|
|
1036
|
+
body: text$1(row, "body"),
|
|
1039
1037
|
evidence: strings(row, "evidence"),
|
|
1040
|
-
fingerprint: text(row, "fingerprint"),
|
|
1038
|
+
fingerprint: text$1(row, "fingerprint"),
|
|
1041
1039
|
origin
|
|
1042
1040
|
};
|
|
1043
|
-
const code = text(row, "code");
|
|
1041
|
+
const code = text$1(row, "code");
|
|
1044
1042
|
if (code !== "") feedback.code = code;
|
|
1045
1043
|
const record = {
|
|
1046
1044
|
id: Number(row?.["id"]),
|
|
1047
|
-
runId: text(row, "run_id"),
|
|
1045
|
+
runId: text$1(row, "run_id"),
|
|
1048
1046
|
seq: Number(row?.["seq"]),
|
|
1049
|
-
at: text(row, "at"),
|
|
1047
|
+
at: text$1(row, "at"),
|
|
1050
1048
|
feedback
|
|
1051
1049
|
};
|
|
1052
|
-
const node = text(row, "node");
|
|
1050
|
+
const node = text$1(row, "node");
|
|
1053
1051
|
if (node !== "") record.node = node;
|
|
1054
1052
|
return record;
|
|
1055
1053
|
};
|
|
1056
1054
|
const itemOf = (row) => {
|
|
1057
1055
|
const severities = strings(row, "severities");
|
|
1058
1056
|
return {
|
|
1059
|
-
id: text(row, "fingerprint"),
|
|
1060
|
-
status: oneOf$1(STATUSES$1, text(row, "status"), "new"),
|
|
1061
|
-
title: text(row, "title"),
|
|
1062
|
-
target: oneOf$1(FEEDBACK_TARGETS, text(row, "target"), "workflow"),
|
|
1063
|
-
kind: oneOf$1(FEEDBACK_KINDS, text(row, "kind"), "idea"),
|
|
1057
|
+
id: text$1(row, "fingerprint"),
|
|
1058
|
+
status: oneOf$1(STATUSES$1, text$1(row, "status"), "new"),
|
|
1059
|
+
title: text$1(row, "title"),
|
|
1060
|
+
target: oneOf$1(FEEDBACK_TARGETS, text$1(row, "target"), "workflow"),
|
|
1061
|
+
kind: oneOf$1(FEEDBACK_KINDS, text$1(row, "kind"), "idea"),
|
|
1064
1062
|
severity: FEEDBACK_SEVERITIES.toReversed().find((s) => severities.includes(s)) ?? "medium",
|
|
1065
1063
|
sources: strings(row, "sources").flatMap((s) => SOURCES.filter((known) => known === s)),
|
|
1066
1064
|
count: Number(row?.["count"]),
|
|
1067
|
-
firstSeen: text(row, "first_seen"),
|
|
1068
|
-
lastSeen: text(row, "last_seen"),
|
|
1065
|
+
firstSeen: text$1(row, "first_seen"),
|
|
1066
|
+
lastSeen: text$1(row, "last_seen"),
|
|
1069
1067
|
runs: strings(row, "runs"),
|
|
1070
1068
|
workflows: strings(row, "workflows"),
|
|
1071
1069
|
models: strings(row, "models")
|
|
@@ -1113,11 +1111,11 @@ const feedbackQueries = (db) => {
|
|
|
1113
1111
|
* (`ork migrate`) with its runs' workflow files.
|
|
1114
1112
|
*/
|
|
1115
1113
|
const recordOf$1 = (row) => ({
|
|
1116
|
-
id: text(row, "id"),
|
|
1117
|
-
root: text(row, "root"),
|
|
1118
|
-
mode: PROJECT_MODES.find((m) => m === text(row, "mode")) ?? "in-repo",
|
|
1119
|
-
dir: text(row, "dir"),
|
|
1120
|
-
registeredAt: text(row, "registered_at")
|
|
1114
|
+
id: text$1(row, "id"),
|
|
1115
|
+
root: text$1(row, "root"),
|
|
1116
|
+
mode: PROJECT_MODES.find((m) => m === text$1(row, "mode")) ?? "in-repo",
|
|
1117
|
+
dir: text$1(row, "dir"),
|
|
1118
|
+
registeredAt: text$1(row, "registered_at")
|
|
1121
1119
|
});
|
|
1122
1120
|
const projectQueries = (db) => {
|
|
1123
1121
|
const all = db.prepare("SELECT * FROM projects ORDER BY id");
|
|
@@ -1185,16 +1183,16 @@ const STATUSES = [
|
|
|
1185
1183
|
const statusOf = (value) => STATUSES.find((s) => s === value) ?? "failed";
|
|
1186
1184
|
const recordOf = (row) => {
|
|
1187
1185
|
const record = {
|
|
1188
|
-
id: text(row, "id"),
|
|
1189
|
-
workflow: text(row, "workflow"),
|
|
1190
|
-
file: text(row, "file"),
|
|
1191
|
-
project: text(row, "project"),
|
|
1192
|
-
status: statusOf(text(row, "status")),
|
|
1186
|
+
id: text$1(row, "id"),
|
|
1187
|
+
workflow: text$1(row, "workflow"),
|
|
1188
|
+
file: text$1(row, "file"),
|
|
1189
|
+
project: text$1(row, "project"),
|
|
1190
|
+
status: statusOf(text$1(row, "status")),
|
|
1193
1191
|
pid: Number(row?.["pid"]),
|
|
1194
|
-
startedAt: text(row, "started_at"),
|
|
1192
|
+
startedAt: text$1(row, "started_at"),
|
|
1195
1193
|
costUsd: Number(row?.["cost_usd"])
|
|
1196
1194
|
};
|
|
1197
|
-
const finished = text(row, "finished_at");
|
|
1195
|
+
const finished = text$1(row, "finished_at");
|
|
1198
1196
|
if (finished !== "") record.finishedAt = finished;
|
|
1199
1197
|
return record;
|
|
1200
1198
|
};
|
|
@@ -1270,8 +1268,8 @@ const openStore = (file = storePath()) => {
|
|
|
1270
1268
|
events: (runId, after = 0) => eventsAfter.all(runId, after).map((r) => ({
|
|
1271
1269
|
runId,
|
|
1272
1270
|
seq: Number(r["seq"]),
|
|
1273
|
-
at: text(r, "at"),
|
|
1274
|
-
event: eventOf(text(r, "data"))
|
|
1271
|
+
at: text$1(r, "at"),
|
|
1272
|
+
event: eventOf(text$1(r, "data"))
|
|
1275
1273
|
})),
|
|
1276
1274
|
close: () => {
|
|
1277
1275
|
db.close();
|
|
@@ -1348,6 +1346,144 @@ const alive = (pid) => {
|
|
|
1348
1346
|
};
|
|
1349
1347
|
/** A run's status, with `crashed` for a `running` run whose process is gone. */
|
|
1350
1348
|
const liveStatus = (run) => run.status === "running" && !alive(run.pid) ? "crashed" : run.status;
|
|
1349
|
+
// ../core/src/update/version.ts
|
|
1350
|
+
const VERSION = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/u;
|
|
1351
|
+
/** `1.2.3`, `v1.2.3-beta.1` and the like; `null` for anything else. */
|
|
1352
|
+
const parseVersion = (text) => {
|
|
1353
|
+
const match = VERSION.exec(text.trim());
|
|
1354
|
+
if (match === null) return null;
|
|
1355
|
+
const [, major = "0", minor = "0", patch = "0", pre] = match;
|
|
1356
|
+
return {
|
|
1357
|
+
core: [
|
|
1358
|
+
Number(major),
|
|
1359
|
+
Number(minor),
|
|
1360
|
+
Number(patch)
|
|
1361
|
+
],
|
|
1362
|
+
pre: pre === void 0 ? [] : pre.split(".")
|
|
1363
|
+
};
|
|
1364
|
+
};
|
|
1365
|
+
const NUMERIC = /^\d+$/u;
|
|
1366
|
+
/** One pre-release identifier against another: numbers sort below words. */
|
|
1367
|
+
const compareIdentifier = (a, b) => {
|
|
1368
|
+
const an = NUMERIC.test(a);
|
|
1369
|
+
const bn = NUMERIC.test(b);
|
|
1370
|
+
if (an && bn) return Number(a) - Number(b);
|
|
1371
|
+
if (an !== bn) return an ? -1 : 1;
|
|
1372
|
+
return a < b ? -1 : Number(a > b);
|
|
1373
|
+
};
|
|
1374
|
+
const comparePre = (a, b) => {
|
|
1375
|
+
if (a.length === 0 || b.length === 0) return b.length - a.length;
|
|
1376
|
+
for (let i = 0; i < Math.min(a.length, b.length); i += 1) {
|
|
1377
|
+
const order = compareIdentifier(a[i] ?? "", b[i] ?? "");
|
|
1378
|
+
if (order !== 0) return order;
|
|
1379
|
+
}
|
|
1380
|
+
return a.length - b.length;
|
|
1381
|
+
};
|
|
1382
|
+
/** Semver precedence: negative when `a` is older, 0 when equal. */
|
|
1383
|
+
const compareVersions = (a, b) => {
|
|
1384
|
+
for (let i = 0; i < 3; i += 1) {
|
|
1385
|
+
const order = (a.core[i] ?? 0) - (b.core[i] ?? 0);
|
|
1386
|
+
if (order !== 0) return order;
|
|
1387
|
+
}
|
|
1388
|
+
return comparePre(a.pre, b.pre);
|
|
1389
|
+
};
|
|
1390
|
+
/** Whether `candidate` is newer than `current`; false when either is invalid. */
|
|
1391
|
+
const isNewer = (candidate, current) => {
|
|
1392
|
+
const a = parseVersion(candidate);
|
|
1393
|
+
const b = parseVersion(current);
|
|
1394
|
+
return a !== null && b !== null && compareVersions(a, b) > 0;
|
|
1395
|
+
};
|
|
1396
|
+
const updateCachePath = (home = homedir()) => path.join(home, ".ork", "update-check.json");
|
|
1397
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1398
|
+
const text = (record, key) => {
|
|
1399
|
+
const value = record[key];
|
|
1400
|
+
return typeof value === "string" ? value : void 0;
|
|
1401
|
+
};
|
|
1402
|
+
const notifiedOf = (value) => {
|
|
1403
|
+
if (!isRecord(value)) return void 0;
|
|
1404
|
+
const version = text(value, "version");
|
|
1405
|
+
const at = text(value, "at");
|
|
1406
|
+
return version === void 0 || at === void 0 ? void 0 : {
|
|
1407
|
+
version,
|
|
1408
|
+
at
|
|
1409
|
+
};
|
|
1410
|
+
};
|
|
1411
|
+
/** The cache, or `null` when there is none or it cannot be read. */
|
|
1412
|
+
const readUpdateCache = (file) => {
|
|
1413
|
+
let parsed;
|
|
1414
|
+
try {
|
|
1415
|
+
parsed = JSON.parse(readFileSync(file, "utf8"));
|
|
1416
|
+
} catch {
|
|
1417
|
+
return null;
|
|
1418
|
+
}
|
|
1419
|
+
if (!isRecord(parsed)) return null;
|
|
1420
|
+
const checkedAt = text(parsed, "checkedAt");
|
|
1421
|
+
if (checkedAt === void 0) return null;
|
|
1422
|
+
const latest = text(parsed, "latest");
|
|
1423
|
+
const notified = notifiedOf(parsed["notified"]);
|
|
1424
|
+
return {
|
|
1425
|
+
checkedAt,
|
|
1426
|
+
...latest === void 0 ? {} : { latest },
|
|
1427
|
+
...notified === void 0 ? {} : { notified }
|
|
1428
|
+
};
|
|
1429
|
+
};
|
|
1430
|
+
/** Writes the cache whole (a temporary file, then a rename). */
|
|
1431
|
+
const writeUpdateCache = (file, cache) => {
|
|
1432
|
+
mkdirSync(path.dirname(file), { recursive: true });
|
|
1433
|
+
const temporary = `${file}.${process.pid}.tmp`;
|
|
1434
|
+
writeFileSync(temporary, `${JSON.stringify(cache, null, 2)}\n`);
|
|
1435
|
+
renameSync(temporary, file);
|
|
1436
|
+
};
|
|
1437
|
+
const age = (iso, now) => {
|
|
1438
|
+
const time = Date.parse(iso);
|
|
1439
|
+
return Number.isNaN(time) ? Infinity : now.getTime() - time;
|
|
1440
|
+
};
|
|
1441
|
+
/** Whether it is time to ask the registry again. */
|
|
1442
|
+
const checkDue = (cache, now) => cache === null || age(cache.checkedAt, now) >= 864e5;
|
|
1443
|
+
/** The package's `latest` version on the registry, or `null` on any failure. */
|
|
1444
|
+
const fetchLatest = async (name, options = {}) => {
|
|
1445
|
+
const registry = (options.registry ?? "https://registry.npmjs.org").replace(/\/+$/u, "");
|
|
1446
|
+
const get = options.fetch ?? globalThis.fetch;
|
|
1447
|
+
try {
|
|
1448
|
+
const response = await get(`${registry}/-/package/${name}/dist-tags`, {
|
|
1449
|
+
headers: { accept: "application/json" },
|
|
1450
|
+
signal: AbortSignal.timeout(options.timeoutMs ?? 5e3)
|
|
1451
|
+
});
|
|
1452
|
+
if (!response.ok) return null;
|
|
1453
|
+
const tags = await response.json();
|
|
1454
|
+
return (isRecord(tags) ? text(tags, "latest") : void 0) ?? null;
|
|
1455
|
+
} catch {
|
|
1456
|
+
return null;
|
|
1457
|
+
}
|
|
1458
|
+
};
|
|
1459
|
+
/**
|
|
1460
|
+
* Asks the registry and records the answer. A failed check is recorded too, so
|
|
1461
|
+
* an offline machine is not asked again until the interval passes; the last
|
|
1462
|
+
* known `latest` is kept.
|
|
1463
|
+
*/
|
|
1464
|
+
const refreshUpdateCache = async (file, name, now, options = {}) => {
|
|
1465
|
+
const before = readUpdateCache(file);
|
|
1466
|
+
const latest = await fetchLatest(name, options) ?? before?.latest;
|
|
1467
|
+
const cache = {
|
|
1468
|
+
...before,
|
|
1469
|
+
checkedAt: now.toISOString(),
|
|
1470
|
+
...latest === void 0 ? {} : { latest }
|
|
1471
|
+
};
|
|
1472
|
+
writeUpdateCache(file, cache);
|
|
1473
|
+
return cache;
|
|
1474
|
+
};
|
|
1475
|
+
/** The newer version the cache knows of, if any. */
|
|
1476
|
+
const newerVersion = (cache, current) => cache?.latest !== void 0 && isNewer(cache.latest, current) ? cache.latest : null;
|
|
1477
|
+
/**
|
|
1478
|
+
* The version to tell the user about now: newer than theirs, and not already
|
|
1479
|
+
* told today. `null` when there is nothing to say.
|
|
1480
|
+
*/
|
|
1481
|
+
const pendingNotice = (cache, current, now) => {
|
|
1482
|
+
const newer = newerVersion(cache, current);
|
|
1483
|
+
if (newer === null) return null;
|
|
1484
|
+
const told = cache?.notified;
|
|
1485
|
+
return told?.version === newer && age(told.at, now) < 864e5 ? null : newer;
|
|
1486
|
+
};
|
|
1351
1487
|
// ../core/src/worktree/list.ts
|
|
1352
1488
|
/**
|
|
1353
1489
|
* @file What `ork worktrees` shows and cleans: every worktree and branch Ork's
|
|
@@ -2817,6 +2953,15 @@ const missingPrograms = (workflow, installed = (program) => onPath(program)) =>
|
|
|
2817
2953
|
}
|
|
2818
2954
|
return [...missing.values()];
|
|
2819
2955
|
};
|
|
2956
|
+
// src/__build_info__.ts
|
|
2957
|
+
/**
|
|
2958
|
+
* @file The published package's name and version, bound to
|
|
2959
|
+
* `packages/cli/package.json`. Do not edit by hand: `pnpm changesets:version`
|
|
2960
|
+
* and `pnpm build` rewrite it (`tools/release/build-info.ts`), and a test
|
|
2961
|
+
* fails when it is stale.
|
|
2962
|
+
*/
|
|
2963
|
+
const ORK_NAME = "@krapht/ork";
|
|
2964
|
+
const ORK_VERSION = "0.2.0";
|
|
2820
2965
|
// src/run/origin.ts
|
|
2821
2966
|
/**
|
|
2822
2967
|
* @file Where a run's feedback comes from, beyond what its events say: Ork's
|
|
@@ -3190,7 +3335,7 @@ const flagValues = (rawArgs, flag) => {
|
|
|
3190
3335
|
};
|
|
3191
3336
|
const isDriver = (value) => CLAUDE_DRIVERS.some((d) => d.id === value);
|
|
3192
3337
|
/** Loads the drivers only when a run starts, so other commands stay fast. */
|
|
3193
|
-
const loadRuntime = async (driver) => (await import("./src-
|
|
3338
|
+
const loadRuntime = async (driver) => (await import("./src-DAsOYtoR.mjs")).createRuntime(driver, process.env);
|
|
3194
3339
|
/** Ctrl-C asks the run to stop. */
|
|
3195
3340
|
const onInterrupt = (stop) => {
|
|
3196
3341
|
process.once("SIGINT", () => {
|
|
@@ -3942,6 +4087,428 @@ const resume = defineCommand({
|
|
|
3942
4087
|
}
|
|
3943
4088
|
}
|
|
3944
4089
|
});
|
|
4090
|
+
// src/update/notify.ts
|
|
4091
|
+
/**
|
|
4092
|
+
* @file The new-version notice (ADR-038). At most once a day a command starts a
|
|
4093
|
+
* detached copy of `ork` that asks the npm registry for the latest version
|
|
4094
|
+
* and records it in `~/.ork/update-check.json`, so no command waits on the
|
|
4095
|
+
* network; a later command, when a newer version is known, ends with one line
|
|
4096
|
+
* on stderr (once a day). Off in CI, without a terminal, with `--json`, and
|
|
4097
|
+
* with `ORK_NO_UPDATE_CHECK=1` or `NO_UPDATE_NOTIFIER=1`.
|
|
4098
|
+
*/
|
|
4099
|
+
/** Set in the background copy of `ork` that only runs the check. */
|
|
4100
|
+
const UPDATE_CHILD_ENV = "ORK_UPDATE_CHECK_CHILD";
|
|
4101
|
+
/** A registry to ask instead of npm's, e.g. a company mirror. */
|
|
4102
|
+
const UPDATE_REGISTRY_ENV = "ORK_UPDATE_REGISTRY";
|
|
4103
|
+
const on = (value) => value !== void 0 && value !== "" && value !== "0" && value.toLowerCase() !== "false";
|
|
4104
|
+
/** Why the automatic check is off for this command, or `null` when it is on. */
|
|
4105
|
+
const updateCheckOff = (env, argv, terminal) => {
|
|
4106
|
+
if (on(env["ORK_NO_UPDATE_CHECK"]) || on(env["NO_UPDATE_NOTIFIER"])) return "turned off";
|
|
4107
|
+
if (on(env["CI"])) return "in CI";
|
|
4108
|
+
if (on(env["ORK_DETACHED"]) || !terminal) return "no terminal";
|
|
4109
|
+
if (argv.includes("--json")) return "JSON output";
|
|
4110
|
+
if (argv[0] === "upgrade") return "ork upgrade checks for itself";
|
|
4111
|
+
return null;
|
|
4112
|
+
};
|
|
4113
|
+
const registryOf = (env) => {
|
|
4114
|
+
const registry = env[UPDATE_REGISTRY_ENV];
|
|
4115
|
+
return registry === void 0 || registry === "" ? void 0 : registry;
|
|
4116
|
+
};
|
|
4117
|
+
/** The notice's text. */
|
|
4118
|
+
const updateNotice = (latest, current) => `Ork ${latest} is out (you have ${current}). Upgrade with: ork upgrade`;
|
|
4119
|
+
/**
|
|
4120
|
+
* Starts the background check when one is due. Records the attempt first, so
|
|
4121
|
+
* commands started in the next moments do not start one too.
|
|
4122
|
+
*/
|
|
4123
|
+
const startUpdateCheck = (file = updateCachePath(), now = /* @__PURE__ */ new Date()) => {
|
|
4124
|
+
try {
|
|
4125
|
+
const cache = readUpdateCache(file);
|
|
4126
|
+
if (!checkDue(cache, now)) return;
|
|
4127
|
+
writeUpdateCache(file, {
|
|
4128
|
+
...cache,
|
|
4129
|
+
checkedAt: now.toISOString()
|
|
4130
|
+
});
|
|
4131
|
+
const child = spawn(process.execPath, [...process.execArgv, process.argv[1] ?? ""], {
|
|
4132
|
+
detached: true,
|
|
4133
|
+
stdio: "ignore",
|
|
4134
|
+
env: {
|
|
4135
|
+
...process.env,
|
|
4136
|
+
[UPDATE_CHILD_ENV]: "1"
|
|
4137
|
+
}
|
|
4138
|
+
});
|
|
4139
|
+
child.on("error", () => {});
|
|
4140
|
+
child.unref();
|
|
4141
|
+
} catch {}
|
|
4142
|
+
};
|
|
4143
|
+
/** The background copy's whole job: ask the registry, record the answer. */
|
|
4144
|
+
const runUpdateChild = async () => {
|
|
4145
|
+
const registry = registryOf(process.env);
|
|
4146
|
+
await refreshUpdateCache(updateCachePath(), ORK_NAME, /* @__PURE__ */ new Date(), registry === void 0 ? {} : { registry });
|
|
4147
|
+
};
|
|
4148
|
+
/** Prints the notice when a newer version is known and not told today. */
|
|
4149
|
+
const showUpdateNotice = (file = updateCachePath(), now = /* @__PURE__ */ new Date()) => {
|
|
4150
|
+
try {
|
|
4151
|
+
const cache = readUpdateCache(file);
|
|
4152
|
+
const latest = pendingNotice(cache, ORK_VERSION, now);
|
|
4153
|
+
if (cache === null || latest === null) return;
|
|
4154
|
+
const text = updateNotice(latest, ORK_VERSION);
|
|
4155
|
+
process.stderr.write(`\n${styleText("yellow", text, { stream: process.stderr })}\n`);
|
|
4156
|
+
writeUpdateCache(file, {
|
|
4157
|
+
...cache,
|
|
4158
|
+
notified: {
|
|
4159
|
+
version: latest,
|
|
4160
|
+
at: now.toISOString()
|
|
4161
|
+
}
|
|
4162
|
+
});
|
|
4163
|
+
} catch {}
|
|
4164
|
+
};
|
|
4165
|
+
// src/update/upgrade.ts
|
|
4166
|
+
/**
|
|
4167
|
+
* @file What `ork upgrade` does (ADR-038): compares the registry's latest
|
|
4168
|
+
* version with this one, works out how Ork was installed, and gives the
|
|
4169
|
+
* command to run (a global npm, pnpm, yarn, bun or Volta install) or advice
|
|
4170
|
+
* (a clone, `npx`); runs the command when the user agrees. Also the version
|
|
4171
|
+
* line of `ork doctor`, read from the cache without asking the network.
|
|
4172
|
+
*/
|
|
4173
|
+
const REPOSITORY = "https://github.com/mimshins/ork";
|
|
4174
|
+
/** The GitHub release page of a version, with its changes. */
|
|
4175
|
+
const releaseNotes = (name, version) => `${REPOSITORY}/releases/tag/${encodeURIComponent(`${name}@${version}`)}`;
|
|
4176
|
+
/** The running script's real path, which tells how Ork was installed. */
|
|
4177
|
+
const runningScript = () => {
|
|
4178
|
+
const script = process.argv[1] ?? "";
|
|
4179
|
+
try {
|
|
4180
|
+
return realpathSync(script);
|
|
4181
|
+
} catch {
|
|
4182
|
+
return script;
|
|
4183
|
+
}
|
|
4184
|
+
};
|
|
4185
|
+
const planUpgrade = (options) => {
|
|
4186
|
+
const { latest, name } = options;
|
|
4187
|
+
if (latest === null) return { kind: "unreachable" };
|
|
4188
|
+
if (!isNewer(latest, options.current)) return { kind: "latest" };
|
|
4189
|
+
const spec = `${name}@${latest}`;
|
|
4190
|
+
const installer = detectInstaller(options.script, options.installers);
|
|
4191
|
+
if (installer?.command !== void 0) return {
|
|
4192
|
+
kind: "command",
|
|
4193
|
+
latest,
|
|
4194
|
+
installer,
|
|
4195
|
+
argv: installer.command(spec)
|
|
4196
|
+
};
|
|
4197
|
+
return {
|
|
4198
|
+
kind: "advice",
|
|
4199
|
+
latest,
|
|
4200
|
+
text: installer?.advice?.(spec, options.script.replaceAll("\\", "/")) ?? `Ork could not tell how it was installed. With npm: npm install --global ${spec}`
|
|
4201
|
+
};
|
|
4202
|
+
};
|
|
4203
|
+
/** Runs the installer's command on this terminal; resolves its exit code. */
|
|
4204
|
+
const runInstall = (argv) => new Promise((resolve) => {
|
|
4205
|
+
const [program = "", ...args] = argv;
|
|
4206
|
+
const child = spawn(program, args, { stdio: "inherit" });
|
|
4207
|
+
child.on("error", (error) => {
|
|
4208
|
+
process.stderr.write(`Could not run ${program}: ${error.message}\n`);
|
|
4209
|
+
resolve(127);
|
|
4210
|
+
});
|
|
4211
|
+
child.on("close", (code) => {
|
|
4212
|
+
resolve(code ?? 1);
|
|
4213
|
+
});
|
|
4214
|
+
});
|
|
4215
|
+
/** `ork doctor`'s line for Ork itself, from the last check. */
|
|
4216
|
+
const versionCheck = (current, cache, checksOff) => {
|
|
4217
|
+
const newer = newerVersion(cache, current);
|
|
4218
|
+
if (newer !== null) return {
|
|
4219
|
+
name: "ork",
|
|
4220
|
+
status: "warn",
|
|
4221
|
+
detail: `${current}; ${newer} is out`,
|
|
4222
|
+
hint: "Run ork upgrade."
|
|
4223
|
+
};
|
|
4224
|
+
return {
|
|
4225
|
+
name: "ork",
|
|
4226
|
+
status: "ok",
|
|
4227
|
+
detail: `${current} (${cache?.latest === void 0 ? checksOff ? "update check off" : "not checked for updates yet" : `the latest as of ${cache.checkedAt.slice(0, 10)}`})`
|
|
4228
|
+
};
|
|
4229
|
+
};
|
|
4230
|
+
// src/doctor/checks.ts
|
|
4231
|
+
/**
|
|
4232
|
+
* @file The checks behind `ork doctor`: Ork's own version against the last
|
|
4233
|
+
* update check, and each tool's presence and login status, through an
|
|
4234
|
+
* injectable `exec` so tests need no real tools. Never prints a token or an
|
|
4235
|
+
* account.
|
|
4236
|
+
*/
|
|
4237
|
+
const TIMEOUT_MS = 15e3;
|
|
4238
|
+
const exec = (program, args) => new Promise((resolve) => {
|
|
4239
|
+
execFile(program, args, {
|
|
4240
|
+
timeout: TIMEOUT_MS,
|
|
4241
|
+
encoding: "utf8"
|
|
4242
|
+
}, (error, stdout) => {
|
|
4243
|
+
resolve({
|
|
4244
|
+
ok: error === null,
|
|
4245
|
+
stdout
|
|
4246
|
+
});
|
|
4247
|
+
});
|
|
4248
|
+
});
|
|
4249
|
+
/** Ork's own version, against the last update check (no network). */
|
|
4250
|
+
const orkCheck = () => versionCheck(ORK_VERSION, readUpdateCache(updateCachePath()), updateCheckOff(process.env, [], true) !== null);
|
|
4251
|
+
const MIN_NODE = [24, 20];
|
|
4252
|
+
const checkNode = (version) => {
|
|
4253
|
+
const [major = 0, minor = 0] = version.split(".").map(Number);
|
|
4254
|
+
const [minMajor, minMinor] = MIN_NODE;
|
|
4255
|
+
return major > minMajor || major === minMajor && minor >= minMinor ? {
|
|
4256
|
+
name: "node",
|
|
4257
|
+
status: "ok",
|
|
4258
|
+
detail: `v${version}`
|
|
4259
|
+
} : {
|
|
4260
|
+
name: "node",
|
|
4261
|
+
status: "fail",
|
|
4262
|
+
detail: `v${version}`,
|
|
4263
|
+
hint: `Ork needs Node ${MIN_NODE.join(".")} or later.`
|
|
4264
|
+
};
|
|
4265
|
+
};
|
|
4266
|
+
const checkGit = async (run) => {
|
|
4267
|
+
const version = await run("git", ["--version"]);
|
|
4268
|
+
if (!version.ok) return [{
|
|
4269
|
+
name: "git",
|
|
4270
|
+
status: "fail",
|
|
4271
|
+
detail: "not found",
|
|
4272
|
+
hint: "Install git; Ork gives each writing node its own worktree."
|
|
4273
|
+
}];
|
|
4274
|
+
const inside = await run("git", ["rev-parse", "--is-inside-work-tree"]);
|
|
4275
|
+
return [{
|
|
4276
|
+
name: "git",
|
|
4277
|
+
status: "ok",
|
|
4278
|
+
detail: firstLine$1(version.stdout)
|
|
4279
|
+
}, inside.ok ? {
|
|
4280
|
+
name: "git repo",
|
|
4281
|
+
status: "ok",
|
|
4282
|
+
detail: "inside a work tree"
|
|
4283
|
+
} : {
|
|
4284
|
+
name: "git repo",
|
|
4285
|
+
status: "warn",
|
|
4286
|
+
detail: "not a git repository",
|
|
4287
|
+
hint: "Run Ork inside a git repository so nodes can use worktrees."
|
|
4288
|
+
}];
|
|
4289
|
+
};
|
|
4290
|
+
/**
|
|
4291
|
+
* Runs every check: Node and git, then every registered Claude driver and
|
|
4292
|
+
* integration (ADR-022). Node, git, and at least one driver are required;
|
|
4293
|
+
* integrations are optional.
|
|
4294
|
+
*/
|
|
4295
|
+
const runChecks = async (run, env, nodeVersion) => {
|
|
4296
|
+
const [git, drivers, tools] = await Promise.all([
|
|
4297
|
+
checkGit(run),
|
|
4298
|
+
Promise.all(CLAUDE_DRIVERS.map((d) => d.doctor(run, env))),
|
|
4299
|
+
Promise.all(INTEGRATIONS.flatMap((i) => i.doctor === void 0 ? [] : [i.doctor(run, env)]))
|
|
4300
|
+
]);
|
|
4301
|
+
const results = [
|
|
4302
|
+
checkNode(nodeVersion),
|
|
4303
|
+
...git,
|
|
4304
|
+
...drivers,
|
|
4305
|
+
...tools
|
|
4306
|
+
];
|
|
4307
|
+
return {
|
|
4308
|
+
results,
|
|
4309
|
+
ok: drivers.some((d) => d.status === "ok") && !results.some((r) => r.status === "fail")
|
|
4310
|
+
};
|
|
4311
|
+
};
|
|
4312
|
+
// src/project/link.ts
|
|
4313
|
+
/**
|
|
4314
|
+
* @file What `ork link` and `ork doctor` say about a project's tool folders:
|
|
4315
|
+
* one line per folder (linked, created, kept as a real folder, pointing
|
|
4316
|
+
* elsewhere, broken), and one doctor check per tool the project uses, only
|
|
4317
|
+
* once the project has `.ork/`.
|
|
4318
|
+
*/
|
|
4319
|
+
const DESCRIBE = {
|
|
4320
|
+
linked: (link) => `${link.path} → ${link.origin}`,
|
|
4321
|
+
missing: (link) => `${link.path} → ${link.origin}`,
|
|
4322
|
+
broken: (link) => `${link.path} → ${link.target ?? "?"}, which does not exist`,
|
|
4323
|
+
elsewhere: (link) => `${link.path} → ${link.target ?? "?"}, not ${link.origin}`,
|
|
4324
|
+
folder: (link) => `${link.path} is a real folder, left as it is`
|
|
4325
|
+
};
|
|
4326
|
+
const describe = (link) => DESCRIBE[link.state](link);
|
|
4327
|
+
const LABEL = {
|
|
4328
|
+
linked: style("dim", "ok "),
|
|
4329
|
+
missing: style("yellow", "missing"),
|
|
4330
|
+
broken: style("red", "broken "),
|
|
4331
|
+
elsewhere: style("yellow", "kept "),
|
|
4332
|
+
folder: style("yellow", "kept ")
|
|
4333
|
+
};
|
|
4334
|
+
/** One line per folder; `created` marks the links this call made. */
|
|
4335
|
+
const formatLinks = (links, created = []) => links.map((link) => {
|
|
4336
|
+
return `${created.includes(link.path) ? style("green", "linked ") : LABEL[link.state]} ${describe(link)}`;
|
|
4337
|
+
});
|
|
4338
|
+
const checkOf = (tool, links) => {
|
|
4339
|
+
const name = `${AGENT_TOOL_LAYOUTS.find((l) => l.id === tool)?.name ?? tool} links`;
|
|
4340
|
+
const detail = links.map((link) => describe(link)).join("; ");
|
|
4341
|
+
if (links.every((l) => l.state === "linked")) return {
|
|
4342
|
+
name,
|
|
4343
|
+
status: "ok",
|
|
4344
|
+
detail: links.map((l) => l.path).join(", ")
|
|
4345
|
+
};
|
|
4346
|
+
if (links.some((l) => l.state === "broken" || l.state === "missing")) return {
|
|
4347
|
+
name,
|
|
4348
|
+
status: "warn",
|
|
4349
|
+
detail,
|
|
4350
|
+
hint: `Run ork link ${tool} to link what is missing; a broken link needs fixing by hand.`
|
|
4351
|
+
};
|
|
4352
|
+
return {
|
|
4353
|
+
name,
|
|
4354
|
+
status: "skip",
|
|
4355
|
+
detail
|
|
4356
|
+
};
|
|
4357
|
+
};
|
|
4358
|
+
/**
|
|
4359
|
+
* One check per tool the project uses, once it has `.ork/`: are its folders
|
|
4360
|
+
* linked to `.ork/agents` and `.ork/skills`?
|
|
4361
|
+
*/
|
|
4362
|
+
const linkChecks = (projectRoot) => {
|
|
4363
|
+
const ork = path.dirname(ORK_LAYOUT.agents);
|
|
4364
|
+
if (!existsSync(path.join(projectRoot, ork))) return [];
|
|
4365
|
+
const links = toolLinks(projectRoot, toolsInUse(projectRoot));
|
|
4366
|
+
return Object.entries(Object.groupBy(links, (link) => link.tool)).map(([tool, group = []]) => checkOf(tool, group));
|
|
4367
|
+
};
|
|
4368
|
+
// src/commands/install.ts
|
|
4369
|
+
/**
|
|
4370
|
+
* @file This installation of Ork: its name and version for the root command;
|
|
4371
|
+
* `ork doctor`, which prints Ork's version against the last update check and
|
|
4372
|
+
* the environment checks (Node, git, the user's own claude, gh and aws
|
|
4373
|
+
* logins, the project's links) and fails when no Claude driver can run; and
|
|
4374
|
+
* `ork upgrade`, which asks the npm registry for the latest version and, when
|
|
4375
|
+
* it is newer, upgrades Ork the way it was installed after the user agrees
|
|
4376
|
+
* (`--yes` skips the question; `--check` only reports). A clone or `npx` gets
|
|
4377
|
+
* advice instead. Ork never upgrades itself unasked, and stores no
|
|
4378
|
+
* credentials (ADR-038).
|
|
4379
|
+
*/
|
|
4380
|
+
/** The root command's name, version and description. */
|
|
4381
|
+
const ORK_META = {
|
|
4382
|
+
name: "ork",
|
|
4383
|
+
version: ORK_VERSION,
|
|
4384
|
+
description: "Orchestrate Claude agents on your machine"
|
|
4385
|
+
};
|
|
4386
|
+
const MARK = {
|
|
4387
|
+
ok: style("green", "✓"),
|
|
4388
|
+
warn: style("yellow", "!"),
|
|
4389
|
+
fail: style("red", "✗"),
|
|
4390
|
+
skip: style("dim", "-")
|
|
4391
|
+
};
|
|
4392
|
+
const formatCheck = (result) => {
|
|
4393
|
+
const line = `${MARK[result.status]} ${result.name.padEnd(20)} ${result.detail}`;
|
|
4394
|
+
return result.hint === void 0 ? [line] : [line, ` ${style("dim", result.hint)}`];
|
|
4395
|
+
};
|
|
4396
|
+
const doctor = defineCommand({
|
|
4397
|
+
meta: {
|
|
4398
|
+
name: "doctor",
|
|
4399
|
+
description: "Check Node, git, and your own claude, gh, and aws logins (Ork stores no credentials)"
|
|
4400
|
+
},
|
|
4401
|
+
run: async () => {
|
|
4402
|
+
const { results, ok } = await runChecks(exec, process.env, process.versions.node);
|
|
4403
|
+
const place = projectAt(process.cwd());
|
|
4404
|
+
for (const result of [
|
|
4405
|
+
orkCheck(),
|
|
4406
|
+
...results,
|
|
4407
|
+
...place.mode === "attached" ? [] : linkChecks(place.root)
|
|
4408
|
+
]) for (const line of formatCheck(result)) write(line);
|
|
4409
|
+
if (!ok) {
|
|
4410
|
+
write(`\n${style("red", "Not ready:")} Ork needs Node, git, and at least one Claude driver.`);
|
|
4411
|
+
process.exitCode = 1;
|
|
4412
|
+
}
|
|
4413
|
+
}
|
|
4414
|
+
});
|
|
4415
|
+
/** The ids of runs still running anywhere on this machine. */
|
|
4416
|
+
const runningRuns = () => {
|
|
4417
|
+
const store = openStore();
|
|
4418
|
+
try {
|
|
4419
|
+
return store.runs({ limit: 500 }).filter((run) => liveStatus(run) === "running").map((run) => run.id.slice(0, 8));
|
|
4420
|
+
} finally {
|
|
4421
|
+
store.close();
|
|
4422
|
+
}
|
|
4423
|
+
};
|
|
4424
|
+
/** Records what the registry said, so the notice and doctor agree with it. */
|
|
4425
|
+
const remember = (latest, notified) => {
|
|
4426
|
+
try {
|
|
4427
|
+
const file = updateCachePath();
|
|
4428
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4429
|
+
writeUpdateCache(file, {
|
|
4430
|
+
...readUpdateCache(file),
|
|
4431
|
+
checkedAt: now,
|
|
4432
|
+
latest,
|
|
4433
|
+
...notified ? { notified: {
|
|
4434
|
+
version: latest,
|
|
4435
|
+
at: now
|
|
4436
|
+
} } : {}
|
|
4437
|
+
});
|
|
4438
|
+
} catch {}
|
|
4439
|
+
};
|
|
4440
|
+
/** Whether to go ahead: `--yes`, or the user says so on a terminal. */
|
|
4441
|
+
const agreed = (yes, command) => {
|
|
4442
|
+
if (yes) return Promise.resolve(true);
|
|
4443
|
+
if (!process.stdin.isTTY) {
|
|
4444
|
+
write(`Run it yourself: ${command}\nor run ork upgrade --yes.`);
|
|
4445
|
+
return Promise.resolve(false);
|
|
4446
|
+
}
|
|
4447
|
+
return confirm(`Run ${style("bold", command)}? [y/N] `);
|
|
4448
|
+
};
|
|
4449
|
+
const upgrade = defineCommand({
|
|
4450
|
+
meta: {
|
|
4451
|
+
name: "upgrade",
|
|
4452
|
+
description: "Upgrade Ork to the latest version, the way it was installed (asks first)"
|
|
4453
|
+
},
|
|
4454
|
+
args: {
|
|
4455
|
+
check: {
|
|
4456
|
+
type: "boolean",
|
|
4457
|
+
description: "Only say whether a newer version is out",
|
|
4458
|
+
default: false
|
|
4459
|
+
},
|
|
4460
|
+
yes: {
|
|
4461
|
+
type: "boolean",
|
|
4462
|
+
description: "Run the upgrade without asking",
|
|
4463
|
+
default: false
|
|
4464
|
+
}
|
|
4465
|
+
},
|
|
4466
|
+
run: async ({ args }) => {
|
|
4467
|
+
const registry = registryOf(process.env);
|
|
4468
|
+
const latest = await fetchLatest(ORK_NAME, {
|
|
4469
|
+
...registry === void 0 ? {} : { registry },
|
|
4470
|
+
timeoutMs: 15e3
|
|
4471
|
+
});
|
|
4472
|
+
const plan = planUpgrade({
|
|
4473
|
+
name: ORK_NAME,
|
|
4474
|
+
current: ORK_VERSION,
|
|
4475
|
+
latest,
|
|
4476
|
+
script: runningScript()
|
|
4477
|
+
});
|
|
4478
|
+
if (plan.kind === "unreachable") {
|
|
4479
|
+
writeErr("Could not reach the npm registry to look for a new version.");
|
|
4480
|
+
process.exitCode = 1;
|
|
4481
|
+
return;
|
|
4482
|
+
}
|
|
4483
|
+
if (latest !== null) remember(latest, plan.kind !== "latest");
|
|
4484
|
+
if (plan.kind === "latest") {
|
|
4485
|
+
write(`${style("green", "✓")} Ork ${ORK_VERSION} is the latest version.`);
|
|
4486
|
+
return;
|
|
4487
|
+
}
|
|
4488
|
+
write(`Ork ${style("bold", plan.latest)} is out (you have ${ORK_VERSION}).\nWhat changed: ${releaseNotes(ORK_NAME, plan.latest)}`);
|
|
4489
|
+
if (args.check) return;
|
|
4490
|
+
if (plan.kind === "advice") {
|
|
4491
|
+
write(plan.text);
|
|
4492
|
+
process.exitCode = 1;
|
|
4493
|
+
return;
|
|
4494
|
+
}
|
|
4495
|
+
const running = runningRuns();
|
|
4496
|
+
if (running.length > 0) write(`${style("yellow", "!")} Running now: ${running.join(", ")}. Their next sessions would start from the new install; upgrading after they finish is safer.`);
|
|
4497
|
+
const command = plan.argv.join(" ");
|
|
4498
|
+
write(`Installed with ${plan.installer.label}.`);
|
|
4499
|
+
if (!await agreed(args.yes, command)) {
|
|
4500
|
+
process.exitCode = 1;
|
|
4501
|
+
return;
|
|
4502
|
+
}
|
|
4503
|
+
const code = await runInstall(plan.argv);
|
|
4504
|
+
if (code !== 0) {
|
|
4505
|
+
writeErr(`${command} failed (exit ${code}); the upgrade did not finish.`);
|
|
4506
|
+
process.exitCode = code;
|
|
4507
|
+
return;
|
|
4508
|
+
}
|
|
4509
|
+
write(`${style("green", "✓")} Upgraded Ork to ${plan.latest}.`);
|
|
4510
|
+
}
|
|
4511
|
+
});
|
|
3945
4512
|
// src/project/draft.ts
|
|
3946
4513
|
/**
|
|
3947
4514
|
* @file `ork migrate --from-agents` (ADR-036): drafts a workflow from a
|
|
@@ -4370,62 +4937,6 @@ const initRecorded = (store, cwd, options = {}) => {
|
|
|
4370
4937
|
const recorded = recordInRepo(store, cwd);
|
|
4371
4938
|
return recorded.ok ? initProject(cwd, options) : { error: recorded.error };
|
|
4372
4939
|
};
|
|
4373
|
-
// src/project/link.ts
|
|
4374
|
-
/**
|
|
4375
|
-
* @file What `ork link` and `ork doctor` say about a project's tool folders:
|
|
4376
|
-
* one line per folder (linked, created, kept as a real folder, pointing
|
|
4377
|
-
* elsewhere, broken), and one doctor check per tool the project uses, only
|
|
4378
|
-
* once the project has `.ork/`.
|
|
4379
|
-
*/
|
|
4380
|
-
const DESCRIBE = {
|
|
4381
|
-
linked: (link) => `${link.path} → ${link.origin}`,
|
|
4382
|
-
missing: (link) => `${link.path} → ${link.origin}`,
|
|
4383
|
-
broken: (link) => `${link.path} → ${link.target ?? "?"}, which does not exist`,
|
|
4384
|
-
elsewhere: (link) => `${link.path} → ${link.target ?? "?"}, not ${link.origin}`,
|
|
4385
|
-
folder: (link) => `${link.path} is a real folder, left as it is`
|
|
4386
|
-
};
|
|
4387
|
-
const describe = (link) => DESCRIBE[link.state](link);
|
|
4388
|
-
const LABEL = {
|
|
4389
|
-
linked: style("dim", "ok "),
|
|
4390
|
-
missing: style("yellow", "missing"),
|
|
4391
|
-
broken: style("red", "broken "),
|
|
4392
|
-
elsewhere: style("yellow", "kept "),
|
|
4393
|
-
folder: style("yellow", "kept ")
|
|
4394
|
-
};
|
|
4395
|
-
/** One line per folder; `created` marks the links this call made. */
|
|
4396
|
-
const formatLinks = (links, created = []) => links.map((link) => {
|
|
4397
|
-
return `${created.includes(link.path) ? style("green", "linked ") : LABEL[link.state]} ${describe(link)}`;
|
|
4398
|
-
});
|
|
4399
|
-
const checkOf = (tool, links) => {
|
|
4400
|
-
const name = `${AGENT_TOOL_LAYOUTS.find((l) => l.id === tool)?.name ?? tool} links`;
|
|
4401
|
-
const detail = links.map((link) => describe(link)).join("; ");
|
|
4402
|
-
if (links.every((l) => l.state === "linked")) return {
|
|
4403
|
-
name,
|
|
4404
|
-
status: "ok",
|
|
4405
|
-
detail: links.map((l) => l.path).join(", ")
|
|
4406
|
-
};
|
|
4407
|
-
if (links.some((l) => l.state === "broken" || l.state === "missing")) return {
|
|
4408
|
-
name,
|
|
4409
|
-
status: "warn",
|
|
4410
|
-
detail,
|
|
4411
|
-
hint: `Run ork link ${tool} to link what is missing; a broken link needs fixing by hand.`
|
|
4412
|
-
};
|
|
4413
|
-
return {
|
|
4414
|
-
name,
|
|
4415
|
-
status: "skip",
|
|
4416
|
-
detail
|
|
4417
|
-
};
|
|
4418
|
-
};
|
|
4419
|
-
/**
|
|
4420
|
-
* One check per tool the project uses, once it has `.ork/`: are its folders
|
|
4421
|
-
* linked to `.ork/agents` and `.ork/skills`?
|
|
4422
|
-
*/
|
|
4423
|
-
const linkChecks = (projectRoot) => {
|
|
4424
|
-
const ork = path.dirname(ORK_LAYOUT.agents);
|
|
4425
|
-
if (!existsSync(path.join(projectRoot, ork))) return [];
|
|
4426
|
-
const links = toolLinks(projectRoot, toolsInUse(projectRoot));
|
|
4427
|
-
return Object.entries(Object.groupBy(links, (link) => link.tool)).map(([tool, group = []]) => checkOf(tool, group));
|
|
4428
|
-
};
|
|
4429
4940
|
// src/project/migrate.ts
|
|
4430
4941
|
/**
|
|
4431
4942
|
* @file `ork migrate` (ADR-036): moves an attached project's folder into its
|
|
@@ -5357,94 +5868,13 @@ const addAgent = (options) => {
|
|
|
5357
5868
|
if (options.model !== void 0) agent.model = options.model;
|
|
5358
5869
|
return createAgent(options.name, agent, options);
|
|
5359
5870
|
};
|
|
5360
|
-
// src/doctor/checks.ts
|
|
5361
|
-
/**
|
|
5362
|
-
* @file The checks behind `ork doctor`: each tool's presence and login status,
|
|
5363
|
-
* through an injectable `exec` so tests need no real tools. Never prints a
|
|
5364
|
-
* token or an account.
|
|
5365
|
-
*/
|
|
5366
|
-
const TIMEOUT_MS = 15e3;
|
|
5367
|
-
const exec = (program, args) => new Promise((resolve) => {
|
|
5368
|
-
execFile(program, args, {
|
|
5369
|
-
timeout: TIMEOUT_MS,
|
|
5370
|
-
encoding: "utf8"
|
|
5371
|
-
}, (error, stdout) => {
|
|
5372
|
-
resolve({
|
|
5373
|
-
ok: error === null,
|
|
5374
|
-
stdout
|
|
5375
|
-
});
|
|
5376
|
-
});
|
|
5377
|
-
});
|
|
5378
|
-
const MIN_NODE = [24, 20];
|
|
5379
|
-
const checkNode = (version) => {
|
|
5380
|
-
const [major = 0, minor = 0] = version.split(".").map(Number);
|
|
5381
|
-
const [minMajor, minMinor] = MIN_NODE;
|
|
5382
|
-
return major > minMajor || major === minMajor && minor >= minMinor ? {
|
|
5383
|
-
name: "node",
|
|
5384
|
-
status: "ok",
|
|
5385
|
-
detail: `v${version}`
|
|
5386
|
-
} : {
|
|
5387
|
-
name: "node",
|
|
5388
|
-
status: "fail",
|
|
5389
|
-
detail: `v${version}`,
|
|
5390
|
-
hint: `Ork needs Node ${MIN_NODE.join(".")} or later.`
|
|
5391
|
-
};
|
|
5392
|
-
};
|
|
5393
|
-
const checkGit = async (run) => {
|
|
5394
|
-
const version = await run("git", ["--version"]);
|
|
5395
|
-
if (!version.ok) return [{
|
|
5396
|
-
name: "git",
|
|
5397
|
-
status: "fail",
|
|
5398
|
-
detail: "not found",
|
|
5399
|
-
hint: "Install git; Ork gives each writing node its own worktree."
|
|
5400
|
-
}];
|
|
5401
|
-
const inside = await run("git", ["rev-parse", "--is-inside-work-tree"]);
|
|
5402
|
-
return [{
|
|
5403
|
-
name: "git",
|
|
5404
|
-
status: "ok",
|
|
5405
|
-
detail: firstLine$1(version.stdout)
|
|
5406
|
-
}, inside.ok ? {
|
|
5407
|
-
name: "git repo",
|
|
5408
|
-
status: "ok",
|
|
5409
|
-
detail: "inside a work tree"
|
|
5410
|
-
} : {
|
|
5411
|
-
name: "git repo",
|
|
5412
|
-
status: "warn",
|
|
5413
|
-
detail: "not a git repository",
|
|
5414
|
-
hint: "Run Ork inside a git repository so nodes can use worktrees."
|
|
5415
|
-
}];
|
|
5416
|
-
};
|
|
5417
|
-
/**
|
|
5418
|
-
* Runs every check: Node and git, then every registered Claude driver and
|
|
5419
|
-
* integration (ADR-022). Node, git, and at least one driver are required;
|
|
5420
|
-
* integrations are optional.
|
|
5421
|
-
*/
|
|
5422
|
-
const runChecks = async (run, env, nodeVersion) => {
|
|
5423
|
-
const [git, drivers, tools] = await Promise.all([
|
|
5424
|
-
checkGit(run),
|
|
5425
|
-
Promise.all(CLAUDE_DRIVERS.map((d) => d.doctor(run, env))),
|
|
5426
|
-
Promise.all(INTEGRATIONS.flatMap((i) => i.doctor === void 0 ? [] : [i.doctor(run, env)]))
|
|
5427
|
-
]);
|
|
5428
|
-
const results = [
|
|
5429
|
-
checkNode(nodeVersion),
|
|
5430
|
-
...git,
|
|
5431
|
-
...drivers,
|
|
5432
|
-
...tools
|
|
5433
|
-
];
|
|
5434
|
-
return {
|
|
5435
|
-
results,
|
|
5436
|
-
ok: drivers.some((d) => d.status === "ok") && !results.some((r) => r.status === "fail")
|
|
5437
|
-
};
|
|
5438
|
-
};
|
|
5439
5871
|
// src/commands/setup.ts
|
|
5440
5872
|
/**
|
|
5441
5873
|
* @file Setting up: `ork init` scaffolds `.ork/` in a project, records it, and
|
|
5442
5874
|
* links the tool folders it uses; `ork link` points tools' agents and skills
|
|
5443
5875
|
* folders at `.ork/` (never in an attached project); `ork agents` lists,
|
|
5444
|
-
* shows, adds and removes the agents nodes can take
|
|
5445
|
-
*
|
|
5446
|
-
* and the project's links) and fails when no Claude driver can run. Ork
|
|
5447
|
-
* stores no credentials.
|
|
5876
|
+
* shows, adds and removes the agents nodes can take. `ork doctor` and `ork
|
|
5877
|
+
* upgrade` live in `install.ts`.
|
|
5448
5878
|
*/
|
|
5449
5879
|
const init = defineCommand({
|
|
5450
5880
|
meta: {
|
|
@@ -5512,31 +5942,6 @@ const link = defineCommand({
|
|
|
5512
5942
|
for (const line of formatLinks(result.links, result.created)) write(line);
|
|
5513
5943
|
}
|
|
5514
5944
|
});
|
|
5515
|
-
const MARK = {
|
|
5516
|
-
ok: style("green", "✓"),
|
|
5517
|
-
warn: style("yellow", "!"),
|
|
5518
|
-
fail: style("red", "✗"),
|
|
5519
|
-
skip: style("dim", "-")
|
|
5520
|
-
};
|
|
5521
|
-
const formatCheck = (result) => {
|
|
5522
|
-
const line = `${MARK[result.status]} ${result.name.padEnd(20)} ${result.detail}`;
|
|
5523
|
-
return result.hint === void 0 ? [line] : [line, ` ${style("dim", result.hint)}`];
|
|
5524
|
-
};
|
|
5525
|
-
const doctor = defineCommand({
|
|
5526
|
-
meta: {
|
|
5527
|
-
name: "doctor",
|
|
5528
|
-
description: "Check Node, git, and your own claude, gh, and aws logins (Ork stores no credentials)"
|
|
5529
|
-
},
|
|
5530
|
-
run: async () => {
|
|
5531
|
-
const { results, ok } = await runChecks(exec, process.env, process.versions.node);
|
|
5532
|
-
const place = projectAt(process.cwd());
|
|
5533
|
-
for (const result of [...results, ...place.mode === "attached" ? [] : linkChecks(place.root)]) for (const line of formatCheck(result)) write(line);
|
|
5534
|
-
if (!ok) {
|
|
5535
|
-
write(`\n${style("red", "Not ready:")} Ork needs Node, git, and at least one Claude driver.`);
|
|
5536
|
-
process.exitCode = 1;
|
|
5537
|
-
}
|
|
5538
|
-
}
|
|
5539
|
-
});
|
|
5540
5945
|
const json = {
|
|
5541
5946
|
type: "boolean",
|
|
5542
5947
|
description: "Print JSON",
|
|
@@ -5673,11 +6078,7 @@ const agents = defineCommand({
|
|
|
5673
6078
|
// src/main.ts
|
|
5674
6079
|
/** @file The root `ork` command and its subcommands. */
|
|
5675
6080
|
const main = defineCommand({
|
|
5676
|
-
meta:
|
|
5677
|
-
name: "ork",
|
|
5678
|
-
version: ORK_VERSION,
|
|
5679
|
-
description: "Orchestrate Claude agents on your machine"
|
|
5680
|
-
},
|
|
6081
|
+
meta: ORK_META,
|
|
5681
6082
|
subCommands: {
|
|
5682
6083
|
init,
|
|
5683
6084
|
register,
|
|
@@ -5698,13 +6099,26 @@ const main = defineCommand({
|
|
|
5698
6099
|
context,
|
|
5699
6100
|
worktrees,
|
|
5700
6101
|
wt: worktrees,
|
|
5701
|
-
doctor
|
|
6102
|
+
doctor,
|
|
6103
|
+
upgrade
|
|
5702
6104
|
}
|
|
5703
6105
|
});
|
|
5704
6106
|
// src/bin.ts
|
|
5705
6107
|
/**
|
|
5706
6108
|
* @file The `ork` executable: runs the CLI's root command. The published
|
|
5707
|
-
* package's `bin` points at its bundle.
|
|
6109
|
+
* package's `bin` points at its bundle. When the new-version check is on, it
|
|
6110
|
+
* starts the daily background check and ends with the notice (ADR-038); the
|
|
6111
|
+
* background copy it starts only runs the check.
|
|
5708
6112
|
*/
|
|
5709
|
-
await
|
|
6113
|
+
if (process.env["ORK_UPDATE_CHECK_CHILD"] === "1") await runUpdateChild();
|
|
6114
|
+
else {
|
|
6115
|
+
const argv = process.argv.slice(2);
|
|
6116
|
+
if (updateCheckOff(process.env, argv, process.stderr.isTTY) === null) {
|
|
6117
|
+
startUpdateCheck();
|
|
6118
|
+
process.once("exit", () => {
|
|
6119
|
+
showUpdateNotice();
|
|
6120
|
+
});
|
|
6121
|
+
}
|
|
6122
|
+
await runMain(main);
|
|
6123
|
+
}
|
|
5710
6124
|
export {};
|
|
@@ -4581,6 +4581,82 @@ const startRun = (options) => {
|
|
|
4581
4581
|
tell: ports.bus.tell
|
|
4582
4582
|
};
|
|
4583
4583
|
};
|
|
4584
|
+
// ../core/src/update/installers.ts
|
|
4585
|
+
const SOURCE = "/packages/cli/";
|
|
4586
|
+
const has = (...parts) => (script) => parts.some((part) => script.includes(part));
|
|
4587
|
+
const INSTALLERS = [
|
|
4588
|
+
{
|
|
4589
|
+
id: "source",
|
|
4590
|
+
label: "a clone of the repository",
|
|
4591
|
+
owns: (script) => script.includes(SOURCE) && !script.includes("/node_modules/"),
|
|
4592
|
+
advice: (_spec, script) => `In ${script.slice(0, script.indexOf(SOURCE))}: git pull, then pnpm install and pnpm build.`
|
|
4593
|
+
},
|
|
4594
|
+
{
|
|
4595
|
+
id: "npx",
|
|
4596
|
+
label: "npx",
|
|
4597
|
+
owns: has("/_npx/"),
|
|
4598
|
+
advice: (spec) => `npx keeps its own copy: run npx ${spec} …, or install it with npm install --global ${spec}.`
|
|
4599
|
+
},
|
|
4600
|
+
{
|
|
4601
|
+
id: "pnpm",
|
|
4602
|
+
label: "pnpm (global)",
|
|
4603
|
+
owns: has("/pnpm/global/", "/.pnpm-global/"),
|
|
4604
|
+
command: (spec) => [
|
|
4605
|
+
"pnpm",
|
|
4606
|
+
"add",
|
|
4607
|
+
"--global",
|
|
4608
|
+
spec
|
|
4609
|
+
]
|
|
4610
|
+
},
|
|
4611
|
+
{
|
|
4612
|
+
id: "yarn",
|
|
4613
|
+
label: "yarn (global)",
|
|
4614
|
+
owns: has("/yarn/global/"),
|
|
4615
|
+
command: (spec) => [
|
|
4616
|
+
"yarn",
|
|
4617
|
+
"global",
|
|
4618
|
+
"add",
|
|
4619
|
+
spec
|
|
4620
|
+
]
|
|
4621
|
+
},
|
|
4622
|
+
{
|
|
4623
|
+
id: "bun",
|
|
4624
|
+
label: "bun (global)",
|
|
4625
|
+
owns: has("/.bun/install/global/"),
|
|
4626
|
+
command: (spec) => [
|
|
4627
|
+
"bun",
|
|
4628
|
+
"add",
|
|
4629
|
+
"--global",
|
|
4630
|
+
spec
|
|
4631
|
+
]
|
|
4632
|
+
},
|
|
4633
|
+
{
|
|
4634
|
+
id: "volta",
|
|
4635
|
+
label: "Volta",
|
|
4636
|
+
owns: has("/.volta/tools/"),
|
|
4637
|
+
command: (spec) => [
|
|
4638
|
+
"volta",
|
|
4639
|
+
"install",
|
|
4640
|
+
spec
|
|
4641
|
+
]
|
|
4642
|
+
},
|
|
4643
|
+
{
|
|
4644
|
+
id: "npm",
|
|
4645
|
+
label: "npm (global)",
|
|
4646
|
+
owns: has("/node_modules/"),
|
|
4647
|
+
command: (spec) => [
|
|
4648
|
+
"npm",
|
|
4649
|
+
"install",
|
|
4650
|
+
"--global",
|
|
4651
|
+
spec
|
|
4652
|
+
]
|
|
4653
|
+
}
|
|
4654
|
+
];
|
|
4655
|
+
/** The installer of the running script, or `undefined` when none fits. */
|
|
4656
|
+
const detectInstaller = (script, installers = INSTALLERS) => {
|
|
4657
|
+
const normal = script.replaceAll("\\", "/");
|
|
4658
|
+
return installers.find((i) => i.owns(normal));
|
|
4659
|
+
};
|
|
4584
4660
|
// ../core/src/worktree/git.ts
|
|
4585
4661
|
/**
|
|
4586
4662
|
* @file A minimal `git` runner for the worktree manager: arguments without a
|
|
@@ -5354,4 +5430,4 @@ const createWorkspace = (options) => {
|
|
|
5354
5430
|
close
|
|
5355
5431
|
};
|
|
5356
5432
|
};
|
|
5357
|
-
export {
|
|
5433
|
+
export { SKILL_FILE as $, contextDiagnostics as A, loadAgents as B, createFeedback as C, maskSecrets as D, maskReplacer as E, toolLinks as F, placeFiles as G, hasInRepoFiles as H, toolsInUse as I, runDirOf as J, placeFor as K, createAgent as L, applySteps as M, planMoveInto as N, parseTemplate as O, linkTools as P, projectRootOf as Q, deleteAgent as R, FEEDBACK_TARGETS as S, targetOf as T, inRepoPlace as U, PROJECT_MODES as V, newProjectId as W, workflowDirOf as X, shownPath as Y, inside as Z, compileWorkflow as _, removeWorktree as a, isToolNode as at, FEEDBACK_KINDS as b, lines as c, integrationFor as ct, planNodeSession as d, CLAUDE_DRIVERS as dt, parseWorkflow as et, parseReport as f, firstLine as ft, resolvePractices as g, practiceInstructions as h, inspectProject as i, isClaudeNode as it, planAgentMigration as j, renderTemplate as k, detectInstaller as l, AGENT_TOOL_LAYOUTS as lt, runScope as m, resolveBase as n, WorkflowSchema as nt, git as o, INTEGRATIONS as ot, userMessageText as p, projectsHome as q, BRANCH_PREFIX as r, settingsOf as rt, gitOk as s, TOOL_PREFIX as st, createWorkspace as t, serializeWorkflow as tt, startRun as u, ORK_LAYOUT as ut, gitRulesOf as v, fingerprint as w, FEEDBACK_SEVERITIES as x, loadProject as y, catalogAgent as z };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { S as FEEDBACK_TARGETS, b as FEEDBACK_KINDS, f as parseReport, x as FEEDBACK_SEVERITIES } from "./src-CJ_zF0Zj.mjs";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { randomBytes, timingSafeEqual } from "node:crypto";
|
|
4
4
|
import { createInterface } from "node:readline";
|