humanish 0.110.0 → 0.110.2

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 (178) hide show
  1. package/ARCHITECTURE.md +15 -12
  2. package/CHANGELOG.md +26 -0
  3. package/CONTEXT.md +8 -6
  4. package/README.md +103 -53
  5. package/dist/actors/computer-use/loop/ending.js +4 -4
  6. package/dist/actors/local-agent/claude-participant.d.ts +74 -0
  7. package/dist/actors/local-agent/claude-participant.js +243 -0
  8. package/dist/actors/local-agent/claude-session.d.ts +16 -3
  9. package/dist/actors/local-agent/claude-session.js +64 -23
  10. package/dist/actors/local-agent/cli.d.ts +17 -0
  11. package/dist/actors/local-agent/cli.js +111 -24
  12. package/dist/actors/local-agent/readiness.d.ts +2 -2
  13. package/dist/actors/local-agent/readiness.js +20 -12
  14. package/dist/cli/commands/analyze.js +21 -5
  15. package/dist/cli/commands/observe.js +15 -2
  16. package/dist/cli/commands/run-signals.d.ts +2 -0
  17. package/dist/cli/commands/run-signals.js +106 -45
  18. package/dist/cli/commands/runs.d.ts +5 -0
  19. package/dist/cli/commands/runs.js +113 -74
  20. package/dist/cli/commands/setup.js +23 -14
  21. package/dist/cli/commands/study-route-computer-use.js +9 -3
  22. package/dist/cli/commands/study-route-open.js +1 -0
  23. package/dist/cli/commands/study-route-preview.js +1 -0
  24. package/dist/cli/commands/study-route-scripted.js +2 -0
  25. package/dist/cli/commands/study-route-shared-world.js +6 -2
  26. package/dist/cli/commands/study-route-terminal.js +2 -0
  27. package/dist/cli/commands/watch.js +14 -4
  28. package/dist/cli/doctor.d.ts +4 -1
  29. package/dist/cli/doctor.js +51 -10
  30. package/dist/cli/findings.d.ts +103 -0
  31. package/dist/cli/findings.js +317 -0
  32. package/dist/cli/first-run-path.d.ts +32 -11
  33. package/dist/cli/first-run-path.js +113 -69
  34. package/dist/cli/invocation.d.ts +7 -0
  35. package/dist/cli/invocation.js +32 -0
  36. package/dist/cli/io.d.ts +3 -1
  37. package/dist/cli/io.js +8 -15
  38. package/dist/cli/observer-follow.d.ts +20 -0
  39. package/dist/cli/observer-follow.js +36 -1
  40. package/dist/cli/orientation.js +16 -14
  41. package/dist/cli/product-sentence.d.ts +3 -3
  42. package/dist/cli/product-sentence.js +3 -3
  43. package/dist/cli/version.d.ts +1 -0
  44. package/dist/cli/version.js +11 -0
  45. package/dist/evidence/encoded-text.d.ts +9 -6
  46. package/dist/evidence/encoded-text.js +84 -19
  47. package/dist/evidence/redaction.d.ts +2 -0
  48. package/dist/evidence/redaction.js +123 -21
  49. package/dist/feedback/export-bundle.js +2 -2
  50. package/dist/keys/env-file.d.ts +4 -0
  51. package/dist/keys/env-file.js +17 -0
  52. package/dist/keys/key-resolution.js +6 -5
  53. package/dist/observer/data.d.ts +14 -1
  54. package/dist/observer/data.js +19 -9
  55. package/dist/observer/exposure.js +1 -1
  56. package/dist/observer/library.d.ts +4 -0
  57. package/dist/observer/library.js +21 -17
  58. package/dist/observer/render.js +10 -3
  59. package/dist/observer/run-routes.d.ts +2 -0
  60. package/dist/observer/run-routes.js +51 -35
  61. package/dist/observer/serve.js +2 -1
  62. package/dist/observer-app.html +3 -3
  63. package/dist/routes/computer-use/bundle.js +3 -1
  64. package/dist/routes/computer-use/fanout-records.js +1 -1
  65. package/dist/routes/computer-use/participant-execution.js +3 -1
  66. package/dist/routes/computer-use/participant-model.js +5 -2
  67. package/dist/routes/computer-use/plan.js +9 -4
  68. package/dist/routes/computer-use/preflight.js +1 -1
  69. package/dist/routes/computer-use/result.js +17 -28
  70. package/dist/routes/computer-use/single-bundle.js +7 -1
  71. package/dist/routes/scripted/bundle.js +4 -2
  72. package/dist/routes/scripted/plan.js +2 -2
  73. package/dist/routes/scripted/result.d.ts +3 -5
  74. package/dist/routes/scripted/result.js +16 -22
  75. package/dist/routes/scripted/scenario.js +4 -4
  76. package/dist/routes/shared-world/bundle-records.js +6 -1
  77. package/dist/routes/shared-world/bundle.js +2 -0
  78. package/dist/routes/shared-world/plan.js +6 -2
  79. package/dist/routes/shared-world/result.js +22 -22
  80. package/dist/routes/terminal/bundle.js +6 -3
  81. package/dist/routes/terminal/credentials.d.ts +5 -5
  82. package/dist/routes/terminal/credentials.js +14 -6
  83. package/dist/routes/terminal/dry-run.js +8 -12
  84. package/dist/routes/terminal/ledger.d.ts +3 -3
  85. package/dist/routes/terminal/ledger.js +6 -6
  86. package/dist/routes/terminal/live-finish.js +15 -13
  87. package/dist/routes/terminal/plan.js +12 -4
  88. package/dist/routes/terminal/result.d.ts +6 -7
  89. package/dist/routes/terminal/result.js +6 -22
  90. package/dist/routes/terminal/route.js +5 -4
  91. package/dist/routes/terminal/runtime-auth.d.ts +6 -0
  92. package/dist/routes/terminal/runtime-auth.js +5 -0
  93. package/dist/routes/terminal/session.js +1 -1
  94. package/dist/run/bundle-shape.d.ts +3 -1
  95. package/dist/run/bundle-shape.js +25 -1
  96. package/dist/run/bundle.d.ts +24 -0
  97. package/dist/run/display.d.ts +59 -0
  98. package/dist/run/display.js +106 -0
  99. package/dist/run/dry-run.js +13 -8
  100. package/dist/run/judge.d.ts +3 -2
  101. package/dist/run/judge.js +12 -1
  102. package/dist/run/local-only-files.d.ts +13 -0
  103. package/dist/run/local-only-files.js +35 -0
  104. package/dist/run/pricing.js +1 -1
  105. package/dist/run/projection.d.ts +0 -2
  106. package/dist/run/projection.js +0 -11
  107. package/dist/run/reclaim-outcomes.d.ts +89 -0
  108. package/dist/run/reclaim-outcomes.js +213 -0
  109. package/dist/run/reclaim.d.ts +23 -17
  110. package/dist/run/reclaim.js +213 -114
  111. package/dist/run/results.d.ts +10 -20
  112. package/dist/run/results.js +2 -2
  113. package/dist/run/run-index.d.ts +11 -1
  114. package/dist/run/run-index.js +11 -0
  115. package/dist/run/run-not-found.js +3 -2
  116. package/dist/run/run.d.ts +34 -18
  117. package/dist/run/run.js +166 -67
  118. package/dist/run/sandbox-creates.d.ts +59 -0
  119. package/dist/run/sandbox-creates.js +140 -0
  120. package/dist/run/sandbox-receipts.d.ts +20 -5
  121. package/dist/run/sandbox-receipts.js +56 -7
  122. package/dist/run/stats.d.ts +5 -0
  123. package/dist/run/stats.js +8 -4
  124. package/dist/run/status.d.ts +6 -9
  125. package/dist/run/status.js +7 -23
  126. package/dist/run/stored-runs.d.ts +5 -14
  127. package/dist/run/stored-runs.js +31 -146
  128. package/dist/run/streams.d.ts +3 -2
  129. package/dist/study/agents-section.d.ts +21 -0
  130. package/dist/study/agents-section.js +86 -0
  131. package/dist/study/doctor.d.ts +1 -1
  132. package/dist/study/doctor.js +8 -4
  133. package/dist/study/init-templates.js +0 -95
  134. package/dist/study/init.js +45 -37
  135. package/dist/study/keys.d.ts +12 -0
  136. package/dist/study/keys.js +32 -0
  137. package/dist/study/local-browser-host.d.ts +11 -0
  138. package/dist/study/local-browser-host.js +42 -0
  139. package/dist/study/parse/actors.js +9 -1
  140. package/dist/study/parse/subject.js +12 -0
  141. package/dist/study/parse/url-credentials.d.ts +9 -0
  142. package/dist/study/parse/url-credentials.js +50 -0
  143. package/dist/study/persona.d.ts +4 -0
  144. package/dist/study/persona.js +4 -2
  145. package/dist/study/preflight-probes.js +2 -1
  146. package/dist/study/types.d.ts +8 -6
  147. package/dist/study/url-credentials.d.ts +6 -0
  148. package/dist/study/url-credentials.js +34 -0
  149. package/dist/substrates/e2b/peer-install.d.ts +51 -0
  150. package/dist/substrates/e2b/peer-install.js +161 -0
  151. package/dist/substrates/e2b/sandbox.d.ts +34 -6
  152. package/dist/substrates/e2b/sandbox.js +141 -30
  153. package/dist/substrates/e2b/sdk.d.ts +33 -0
  154. package/dist/substrates/e2b/sdk.js +21 -21
  155. package/dist/substrates/local/firecracker-desktop.js +3 -2
  156. package/dist/substrates/local/runtime-host.js +5 -4
  157. package/dist/substrates/local/runtime.js +5 -4
  158. package/dist/substrates/shell.d.ts +2 -0
  159. package/dist/substrates/shell.js +4 -0
  160. package/dist/tui/contract.js +5 -4
  161. package/dist/tui-app.js +133 -133
  162. package/dist/verify/liveness.d.ts +19 -3
  163. package/dist/verify/liveness.js +47 -25
  164. package/dist/verify/verify.d.ts +3 -0
  165. package/dist/verify/verify.js +8 -3
  166. package/docs/architecture/actor-contract.md +2 -1
  167. package/docs/architecture/observer.md +22 -7
  168. package/docs/architecture/task-protocol-support.md +13 -13
  169. package/docs/architecture/terminal-product-route.md +15 -13
  170. package/docs/contracts/run-bundle.md +95 -10
  171. package/docs/contracts/schemas.md +44 -26
  172. package/docs/contracts/study-analysis.md +45 -0
  173. package/docs/contracts/study-costs.md +8 -0
  174. package/docs/decisions/0001-run-bundle-is-the-source-of-truth.md +3 -0
  175. package/docs/decisions/0003-contained-paths-and-cleanup-authority.md +9 -3
  176. package/docs/principles/invariants-and-defaults.md +11 -8
  177. package/package.json +6 -3
  178. package/skills/humanish/SKILL.md +38 -20
