pi-chassis 0.0.0-stage → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/CHANGELOG.md +118 -0
  2. package/LICENSE +21 -0
  3. package/README.md +51 -2
  4. package/THIRD_PARTY_NOTICES +30 -0
  5. package/dist/chassis/alert.d.ts +50 -0
  6. package/dist/chassis/alert.js +107 -0
  7. package/dist/chassis/api.d.ts +411 -0
  8. package/dist/chassis/api.js +676 -0
  9. package/dist/chassis/bash-output.d.ts +89 -0
  10. package/dist/chassis/bash-output.js +157 -0
  11. package/dist/chassis/brand.d.ts +99 -0
  12. package/dist/chassis/brand.js +79 -0
  13. package/dist/chassis/client.d.ts +87 -0
  14. package/dist/chassis/client.js +271 -0
  15. package/dist/chassis/commands.d.ts +60 -0
  16. package/dist/chassis/commands.js +211 -0
  17. package/dist/chassis/editor.d.ts +70 -0
  18. package/dist/chassis/editor.js +213 -0
  19. package/dist/chassis/footer.d.ts +14 -0
  20. package/dist/chassis/footer.js +136 -0
  21. package/dist/chassis/header.d.ts +12 -0
  22. package/dist/chassis/header.js +38 -0
  23. package/dist/chassis/home.d.ts +48 -0
  24. package/dist/chassis/home.js +184 -0
  25. package/dist/chassis/icons.d.ts +203 -0
  26. package/dist/chassis/icons.js +190 -0
  27. package/dist/chassis/index.d.ts +21 -0
  28. package/dist/chassis/index.js +582 -0
  29. package/dist/chassis/layout.d.ts +40 -0
  30. package/dist/chassis/layout.js +264 -0
  31. package/dist/chassis/message-modes.d.ts +21 -0
  32. package/dist/chassis/message-modes.js +49 -0
  33. package/dist/chassis/messages.d.ts +75 -0
  34. package/dist/chassis/messages.js +392 -0
  35. package/dist/chassis/modes.d.ts +21 -0
  36. package/dist/chassis/modes.js +99 -0
  37. package/dist/chassis/palette.d.ts +84 -0
  38. package/dist/chassis/palette.js +235 -0
  39. package/dist/chassis/peek.d.ts +61 -0
  40. package/dist/chassis/peek.js +156 -0
  41. package/dist/chassis/prompt-row.d.ts +42 -0
  42. package/dist/chassis/prompt-row.js +68 -0
  43. package/dist/chassis/provider-view.d.ts +34 -0
  44. package/dist/chassis/provider-view.js +107 -0
  45. package/dist/chassis/provider.d.ts +69 -0
  46. package/dist/chassis/provider.js +73 -0
  47. package/dist/chassis/readonly-command.d.ts +19 -0
  48. package/dist/chassis/readonly-command.js +454 -0
  49. package/dist/chassis/sanitize.d.ts +42 -0
  50. package/dist/chassis/sanitize.js +103 -0
  51. package/dist/chassis/session-info.d.ts +39 -0
  52. package/dist/chassis/session-info.js +107 -0
  53. package/dist/chassis/sidebar.d.ts +48 -0
  54. package/dist/chassis/sidebar.js +273 -0
  55. package/dist/chassis/state.d.ts +38 -0
  56. package/dist/chassis/state.js +19 -0
  57. package/dist/chassis/telemetry.d.ts +60 -0
  58. package/dist/chassis/telemetry.js +191 -0
  59. package/dist/chassis/tools.d.ts +112 -0
  60. package/dist/chassis/tools.js +588 -0
  61. package/dist/chassis/version.d.ts +2 -0
  62. package/dist/chassis/version.js +20 -0
  63. package/dist/diff/git-diff.d.ts +71 -0
  64. package/dist/diff/git-diff.js +325 -0
  65. package/dist/diff/host.d.ts +34 -0
  66. package/dist/diff/host.js +50 -0
  67. package/dist/diff/index.d.ts +13 -0
  68. package/dist/diff/index.js +86 -0
  69. package/dist/diff/render.d.ts +67 -0
  70. package/dist/diff/render.js +196 -0
  71. package/dist/diff/types.d.ts +60 -0
  72. package/dist/diff/types.js +4 -0
  73. package/dist/diff/view.d.ts +118 -0
  74. package/dist/diff/view.js +528 -0
  75. package/dist/git/flows.d.ts +92 -0
  76. package/dist/git/flows.js +415 -0
  77. package/dist/git/forge.d.ts +26 -0
  78. package/dist/git/forge.js +41 -0
  79. package/dist/git/index.d.ts +20 -0
  80. package/dist/git/index.js +121 -0
  81. package/dist/git/repo.d.ts +64 -0
  82. package/dist/git/repo.js +186 -0
  83. package/dist/git/run.d.ts +38 -0
  84. package/dist/git/run.js +127 -0
  85. package/dist/git/settings.d.ts +8 -0
  86. package/dist/git/settings.js +38 -0
  87. package/dist/git/text.d.ts +51 -0
  88. package/dist/git/text.js +109 -0
  89. package/dist/widgets/container/detect.d.ts +23 -0
  90. package/dist/widgets/container/detect.js +67 -0
  91. package/dist/widgets/container/index.d.ts +9 -0
  92. package/dist/widgets/container/index.js +26 -0
  93. package/dist/widgets/mcp/index.d.ts +13 -0
  94. package/dist/widgets/mcp/index.js +50 -0
  95. package/dist/widgets/mcp/status.d.ts +50 -0
  96. package/dist/widgets/mcp/status.js +70 -0
  97. package/dist/widgets/session-todos/index.d.ts +12 -0
  98. package/dist/widgets/session-todos/index.js +52 -0
  99. package/dist/widgets/subagents/index.d.ts +16 -0
  100. package/dist/widgets/subagents/index.js +173 -0
  101. package/dist/widgets/subagents/status.d.ts +61 -0
  102. package/dist/widgets/subagents/status.js +198 -0
  103. package/dist/widgets/todo/index.d.ts +13 -0
  104. package/dist/widgets/todo/index.js +65 -0
  105. package/dist/widgets/todo/parse.d.ts +29 -0
  106. package/dist/widgets/todo/parse.js +122 -0
  107. package/docs/branding.md +25 -0
  108. package/docs/development.md +22 -0
  109. package/docs/diff-api.md +18 -0
  110. package/docs/git.md +23 -0
  111. package/docs/internals.md +13 -0
  112. package/docs/provider-status.md +23 -0
  113. package/docs/status-widgets.md +157 -0
  114. package/docs/tool-rows.md +50 -0
  115. package/docs/usage.md +62 -0
  116. package/docs/widgets.md +113 -0
  117. package/extensions/chassis/alert.ts +127 -0
  118. package/extensions/chassis/api.ts +967 -0
  119. package/extensions/chassis/bash-output.ts +186 -0
  120. package/extensions/chassis/brand.ts +151 -0
  121. package/extensions/chassis/client.ts +284 -0
  122. package/extensions/chassis/commands.ts +244 -0
  123. package/extensions/chassis/editor.ts +253 -0
  124. package/extensions/chassis/footer.ts +131 -0
  125. package/extensions/chassis/header.ts +43 -0
  126. package/extensions/chassis/home.ts +186 -0
  127. package/extensions/chassis/icons.ts +220 -0
  128. package/extensions/chassis/index.ts +605 -0
  129. package/extensions/chassis/layout.ts +298 -0
  130. package/extensions/chassis/message-modes.ts +48 -0
  131. package/extensions/chassis/messages.ts +403 -0
  132. package/extensions/chassis/modes.ts +108 -0
  133. package/extensions/chassis/palette.ts +258 -0
  134. package/extensions/chassis/peek.ts +167 -0
  135. package/extensions/chassis/prompt-row.ts +86 -0
  136. package/extensions/chassis/provider-view.ts +118 -0
  137. package/extensions/chassis/provider.ts +115 -0
  138. package/extensions/chassis/readonly-command.ts +440 -0
  139. package/extensions/chassis/sanitize.ts +108 -0
  140. package/extensions/chassis/session-info.ts +127 -0
  141. package/extensions/chassis/sidebar.ts +285 -0
  142. package/extensions/chassis/state.ts +50 -0
  143. package/extensions/chassis/telemetry.ts +222 -0
  144. package/extensions/chassis/tools.ts +618 -0
  145. package/extensions/chassis/version.ts +20 -0
  146. package/extensions/diff/git-diff.ts +365 -0
  147. package/extensions/diff/host.ts +58 -0
  148. package/extensions/diff/index.ts +101 -0
  149. package/extensions/diff/render.ts +217 -0
  150. package/extensions/diff/types.ts +67 -0
  151. package/extensions/diff/view.ts +523 -0
  152. package/extensions/git/flows.ts +485 -0
  153. package/extensions/git/forge.ts +54 -0
  154. package/extensions/git/index.ts +117 -0
  155. package/extensions/git/repo.ts +200 -0
  156. package/extensions/git/run.ts +143 -0
  157. package/extensions/git/settings.ts +38 -0
  158. package/extensions/git/text.ts +133 -0
  159. package/extensions/widgets/container/detect.ts +81 -0
  160. package/extensions/widgets/container/index.ts +29 -0
  161. package/extensions/widgets/mcp/index.ts +53 -0
  162. package/extensions/widgets/mcp/status.ts +94 -0
  163. package/extensions/widgets/session-todos/index.ts +55 -0
  164. package/extensions/widgets/subagents/index.ts +170 -0
  165. package/extensions/widgets/subagents/status.ts +237 -0
  166. package/extensions/widgets/todo/index.ts +66 -0
  167. package/extensions/widgets/todo/parse.ts +119 -0
  168. package/package.json +91 -4
  169. package/scripts/link-pi.mjs +76 -0
