dw-sdlc 0.20.0 → 0.21.1

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 (97) hide show
  1. package/README.md +13 -13
  2. package/config/agentic.config.schema.json +1 -1
  3. package/dist/agentic-config.d.ts +16 -0
  4. package/dist/agentic-config.js +28 -23
  5. package/dist/cannot-run.d.ts +27 -0
  6. package/dist/cannot-run.js +48 -0
  7. package/dist/catalogue.js +2 -1
  8. package/dist/checks/agents-map.js +7 -8
  9. package/dist/checks/check.d.ts +10 -8
  10. package/dist/checks/check.js +9 -7
  11. package/dist/checks/decision-records.js +3 -2
  12. package/dist/checks/file-size-ratchet.js +13 -17
  13. package/dist/checks/index.d.ts +6 -5
  14. package/dist/checks/index.js +10 -9
  15. package/dist/checks/markdown-refs.js +58 -6
  16. package/dist/checks/primitives.js +5 -5
  17. package/dist/checks/routed-docs.js +1 -3
  18. package/dist/checks/skill-sections.js +12 -20
  19. package/dist/checks/tree.js +7 -7
  20. package/dist/cli.d.ts +2 -1
  21. package/dist/cli.js +26 -5
  22. package/dist/commands/check.js +27 -42
  23. package/dist/commands/config.js +22 -27
  24. package/dist/commands/doctor.d.ts +2 -0
  25. package/dist/commands/doctor.js +11 -18
  26. package/dist/commands/gate.js +35 -40
  27. package/dist/commands/lead-start.js +28 -42
  28. package/dist/commands/pr-reviewed.js +41 -59
  29. package/dist/commands/pr-ship.js +72 -87
  30. package/dist/commands/primitives.js +38 -40
  31. package/dist/commands/recap.js +23 -74
  32. package/dist/commands/references-setup.js +28 -76
  33. package/dist/commands/references-sync.js +29 -53
  34. package/dist/commands/setup.d.ts +2 -7
  35. package/dist/commands/setup.js +38 -90
  36. package/dist/commands/sweep.js +46 -157
  37. package/dist/commands/worker-ls.js +15 -31
  38. package/dist/commands/worker-report.js +36 -52
  39. package/dist/commands/worker-start.js +43 -57
  40. package/dist/drift.js +2 -1
  41. package/dist/exec.d.ts +20 -0
  42. package/dist/exec.js +37 -4
  43. package/dist/gate-cache.js +2 -1
  44. package/dist/git.d.ts +32 -0
  45. package/dist/git.js +60 -0
  46. package/dist/json-schema.js +1 -3
  47. package/dist/landed.js +11 -22
  48. package/dist/paused-operation.d.ts +2 -0
  49. package/dist/paused-operation.js +17 -0
  50. package/dist/pins.js +2 -1
  51. package/dist/pr/decide.d.ts +4 -16
  52. package/dist/pr/decide.js +48 -83
  53. package/dist/pr/delta.d.ts +3 -4
  54. package/dist/pr/delta.js +11 -20
  55. package/dist/pr/read.d.ts +27 -30
  56. package/dist/pr/read.js +96 -158
  57. package/dist/pr/reply.d.ts +34 -0
  58. package/dist/pr/reply.js +112 -0
  59. package/dist/pr/route.d.ts +11 -3
  60. package/dist/pr/route.js +5 -5
  61. package/dist/pr/standing.d.ts +138 -0
  62. package/dist/pr/standing.js +181 -0
  63. package/dist/pr/state.d.ts +14 -69
  64. package/dist/pr/state.js +75 -148
  65. package/dist/pr/upstream.d.ts +1 -6
  66. package/dist/pr/upstream.js +2 -2
  67. package/dist/primitives-diff.js +1 -1
  68. package/dist/recap-next.js +34 -47
  69. package/dist/recap-primitives.js +1 -1
  70. package/dist/references-key.d.ts +0 -1
  71. package/dist/references-key.js +3 -4
  72. package/dist/run-command.d.ts +4 -1
  73. package/dist/run-command.js +32 -11
  74. package/dist/setup-gate.d.ts +1 -7
  75. package/dist/setup-rules.js +2 -6
  76. package/dist/setup-writer.d.ts +32 -0
  77. package/dist/setup-writer.js +60 -0
  78. package/dist/shell-line.d.ts +2 -0
  79. package/dist/shell-line.js +5 -0
  80. package/dist/sweep-removal.d.ts +31 -0
  81. package/dist/sweep-removal.js +133 -0
  82. package/dist/sweep-terminals.d.ts +1 -1
  83. package/dist/sweep-terminals.js +21 -57
  84. package/dist/sweep-trees.d.ts +19 -3
  85. package/dist/sweep-trees.js +67 -31
  86. package/dist/worker/herdr.d.ts +67 -23
  87. package/dist/worker/herdr.js +126 -77
  88. package/dist/worker/launch.js +32 -45
  89. package/dist/worker/origin.d.ts +1 -1
  90. package/dist/worker/origin.js +9 -11
  91. package/dist/worker/tabs.js +11 -7
  92. package/dist/working-tree.d.ts +0 -8
  93. package/dist/working-tree.js +6 -22
  94. package/dist/worktree-lock.js +1 -1
  95. package/package.json +1 -1
  96. package/dist/git-env.d.ts +0 -2
  97. package/dist/git-env.js +0 -10