@@ -5,27 +5,32 @@
5
5
  // E2BDesktopSandbox is the command, file, stream, launch and input subset the participants use; `open` is
6
6
  // optional because older SDKs lack it (the computer-use route then falls back to launch). It
7
7
  // satisfies the executor's E2BDesktopLike port (src/substrates/e2b/desktop-executor.ts) as is.
8
- import { sep } from "node:path";
9
- import { fileURLToPath } from "node:url";
10
8
  import { protectDesktopScreenshotCleanup } from "./desktop-screenshot-cleanup.js";
9
+ import { desktopPeerAdvice, humanishInstall } from "./peer-install.js";
11
10
  export async function loadE2BDesktopModule() {
12
11
  try {
13
12
  return guardDesktopSandboxCreate((await import("@e2b/desktop")));
14
13
  }
15
14
  catch (error) {
16
15
  if (isMissingE2BDesktopDependency(error)) {
17
- throw new Error(runningFromProject()
18
- ? "Live E2B desktop launch requires the optional peer @e2b/desktop. Install it beside humanish " +
19
- "in this project: `npm i -D @e2b/desktop`."
20
- : "Live E2B desktop launch requires the optional peer @e2b/desktop, and humanish is running from an " +
21
- "npx cache. Node resolves the peer relative to humanish itself, so a copy installed in this project " +
22
- "is not found. Install both into the project and run it from there: " +
23
- "`npm i -D humanish @e2b/desktop` then `npx humanish run <study>`.");
16
+ const { where, advice } = desktopPeerAdvice(humanishInstall());
17
+ throw new Error(`Live E2B desktop launch requires the optional peer @e2b/desktop${where}. ${advice}`);
24
18
  }
25
19
  throw error;
26
20
  }
27
21
  }
