@agentick/tui 0.5.0 → 0.7.0

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 (141) hide show
  1. package/README.md +164 -21
  2. package/dist/commands-context.d.ts +10 -0
  3. package/dist/commands-context.d.ts.map +1 -0
  4. package/dist/commands-context.js +10 -0
  5. package/dist/commands-context.js.map +1 -0
  6. package/dist/commands.d.ts +30 -0
  7. package/dist/commands.d.ts.map +1 -0
  8. package/dist/commands.js +165 -0
  9. package/dist/commands.js.map +1 -0
  10. package/dist/components/CompletionPicker.d.ts +12 -0
  11. package/dist/components/CompletionPicker.d.ts.map +1 -0
  12. package/dist/components/CompletionPicker.js +41 -0
  13. package/dist/components/CompletionPicker.js.map +1 -0
  14. package/dist/components/DiffView.d.ts +21 -0
  15. package/dist/components/DiffView.d.ts.map +1 -0
  16. package/dist/components/DiffView.js +94 -0
  17. package/dist/components/DiffView.js.map +1 -0
  18. package/dist/components/ErrorDisplay.d.ts +6 -6
  19. package/dist/components/ErrorDisplay.d.ts.map +1 -1
  20. package/dist/components/ErrorDisplay.js +6 -8
  21. package/dist/components/ErrorDisplay.js.map +1 -1
  22. package/dist/components/InputBar.d.ts +8 -17
  23. package/dist/components/InputBar.d.ts.map +1 -1
  24. package/dist/components/InputBar.js +7 -22
  25. package/dist/components/InputBar.js.map +1 -1
  26. package/dist/components/MessageList.d.ts +11 -6
  27. package/dist/components/MessageList.d.ts.map +1 -1
  28. package/dist/components/MessageList.js +37 -54
  29. package/dist/components/MessageList.js.map +1 -1
  30. package/dist/components/RichTextInput.d.ts +14 -0
  31. package/dist/components/RichTextInput.d.ts.map +1 -0
  32. package/dist/components/RichTextInput.js +24 -0
  33. package/dist/components/RichTextInput.js.map +1 -0
  34. package/dist/components/ToolCallIndicator.d.ts.map +1 -1
  35. package/dist/components/ToolCallIndicator.js +12 -5
  36. package/dist/components/ToolCallIndicator.js.map +1 -1
  37. package/dist/components/ToolConfirmationPrompt.d.ts +9 -6
  38. package/dist/components/ToolConfirmationPrompt.d.ts.map +1 -1
  39. package/dist/components/ToolConfirmationPrompt.js +18 -19
  40. package/dist/components/ToolConfirmationPrompt.js.map +1 -1
  41. package/dist/components/status-bar/DefaultStatusBar.d.ts +20 -0
  42. package/dist/components/status-bar/DefaultStatusBar.d.ts.map +1 -0
  43. package/dist/components/status-bar/DefaultStatusBar.js +11 -0
  44. package/dist/components/status-bar/DefaultStatusBar.js.map +1 -0
  45. package/dist/components/status-bar/StatusBar.d.ts +22 -0
  46. package/dist/components/status-bar/StatusBar.d.ts.map +1 -0
  47. package/dist/components/status-bar/StatusBar.js +16 -0
  48. package/dist/components/status-bar/StatusBar.js.map +1 -0
  49. package/dist/components/status-bar/StatusBarRight.d.ts +12 -0
  50. package/dist/components/status-bar/StatusBarRight.d.ts.map +1 -0
  51. package/dist/components/status-bar/StatusBarRight.js +29 -0
  52. package/dist/components/status-bar/StatusBarRight.js.map +1 -0
  53. package/dist/components/status-bar/context.d.ts +27 -0
  54. package/dist/components/status-bar/context.d.ts.map +1 -0
  55. package/dist/components/status-bar/context.js +18 -0
  56. package/dist/components/status-bar/context.js.map +1 -0
  57. package/dist/components/status-bar/index.d.ts +5 -0
  58. package/dist/components/status-bar/index.d.ts.map +1 -0
  59. package/dist/components/status-bar/index.js +5 -0
  60. package/dist/components/status-bar/index.js.map +1 -0
  61. package/dist/components/status-bar/widgets/BrandLabel.d.ts +8 -0
  62. package/dist/components/status-bar/widgets/BrandLabel.d.ts.map +1 -0
  63. package/dist/components/status-bar/widgets/BrandLabel.js +6 -0
  64. package/dist/components/status-bar/widgets/BrandLabel.js.map +1 -0
  65. package/dist/components/status-bar/widgets/ContextUtilization.d.ts +11 -0
  66. package/dist/components/status-bar/widgets/ContextUtilization.d.ts.map +1 -0
  67. package/dist/components/status-bar/widgets/ContextUtilization.js +12 -0
  68. package/dist/components/status-bar/widgets/ContextUtilization.js.map +1 -0
  69. package/dist/components/status-bar/widgets/KeyboardHints.d.ts +16 -0
  70. package/dist/components/status-bar/widgets/KeyboardHints.d.ts.map +1 -0
  71. package/dist/components/status-bar/widgets/KeyboardHints.js +25 -0
  72. package/dist/components/status-bar/widgets/KeyboardHints.js.map +1 -0
  73. package/dist/components/status-bar/widgets/ModelInfo.d.ts +7 -0
  74. package/dist/components/status-bar/widgets/ModelInfo.d.ts.map +1 -0
  75. package/dist/components/status-bar/widgets/ModelInfo.js +10 -0
  76. package/dist/components/status-bar/widgets/ModelInfo.js.map +1 -0
  77. package/dist/components/status-bar/widgets/Separator.d.ts +7 -0
  78. package/dist/components/status-bar/widgets/Separator.d.ts.map +1 -0
  79. package/dist/components/status-bar/widgets/Separator.js +6 -0
  80. package/dist/components/status-bar/widgets/Separator.js.map +1 -0
  81. package/dist/components/status-bar/widgets/StateIndicator.d.ts +12 -0
  82. package/dist/components/status-bar/widgets/StateIndicator.d.ts.map +1 -0
  83. package/dist/components/status-bar/widgets/StateIndicator.js +21 -0
  84. package/dist/components/status-bar/widgets/StateIndicator.js.map +1 -0
  85. package/dist/components/status-bar/widgets/TickCount.d.ts +9 -0
  86. package/dist/components/status-bar/widgets/TickCount.d.ts.map +1 -0
  87. package/dist/components/status-bar/widgets/TickCount.js +11 -0
  88. package/dist/components/status-bar/widgets/TickCount.js.map +1 -0
  89. package/dist/components/status-bar/widgets/TokenCount.d.ts +12 -0
  90. package/dist/components/status-bar/widgets/TokenCount.d.ts.map +1 -0
  91. package/dist/components/status-bar/widgets/TokenCount.js +28 -0
  92. package/dist/components/status-bar/widgets/TokenCount.js.map +1 -0
  93. package/dist/components/status-bar/widgets/index.d.ts +9 -0
  94. package/dist/components/status-bar/widgets/index.d.ts.map +1 -0
  95. package/dist/components/status-bar/widgets/index.js +9 -0
  96. package/dist/components/status-bar/widgets/index.js.map +1 -0
  97. package/dist/create-tui.d.ts +5 -0
  98. package/dist/create-tui.d.ts.map +1 -1
  99. package/dist/create-tui.js +15 -12
  100. package/dist/create-tui.js.map +1 -1
  101. package/dist/hooks/use-double-ctrl-c.d.ts +5 -0
  102. package/dist/hooks/use-double-ctrl-c.d.ts.map +1 -0
  103. package/dist/hooks/use-double-ctrl-c.js +32 -0
  104. package/dist/hooks/use-double-ctrl-c.js.map +1 -0
  105. package/dist/hooks/use-line-editor.d.ts +26 -0
  106. package/dist/hooks/use-line-editor.d.ts.map +1 -0
  107. package/dist/hooks/use-line-editor.js +63 -0
  108. package/dist/hooks/use-line-editor.js.map +1 -0
  109. package/dist/index.d.ts +13 -3
  110. package/dist/index.d.ts.map +1 -1
  111. package/dist/index.js +14 -0
  112. package/dist/index.js.map +1 -1
  113. package/dist/input-utils.d.ts +15 -0
  114. package/dist/input-utils.d.ts.map +1 -0
  115. package/dist/input-utils.js +29 -0
  116. package/dist/input-utils.js.map +1 -0
  117. package/dist/rendering/content-block.d.ts +12 -0
  118. package/dist/rendering/content-block.d.ts.map +1 -0
  119. package/dist/rendering/content-block.js +82 -0
  120. package/dist/rendering/content-block.js.map +1 -0
  121. package/dist/rendering/index.d.ts +5 -0
  122. package/dist/rendering/index.d.ts.map +1 -0
  123. package/dist/rendering/index.js +5 -0
  124. package/dist/rendering/index.js.map +1 -0
  125. package/dist/rendering/markdown.d.ts +14 -0
  126. package/dist/rendering/markdown.d.ts.map +1 -0
  127. package/dist/rendering/markdown.js +61 -0
  128. package/dist/rendering/markdown.js.map +1 -0
  129. package/dist/rendering/message.d.ts +25 -0
  130. package/dist/rendering/message.d.ts.map +1 -0
  131. package/dist/rendering/message.js +54 -0
  132. package/dist/rendering/message.js.map +1 -0
  133. package/dist/rendering/theme.d.ts +37 -0
  134. package/dist/rendering/theme.d.ts.map +1 -0
  135. package/dist/rendering/theme.js +49 -0
  136. package/dist/rendering/theme.js.map +1 -0
  137. package/dist/ui/chat.d.ts +26 -4
  138. package/dist/ui/chat.d.ts.map +1 -1
  139. package/dist/ui/chat.js +91 -68
  140. package/dist/ui/chat.js.map +1 -1
  141. package/package.json +11 -6
