@h-rig/cli 0.0.6-alpha.81 → 0.0.6-alpha.83

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 (100) hide show
  1. package/dist/bin/build-rig-binaries.js +40 -8
  2. package/dist/bin/rig.js +2807 -1443
  3. package/dist/src/app/board.js +62 -13
  4. package/dist/src/app-opentui/adapters/command.d.ts +2 -0
  5. package/dist/src/app-opentui/adapters/command.js +329 -0
  6. package/dist/src/app-opentui/adapters/common.js +2 -2
  7. package/dist/src/app-opentui/adapters/doctor.js +63 -14
  8. package/dist/src/app-opentui/adapters/fleet.js +84 -20
  9. package/dist/src/app-opentui/adapters/inbox.js +83 -19
  10. package/dist/src/app-opentui/adapters/init.js +87 -23
  11. package/dist/src/app-opentui/adapters/pi-attach.js +102 -36
  12. package/dist/src/app-opentui/adapters/run-detail.js +83 -19
  13. package/dist/src/app-opentui/adapters/server.js +100 -23
  14. package/dist/src/app-opentui/adapters/tasks.d.ts +11 -0
  15. package/dist/src/app-opentui/adapters/tasks.js +742 -94
  16. package/dist/src/app-opentui/bootstrap.d.ts +1 -6
  17. package/dist/src/app-opentui/bootstrap.js +13776 -12369
  18. package/dist/src/app-opentui/command-pty-host.d.ts +62 -0
  19. package/dist/src/app-opentui/command-pty-host.js +248 -0
  20. package/dist/src/app-opentui/events.js +1 -1
  21. package/dist/src/app-opentui/focus-manager.d.ts +14 -0
  22. package/dist/src/app-opentui/focus-manager.js +24 -0
  23. package/dist/src/app-opentui/index.js +3806 -2878
  24. package/dist/src/app-opentui/intent.js +154 -48
  25. package/dist/src/app-opentui/keymap.d.ts +20 -0
  26. package/dist/src/app-opentui/keymap.js +707 -0
  27. package/dist/src/app-opentui/launch-routing.d.ts +16 -0
  28. package/dist/src/app-opentui/launch-routing.js +55 -0
  29. package/dist/src/app-opentui/layout.js +2 -2
  30. package/dist/src/app-opentui/pi-host-child.js +66 -16
  31. package/dist/src/app-opentui/pi-pty-host.d.ts +9 -0
  32. package/dist/src/app-opentui/pi-pty-host.js +15 -13
  33. package/dist/src/app-opentui/registry.js +3228 -2396
  34. package/dist/src/app-opentui/render/ascii-fleet.d.ts +15 -0
  35. package/dist/src/app-opentui/render/ascii-fleet.js +82 -0
  36. package/dist/src/app-opentui/render/graphics.d.ts +1 -1
  37. package/dist/src/app-opentui/render/graphics.js +131 -16
  38. package/dist/src/app-opentui/render/image-visual-layer-worker.js +413 -958
  39. package/dist/src/app-opentui/render/image-visual-layer.d.ts +19 -10
  40. package/dist/src/app-opentui/render/image-visual-layer.js +391 -357
  41. package/dist/src/app-opentui/render/panel-layout.d.ts +38 -0
  42. package/dist/src/app-opentui/render/panel-layout.js +48 -0
  43. package/dist/src/app-opentui/render/panels.d.ts +2 -0
  44. package/dist/src/app-opentui/render/panels.js +62 -29
  45. package/dist/src/app-opentui/render/preloader.d.ts +10 -0
  46. package/dist/src/app-opentui/render/preloader.js +163 -0
  47. package/dist/src/app-opentui/render/scene.d.ts +3 -0
  48. package/dist/src/app-opentui/render/scene.js +12 -0
  49. package/dist/src/app-opentui/render/text.js +5 -1
  50. package/dist/src/app-opentui/render/type-bar.d.ts +2 -1
  51. package/dist/src/app-opentui/render/type-bar.js +51 -16
  52. package/dist/src/app-opentui/runtime-resources.d.ts +16 -0
  53. package/dist/src/app-opentui/runtime-resources.js +62 -0
  54. package/dist/src/app-opentui/runtime.js +2329 -1512
  55. package/dist/src/app-opentui/scenes/command.d.ts +3 -0
  56. package/dist/src/app-opentui/scenes/command.js +103 -0
  57. package/dist/src/app-opentui/scenes/doctor.d.ts +2 -1
  58. package/dist/src/app-opentui/scenes/doctor.js +53 -14
  59. package/dist/src/app-opentui/scenes/error.js +44 -14
  60. package/dist/src/app-opentui/scenes/fleet.js +40 -21
  61. package/dist/src/app-opentui/scenes/handoff.js +142 -27
  62. package/dist/src/app-opentui/scenes/help.js +63 -48
  63. package/dist/src/app-opentui/scenes/inbox.d.ts +2 -1
  64. package/dist/src/app-opentui/scenes/inbox.js +64 -17
  65. package/dist/src/app-opentui/scenes/init.d.ts +2 -1
  66. package/dist/src/app-opentui/scenes/init.js +62 -21
  67. package/dist/src/app-opentui/scenes/main.d.ts +2 -1
  68. package/dist/src/app-opentui/scenes/main.js +80 -24
  69. package/dist/src/app-opentui/scenes/run-detail.d.ts +2 -1
  70. package/dist/src/app-opentui/scenes/run-detail.js +39 -25
  71. package/dist/src/app-opentui/scenes/server.d.ts +2 -1
  72. package/dist/src/app-opentui/scenes/server.js +71 -20
  73. package/dist/src/app-opentui/scenes/tasks.js +44 -22
  74. package/dist/src/app-opentui/state.js +70 -30
  75. package/dist/src/app-opentui/terminal-capabilities.d.ts +7 -0
  76. package/dist/src/app-opentui/terminal-capabilities.js +15 -0
  77. package/dist/src/app-opentui/types.d.ts +10 -2
  78. package/dist/src/commands/_doctor-checks.js +62 -13
  79. package/dist/src/commands/_operator-view.js +63 -13
  80. package/dist/src/commands/_pi-frontend.js +63 -13
  81. package/dist/src/commands/_preflight.js +64 -14
  82. package/dist/src/commands/_server-client.js +82 -18
  83. package/dist/src/commands/_snapshot-upload.js +62 -13
  84. package/dist/src/commands/connect.js +7 -1
  85. package/dist/src/commands/doctor.js +62 -13
  86. package/dist/src/commands/github.js +144 -23
  87. package/dist/src/commands/inbox.js +62 -13
  88. package/dist/src/commands/init.js +82 -18
  89. package/dist/src/commands/inspect.js +62 -13
  90. package/dist/src/commands/run.js +66 -13
  91. package/dist/src/commands/server.js +69 -14
  92. package/dist/src/commands/setup.js +62 -13
  93. package/dist/src/commands/stats.js +62 -13
  94. package/dist/src/commands/task-run-driver.js +62 -13
  95. package/dist/src/commands/task.js +65 -14
  96. package/dist/src/commands.js +227 -92
  97. package/dist/src/index.js +234 -99
  98. package/package.json +8 -9
  99. package/dist/src/app-opentui/render/image-visual-layer-native-canvas.d.ts +0 -2
  100. package/dist/src/app-opentui/render/image-visual-layer-native-canvas.js +0 -1480
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Interactive CLI is app-shell first.
3
+ *
4
+ * Native scenes handle the common surfaces (project menu, runs, tasks, inbox,
5
+ * server, init, doctor, Pi attach). Every other interactive command still
6
+ * enters OpenTUI and is executed inside an embedded plain-CLI PTY, preserving
7
+ * the exact existing options/actions/wizards without pretending unsupported
8
+ * commands were handled by a read-only scene. Machine/internal paths stay plain.
9
+ */
10
+ export declare function isOpenTuiAppCommand(argv: readonly string[], env?: NodeJS.ProcessEnv): boolean;
11
+ export declare function shouldLaunchTtyApp(input: {
12
+ readonly argv: readonly string[];
13
+ readonly stdoutIsTty?: boolean;
14
+ readonly stdinIsTty?: boolean;
15
+ readonly env?: NodeJS.ProcessEnv;
16
+ }): boolean;
@@ -0,0 +1,55 @@
1
+ // @bun
2
+ // packages/cli/src/app-opentui/launch-routing.ts
3
+ var GLOBAL_OPTIONS_WITH_VALUE = new Set(["--project", "--policy-mode", "--run-id"]);
4
+ var GLOBAL_FLAGS = new Set(["--dry-run"]);
5
+ function envTruthy(value) {
6
+ if (!value)
7
+ return false;
8
+ const normalized = value.trim().toLowerCase();
9
+ return normalized !== "0" && normalized !== "false" && normalized !== "no";
10
+ }
11
+ function stripGlobalAppOptions(argv) {
12
+ const rest = [];
13
+ for (let index = 0;index < argv.length; index += 1) {
14
+ const arg = argv[index];
15
+ if (GLOBAL_FLAGS.has(arg))
16
+ continue;
17
+ if (GLOBAL_OPTIONS_WITH_VALUE.has(arg)) {
18
+ index += 1;
19
+ continue;
20
+ }
21
+ rest.push(arg);
22
+ }
23
+ return rest;
24
+ }
25
+ function isOpenTuiAppCommand(argv, env = process.env) {
26
+ const commandArgv = stripGlobalAppOptions(argv);
27
+ const [group = "", command = ""] = commandArgv;
28
+ const normalizedGroup = group.toLowerCase();
29
+ const normalizedCommand = command.toLowerCase();
30
+ if (!normalizedGroup)
31
+ return true;
32
+ if (normalizedGroup === "__opentui-pi-host" || normalizedGroup === "__rig-server")
33
+ return false;
34
+ if (normalizedGroup === "server" && normalizedCommand === "task-run")
35
+ return false;
36
+ if (normalizedGroup === "server" && normalizedCommand === "start" && env.RIG_SERVER_INTERNAL_EXEC === "1")
37
+ return false;
38
+ return true;
39
+ }
40
+ function shouldLaunchTtyApp(input) {
41
+ const env = input.env ?? process.env;
42
+ if (input.argv.includes("--json"))
43
+ return false;
44
+ if (env.RIG_CLI_PLAIN_HELP === "1" || env.RIG_PLAIN === "1")
45
+ return false;
46
+ if (envTruthy(env.CI))
47
+ return false;
48
+ if (!isOpenTuiAppCommand(input.argv, env))
49
+ return false;
50
+ return Boolean(input.stdoutIsTty && input.stdinIsTty);
51
+ }
52
+ export {
53
+ shouldLaunchTtyApp,
54
+ isOpenTuiAppCommand
55
+ };
@@ -7,10 +7,10 @@ function computeStageLayout(width, height) {
7
7
  const horizontalGutter = compact ? 1 : 2;
8
8
  const centerWidth = Math.max(36, safeWidth - horizontalGutter * 2);
9
9
  const centerLeft = horizontalGutter;
10
- const typeBarTop = Math.max(0, safeHeight - 3);
11
10
  const footerTop = Math.max(0, safeHeight - 1);
11
+ const typeBarTop = footerTop;
12
12
  const centerTop = compact ? 1 : Math.max(1, Math.floor(safeHeight * 0.055));
13
- const centerHeight = Math.max(8, typeBarTop - centerTop - 1);
13
+ const centerHeight = Math.max(8, footerTop - centerTop);
14
14
  return { width: safeWidth, height: safeHeight, centerWidth, centerLeft, centerTop, centerHeight, typeBarTop, footerTop, compact };
15
15
  }