@@ -1,11 +1,10 @@
1
1
  // What a Sweep does with herdr terminals: it stops and removes only a Worker of its own whose turn has ended,
2
2
  // in a worktree that goes in the same Sweep or is already gone. docs/adr/0015 sets the bounds.
3
3
  import { existsSync, realpathSync } from "node:fs";
4
- import { isObject, parseObject } from "./json-text.js";
5
- import { shellLine } from "./pr/decide.js";
6
- import { CannotRun } from "./pr/read.js";
4
+ import { CannotRun } from "./cannot-run.js";
5
+ import { shellLine } from "./shell-line.js";
7
6
  import { worktreesOf } from "./sweep-trees.js";
8
- import { call, hcObject, isLive, Refusal, said, workersOf } from "./worker/herdr.js";
7
+ import { isLive, listTerminals, readTerminal, removeTerminal, stopTerminal, workersOf } from "./worker/herdr.js";
9
8
  import { lockVerdict } from "./worktree-lock.js";
10
9
  /** Statuses of a Worker that is not at the end of its turn, with how the Sweep says it. */
11
10
  const AT_WORK = {
@@ -18,53 +17,30 @@ export function sweepingTerminal() {
18
17
  const id = process.env.HC_TERMINAL_ID;
19
18
  return id === undefined || id === "" ? undefined : id;
20
19
  }
21
- function word(value) {
22
- return typeof value === "string" && value !== "" ? value : undefined;
23
- }
24
20
  /**
25
- * Each live terminal with the folder it works in, `hc get`'s foreground. An `hc get` that fails stops the
26
- * Sweep, since a terminal in a folder it cannot see must not count as absent; one gone since the listing is dropped.
21
+ * Each Worker with the folder it works in and its program's pid, as `hc get` reports them. One gone since the
22
+ * listing is dropped; an `hc get` that fails stops the Sweep, since a terminal in a folder it cannot see must
23
+ * not count as absent.
27
24
  */
28
- function foldersOf(list) {
29
- return list.flatMap((worker) => {
25
+ function located(workers) {
26
+ return workers.flatMap((worker) => {
30
27
  if (!isLive(worker)) {
31
28
  return [{ ...worker, pid: undefined }];
32
29
  }
33
- const got = call("hc", ["get", worker.address, "--json"]);
34
- if (got.status === 3) {
35
- return [];
36
- }
37
- const parsed = got.status === 0 ? parseObject(got.stdout) : undefined;
38
- if (parsed === undefined) {
39
- throw new Refusal(`hc get ${worker.address} failed: ${got.status === 0 ? "its reply is no JSON object" : said(got)}`);
40
- }
41
- const { cwd, pid } = isObject(parsed.foreground) ? parsed.foreground : { cwd: undefined, pid: undefined };
42
- const there = typeof cwd === "string" && cwd !== "" ? { ...worker, folder: cwd } : worker;
43
- return [{ ...there, pid: typeof pid === "number" ? pid : undefined }];
30
+ const reading = readTerminal(worker.address);
31
+ return "gone" in reading ? [] : [{ ...worker, folder: reading.folder ?? worker.folder, pid: reading.pid }];
44
32
  });
45
33
  }
46
34
  /** The live terminals `hc ls` lists, each with the folder it works in. */
47
35
  function liveTerminals() {
48
- const { terminals } = hcObject(["ls", "--json"]);
49
- if (!Array.isArray(terminals)) {
50
- throw new Refusal("hc ls answered no list of terminals", "check hc --version");
51
- }
52
- const places = new Map();
53
- const listed = terminals.flatMap((entry) => {
54
- const address = isObject(entry) ? word(entry.address) : undefined;
55
- if (!isObject(entry) || address === undefined || typeof entry.cwd !== "string") {
56
- throw new Refusal("hc ls answered a terminal without an address or a folder", "check hc --version");
36
+ return listTerminals().flatMap(({ address, name, folder, place }) => {
37
+ const reading = readTerminal(address);
38
+ if ("gone" in reading) {
39
+ return [];
57
40
  }
58
- places.set(address, word(entry.where));
59
- const name = word(entry.name) ?? address;
60
- return [{ name, address, state: "live", status: word(entry.status) ?? "unknown", folder: entry.cwd }];
41
+ const working = reading.folder ?? folder;
42
+ return [{ address, name, folder: working, place: place ?? working }];
61
43
  });
62
- return foldersOf(listed).map(({ name, address, folder }) => ({
63
- name,
64
- address,
65
- folder,
66
- place: places.get(address) ?? folder
67
- }));
68
44
  }
69
45
  /**
70
46
  * Reads herdr for the sweeping terminal `own`: its Workers, finished ones included, and the other live
@@ -72,7 +48,7 @@ function liveTerminals() {
72
48
  */
73
49
  export function readTerminals(own) {
74
50
  try {
75
- const workers = foldersOf(workersOf(own, true));
51
+ const workers = located(workersOf(own, true));
76
52
  const live = liveTerminals();
77
53
  const addresses = new Set(workers.map(({ address }) => address));
78
54
  return {
@@ -82,10 +58,7 @@ export function readTerminals(own) {
82
58
  };
83
59
  }
84
60
  catch (error) {
85
- if (error instanceof Refusal) {
86
- throw new CannotRun(error.message, error.remedy ?? "check hc, then call again");
87
- }
88
- throw error;
61
+ throw error instanceof CannotRun ? error.orRemedy("check hc, then call again") : error;
89
62
  }
90
63
  }
91
64
  function canonical(path) {
@@ -176,21 +149,12 @@ export function stopsOf(holders, tree) {
176
149
  : []);
177
150
  }
178
151
  /** Stops, if `item` asks, and removes its terminal; the words of the `hc` that refused, or undefined when it is gone. */
179
- export function removeTerminal(item) {
152
+ export function takeDown(item) {
180
153
  try {
181
- if (item.stop === true) {
182
- const killed = call("hc", ["kill", item.name]);
183
- // 11: the terminal finished between the read and the stop.
184
- if (killed.status !== 0 && killed.status !== 11) {
185
- return `hc kill ${item.name} failed: ${said(killed)}`;
186
- }
187
- }
188
- const removed = call("hc", ["rm", item.name]);
189
- // 3: the terminal is gone already.
190
- return removed.status === 0 || removed.status === 3 ? undefined : `hc rm ${item.name} failed: ${said(removed)}`;
154
+ return (item.stop === true ? stopTerminal(item.name) : undefined) ?? removeTerminal(item.name);
191
155
  }
192
156
  catch (error) {
193
- if (error instanceof Refusal) {
157
+ if (error instanceof CannotRun) {
194
158
  return error.message;
195
159
  }
196
160
  throw error;
@@ -1,5 +1,11 @@
1
- /** `git <args>` at `root`, or a stop naming its problem. */
2
- export declare function must(root: string, args: string[]): string;
1
+ /** The forced delete of `branch`, the dev's to run. */
2
+ export declare function forcedDelete(branch: string): string;
3
+ /** The forced removal of the worktree at `path`, the dev's to run; git wants --force twice for one still locked. */
4
+ export declare function forcedRemoval(path: string, locked?: boolean): string;
5
+ /** The unlock of the worktree at `path`, the dev's to run. */
6
+ export declare function unlockCommand(path: string): string;
7
+ /** The commit the local `branch` points at, or undefined once it is gone. */
8
+ export declare function tipOf(root: string, branch: string): string | undefined;
3
9
  /** The local branches of the repository at `root`. */
4
10
  export declare function branchesOf(root: string): string[];
5
11
  export type Tree = {
@@ -9,14 +15,24 @@ export type Tree = {
9
15
  lock: string | undefined;
10
16
  prunable: boolean;
11
17
  };
12
- /** Every worktree of the repository, the main checkout first. */
18
+ /** Every worktree of the repository, the main checkout first; -z leaves a path or a lock's reason unquoted. */
13
19
  export declare function worktreesOf(root: string): Tree[];
14
20
  /** The files `git status` lists in the worktree at `path`, ignored ones aside, or why it cannot be read. */
15
21
  export declare function dirtyFiles(path: string): string[] | {
16
22
  error: string;
17
23
  };
24
+ /** What in the worktree at `path` a removal could lose, a paused operation or a file git status lists, or null. */
25
+ export declare function whyUnclean(path: string, locked: boolean): {
26
+ reason: string;
27
+ command: string | null;
28
+ } | null;
18
29
  /** The operation paused in the worktree at `path`: its git dir holds the state git resumes it from. */
19
30
  export declare function pausedOperation(path: string): string | undefined;
31
+ /** The branch git keeps from a delete for the worktree `tree`, and why: checked out there, or a rebase of it paused there. */
32
+ export declare function heldBranch(tree: Tree): {
33
+ branch: string;
34
+ reason: string;
35
+ } | undefined;
20
36
  /** The dev's way off `branch`, checked out in the main checkout at `root`: null until it is Landed and nothing there could be lost. */
21
37
  export declare function switchOff(root: string, base: {
22
38
  name: string;
@@ -1,20 +1,27 @@
1
1
  // What a Sweep reads of a repository's branches and worktrees: where each worktree lies, what it has checked
2
- // out, its lock, the files that hold it and the operation paused in it, and the way off the main checkout's.
3
- import { existsSync } from "node:fs";
2
+ // out, its lock, the files that hold it and the operation paused in it, and the way off the main checkout's;
3
+ // beside them, the commands the dev runs for what the Sweep keeps.
4
+ import { existsSync, readFileSync } from "node:fs";
4
5
  import { join } from "node:path";
5
- import { exec } from "./exec.js";
6
- import { repositoryFreeEnv } from "./git-env.js";
6
+ import { asked, git, must } from "./git.js";
7
7
  import { isLanded } from "./landed.js";
8
- import { shellLine } from "./pr/decide.js";
9
- import { CannotRun } from "./pr/read.js";
10
- import { asked, git } from "./working-tree.js";
11
- /** `git <args>` at `root`, or a stop naming its problem. */
12
- export function must(root, args) {
13
- const result = git(root, args);
14
- if ("error" in result) {
15
- throw new CannotRun(result.error, "check the repository with git status");
16
- }
17
- return result.out;
8
+ import { pausedIn } from "./paused-operation.js";
9
+ import { shellLine } from "./shell-line.js";
10
+ /** The forced delete of `branch`, the dev's to run. */
11
+ export function forcedDelete(branch) {
12
+ return shellLine(["git", "branch", "-D", branch]);
13
+ }
14
+ /** The forced removal of the worktree at `path`, the dev's to run; git wants --force twice for one still locked. */
15
+ export function forcedRemoval(path, locked = false) {
16
+ return shellLine(["git", "worktree", "remove", "--force", ...(locked ? ["--force"] : []), path]);
17
+ }
18
+ /** The unlock of the worktree at `path`, the dev's to run. */
19
+ export function unlockCommand(path) {
20
+ return shellLine(["git", "worktree", "unlock", path]);
21
+ }
22
+ /** The commit the local `branch` points at, or undefined once it is gone. */
23
+ export function tipOf(root, branch) {
24
+ return asked(root, ["rev-parse", "--verify", "-q", `refs/heads/${branch}^{commit}`]);
18
25
  }
19
26
  /** The local branches of the repository at `root`. */
20
27
  export function branchesOf(root) {
@@ -22,11 +29,11 @@ export function branchesOf(root) {
22
29
  .split("\n")
23
30
  .filter((name) => name !== "");
24
31
  }
25
- /** Every worktree of the repository, the main checkout first. */
32
+ /** Every worktree of the repository, the main checkout first; -z leaves a path or a lock's reason unquoted. */
26
33
  export function worktreesOf(root) {
27
34
  const trees = [];
28
35
  let tree;
29
- for (const line of must(root, ["worktree", "list", "--porcelain"]).split("\n")) {
36
+ for (const line of must(root, ["worktree", "list", "--porcelain", "-z"]).split("\0")) {
30
37
  if (line.startsWith("worktree ")) {
31
38
  tree = { path: line.slice("worktree ".length), head: "", branch: undefined, lock: undefined, prunable: false };
32
39
  trees.push(tree);
@@ -48,14 +55,12 @@ export function worktreesOf(root) {
48
55
  }
49
56
  /** The files `git status` lists in the worktree at `path`, ignored ones aside, or why it cannot be read. */
50
57
  export function dirtyFiles(path) {
51
- const args = ["-C", path, "status", "--porcelain=v1", "-z", "--untracked-files=all"];
52
- const result = exec("git", args, { env: repositoryFreeEnv() });
53
- if (result.status !== 0) {
54
- const said = result.stderr.split("\n").find((line) => line.trim() !== "");
55
- return { error: `git status: ${said?.trim() ?? `exited ${result.status ?? "on a signal"}`}` };
58
+ const status = git(path, ["status", "--porcelain=v1", "-z", "--untracked-files=all"], { raw: true });
59
+ if ("error" in status) {
60
+ return status;
56
61
  }
57
62
  const files = [];
58
- const entries = result.stdout.split("\0").filter((entry) => entry !== "");
63
+ const entries = status.out.split("\0").filter((entry) => entry !== "");
59
64
  for (let index = 0; index < entries.length; index++) {
60
65
  const entry = entries[index] ?? "";
61
66
  files.push(entry.slice(3));
@@ -65,18 +70,49 @@ export function dirtyFiles(path) {
65
70
  }
66
71
  return files;
67
72
  }
68
- const OPERATIONS = [
69
- ["rebase-merge", "rebase"],
70
- ["rebase-apply", "rebase or am"],
71
- ["MERGE_HEAD", "merge"],
72
- ["CHERRY_PICK_HEAD", "cherry-pick"],
73
- ["REVERT_HEAD", "revert"],
74
- ["BISECT_LOG", "bisect"]
75
- ];
73
+ /** What in the worktree at `path` a removal could lose, a paused operation or a file git status lists, or null. */
74
+ export function whyUnclean(path, locked) {
75
+ const operation = pausedOperation(path);
76
+ if (operation !== undefined) {
77
+ return { reason: `a ${operation} is paused in it`, command: null };
78
+ }
79
+ const files = dirtyFiles(path);
80
+ if ("error" in files) {
81
+ return { reason: files.error, command: null };
82
+ }
83
+ return files.length === 0
84
+ ? null
85
+ : { reason: `modified or untracked: ${files.join(", ")}`, command: forcedRemoval(path, locked) };
86
+ }
76
87
  /** The operation paused in the worktree at `path`: its git dir holds the state git resumes it from. */
77
88
  export function pausedOperation(path) {
78
89
  const dir = asked(path, ["rev-parse", "--absolute-git-dir"]);
79
- return dir === undefined ? undefined : OPERATIONS.find(([file]) => existsSync(join(dir, file)))?.[1];
90
+ return dir === undefined ? undefined : pausedIn(dir);
91
+ }
92
+ /** The branch a rebase paused in the worktree at `path` works on; git lists that worktree as detached. */
93
+ function rebasedBranch(path) {
94
+ const dir = asked(path, ["rev-parse", "--absolute-git-dir"]);
95
+ if (dir === undefined) {
96
+ return undefined;
97
+ }
98
+ for (const state of ["rebase-merge", "rebase-apply"]) {
99
+ const file = join(dir, state, "head-name");
100
+ const name = existsSync(file) ? readFileSync(file, "utf8").trim() : "";
101
+ if (name.startsWith("refs/heads/")) {
102
+ return name.slice("refs/heads/".length);
103
+ }
104
+ }
105
+ return undefined;
106
+ }
107
+ /** The branch git keeps from a delete for the worktree `tree`, and why: checked out there, or a rebase of it paused there. */
108
+ export function heldBranch(tree) {
109
+ if (tree.branch !== undefined) {
110
+ return { branch: tree.branch, reason: `checked out in the worktree at ${tree.path}` };
111
+ }
112
+ const branch = rebasedBranch(tree.path);
113
+ return branch === undefined
114
+ ? undefined
115
+ : { branch, reason: `a rebase paused in the worktree at ${tree.path} holds it` };
80
116
  }
81
117
  /** The dev's way off `branch`, checked out in the main checkout at `root`: null until it is Landed and nothing there could be lost. */
82
118
  export function switchOff(root, base, branch) {
@@ -1,24 +1,68 @@
1
- import { type JsonObject } from "../json-text.ts";
2
- /** What stops a `worker` command before it acts: exit 2, the problem and, when there is one, its remedy. */
3
- export declare class Refusal extends Error {
4
- remedy: string | undefined;
5
- constructor(message: string, remedy?: string);
6
- }
7
- export type Called = {
8
- status: number;
9
- stdout: string;
10
- stderr: string;
1
+ import { type Called } from "../exec.ts";
2
+ /** A terminal as `hc ls --json` lists it. */
3
+ export type Listed = {
4
+ address: string;
5
+ /** Its name, else its address. */
6
+ name: string;
7
+ /** The terminal that started it. */
8
+ parent: string | undefined;
9
+ /** herdr's state: live or finished. */
10
+ state: string;
11
+ /** The word it reports, undefined before it reports one. */
12
+ status: string | undefined;
13
+ /** The folder it was launched in. */
14
+ folder: string;
15
+ /** The place herdr shows it at: its space and tab. */
16
+ place: string | undefined;
17
+ };
18
+ /** A terminal as `hc get --json` reports it. */
19
+ export type Reading = {
20
+ /** herdr's state: live or finished. */
21
+ state: string | undefined;
22
+ /** The state its program reports. */
23
+ status: string | undefined;
24
+ /** The herdr space it sits in, its `herdr.space` label. */
25
+ space: string | undefined;
26
+ /** Whether herdr sent its first message, and why not when it did not. */
27
+ firstMessage: {
28
+ sent: boolean;
29
+ reason: string | undefined;
30
+ } | undefined;
31
+ /** The folder its foreground program works in. */
32
+ folder: string | undefined;
33
+ /** That program's pid. */
34
+ pid: number | undefined;
35
+ };
36
+ /** `herdr-app args`, the window's CLI; one not on PATH, or past its limit, stops the command. */
37
+ export declare function herdrApp(args: string[]): Called;
38
+ /** The terminals `hc ls` lists: the live ones, every one with `all`, only those labelled `label` when given. */
39
+ export declare function listTerminals(filter?: {
40
+ all?: boolean;
41
+ label?: string;
42
+ }): Listed[];
43
+ /** What `hc get` reports of the terminal at `address`, or `gone`, in hc's words, when herdr has no such terminal. */
44
+ export declare function readTerminal(address: string): Reading | {
45
+ gone: string;
46
+ };
47
+ export type Start = {
48
+ name: string;
49
+ /** Its labels, each passed as `--label key=value` in order. */
50
+ labels: Record<string, string>;
51
+ /** The folder it starts in. */
52
+ cwd: string;
53
+ /** The program and its arguments. */
54
+ argv: string[];
55
+ };
56
+ /** Runs `argv` in a terminal of its own in the background, outliving its caller; the address hc gives it. */
57
+ export declare function startTerminal({ name, labels, cwd, argv }: Start): string;
58
+ /** Stops the terminal at `address`: hc's words when it refuses, undefined once it is stopped or had finished. */
59
+ export declare function stopTerminal(address: string): string | undefined;
60
+ /** Removes the terminal at `address`: hc's words when it refuses, undefined once it is gone. */
61
+ export declare function removeTerminal(address: string): string | undefined;
62
+ /** Types `message` into the terminal at `address`: `sent`, `blocked` while it sits on a dialog, else hc's words. */
63
+ export declare function sendTo(address: string, message: string): "sent" | "blocked" | {
64
+ refused: string;
11
65
  };
12
- /** The first line of a tool's output that says anything, or the exit it ended with. */
13
- export declare function said(called: Called): string;
14
- /** `tool args` as a child process; a tool that is not on PATH stops the command. */
15
- export declare function call(tool: "hc" | "herdr-app", args: string[]): Called;
16
- /** The JSON object `hc <args>` prints; a failing call or a reply that is no object stops the command. */
17
- export declare function hcObject(args: string[]): JsonObject;
18
- /** What an error says, whatever was thrown. */
19
- export declare function describe(error: unknown): string;
20
- /** Reports a `Refusal` of `command` on stderr and answers exit 2; any other error is a bug and goes on up. */
21
- export declare function refuse(command: string, error: unknown): number;
22
66
  /** The option `flag` as a whole number from `min`, `fallback` when the command line leaves it out. */
23
67
  export declare function wholeNumber(flag: string, value: string | undefined, fallback: number, min: number): number | {
24
68
  error: string;
@@ -38,11 +82,11 @@ export type Worker = {
38
82
  /** Whether herdr still runs the Worker's terminal; the one test of it every command shares. */
39
83
  export declare function isLive(worker: Worker): boolean;
40
84
  /**
41
- * The Workers with the folder each live one works in, its worktree, which `hc get` reports as
42
- * `foreground.cwd` and `hc ls` leaves out. A Worker herdr cannot tell it for keeps its launch folder.
85
+ * The Workers with the folder each live one works in, its worktree, which `hc get` reports and `hc ls`
86
+ * leaves out. A Worker herdr cannot tell it for keeps its launch folder.
43
87
  */
44
88
  export declare function workingFolders(workers: Worker[]): Worker[];
45
- /** The Workers `lead` started, in the order herdr lists them; `all` adds the finished ones. */
89
+ /** The Workers `leadId` started, in the order herdr lists them; `all` adds the finished ones. */
46
90
  export declare function workersOf(leadId: string, all: boolean): Worker[];
47
91
  /** A table of rows, every column but the last padded to its widest cell and two spaces apart. */
48
92
  export declare function table(rows: string[][]): string;