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

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 (163) hide show
  1. package/dist/bin/rig.js +22085 -14618
  2. package/dist/src/app/board.js +155 -28
  3. package/dist/src/app-opentui/adapters/doctor.d.ts +0 -2
  4. package/dist/src/app-opentui/adapters/doctor.js +1 -25
  5. package/dist/src/app-opentui/adapters/family.d.ts +62 -0
  6. package/dist/src/app-opentui/adapters/family.js +14305 -0
  7. package/dist/src/app-opentui/adapters/fleet.d.ts +0 -2
  8. package/dist/src/app-opentui/adapters/fleet.js +6 -40
  9. package/dist/src/app-opentui/adapters/inbox.d.ts +12 -2
  10. package/dist/src/app-opentui/adapters/inbox.js +54 -59
  11. package/dist/src/app-opentui/adapters/init.d.ts +0 -2
  12. package/dist/src/app-opentui/adapters/init.js +0 -26
  13. package/dist/src/app-opentui/adapters/inspect.d.ts +52 -0
  14. package/dist/src/app-opentui/adapters/inspect.js +1024 -0
  15. package/dist/src/app-opentui/adapters/pi-attach.d.ts +15 -6
  16. package/dist/src/app-opentui/adapters/pi-attach.js +348 -93
  17. package/dist/src/app-opentui/adapters/pi.d.ts +23 -0
  18. package/dist/src/app-opentui/adapters/pi.js +363 -0
  19. package/dist/src/app-opentui/adapters/plugin.d.ts +84 -0
  20. package/dist/src/app-opentui/adapters/plugin.js +544 -0
  21. package/dist/src/app-opentui/adapters/repo.d.ts +37 -0
  22. package/dist/src/app-opentui/adapters/repo.js +186 -0
  23. package/dist/src/app-opentui/adapters/run-detail.d.ts +9 -2
  24. package/dist/src/app-opentui/adapters/run-detail.js +97 -44
  25. package/dist/src/app-opentui/adapters/server.d.ts +10 -2
  26. package/dist/src/app-opentui/adapters/server.js +96 -27
  27. package/dist/src/app-opentui/adapters/tasks.d.ts +0 -2
  28. package/dist/src/app-opentui/adapters/tasks.js +437 -105
  29. package/dist/src/app-opentui/adapters/workspace.d.ts +49 -0
  30. package/dist/src/app-opentui/adapters/workspace.js +333 -0
  31. package/dist/src/app-opentui/autocomplete.d.ts +20 -0
  32. package/dist/src/app-opentui/autocomplete.js +576 -0
  33. package/dist/src/app-opentui/bootstrap.js +24296 -16919
  34. package/dist/src/app-opentui/command-palette.d.ts +3 -0
  35. package/dist/src/app-opentui/command-palette.js +1010 -0
  36. package/dist/src/app-opentui/drone.js +8 -6
  37. package/dist/src/app-opentui/fleet-stats.d.ts +32 -0
  38. package/dist/src/app-opentui/fleet-stats.js +114 -0
  39. package/dist/src/app-opentui/index.js +3438 -1724
  40. package/dist/src/app-opentui/intent.js +71 -48
  41. package/dist/src/app-opentui/keymap.d.ts +6 -5
  42. package/dist/src/app-opentui/keymap.js +1064 -23
  43. package/dist/src/app-opentui/layout.d.ts +7 -0
  44. package/dist/src/app-opentui/layout.js +13 -6
  45. package/dist/src/app-opentui/list-search.d.ts +24 -0
  46. package/dist/src/app-opentui/list-search.js +88 -1
  47. package/dist/src/app-opentui/pi-host-child.js +33 -1
  48. package/dist/src/app-opentui/pi-pty-host.d.ts +5 -0
  49. package/dist/src/app-opentui/pi-pty-host.js +21 -3
  50. package/dist/src/app-opentui/react/App.d.ts +9 -0
  51. package/dist/src/app-opentui/react/App.js +5144 -0
  52. package/dist/src/app-opentui/react/Backdrop.d.ts +5 -0
  53. package/dist/src/app-opentui/react/Backdrop.js +1834 -0
  54. package/dist/src/app-opentui/react/ChromeHost.d.ts +5 -0
  55. package/dist/src/app-opentui/react/ChromeHost.js +2942 -0
  56. package/dist/src/app-opentui/react/SceneFrameView.d.ts +7 -0
  57. package/dist/src/app-opentui/react/SceneFrameView.js +529 -0
  58. package/dist/src/app-opentui/react/context.d.ts +17 -0
  59. package/dist/src/app-opentui/react/context.js +37 -0
  60. package/dist/src/app-opentui/react/launch.d.ts +2 -0
  61. package/dist/src/app-opentui/react/launch.js +5743 -0
  62. package/dist/src/app-opentui/react/nav.d.ts +18 -0
  63. package/dist/src/app-opentui/react/nav.js +54 -0
  64. package/dist/src/app-opentui/react/scroll.d.ts +12 -0
  65. package/dist/src/app-opentui/react/scroll.js +21 -0
  66. package/dist/src/app-opentui/react/syntax.d.ts +2 -0
  67. package/dist/src/app-opentui/react/syntax.js +64 -0
  68. package/dist/src/app-opentui/registry.js +19923 -5151
  69. package/dist/src/app-opentui/render/constants.d.ts +31 -0
  70. package/dist/src/app-opentui/render/constants.js +66 -0
  71. package/dist/src/app-opentui/render/graphics.d.ts +2 -1
  72. package/dist/src/app-opentui/render/graphics.js +106 -39
  73. package/dist/src/app-opentui/render/image-visual-layer-worker.js +108 -20
  74. package/dist/src/app-opentui/render/image-visual-layer.d.ts +7 -0
  75. package/dist/src/app-opentui/render/image-visual-layer.js +109 -20
  76. package/dist/src/app-opentui/render/native-host.d.ts +37 -0
  77. package/dist/src/app-opentui/render/native-host.js +179 -0
  78. package/dist/src/app-opentui/render/panels.js +18 -11
  79. package/dist/src/app-opentui/render/preloader.js +8 -6
  80. package/dist/src/app-opentui/render/scene.d.ts +50 -1
  81. package/dist/src/app-opentui/render/scene.js +183 -6
  82. package/dist/src/app-opentui/render/text.d.ts +7 -1
  83. package/dist/src/app-opentui/render/text.js +10 -7
  84. package/dist/src/app-opentui/render/type-bar.js +69 -30
  85. package/dist/src/app-opentui/runtime.d.ts +44 -1
  86. package/dist/src/app-opentui/runtime.js +3231 -1363
  87. package/dist/src/app-opentui/scenes/command.js +20 -6
  88. package/dist/src/app-opentui/scenes/doctor.js +176 -11
  89. package/dist/src/app-opentui/scenes/error.js +20 -10
  90. package/dist/src/app-opentui/scenes/family-domains/agent.d.ts +2 -0
  91. package/dist/src/app-opentui/scenes/family-domains/agent.js +348 -0
  92. package/dist/src/app-opentui/scenes/family-domains/browser.d.ts +2 -0
  93. package/dist/src/app-opentui/scenes/family-domains/browser.js +195 -0
  94. package/dist/src/app-opentui/scenes/family-domains/dist.d.ts +2 -0
  95. package/dist/src/app-opentui/scenes/family-domains/dist.js +243 -0
  96. package/dist/src/app-opentui/scenes/family-domains/git.d.ts +2 -0
  97. package/dist/src/app-opentui/scenes/family-domains/git.js +195 -0
  98. package/dist/src/app-opentui/scenes/family-domains/github.d.ts +2 -0
  99. package/dist/src/app-opentui/scenes/family-domains/github.js +274 -0
  100. package/dist/src/app-opentui/scenes/family-domains/harness.d.ts +2 -0
  101. package/dist/src/app-opentui/scenes/family-domains/harness.js +152 -0
  102. package/dist/src/app-opentui/scenes/family-domains/index.d.ts +4 -0
  103. package/dist/src/app-opentui/scenes/family-domains/index.js +1679 -0
  104. package/dist/src/app-opentui/scenes/family-domains/kit.d.ts +76 -0
  105. package/dist/src/app-opentui/scenes/family-domains/kit.js +305 -0
  106. package/dist/src/app-opentui/scenes/family-domains/profile.d.ts +2 -0
  107. package/dist/src/app-opentui/scenes/family-domains/profile.js +212 -0
  108. package/dist/src/app-opentui/scenes/family-domains/queue.d.ts +2 -0
  109. package/dist/src/app-opentui/scenes/family-domains/queue.js +146 -0
  110. package/dist/src/app-opentui/scenes/family-domains/remote.d.ts +2 -0
  111. package/dist/src/app-opentui/scenes/family-domains/remote.js +518 -0
  112. package/dist/src/app-opentui/scenes/family-domains/review.d.ts +2 -0
  113. package/dist/src/app-opentui/scenes/family-domains/review.js +280 -0
  114. package/dist/src/app-opentui/scenes/family-domains/setup.d.ts +2 -0
  115. package/dist/src/app-opentui/scenes/family-domains/setup.js +267 -0
  116. package/dist/src/app-opentui/scenes/family-domains/stats.d.ts +2 -0
  117. package/dist/src/app-opentui/scenes/family-domains/stats.js +370 -0
  118. package/dist/src/app-opentui/scenes/family.d.ts +3 -0
  119. package/dist/src/app-opentui/scenes/family.js +2144 -0
  120. package/dist/src/app-opentui/scenes/fleet.js +521 -31
  121. package/dist/src/app-opentui/scenes/handoff.js +204 -12
  122. package/dist/src/app-opentui/scenes/help.js +609 -40
  123. package/dist/src/app-opentui/scenes/inbox.js +278 -17
  124. package/dist/src/app-opentui/scenes/init.js +84 -39
  125. package/dist/src/app-opentui/scenes/inspect.d.ts +3 -0
  126. package/dist/src/app-opentui/scenes/inspect.js +793 -0
  127. package/dist/src/app-opentui/scenes/main.js +218 -39
  128. package/dist/src/app-opentui/scenes/pi.d.ts +3 -0
  129. package/dist/src/app-opentui/scenes/pi.js +508 -0
  130. package/dist/src/app-opentui/scenes/plugin.d.ts +3 -0
  131. package/dist/src/app-opentui/scenes/plugin.js +486 -0
  132. package/dist/src/app-opentui/scenes/repo.d.ts +3 -0
  133. package/dist/src/app-opentui/scenes/repo.js +424 -0
  134. package/dist/src/app-opentui/scenes/run-detail.js +333 -20
  135. package/dist/src/app-opentui/scenes/server.js +181 -15
  136. package/dist/src/app-opentui/scenes/tasks.js +486 -31
  137. package/dist/src/app-opentui/scenes/workspace.d.ts +3 -0
  138. package/dist/src/app-opentui/scenes/workspace.js +426 -0
  139. package/dist/src/app-opentui/selectable.d.ts +19 -0
  140. package/dist/src/app-opentui/selectable.js +79 -0
  141. package/dist/src/app-opentui/state.js +83 -30
  142. package/dist/src/app-opentui/surface-catalog.d.ts +20 -0
  143. package/dist/src/app-opentui/surface-catalog.js +540 -0
  144. package/dist/src/app-opentui/theme.d.ts +28 -6
  145. package/dist/src/app-opentui/theme.js +61 -8
  146. package/dist/src/app-opentui/types.d.ts +121 -4
  147. package/dist/src/commands/_authority-runs.d.ts +1 -1
  148. package/dist/src/commands/_authority-runs.js +2 -12
  149. package/dist/src/commands/_help-catalog.js +95 -15
  150. package/dist/src/commands/_operator-view.js +34 -2
  151. package/dist/src/commands/_pi-frontend.d.ts +2 -0
  152. package/dist/src/commands/_pi-frontend.js +34 -0
  153. package/dist/src/commands/_server-events.d.ts +26 -0
  154. package/dist/src/commands/_server-events.js +310 -0
  155. package/dist/src/commands/agent.js +2 -12
  156. package/dist/src/commands/run.js +34 -2
  157. package/dist/src/commands/server.js +2 -12
  158. package/dist/src/commands/stats.js +95 -15
  159. package/dist/src/commands/task-run-driver.js +2 -12
  160. package/dist/src/commands/task.js +131 -29
  161. package/dist/src/commands.js +192 -31
  162. package/dist/src/index.js +192 -31
  163. package/package.json +11 -9