16
16
  function visibleWidth(text) {
@@ -143,17 +143,21 @@ function cleanToken(value) {
143
143
  const trimmed = value?.trim();
144
144
  return trimmed ? trimmed : null;
145
145
  }
146
- function readPrivateRemoteSessionToken(projectRoot) {
146
+ function readRemoteAuthState(projectRoot) {
147
147
  const path = resolve2(projectRoot, ".rig", "state", "github-auth.json");
148
148
  if (!existsSync2(path))
149
149
  return null;
150
150
  try {
151
151
  const parsed = JSON.parse(readFileSync2(path, "utf8"));
152
- return cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined);
152
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
153
153
  } catch {
154
154
  return null;
155
155
  }
156
156
  }
157
+ function readPrivateRemoteSessionToken(projectRoot) {
158
+ const parsed = readRemoteAuthState(projectRoot);
159
+ return parsed ? cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined) : null;
160
+ }
157
161
  function readGitHubBearerTokenForRemote(projectRoot) {
158
162
  const scopedKey = resolve2(projectRoot);
159
163
  if (scopedGitHubBearerTokens.has(scopedKey))
@@ -164,15 +168,25 @@ function readGitHubBearerTokenForRemote(projectRoot) {
164
168
  return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
165
169
  }
166
170
  function readStoredGitHubAuthToken(projectRoot) {
167
- const path = resolve2(projectRoot, ".rig", "state", "github-auth.json");
168
- if (!existsSync2(path))
171
+ const parsed = readRemoteAuthState(projectRoot);
172
+ return parsed ? cleanToken(typeof parsed.token === "string" ? parsed.token : undefined) : null;
173
+ }
174
+ function inferRemoteServerProjectRootFromAuthState(projectRoot) {
175
+ const repo = readRepoConnection(projectRoot);
176
+ const slug = repo?.project?.trim();
177
+ if (!slug || !/^[^/]+\/[^/]+$/.test(slug))
169
178
  return null;
170
- try {
171
- const parsed = JSON.parse(readFileSync2(path, "utf8"));
172
- return cleanToken(typeof parsed.token === "string" ? parsed.token : undefined);
173
- } catch {
179
+ const auth = readRemoteAuthState(projectRoot);
180
+ const authUpdatedAt = typeof auth?.updatedAt === "string" ? Date.parse(auth.updatedAt) : Number.NaN;
181
+ const repoLinkedAt = typeof repo?.linkedAt === "string" ? Date.parse(repo.linkedAt) : Number.NaN;
182
+ if (Number.isFinite(authUpdatedAt) && Number.isFinite(repoLinkedAt) && authUpdatedAt + 1000 < repoLinkedAt)
174
183
  return null;
175
- }
184
+ const checkoutBaseDir = typeof auth?.checkoutBaseDir === "string" && auth.checkoutBaseDir.trim() ? auth.checkoutBaseDir.trim() : null;
185
+ if (!checkoutBaseDir)
186
+ return null;
187
+ const inferred = `${checkoutBaseDir.replace(/\/+$/, "")}/${slug}`;
188
+ writeRepoServerProjectRoot(projectRoot, inferred);
189
+ return inferred;
176
190
  }
177
191
  function readLocalConnectionFallbackToken(projectRoot) {
178
192
  return readGitHubBearerTokenForRemote(projectRoot) ?? cleanToken(process.env.RIG_GITHUB_TOKEN) ?? readStoredGitHubAuthToken(projectRoot);
@@ -183,7 +197,7 @@ async function ensureServerForCli(projectRoot) {
183
197
  if (selected?.connection.kind === "remote") {
184
198
  reportServerPhase(`Connecting to ${selected.alias}\u2026`);
185
199
  const authToken = readGitHubBearerTokenForRemote(projectRoot);
186
- const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
200
+ const serverProjectRoot = selected.serverProjectRoot ?? inferRemoteServerProjectRootFromAuthState(projectRoot) ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
187
201
  return {
188
202
  baseUrl: selected.connection.baseUrl,
189
203
  authToken,
@@ -212,7 +226,10 @@ async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken)
212
226
  if (!slug)
213
227
  return null;
214
228
  try {
215
- const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
229
+ const url = new URL(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`);
230
+ if (authToken && queryAuthFallbackEnabled())
231
+ url.searchParams.set("rt", authToken);
232
+ const response = await fetch(url, {
216
233
  headers: mergeHeaders(undefined, authToken)
217
234
  });
218
235
  if (!response.ok)
@@ -229,10 +246,23 @@ async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken)
229
246
  return null;
230
247
  }
231
248
  }
249
+ function mergeCookie(existing, name, value) {
250
+ const encoded = `${name}=${encodeURIComponent(value)}`;
251
+ if (!existing?.trim())
252
+ return encoded;
253
+ const parts = existing.split(";").map((part) => part.trim()).filter((part) => part && !part.startsWith(`${name}=`));
254
+ return [...parts, encoded].join("; ");
255
+ }
256
+ function queryAuthFallbackEnabled(env = process.env) {
257
+ return env.RIG_ENABLE_QUERY_AUTH_FALLBACK === "1" || env.RIG_QUERY_AUTH_FALLBACK === "1";
258
+ }
232
259
  function mergeHeaders(headers, authToken) {
233
260
  const merged = new Headers(headers);
234
261
  if (authToken) {
235
- merged.set("authorization", `Bearer ${authToken}`);
262
+ const bearer = `Bearer ${authToken}`;
263
+ merged.set("authorization", bearer);
264
+ merged.set("x-auth", bearer);
265
+ merged.set("cookie", mergeCookie(merged.get("cookie"), "rig_auth", authToken));
236
266
  }
237
267
  return merged;
238
268
  }
@@ -293,15 +323,34 @@ async function buildServerFailureContext(projectRoot, server) {
293
323
  hint: "Check the selected server with `rig server status`, or switch with `rig server use <alias|local>`."
294
324
  };
295
325
  }
326
+ function isLoopbackRemoteBaseUrl(baseUrl) {
327
+ try {
328
+ const host = new URL(baseUrl).hostname.toLowerCase();
329
+ return host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "[::1]";
330
+ } catch {
331
+ return false;
332
+ }
333
+ }
334
+ function canUseRemoteWithoutProjectRoot(pathname) {
335
+ return pathname === "/health" || pathname === "/api/health" || pathname === "/api/server/status" || pathname === "/api/server/project-root" || pathname.startsWith("/api/github/auth/") || pathname === "/api/projects" || pathname.startsWith("/api/projects/");
336
+ }
296
337
  async function requestServerJson(context, pathname, init = {}) {
297
338
  const server = await ensureServerForCli(context.projectRoot);
339
+ const requestUrl = new URL(`${server.baseUrl}${pathname}`);
340
+ if (server.connectionKind === "remote" && !server.serverProjectRoot && !canUseRemoteWithoutProjectRoot(requestUrl.pathname) && (server.authToken || !isLoopbackRemoteBaseUrl(server.baseUrl))) {
341
+ const repo = readRepoConnection(context.projectRoot);
342
+ throw new CliError(`Remote server is selected for ${repo?.project ?? "this repo"}, but this checkout has no server-host project root link.`, 1, { hint: "Run `rig init --repair` or `rig init --yes --repo owner/repo --server remote` to repair the remote project link before task/run commands." });
343
+ }
298
344
  const headers = mergeHeaders(init.headers, server.authToken);
299
345
  if (server.serverProjectRoot)
300
346
  headers.set("x-rig-project-root", server.serverProjectRoot);
347
+ if (server.connectionKind === "remote" && server.authToken && queryAuthFallbackEnabled()) {
348
+ requestUrl.searchParams.set("rt", server.authToken);
349
+ }
301
350
  reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
302
351
  let response;
303
352
  try {
304
- response = await fetch(`${server.baseUrl}${pathname}`, {
353
+ response = await fetch(requestUrl, {
305
354
  ...init,
306
355
  headers
307
356
  });
@@ -711,6 +760,7 @@ async function attachRunBundledPiFrontend(context, input) {
711
760
  let detached = false;
712
761
  try {
713
762
  await runPiMain([
763
+ "--offline",
714
764
  "--no-extensions",
715
765
  "--no-skills",
716
766
  "--no-prompt-templates",
@@ -751,9 +801,9 @@ function parseArgs(argv) {
751
801
  const runId = takeOption(argv, "--run-id")?.trim();
752
802
  const projectRoot = takeOption(argv, "--project-root")?.trim();
753
803
  if (!runId)
754
- throw new Error("Missing --run-id for Rig Pi host child.");
804
+ throw new Error("Missing --run-id for Pi host child.");
755
805
  if (!projectRoot)
756
- throw new Error("Missing --project-root for Rig Pi host child.");
806
+ throw new Error("Missing --project-root for Pi host child.");
757
807
  return { runId, projectRoot };
758
808
  }
759
809
  async function runOpenTuiPiHostChild(argv = process.argv.slice(2)) {
@@ -767,7 +817,7 @@ if (import.meta.main) {
767
817
  } catch (error) {
768
818
  const message = error instanceof Error ? error.stack || error.message : String(error);
769
819
  process.stderr.write(`
770
- Rig Pi host failed:
820
+ Pi host failed:
771
821
  ${message}
772
822
  `);
773
823
  process.exit(1);
@@ -1,5 +1,14 @@
1
1
  import { StyledText } from "@opentui/core";
2
2
  export type PiPtyHostStatus = "starting" | "running" | "exited" | "failed";
3
+ /**
4
+ * Temporary embedded-Pi presentation contract.
5
+ *
6
+ * The worker/remote Pi daemon still owns tools, skills, cwd, model, session and
7
+ * context. This local host only mirrors a PTY/xterm viewport into selectable
8
+ * OpenTUI text. Keep this snapshot path explicit until Rig grows a dedicated
9
+ * PTY/xterm viewport renderable or deliberately suspends OpenTUI for direct Pi
10
+ * terminal ownership.
11
+ */
3
12
  export type PiPtyHostSnapshot = {
4
13
  readonly runId: string;
5
14
  readonly status: PiPtyHostStatus;
@@ -7,7 +7,9 @@ import { Terminal as XtermTerminal } from "@xterm/headless";
7
7
  var MIN_COLS = 40;
8
8
  var MIN_ROWS = 12;
9
9
  var MAX_ROWS = 300;
10
- var MAX_SNAPSHOT_LINES = 1200;
10
+ var MAX_SNAPSHOT_LINES = 360;
11
+ var STYLED_SNAPSHOT_MARGIN = 6;
12
+ var SNAPSHOT_DELAY_MS = 120;
11
13
  var fallbackChildScriptPath = fileURLToPath(new URL("./pi-host-child.ts", import.meta.url));
12
14
  var activeHost = null;
13
15
  function clampCols(cols) {
@@ -105,17 +107,15 @@ function lineToStyledText(line, cols) {
105
107
  function childCommandPrefix() {
106
108
  const execName = basename(process.execPath).toLowerCase();
107
109
  const currentEntry = process.argv[1];
108
- if ((execName === "bun" || execName === "bun.exe") && currentEntry) {
109
- return [process.execPath, currentEntry, "__opentui-pi-host"];
110
+ if (execName === "bun" || execName === "bun.exe") {
111
+ return currentEntry ? [process.execPath, currentEntry, "__opentui-pi-host"] : [process.execPath, fallbackChildScriptPath];
110
112
  }
111
- if (currentEntry)
112
- return [process.execPath, "__opentui-pi-host"];
113
- return [process.execPath, fallbackChildScriptPath];
113
+ return [process.execPath, "__opentui-pi-host"];
114
114
  }
115
115
  function withEnv(base) {
116
116
  const env = {};
117
117
  for (const [key, value] of Object.entries(base ?? process.env)) {
118
- if (key === "RIG_PLAIN" || key === "RIG_CLI_PLAIN_HELP")
118
+ if (key === "RIG_PLAIN" || key === "RIG_CLI_PLAIN_HELP" || key === "NO_COLOR")
119
119
  continue;
120
120
  if (typeof value === "string")
121
121
  env[key] = value;
@@ -174,7 +174,7 @@ class PiPtyHost {
174
174
  allowProposedApi: true,
175
175
  cols: this.cols,
176
176
  rows: this.rows,
177
- scrollback: 5000
177
+ scrollback: 1000
178
178
  });
179
179
  this.registerTerminalResponders();
180
180
  }
@@ -188,7 +188,7 @@ class PiPtyHost {
188
188
  if (this._disposed)
189
189
  throw new Error("Pi PTY host is disposed.");
190
190
  if (typeof Bun.Terminal !== "function") {
191
- throw new Error("Bun native PTY is unavailable in this runtime. Rig OpenTUI Pi host requires Bun.Terminal.");
191
+ throw new Error("Bun native PTY is unavailable in this runtime. Pi host requires Bun.Terminal.");
192
192
  }
193
193
  const spawnOptions = {
194
194
  cwd: this.projectRoot,
@@ -212,7 +212,7 @@ class PiPtyHost {
212
212
  this.proc = proc;
213
213
  this.pty = proc.terminal;
214
214
  this.status = "running";
215
- this.message = "bundled Pi running inside Rig";
215
+ this.message = "bundled Pi running inside this app";
216
216
  this.emitSnapshotSoon(0);
217
217
  proc.exited.then((exitCode) => {
218
218
  if (this._disposed)
@@ -307,7 +307,7 @@ class PiPtyHost {
307
307
  this.respondToRawTerminalQueries(text);
308
308
  this.terminal.write(data, () => this.emitSnapshotSoon());
309
309
  }
310
- emitSnapshotSoon(delayMs = 16) {
310
+ emitSnapshotSoon(delayMs = SNAPSHOT_DELAY_MS) {
311
311
  if (this._disposed || this.notifyTimer)
312
312
  return;
313
313
  this.notifyTimer = setTimeout(() => {
@@ -321,16 +321,18 @@ class PiPtyHost {
321
321
  const buffer = this.terminal.buffer.active;
322
322
  const end = buffer.length;
323
323
  const start = Math.max(0, end - MAX_SNAPSHOT_LINES);
324
+ const styledStart = Math.max(start, end - this.rows - STYLED_SNAPSHOT_MARGIN);
324
325
  const lines = [];
325
326
  const styledLines = [];
326
327
  for (let row = start;row < end; row += 1) {
327
328
  const line = buffer.getLine(row);
328
329
  lines.push((line?.translateToString(false) ?? "").slice(0, this.cols));
329
- styledLines.push(lineToStyledText(line, this.cols));
330
+ if (row >= styledStart)
331
+ styledLines[lines.length - 1] = lineToStyledText(line, this.cols);
330
332
  }
331
333
  while (lines.length < this.rows) {
332
334
  lines.push("");
333
- styledLines.push(new StyledText([{ __isChunk: true, text: "" }]));
335
+ styledLines[lines.length - 1] = new StyledText([{ __isChunk: true, text: "" }]);
334
336
  }
335
337
  return {
336
338
  runId: this.runId,