toolroll 0.9.59 → 0.9.61

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 (152) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +8 -6
  3. package/dist/agent-board.d.ts +208 -0
  4. package/dist/agent-board.js +454 -0
  5. package/dist/agent-fence.d.ts +19 -0
  6. package/dist/agent-fence.js +100 -8
  7. package/dist/agent-pulse.d.ts +43 -0
  8. package/dist/agent-pulse.js +214 -0
  9. package/dist/agent-turns.d.ts +52 -0
  10. package/dist/agent-turns.js +137 -0
  11. package/dist/agents-board.d.ts +101 -0
  12. package/dist/agents-board.js +225 -0
  13. package/dist/assignment-adapters.d.ts +2 -1
  14. package/dist/assignment-adapters.js +1 -1
  15. package/dist/assignment-delivery.d.ts +16 -0
  16. package/dist/assignment-delivery.js +54 -28
  17. package/dist/assignment-status.d.ts +14 -5
  18. package/dist/assignment-status.js +70 -39
  19. package/dist/assignment.d.ts +10 -2
  20. package/dist/assignment.js +21 -14
  21. package/dist/bin.js +11 -2
  22. package/dist/brief-fence.d.ts +9 -0
  23. package/dist/brief-fence.js +22 -0
  24. package/dist/browser/workspace.css +2 -2
  25. package/dist/browser/workspace.js +29 -25
  26. package/dist/browser-crew.js +2 -0
  27. package/dist/browser-workspace.d.ts +79 -1
  28. package/dist/browser-workspace.js +1 -0
  29. package/dist/builder.d.ts +8 -0
  30. package/dist/builder.js +178 -29
  31. package/dist/chat-channel.js +10 -1
  32. package/dist/chat-core.d.ts +6 -0
  33. package/dist/chat-core.js +8 -0
  34. package/dist/chat-delivery.js +22 -0
  35. package/dist/chat-messages.js +8 -1
  36. package/dist/chat-quiet.js +1 -0
  37. package/dist/chat-voice.d.ts +2 -0
  38. package/dist/chat-voice.js +2 -0
  39. package/dist/checkout-cleanup.d.ts +3 -0
  40. package/dist/checkout-cleanup.js +11 -2
  41. package/dist/cli-fast.d.ts +8 -0
  42. package/dist/cli-fast.js +18 -0
  43. package/dist/cli-help.d.ts +5 -0
  44. package/dist/cli-help.js +91 -0
  45. package/dist/cli.d.ts +6 -5
  46. package/dist/cli.js +17 -96
  47. package/dist/contracts/console-api.d.ts +3 -1
  48. package/dist/contracts/console-api.js +5 -1
  49. package/dist/contracts/lead-tools.d.ts +16 -2
  50. package/dist/contracts/lead-tools.js +5 -1
  51. package/dist/design/tokens.css +234 -0
  52. package/dist/desktop-update.d.ts +1 -1
  53. package/dist/discord.js +2 -1
  54. package/dist/evidence-pack.js +1 -1
  55. package/dist/evidence.d.ts +28 -0
  56. package/dist/evidence.js +87 -17
  57. package/dist/exec.d.ts +32 -0
  58. package/dist/exec.js +229 -10
  59. package/dist/flow-actions.js +2 -1
  60. package/dist/guides.js +14 -11
  61. package/dist/invoke.js +110 -18
  62. package/dist/landed-cleanup.d.ts +157 -0
  63. package/dist/landed-cleanup.js +707 -0
  64. package/dist/lead-cli.js +22 -19
  65. package/dist/lead-contract.js +1 -1
  66. package/dist/lead-doors.js +49 -2
  67. package/dist/lead-follow.d.ts +2 -0
  68. package/dist/lead-follow.js +138 -88
  69. package/dist/lead-tools.js +48 -8
  70. package/dist/live-input.d.ts +68 -0
  71. package/dist/live-input.js +254 -0
  72. package/dist/live.d.ts +1 -1
  73. package/dist/live.js +4 -2
  74. package/dist/mailbox.js +3 -2
  75. package/dist/maintenance.d.ts +2 -0
  76. package/dist/maintenance.js +5 -2
  77. package/dist/mcp-core.js +1 -1
  78. package/dist/operate-remote-arguments.js +5 -1
  79. package/dist/operate-remote.js +1 -1
  80. package/dist/operate.d.ts +1 -1
  81. package/dist/operate.js +201 -19
  82. package/dist/orphan-sweep.d.ts +2 -0
  83. package/dist/orphan-sweep.js +5 -3
  84. package/dist/project-delete-ui.d.ts +11 -0
  85. package/dist/project-delete-ui.js +29 -2
  86. package/dist/project-tools.d.ts +7 -11
  87. package/dist/project-tools.js +13 -37
  88. package/dist/provider.d.ts +73 -8
  89. package/dist/provider.js +87 -17
  90. package/dist/result-completion.d.ts +7 -0
  91. package/dist/result-completion.js +9 -0
  92. package/dist/runner.d.ts +4 -2
  93. package/dist/runner.js +3 -3
  94. package/dist/serve.js +22 -3
  95. package/dist/server/chrome.d.ts +6 -3
  96. package/dist/server/chrome.js +8 -4
  97. package/dist/server/console.css +9 -64
  98. package/dist/server/live.d.ts +3 -0
  99. package/dist/server/live.js +35 -2
  100. package/dist/server/pages.js +59 -35
  101. package/dist/server/render-chat.js +14 -2
  102. package/dist/server/render-tasks.d.ts +14 -2
  103. package/dist/server/render-tasks.js +38 -20
  104. package/dist/server/route-table.js +4 -1
  105. package/dist/server/settings.js +2 -1
  106. package/dist/server/task-views.js +10 -0
  107. package/dist/server/tasks.js +63 -0
  108. package/dist/shared-deps.d.ts +9 -4
  109. package/dist/shared-deps.js +89 -13
  110. package/dist/slack-socket.d.ts +12 -0
  111. package/dist/slack-socket.js +29 -0
  112. package/dist/slack.d.ts +0 -7
  113. package/dist/slack.js +2 -24
  114. package/dist/staged-runtimes.d.ts +16 -4
  115. package/dist/staged-runtimes.js +160 -38
  116. package/dist/steer-live.d.ts +19 -0
  117. package/dist/steer-live.js +93 -0
  118. package/dist/steer-mailbox.d.ts +39 -0
  119. package/dist/steer-mailbox.js +207 -0
  120. package/dist/steer-status.d.ts +25 -0
  121. package/dist/steer-status.js +44 -0
  122. package/dist/storage-sweep-child.d.ts +1 -0
  123. package/dist/storage-sweep-child.js +76 -0
  124. package/dist/storage-sweep-process.d.ts +41 -0
  125. package/dist/storage-sweep-process.js +187 -0
  126. package/dist/storage-sweep.d.ts +30 -6
  127. package/dist/storage-sweep.js +214 -28
  128. package/dist/storage-ui.d.ts +2 -0
  129. package/dist/storage-ui.js +16 -4
  130. package/dist/storage.d.ts +6 -0
  131. package/dist/storage.js +35 -0
  132. package/dist/store.d.ts +202 -2
  133. package/dist/store.js +440 -13
  134. package/dist/surface.js +5 -3
  135. package/dist/sweep-gate.d.ts +10 -0
  136. package/dist/sweep-gate.js +18 -0
  137. package/dist/sweep-quarantine.d.ts +28 -0
  138. package/dist/sweep-quarantine.js +153 -0
  139. package/dist/task-status.d.ts +2 -0
  140. package/dist/task-status.js +5 -1
  141. package/dist/team-updates.js +3 -3
  142. package/dist/test-temp.d.ts +6 -3
  143. package/dist/test-temp.js +15 -9
  144. package/dist/toolroll-update.js +15 -2
  145. package/dist/work-index.d.ts +1 -0
  146. package/dist/work-index.js +32 -12
  147. package/dist/work-summary.d.ts +2 -2
  148. package/dist/workspace-revision.js +3 -1
  149. package/dist/worktree.d.ts +3 -1
  150. package/dist/worktree.js +93 -10
  151. package/package.json +21 -21
  152. package/scripts/provider-canary.mjs +235 -147