@@ -0,0 +1,186 @@
1
+ // @bun
2
+ var __require = import.meta.require;
3
+
4
+ // packages/cli/src/app-opentui/adapters/common.ts
5
+ function projectRootOf(ctx) {
6
+ const root = ctx.rig?.projectRoot ?? ctx.projectRoot ?? ctx.getState().projectRoot;
7
+ if (typeof root === "string" && root.trim())
8
+ return root;
9
+ throw new Error("App adapter requires a projectRoot.");
10
+ }
11
+ function normalizeAppError(error) {
12
+ const message = error instanceof Error ? error.message : String(error);
13
+ const rawHint = error instanceof Error ? error.hint : undefined;
14
+ return {
15
+ message,
16
+ ...typeof rawHint === "string" && rawHint.trim() ? { hint: rawHint.trim() } : {}
17
+ };
18
+ }
19
+ function emitStarted(ctx, label, optimistic) {
20
+ ctx.emit({ type: "action.started", label, ...optimistic ? { optimistic } : {} });
21
+ }
22
+ function emitProgress(ctx, label, detail, data) {
23
+ ctx.emit({ type: "action.progress", label, ...detail ? { detail } : {}, ...data ? { data } : {} });
24
+ }
25
+ function emitCompleted(ctx, label, data) {
26
+ ctx.emit({ type: "action.completed", label, ...data ? { data } : {} });
27
+ }
28
+ function emitFailed(ctx, label, error, data) {
29
+ if (data)
30
+ patchData(ctx, data);
31
+ const normalized = normalizeAppError(error);
32
+ ctx.emit({
33
+ type: "action.failed",
34
+ label,
35
+ message: normalized.message,
36
+ ...normalized.hint ? { hint: normalized.hint } : {},
37
+ cause: error
38
+ });
39
+ }
40
+ function patchData(ctx, data) {
41
+ ctx.emit({ type: "data.patch", data });
42
+ }
43
+ function patchFooter(ctx, footer) {
44
+ ctx.emit({ type: "footer.patch", footer });
45
+ }
46
+ async function captureConsole(fn) {
47
+ const output = [];
48
+ const original = {
49
+ log: console.log,
50
+ info: console.info,
51
+ warn: console.warn,
52
+ error: console.error
53
+ };
54
+ const capture = (...parts) => {
55
+ output.push(parts.map((part) => typeof part === "string" ? part : JSON.stringify(part)).join(" "));
56
+ };
57
+ console.log = capture;
58
+ console.info = capture;
59
+ console.warn = capture;
60
+ console.error = capture;
61
+ try {
62
+ const result = await fn();
63
+ return { result, output };
64
+ } finally {
65
+ console.log = original.log;
66
+ console.info = original.info;
67
+ console.warn = original.warn;
68
+ console.error = original.error;
69
+ }
70
+ }
71
+
72
+ // packages/cli/src/app-opentui/adapters/repo.ts
73
+ function repoActionRequest(ctx) {
74
+ const intent = ctx.getState().intent;
75
+ const payload = intent?.action?.payload;
76
+ if (!payload)
77
+ return { kind: "none" };
78
+ if (payload.repoReset === true)
79
+ return { kind: "reset" };
80
+ const alias = payload.repoSync;
81
+ if (typeof alias === "string" && alias.trim())
82
+ return { kind: "sync", alias: alias.trim() };
83
+ return { kind: "none" };
84
+ }
85
+ function readRepoGitStatus(projectRoot, checkoutRoot, pinnedCommit, runCapture, existsSync, joinPath) {
86
+ const unavailable = { available: false, clean: false, dirtyCount: 0, head: "", branch: "", ahead: 0, behind: 0 };
87
+ if (!checkoutRoot || !existsSync(joinPath(checkoutRoot, ".git")))
88
+ return unavailable;
89
+ const porcelain = runCapture(["git", "-C", checkoutRoot, "status", "--porcelain"], projectRoot);
90
+ if (porcelain.exitCode !== 0)
91
+ return unavailable;
92
+ const dirtyLines = porcelain.stdout.split(/\r?\n/).filter((row) => row.trim().length > 0);
93
+ const head = runCapture(["git", "-C", checkoutRoot, "rev-parse", "--short", "HEAD"], projectRoot).stdout.trim();
94
+ const branchRaw = runCapture(["git", "-C", checkoutRoot, "rev-parse", "--abbrev-ref", "HEAD"], projectRoot).stdout.trim();
95
+ let ahead = 0;
96
+ let behind = 0;
97
+ if (pinnedCommit) {
98
+ const counts = runCapture(["git", "-C", checkoutRoot, "rev-list", "--left-right", "--count", `${pinnedCommit}...HEAD`], projectRoot);
99
+ if (counts.exitCode === 0) {
100
+ const [behindStr = "0", aheadStr = "0"] = counts.stdout.trim().split(/\s+/);
101
+ behind = Number.parseInt(behindStr, 10) || 0;
102
+ ahead = Number.parseInt(aheadStr, 10) || 0;
103
+ }
104
+ }
105
+ return {
106
+ available: true,
107
+ clean: dirtyLines.length === 0,
108
+ dirtyCount: dirtyLines.length,
109
+ head,
110
+ branch: branchRaw && branchRaw !== "HEAD" ? branchRaw : "(detached)",
111
+ ahead,
112
+ behind
113
+ };
114
+ }
115
+ async function refreshRepo(ctx) {
116
+ const request = repoActionRequest(ctx);
117
+ const label = request.kind === "sync" ? `Syncing ${request.alias}` : request.kind === "reset" ? "Resetting repo baseline" : "Loading repo state";
118
+ emitStarted(ctx, label);
119
+ try {
120
+ const projectRoot = projectRootOf(ctx);
121
+ const [
122
+ { repoBaseline, resetBaseline },
123
+ { currentTaskId },
124
+ { listManagedRepoEntries, resolveManagedRepoIdByAlias },
125
+ { syncManagedRepo, resolveManagedRepoLayoutByAlias },
126
+ { runCapture },
127
+ { existsSync },
128
+ pathMod
129
+ ] = await Promise.all([
130
+ import("@rig/runtime/control-plane/native/repo-ops"),
131
+ import("@rig/runtime/control-plane/native/task-state"),
132
+ import("@rig/runtime/control-plane/repos/registry"),
133
+ import("@rig/runtime/control-plane/repos"),
134
+ import("@rig/runtime/control-plane/native/utils"),
135
+ import("fs"),
136
+ import("path")
137
+ ]);
138
+ const joinPath = (...parts) => pathMod.resolve(...parts);
139
+ if (request.kind === "sync") {
140
+ const repoId = resolveManagedRepoIdByAlias(request.alias);
141
+ if (!repoId)
142
+ throw new Error(`No managed repo registered for alias ${request.alias}.`);
143
+ emitProgress(ctx, label, `re-syncing ${request.alias} from its remote default branch`);
144
+ await captureConsole(() => syncManagedRepo(projectRoot, repoId));
145
+ } else if (request.kind === "reset") {
146
+ emitProgress(ctx, label, "resetting baseline to origin/main and refreshing pins");
147
+ await captureConsole(() => resetBaseline(projectRoot, false));
148
+ }
149
+ emitProgress(ctx, label, "reading baseline pins and managed repos");
150
+ const { result } = await captureConsole(() => ({
151
+ pins: repoBaseline(projectRoot, false),
152
+ taskId: currentTaskId(projectRoot),
153
+ managed: listManagedRepoEntries()
154
+ }));
155
+ const baselinePins = Object.entries(result.pins).map(([alias, commit]) => ({ alias, commit: String(commit) }));
156
+ const pinByAlias = new Map(baselinePins.map((pin) => [pin.alias, pin.commit]));
157
+ const managedRepos = result.managed.map((entry) => {
158
+ const layout = resolveManagedRepoLayoutByAlias(projectRoot, entry.alias);
159
+ const git = layout ? readRepoGitStatus(projectRoot, layout.checkoutRoot, pinByAlias.get(entry.alias), runCapture, existsSync, joinPath) : undefined;
160
+ return {
161
+ id: entry.id,
162
+ alias: entry.alias,
163
+ defaultBranch: entry.defaultBranch,
164
+ defaultRemoteUrl: entry.defaultRemoteUrl,
165
+ ...git ? { git } : {}
166
+ };
167
+ });
168
+ const snapshot = {
169
+ activeTaskId: result.taskId.trim() ? result.taskId.trim() : null,
170
+ baselinePins,
171
+ managedRepos,
172
+ flatLayout: managedRepos.length === 0,
173
+ refreshedAt: Date.now()
174
+ };
175
+ patchData(ctx, { repo: snapshot, lastRefreshError: undefined });
176
+ patchFooter(ctx, { message: snapshot.flatLayout ? "flat layout \xB7 no managed repos" : `${managedRepos.length} managed repos \xB7 ${baselinePins.length} pins` });
177
+ emitCompleted(ctx, label, { managed: managedRepos.length, pins: baselinePins.length, task: snapshot.activeTaskId });
178
+ return snapshot;
179
+ } catch (error) {
180
+ emitFailed(ctx, label, error);
181
+ throw error;
182
+ }
183
+ }
184
+ export {
185
+ refreshRepo
186
+ };
@@ -1,6 +1,4 @@
1
- import type { AppAdapter } from "../types";
2
1
  import { type AppAdapterContext } from "./common";
