@krapht/ork 1.0.0-alpha.12 → 1.0.0-alpha.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/{__build_info__-BJbE3Zvp.mjs → __build_info__-Da73_nBs.mjs} +3 -3
  2. package/dist/{attach-BmV5ckb9.mjs → attach-E6NNyA3T.mjs} +4 -4
  3. package/dist/bin.mjs +114 -55
  4. package/dist/{builtin-files-w0YULJ4c.mjs → builtin-files-n6o6oWpB.mjs} +2 -2
  5. package/dist/{clean-D8GI_2K-.mjs → clean-Bh4KZgzc.mjs} +1 -1
  6. package/dist/{completion-CzElB8nS.mjs → completion-SNNNxnv9.mjs} +6 -6
  7. package/dist/{context-BAwgVSX5.mjs → context-DHudLA5e.mjs} +4 -4
  8. package/dist/{decide-DlbyctOE.mjs → decide-DuIHACvH.mjs} +1 -1
  9. package/dist/{find-p05wlbmu.mjs → find-CjsNYAAf.mjs} +1 -1
  10. package/dist/{format-Dni98cbh.mjs → format-C60TFpRe.mjs} +1 -1
  11. package/dist/{help-groups-DYP_zMRP.mjs → help-groups-CyfPWlST.mjs} +4 -0
  12. package/dist/{in-a-run-BI-hLIl_.mjs → in-a-run-BYjTXaST.mjs} +1 -1
  13. package/dist/{limits-CElmhowV.mjs → limits-C5QKZNLT.mjs} +2 -2
  14. package/dist/{list-zW2xKuGr.mjs → list-BUb5Uc3H.mjs} +1 -1
  15. package/dist/{mcp-CM3b65AZ.mjs → mcp-bcctR-wc.mjs} +6 -6
  16. package/dist/once-DPQu5Q3H.mjs +76 -0
  17. package/dist/{pause-BuS83WAI.mjs → pause-BPvlDIvz.mjs} +3 -3
  18. package/dist/{relay-DAsJA8wF.mjs → relay-C8W0t_zc.mjs} +1 -1
  19. package/dist/{remove-Dh7D_hVn.mjs → remove-BkFWGVYo.mjs} +3 -3
  20. package/dist/run-CQIfq918.mjs +192 -0
  21. package/dist/{send-MFEQZH31.mjs → send-CaZlGG5a.mjs} +2 -2
  22. package/dist/{src-CdDTQLdQ.mjs → src-BeR6f70A.mjs} +424 -206
  23. package/dist/{src-S9yK2OHU.mjs → src-CGJGL1sZ.mjs} +7 -7
  24. package/dist/{src-D1QedD5y.mjs → src-CqRqsb2t.mjs} +44 -7
  25. package/dist/{src-BfxkJOYm.mjs → src-D93tHZ5B.mjs} +110 -35
  26. package/dist/{store-B2OaE4ID.mjs → store-CM3cERGl.mjs} +14 -5
  27. package/dist/ui/THIRD_PARTY_LICENSES.txt +18 -0
  28. package/dist/ui/assets/app-DYRC0V4p.js +111 -0
  29. package/dist/ui/assets/{index-BL7Ad0OA.js → index-CWwm8HTG.js} +2 -2
  30. package/dist/ui/index.html +1 -1
  31. package/dist/{ui-C4Ys26oO.mjs → ui-D2Ydofq0.mjs} +3 -3
  32. package/dist/{usage-C2zx-X6q.mjs → usage-6WIowblz.mjs} +4 -4
  33. package/dist/{watch-DzCeqM05.mjs → watch-uOU_WeBI.mjs} +5 -5
  34. package/dist/{workflows-Rr5w90A0.mjs → workflows-7tQdMnrA.mjs} +2 -2
  35. package/dist/{worktrees-f6SAbum5.mjs → worktrees-ZCII298F.mjs} +3 -3
  36. package/package.json +1 -1
  37. package/dist/ui/assets/app-CAoZtku5.js +0 -75