@@ -64,6 +64,8 @@ export declare function finishedCheckouts(store: Store, now: Date, repo?: string
64
64
  taskId: string | null;
65
65
  finishedAt: string;
66
66
  }[];
67
+ /** Why a checkout stays as a clean-up by hand reads it (in use, or its task not done with it), before its files; null: it may go. */
68
+ export declare function checkoutKeepWhy(pool: WorktreePool, row: WorktreeRow, statuses: ReturnType<typeof taskStatuses>, now: Date): KeepWhy | null;
67
69
  /**
68
70
  * What a clean-up would remove now, how much space it would free, and what stays and why. `manual` is a clean-up by
69
71
  * hand (it doesn't wait for the setting); otherwise it is the worker's pass under the setting. Reads only.
@@ -92,6 +94,7 @@ export declare function cleanCheckouts(store: Store, pool: WorktreePool, now: ()
92
94
  actor: string;
93
95
  repo?: string;
94
96
  only?: ReadonlySet<string>;
97
+ spare?: (path: string) => boolean;
95
98
  }): Promise<CleanResult>;
96
99
  export type SlimResult = {
97
100
  slimmed: {
@@ -242,6 +242,10 @@ function keptFor(pool, row, statuses, now, cleanup) {
242
242
  const dueAt = new Date(Date.parse(since) + days * DAY_MS).toISOString();
243
243
  return dueAt > now.toISOString() ? { why: "not due yet", taskId, dueAt } : { why: null, taskId };
244
244
  }
245
+ /** Why a checkout stays as a clean-up by hand reads it (in use, or its task not done with it), before its files; null: it may go. */
246
+ export function checkoutKeepWhy(pool, row, statuses, now) {
247
+ return keptFor(pool, row, statuses, now, "manual").why;
248
+ }
245
249
  async function eachAtMost(items, limit, work) {
246
250
  const out = new Array(items.length);
247
251
  let next = 0;
@@ -298,11 +302,16 @@ export async function cleanCheckouts(store, pool, now, options) {
298
302
  const sizes = new Map();
299
303
  const wanted = (row, fresh) => {
300
304
  // Right before a checkout goes, where its task stands is read afresh, and its size taken for the ledger.
305
+ // `spare`: live work uses it (storage-sweep.ts), asked at both looks.
301
306
  if (fresh)
302
307
  statuses = taskStatuses(store, now());
303
- const go = keptFor(pool, row, statuses, now(), options.manual ? "manual" : cleanup).why === null;
304
- if (go && fresh)
308
+ const go = keptFor(pool, row, statuses, now(), options.manual ? "manual" : cleanup).why === null && options.spare?.(row.path) !== true;
309
+ if (go && fresh) {
305
310
  sizes.set(row.path, diskBytes([row.path]).get(row.path) ?? 0);
311
+ // du can be slow. The reservation excludes a concurrent checkout lease, and this last read also catches
312
+ // an open run/claim or task change that arrived while size was measured in the sweep child.
313
+ return keptFor(pool, row, taskStatuses(store, now()), now(), options.manual ? "manual" : cleanup).why === null && options.spare?.(row.path) !== true;
314
+ }
306
315
  return go;
307
316
  };
308
317
  const pruned = await pool.prune(rows, wanted, options.manual ? Infinity : AUTO_MAX);
@@ -0,0 +1,8 @@
1
+ /**
2
+ * The answers that need nothing but this file: `--version` and the top-level
3
+ * usage. The bin asks here before importing the CLI, so neither loads the
4
+ * store, the contracts or a single command family. main() asks here too, so
5
+ * both roads print the same bytes.
6
+ */
7
+ /** The line to print and exit 0 with, or null when the CLI has to run. */
8
+ export declare function quickAnswer(argv: readonly string[]): string | null;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * The answers that need nothing but this file: `--version` and the top-level
3
+ * usage. The bin asks here before importing the CLI, so neither loads the
4
+ * store, the contracts or a single command family. main() asks here too, so
5
+ * both roads print the same bytes.
6
+ */
7
+ import { HELP } from "./cli-help.js";
8
+ import { PACKAGE_VERSION } from "./version.js";
9
+ /** Exactly the invocations whose whole answer is top-level usage — anything more goes through the CLI. */
10
+ const HELP_INVOCATIONS = new Set(["--help", "-h", "help"]);
11
+ /** The line to print and exit 0 with, or null when the CLI has to run. */
12
+ export function quickAnswer(argv) {
13
+ if (argv[0] === "--version" || argv[0] === "-v")
14
+ return PACKAGE_VERSION;
15
+ if (argv.length === 1 && HELP_INVOCATIONS.has(argv[0]))
16
+ return HELP;
17
+ return null;
18
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * The top-level usage text, on its own so `toolroll --help` can print it
3
+ * without loading every command family (see cli-fast.ts).
4
+ */
5
+ export declare const HELP = "toolroll \u2014 a control plane for unattended coding agents\n\nUsage\n toolroll [path...] report what is in flight\n toolroll pulls report what is waiting on a person\n toolroll graph report which work graph is already here\n toolroll repos list connected repositories, and how to adjust\n toolroll repos add <path> add a project; it is in the console at once (no path: the repo you are in)\n toolroll repos remove <path>\n take it out of the console; its tasks and results stay\n toolroll repos add-from-github <owner/name> --root <dir>\n preview, then clone and connect (--yes)\n toolroll link put `toolroll` on your PATH\n toolroll unlink take it off again\n toolroll update update to the latest release: verified, drained, undoable\n (preview first; --yes, --now, --at HH:MM, --rollback)\n toolroll release <branch> release Toolroll itself: gate, your approval, deploy, merge, tag, publish\n (resumable: rerun to continue; --help)\n toolroll contract the machine contract: envelope version + capabilities\n (--commands dumps the declared command guide)\n toolroll skills install --claude-code [--dir <path>]\n install this binary's guides for Claude Code (preview first)\n toolroll skills install teach a repo's agents this queue exists (preview first)\n toolroll skills list the guides this exact binary serves\n toolroll skills get <name> print one guide (version-matched, never stale)\n toolroll demo a seeded throwaway sandbox \u2014 see it working in 90 seconds\n toolroll up app + builder for every saved project \u2014 the normal start\n toolroll onboard make your agent Toolroll's lead: add this repo, install its skill (--yes), offer starter flows (--starter)\n toolroll status running, queued, ready results, release check and plan windows\n toolroll health recent server latency, write waits and live streams\n toolroll integrations which integrations work, and what to do about the ones that don't (--json)\n toolroll lead token mint your lead agent's credential: its own work pings nobody\n toolroll lead say \"<text>\" (the lead) one short message in its person's chat\n\nOperating the queue \u2014 `toolroll task` prints the whole surface,\nand any queue command + --help prints it too\n toolroll approver add <name>\n mint the credential that lets a person say yes\n toolroll ready what could be dispatched right now\n toolroll check-progress <run> current or final approved check progress\n toolroll task wait <id> [--timeout <seconds>]\n wait for this attempt to finish or need a person\n toolroll task add <title> queue work\n toolroll task scope <id> --goal <text>\n state what success is; approval binds to it\n toolroll task approve <id>\n the yes \u2014 nothing builds without one\n toolroll claim <id> --runner <name>\n toolroll heartbeat <lease> / worker release <lease> / reap\n toolroll tick --runner <name> --token <t> --repo <path>\n one unattended pass over the ready set\n toolroll flows list | show <id> | create | edit | trigger | script | card | archive\n set up and inspect flows (writes preview until --yes)\n toolroll chat --as <you> --token <t>\n talk to your lead: one conversation across every\n project; it proposes, you confirm (--say \"\u2026\" for one turn)\n toolroll serve --repo <path> advanced: run only the console\n (--editor vscode with --runner: file links\n open in VS Code on the device you browse from)\n toolroll watch / daemon install advanced: run only the builder\n toolroll reconcile recover what the last stretch left behind\n\nWith nothing connected it reports everything it can find below the working\ndirectory. Once you connect repositories it reports those instead.\n\nOptions\n --all report everything discoverable, ignoring connected repos\n --depth <n> directory levels to descend (default: 6)\n --hidden include dot-directories, which are skipped by default\n --dirty also read each working tree for uncommitted files\n --local branches only: no network, no pull requests, no issues\n --json emit a machine-readable envelope instead of a report\n -o <file> also write that envelope to a file (requires --json)\n -h, --help show this\n\nNothing is written, installed, or configured. Every repository is read\nthrough git with --no-optional-locks, so a scan never contends for the\nindex lock with an editor you have open.\n\n--dirty is off by default because reading a working tree is unbounded\nwork: on a large or cloud-synced repo `git status` can take minutes,\nwhere listing branches takes milliseconds.\n\nlink shows what it would do and changes nothing; add --yes to apply it,\nor --to <dir> to choose where. It never uses sudo, never leaves your\nhome directory, and never touches a file it did not create.";
@@ -0,0 +1,91 @@
1
+ /**
2
+ * The top-level usage text, on its own so `toolroll --help` can print it
3
+ * without loading every command family (see cli-fast.ts).
4
+ */
5
+ import { DEFAULT_MAX_DEPTH } from "./scan.js";
6
+ export const HELP = `toolroll — a control plane for unattended coding agents
7
+
8
+ Usage
9
+ toolroll [path...] report what is in flight
10
+ toolroll pulls report what is waiting on a person
11
+ toolroll graph report which work graph is already here
12
+ toolroll repos list connected repositories, and how to adjust
13
+ toolroll repos add <path> add a project; it is in the console at once (no path: the repo you are in)
14
+ toolroll repos remove <path>
15
+ take it out of the console; its tasks and results stay
16
+ toolroll repos add-from-github <owner/name> --root <dir>
17
+ preview, then clone and connect (--yes)
18
+ toolroll link put \`toolroll\` on your PATH
19
+ toolroll unlink take it off again
20
+ toolroll update update to the latest release: verified, drained, undoable
21
+ (preview first; --yes, --now, --at HH:MM, --rollback)
22
+ toolroll release <branch> release Toolroll itself: gate, your approval, deploy, merge, tag, publish
23
+ (resumable: rerun to continue; --help)
24
+ toolroll contract the machine contract: envelope version + capabilities
25
+ (--commands dumps the declared command guide)
26
+ toolroll skills install --claude-code [--dir <path>]
27
+ install this binary's guides for Claude Code (preview first)
28
+ toolroll skills install teach a repo's agents this queue exists (preview first)
29
+ toolroll skills list the guides this exact binary serves
30
+ toolroll skills get <name> print one guide (version-matched, never stale)
31
+ toolroll demo a seeded throwaway sandbox — see it working in 90 seconds
32
+ toolroll up app + builder for every saved project — the normal start
33
+ toolroll onboard make your agent Toolroll's lead: add this repo, install its skill (--yes), offer starter flows (--starter)
34
+ toolroll status running, queued, ready results, release check and plan windows
35
+ toolroll health recent server latency, write waits and live streams
36
+ toolroll integrations which integrations work, and what to do about the ones that don't (--json)
37
+ toolroll lead token mint your lead agent's credential: its own work pings nobody
38
+ toolroll lead say "<text>" (the lead) one short message in its person's chat
39
+
40
+ Operating the queue — \`toolroll task\` prints the whole surface,
41
+ and any queue command + --help prints it too
42
+ toolroll approver add <name>
43
+ mint the credential that lets a person say yes
44
+ toolroll ready what could be dispatched right now
45
+ toolroll check-progress <run> current or final approved check progress
46
+ toolroll task wait <id> [--timeout <seconds>]
47
+ wait for this attempt to finish or need a person
48
+ toolroll task add <title> queue work
49
+ toolroll task scope <id> --goal <text>
50
+ state what success is; approval binds to it
51
+ toolroll task approve <id>
52
+ the yes — nothing builds without one
53
+ toolroll claim <id> --runner <name>
54
+ toolroll heartbeat <lease> / worker release <lease> / reap
55
+ toolroll tick --runner <name> --token <t> --repo <path>
56
+ one unattended pass over the ready set
57
+ toolroll flows list | show <id> | create | edit | trigger | script | card | archive
58
+ set up and inspect flows (writes preview until --yes)
59
+ toolroll chat --as <you> --token <t>
60
+ talk to your lead: one conversation across every
61
+ project; it proposes, you confirm (--say "…" for one turn)
62
+ toolroll serve --repo <path> advanced: run only the console
63
+ (--editor vscode with --runner: file links
64
+ open in VS Code on the device you browse from)
65
+ toolroll watch / daemon install advanced: run only the builder
66
+ toolroll reconcile recover what the last stretch left behind
67
+
68
+ With nothing connected it reports everything it can find below the working
69
+ directory. Once you connect repositories it reports those instead.
70
+
71
+ Options
72
+ --all report everything discoverable, ignoring connected repos
73
+ --depth <n> directory levels to descend (default: ${DEFAULT_MAX_DEPTH})
74
+ --hidden include dot-directories, which are skipped by default
75
+ --dirty also read each working tree for uncommitted files
76
+ --local branches only: no network, no pull requests, no issues
77
+ --json emit a machine-readable envelope instead of a report
78
+ -o <file> also write that envelope to a file (requires --json)
79
+ -h, --help show this
80
+
81
+ Nothing is written, installed, or configured. Every repository is read
82
+ through git with --no-optional-locks, so a scan never contends for the
83
+ index lock with an editor you have open.
84
+
85
+ --dirty is off by default because reading a working tree is unbounded
86
+ work: on a large or cloud-synced repo \`git status\` can take minutes,
87
+ where listing branches takes milliseconds.
88
+
89
+ link shows what it would do and changes nothing; add --yes to apply it,
90
+ or --to <dir> to choose where. It never uses sudo, never leaves your
91
+ home directory, and never touches a file it did not create.`;
package/dist/cli.d.ts CHANGED
@@ -14,9 +14,10 @@ import { previewGithubRepo, cloneGithubRepo } from "./onboard.js";
14
14
  import { type SessionCliOptions } from "./session-cli.js";
15
15
  import { type TeamCliOptions } from "./team-cli.js";
16
16
  import { type RemoteExecOptions } from "./remote-exec.js";
17
- import { type UpdateCliDeps } from "./toolroll-update-cli.js";
18
- import { type ReleaseCliDeps } from "./release-cli.js";
19
- import { type OperateOptions } from "./operate.js";
17
+ import type { UpdateCliDeps } from "./toolroll-update-cli.js";
18
+ import type { ReleaseCliDeps } from "./release-cli.js";
19
+ import type { OperateOptions } from "./operate.js";
20
+ import { HELP } from "./cli-help.js";
20
21
  export type CliOptions = {
21
22
  roots: string[];
22
23
  /** The positional tokens exactly as typed, index-aligned with `roots` —
@@ -41,7 +42,8 @@ export type ParseResult = {
41
42
  error: string;
42
43
  };
43
44
  type Write = (line: string) => void;
44
- export declare const HELP = "toolroll \u2014 a control plane for unattended coding agents\n\nUsage\n toolroll [path...] report what is in flight\n toolroll pulls report what is waiting on a person\n toolroll graph report which work graph is already here\n toolroll repos list connected repositories, and how to adjust\n toolroll repos add <path> add a project; it is in the console at once (no path: the repo you are in)\n toolroll repos remove <path>\n take it out of the console; its tasks and results stay\n toolroll repos add-from-github <owner/name> --root <dir>\n preview, then clone and connect (--yes)\n toolroll link put `toolroll` on your PATH\n toolroll unlink take it off again\n toolroll update update to the latest release: verified, drained, undoable\n (preview first; --yes, --now, --at HH:MM, --rollback)\n toolroll release <branch> release Toolroll itself: gate, your approval, deploy, merge, tag, publish\n (resumable: rerun to continue; --help)\n toolroll contract the machine contract: envelope version + capabilities\n (--commands dumps the declared command guide)\n toolroll skills install --claude-code [--dir <path>]\n install this binary's guides for Claude Code (preview first)\n toolroll skills install teach a repo's agents this queue exists (preview first)\n toolroll skills list the guides this exact binary serves\n toolroll skills get <name> print one guide (version-matched, never stale)\n toolroll demo a seeded throwaway sandbox \u2014 see it working in 90 seconds\n toolroll up app + builder for every saved project \u2014 the normal start\n toolroll onboard make your agent Toolroll's lead: add this repo, install its skill (--yes), offer starter flows (--starter)\n toolroll status running, queued, ready results, release check and plan windows\n toolroll health recent server latency, write waits and live streams\n toolroll integrations which integrations work, and what to do about the ones that don't (--json)\n toolroll lead token mint your lead agent's credential: its own work pings nobody\n toolroll lead say \"<text>\" (the lead) one short message in its person's chat\n\nOperating the queue \u2014 `toolroll task` prints the whole surface,\nand any queue command + --help prints it too\n toolroll approver add <name>\n mint the credential that lets a person say yes\n toolroll ready what could be dispatched right now\n toolroll check-progress <run> current or final approved check progress\n toolroll task wait <id> [--timeout <seconds>]\n wait for this attempt to finish or need a person\n toolroll task add <title> queue work\n toolroll task scope <id> --goal <text>\n state what success is; approval binds to it\n toolroll task approve <id>\n the yes \u2014 nothing builds without one\n toolroll claim <id> --runner <name>\n toolroll heartbeat <lease> / worker release <lease> / reap\n toolroll tick --runner <name> --token <t> --repo <path>\n one unattended pass over the ready set\n toolroll flows list | show <id> | create | edit | trigger | script | card | archive\n set up and inspect flows (writes preview until --yes)\n toolroll chat --as <you> --token <t>\n talk to your lead: one conversation across every\n project; it proposes, you confirm (--say \"\u2026\" for one turn)\n toolroll serve --repo <path> advanced: run only the console\n (--editor vscode with --runner: file links\n open in VS Code on the device you browse from)\n toolroll watch / daemon install advanced: run only the builder\n toolroll reconcile recover what the last stretch left behind\n\nWith nothing connected it reports everything it can find below the working\ndirectory. Once you connect repositories it reports those instead.\n\nOptions\n --all report everything discoverable, ignoring connected repos\n --depth <n> directory levels to descend (default: 6)\n --hidden include dot-directories, which are skipped by default\n --dirty also read each working tree for uncommitted files\n --local branches only: no network, no pull requests, no issues\n --json emit a machine-readable envelope instead of a report\n -o <file> also write that envelope to a file (requires --json)\n -h, --help show this\n\nNothing is written, installed, or configured. Every repository is read\nthrough git with --no-optional-locks, so a scan never contends for the\nindex lock with an editor you have open.\n\n--dirty is off by default because reading a working tree is unbounded\nwork: on a large or cloud-synced repo `git status` can take minutes,\nwhere listing branches takes milliseconds.\n\nlink shows what it would do and changes nothing; add --yes to apply it,\nor --to <dir> to choose where. It never uses sudo, never leaves your\nhome directory, and never touches a file it did not create.";
45
+ /** The top-level usage text (cli-help.ts), re-exported where it always lived. */
46
+ export { HELP };
45
47
  export declare function parseArgs(argv: readonly string[]): ParseResult;
46
48
  /** The commands that operate the queue rather than report on the world. */
47
49
  /** Every verb the CLI routes OUTSIDE runOperate. Exported (with
@@ -143,4 +145,3 @@ export declare const CONTRACT_FLAGS: readonly ["json", "commands"];
143
145
  export declare function isDirectInvocation(moduleUrl: string, entry: string | undefined, toRealPath?: (path: string) => string): boolean;
144
146
  /** The process entry the bin calls: the SQLite notice hushed, the exit code set. */
145
147
  export declare function runCliProcess(argv: readonly string[]): Promise<void>;
146
- export {};
package/dist/cli.js CHANGED
@@ -29,106 +29,23 @@ import { runSessionCommand, SESSION_CLI_ACTIONS } from "./session-cli.js";
29
29
  import { maybeRunTeamCommand, TEAM_CLI_ACTIONS } from "./team-cli.js";
30
30
  import { deprecationWarning } from "./deprecations.js";
31
31
  import { maybeRunRemoteCommand } from "./remote-exec.js";
32
- import { runHealthCommand } from "./health-cli.js";
33
32
  import { discover, inspectAll } from "./discover.js";
34
33
  import { readPulls } from "./pulls.js";
35
34
  import { detectGraphs, chooseBackend, setupOptions, LABELS as GRAPH_LABELS, } from "./graph.js";
36
35
  import { openStore, openStoreReadOnly, databasePath } from "./store.js";
37
- import { PACKAGE_VERSION } from "./version.js";
38
- import { runUpdateCommand } from "./toolroll-update-cli.js";
39
- import { runReleaseCommand } from "./release-cli.js";
40
- import { runOperate, OPERATE_HELP } from "./operate.js";
41
36
  import { renderReport, renderPulls, renderGraph } from "./render.js";
42
- import { readRemote } from "./remote.js";
43
37
  import { DEFAULT_MAX_DEPTH } from "./scan.js";
38
+ import { HELP } from "./cli-help.js";
39
+ import { quickAnswer } from "./cli-fast.js";
44
40
  import { BIN_NAME, chooseTarget, planLink, applyLink, planUnlink, applyUnlink, } from "./link.js";
45
41
  const USAGE_EXIT = 2;
46
- export const HELP = `toolroll — a control plane for unattended coding agents
47
-
48
- Usage
49
- toolroll [path...] report what is in flight
50
- toolroll pulls report what is waiting on a person
51
- toolroll graph report which work graph is already here
52
- toolroll repos list connected repositories, and how to adjust
53
- toolroll repos add <path> add a project; it is in the console at once (no path: the repo you are in)
54
- toolroll repos remove <path>
55
- take it out of the console; its tasks and results stay
56
- toolroll repos add-from-github <owner/name> --root <dir>
57
- preview, then clone and connect (--yes)
58
- toolroll link put \`toolroll\` on your PATH
59
- toolroll unlink take it off again
60
- toolroll update update to the latest release: verified, drained, undoable
61
- (preview first; --yes, --now, --at HH:MM, --rollback)
62
- toolroll release <branch> release Toolroll itself: gate, your approval, deploy, merge, tag, publish
63
- (resumable: rerun to continue; --help)
64
- toolroll contract the machine contract: envelope version + capabilities
65
- (--commands dumps the declared command guide)
66
- toolroll skills install --claude-code [--dir <path>]
67
- install this binary's guides for Claude Code (preview first)
68
- toolroll skills install teach a repo's agents this queue exists (preview first)
69
- toolroll skills list the guides this exact binary serves
70
- toolroll skills get <name> print one guide (version-matched, never stale)
71
- toolroll demo a seeded throwaway sandbox — see it working in 90 seconds
72
- toolroll up app + builder for every saved project — the normal start
73
- toolroll onboard make your agent Toolroll's lead: add this repo, install its skill (--yes), offer starter flows (--starter)
74
- toolroll status running, queued, ready results, release check and plan windows
75
- toolroll health recent server latency, write waits and live streams
76
- toolroll integrations which integrations work, and what to do about the ones that don't (--json)
77
- toolroll lead token mint your lead agent's credential: its own work pings nobody
78
- toolroll lead say "<text>" (the lead) one short message in its person's chat
79
-
80
- Operating the queue — \`toolroll task\` prints the whole surface,
81
- and any queue command + --help prints it too
82
- toolroll approver add <name>
83
- mint the credential that lets a person say yes
84
- toolroll ready what could be dispatched right now
85
- toolroll check-progress <run> current or final approved check progress
86
- toolroll task wait <id> [--timeout <seconds>]
87
- wait for this attempt to finish or need a person
88
- toolroll task add <title> queue work
89
- toolroll task scope <id> --goal <text>
90
- state what success is; approval binds to it
91
- toolroll task approve <id>
92
- the yes — nothing builds without one
93
- toolroll claim <id> --runner <name>
94
- toolroll heartbeat <lease> / worker release <lease> / reap
95
- toolroll tick --runner <name> --token <t> --repo <path>
96
- one unattended pass over the ready set
97
- toolroll flows list | show <id> | create | edit | trigger | script | card | archive
98
- set up and inspect flows (writes preview until --yes)
99
- toolroll chat --as <you> --token <t>
100
- talk to your lead: one conversation across every
101
- project; it proposes, you confirm (--say "…" for one turn)
102
- toolroll serve --repo <path> advanced: run only the console
103
- (--editor vscode with --runner: file links
104
- open in VS Code on the device you browse from)
105
- toolroll watch / daemon install advanced: run only the builder
106
- toolroll reconcile recover what the last stretch left behind
107
-
108
- With nothing connected it reports everything it can find below the working
109
- directory. Once you connect repositories it reports those instead.
110
-
111
- Options
112
- --all report everything discoverable, ignoring connected repos
113
- --depth <n> directory levels to descend (default: ${DEFAULT_MAX_DEPTH})
114
- --hidden include dot-directories, which are skipped by default
115
- --dirty also read each working tree for uncommitted files
116
- --local branches only: no network, no pull requests, no issues
117
- --json emit a machine-readable envelope instead of a report
118
- -o <file> also write that envelope to a file (requires --json)
119
- -h, --help show this
120
-
121
- Nothing is written, installed, or configured. Every repository is read
122
- through git with --no-optional-locks, so a scan never contends for the
123
- index lock with an editor you have open.
124
-
125
- --dirty is off by default because reading a working tree is unbounded
126
- work: on a large or cloud-synced repo \`git status\` can take minutes,
127
- where listing branches takes milliseconds.
128
-
129
- link shows what it would do and changes nothing; add --yes to apply it,
130
- or --to <dir> to choose where. It never uses sudo, never leaves your
131
- home directory, and never touches a file it did not create.`;
42
+ /** The queue's commands live in operate.ts, the largest module graph here: loaded when one of them runs, not before. */
43
+ async function runOperate(command, argv, write, options) {
44
+ const operate = await import("./operate.js");
45
+ return operate.runOperate(command, argv, write, options);
46
+ }
47
+ /** The top-level usage text (cli-help.ts), re-exported where it always lived. */
48
+ export { HELP };
132
49
  export function parseArgs(argv) {
133
50
  const roots = [];
134
51
  const rawRoots = [];
@@ -290,8 +207,9 @@ export function extractOutputFlag(argv) {
290
207
  return { args, ...(outputFile === undefined ? {} : { outputFile }) };
291
208
  }
292
209
  export async function main(argv, write = line => console.log(line), mainOptions = {}) {
293
- if (argv[0] === "--version" || argv[0] === "-v") {
294
- write(PACKAGE_VERSION);
210
+ const quick = quickAnswer(argv);
211
+ if (quick !== null) {
212
+ write(quick);
295
213
  return 0;
296
214
  }
297
215
  const extracted = extractOutputFlag(argv);
@@ -392,8 +310,9 @@ async function dispatch(argv, write, mainOptions) {
392
310
  if (argv[0] === "routine")
393
311
  return routinesMoved(argv, write);
394
312
  // A saved API token sends commands marked remote to the central server, before anything local opens.
313
+ // Command families below load when their command runs, so one command never pays for the others.
395
314
  if (argv[0] === 'health')
396
- return runHealthCommand(argv.slice(1), write, mainOptions.team ?? mainOptions.remote);
315
+ return (await import("./health-cli.js")).runHealthCommand(argv.slice(1), write, mainOptions.team ?? mainOptions.remote);
397
316
  const central = await maybeRunRemoteCommand(argv, mainOptions.remote ?? mainOptions.team);
398
317
  if (typeof central === "number")
399
318
  return central;
@@ -415,8 +334,9 @@ async function dispatch(argv, write, mainOptions) {
415
334
  return runLinkCommand(first, rest, write, mainOptions.binSource);
416
335
  }
417
336
  if (first === "update")
418
- return runUpdateCommand(rest, write, mainOptions.update);
337
+ return (await import("./toolroll-update-cli.js")).runUpdateCommand(rest, write, mainOptions.update);
419
338
  if (first === "release") {
339
+ const { runReleaseCommand } = await import("./release-cli.js");
420
340
  return runReleaseCommand(rest, write, { releaseLease: lease => runOperate("worker", ["release", ...lease], write, mainOptions.operate ?? {}), ...mainOptions.release });
421
341
  }
422
342
  if (first === "contract")
@@ -500,6 +420,7 @@ async function dispatch(argv, write, mainOptions) {
500
420
  async function maybeReadRemote(repos, options) {
501
421
  if (options.local)
502
422
  return undefined;
423
+ const { readRemote } = await import("./remote.js");
503
424
  return readRemote(repos.map(repo => ({ path: repo.path, remoteUrl: repo.remoteUrl })));
504
425
  }
505
426
  function configFile() {
@@ -156,7 +156,8 @@ export declare const CONSOLE_FORMS: {
156
156
  readonly revise: FormContract<"batch" | "return" | "source">;
157
157
  readonly followUp: FormContract<"level" | "return">;
158
158
  readonly resolveIncident: FormContract<"return">;
159
- readonly taskSteer: FormContract<"note">;
159
+ readonly taskSteer: FormContract<"note" | "return" | "run">;
160
+ readonly taskReply: FormContract<"answer" | "question">;
160
161
  readonly taskHold: FormContract<"reason">;
161
162
  readonly taskBlock: FormContract<"on">;
162
163
  readonly taskRepairDependency: FormContract<"blocker" | "operation" | "replacement">;
@@ -173,6 +174,7 @@ export declare const CONSOLE_FORMS: {
173
174
  readonly taskAcceptProof: FormContract<"note" | "return" | "run">;
174
175
  readonly taskComplete: FormContract<"accept" | "note" | "publish" | "receipt" | "return" | "run">;
175
176
  readonly taskStop: FormContract<"return" | "run">;
177
+ readonly taskPause: FormContract<"mode" | "note" | "request" | "return" | "run">;
176
178
  readonly taskResume: FormContract<"nonce" | "return" | "run" | "token">;
177
179
  };
178
180
  export type ConsoleFormName = keyof typeof CONSOLE_FORMS;
@@ -167,7 +167,8 @@ export const CONSOLE_FORMS = {
167
167
  followUp: formContract("POST /r/<run>/checks|add-tests", ["return", "level"]),
168
168
  resolveIncident: formContract("POST /i/<id>/resolve", ["return"]),
169
169
  // A task's verbs (/t/<task>/<verb>), one body each.
170
- taskSteer: formContract("POST /t/<task>/steer", ["note"]),
170
+ taskSteer: formContract("POST /t/<task>/steer", ["note", "return", "run"]),
171
+ taskReply: formContract("POST /t/<task>/reply", ["question", "answer"]),
171
172
  taskHold: formContract("POST /t/<task>/hold", ["reason"]),
172
173
  taskBlock: formContract("POST /t/<task>/block|unblock", ["on"]),
173
174
  taskRepairDependency: formContract("POST /t/<task>/repair-dependency", ["blocker", "operation", "replacement"]),
@@ -184,6 +185,9 @@ export const CONSOLE_FORMS = {
184
185
  taskAcceptProof: formContract("POST /t/<task>/accept-proof", ["run", "note", "return"]),
185
186
  taskComplete: formContract("POST /t/<task>/complete", ["receipt", "run", "accept", "note", "publish", "return"]),
186
187
  taskStop: formContract("POST /t/<task>/stop", ["run", "return"]),
188
+ // Pause, or Pause and steer when a note rides along, or Resume (mode=resume): the exact run shown, and the page's own
189
+ // request id.
190
+ taskPause: formContract("POST /t/<task>/pause", ["run", "request", "note", "mode", "return"]),
187
191
  taskResume: formContract("POST /t/<task>/resume|resume-arm", ["run", "token", "nonce", "return"]),
188
192
  };
189
193
  /** The console POSTs whose handlers read no field (sign-out not even the shared guard): their contract is the empty form. */
@@ -656,7 +656,14 @@ export declare const LEAD_TOOL_INPUTS: {
656
656
  }, z.core.$strict>;
657
657
  readonly propose_steer: z.ZodObject<{
658
658
  task: z.ZodString;
659
- note: z.ZodString;
659
+ note: z.ZodOptional<z.ZodString>;
660
+ mode: z.ZodOptional<z.ZodEnum<{
661
+ note: "note";
662
+ pause: "pause";
663
+ "pause-and-steer": "pause-and-steer";
664
+ resume: "resume";
665
+ }>>;
666
+ question: z.ZodOptional<z.ZodNumber>;
660
667
  }, z.core.$strict>;
661
668
  readonly propose_dependency_repair: z.ZodObject<{
662
669
  task: z.ZodString;
@@ -1318,7 +1325,14 @@ export declare const LEAD_TOOL_READ_INPUTS: {
1318
1325
  }, z.core.$strict>;
1319
1326
  readonly propose_steer: z.ZodObject<{
1320
1327
  task: z.ZodString;
1321
- note: z.ZodString;
1328
+ note: z.ZodOptional<z.ZodString>;
1329
+ mode: z.ZodOptional<z.ZodEnum<{
1330
+ note: "note";
1331
+ pause: "pause";
1332
+ "pause-and-steer": "pause-and-steer";
1333
+ resume: "resume";
1334
+ }>>;
1335
+ question: z.ZodOptional<z.ZodNumber>;
1322
1336
  }, z.core.$strict>;
1323
1337
  readonly propose_dependency_repair: z.ZodObject<{
1324
1338
  task: z.ZodString;
@@ -130,7 +130,11 @@ export const LEAD_TOOL_INPUTS = {
130
130
  propose_reserve: z.strictObject({ task: taskRef, worker: text("workerName").nullable() }),
131
131
  propose_hold: z.strictObject({ task: taskRef, reason: text("proposalReason") }),
132
132
  propose_unhold: z.strictObject({ task: taskRef }),
133
- propose_steer: z.strictObject({ task: taskRef, note }),
133
+ // mode: a note (the default), Pause (stop a running Claude build's turn, keep the build), Pause and steer, or Resume
134
+ // (a paused build goes on where it stopped, without a note).
135
+ // question: a running agent's open question (Agents board); the note is its reply, sent as an ordinary note.
136
+ propose_steer: z.strictObject({ task: taskRef, note: note.optional(), mode: z.enum(["note", "pause", "pause-and-steer", "resume"]).optional(),
137
+ question: z.number().int().positive().optional().describe("A running agent's open question (get_task agentQuestions): the note becomes its reply and closes it.") }),
134
138
  propose_dependency_repair: z.strictObject({ task: taskRef, blocker: taskRef, operation: z.enum(["retry", "unlink", "replace"]), replacement: taskRef.optional() }),
135
139
  propose_scope: z.strictObject({ task: taskRef, goal: scopeText, not: scopeText.nullable().optional(), touches: touches.optional(), acceptance }),
136
140
  propose_agents: z.strictObject({