@@ -0,0 +1,411 @@
1
+ /**
2
+ * Widget registry: the one place info and status widgets are declared.
3
+ *
4
+ * A widget is a small object with an id and a render function. The TUI decides where it shows up
5
+ * (sidebar section, footer segment, or both) from its `slots`, orders it by `order`, and refreshes
6
+ * it on `refreshMs` or when the widget calls `handle.refresh()`.
7
+ *
8
+ * import { registerWidget } from "../chassis/api.ts";
9
+ *
10
+ * registerWidget(pi, {
11
+ * id: "deploy",
12
+ * title: "Deploy",
13
+ * slots: ["footer", "sidebar"],
14
+ * order: 10,
15
+ * refreshMs: 30_000,
16
+ * render: () => ({ icon: "\uF0E7", text: "staging·v42", color: "brand" }),
17
+ * detail: () => ["Last deploy 12m ago"],
18
+ * });
19
+ *
20
+ * The registry lives on globalThis because pi loads every extension file in its own module scope
21
+ * (jiti, moduleCache off), so a plain module-level Map would give each extension its own copy.
22
+ * Widgets registered before chassis loads are kept and shown once it starts.
23
+ *
24
+ * Plain `ctx.ui.setStatus()` from other extensions still works: the TUI shows those statuses as
25
+ * footer segments too, unless a registered widget claims the same key (`statusKey`).
26
+ */
27
+ import type { ExtensionAPI, ExtensionContext, Theme } from "@earendil-works/pi-coding-agent";
28
+ import type { IconLike } from "./icons.ts";
29
+ /** A render or detail call slower than this counts as a strike against the widget. */
30
+ export declare const SLOW_RENDER_MS = 50;
31
+ /** This many slow calls in a row suspend the widget until `/widgets enable ID`. */
32
+ export declare const SLOW_RENDER_STRIKES = 3;
33
+ /** An `update` that has not settled after this long is abandoned and reported as an error. */
34
+ export declare const DEFAULT_UPDATE_TIMEOUT_MS = 10000;
35
+ /** Where a widget can appear. */
36
+ export type WidgetSlot = "sidebar" | "footer";
37
+ /**
38
+ * Colours a widget can ask for: theme tokens, the brand colours (`brand`, `brandDark`, `plan`; see
39
+ * brand.ts), or any `#RRGGBB` value.
40
+ */
41
+ export type WidgetColor = "text" | "muted" | "dim" | "accent" | "success" | "warning" | "error" | "brand" | "brandDark" | "plan" | `#${string}`;
42
+ /** Status level: drives the dot colour in the sidebar heading when no colour is given. */
43
+ export type WidgetLevel = "ok" | "warn" | "error" | "off" | "info";
44
+ /** What a widget renders: one short line for the footer and the sidebar heading row. */
45
+ export interface WidgetView {
46
+ /** Optional leading glyph. A string is used as given with a Nerd Font and dropped (private-use characters removed) without one; `{ nerd, plain }` gives one for each set. */
47
+ icon?: IconLike;
48
+ /**
49
+ * Short status text, e.g. "staging·v42". Plain text: the TUI applies the colour. May be omitted by
50
+ * a `sidebar: "section"` widget that only has detail lines.
51
+ */
52
+ text?: string;
53
+ /**
54
+ * Footer-only label shown between icon and text (e.g. "Deploy" gives "<icon> Deploy staging·v42").
55
+ * The sidebar shows the widget title in its own column instead.
56
+ */
57
+ label?: string;
58
+ /**
59
+ * Sidebar row only: already-styled text drawn after `text`, separated by a space, in its own
60
+ * colours (e.g. a role name coloured by what it allows). Not shown in the footer.
61
+ */
62
+ tag?: string;
63
+ /** Colour for icon and text. Defaults from `level`, else "text". */
64
+ color?: WidgetColor;
65
+ /** Status level, used for colour defaults and the sidebar dot. */
66
+ level?: WidgetLevel;
67
+ }
68
+ /** Something the user can run from the command palette (ctrl+p), shown as "<title>: <label>". */
69
+ export interface WidgetAction {
70
+ /** Unique within the widget. */
71
+ id: string;
72
+ label: string;
73
+ description?: string;
74
+ /** A thrown error or rejection is shown as a notification and recorded on the widget. */
75
+ run: (ctx: ExtensionContext) => void | Promise<void>;
76
+ }
77
+ export interface WidgetRenderContext {
78
+ theme: Theme;
79
+ /** The latest session context, if a session is running. */
80
+ ctx: ExtensionContext | undefined;
81
+ /** Width available to this widget's line(s), in terminal columns. */
82
+ width: number;
83
+ }
84
+ export interface WidgetSpec {
85
+ /** Unique id; registering the same id again replaces the widget. */
86
+ id: string;
87
+ /** Sidebar section heading. Defaults to the id. */
88
+ title?: string;
89
+ /**
90
+ * Glyph in front of the widget in the sidebar: before a `sidebar: "section"` heading, or, for a
91
+ * row, as its marker in place of the bullet (and of the level marker). A row then leaves
92
+ * `view.icon` out of the value; the footer still draws it. A string is used as given with a Nerd
93
+ * Font and dropped without one; `{ nerd, plain }` gives one for each icon set. With no icon in
94
+ * the current set, a row shows its level marker or the bullet and a section shows no glyph.
95
+ */
96
+ icon?: IconLike;
97
+ /** Where it shows. Default ["footer", "sidebar"]. */
98
+ slots?: WidgetSlot[];
99
+ /** Lower comes first. Default 100. Built-ins: container 0, mcp 30, subagents 35, todo 40 and session-todos 50. */
100
+ order?: number;
101
+ /** Re-render interval while a session runs. Omit for event-driven widgets. */
102
+ refreshMs?: number;
103
+ /** Give up on a slow `update` after this many ms (default 10000). The widget shows the error. */
104
+ updateTimeoutMs?: number;
105
+ /**
106
+ * Called on each refresh tick (and on `handle.refresh()`), before rendering. Use it to poll
107
+ * slow sources; rendering itself should be cheap and synchronous.
108
+ */
109
+ update?: (ctx: ExtensionContext) => void | Promise<void>;
110
+ /**
111
+ * Opt in to caching: `render` and `detail` then run again only when this string, the registry
112
+ * version (any widget update or refresh), the width, the theme or the context changes. Use it
113
+ * for widgets that read session state (return something that changes with it) and are costly
114
+ * to compute. Do not use it when the output depends on the clock.
115
+ */
116
+ cacheKey?: (rc: WidgetRenderContext) => string;
117
+ /** Return the one-line view, or undefined to hide the widget. */
118
+ render: (rc: WidgetRenderContext) => WidgetView | undefined;
119
+ /**
120
+ * Extra sidebar lines under the heading (plain or themed strings). A line can also be a list of
121
+ * pieces, each a string or a `{ text, url }` link, for a line that links part of its text; see
122
+ * `DetailLine`. A link shows as its label alone where the terminal cannot show links, and is
123
+ * left out unless `url` is a plain https URL.
124
+ */
125
+ detail?: (rc: WidgetRenderContext) => DetailLine[] | undefined;
126
+ /**
127
+ * Called when the user clicks a line returned by `detail`, with that line's index. Only the
128
+ * lines `detail` returned are clickable, not the heading or an error line. A thrown error or
129
+ * rejection is shown as a notification.
130
+ */
131
+ onDetailClick?: (index: number, ctx: ExtensionContext) => void | Promise<void>;
132
+ /** Palette actions, listed while the widget is enabled and not suspended. */
133
+ actions?: WidgetAction[];
134
+ /** A setStatus() key this widget replaces, so the raw status isn't shown twice. Default: the id. */
135
+ statusKey?: string;
136
+ /** More setStatus() keys this widget replaces, for a widget that covers several raw statuses. */
137
+ statusKeys?: string[];
138
+ /**
139
+ * Sidebar layout. "row" (default): one "• Title value" line in the status list, detail lines
140
+ * indented under it. "section": its own sidebar section, with a bold title, the value beside
141
+ * it, and the detail lines as the section body (plain or themed strings, shown as given).
142
+ */
143
+ sidebar?: "row" | "section";
144
+ }
145
+ /** A link inside a detail line: the visible `text`, and the https `url` it opens. */
146
+ export interface DetailLink {
147
+ text: string;
148
+ url: string;
149
+ }
150
+ /**
151
+ * One detail line: a string, or a list of pieces. A piece is a string or a `DetailLink`. Links only
152
+ * work as pieces: chassis removes every escape sequence from text, so a link written into a string
153
+ * is shown as its label alone. That keeps text that came from outside (a ticket summary, an
154
+ * incident title) from drawing a link of its own.
155
+ */
156
+ export type DetailLine = string | Array<string | DetailLink>;
157
+ export interface WidgetHandle {
158
+ /** Run `update` (if any) and re-render now. */
159
+ refresh(): void;
160
+ /** Remove the widget. */
161
+ dispose(): void;
162
+ }
163
+ /** The last failure of a widget, kept for the sidebar and `/widgets`. */
164
+ export interface WidgetError {
165
+ phase: "update" | "render" | "detail" | "action";
166
+ message: string;
167
+ at: number;
168
+ }
169
+ interface RegistryEntry {
170
+ spec: WidgetSpec;
171
+ timer?: ReturnType<typeof setInterval>;
172
+ /** Unsubscribe functions for the pi.on handlers this widget added. */
173
+ offs: Array<() => void>;
174
+ /** An `update` is running; ticks are skipped until it settles or times out. */
175
+ inflight?: boolean;
176
+ lastUpdateAt?: number;
177
+ lastDurationMs?: number;
178
+ error?: WidgetError;
179
+ /** Cached output for widgets with `cacheKey`. */
180
+ cache?: {
181
+ key: string;
182
+ theme: unknown;
183
+ ctx: unknown;
184
+ width: number;
185
+ version: number;
186
+ view?: {
187
+ value: WidgetView | undefined;
188
+ };
189
+ lines?: {
190
+ value: string[];
191
+ };
192
+ };
193
+ /** Consecutive slow render/detail calls. */
194
+ strikes: number;
195
+ /** Set after too many slow calls; cleared by `/widgets enable`. */
196
+ suspended?: boolean;
197
+ lastRenderMs?: number;
198
+ }
199
+ /** The result of a tool call as a tool row spec sees it. Anything in it can be influenced from outside. */
200
+ export interface ToolRowResult {
201
+ content?: Array<{
202
+ type: string;
203
+ text?: string;
204
+ }>;
205
+ details?: any;
206
+ }
207
+ /**
208
+ * How chassis draws the rows of a tool that belongs to another extension: one line per call
209
+ * ("<icon> <title> <describe>" on the left, `summarize` on the right) and, when the row is opened
210
+ * with ctrl+o, the lines `expand` returns.
211
+ *
212
+ * `args` and `result` come from the model and the tool, so treat them as untrusted and possibly
213
+ * partial: a call is drawn while its arguments are still streaming. Every string a spec returns is
214
+ * cleaned before it is drawn (escape sequences, control characters and bidi controls removed, line
215
+ * breaks turned into spaces; SGR colour from `theme` is kept), and a spec that throws makes chassis
216
+ * draw that call with the tool's own renderer instead.
217
+ */
218
+ export interface ToolRowSpec {
219
+ /**
220
+ * Glyph in front of the row. A string is used as given in the Nerd Font set; in the plain set
221
+ * its private-use (Nerd Font) characters are dropped, so a string of ordinary characters such as
222
+ * "#" is kept in both and a bare Nerd Font glyph shows nothing. `{ nerd, plain }` gives one for
223
+ * each icon set. Omit for no glyph.
224
+ */
225
+ icon?: IconLike;
226
+ /** Short verb or noun before the description, drawn in the text colour. May be empty. */
227
+ title: string;
228
+ /** What the call is about, from its arguments: a path, a query, an id. One line. */
229
+ describe: (args: any, cwd: string, theme: Theme) => string;
230
+ /** The right-aligned outcome of a finished call, for example "3 matches". `isError` is whether the tool reported an error. One line. */
231
+ summarize: (result: ToolRowResult, args: any, theme: Theme, isError: boolean) => string;
232
+ /** Body lines shown when the row is opened (ctrl+o or a click). More than 120 lines are cut. */
233
+ expand?: (result: ToolRowResult, args: any, theme: Theme) => string[];
234
+ }
235
+ /** Tool row specs by tool name. */
236
+ export type ToolRowSpecs = Record<string, ToolRowSpec>;
237
+ /** Options for `registerToolRows`. */
238
+ export interface ToolRowsOptions {
239
+ /**
240
+ * Names the registrant. Registering again with the same id replaces that registrant's earlier
241
+ * group, so a package that registers on every `session_start` (and again after /reload) never
242
+ * piles up groups. Without an id each call adds a group that lives until its remover is called.
243
+ */
244
+ id?: string;
245
+ }
246
+ /** One registration: the specs of one `registerToolRows` call. */
247
+ export interface ToolRowGroup {
248
+ id?: string;
249
+ specs: Map<string, ToolRowSpec>;
250
+ }
251
+ /** Shared state between chassis and widget extensions. */
252
+ export interface Registry {
253
+ widgets: Map<string, RegistryEntry>;
254
+ /** Set by chassis while a session runs. */
255
+ ctx: ExtensionContext | undefined;
256
+ /** Set by chassis: asks the TUI to repaint. */
257
+ requestRender: () => void;
258
+ /** Subscribers to registry changes (chassis's renderers). */
259
+ listeners: Set<() => void>;
260
+ /** Bumped on every change, so renderers can cache. */
261
+ version: number;
262
+ /** Widget ids the user switched off (persisted, see `/widgets`). */
263
+ disabled: Set<string>;
264
+ /** Set by chassis's `installHost()`: the version of this API. Absent when chassis isn't loaded. */
265
+ apiVersion?: number;
266
+ /**
267
+ * Set by `installHost()`: what client.ts calls to register a widget with the host. `ctx` is
268
+ * optional: a caller inside a `session_start` handler passes the handler's context, so the
269
+ * widget's first `update` and its timer start even if chassis's own handler has not run yet.
270
+ */
271
+ register?: (pi: ExtensionAPI, spec: WidgetSpec, ctx?: ExtensionContext) => WidgetHandle;
272
+ /** Set by `installHost()`: the version of the tool row API. Absent when the host predates it, so a client can feature-detect. */
273
+ toolRowsVersion?: number;
274
+ /** Set by `installHost()`: registers tool row specs and returns a function that removes them. */
275
+ registerToolRows?: (specs: ToolRowSpecs, options?: ToolRowsOptions) => () => void;
276
+ /** Registered tool row groups, oldest first. Absent until the first registration; read through `toolRowGroups`. */
277
+ toolRows?: ToolRowGroup[];
278
+ }
279
+ /** Bumped on a breaking change to the registry or WidgetSpec; client.ts checks it. */
280
+ export declare const API_VERSION = 1;
281
+ /** Version of the tool row API (`registerToolRows`). Bumped on a breaking change to it or to ToolRowSpec. */
282
+ export declare const TOOL_ROWS_VERSION = 1;
283
+ /** A registration made (through client.ts) before chassis had loaded. */
284
+ export interface PendingRegistration {
285
+ pi: ExtensionAPI;
286
+ spec: WidgetSpec;
287
+ /** Hands the real handle back to the client once the host has registered the widget. */
288
+ attach(handle: WidgetHandle): void;
289
+ }
290
+ /** Tool rows registered (through client.ts or the raw protocol) before chassis had loaded. */
291
+ export interface PendingToolRows {
292
+ specs: ToolRowSpecs;
293
+ /** The registrant's id, as in `ToolRowsOptions`. */
294
+ id?: string;
295
+ /** Hands the host's unregister function back to the registrant once the host has taken the rows. */
296
+ attach(unregister: () => void): void;
297
+ }
298
+ /** Register everything that client.ts queued before the host loaded. */
299
+ export declare function drainPending(): void;
300
+ /**
301
+ * Called once by chassis when it loads: announces the host (so client.ts registers straight into
302
+ * the registry) and takes over widgets that were queued before it loaded.
303
+ */
304
+ export declare function installHost(): void;
305
+ /** Where the settings file is stored. PI_CHASSIS_STATE overrides it (used by tests). */
306
+ export declare function statePath(): string;
307
+ export declare function getRegistry(): Registry;
308
+ /** The registered tool row groups, oldest first (created on first use, so an older registry works). */
309
+ export declare function toolRowGroups(): ToolRowGroup[];
310
+ /** A spec that failed this many finished calls is no longer used until it is registered again. */
311
+ export declare const TOOL_ROW_FAILURE_LIMIT = 3;
312
+ /** Count a failure of `spec` on a finished call (not while arguments or output were still arriving). */
313
+ export declare function noteToolRowFailure(spec: ToolRowSpec): void;
314
+ /** Whether `spec` failed too many finished calls to be used for new ones. */
315
+ export declare function toolRowBroken(spec: ToolRowSpec): boolean;
316
+ /**
317
+ * Register chassis-styled rows for tools that belong to another extension, by tool name. Returns a
318
+ * function that removes them (calling it twice is harmless).
319
+ *
320
+ * The rows are looked up when a call is drawn, so it does not matter whether the extension that
321
+ * owns the tool, the one registering the rows or chassis loads first. A call already on screen keeps
322
+ * the renderer it was created with.
323
+ *
324
+ * chassis's own rows for pi's built-in tools (read, write, edit, bash, grep, find, ls) always win: a
325
+ * spec registered under one of those names is never used. For any other name the most recently
326
+ * registered spec wins, and removing it brings back the one before it. An entry is used only if
327
+ * it has a string `title`, function `describe` and `summarize`, and `expand` is a function when
328
+ * given; any other entry is skipped.
329
+ *
330
+ * Pass `{ id }` when the registrant can run more than once in a process (an extension that
331
+ * registers on every `session_start`, or is loaded again by /reload): registering again with the
332
+ * same id replaces its earlier group instead of adding another. Without an id, call the returned
333
+ * remover (for example on `session_shutdown`) before registering again.
334
+ */
335
+ export declare function registerToolRows(specs: ToolRowSpecs, options?: ToolRowsOptions): () => void;
336
+ /** The registered row spec for a tool, newest registration first, or undefined. */
337
+ export declare function toolRowFor(toolName: string): ToolRowSpec | undefined;
338
+ /** Make every widget render again (an icon set change, say), dropping cached output. */
339
+ export declare function invalidateWidgets(): void;
340
+ /**
341
+ * Register (or replace) a widget. Returns a handle for on-demand refresh and removal.
342
+ *
343
+ * Pass `pi` so the widget's timers follow the session lifecycle: they start on session_start and
344
+ * stop on session_shutdown. The widget itself stays registered across /reload of chassis.
345
+ *
346
+ * Registering from inside a `session_start` handler (as a package that does not import chassis does)
347
+ * misses that session's own `session_start` event for the widget, so pass the handler's `ctx`.
348
+ */
349
+ export declare function registerWidget(pi: ExtensionAPI, spec: WidgetSpec, ctx?: ExtensionContext): WidgetHandle;
350
+ /** Widgets for a slot, sorted by order then id. */
351
+ export declare function widgetsFor(slot: WidgetSlot): WidgetSpec[];
352
+ /** setStatus() keys claimed by registered widgets. */
353
+ export declare function claimedStatusKeys(): Set<string>;
354
+ /**
355
+ * Render a widget without letting it throw: a failure is recorded on the widget and returned as
356
+ * an "error" view. Returns undefined when the widget itself returns undefined (hidden).
357
+ * Widgets with `cacheKey` are only re-rendered when the key, width, theme, context or registry
358
+ * version changes; a widget that renders slowly several times in a row is suspended.
359
+ */
360
+ export declare function renderWidget(spec: WidgetSpec, rc: WidgetRenderContext): WidgetView | undefined;
361
+ /** Detail lines of a widget; a failure is recorded and yields no lines. Cached like `renderWidget`. */
362
+ export declare function detailWidget(spec: WidgetSpec, rc: WidgetRenderContext): string[];
363
+ /** The last recorded failure of a widget, if any. */
364
+ export declare function widgetError(id: string): WidgetError | undefined;
365
+ /** Turn a widget off or on. The choice is saved and survives restarts. */
366
+ export declare function setWidgetDisabled(id: string, disabled: boolean): boolean;
367
+ /** Run a widget's update now, even outside its timer. */
368
+ export declare function refreshWidget(id: string): boolean;
369
+ export interface WidgetInfo {
370
+ id: string;
371
+ title: string;
372
+ slots: WidgetSlot[];
373
+ disabled: boolean;
374
+ refreshMs?: number;
375
+ lastUpdateAt?: number;
376
+ lastDurationMs?: number;
377
+ inflight: boolean;
378
+ suspended: boolean;
379
+ lastRenderMs?: number;
380
+ error?: WidgetError;
381
+ }
382
+ /** Snapshot of every registered widget, for `/widgets`. */
383
+ export declare function widgetInfo(): WidgetInfo[];
384
+ /** Plain-text table for `/widgets list`. */
385
+ export declare function formatWidgetList(list: WidgetInfo[], now?: number): string;
386
+ export interface WidgetActionInfo {
387
+ widgetId: string;
388
+ widgetTitle: string;
389
+ id: string;
390
+ label: string;
391
+ description?: string;
392
+ }
393
+ /** Palette actions of every enabled, non-suspended widget, in widget order. */
394
+ export declare function widgetActions(): WidgetActionInfo[];
395
+ /** Palette value for an action: ids are encoded, so they may contain any character. */
396
+ export declare function widgetActionValue(widgetId: string, actionId: string): string;
397
+ export declare function parseWidgetActionValue(value: string): {
398
+ widgetId: string;
399
+ actionId: string;
400
+ } | undefined;
401
+ /**
402
+ * Run a widget's `onDetailClick` for the clicked detail line. Resolves to undefined on success, or
403
+ * to a short message when it failed (also recorded on the widget as an "action" error).
404
+ */
405
+ export declare function runWidgetClick(widgetId: string, index: number, ctx: ExtensionContext): Promise<string | undefined>;
406
+ /**
407
+ * Run a widget action. Resolves to undefined on success, or to a short message when the action
408
+ * is unknown or failed (the failure is also recorded on the widget).
409
+ */
410
+ export declare function runWidgetAction(widgetId: string, actionId: string, ctx: ExtensionContext): Promise<string | undefined>;
411
+ export {};