@@ -1,8 +1,8 @@
1
- import { u as isNewer } from "./src-CdDTQLdQ.mjs";
2
- import { n as ORK_VERSION } from "./__build_info__-BJbE3Zvp.mjs";
3
- import { p as runMarkerIn } from "./src-BfxkJOYm.mjs";
4
- import { n as explainNode } from "./context-BAwgVSX5.mjs";
5
- import { a as openMcp, c as refused, o as tool, s as answer } from "./src-S9yK2OHU.mjs";
1
+ import { d as isNewer } from "./src-BeR6f70A.mjs";
2
+ import { n as ORK_VERSION } from "./__build_info__-Da73_nBs.mjs";
3
+ import { p as runMarkerIn } from "./src-D93tHZ5B.mjs";
4
+ import { n as explainNode } from "./context-DHudLA5e.mjs";
5
+ import { a as openMcp, c as refused, o as tool, s as answer } from "./src-CGJGL1sZ.mjs";
6
6
  import { z } from "zod";
7
7
  import { defineCommand } from "citty";
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, "1.0.0-alpha.12") ? tooOld(all, options.expects) : all;
46
+ const tools = options.expects !== void 0 && isNewer(options.expects, "1.0.0-alpha.13") ? tooOld(all, options.expects) : all;
47
47
  const server = new McpServer({
48
48
  name: "ork",
49
49
  version: ORK_VERSION
@@ -0,0 +1,76 @@
1
+ import "./src-BeR6f70A.mjs";
2
+ import { t as runOnce } from "./run-CQIfq918.mjs";
3
+ import { a as style, o as write, s as writeErr } from "./output-CnRzrbUM.mjs";
4
+ import { t as ago } from "./format-C60TFpRe.mjs";
5
+ import { spawn } from "node:child_process";
6
+ import { defineCommand, parseArgs } from "citty";
7
+ // src/commands/once.ts
8
+ /**
9
+ * @file `ork once -- <command>`: runs a command unless the same check already
10
+ * passed on identical files, and records a pass (specs/check-once.md). The
11
+ * command's own output and exit code are `ork once`'s; a hit prints one line
12
+ * and exits 0. Everything after `--` is the command, so its flags are never
13
+ * Ork's.
14
+ */
15
+ const args = {
16
+ as: {
17
+ type: "string",
18
+ description: "The check's name (default: the command's text)"
19
+ },
20
+ covers: {
21
+ type: "string",
22
+ description: "Also record the pass for this name (repeat for more)"
23
+ },
24
+ "with-head": {
25
+ type: "boolean",
26
+ description: "Key the check by HEAD too (for checks relative to it)"
27
+ },
28
+ force: {
29
+ type: "boolean",
30
+ description: "Run even after a pass"
31
+ }
32
+ };
33
+ /** The command's exit code: a signal or a program that can't start is 1. */
34
+ const exitCodeOf = (command) => new Promise((resolve) => {
35
+ const [program = "", ...rest] = command;
36
+ const child = spawn(program, rest, { stdio: "inherit" });
37
+ child.on("error", (error) => {
38
+ writeErr(`Cannot run ${program}: ${error.message}`);
39
+ resolve(1);
40
+ });
41
+ child.on("close", (code) => {
42
+ resolve(code ?? 1);
43
+ });
44
+ });
45
+ const listOf = (value) => [value].flat().filter((item) => typeof item === "string" && item !== "");
46
+ const once = defineCommand({
47
+ meta: {
48
+ name: "once",
49
+ description: "Run a command unless the same check already passed on these files: ork once [flags] -- <command…>"
50
+ },
51
+ args,
52
+ run: async ({ rawArgs }) => {
53
+ const split = rawArgs.indexOf("--");
54
+ const command = split === -1 ? [] : rawArgs.slice(split + 1);
55
+ if (command.length === 0) {
56
+ writeErr("Say what to run after --: ork once [--as <name>] [--covers <name>] [--with-head] [--force] -- <command…>");
57
+ process.exitCode = 1;
58
+ return;
59
+ }
60
+ const given = parseArgs(rawArgs.slice(0, split), args);
61
+ const run = process.env["ORK_RUN"];
62
+ const node = process.env["ORK_NODE"];
63
+ const result = await runOnce({
64
+ cwd: process.cwd(),
65
+ name: given["as"] ?? command.join(" "),
66
+ covers: listOf(given["covers"]),
67
+ withHead: given["with-head"] === true,
68
+ force: given["force"] === true,
69
+ ...run === void 0 || node === void 0 ? {} : { who: `${run}/${node}` },
70
+ run: () => exitCodeOf(command)
71
+ });
72
+ if (result.skipped) write(`${style("green", "✓")} check already passed on these files · ${ago(result.pass.at)} · ${result.pass.who}`);
73
+ process.exitCode = result.code;
74
+ }
75
+ });
76
+ export { once };
@@ -1,7 +1,7 @@
1
- import { C as openStore, G as requestContinue, K as requestPause } from "./src-CdDTQLdQ.mjs";
2
- import { t as findRun } from "./find-p05wlbmu.mjs";
1
+ import { U as requestContinue, W as requestPause, w as openStore } from "./src-BeR6f70A.mjs";
2
+ import { t as findRun } from "./find-CjsNYAAf.mjs";
3
3
  import { a as style, o as write, s as writeErr } from "./output-CnRzrbUM.mjs";
4
- import { i as continueRefusal } from "./in-a-run-BI-hLIl_.mjs";
4
+ import { i as continueRefusal } from "./in-a-run-BYjTXaST.mjs";
5
5
  import { defineCommand } from "citty";
6
6
  // src/commands/pause.ts
7
7
  /**
@@ -1,4 +1,4 @@
1
- import { B as linkNodes, C as openStore, L as addressOf, U as unlinkNodes } from "./src-CdDTQLdQ.mjs";
1
+ import { F as addressOf, R as linkNodes, V as unlinkNodes, w as openStore } from "./src-BeR6f70A.mjs";
2
2
  import { a as style, o as write, s as writeErr } from "./output-CnRzrbUM.mjs";
3
3
  import { defineCommand } from "citty";
4
4
  // src/commands/relay.ts
@@ -1,6 +1,6 @@
1
- import { A as lines, O as git, xt as liveStatus } from "./src-CdDTQLdQ.mjs";
2
- import { n as listOrkWorktrees, t as createdWorktrees } from "./list-zW2xKuGr.mjs";
3
- import { i as keyOf, n as applyStep, r as cleanStep, s as stepPrunes } from "./clean-D8GI_2K-.mjs";
1
+ import { Bt as lines, Rt as git, _t as liveStatus } from "./src-BeR6f70A.mjs";
2
+ import { n as listOrkWorktrees, t as createdWorktrees } from "./list-BUb5Uc3H.mjs";
3
+ import { i as keyOf, n as applyStep, r as cleanStep, s as stepPrunes } from "./clean-Bh4KZgzc.mjs";
4
4
  // ../core/src/worktree/words.ts
5
5
  /**
6
6
  * @file The plain words for what `ork worktrees` shows, in one place, so the
@@ -0,0 +1,192 @@
1
+ import { Rt as git, zt as gitOk } from "./src-BeR6f70A.mjs";
2
+ import path from "node:path";
3
+ import { copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
4
+ import { createHash, randomUUID } from "node:crypto";
5
+ import { tmpdir } from "node:os";
6
+ // ../core/src/once/key.ts
7
+ /**
8
+ * @file The key of a check run once (`ork once`, `verify: { once: true }`): the
9
+ * working tree's files as a git tree (tracked and untracked, not ignored),
10
+ * written through a temporary index so the real one is untouched, with `HEAD`
11
+ * and the repository's common git folder, where passes are kept for every
12
+ * worktree. The key is the files, not the commit: a commit or a no-op rebase
13
+ * keeps it.
14
+ */
15
+ /**
16
+ * The key of the files in the worktree at `cwd`. Rejects outside a git
17
+ * repository. Starts from a copy of the real index, so git hashes only the
18
+ * files whose stat changed.
19
+ */
20
+ const treeKeyOf = async (cwd) => {
21
+ const [commonDir = "", index = ""] = (await gitOk(cwd, [
22
+ "rev-parse",
23
+ "--git-common-dir",
24
+ "--git-path",
25
+ "index"
26
+ ])).split("\n").map((line) => path.resolve(cwd, line.trim()));
27
+ const scratch = mkdtempSync(path.join(tmpdir(), "ork-once-"));
28
+ const env = { GIT_INDEX_FILE: path.join(scratch, "index") };
29
+ try {
30
+ if (existsSync(index)) copyFileSync(index, env.GIT_INDEX_FILE);
31
+ await gitOk(cwd, ["add", "-A"], env);
32
+ const tree = (await gitOk(cwd, ["write-tree"], env)).trim();
33
+ const head = await git(cwd, [
34
+ "rev-parse",
35
+ "--verify",
36
+ "-q",
37
+ "HEAD"
38
+ ]);
39
+ return {
40
+ tree,
41
+ head: head.code === 0 ? head.stdout.trim() : null,
42
+ commonDir
43
+ };
44
+ } finally {
45
+ rmSync(scratch, {
46
+ recursive: true,
47
+ force: true
48
+ });
49
+ }
50
+ };
51
+ // ../core/src/once/store.ts
52
+ /**
53
+ * @file The passes of checks run once, kept in `<git-common-dir>/ork-once/`,
54
+ * outside every worktree and shared by all of them. A pass is one JSON file
55
+ * per check name, files and Node version; it holds the tree, `HEAD`, the time
56
+ * and who ran it, nothing else. Only a passing run writes one (Ork's process
57
+ * or the check's own, never an agent's edit). Pruned past 7 days or 500
58
+ * entries.
59
+ */
60
+ const ONCE_MAX_AGE_MS = 6048e5;
61
+ /** The folder of passes for a repository's common git folder. */
62
+ const onceDirOf = (commonDir) => path.join(commonDir, "ork-once");
63
+ const fileOf = (dir, name, tree, node) => path.join(dir, `${createHash("sha256").update(JSON.stringify([
64
+ name,
65
+ tree,
66
+ node
67
+ ])).digest("hex")}.json`);
68
+ const read = (file) => {
69
+ try {
70
+ const value = JSON.parse(readFileSync(file, "utf8"));
71
+ if (typeof value !== "object" || value === null || !("name" in value && "tree" in value && "head" in value && "node" in value && "at" in value && "who" in value)) return null;
72
+ const { name, tree, head, node, at, who } = value;
73
+ return typeof name === "string" && typeof tree === "string" && (typeof head === "string" || head === null) && typeof node === "string" && typeof at === "string" && typeof who === "string" ? {
74
+ name,
75
+ tree,
76
+ head,
77
+ node,
78
+ at,
79
+ who
80
+ } : null;
81
+ } catch {
82
+ return null;
83
+ }
84
+ };
85
+ /** The pass of `name` on these files, or `null`. */
86
+ const findPass = ({ key, name, withHead = false, node = process.version }) => {
87
+ const pass = read(fileOf(onceDirOf(key.commonDir), name, key.tree, node));
88
+ if (pass === null || pass.tree !== key.tree || pass.node !== node) return null;
89
+ return withHead && pass.head !== key.head ? null : pass;
90
+ };
91
+ /** Records a pass under `name` and each covered name, then prunes. */
92
+ const recordPass = ({ key, name, covers = [], who, node = process.version, at = /* @__PURE__ */ new Date() }) => {
93
+ const dir = onceDirOf(key.commonDir);
94
+ mkdirSync(dir, { recursive: true });
95
+ const pass = {
96
+ name,
97
+ tree: key.tree,
98
+ head: key.head,
99
+ node,
100
+ at: at.toISOString(),
101
+ who
102
+ };
103
+ for (const each of [name, ...covers]) {
104
+ const temp = path.join(dir, `.${randomUUID()}.tmp`);
105
+ writeFileSync(temp, `${JSON.stringify({
106
+ ...pass,
107
+ name: each
108
+ })}\n`);
109
+ renameSync(temp, fileOf(dir, each, pass.tree, node));
110
+ }
111
+ prunePasses(key.commonDir, { now: at });
112
+ return pass;
113
+ };
114
+ /**
115
+ * Removes passes older than `maxAgeMs` or past the newest `maxEntries`, and
116
+ * files that don't read as a pass. Returns how many it removed.
117
+ */
118
+ const prunePasses = (commonDir, { now = /* @__PURE__ */ new Date(), maxAgeMs = ONCE_MAX_AGE_MS, maxEntries = 500 } = {}) => {
119
+ const dir = onceDirOf(commonDir);
120
+ let names;
121
+ try {
122
+ names = readdirSync(dir).filter((name) => name.endsWith(".json"));
123
+ } catch {
124
+ return 0;
125
+ }
126
+ const kept = [];
127
+ const stale = [];
128
+ for (const name of names) {
129
+ const file = path.join(dir, name);
130
+ const at = Date.parse(read(file)?.at ?? "");
131
+ if (Number.isNaN(at) || now.getTime() - at > maxAgeMs) stale.push(file);
132
+ else kept.push({
133
+ file,
134
+ at
135
+ });
136
+ }
137
+ kept.sort((a, b) => b.at - a.at);
138
+ stale.push(...kept.slice(maxEntries).map((entry) => entry.file));
139
+ for (const file of stale) rmSync(file, { force: true });
140
+ return stale.length;
141
+ };
142
+ // ../core/src/once/run.ts
143
+ /**
144
+ * @file Runs a check once per set of files, for `ork once` and for
145
+ * `verify:`/tool commands with `once: true`: skip it when the same check
146
+ * already passed on identical files, else run it and record a pass when it
147
+ * exits 0 and the files didn't change while it ran. `force` and
148
+ * `ORK_ONCE=off` run it anyway; outside a git repository it always runs.
149
+ */
150
+ /** `null` outside a git repository: nothing to key the files by. */
151
+ const keyOf = (cwd) => treeKeyOf(cwd).catch(() => null);
152
+ /** Runs `run` unless the check already passed on these files. */
153
+ const runOnce = async ({ cwd, name, covers = [], withHead = false, force = false, who = "you", run, env = process.env, node = process.version }) => {
154
+ if (env["ORK_ONCE"] === "off") return {
155
+ skipped: false,
156
+ code: await run()
157
+ };
158
+ const before = await keyOf(cwd);
159
+ const earlier = before === null || force ? null : findPass({
160
+ key: before,
161
+ name,
162
+ withHead,
163
+ node
164
+ });
165
+ if (earlier !== null) return {
166
+ skipped: true,
167
+ pass: earlier,
168
+ code: 0
169
+ };
170
+ const code = await run();
171
+ if (code !== 0 || before === null) return {
172
+ skipped: false,
173
+ code
174
+ };
175
+ const after = await keyOf(cwd);
176
+ if (after?.tree !== before.tree || after.head !== before.head) return {
177
+ skipped: false,
178
+ code
179
+ };
180
+ return {
181
+ skipped: false,
182
+ pass: recordPass({
183
+ key: before,
184
+ name,
185
+ covers,
186
+ who,
187
+ node
188
+ }),
189
+ code
190
+ };
191
+ };
192
+ export { runOnce as t };
@@ -1,6 +1,6 @@
1
- import { C as openStore, Cn as wakeRun, jt as scriptContactsOf, xn as socketPath, xt as liveStatus } from "./src-CdDTQLdQ.mjs";
1
+ import { Cn as socketPath, Dt as scriptContactsOf, Tn as wakeRun, _t as liveStatus, w as openStore } from "./src-BeR6f70A.mjs";
2
2
  import { s as writeErr } from "./output-CnRzrbUM.mjs";
3
- import { u as inheritedScrubber } from "./src-BfxkJOYm.mjs";
3
+ import { u as inheritedScrubber } from "./src-D93tHZ5B.mjs";
4
4
  import { defineCommand } from "citty";
5
5
  // ../core/src/remote/send.ts
6
6
  /**