28
22
  export const DESKTOP_CREATE_CLEANUP_TIMEOUT_MS = 10_000;
23
+ const allocationObservers = new WeakMap();
24
+ /**
25
+ * Call `observer` with the sandbox id as soon as the guarded SDK constructs the handle for a
26
+ * create made with this exact options object, before desktop startup runs. Startup can take
27
+ * seconds after E2B created the sandbox; this is the earliest point the id exists in this process.
28
+ * A module without the guard (a test fake) never calls it, and create's own result still reports
29
+ * the id.
30
+ */
31
+ export function observeDesktopAllocation(options, observer) {
32
+ allocationObservers.set(options, observer);
33
+ }
29
34
  /** Startup failed after this call acquired a handle. No credentials/options are included here. */
30
35
  export class E2BDesktopStartupError extends Error {
31
36
  cleanup;
@@ -67,6 +72,7 @@ export function guardDesktopSandboxCreate(module) {
67
72
  : DESKTOP_CREATE_CLEANUP_TIMEOUT_MS;
68
73
  let cleanupOwned;
69
74
  let restoreKill;
75
+ const observeAllocation = createOptions === undefined ? undefined : allocationObservers.get(createOptions);
70
76
  // oxlint-disable-next-line typescript/no-this-alias -- the attempt subclasses whichever SDK class was called
71
77
  const CallingSandbox = this;
72
78
  class AttemptSandbox extends CallingSandbox {
@@ -91,6 +97,12 @@ export function guardDesktopSandboxCreate(module) {
91
97
  else
92
98
  Reflect.deleteProperty(this, "kill");
93
99
  };
100
+ try {
101
+ observeAllocation?.(this.sandboxId);
102
+ }
103
+ catch {
104
+ // An observer that throws must not fail the create; the resolved handle reports the id.
105
+ }
94
106
  }
95
107
  }
96
108
  try {
@@ -144,18 +156,6 @@ async function reclaimFailedDesktopCreate(kill, timeoutMs) {
144
156
  clearTimeout(timer);
145
157
  }
146
158
  }
147
- /**
148
- * Is humanish running from this project's node_modules, or from an npx cache?
149
- *
150
- * It decides which advice is true. `npx humanish@latest` resolves its optional peer relative to
151
- * itself, so "install @e2b/desktop in this project" is advice that cannot work there, and that
152
- * message cost two cold verification runs before the difference was noticed. A one-shot npx
153
- * invocation cannot do a live desktop run at all; humanish has to be installed alongside the peer.
154
- */
155
- function runningFromProject() {
156
- const here = fileURLToPath(import.meta.url);
157
- return here.startsWith(`${process.cwd()}${sep}node_modules${sep}`);
158
- }
159
159
  function isMissingE2BDesktopDependency(error) {
160
160
  const value = error;
161
161
  return value.code === "ERR_MODULE_NOT_FOUND" && value.message?.includes("@e2b/desktop") === true;
@@ -9,6 +9,7 @@ import { ownDesktopAllocation, } from "../desktop-session.js";
9
9
  import { runtimeDocker, runtimeExec, usesLima } from "./runtime-host.js";
10
10
  import { openLimaTunnel } from "./runtime-ssh.js";
11
11
  import { guestMediaConfigSchema } from "../../guest/media-config.js";
12
+ import { cli } from "../../cli/invocation.js";
12
13
  const docker = async (args) => (await runtimeDocker(args, {}, 60_000)).stdout.trim();
13
14
  const readLogs = async (id) => {
14
15
  const result = await runtimeDocker(["logs", "--tail", "100", id], {}, 10_000);
@@ -18,12 +19,12 @@ function checkDesktopOptions(options) {
18
19
  if (options.media !== undefined) {
19
20
  guestMediaConfigSchema.parse(options.media);
20
21
  if (options.assets.media !== true)
21
- throw new Error("This local runtime does not include media. Run humanish runtime setup --media.");
22
+ throw new Error(`This local runtime does not include media. Run ${cli("runtime setup --media")}.`);
22
23
  }
23
24
  if (options.recording !== undefined) {
24
25
  desktopRecordingConfigSchema.parse(options.recording);
25
26
  if (options.assets.media !== true)
26
- throw new Error("This runtime does not include the recorder. Run humanish runtime setup --media.");
27
+ throw new Error(`This runtime does not include the recorder. Run ${cli("runtime setup --media")}.`);
27
28
  }
28
29
  let url;
29
30
  try {
@@ -3,6 +3,7 @@ import { mkdtemp, rm, writeFile } from "node:fs/promises";
3
3
  import { tmpdir } from "node:os";
4
4
  import path from "node:path";
5
5
  import { promisify } from "node:util";
6
+ import { cli } from "../../cli/invocation.js";
6
7
  const exec = promisify(execFile);
7
8
  export const LIMA_INSTANCE = "humanish-runtime";
8
9
  export const usesLima = (options = {}) => (options.platform ?? process.platform) === "darwin";
@@ -86,11 +87,11 @@ export async function limaStatus(options = {}) {
86
87
  version = (await hostExec("limactl", ["--version"], options)).stdout;
87
88
  }
88
89
  catch {
89
- throw new Error("Install Lima 2.2 or newer (brew install lima), then run humanish runtime setup. Docker Desktop is not required.");
90
+ throw new Error(`Install Lima 2.2 or newer (brew install lima), then run ${cli("runtime setup")}. Docker Desktop is not required.`);
90
91
  }
91
92
  const match = /\b(\d+)\.(\d+)\.\d+/.exec(version);
92
93
  if (!match || Number(match[1]) < 2 || (Number(match[1]) === 2 && Number(match[2]) < 2))
93
- throw new Error("Update Lima to 2.2 or newer (brew upgrade lima), then run humanish runtime setup.");
94
+ throw new Error(`Update Lima to 2.2 or newer (brew upgrade lima), then run ${cli("runtime setup")}.`);
94
95
  const output = (await hostExec("limactl", ["list", "--json"], options)).stdout.trim();
95
96
  const instance = output
96
97
  .split("\n")
@@ -104,8 +105,8 @@ export async function limaStatus(options = {}) {
104
105
  ready: instance?.status === "Running",
105
106
  exists: !!instance,
106
107
  message: instance
107
- ? "The humanish Lima host is stopped. Run humanish runtime setup to start it."
108
- : "Run humanish runtime setup to create the humanish Lima host and install the browser runtime.",
108
+ ? `The humanish Lima host is stopped. Run ${cli("runtime setup")} to start it.`
109
+ : `Run ${cli("runtime setup")} to create the humanish Lima host and install the browser runtime.`,
109
110
  };
110
111
  }
111
112
  export async function prepareLima(options = {}, progress) {
@@ -7,6 +7,7 @@ import { Readable, Transform } from "node:stream";
7
7
  import { pipeline } from "node:stream/promises";
8
8
  import { LOCAL_MEDIA_RUNTIME_RELEASES, LOCAL_RUNTIME_RELEASES } from "./runtime-release.js";
9
9
  import { limaStatus, loadRuntimeArchive, prepareLima, runtimeArchitecture, runtimeDocker, runtimeExec, usesLima, } from "./runtime-host.js";
10
+ import { cli } from "../../cli/invocation.js";
10
11
  const docker = async (args, options) => (await runtimeDocker(args, options)).stdout.trim();
11
12
  const runtimeRelease = (options) => options.release ??
12
13
  (options.media ? LOCAL_MEDIA_RUNTIME_RELEASES : LOCAL_RUNTIME_RELEASES)[runtimeArchitecture(options) ?? "amd64"];
@@ -80,7 +81,7 @@ export async function localRuntimeStatus(options = {}) {
80
81
  installed: false,
81
82
  message: lima
82
83
  ? "Docker is unavailable inside the humanish Lima host. Check limactl shell humanish-runtime -- sudo systemctl status docker; Docker Desktop is not used."
83
- : "Docker is unavailable. Install/start Docker Engine and give your account access, then run humanish runtime setup.",
84
+ : `Docker is unavailable. Install/start Docker Engine and give your account access, then run ${cli("runtime setup")}.`,
84
85
  };
85
86
  }
86
87
  const release = runtimeRelease(options);
@@ -108,7 +109,7 @@ export async function localRuntimeStatus(options = {}) {
108
109
  : {
109
110
  ok: true,
110
111
  installed: false,
111
- message: "Local runtime will download before the first live run. Run humanish runtime setup to prepare it now.",
112
+ message: `Local runtime will download before the first live run. Run ${cli("runtime setup")} to prepare it now.`,
112
113
  };
113
114
  }
114
115
  const image = images[0];
@@ -161,7 +162,7 @@ export async function prepareLocalRuntime(options = {}) {
161
162
  try {
162
163
  const response = await fetch(release.url, { signal });
163
164
  if (!response.ok || !response.body)
164
- throw new Error(`Runtime download failed (HTTP ${response.status}). Run humanish runtime setup to retry.`);
165
+ throw new Error(`Runtime download failed (HTTP ${response.status}). Run ${cli("runtime setup")} to retry.`);
165
166
  let bytes = 0;
166
167
  const hash = createHash("sha256");
167
168
  await pipeline(Readable.fromWeb(response.body), new Transform({
@@ -181,7 +182,7 @@ export async function prepareLocalRuntime(options = {}) {
181
182
  await loadRuntimeArchive(archive, { ...options, signal });
182
183
  const after = await localRuntimeStatus(options);
183
184
  if (!after.assets)
184
- throw new Error("Downloaded runtime did not provide the expected image. Run humanish runtime setup to retry.");
185
+ throw new Error(`Downloaded runtime did not provide the expected image. Run ${cli("runtime setup")} to retry.`);
185
186
  return after.assets;
186
187
  }
187
188
  finally {
@@ -39,6 +39,8 @@ export declare function runOrThrow(shell: Shell, command: string, options?: Shel
39
39
  export declare function throwOnExit(result: ShellResult): ShellResult;
40
40
  /** Single-quote a value for a Shell command line. */
41
41
  export declare function shellQuote(value: string): string;
42
+ /** A command-line argument, single-quoted only when the shell would split or expand it. */
43
+ export declare function shellArg(value: string): string;
42
44
  /** Sanitized, whitespace-collapsed, length-capped tail of command output. */
43
45
  export declare function tailOf(value: string | undefined): string;
44
46
  export {};
@@ -16,6 +16,10 @@ export function throwOnExit(result) {
16
16
  export function shellQuote(value) {
17
17
  return `'${value.replace(/'/g, "'\\''")}'`;
18
18
  }
19
+ /** A command-line argument, single-quoted only when the shell would split or expand it. */
20
+ export function shellArg(value) {
21
+ return /^[\w@%+=:,./-]+$/.test(value) ? value : shellQuote(value);
22
+ }
19
23
  /** Sanitized, whitespace-collapsed, length-capped tail of command output. */
20
24
  export function tailOf(value) {
21
25
  return (value ?? "").trim().replace(/\s+/g, " ").slice(-240);
@@ -12,6 +12,7 @@
12
12
  //
13
13
  // It also makes the UI testable without a terminal: a test hands `startTui` a fake index and reads
14
14
  // the frames back.
15
+ import { cli } from "../cli/invocation.js";
15
16
  /** Node version the Ink runtime requires (ink@7 declares `engines.node >= 22`). */
16
17
  export const TUI_MIN_NODE_MAJOR = 22;
17
18
  /**
@@ -41,12 +42,12 @@ export const TUI_BUNDLE_URL = new URL("../tui-app.js", import.meta.url);
41
42
  */
42
43
  export function terminalSurfaceMessage(state) {
43
44
  if (!state.supported) {
44
- return `\`humanish tui\` needs Node ${TUI_MIN_NODE_MAJOR}+ (this is ${state.nodeVersion}); every other command works here`;
45
+ return `\`${cli("tui")}\` needs Node ${TUI_MIN_NODE_MAJOR}+ (this is ${state.nodeVersion}); every other command works here`;
45
46
  }
46
47
  if (!state.bundlePresent) {
47
- return "`humanish tui` is not built in this checkout; run `pnpm build` (installed packages ship it prebuilt)";
48
+ return `\`${cli("tui")}\` is not built in this checkout; run \`pnpm build\` (installed packages ship it prebuilt)`;
48
49
  }
49
50
  return state.interactive
50
- ? "`humanish tui` opens the interactive surface for browsing studies and runs"
51
- : "`humanish tui` is the interactive screen a person uses to watch and start runs here. This shell is not a terminal, so pass it on to a person at one";
51
+ ? `\`${cli("tui")}\` opens the interactive surface for browsing studies and runs`
52
+ : `\`${cli("tui")}\` is the interactive screen a person uses to watch and start runs here. This shell is not a terminal, so pass it on to a person at one`;
52
53
  }