3
- export declare function createRunDetailAdapter(): AppAdapter;
4
2
  export type AppRunSnapshot = {
5
3
  readonly runId: string;
6
4
  readonly status: string;
@@ -9,10 +7,19 @@ export type AppRunSnapshot = {
9
7
  readonly logs: Record<string, unknown>[];
10
8
  readonly timeline: Record<string, unknown>[];
11
9
  readonly timelineCursor: string | null;
10
+ /** Server-reported totals (when available) so the scene can show "N of M" and
11
+ * decide whether a "load more" affordance is warranted (#2). */
12
+ readonly logsTotal?: number;
13
+ readonly timelineTotal?: number;
12
14
  };
13
15
  export declare function loadRunDetail(ctx: AppAdapterContext, runId: string, options?: {
14
16
  readonly timelineCursor?: string | null;
15
17
  }): Promise<AppRunSnapshot>;
18
+ /** "Load more" for the timeline (#2): fetch the NEXT page using the stored
19
+ * `timelineCursor` and APPEND it to the existing snapshot, advancing the cursor.
20
+ * The previously-loaded rows stay; nothing is re-fetched. No cursor ⇒ nothing
21
+ * more to load (the scene only offers the affordance while a cursor exists). */
22
+ export declare function loadMoreRunTimeline(ctx: AppAdapterContext, runId: string): Promise<AppRunSnapshot | null>;
16
23
  export declare function steerRunFromApp(ctx: AppAdapterContext, input: {
17
24
  readonly runId: string;
18
25
  readonly message: string;
@@ -767,15 +767,6 @@ var init__server_client = __esm(() => {
767
767
  });
768
768
 
769
769
  // packages/cli/src/app-opentui/adapters/common.ts
770
- function adapterContextFromRuntime(runtime, renderer) {
771
- return {
772
- projectRoot: runtime.getState().projectRoot,
773
- renderer,
774
- ensureRuntime: () => runtime.getRunnerContext(),
775
- emit: (event) => runtime.emit(event),
776
- getState: () => runtime.getState()
777
- };
778
- }
779
770
  function projectRootOf(ctx) {
780
771
  const root = ctx.rig?.projectRoot ?? ctx.projectRoot ?? ctx.getState().projectRoot;
781
772
  if (typeof root === "string" && root.trim())
@@ -838,33 +829,17 @@ function stringField(record, keys, fallback = "") {
838
829
  }
839
830
  return fallback;
840
831
  }
841
- function payloadString(intent, key) {
842
- const value = intent.action.payload?.[key];
843
- return typeof value === "string" && value.trim() ? value.trim() : undefined;
844
- }
845
832
 
846
833
  // packages/cli/src/app-opentui/adapters/run-detail.ts
847
- function createRunDetailAdapter() {
848
- return {
849
- id: "run-detail",
850
- async handleIntent(runtime, intent) {
851
- if (intent.scene !== "run-detail" && intent.action.kind !== "run-steer")
852
- return false;
853
- const ctx = adapterContextFromRuntime(runtime);
854
- const runId = payloadString(intent, "runId") ?? runtime.getState().data.selectedRunId;
855
- if (intent.action.kind === "run-steer") {
856
- const message = payloadString(intent, "message") ?? intent.raw;
857
- if (runId && message)
858
- await steerRunFromApp(ctx, { runId, message });
859
- return true;
860
- }
861
- if (runId) {
862
- await loadRunDetail(ctx, runId);
863
- return true;
864
- }
865
- return false;
866
- }
867
- };
834
+ var LOG_PAGE = 100;
835
+ var TIMELINE_PAGE = 200;
836
+ function payloadTotal(payload) {
837
+ for (const key of ["total", "totalCount", "count"]) {
838
+ const value = payload[key];
839
+ if (typeof value === "number" && Number.isFinite(value))
840
+ return value;
841
+ }
842
+ return;
868
843
  }
869
844
  function unwrapRun(payload) {
870
845
  return payload.run && typeof payload.run === "object" && !Array.isArray(payload.run) ? payload.run : payload;
@@ -873,8 +848,24 @@ function nextCursor(payload) {
873
848
  const cursor = payload.nextCursor ?? payload.cursor;
874
849
  return typeof cursor === "string" && cursor.trim() ? cursor : null;
875
850
  }
851
+ function loadMoreRequest(ctx, runId) {
852
+ const value = ctx.getState().data?.runDetailLoadMore;
853
+ if (!value || typeof value !== "object" || Array.isArray(value))
854
+ return null;
855
+ const record = value;
856
+ if (record.runId !== runId || typeof record.cursor !== "string" || !record.cursor.trim())
857
+ return null;
858
+ return { runId, cursor: record.cursor };
859
+ }
876
860
  async function loadRunDetail(ctx, runId, options = {}) {
877
861
  const cleanRunId = runId.trim();
862
+ const more = loadMoreRequest(ctx, cleanRunId);
863
+ if (more) {
864
+ patchData(ctx, { runDetailLoadMore: undefined });
865
+ const appended = await loadMoreRunTimeline(ctx, cleanRunId).catch(() => null);
866
+ if (appended)
867
+ return appended;
868
+ }
878
869
  const label = `Loading run ${cleanRunId.slice(0, 8)}`;
879
870
  if (!cleanRunId) {
880
871
  const error = new Error("Run detail requires a run id.");
@@ -888,10 +879,12 @@ async function loadRunDetail(ctx, runId, options = {}) {
888
879
  emitProgress(ctx, label, "loading run, logs, and timeline");
889
880
  const [runPayload, logsPayload, timelinePayload] = await Promise.all([
890
881
  getRunDetailsViaServer2(context, cleanRunId),
891
- getRunLogsViaServer2(context, cleanRunId, { limit: 100 }),
892
- getRunTimelineViaServer2(context, cleanRunId, { limit: 200, ...options.timelineCursor ? { cursor: options.timelineCursor } : {} })
882
+ getRunLogsViaServer2(context, cleanRunId, { limit: LOG_PAGE }),
883
+ getRunTimelineViaServer2(context, cleanRunId, { limit: TIMELINE_PAGE, ...options.timelineCursor ? { cursor: options.timelineCursor } : {} })
893
884
  ]);
894
885
  const run = unwrapRun(runPayload);
886
+ const logsTotal = payloadTotal(logsPayload);
887
+ const timelineTotal = payloadTotal(timelinePayload);
895
888
  const snapshot = {
896
889
  runId: cleanRunId,
897
890
  status: stringField(run, ["status", "state"], "unknown"),
@@ -899,9 +892,12 @@ async function loadRunDetail(ctx, runId, options = {}) {
899
892
  run,
900
893
  logs: arrayFromPayload(logsPayload),
901
894
  timeline: arrayFromPayload(timelinePayload),
902
- timelineCursor: nextCursor(timelinePayload)
895
+ timelineCursor: nextCursor(timelinePayload),
896
+ ...logsTotal !== undefined ? { logsTotal } : {},
897
+ ...timelineTotal !== undefined ? { timelineTotal } : {}
903
898
  };
904
- patchData(ctx, { selectedRunId: cleanRunId, runDetail: snapshot });
899
+ const clearSettledSteer = settledSteerForRun(ctx, cleanRunId) ? { pendingRunMessage: undefined } : {};
900
+ patchData(ctx, { selectedRunId: cleanRunId, runDetail: snapshot, lastRefreshError: undefined, ...clearSettledSteer });
905
901
  patchFooter(ctx, { run: `${cleanRunId.slice(0, 8)} ${snapshot.status}` });
906
902
  emitCompleted(ctx, label, { runId: cleanRunId, status: snapshot.status, logs: snapshot.logs.length, timeline: snapshot.timeline.length });
907
903
  return snapshot;
@@ -910,6 +906,56 @@ async function loadRunDetail(ctx, runId, options = {}) {
910
906
  throw error;
911
907
  }
912
908
  }
909
+ function settledSteerForRun(ctx, runId) {
910
+ const value = ctx.getState().data?.pendingRunMessage;
911
+ if (!value || typeof value !== "object" || Array.isArray(value))
912
+ return false;
913
+ const record = value;
914
+ if (record.runId !== runId)
915
+ return false;
916
+ return record.status === "sent" || record.status === "delivered" || record.status === "failed";
917
+ }
918
+ function currentSnapshot(ctx) {
919
+ const value = ctx.getState().data?.runDetail;
920
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
921
+ }
922
+ async function loadMoreRunTimeline(ctx, runId) {
923
+ const cleanRunId = runId.trim();
924
+ const snapshot = currentSnapshot(ctx);
925
+ if (!snapshot || snapshot.runId !== cleanRunId || !snapshot.timelineCursor)
926
+ return snapshot;
927
+ const label = `More timeline ${cleanRunId.slice(0, 8)}`;
928
+ emitStarted(ctx, label);
929
+ try {
930
+ const { getRunTimelineViaServer: getRunTimelineViaServer2 } = await Promise.resolve().then(() => (init__server_client(), exports__server_client));
931
+ const context = { projectRoot: projectRootOf(ctx) };
932
+ const page = await getRunTimelineViaServer2(context, cleanRunId, { limit: TIMELINE_PAGE, cursor: snapshot.timelineCursor });
933
+ const more = arrayFromPayload(page);
934
+ const timelineTotal = payloadTotal(page) ?? snapshot.timelineTotal;
935
+ const next = {
936
+ ...snapshot,
937
+ timeline: [...snapshot.timeline, ...more],
938
+ timelineCursor: nextCursor(page),
939
+ ...timelineTotal !== undefined ? { timelineTotal } : {}
940
+ };
941
+ patchData(ctx, { runDetail: next, lastRefreshError: undefined });
942
+ emitCompleted(ctx, label, { runId: cleanRunId, added: more.length, timeline: next.timeline.length });
943
+ return next;
944
+ } catch (error) {
945
+ emitFailed(ctx, label, error, { runId: cleanRunId });
946
+ throw error;
947
+ }
948
+ }
949
+ function steerResultSummary(result) {
950
+ for (const key of ["status", "state", "message", "detail"]) {
951
+ const value = result[key];
952
+ if (typeof value === "string" && value.trim())
953
+ return value.trim();
954
+ }
955
+ if (result.ok === true || result.queued === true || result.delivered === true)
956
+ return "accepted by run";
957
+ return "delivered";
958
+ }
913
959
  async function steerRunFromApp(ctx, input) {
914
960
  const label = `Steering ${input.runId.slice(0, 8)}`;
915
961
  const message = input.message.trim();
@@ -918,17 +964,21 @@ async function steerRunFromApp(ctx, input) {
918
964
  emitFailed(ctx, label, error, { runId: input.runId });
919
965
  throw error;
920
966
  }
921
- emitStarted(ctx, label, { runMessage: { runId: input.runId, text: message, status: "queued" } });
922
- patchData(ctx, { pendingRunMessage: { runId: input.runId, text: message, status: "queued" } });
967
+ const pending = { runId: input.runId, text: message, status: "queued" };
968
+ emitStarted(ctx, label, { pendingRunMessage: pending });
969
+ patchData(ctx, { pendingRunMessage: pending });
923
970
  try {
924
971
  const { sendRunPiPromptViaServer: sendRunPiPromptViaServer2, steerRunViaServer: steerRunViaServer2 } = await Promise.resolve().then(() => (init__server_client(), exports__server_client));
925
972
  const context = { projectRoot: projectRootOf(ctx) };
926
973
  const result = await sendRunPiPromptViaServer2(context, input.runId, message, "steer").catch(() => steerRunViaServer2(context, input.runId, message));
974
+ const summary = steerResultSummary(result);
975
+ patchData(ctx, { pendingRunMessage: { runId: input.runId, text: message, status: "sent", result: summary } });
927
976
  emitCompleted(ctx, label, { runId: input.runId, result });
928
- patchData(ctx, { pendingRunMessage: null });
929
977
  return result;
930
978
  } catch (error) {
931
- emitFailed(ctx, label, error, { runId: input.runId, revert: { pendingRunMessage: null } });
979
+ const message2 = normalizeAppError(error).message;
980
+ emitFailed(ctx, label, error, { runId: input.runId });
981
+ patchData(ctx, { pendingRunMessage: { runId: input.runId, text: message, status: "failed", result: message2 } });
932
982
  throw error;
933
983
  }
934
984
  }
@@ -942,7 +992,10 @@ async function stopRunFromDetail(ctx, runId) {
942
992
  const { stopRunViaServer: stopRunViaServer2 } = await Promise.resolve().then(() => (init__server_client(), exports__server_client));
943
993
  const result = await stopRunViaServer2({ projectRoot: projectRootOf(ctx) }, cleanRunId);
944
994
  emitCompleted(ctx, label, { runId: cleanRunId, result });
945
- await loadRunDetail(ctx, cleanRunId).catch(() => null);
995
+ await loadRunDetail(ctx, cleanRunId).catch((error) => {
996
+ patchData(ctx, { lastRefreshError: normalizeAppError(error).message });
997
+ return null;
998
+ });
946
999
  return result;
947
1000
  } catch (error) {
948
1001
  emitFailed(ctx, label, error, { runId: cleanRunId, revert: { status: "unknown" } });
@@ -953,5 +1006,5 @@ export {
953
1006
  stopRunFromDetail,
954
1007
  steerRunFromApp,
955
1008
  loadRunDetail,
956
- createRunDetailAdapter
1009
+ loadMoreRunTimeline
957
1010
  };
@@ -1,6 +1,4 @@
1
- import type { AppAdapter } from "../types";
2
1
  import { type AppAdapterContext } from "./common";
3
- export declare function createServerAdapter(): AppAdapter;
4
2
  export type AppServerState = {
5
3
  readonly label: string;
6
4
  readonly baseUrl?: string;
@@ -11,4 +9,14 @@ export type AppServerState = {
11
9
  readonly auth?: Record<string, unknown>;
12
10
  readonly error?: string;
13
11
  };
12
+ /** Start the local Rig server DETACHED and return to the server scene (bug #3).
13
+ *
14
+ * `rig server start` is a blocking long-running process; running it through the
15
+ * foreground command PTY traps the operator (kitty-mode even swallows ctrl-c).
16
+ * Instead we: (a) switch this repo's connection to local, (b) spawn the server
17
+ * detached + wait for ready via ensureLocalRigServerConnection (it writes the
18
+ * published server state and unrefs the child), then (c) re-probe. The operator
19
+ * is never blocked: this returns once the detached server answers.
20
+ */
21
+ export declare function startLocalServerFromApp(ctx: AppAdapterContext): Promise<AppServerState>;
14
22
  export declare function refreshServerStatus(ctx: AppAdapterContext): Promise<AppServerState>;
@@ -14,6 +14,7 @@ var __export = (target, all) => {
14
14
  });
15
15
  };
16
16
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
17
+ var __require = import.meta.require;
17
18
 
18
19
  // packages/cli/src/runner.ts
19
20
  import { EventBus } from "@rig/runtime/control-plane/runtime/events";
@@ -34,6 +35,19 @@ var init_runner = __esm(() => {
34
35
  });
35
36
 
36
37
  // packages/cli/src/commands/_connection-state.ts
38
+ var exports__connection_state = {};
39
+ __export(exports__connection_state, {
40
+ writeRepoServerProjectRoot: () => writeRepoServerProjectRoot,
41
+ writeRepoConnection: () => writeRepoConnection,
42
+ writeGlobalConnections: () => writeGlobalConnections,
43
+ upsertGlobalConnection: () => upsertGlobalConnection,
44
+ resolveSelectedConnection: () => resolveSelectedConnection,
45
+ resolveRepoConnectionPath: () => resolveRepoConnectionPath,
46
+ resolveGlobalConnectionsPath: () => resolveGlobalConnectionsPath,
47
+ readRepoConnection: () => readRepoConnection,
48
+ readGlobalConnections: () => readGlobalConnections,
49
+ isRemoteConnectionSelected: () => isRemoteConnectionSelected
50
+ });
37
51
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
38
52
  import { homedir } from "os";
39
53
  import { dirname, resolve } from "path";
@@ -92,6 +106,18 @@ function readGlobalConnections(options = {}) {
92
106
  }
93
107
  return { connections };
94
108
  }
109
+ function writeGlobalConnections(state, options = {}) {
110
+ writeJsonFile(resolveGlobalConnectionsPath(options.env ?? process.env), state);
111
+ }
112
+ function upsertGlobalConnection(alias, connection, options = {}) {
113
+ const cleanAlias = alias.trim();
114
+ if (!cleanAlias)
115
+ throw new CliError("Connection alias is required.", 1, { hint: "Save a server with `rig server add <alias> <url>`." });
116
+ const state = readGlobalConnections(options);
117
+ state.connections[cleanAlias] = connection;
118
+ writeGlobalConnections(state, options);
119
+ return state;
120
+ }
95
121
  function readRepoConnection(projectRoot) {
96
122
  const payload = readJsonFile(resolveRepoConnectionPath(projectRoot));
97
123
  if (!payload || typeof payload !== "object" || Array.isArray(payload))
@@ -129,6 +155,9 @@ function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
129
155
  return;
130
156
  writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
131
157
  }
158
+ function isRemoteConnectionSelected(projectRoot) {
159
+ return resolveSelectedConnection(projectRoot)?.connection.kind === "remote";
160
+ }
132
161
  var init__connection_state = __esm(() => {
133
162
  init_runner();
134
163
  });
@@ -767,15 +796,6 @@ var init__server_client = __esm(() => {
767
796
  });
768
797
 
769
798
  // packages/cli/src/app-opentui/adapters/common.ts
770
- function adapterContextFromRuntime(runtime, renderer) {
771
- return {
772
- projectRoot: runtime.getState().projectRoot,
773
- renderer,
774
- ensureRuntime: () => runtime.getRunnerContext(),
775
- emit: (event) => runtime.emit(event),
776
- getState: () => runtime.getState()
777
- };
778
- }
779
799
  function projectRootOf(ctx) {
780
800
  const root = ctx.rig?.projectRoot ?? ctx.projectRoot ?? ctx.getState().projectRoot;
781
801
  if (typeof root === "string" && root.trim())
@@ -799,20 +819,7 @@ function patchFooter(ctx, footer) {
799
819
  }
800
820
 
801
821
  // packages/cli/src/app-opentui/adapters/server.ts
802
- function createServerAdapter() {
803
- return {
804
- id: "server",
805
- async handleIntent(runtime, intent) {
806
- if (intent.scene !== "server")
807
- return false;
808
- if (intent.action.kind === "refresh" || intent.action.kind === "navigate") {
809
- await refreshServerStatus(adapterContextFromRuntime(runtime));
810
- return true;
811
- }
812
- return false;
813
- }
814
- };
815
- }
822
+ var SERVER_PROBE_TIMEOUT_MS = Number(process.env.RIG_SERVER_PROBE_TIMEOUT_MS) || 5000;
816
823
  function withTimeout(promise, ms, label) {
817
824
  return new Promise((resolve3, reject) => {
818
825
  const timer = setTimeout(() => reject(new Error(`${label} timed out after ${ms}ms`)), ms);
@@ -825,7 +832,69 @@ function withTimeout(promise, ms, label) {
825
832
  });
826
833
  });
827
834
  }
835
+ async function withRetry(label, attempt, opts) {
836
+ const retries = opts?.retries ?? 1;
837
+ const baseDelayMs = opts?.baseDelayMs ?? 250;
838
+ let lastError;
839
+ for (let n = 0;n <= retries; n++) {
840
+ try {
841
+ return await attempt();
842
+ } catch (error) {
843
+ lastError = error;
844
+ if (n < retries) {
845
+ const delay = baseDelayMs * 2 ** n;
846
+ await new Promise((r) => setTimeout(r, delay));
847
+ }
848
+ }
849
+ }
850
+ throw lastError instanceof Error ? lastError : new Error(`${label} failed: ${String(lastError)}`);
851
+ }
852
+ function startLocalRequested(ctx) {
853
+ const intent = ctx.getState().data?.lastIntent;
854
+ if (!intent || typeof intent !== "object" || Array.isArray(intent))
855
+ return false;
856
+ const action = intent.action;
857
+ return action?.payload?.startLocal === true;
858
+ }
859
+ async function startLocalServerFromApp(ctx) {
860
+ const label = "Starting local server";
861
+ emitStarted(ctx, label);
862
+ patchData(ctx, {
863
+ lastIntent: undefined,
864
+ server: { label: "starting local server", reachable: false, error: "spawning local rig-server (detached)\u2026" }
865
+ });
866
+ try {
867
+ const projectRoot = projectRootOf(ctx);
868
+ emitProgress(ctx, label, "selecting local server for this repo");
869
+ const { writeRepoConnection: writeRepoConnection2, readRepoConnection: readRepoConnection2 } = await Promise.resolve().then(() => (init__connection_state(), exports__connection_state));
870
+ const previous = readRepoConnection2(projectRoot);
871
+ writeRepoConnection2(projectRoot, {
872
+ selected: "local",
873
+ ...previous?.project ? { project: previous.project } : {},
874
+ linkedAt: new Date().toISOString()
875
+ });
876
+ emitProgress(ctx, label, "spawning local rig-server (detached) and waiting for ready");
877
+ const { ensureLocalRigServerConnection: ensureLocalRigServerConnection2 } = await import("@rig/runtime/local-server");
878
+ const connection = await ensureLocalRigServerConnection2(projectRoot);
879
+ emitProgress(ctx, label, "local server ready", { baseUrl: connection.baseUrl });
880
+ emitCompleted(ctx, label, { baseUrl: connection.baseUrl });
881
+ return await refreshServerStatus(ctx);
882
+ } catch (error) {
883
+ const state = {
884
+ label: "local server failed to start",
885
+ reachable: false,
886
+ error: error instanceof Error ? error.message : String(error)
887
+ };
888
+ patchData(ctx, { server: state, lastRefreshError: state.error });
889
+ patchFooter(ctx, { server: "unavailable" });
890
+ emitCompleted(ctx, label, { server: state.label, reachable: false, error: state.error });
891
+ return state;
892
+ }
893
+ }
828
894
  async function refreshServerStatus(ctx) {
895
+ if (startLocalRequested(ctx)) {
896
+ return startLocalServerFromApp(ctx);
897
+ }
829
898
  const label = "Checking server";
830
899
  emitStarted(ctx, label);
831
900
  patchData(ctx, { server: { label: "checking selected server", reachable: false, error: "checking selected server\u2026" } });
@@ -840,8 +909,8 @@ async function refreshServerStatus(ctx) {
840
909
  ]);
841
910
  emitProgress(ctx, label, "requesting server status", { baseUrl: server.baseUrl, kind: server.connectionKind });
842
911
  const [status, auth] = await Promise.all([
843
- withTimeout(requestServerJson2({ projectRoot }, "/api/server/status"), 5000, "server status").catch((error) => ({ ok: false, error: error instanceof Error ? error.message : String(error) })),
844
- withTimeout(requestServerJson2({ projectRoot }, "/api/github/auth/status"), 5000, "github auth status").catch((error) => ({ ok: false, error: error instanceof Error ? error.message : String(error) }))
912
+ withRetry("server status", () => withTimeout(requestServerJson2({ projectRoot }, "/api/server/status"), SERVER_PROBE_TIMEOUT_MS, "server status")).catch((error) => ({ ok: false, error: error instanceof Error ? error.message : String(error) })),
913
+ withRetry("github auth status", () => withTimeout(requestServerJson2({ projectRoot }, "/api/github/auth/status"), SERVER_PROBE_TIMEOUT_MS, "github auth status")).catch((error) => ({ ok: false, error: error instanceof Error ? error.message : String(error) }))
845
914
  ]);
846
915
  const state = {
847
916
  label: connectionLabel,
@@ -870,6 +939,6 @@ async function refreshServerStatus(ctx) {
870
939
  }
871
940
  }
872
941
  export {
873
- refreshServerStatus,
874
- createServerAdapter
942
+ startLocalServerFromApp,
943
+ refreshServerStatus
875
944
  };
@@ -1,6 +1,4 @@
1
- import type { AppAdapter } from "../types";
2
1
  import { type AppAdapterContext } from "./common";
3
- export declare function createTasksAdapter(): AppAdapter;
4
2
  export type AppTaskSort = "priority" | "recency" | "status" | "id";
5
3
  export type AppTaskFilters = {
6
4
  readonly preset?: "open" | "ready" | "blocked" | "closed" | "mine" | "all" | "custom";