@krapht/ork 0.3.0 → 0.4.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/dist/__build_info__-CtSVVuZE.mjs +691 -0
- package/dist/bin.mjs +826 -526
- package/dist/clean-Dcr807be.mjs +88 -0
- package/dist/{context-BFGC_C02.mjs → context-C_bksb0C.mjs} +45 -129
- package/dist/{find-BcvhT6UN.mjs → find-DptmbhGd.mjs} +1 -1
- package/dist/{format-DqpObyMl.mjs → format-J-a21Yvn.mjs} +3 -163
- package/dist/{in-a-run-BbLhZOFW.mjs → in-a-run-D1Qc5Mit.mjs} +1 -1
- package/dist/{limits-BKYAmqCR.mjs → limits-BDZCmGEu.mjs} +1 -1
- package/dist/list-ZvY2OcLq.mjs +110 -0
- package/dist/{mcp-BWsdQyri.mjs → mcp-CHVKKg3B.mjs} +6 -6
- package/dist/{output-Q2EdeVUt.mjs → output-CXSJO00X.mjs} +68 -24
- package/dist/{pause-DKACDfV1.mjs → pause-BPSmg4yz.mjs} +4 -4
- package/dist/{relay-CbK-Wme9.mjs → relay-Do-aNbSD.mjs} +2 -2
- package/dist/remove-AwaMMaHf.mjs +194 -0
- package/dist/{send-C6yr04Fe.mjs → send-DhGBkowy.mjs} +3 -3
- package/dist/{src-CZOVF2yU.mjs → src-BHnxpJyH.mjs} +1 -1
- package/dist/{src-Cz1yOUTg.mjs → src-CGqMati3.mjs} +1411 -747
- package/dist/{src-D5ruZFgD.mjs → src-CpDKIulI.mjs} +1200 -55
- package/dist/{src-DtN1k-B3.mjs → src-UWkk1sl9.mjs} +1 -1
- package/dist/ui/assets/app-BgVDC_5l.js +67 -0
- package/dist/ui/assets/{app-C722xNZ3.css → app-DVf68fj5.css} +1 -1
- package/dist/ui/assets/{index-BoG6O-Lp.js → index-MQNNgsyT.js} +2 -2
- package/dist/ui/index.html +1 -1
- package/dist/ui-DhutUMgQ.mjs +472 -0
- package/dist/{usage-BIjf9Xdt.mjs → usage-CT7Z-yjr.mjs} +5 -4
- package/dist/{watch-B8o4Szd_.mjs → watch-0Qr2UfCQ.mjs} +6 -6
- package/dist/worktrees-DHl80k96.mjs +212 -0
- package/package.json +4 -4
- package/dist/__build_info__-CXXMLvjt.mjs +0 -104
- package/dist/ui/assets/app-BlDdP396.js +0 -61
- package/dist/ui-CZS1XKla.mjs +0 -160
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import "./src-
|
|
2
|
-
import { n as ORK_VERSION } from "./__build_info__-
|
|
3
|
-
import { a as openMcp, c as refused, o as tool, s as answer } from "./src-
|
|
4
|
-
import { n as explainNode
|
|
5
|
-
import { p as runMarkerIn } from "./src-
|
|
1
|
+
import { m as isNewer } from "./src-CGqMati3.mjs";
|
|
2
|
+
import { n as ORK_VERSION } from "./__build_info__-CtSVVuZE.mjs";
|
|
3
|
+
import { a as openMcp, c as refused, o as tool, s as answer } from "./src-CpDKIulI.mjs";
|
|
4
|
+
import { n as explainNode } from "./context-C_bksb0C.mjs";
|
|
5
|
+
import { p as runMarkerIn } from "./src-UWkk1sl9.mjs";
|
|
6
6
|
import { defineCommand } from "citty";
|
|
7
7
|
import { z } from "zod";
|
|
8
8
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -43,7 +43,7 @@ const orkMcpServer = (options = {}) => {
|
|
|
43
43
|
...options.script === void 0 ? {} : { script: options.script }
|
|
44
44
|
});
|
|
45
45
|
const all = [...opened.tools, explainTool(opened, options.home)];
|
|
46
|
-
const tools = options.expects !== void 0 && isNewer(options.expects, "0.
|
|
46
|
+
const tools = options.expects !== void 0 && isNewer(options.expects, "0.4.0") ? tooOld(all, options.expects) : all;
|
|
47
47
|
const server = new McpServer({
|
|
48
48
|
name: "ork",
|
|
49
49
|
version: ORK_VERSION
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { At as FEEDBACK_KINDS, Ht as maskReplacer, J as eventOf, K as backfillUsage, Mt as FEEDBACK_TARGETS, Ut as maskSecrets, X as text, Y as inTransaction, jt as FEEDBACK_SEVERITIES, pt as ownProcessStart, q as usageQueries, sn as PROJECT_MODES } from "./src-CGqMati3.mjs";
|
|
2
2
|
import { createInterface } from "node:readline/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { mkdirSync } from "node:fs";
|
|
@@ -213,8 +213,10 @@ const recordOf$3 = (row) => {
|
|
|
213
213
|
};
|
|
214
214
|
const itemOf = (row) => {
|
|
215
215
|
const severities = strings(row, "severities");
|
|
216
|
+
const id = text(row, "fingerprint");
|
|
216
217
|
return {
|
|
217
|
-
id
|
|
218
|
+
id,
|
|
219
|
+
merged: strings(row, "fingerprints").filter((f) => f !== id),
|
|
218
220
|
status: oneOf(STATUSES$1, text(row, "status"), "new"),
|
|
219
221
|
title: text(row, "title"),
|
|
220
222
|
target: oneOf(FEEDBACK_TARGETS, text(row, "target"), "workflow"),
|
|
@@ -229,16 +231,20 @@ const itemOf = (row) => {
|
|
|
229
231
|
models: strings(row, "models")
|
|
230
232
|
};
|
|
231
233
|
};
|
|
232
|
-
|
|
233
|
-
|
|
234
|
+
/** Each record with the item it lists under: its own, or the one it merged into. */
|
|
235
|
+
const ITEMS = `SELECT r.fingerprint, r.status,
|
|
236
|
+
(SELECT title FROM feedback WHERE fingerprint = r.fingerprint ORDER BY id LIMIT 1) AS title,
|
|
234
237
|
MIN(f.target) AS target, MIN(f.kind) AS kind,
|
|
235
238
|
json_group_array(DISTINCT f.severity) AS severities,
|
|
236
239
|
json_group_array(DISTINCT f.source) AS sources,
|
|
237
240
|
COUNT(*) AS count, MIN(f.at) AS first_seen, MAX(f.at) AS last_seen,
|
|
238
241
|
json_group_array(DISTINCT f.run_id) AS runs,
|
|
239
242
|
json_group_array(DISTINCT f.workflow) AS workflows,
|
|
240
|
-
json_group_array(DISTINCT f.model) AS models
|
|
241
|
-
|
|
243
|
+
json_group_array(DISTINCT f.model) AS models,
|
|
244
|
+
json_group_array(DISTINCT f.fingerprint) AS fingerprints
|
|
245
|
+
FROM feedback f
|
|
246
|
+
JOIN feedback_items i ON i.fingerprint = f.fingerprint
|
|
247
|
+
JOIN feedback_items r ON r.fingerprint = COALESCE(i.merged_into, i.fingerprint)`;
|
|
242
248
|
const feedbackQueries = (db) => {
|
|
243
249
|
const insert = db.prepare(`INSERT INTO feedback (run_id, seq, at, node, source, kind, target, severity,
|
|
244
250
|
title, body, evidence, code, fingerprint, workflow, workflow_hash,
|
|
@@ -246,21 +252,51 @@ const feedbackQueries = (db) => {
|
|
|
246
252
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
247
253
|
const insertItem = db.prepare(`INSERT INTO feedback_items (fingerprint, status, updated_at)
|
|
248
254
|
VALUES (?, 'new', ?) ON CONFLICT (fingerprint) DO NOTHING`);
|
|
249
|
-
const shown = db.prepare(`${ITEMS} WHERE
|
|
250
|
-
const every = db.prepare(`${ITEMS} GROUP BY
|
|
251
|
-
const byPrefix = db.prepare(`${ITEMS} WHERE
|
|
252
|
-
|
|
255
|
+
const shown = db.prepare(`${ITEMS} WHERE r.status != 'dismissed' GROUP BY r.fingerprint ORDER BY last_seen DESC`);
|
|
256
|
+
const every = db.prepare(`${ITEMS} GROUP BY r.fingerprint ORDER BY last_seen DESC`);
|
|
257
|
+
const byPrefix = db.prepare(`${ITEMS} WHERE r.fingerprint IN (
|
|
258
|
+
SELECT COALESCE(merged_into, fingerprint) FROM feedback_items
|
|
259
|
+
WHERE substr(fingerprint, 1, ?) = ?
|
|
260
|
+
) GROUP BY r.fingerprint ORDER BY last_seen DESC`);
|
|
261
|
+
const records = db.prepare(`SELECT * FROM feedback
|
|
262
|
+
WHERE fingerprint = ? OR fingerprint IN (
|
|
263
|
+
SELECT fingerprint FROM feedback_items WHERE merged_into = ?
|
|
264
|
+
) ORDER BY id`);
|
|
265
|
+
const rootOf = db.prepare("SELECT COALESCE(merged_into, fingerprint) AS root FROM feedback_items WHERE fingerprint = ?");
|
|
266
|
+
const mergeInto = db.prepare(`UPDATE feedback_items SET merged_into = ?, updated_at = ?
|
|
267
|
+
WHERE (fingerprint = ? OR merged_into = ?) AND fingerprint != ?`);
|
|
268
|
+
const exported = db.prepare(`SELECT f.* FROM feedback f
|
|
269
|
+
LEFT JOIN feedback_items i ON i.fingerprint = f.fingerprint
|
|
270
|
+
LEFT JOIN feedback_items r ON r.fingerprint = COALESCE(i.merged_into, f.fingerprint)
|
|
271
|
+
WHERE ? = 1 OR COALESCE(r.status, 'new') != 'dismissed' ORDER BY f.id`);
|
|
272
|
+
const known = db.prepare("SELECT 1 FROM feedback WHERE run_id = ? AND seq = ?");
|
|
273
|
+
const split = db.prepare("UPDATE feedback_items SET merged_into = NULL, updated_at = ? WHERE merged_into = ?");
|
|
253
274
|
const setStatus = db.prepare("UPDATE feedback_items SET status = ?, updated_at = ? WHERE fingerprint = ?");
|
|
275
|
+
/** Writes a record, its secrets masked, and its item when it's new. */
|
|
276
|
+
const insertRecord = (runId, seq, at, node, feedback) => {
|
|
277
|
+
const { origin } = feedback;
|
|
278
|
+
insert.run(runId, seq, at, node ?? null, feedback.source, feedback.kind, feedback.target, feedback.severity, maskSecrets(feedback.title), maskSecrets(feedback.body), JSON.stringify(feedback.evidence.map((e) => maskSecrets(e))), feedback.code ?? null, feedback.fingerprint, origin.workflow ?? null, origin.workflowHash ?? null, origin.nodeType ?? null, origin.model ?? null, origin.driver ?? null, origin.version ?? null, 1);
|
|
279
|
+
insertItem.run(feedback.fingerprint, at);
|
|
280
|
+
};
|
|
254
281
|
return {
|
|
255
|
-
insertFeedback:
|
|
256
|
-
const { origin } = feedback;
|
|
257
|
-
insert.run(runId, seq, at, node ?? null, feedback.source, feedback.kind, feedback.target, feedback.severity, maskSecrets(feedback.title), maskSecrets(feedback.body), JSON.stringify(feedback.evidence.map((e) => maskSecrets(e))), feedback.code ?? null, feedback.fingerprint, origin.workflow ?? null, origin.workflowHash ?? null, origin.nodeType ?? null, origin.model ?? null, origin.driver ?? null, origin.version ?? null, 1);
|
|
258
|
-
insertItem.run(feedback.fingerprint, at);
|
|
259
|
-
},
|
|
282
|
+
insertFeedback: insertRecord,
|
|
260
283
|
feedbackItems: (filter = {}) => (filter.all === true ? every : shown).all().map((r) => itemOf(r)),
|
|
261
284
|
findFeedback: (prefix) => byPrefix.all(prefix.length, prefix).map((r) => itemOf(r)),
|
|
262
|
-
feedbackRecords: (fingerprint) => records.all(fingerprint).map((r) => recordOf$3(r)),
|
|
263
|
-
setFeedbackStatus: (fingerprint, status) => Number(setStatus.run(status, (/* @__PURE__ */ new Date()).toISOString(), fingerprint).changes) > 0
|
|
285
|
+
feedbackRecords: (fingerprint) => records.all(fingerprint, fingerprint).map((r) => recordOf$3(r)),
|
|
286
|
+
setFeedbackStatus: (fingerprint, status) => Number(setStatus.run(status, (/* @__PURE__ */ new Date()).toISOString(), fingerprint).changes) > 0,
|
|
287
|
+
mergeFeedback: (into, from) => {
|
|
288
|
+
const root = text(rootOf.get(into), "root");
|
|
289
|
+
if (root === "") return 0;
|
|
290
|
+
const at = (/* @__PURE__ */ new Date()).toISOString();
|
|
291
|
+
return inTransaction(db, () => from.filter((fingerprint) => fingerprint !== root).reduce((moved, fingerprint) => moved + Number(mergeInto.run(root, at, fingerprint, fingerprint, root).changes), 0));
|
|
292
|
+
},
|
|
293
|
+
splitFeedback: (fingerprint) => Number(split.run((/* @__PURE__ */ new Date()).toISOString(), fingerprint).changes),
|
|
294
|
+
exportFeedback: (filter = {}) => exported.all(filter.all === true ? 1 : 0).map((r) => recordOf$3(r)),
|
|
295
|
+
importFeedback: (record) => {
|
|
296
|
+
if (known.get(record.runId, record.seq) !== void 0) return false;
|
|
297
|
+
insertRecord(record.runId, record.seq, record.at, record.node, record.feedback);
|
|
298
|
+
return true;
|
|
299
|
+
}
|
|
264
300
|
};
|
|
265
301
|
};
|
|
266
302
|
// ../core/src/store/links.ts
|
|
@@ -681,7 +717,10 @@ const MIGRATIONS = [
|
|
|
681
717
|
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
682
718
|
pid INTEGER NOT NULL,
|
|
683
719
|
at TEXT NOT NULL
|
|
684
|
-
)
|
|
720
|
+
);`,
|
|
721
|
+
`ALTER TABLE feedback_items ADD COLUMN merged_into TEXT;
|
|
722
|
+
CREATE INDEX feedback_items_merged ON feedback_items (merged_into);`,
|
|
723
|
+
`ALTER TABLE runs ADD COLUMN process_start TEXT;`
|
|
685
724
|
];
|
|
686
725
|
const versionOf = (db) => Number(db.prepare("PRAGMA user_version").get()?.["user_version"] ?? 0);
|
|
687
726
|
/** Applies the migrations the database has not had yet. */
|
|
@@ -728,6 +767,8 @@ const recordOf = (row) => {
|
|
|
728
767
|
costUsd: Number(row?.["cost_usd"]),
|
|
729
768
|
depth: Number(row?.["depth"] ?? 0)
|
|
730
769
|
};
|
|
770
|
+
const processStart = text(row, "process_start");
|
|
771
|
+
if (processStart !== "") record.processStart = processStart;
|
|
731
772
|
const parent = text(row, "parent");
|
|
732
773
|
if (parent !== "") record.parent = parent;
|
|
733
774
|
const finished = text(row, "finished_at");
|
|
@@ -741,7 +782,8 @@ const nodeOf = (event) => {
|
|
|
741
782
|
return null;
|
|
742
783
|
};
|
|
743
784
|
/** Opens (and creates) the store at `file`; `:memory:` for tests. */
|
|
744
|
-
const openStore = (file = storePath()) => {
|
|
785
|
+
const openStore = (file = storePath(), options = {}) => {
|
|
786
|
+
const ownStart = options.processStart ?? ownProcessStart;
|
|
745
787
|
if (file !== ":memory:") mkdirSync(path.dirname(file), { recursive: true });
|
|
746
788
|
const db = new DatabaseSync(file);
|
|
747
789
|
db.exec("PRAGMA busy_timeout = 5000");
|
|
@@ -749,12 +791,12 @@ const openStore = (file = storePath()) => {
|
|
|
749
791
|
db.exec("PRAGMA synchronous = NORMAL");
|
|
750
792
|
db.exec("PRAGMA foreign_keys = ON");
|
|
751
793
|
migrate(db);
|
|
752
|
-
const insertRun = db.prepare(`INSERT INTO runs (id, workflow, file, project, status, pid, started_at, parent, depth)
|
|
753
|
-
VALUES (?, ?, ?, ?, 'running', ?, ?, ?, ?)`);
|
|
794
|
+
const insertRun = db.prepare(`INSERT INTO runs (id, workflow, file, project, status, pid, process_start, started_at, parent, depth)
|
|
795
|
+
VALUES (?, ?, ?, ?, 'running', ?, ?, ?, ?, ?)`);
|
|
754
796
|
const lastSeq = db.prepare("SELECT COALESCE(MAX(seq), 0) AS seq FROM events WHERE run_id = ?");
|
|
755
797
|
const insertEvent = db.prepare("INSERT INTO events (run_id, seq, at, type, node, data) VALUES (?, ?, ?, ?, ?, ?)");
|
|
756
798
|
const finishRun = db.prepare("UPDATE runs SET status = ?, finished_at = ?, cost_usd = ? WHERE id = ?");
|
|
757
|
-
const reopenRun = db.prepare("UPDATE runs SET status = 'running', pid = ?, finished_at = NULL WHERE id = ?");
|
|
799
|
+
const reopenRun = db.prepare("UPDATE runs SET status = 'running', pid = ?, process_start = ?, finished_at = NULL WHERE id = ?");
|
|
758
800
|
const addCost = db.prepare("UPDATE runs SET cost_usd = cost_usd + ? WHERE id = ?");
|
|
759
801
|
const byId = db.prepare("SELECT * FROM runs WHERE id = ?");
|
|
760
802
|
const byPrefix = db.prepare("SELECT * FROM runs WHERE substr(id, 1, ?) = ? ORDER BY started_at DESC, rowid DESC");
|
|
@@ -767,18 +809,20 @@ const openStore = (file = storePath()) => {
|
|
|
767
809
|
begin: (run) => {
|
|
768
810
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
769
811
|
const depth = run.parent === void 0 ? 0 : Number(byId.get(run.parent)?.["depth"] ?? 0) + 1;
|
|
770
|
-
|
|
812
|
+
const processStart = ownStart();
|
|
813
|
+
insertRun.run(run.id, run.workflow, run.file, run.project, process.pid, processStart ?? null, startedAt, run.parent ?? null, depth);
|
|
771
814
|
return {
|
|
772
815
|
...run,
|
|
773
816
|
depth,
|
|
774
817
|
status: "running",
|
|
775
818
|
pid: process.pid,
|
|
819
|
+
...processStart === void 0 ? {} : { processStart },
|
|
776
820
|
startedAt,
|
|
777
821
|
costUsd: 0
|
|
778
822
|
};
|
|
779
823
|
},
|
|
780
824
|
reopen: (runId) => {
|
|
781
|
-
reopenRun.run(process.pid, runId);
|
|
825
|
+
reopenRun.run(process.pid, ownStart() ?? null, runId);
|
|
782
826
|
},
|
|
783
827
|
append: (runId, event) => inTransaction(db, () => {
|
|
784
828
|
const at = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as findRun } from "./find-
|
|
3
|
-
import { a as write, c as openStore, i as style, o as writeErr } from "./output-
|
|
4
|
-
import { r as continueRefusal } from "./in-a-run-
|
|
1
|
+
import { H as requestContinue, U as requestPause } from "./src-CGqMati3.mjs";
|
|
2
|
+
import { t as findRun } from "./find-DptmbhGd.mjs";
|
|
3
|
+
import { a as write, c as openStore, i as style, o as writeErr } from "./output-CXSJO00X.mjs";
|
|
4
|
+
import { r as continueRefusal } from "./in-a-run-D1Qc5Mit.mjs";
|
|
5
5
|
import { defineCommand } from "citty";
|
|
6
6
|
// src/commands/pause.ts
|
|
7
7
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as write, c as openStore, i as style, o as writeErr } from "./output-
|
|
1
|
+
import { B as unlinkNodes, L as linkNodes, P as addressOf } from "./src-CGqMati3.mjs";
|
|
2
|
+
import { a as write, c as openStore, i as style, o as writeErr } from "./output-CXSJO00X.mjs";
|
|
3
3
|
import { defineCommand } from "citty";
|
|
4
4
|
// src/commands/relay.ts
|
|
5
5
|
/**
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { D as git, k as lines, lt as liveStatus } from "./src-CGqMati3.mjs";
|
|
2
|
+
import { n as listOrkWorktrees, t as createdWorktrees } from "./list-ZvY2OcLq.mjs";
|
|
3
|
+
import { i as keyOf, n as applyStep, r as cleanStep, s as stepPrunes } from "./clean-Dcr807be.mjs";
|
|
4
|
+
// ../core/src/worktree/words.ts
|
|
5
|
+
/**
|
|
6
|
+
* @file The plain words for what `ork worktrees` shows, in one place, so the
|
|
7
|
+
* CLI and the Worktrees panel say the same thing: an item's name, its merge
|
|
8
|
+
* state, its state as parts (each marked when it's work that removing would
|
|
9
|
+
* lose), a plan's line, and why an item can't be removed. The CLI colors
|
|
10
|
+
* them; the panel lays them out.
|
|
11
|
+
*/
|
|
12
|
+
/** An item's name: its branch, or its run and node when created detached. */
|
|
13
|
+
const labelOf = (item) => item.branch ?? `${item.runId}/${item.nodeId}`;
|
|
14
|
+
/** Merged into the current branch, not, or detached on no branch at all. */
|
|
15
|
+
const mergeStateOf = (item) => {
|
|
16
|
+
if (item.branch === void 0 && item.checkedOut === void 0) return "detached";
|
|
17
|
+
return item.merged ? "merged" : "unmerged";
|
|
18
|
+
};
|
|
19
|
+
/** `3 commits`, `1 commit`. */
|
|
20
|
+
const counted = (count, noun) => `${String(count)} ${noun}${count === 1 ? "" : "s"}`;
|
|
21
|
+
/**
|
|
22
|
+
* An item's state, in order: where its worktree is when it isn't plain, the
|
|
23
|
+
* workflow's own branch, uncommitted changes, unmerged or unreachable commits,
|
|
24
|
+
* and the main checkout or another worktree holding the branch. `into` names
|
|
25
|
+
* the branch "not merged" is measured against, when the words should say it.
|
|
26
|
+
*/
|
|
27
|
+
const stateParts = (item, into) => {
|
|
28
|
+
const parts = [];
|
|
29
|
+
const part = (text, risk = false) => {
|
|
30
|
+
parts.push({
|
|
31
|
+
text,
|
|
32
|
+
risk
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
if (item.path === null) part("no worktree");
|
|
36
|
+
else if (item.missing) part("missing (prunable)");
|
|
37
|
+
if (item.checkedOut !== void 0) part(`on the workflow's branch ${item.checkedOut}`);
|
|
38
|
+
if (item.dirty) part("uncommitted changes", true);
|
|
39
|
+
const state = mergeStateOf(item);
|
|
40
|
+
if (state === "unmerged") {
|
|
41
|
+
const where = into === void 0 ? "" : ` into ${into}`;
|
|
42
|
+
part(`${counted(item.ahead, "commit")} not merged${where}`);
|
|
43
|
+
}
|
|
44
|
+
if (state === "detached") {
|
|
45
|
+
if (item.ahead > 0) part(`${counted(item.ahead, "commit")} on no branch`, true);
|
|
46
|
+
else if (item.path !== null && !item.missing) part("nothing committed");
|
|
47
|
+
}
|
|
48
|
+
if (item.inMainCheckout) part("checked out in the main checkout");
|
|
49
|
+
if (item.heldBy !== void 0) part(`checked out in ${item.heldBy}`);
|
|
50
|
+
return parts;
|
|
51
|
+
};
|
|
52
|
+
/** What a step does, as a plan says it. */
|
|
53
|
+
const planWhat = (step) => {
|
|
54
|
+
const actions = [
|
|
55
|
+
...step.removeWorktree ? ["remove worktree"] : [],
|
|
56
|
+
...stepPrunes(step) && !step.removeWorktree ? ["prune worktree"] : [],
|
|
57
|
+
...step.deleteBranch ? ["delete branch"] : []
|
|
58
|
+
];
|
|
59
|
+
return actions.length > 0 ? actions.join(" + ") : "keep";
|
|
60
|
+
};
|
|
61
|
+
/** `ork/r1/lead: remove worktree`, `r1/check (detached): keep (…)`. */
|
|
62
|
+
const planLine = (step) => {
|
|
63
|
+
const { item } = step;
|
|
64
|
+
return {
|
|
65
|
+
text: `${item.branch === void 0 && item.checkedOut === void 0 ? `${labelOf(item)} (detached)` : labelOf(item)}: ${planWhat(step)}`,
|
|
66
|
+
...step.skip === void 0 ? {} : { reason: step.skip }
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Why an item can't be removed at all, even forced, if it can't: the main
|
|
71
|
+
* checkout or a worktree Ork didn't create holds it, or its run is still
|
|
72
|
+
* going.
|
|
73
|
+
*/
|
|
74
|
+
const removeRefusal = (item, live) => {
|
|
75
|
+
if (item.inMainCheckout) return "checked out in the main checkout; switch it to another branch first";
|
|
76
|
+
if (item.heldBy !== void 0) return `checked out in ${item.heldBy}, a worktree Ork didn't create; switch it to another branch first`;
|
|
77
|
+
return live ? `run ${item.runId} is still going; stop it first` : void 0;
|
|
78
|
+
};
|
|
79
|
+
// ../core/src/worktree/remove.ts
|
|
80
|
+
/** The ids of the project's runs whose process is still going. */
|
|
81
|
+
const liveRunIds = (store, root) => new Set(store.runs({ project: root }).filter((run) => liveStatus(run) === "running").map((run) => run.id));
|
|
82
|
+
/**
|
|
83
|
+
* Whether `root` is a repository with a commit, and the branch checked out
|
|
84
|
+
* there, which "merged" means merged into (null when its HEAD is detached).
|
|
85
|
+
*/
|
|
86
|
+
const projectHead = async (root) => {
|
|
87
|
+
if ((await git(root, [
|
|
88
|
+
"rev-parse",
|
|
89
|
+
"--verify",
|
|
90
|
+
"--quiet",
|
|
91
|
+
"HEAD"
|
|
92
|
+
])).code !== 0) return { ok: false };
|
|
93
|
+
const answer = await git(root, [
|
|
94
|
+
"symbolic-ref",
|
|
95
|
+
"--short",
|
|
96
|
+
"-q",
|
|
97
|
+
"HEAD"
|
|
98
|
+
]);
|
|
99
|
+
return {
|
|
100
|
+
ok: true,
|
|
101
|
+
branch: answer.code === 0 ? answer.stdout.trim() : null
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
/** What Ork created in the repo at `root`, from its runs' records and git. */
|
|
105
|
+
const readWorktrees = async (store, root) => ({
|
|
106
|
+
items: await listOrkWorktrees(root, createdWorktrees(store, root)),
|
|
107
|
+
live: liveRunIds(store, root)
|
|
108
|
+
});
|
|
109
|
+
/**
|
|
110
|
+
* The plan to remove one item, named by `keyOf`. A branch with no worktree is
|
|
111
|
+
* removed by deleting it, so `deleteBranch` is implied there.
|
|
112
|
+
*/
|
|
113
|
+
const planRemove = ({ items, live }, key, deleteBranch) => {
|
|
114
|
+
const item = items.find((i) => keyOf(i) === key);
|
|
115
|
+
if (item === void 0) return {
|
|
116
|
+
ok: false,
|
|
117
|
+
error: `No Ork worktree or branch ${key}.`,
|
|
118
|
+
gone: true
|
|
119
|
+
};
|
|
120
|
+
const refusal = removeRefusal(item, live.has(item.runId));
|
|
121
|
+
if (refusal !== void 0) return {
|
|
122
|
+
ok: false,
|
|
123
|
+
error: `Can't remove ${labelOf(item)}: ${refusal}.`,
|
|
124
|
+
gone: false
|
|
125
|
+
};
|
|
126
|
+
const branches = item.branch !== void 0 && (deleteBranch || item.path === null);
|
|
127
|
+
const careful = cleanStep(item, {
|
|
128
|
+
branches,
|
|
129
|
+
force: false,
|
|
130
|
+
live
|
|
131
|
+
});
|
|
132
|
+
if (careful.skip === void 0) return {
|
|
133
|
+
ok: true,
|
|
134
|
+
plan: {
|
|
135
|
+
step: careful,
|
|
136
|
+
force: false
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
return {
|
|
140
|
+
ok: true,
|
|
141
|
+
plan: {
|
|
142
|
+
step: cleanStep(item, {
|
|
143
|
+
branches,
|
|
144
|
+
force: true,
|
|
145
|
+
live
|
|
146
|
+
}),
|
|
147
|
+
force: true
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
};
|
|
151
|
+
const out = async (cwd, args) => lines((await git(cwd, args)).stdout);
|
|
152
|
+
/** What carrying out `plan` would lose, read from git. */
|
|
153
|
+
const lostWork = async (root, { step }) => {
|
|
154
|
+
const { item } = step;
|
|
155
|
+
const inside = item.path !== null && !item.missing ? item.path : null;
|
|
156
|
+
const changes = inside !== null && item.dirty && step.removeWorktree ? await out(inside, ["status", "--porcelain"]) : [];
|
|
157
|
+
if (step.deleteBranch && item.branch !== void 0 && !item.merged) return {
|
|
158
|
+
changes,
|
|
159
|
+
commits: await out(root, [
|
|
160
|
+
"log",
|
|
161
|
+
"--oneline",
|
|
162
|
+
`HEAD..${item.branch}`
|
|
163
|
+
]),
|
|
164
|
+
commitsOn: "unmerged"
|
|
165
|
+
};
|
|
166
|
+
if (item.branch === void 0 && item.checkedOut === void 0 && inside !== null && item.ahead > 0 && step.removeWorktree) return {
|
|
167
|
+
changes,
|
|
168
|
+
commits: await out(inside, [
|
|
169
|
+
"log",
|
|
170
|
+
"--oneline",
|
|
171
|
+
"HEAD",
|
|
172
|
+
"--not",
|
|
173
|
+
"--branches",
|
|
174
|
+
"--tags",
|
|
175
|
+
"--remotes"
|
|
176
|
+
]),
|
|
177
|
+
commitsOn: "no branch"
|
|
178
|
+
};
|
|
179
|
+
return {
|
|
180
|
+
changes,
|
|
181
|
+
commits: [],
|
|
182
|
+
commitsOn: null
|
|
183
|
+
};
|
|
184
|
+
};
|
|
185
|
+
/**
|
|
186
|
+
* Carries out a remove plan; what it did goes into `done` as it goes. The
|
|
187
|
+
* caller has checked that a plan needing `force` was asked to force. It touches
|
|
188
|
+
* only its own item, as each of clean's steps does.
|
|
189
|
+
*/
|
|
190
|
+
const applyRemove = async (root, { step, force }, done = []) => {
|
|
191
|
+
await applyStep(root, step, force, done);
|
|
192
|
+
return done;
|
|
193
|
+
};
|
|
194
|
+
export { readWorktrees as a, planLine as c, projectHead as i, removeRefusal as l, lostWork as n, labelOf as o, planRemove as r, mergeStateOf as s, applyRemove as t, stateParts as u };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { c as openStore, o as writeErr } from "./output-
|
|
3
|
-
import { u as inheritedScrubber } from "./src-
|
|
1
|
+
import { Xt as wakeRun, Yt as socketPath, lt as liveStatus, wt as scriptContactsOf } from "./src-CGqMati3.mjs";
|
|
2
|
+
import { c as openStore, o as writeErr } from "./output-CXSJO00X.mjs";
|
|
3
|
+
import { u as inheritedScrubber } from "./src-UWkk1sl9.mjs";
|
|
4
4
|
import { defineCommand } from "citty";
|
|
5
5
|
// ../core/src/remote/send.ts
|
|
6
6
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { At as FEEDBACK_KINDS, Mt as FEEDBACK_TARGETS, ct as parseReport, jt as FEEDBACK_SEVERITIES } from "./src-CGqMati3.mjs";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { randomBytes, timingSafeEqual } from "node:crypto";
|
|
4
4
|
import { createInterface } from "node:readline";
|