package/README.md CHANGED
@@ -148,42 +148,185 @@ const MyUI: TUIComponent = ({ sessionId }) => {
148
148
 
149
149
  All components are exported for building custom UIs.
150
150
 
151
- | Component | Purpose |
152
- | ------------------------ | ------------------------------------------------- |
153
- | `Chat` | Default chat UI (built-in) |
154
- | `MessageList` | Completed messages (Ink `<Static>`) |
155
- | `StreamingMessage` | Live streaming response with cursor |
156
- | `ToolCallIndicator` | Spinner during tool execution |
157
- | `ToolConfirmationPrompt` | Y/N/A prompt for tools with `requireConfirmation` |
158
- | `ErrorDisplay` | Error box with optional dismiss |
159
- | `InputBar` | Text input with controlled/uncontrolled modes |
151
+ | Component | Purpose |
152
+ | ------------------------ | --------------------------------------------------- |
153
+ | `Chat` | Default conversational interface (block rendering) |
154
+ | `MessageList` | Prop-driven message display (Static + in-progress) |
155
+ | `StreamingMessage` | Live streaming response with cursor |
156
+ | `ToolCallIndicator` | Spinner during tool execution |
157
+ | `ToolConfirmationPrompt` | Y/N/A prompt for tools with `requireConfirmation` |
158
+ | `DiffView` | Side-by-side diff display for file changes |
159
+ | `ErrorDisplay` | Error box with optional dismiss |
160
+ | `InputBar` | Visual-only text input (value + cursor from parent) |
161
+ | `CompletionPicker` | Windowed completion list (emerald-themed) |
162
+ | `StatusBar` | Container with context provider and layout |
163
+ | `DefaultStatusBar` | Pre-composed status bar with responsive layout |
164
+
165
+ **Status bar widgets** (use standalone or inside `<StatusBar>`):
166
+
167
+ | Widget | Purpose |
168
+ | -------------------- | ------------------------------------------- |
169
+ | `ModelInfo` | Model name/id display |
170
+ | `TokenCount` | Formatted token count (cumulative or tick) |
171
+ | `TickCount` | Current execution tick number |
172
+ | `ContextUtilization` | Utilization % with color thresholds |
173
+ | `StateIndicator` | Mode label with color (idle/streaming/etc.) |
174
+ | `KeyboardHints` | Contextual keyboard shortcut hints |
175
+ | `BrandLabel` | Styled app name |
176
+ | `Separator` | Visual divider between segments |
160
177
 
161
178
  ### InputBar
162
179
 
163
- Supports two modes:
180
+ Visual-only input display. Renders the current value and cursor position — all keystroke handling lives in the parent via `useLineEditor`.
164
181
 
165
- **Uncontrolled** (default) — manages its own value, clears on submit:
182
+ ```typescript
183
+ import { InputBar, useLineEditor } from "@agentick/tui";
184
+
185
+ const editor = useLineEditor({ onSubmit: handleSubmit });
186
+
187
+ <InputBar
188
+ value={editor.value}
189
+ cursor={editor.cursor}
190
+ isActive={chatMode === "idle"}
191
+ placeholder="Type a message..."
192
+ />
193
+ ```
194
+
195
+ ### useLineEditor
196
+
197
+ Ink-specific wrapper around `@agentick/client`'s `LineEditor` class. Provides
198
+ readline-quality editing with cursor movement, history, kill/yank, and word
199
+ navigation.
166
200
 
167
201
  ```typescript
168
- <InputBar onSubmit={(text) => send(text)} isDisabled={isStreaming} />
202
+ const editor = useLineEditor({
203
+ onSubmit: (text) => send(text),
204
+ });
205
+
206
+ // In your centralized useInput handler:
207
+ if (chatMode === "idle") {
208
+ editor.handleInput(input, key);
209
+ }
169
210
  ```
170
211
 
171
- **Controlled** parent owns the value (needed for Ctrl+L clear, scroll mode, etc.):
212
+ Returns `{ value, cursor, completion, completedRanges, handleInput, setValue, clear, editor }`. Does not call `useInput` internally — the parent routes keystrokes to `editor.handleInput` when appropriate.
213
+
214
+ The `editor` property exposes the raw `LineEditor` instance for registering completion sources. The `completion` property is the current `CompletionState | null` — pass it to `CompletionPicker` to render the autocomplete picker.
215
+
216
+ For framework-agnostic usage (web, Angular), use `LineEditor` from `@agentick/client` directly, or `useLineEditor` from `@agentick/react`.
217
+
218
+ ### CompletionPicker
219
+
220
+ Renders a windowed completion list from `CompletionState`. Emerald-themed border, inverse highlight for the selected item, loading spinner, and "No matches" empty state.
172
221
 
173
222
  ```typescript
174
- const [value, setValue] = useState("");
223
+ import { CompletionPicker } from "@agentick/tui";
175
224
 
176
- <InputBar
177
- value={value}
178
- onChange={setValue}
179
- onSubmit={(text) => { send(text); setValue(""); }}
180
- isDisabled={isStreaming}
181
- />
225
+ {editor.completion && <CompletionPicker completion={editor.completion} />}
226
+ ```
227
+
228
+ ### Slash Commands
229
+
230
+ `useSlashCommands` provides a command registry with dispatch, dynamic add/remove, and completion integration.
231
+
232
+ ```typescript
233
+ import {
234
+ useSlashCommands,
235
+ helpCommand,
236
+ exitCommand,
237
+ createCommandCompletionSource,
238
+ } from "@agentick/tui";
239
+
240
+ const { dispatch, commands } = useSlashCommands([helpCommand(), exitCommand(exit)], {
241
+ sessionId,
242
+ send,
243
+ abort,
244
+ output: console.log,
245
+ });
246
+
247
+ // Wire into completion
248
+ useEffect(() => {
249
+ return editor.editor.registerCompletion(createCommandCompletionSource(commands));
250
+ }, [editor.editor, commands]);
251
+ ```
252
+
253
+ See [`COMPLETION.md`](../client/COMPLETION.md) for the full completion system reference.
254
+
255
+ ### handleConfirmationKey
256
+
257
+ Input routing utility for tool confirmation prompts. Maps `y`/`n`/`a` keys to confirmation responses.
258
+
259
+ ```typescript
260
+ import { handleConfirmationKey } from "@agentick/tui";
261
+
262
+ if (chatMode === "confirming_tool") {
263
+ handleConfirmationKey(input, respondToConfirmation);
264
+ }
182
265
  ```
183
266
 
184
267
  ### MessageList
185
268
 
186
- Uses `execution_end` events as its data source. When the event includes `newTimelineEntries` (a delta of entries added during that execution), MessageList appends them. Falls back to replacing all messages from `output.timeline` for backwards compatibility.
269
+ Prop-driven message display. Accepts `messages` from `useChat` and splits them into committed (Ink `<Static>`) and in-progress (regular render) based on `isExecuting`.
270
+
271
+ ```typescript
272
+ <MessageList messages={messages} isExecuting={isExecuting} />
273
+ ```
274
+
275
+ `Chat` uses `useChat({ renderMode: "block" })` so messages appear block-at-a-time as content completes, rather than waiting for the entire execution to finish.
276
+
277
+ ### StatusBar
278
+
279
+ `<StatusBar>` is a container that calls `useContextInfo` and `useStreamingText` once, provides the data via React context, and renders a left/right flexbox layout with a top border.
280
+
281
+ Widgets read from context automatically when inside a `<StatusBar>`, or accept explicit props when used standalone.
282
+
283
+ **Default** — `Chat` renders `<DefaultStatusBar>` automatically:
284
+
285
+ ```
286
+ Enter send | Ctrl+C exit GPT-4o | 6.2K 35% | idle
287
+ ```
288
+
289
+ The default is responsive — hides token/utilization info in narrow terminals.
290
+
291
+ **Custom** — compose your own from widgets:
292
+
293
+ ```typescript
294
+ import { StatusBar, BrandLabel, ModelInfo, TokenCount,
295
+ ContextUtilization, StateIndicator, Separator, KeyboardHints } from "@agentick/tui";
296
+
297
+ <StatusBar sessionId={sessionId} mode={chatMode}
298
+ left={<KeyboardHints hints={{ idle: [{ key: "Tab", action: "complete" }] }} />}
299
+ right={<>
300
+ <BrandLabel name="myapp" />
301
+ <Separator />
302
+ <ModelInfo />
303
+ <Separator />
304
+ <TokenCount cumulative />
305
+ <Separator />
306
+ <ContextUtilization />
307
+ <Separator />
308
+ <StateIndicator labels={{ streaming: "active" }} />
309
+ </>}
310
+ />
311
+ ```
312
+
313
+ **Chat integration** — control the status bar via the `statusBar` prop:
314
+
315
+ ```typescript
316
+ // Default (renders DefaultStatusBar)
317
+ <Chat sessionId="main" />
318
+
319
+ // Disabled
320
+ <Chat sessionId="main" statusBar={false} />
321
+
322
+ // Render prop — receives chat state
323
+ <Chat sessionId="main" statusBar={({ mode, sessionId }) => (
324
+ <StatusBar sessionId={sessionId} mode={mode}
325
+ left={<KeyboardHints />}
326
+ right={<StateIndicator />}
327
+ />
328
+ )} />
329
+ ```
187
330
 
188
331
  ## Architecture
189
332
 
@@ -0,0 +1,10 @@
1
+ import type { ReactNode } from "react";
2
+ import type { SlashCommand } from "./commands.js";
3
+ interface CommandsProviderProps {
4
+ commands: (SlashCommand | SlashCommand[])[];
5
+ children: ReactNode;
6
+ }
7
+ export declare function CommandsProvider({ commands, children }: CommandsProviderProps): import("react/jsx-runtime").JSX.Element;
8
+ export declare function useCommandsConfig(): (SlashCommand | SlashCommand[])[];
9
+ export {};
10
+ //# sourceMappingURL=commands-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands-context.d.ts","sourceRoot":"","sources":["../src/commands-context.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAIlD,UAAU,qBAAqB;IAC7B,QAAQ,EAAE,CAAC,YAAY,GAAG,YAAY,EAAE,CAAC,EAAE,CAAC;IAC5C,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,wBAAgB,gBAAgB,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,qBAAqB,2CAI7E;AAED,wBAAgB,iBAAiB,IAAI,CAAC,YAAY,GAAG,YAAY,EAAE,CAAC,EAAE,CAErE"}
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, useContext } from "react";
3
+ const CommandsConfigContext = createContext([]);
4
+ export function CommandsProvider({ commands, children }) {
5
+ return (_jsx(CommandsConfigContext.Provider, { value: commands, children: children }));
6
+ }
7
+ export function useCommandsConfig() {
8
+ return useContext(CommandsConfigContext);
9
+ }
10
+ //# sourceMappingURL=commands-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands-context.js","sourceRoot":"","sources":["../src/commands-context.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAIlD,MAAM,qBAAqB,GAAG,aAAa,CAAoC,EAAE,CAAC,CAAC;AAOnF,MAAM,UAAU,gBAAgB,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAyB;IAC5E,OAAO,CACL,KAAC,qBAAqB,CAAC,QAAQ,IAAC,KAAK,EAAE,QAAQ,YAAG,QAAQ,GAAkC,CAC7F,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,OAAO,UAAU,CAAC,qBAAqB,CAAC,CAAC;AAC3C,CAAC"}
@@ -0,0 +1,30 @@
1
+ import type { CompletionSource } from "@agentick/client";
2
+ export interface SlashCommand {
3
+ name: string;
4
+ description: string;
5
+ aliases?: string[];
6
+ args?: string;
7
+ handler: (args: string, ctx: CommandContext) => void | Promise<void>;
8
+ }
9
+ export interface CommandContext {
10
+ sessionId: string;
11
+ send: (...args: any[]) => any;
12
+ abort: () => void;
13
+ output: (text: string) => void;
14
+ addCommand: (cmd: SlashCommand) => void;
15
+ removeCommand: (name: string) => void;
16
+ }
17
+ export declare function helpCommand(): SlashCommand;
18
+ export declare function clearCommand(onClear: () => void): SlashCommand;
19
+ export declare function exitCommand(onExit: () => void): SlashCommand;
20
+ export declare function loadCommand(): SlashCommand;
21
+ export declare function createCommandCompletionSource(commands: SlashCommand[]): CompletionSource;
22
+ interface UseSlashCommandsResult {
23
+ dispatch: (input: string) => boolean;
24
+ commands: SlashCommand[];
25
+ addCommand: (cmd: SlashCommand) => void;
26
+ removeCommand: (name: string) => void;
27
+ }
28
+ export declare function useSlashCommands(initial: (SlashCommand | SlashCommand[])[], ctx: Omit<CommandContext, "addCommand" | "removeCommand">): UseSlashCommandsResult;
29
+ export {};
30
+ //# sourceMappingURL=commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAIzD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,cAAc,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtE;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAElB,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAC;IAC9B,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,UAAU,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,IAAI,CAAC;IACxC,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC;AAID,wBAAgB,WAAW,IAAI,YAAY,CAS1C;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,YAAY,CAQ9D;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,IAAI,GAAG,YAAY,CAS5D;AAED,wBAAgB,WAAW,IAAI,YAAY,CAkC1C;AAuBD,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,gBAAgB,CAmBxF;AAID,UAAU,sBAAsB;IAC9B,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IACrC,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,UAAU,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,IAAI,CAAC;IACxC,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC;AAED,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,CAAC,YAAY,GAAG,YAAY,EAAE,CAAC,EAAE,EAC1C,GAAG,EAAE,IAAI,CAAC,cAAc,EAAE,YAAY,GAAG,eAAe,CAAC,GACxD,sBAAsB,CAmFxB"}
@@ -0,0 +1,165 @@
1
+ import { useState, useCallback, useMemo, useRef } from "react";
2
+ // --- Built-in command factories ---
3
+ export function helpCommand() {
4
+ return {
5
+ name: "help",
6
+ description: "Show available commands",
7
+ handler: (_args, ctx) => {
8
+ // Access commands via closure — the registry ref is captured in useSlashCommands
9
+ ctx.output("Unknown command registry — /help is wired internally");
10
+ },
11
+ };
12
+ }
13
+ export function clearCommand(onClear) {
14
+ return {
15
+ name: "clear",
16
+ description: "Clear message history",
17
+ handler: () => {
18
+ onClear();
19
+ },
20
+ };
21
+ }
22
+ export function exitCommand(onExit) {
23
+ return {
24
+ name: "exit",
25
+ description: "Exit",
26
+ aliases: ["quit"],
27
+ handler: () => {
28
+ onExit();
29
+ },
30
+ };
31
+ }
32
+ export function loadCommand() {
33
+ return {
34
+ name: "load",
35
+ description: "Load a command from a file",
36
+ args: "<path>",
37
+ handler: async (args, ctx) => {
38
+ const filePath = args.trim();
39
+ if (!filePath) {
40
+ ctx.output("Usage: /load <path>");
41
+ return;
42
+ }
43
+ const resolved = filePath.startsWith("/") ? filePath : `${process.cwd()}/${filePath}`;
44
+ try {
45
+ const mod = await import(resolved);
46
+ const cmd = mod.command ?? mod.default;
47
+ if (!cmd || !cmd.name || !cmd.description || !cmd.handler) {
48
+ ctx.output(`Invalid command file: expected export const command: SlashCommand or export default`);
49
+ return;
50
+ }
51
+ ctx.addCommand(cmd);
52
+ ctx.output(`Loaded /${cmd.name} — ${cmd.description}`);
53
+ }
54
+ catch (err) {
55
+ ctx.output(`Failed to load ${resolved}: ${err instanceof Error ? err.message : String(err)}`);
56
+ }
57
+ },
58
+ };
59
+ }
60
+ // --- Help formatting ---
61
+ function formatHelp(commands) {
62
+ const sorted = [...commands].sort((a, b) => a.name.localeCompare(b.name));
63
+ const lines = [];
64
+ for (const cmd of sorted) {
65
+ const nameWithArgs = cmd.args ? `/${cmd.name} ${cmd.args}` : `/${cmd.name}`;
66
+ const aliasStr = cmd.aliases && cmd.aliases.length > 0
67
+ ? ` (aliases: ${cmd.aliases.map((a) => `/${a}`).join(", ")})`
68
+ : "";
69
+ const padded = nameWithArgs.padEnd(20);
70
+ lines.push(` ${padded}${cmd.description}${aliasStr}`);
71
+ }
72
+ return lines.join("\n");
73
+ }
74
+ // --- Completion source factory ---
75
+ export function createCommandCompletionSource(commands) {
76
+ return {
77
+ id: "command",
78
+ match({ value, cursor }) {
79
+ if (cursor < 1 || value[0] !== "/")
80
+ return null;
81
+ const spaceIdx = value.indexOf(" ");
82
+ if (spaceIdx >= 0 && cursor > spaceIdx)
83
+ return null;
84
+ return { from: 0, query: value.slice(1, cursor) };
85
+ },
86
+ resolve({ query }) {
87
+ return commands
88
+ .filter((cmd) => !query || cmd.name.startsWith(query))
89
+ .map((cmd) => ({
90
+ label: cmd.name,
91
+ value: cmd.args ? `/${cmd.name} ` : `/${cmd.name}`,
92
+ description: cmd.description,
93
+ }));
94
+ },
95
+ };
96
+ }
97
+ export function useSlashCommands(initial, ctx) {
98
+ const [registry, setRegistry] = useState(() => {
99
+ const map = new Map();
100
+ for (const item of initial.flat()) {
101
+ map.set(item.name, item);
102
+ }
103
+ return map;
104
+ });
105
+ // Keep a ref so dispatch always sees current registry without re-creating the callback
106
+ const registryRef = useRef(registry);
107
+ registryRef.current = registry;
108
+ const addCommand = useCallback((cmd) => {
109
+ setRegistry((prev) => {
110
+ const next = new Map(prev);
111
+ next.set(cmd.name, cmd);
112
+ return next;
113
+ });
114
+ }, []);
115
+ const removeCommand = useCallback((name) => {
116
+ setRegistry((prev) => {
117
+ const next = new Map(prev);
118
+ next.delete(name);
119
+ return next;
120
+ });
121
+ }, []);
122
+ const dispatch = useCallback((input) => {
123
+ if (!input.startsWith("/"))
124
+ return false;
125
+ const spaceIdx = input.indexOf(" ");
126
+ const name = spaceIdx === -1 ? input.slice(1) : input.slice(1, spaceIdx);
127
+ const args = spaceIdx === -1 ? "" : input.slice(spaceIdx + 1);
128
+ const reg = registryRef.current;
129
+ // Direct name match
130
+ let cmd = reg.get(name);
131
+ // Alias match
132
+ if (!cmd) {
133
+ for (const candidate of reg.values()) {
134
+ if (candidate.aliases?.includes(name)) {
135
+ cmd = candidate;
136
+ break;
137
+ }
138
+ }
139
+ }
140
+ if (!cmd) {
141
+ ctx.output(`Unknown command: /${name}. Type /help for available commands.`);
142
+ return true;
143
+ }
144
+ // Special-case /help: format with current registry
145
+ if (cmd.name === "help") {
146
+ ctx.output(formatHelp([...reg.values()]));
147
+ return true;
148
+ }
149
+ const fullCtx = {
150
+ ...ctx,
151
+ addCommand,
152
+ removeCommand,
153
+ };
154
+ cmd.handler(args, fullCtx);
155
+ return true;
156
+ }, [ctx, addCommand, removeCommand]);
157
+ const commands = useMemo(() => [...registry.values()], [registry]);
158
+ return {
159
+ dispatch,
160
+ commands,
161
+ addCommand,
162
+ removeCommand,
163
+ };
164
+ }
165
+ //# sourceMappingURL=commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.js","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * CompletionPicker — renders a completion dropdown for the TUI.
3
+ *
4
+ * Pure rendering component. Takes CompletionState, renders items in an
5
+ * emerald-themed bordered box with windowed scrolling.
6
+ */
7
+ import type { CompletionState } from "@agentick/client";
8
+ export interface CompletionPickerProps {
9
+ completion: CompletionState;
10
+ }
11
+ export declare function CompletionPicker({ completion }: CompletionPickerProps): import("react/jsx-runtime").JSX.Element;
12
+ //# sourceMappingURL=CompletionPicker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CompletionPicker.d.ts","sourceRoot":"","sources":["../../src/components/CompletionPicker.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAKxD,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,eAAe,CAAC;CAC7B;AAED,wBAAgB,gBAAgB,CAAC,EAAE,UAAU,EAAE,EAAE,qBAAqB,2CA+DrE"}
@@ -0,0 +1,41 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * CompletionPicker — renders a completion dropdown for the TUI.
4
+ *
5
+ * Pure rendering component. Takes CompletionState, renders items in an
6
+ * emerald-themed bordered box with windowed scrolling.
7
+ */
8
+ import { Box, Text } from "ink";
9
+ const MAX_VISIBLE = 8;
10
+ const BORDER_COLOR = "#34d399";
11
+ export function CompletionPicker({ completion }) {
12
+ const { items, selectedIndex, loading } = completion;
13
+ if (loading && items.length === 0) {
14
+ return (_jsx(Box, { borderStyle: "single", borderColor: BORDER_COLOR, paddingLeft: 1, paddingRight: 1, children: _jsx(Text, { dimColor: true, children: "Loading..." }) }));
15
+ }
16
+ if (items.length === 0) {
17
+ return (_jsx(Box, { borderStyle: "single", borderColor: BORDER_COLOR, paddingLeft: 1, paddingRight: 1, children: _jsx(Text, { dimColor: true, children: "No matches" }) }));
18
+ }
19
+ // Windowed scrolling — keep selected item visible
20
+ const total = items.length;
21
+ const windowSize = Math.min(total, MAX_VISIBLE);
22
+ let windowStart;
23
+ if (total <= MAX_VISIBLE) {
24
+ windowStart = 0;
25
+ }
26
+ else {
27
+ // Center the selected item in the window
28
+ const half = Math.floor(windowSize / 2);
29
+ windowStart = Math.max(0, Math.min(selectedIndex - half, total - windowSize));
30
+ }
31
+ const windowEnd = windowStart + windowSize;
32
+ const visibleItems = items.slice(windowStart, windowEnd);
33
+ const hasItemsAbove = windowStart > 0;
34
+ const hasItemsBelow = windowEnd < total;
35
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: BORDER_COLOR, paddingLeft: 1, paddingRight: 1, children: [hasItemsAbove && _jsx(Text, { dimColor: true, children: " ..." }), visibleItems.map((item, i) => {
36
+ const actualIndex = windowStart + i;
37
+ const isSelected = actualIndex === selectedIndex;
38
+ return (_jsxs(Box, { flexDirection: "row", gap: 1, children: [_jsx(Text, { inverse: isSelected, bold: isSelected, children: item.label }), item.description && _jsx(Text, { dimColor: true, children: item.description })] }, actualIndex));
39
+ }), hasItemsBelow && _jsx(Text, { dimColor: true, children: " ..." }), loading && _jsx(Text, { dimColor: true, children: " Loading..." })] }));
40
+ }
41
+ //# sourceMappingURL=CompletionPicker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CompletionPicker.js","sourceRoot":"","sources":["../../src/components/CompletionPicker.tsx"],"names":[],"mappings":";AAAA;;;;;GAKG;AAEH,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAGhC,MAAM,WAAW,GAAG,CAAC,CAAC;AACtB,MAAM,YAAY,GAAG,SAAS,CAAC;AAM/B,MAAM,UAAU,gBAAgB,CAAC,EAAE,UAAU,EAAyB;IACpE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,GAAG,UAAU,CAAC;IAErD,IAAI,OAAO,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClC,OAAO,CACL,KAAC,GAAG,IAAC,WAAW,EAAC,QAAQ,EAAC,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,YAClF,KAAC,IAAI,IAAC,QAAQ,iCAAkB,GAC5B,CACP,CAAC;IACJ,CAAC;IAED,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,CACL,KAAC,GAAG,IAAC,WAAW,EAAC,QAAQ,EAAC,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,YAClF,KAAC,IAAI,IAAC,QAAQ,iCAAkB,GAC5B,CACP,CAAC;IACJ,CAAC;IAED,kDAAkD;IAClD,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC;IAC3B,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;IAChD,IAAI,WAAmB,CAAC;IAExB,IAAI,KAAK,IAAI,WAAW,EAAE,CAAC;QACzB,WAAW,GAAG,CAAC,CAAC;IAClB,CAAC;SAAM,CAAC;QACN,yCAAyC;QACzC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;QACxC,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,aAAa,GAAG,IAAI,EAAE,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC;IAChF,CAAC;IAED,MAAM,SAAS,GAAG,WAAW,GAAG,UAAU,CAAC;IAC3C,MAAM,YAAY,GAAG,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;IACzD,MAAM,aAAa,GAAG,WAAW,GAAG,CAAC,CAAC;IACtC,MAAM,aAAa,GAAG,SAAS,GAAG,KAAK,CAAC;IAExC,OAAO,CACL,MAAC,GAAG,IACF,aAAa,EAAC,QAAQ,EACtB,WAAW,EAAC,QAAQ,EACpB,WAAW,EAAE,YAAY,EACzB,WAAW,EAAE,CAAC,EACd,YAAY,EAAE,CAAC,aAEd,aAAa,IAAI,KAAC,IAAI,IAAC,QAAQ,2BAAY,EAC3C,YAAY,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;gBAC5B,MAAM,WAAW,GAAG,WAAW,GAAG,CAAC,CAAC;gBACpC,MAAM,UAAU,GAAG,WAAW,KAAK,aAAa,CAAC;gBAEjD,OAAO,CACL,MAAC,GAAG,IAAmB,aAAa,EAAC,KAAK,EAAC,GAAG,EAAE,CAAC,aAC/C,KAAC,IAAI,IAAC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,YACxC,IAAI,CAAC,KAAK,GACN,EACN,IAAI,CAAC,WAAW,IAAI,KAAC,IAAI,IAAC,QAAQ,kBAAE,IAAI,CAAC,WAAW,GAAQ,KAJrD,WAAW,CAKf,CACP,CAAC;YACJ,CAAC,CAAC,EACD,aAAa,IAAI,KAAC,IAAI,IAAC,QAAQ,2BAAY,EAC3C,OAAO,IAAI,KAAC,IAAI,IAAC,QAAQ,kCAAmB,IACzC,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * DiffView — colored diff rendering with line numbers and gutter markers.
3
+ *
4
+ * Parses unified diff output (from jsdiff's createTwoFilesPatch) and renders:
5
+ * - Summary header with add/remove counts
6
+ * - Line numbers in a left gutter
7
+ * - Colored text (green additions, red removals, dim context)
8
+ * - Hunk headers in cyan
9
+ *
10
+ * Uses chalk for ANSI coloring without background colors — background colors
11
+ * create visible gaps between lines in most terminals due to inter-line
12
+ * pixel spacing from the font engine.
13
+ */
14
+ interface DiffViewProps {
15
+ patch: string;
16
+ filePath?: string;
17
+ maxLines?: number;
18
+ }
19
+ export declare function DiffView({ patch, filePath, maxLines }: DiffViewProps): import("react/jsx-runtime").JSX.Element;
20
+ export {};
21
+ //# sourceMappingURL=DiffView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DiffView.d.ts","sourceRoot":"","sources":["../../src/components/DiffView.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,UAAU,aAAa;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AA2FD,wBAAgB,QAAQ,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAa,EAAE,EAAE,aAAa,2CAmCzE"}
@@ -0,0 +1,94 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * DiffView — colored diff rendering with line numbers and gutter markers.
4
+ *
5
+ * Parses unified diff output (from jsdiff's createTwoFilesPatch) and renders:
6
+ * - Summary header with add/remove counts
7
+ * - Line numbers in a left gutter
8
+ * - Colored text (green additions, red removals, dim context)
9
+ * - Hunk headers in cyan
10
+ *
11
+ * Uses chalk for ANSI coloring without background colors — background colors
12
+ * create visible gaps between lines in most terminals due to inter-line
13
+ * pixel spacing from the font engine.
14
+ */
15
+ import chalk from "chalk";
16
+ import { Box, Text } from "ink";
17
+ const HUNK_RE = /^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/;
18
+ function parsePatch(patch) {
19
+ const lines = patch.split("\n");
20
+ const result = [];
21
+ let oldLine = 0;
22
+ let newLine = 0;
23
+ for (const line of lines) {
24
+ // Skip headers and diff metadata
25
+ if (line.startsWith("Index:") ||
26
+ line.startsWith("diff ") ||
27
+ line.startsWith("===") ||
28
+ line.startsWith("---") ||
29
+ line.startsWith("+++") ||
30
+ line.startsWith("\\")) {
31
+ continue;
32
+ }
33
+ const hunkMatch = HUNK_RE.exec(line);
34
+ if (hunkMatch) {
35
+ oldLine = parseInt(hunkMatch[1], 10);
36
+ newLine = parseInt(hunkMatch[2], 10);
37
+ result.push({ type: "hunk", content: line });
38
+ continue;
39
+ }
40
+ if (line.startsWith("+")) {
41
+ result.push({ type: "add", content: line.slice(1), newLine });
42
+ newLine++;
43
+ }
44
+ else if (line.startsWith("-")) {
45
+ result.push({ type: "remove", content: line.slice(1), oldLine });
46
+ oldLine++;
47
+ }
48
+ else if (line.length > 0 || (oldLine > 0 && newLine > 0)) {
49
+ result.push({ type: "context", content: line.slice(1), oldLine, newLine });
50
+ oldLine++;
51
+ newLine++;
52
+ }
53
+ }
54
+ return result;
55
+ }
56
+ function countChanges(lines) {
57
+ let added = 0;
58
+ let removed = 0;
59
+ for (const line of lines) {
60
+ if (line.type === "add")
61
+ added++;
62
+ if (line.type === "remove")
63
+ removed++;
64
+ }
65
+ return { added, removed };
66
+ }
67
+ // ────────────────────────────────────────────────────────────────────────────
68
+ function renderLine(line, gutterWidth) {
69
+ if (line.type === "hunk") {
70
+ return chalk.cyan.dim(line.content);
71
+ }
72
+ const marker = line.type === "add" ? "+" : line.type === "remove" ? "-" : " ";
73
+ const lineNum = line.type === "remove" ? String(line.oldLine ?? "") : String(line.newLine ?? "");
74
+ const gutter = `${lineNum.padStart(gutterWidth)} ${marker} `;
75
+ if (line.type === "add") {
76
+ return chalk.green.dim(gutter) + chalk.green(line.content);
77
+ }
78
+ if (line.type === "remove") {
79
+ return chalk.red.dim(gutter) + chalk.red(line.content);
80
+ }
81
+ return chalk.dim(gutter + line.content);
82
+ }
83
+ // ────────────────────────────────────────────────────────────────────────────
84
+ export function DiffView({ patch, filePath, maxLines = 80 }) {
85
+ const parsed = parsePatch(patch);
86
+ const { added, removed } = countChanges(parsed);
87
+ const displayLines = parsed.slice(0, maxLines);
88
+ const remaining = parsed.length - maxLines;
89
+ const maxLineNum = Math.max(...parsed.map((l) => l.oldLine ?? l.newLine ?? 0), 1);
90
+ const gutterWidth = String(maxLineNum).length;
91
+ const body = displayLines.map((line) => renderLine(line, gutterWidth)).join("\n");
92
+ return (_jsxs(Box, { flexDirection: "column", children: [filePath && (_jsxs(Box, { gap: 2, flexDirection: "row", children: [_jsx(Text, { bold: true, children: filePath }), _jsxs(Text, { dimColor: true, children: [added > 0 && _jsxs(Text, { color: "green", children: ["+", added] }), added > 0 && removed > 0 && _jsx(Text, { children: " " }), removed > 0 && _jsxs(Text, { color: "red", children: ["-", removed] })] })] })), !filePath && (added > 0 || removed > 0) && (_jsxs(Text, { dimColor: true, children: [added > 0 ? `${added} added` : "", added > 0 && removed > 0 ? ", " : "", removed > 0 ? `${removed} removed` : ""] })), _jsx(Text, { children: body }), remaining > 0 && _jsxs(Text, { dimColor: true, children: ["... ", remaining, " more line(s)"] })] }));
93
+ }
94
+ //# sourceMappingURL=DiffView.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DiffView.js","sourceRoot":"","sources":["../../src/components/DiffView.tsx"],"names":[],"mappings":"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"}