nova-terminal-assistant 0.1.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.

Potentially problematic release.


This version of nova-terminal-assistant might be problematic. Click here for more details.

Files changed (192) hide show
  1. package/README.md +358 -0
  2. package/bin/nova +38 -0
  3. package/bin/nova.js +12 -0
  4. package/package.json +67 -0
  5. package/src/cli/commands/SmartCompletion.ts +458 -0
  6. package/src/cli/index.ts +5 -0
  7. package/src/cli/startup/IFlowRepl.ts +212 -0
  8. package/src/cli/startup/InkBasedRepl.ts +1056 -0
  9. package/src/cli/startup/InteractiveRepl.ts +2833 -0
  10. package/src/cli/startup/NovaApp.ts +1861 -0
  11. package/src/cli/startup/index.ts +4 -0
  12. package/src/cli/startup/parseArgs.ts +293 -0
  13. package/src/cli/test-modules.ts +27 -0
  14. package/src/cli/ui/IFlowDropdown.ts +425 -0
  15. package/src/cli/ui/ModernReplUI.ts +276 -0
  16. package/src/cli/ui/SimpleSelector2.ts +215 -0
  17. package/src/cli/ui/components/ConfirmDialog.ts +176 -0
  18. package/src/cli/ui/components/ErrorPanel.ts +364 -0
  19. package/src/cli/ui/components/InkAppRunner.tsx +67 -0
  20. package/src/cli/ui/components/InkComponents.tsx +613 -0
  21. package/src/cli/ui/components/NovaInkApp.tsx +312 -0
  22. package/src/cli/ui/components/ProgressBar.ts +177 -0
  23. package/src/cli/ui/components/ProgressIndicator.ts +298 -0
  24. package/src/cli/ui/components/QuickActions.ts +396 -0
  25. package/src/cli/ui/components/SimpleErrorPanel.ts +231 -0
  26. package/src/cli/ui/components/StatusBar.ts +194 -0
  27. package/src/cli/ui/components/ThinkingBlockRenderer.ts +401 -0
  28. package/src/cli/ui/components/index.ts +27 -0
  29. package/src/cli/ui/ink-prototype.tsx +347 -0
  30. package/src/cli/utils/CliUI.ts +336 -0
  31. package/src/cli/utils/CompletionHelper.ts +388 -0
  32. package/src/cli/utils/EnhancedCompleter.test.ts +226 -0
  33. package/src/cli/utils/EnhancedCompleter.ts +513 -0
  34. package/src/cli/utils/ErrorEnhancer.ts +429 -0
  35. package/src/cli/utils/OutputFormatter.ts +193 -0
  36. package/src/cli/utils/index.ts +9 -0
  37. package/src/core/agents/AgentOrchestrator.ts +515 -0
  38. package/src/core/agents/index.ts +17 -0
  39. package/src/core/audit/AuditLogger.ts +509 -0
  40. package/src/core/audit/index.ts +11 -0
  41. package/src/core/auth/AuthManager.d.ts.map +1 -0
  42. package/src/core/auth/AuthManager.ts +138 -0
  43. package/src/core/auth/index.d.ts.map +1 -0
  44. package/src/core/auth/index.ts +2 -0
  45. package/src/core/config/ConfigManager.d.ts.map +1 -0
  46. package/src/core/config/ConfigManager.test.ts +183 -0
  47. package/src/core/config/ConfigManager.ts +1219 -0
  48. package/src/core/config/index.d.ts.map +1 -0
  49. package/src/core/config/index.ts +1 -0
  50. package/src/core/context/ContextBuilder.d.ts.map +1 -0
  51. package/src/core/context/ContextBuilder.ts +171 -0
  52. package/src/core/context/ContextCompressor.d.ts.map +1 -0
  53. package/src/core/context/ContextCompressor.ts +642 -0
  54. package/src/core/context/LayeredMemoryManager.ts +657 -0
  55. package/src/core/context/MemoryDiscovery.d.ts.map +1 -0
  56. package/src/core/context/MemoryDiscovery.ts +175 -0
  57. package/src/core/context/defaultSystemPrompt.d.ts.map +1 -0
  58. package/src/core/context/defaultSystemPrompt.ts +35 -0
  59. package/src/core/context/index.d.ts.map +1 -0
  60. package/src/core/context/index.ts +22 -0
  61. package/src/core/extensions/SkillGenerator.ts +421 -0
  62. package/src/core/extensions/SkillInstaller.d.ts.map +1 -0
  63. package/src/core/extensions/SkillInstaller.ts +257 -0
  64. package/src/core/extensions/SkillRegistry.d.ts.map +1 -0
  65. package/src/core/extensions/SkillRegistry.ts +361 -0
  66. package/src/core/extensions/SkillValidator.ts +525 -0
  67. package/src/core/extensions/index.ts +15 -0
  68. package/src/core/index.d.ts.map +1 -0
  69. package/src/core/index.ts +42 -0
  70. package/src/core/mcp/McpManager.d.ts.map +1 -0
  71. package/src/core/mcp/McpManager.ts +632 -0
  72. package/src/core/mcp/index.d.ts.map +1 -0
  73. package/src/core/mcp/index.ts +2 -0
  74. package/src/core/model/ModelClient.d.ts.map +1 -0
  75. package/src/core/model/ModelClient.ts +217 -0
  76. package/src/core/model/ModelConnectionTester.ts +363 -0
  77. package/src/core/model/ModelValidator.ts +348 -0
  78. package/src/core/model/index.d.ts.map +1 -0
  79. package/src/core/model/index.ts +6 -0
  80. package/src/core/model/providers/AnthropicProvider.d.ts.map +1 -0
  81. package/src/core/model/providers/AnthropicProvider.ts +279 -0
  82. package/src/core/model/providers/CodingPlanProvider.d.ts.map +1 -0
  83. package/src/core/model/providers/CodingPlanProvider.ts +210 -0
  84. package/src/core/model/providers/OllamaCloudProvider.d.ts.map +1 -0
  85. package/src/core/model/providers/OllamaCloudProvider.ts +405 -0
  86. package/src/core/model/providers/OllamaManager.d.ts.map +1 -0
  87. package/src/core/model/providers/OllamaManager.ts +201 -0
  88. package/src/core/model/providers/OllamaProvider.d.ts.map +1 -0
  89. package/src/core/model/providers/OllamaProvider.ts +73 -0
  90. package/src/core/model/providers/OpenAICompatibleProvider.d.ts.map +1 -0
  91. package/src/core/model/providers/OpenAICompatibleProvider.ts +327 -0
  92. package/src/core/model/providers/OpenAIProvider.d.ts.map +1 -0
  93. package/src/core/model/providers/OpenAIProvider.ts +29 -0
  94. package/src/core/model/providers/index.d.ts.map +1 -0
  95. package/src/core/model/providers/index.ts +12 -0
  96. package/src/core/model/types.d.ts.map +1 -0
  97. package/src/core/model/types.ts +77 -0
  98. package/src/core/security/ApprovalManager.d.ts.map +1 -0
  99. package/src/core/security/ApprovalManager.ts +174 -0
  100. package/src/core/security/FileFilter.d.ts.map +1 -0
  101. package/src/core/security/FileFilter.ts +141 -0
  102. package/src/core/security/HookExecutor.d.ts.map +1 -0
  103. package/src/core/security/HookExecutor.ts +178 -0
  104. package/src/core/security/SandboxExecutor.ts +447 -0
  105. package/src/core/security/index.d.ts.map +1 -0
  106. package/src/core/security/index.ts +8 -0
  107. package/src/core/session/AgentLoop.d.ts.map +1 -0
  108. package/src/core/session/AgentLoop.ts +501 -0
  109. package/src/core/session/SessionManager.d.ts.map +1 -0
  110. package/src/core/session/SessionManager.test.ts +183 -0
  111. package/src/core/session/SessionManager.ts +460 -0
  112. package/src/core/session/index.d.ts.map +1 -0
  113. package/src/core/session/index.ts +3 -0
  114. package/src/core/telemetry/Telemetry.d.ts.map +1 -0
  115. package/src/core/telemetry/Telemetry.ts +90 -0
  116. package/src/core/telemetry/TelemetryService.ts +531 -0
  117. package/src/core/telemetry/index.d.ts.map +1 -0
  118. package/src/core/telemetry/index.ts +12 -0
  119. package/src/core/testing/AutoFixer.ts +385 -0
  120. package/src/core/testing/ErrorAnalyzer.ts +499 -0
  121. package/src/core/testing/TestRunner.ts +265 -0
  122. package/src/core/testing/agent-cli-tests.ts +538 -0
  123. package/src/core/testing/index.ts +11 -0
  124. package/src/core/tools/ToolRegistry.d.ts.map +1 -0
  125. package/src/core/tools/ToolRegistry.test.ts +206 -0
  126. package/src/core/tools/ToolRegistry.ts +260 -0
  127. package/src/core/tools/impl/EditFileTool.d.ts.map +1 -0
  128. package/src/core/tools/impl/EditFileTool.ts +97 -0
  129. package/src/core/tools/impl/ListDirectoryTool.d.ts.map +1 -0
  130. package/src/core/tools/impl/ListDirectoryTool.ts +142 -0
  131. package/src/core/tools/impl/MemoryTool.d.ts.map +1 -0
  132. package/src/core/tools/impl/MemoryTool.ts +102 -0
  133. package/src/core/tools/impl/ReadFileTool.d.ts.map +1 -0
  134. package/src/core/tools/impl/ReadFileTool.ts +58 -0
  135. package/src/core/tools/impl/SearchContentTool.d.ts.map +1 -0
  136. package/src/core/tools/impl/SearchContentTool.ts +94 -0
  137. package/src/core/tools/impl/SearchFileTool.d.ts.map +1 -0
  138. package/src/core/tools/impl/SearchFileTool.ts +61 -0
  139. package/src/core/tools/impl/ShellTool.d.ts.map +1 -0
  140. package/src/core/tools/impl/ShellTool.ts +118 -0
  141. package/src/core/tools/impl/TaskTool.d.ts.map +1 -0
  142. package/src/core/tools/impl/TaskTool.ts +207 -0
  143. package/src/core/tools/impl/TodoTool.d.ts.map +1 -0
  144. package/src/core/tools/impl/TodoTool.ts +122 -0
  145. package/src/core/tools/impl/WebFetchTool.d.ts.map +1 -0
  146. package/src/core/tools/impl/WebFetchTool.ts +103 -0
  147. package/src/core/tools/impl/WebSearchTool.d.ts.map +1 -0
  148. package/src/core/tools/impl/WebSearchTool.ts +89 -0
  149. package/src/core/tools/impl/WriteFileTool.d.ts.map +1 -0
  150. package/src/core/tools/impl/WriteFileTool.ts +49 -0
  151. package/src/core/tools/impl/index.d.ts.map +1 -0
  152. package/src/core/tools/impl/index.ts +16 -0
  153. package/src/core/tools/index.d.ts.map +1 -0
  154. package/src/core/tools/index.ts +7 -0
  155. package/src/core/tools/schemas/execution.d.ts.map +1 -0
  156. package/src/core/tools/schemas/execution.ts +42 -0
  157. package/src/core/tools/schemas/file.d.ts.map +1 -0
  158. package/src/core/tools/schemas/file.ts +119 -0
  159. package/src/core/tools/schemas/index.d.ts.map +1 -0
  160. package/src/core/tools/schemas/index.ts +11 -0
  161. package/src/core/tools/schemas/memory.d.ts.map +1 -0
  162. package/src/core/tools/schemas/memory.ts +52 -0
  163. package/src/core/tools/schemas/orchestration.d.ts.map +1 -0
  164. package/src/core/tools/schemas/orchestration.ts +44 -0
  165. package/src/core/tools/schemas/search.d.ts.map +1 -0
  166. package/src/core/tools/schemas/search.ts +112 -0
  167. package/src/core/tools/schemas/todo.d.ts.map +1 -0
  168. package/src/core/tools/schemas/todo.ts +32 -0
  169. package/src/core/tools/schemas/web.d.ts.map +1 -0
  170. package/src/core/tools/schemas/web.ts +86 -0
  171. package/src/core/types/config.d.ts.map +1 -0
  172. package/src/core/types/config.ts +200 -0
  173. package/src/core/types/errors.d.ts.map +1 -0
  174. package/src/core/types/errors.ts +204 -0
  175. package/src/core/types/index.d.ts.map +1 -0
  176. package/src/core/types/index.ts +8 -0
  177. package/src/core/types/session.d.ts.map +1 -0
  178. package/src/core/types/session.ts +216 -0
  179. package/src/core/types/tools.d.ts.map +1 -0
  180. package/src/core/types/tools.ts +157 -0
  181. package/src/core/utils/CheckpointManager.d.ts.map +1 -0
  182. package/src/core/utils/CheckpointManager.ts +327 -0
  183. package/src/core/utils/Logger.d.ts.map +1 -0
  184. package/src/core/utils/Logger.ts +98 -0
  185. package/src/core/utils/RetryManager.ts +471 -0
  186. package/src/core/utils/TokenCounter.d.ts.map +1 -0
  187. package/src/core/utils/TokenCounter.ts +414 -0
  188. package/src/core/utils/VectorMemoryStore.ts +440 -0
  189. package/src/core/utils/helpers.d.ts.map +1 -0
  190. package/src/core/utils/helpers.ts +89 -0
  191. package/src/core/utils/index.d.ts.map +1 -0
  192. package/src/core/utils/index.ts +19 -0
@@ -0,0 +1,401 @@
1
+ // ============================================================================
2
+ // ThinkingBlockRenderer - Collapsible thinking block display for REPL
3
+ // ============================================================================
4
+
5
+ import chalk from 'chalk';
6
+
7
+ // ============================================================================
8
+ // Types
9
+ // ============================================================================
10
+
11
+ export interface ThinkingBlockOptions {
12
+ /** Whether to show expanded thinking blocks (default: false = compact) */
13
+ expanded?: boolean;
14
+ /** Maximum lines to show in streaming preview (default: 4) */
15
+ maxPreviewLines?: number;
16
+ /** Maximum characters per line in preview (default: 80) */
17
+ maxLineLength?: number;
18
+ /** Show elapsed time (default: true) */
19
+ showElapsedTime?: boolean;
20
+ /** Custom icon for thinking block (default: '💭') */
21
+ icon?: string;
22
+ /** Show streaming preview (default: false - only show summary when complete) */
23
+ showStreamingPreview?: boolean;
24
+ }
25
+
26
+ export interface ThinkingBlockState {
27
+ text: string;
28
+ startTime: number;
29
+ isComplete: boolean;
30
+ }
31
+
32
+ // ============================================================================
33
+ // ANSI helpers for terminal control
34
+ // ============================================================================
35
+
36
+ const ANSI = {
37
+ cursorUp: (n: number): string => `\x1b[${n}A`,
38
+ cursorDown: (n: number): string => `\x1b[${n}B`,
39
+ cursorLineStart: (): string => '\x1b[0G',
40
+ clearDown: (): string => '\x1b[0J',
41
+ clearLine: (): string => '\x1b[2K',
42
+ hideCursor: (): string => '\x1b[?25l',
43
+ showCursor: (): string => '\x1b[?25h',
44
+ saveCursor: (): string => '\x1b[s',
45
+ restoreCursor: (): string => '\x1b[u',
46
+ dim: (): string => '\x1b[2m',
47
+ reset: (): string => '\x1b[0m',
48
+ };
49
+
50
+ // ============================================================================
51
+ // ThinkingBlockRenderer
52
+ // ============================================================================
53
+
54
+ /**
55
+ * Renders a collapsible thinking block in the terminal.
56
+ *
57
+ * Features:
58
+ * - Gray/dim color for thinking content (distinguishable from normal output)
59
+ * - Collapsed mode: single summary line with char/line count
60
+ * - Expanded mode: shows preview of thinking content
61
+ * - Optional streaming preview (disabled by default for cleaner UI)
62
+ */
63
+ export class ThinkingBlockRenderer {
64
+ private options: Required<ThinkingBlockOptions>;
65
+ private state: ThinkingBlockState | null = null;
66
+ private renderedLineCount = 0;
67
+ private isActive = false;
68
+
69
+ constructor(options: ThinkingBlockOptions = {}) {
70
+ this.options = {
71
+ expanded: options.expanded ?? false,
72
+ maxPreviewLines: options.maxPreviewLines ?? 4,
73
+ maxLineLength: options.maxLineLength ?? 80,
74
+ showElapsedTime: options.showElapsedTime ?? true,
75
+ icon: options.icon ?? '💭',
76
+ showStreamingPreview: options.showStreamingPreview ?? false,
77
+ };
78
+ }
79
+
80
+ // ========================================================================
81
+ // Public API
82
+ // ========================================================================
83
+
84
+ /**
85
+ * Set whether to show expanded thinking blocks
86
+ */
87
+ setExpanded(expanded: boolean): void {
88
+ this.options.expanded = expanded;
89
+ }
90
+
91
+ /**
92
+ * Check if currently rendering a thinking block
93
+ */
94
+ isRendering(): boolean {
95
+ return this.isActive;
96
+ }
97
+
98
+ /**
99
+ * Start a new thinking block.
100
+ * Call this when thinking content begins streaming.
101
+ */
102
+ start(): void {
103
+ this.state = {
104
+ text: '',
105
+ startTime: Date.now(),
106
+ isComplete: false,
107
+ };
108
+ this.renderedLineCount = 0;
109
+ this.isActive = true;
110
+
111
+ // Show minimal indicator during thinking
112
+ if (this.options.showStreamingPreview) {
113
+ process.stdout.write(ANSI.hideCursor());
114
+ this.renderStreamingIndicator();
115
+ } else {
116
+ // Just show a subtle indicator
117
+ this.renderMinimalIndicator();
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Append text to the thinking block.
123
+ * Updates the display in real-time only if showStreamingPreview is true.
124
+ */
125
+ append(delta: string): void {
126
+ if (!this.state || !this.isActive) return;
127
+ this.state.text += delta;
128
+
129
+ // Only update display if streaming preview is enabled
130
+ if (this.options.showStreamingPreview) {
131
+ this.renderStreaming();
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Complete the thinking block.
137
+ * Clears any streaming display and shows final collapsed/expanded view.
138
+ */
139
+ complete(): void {
140
+ if (!this.state || !this.isActive) return;
141
+ this.state.isComplete = true;
142
+
143
+ // Clear any streaming display
144
+ if (this.options.showStreamingPreview) {
145
+ this.clearRender();
146
+ process.stdout.write(ANSI.showCursor());
147
+ }
148
+
149
+ // Always show final summary (collapsed or expanded)
150
+ this.renderFinal();
151
+ this.isActive = false;
152
+ }
153
+
154
+ /**
155
+ * Cancel and clear the thinking block display.
156
+ */
157
+ cancel(): void {
158
+ if (!this.isActive) return;
159
+ this.clearRender();
160
+ this.state = null;
161
+ this.isActive = false;
162
+ process.stdout.write(ANSI.showCursor());
163
+ }
164
+
165
+ /**
166
+ * Get the current thinking text (if any)
167
+ */
168
+ getText(): string | null {
169
+ return this.state?.text ?? null;
170
+ }
171
+
172
+ /**
173
+ * Get elapsed time since thinking started
174
+ */
175
+ getElapsed(): string {
176
+ if (!this.state) return '';
177
+ return this.formatDuration(this.state.startTime);
178
+ }
179
+
180
+ // ========================================================================
181
+ // Internal rendering
182
+ // ========================================================================
183
+
184
+ /**
185
+ * Render minimal indicator during thinking (default behavior)
186
+ */
187
+ private renderMinimalIndicator(): void {
188
+ const elapsed = this.formatDuration(this.state?.startTime ?? Date.now());
189
+ // Use gray/dim styling for thinking indicator
190
+ process.stdout.write(
191
+ chalk.gray.dim(' ') +
192
+ chalk.gray.dim(this.options.icon + ' ') +
193
+ chalk.gray.dim.italic('thinking') +
194
+ chalk.gray.dim(` (${elapsed})...`) +
195
+ '\r'
196
+ );
197
+ }
198
+
199
+ /**
200
+ * Render streaming indicator (when showStreamingPreview is true)
201
+ */
202
+ private renderStreamingIndicator(): void {
203
+ const elapsed = this.formatDuration(this.state?.startTime ?? Date.now());
204
+ process.stdout.write(
205
+ ANSI.clearLine() +
206
+ chalk.gray.dim(' ') +
207
+ chalk.gray.dim(this.options.icon + ' ') +
208
+ chalk.gray.dim.italic('thinking') +
209
+ chalk.gray.dim(` (${elapsed})...`) +
210
+ '\n'
211
+ );
212
+ this.renderedLineCount = 1;
213
+ }
214
+
215
+ /**
216
+ * Render the streaming thinking block (when showStreamingPreview is true).
217
+ */
218
+ private renderStreaming(): void {
219
+ if (!this.state) return;
220
+
221
+ const text = this.state.text;
222
+ const termWidth = process.stdout.columns || 80;
223
+ const usableWidth = termWidth - 4;
224
+
225
+ // Calculate text dimensions
226
+ const textLines = this.calculateTextLines(text, usableWidth);
227
+ const maxLines = this.options.maxPreviewLines;
228
+
229
+ // Clear previous render
230
+ if (this.renderedLineCount > 0) {
231
+ this.clearLines(this.renderedLineCount);
232
+ }
233
+
234
+ // Render header with elapsed time
235
+ const elapsed = this.formatDuration(this.state.startTime);
236
+ const header = chalk.gray.dim(` ${this.options.icon} `) +
237
+ chalk.gray.dim.italic('thinking') +
238
+ chalk.gray.dim(` (${elapsed})`);
239
+ process.stdout.write(header + '\n');
240
+
241
+ // Render text preview in gray/dim (truncated)
242
+ const lines = this.wrapTextToLines(text, usableWidth);
243
+ const visibleLines = lines.slice(-maxLines);
244
+
245
+ if (lines.length > maxLines) {
246
+ process.stdout.write(chalk.gray.dim(' ...') + '\n');
247
+ this.renderedLineCount = 2;
248
+ } else {
249
+ this.renderedLineCount = 1;
250
+ }
251
+
252
+ for (const line of visibleLines) {
253
+ const truncated = line.slice(0, this.options.maxLineLength);
254
+ // Use gray.dim for thinking content - clearly distinguishable
255
+ process.stdout.write(chalk.gray.dim(' ') + chalk.gray.dim(truncated) + '\n');
256
+ this.renderedLineCount++;
257
+ }
258
+
259
+ // Move cursor back up so next delta overwrites
260
+ if (this.renderedLineCount > 0) {
261
+ process.stdout.write(ANSI.cursorUp(this.renderedLineCount));
262
+ }
263
+ }
264
+
265
+ /**
266
+ * Clear the current streaming render.
267
+ */
268
+ private clearRender(): void {
269
+ if (this.renderedLineCount > 0) {
270
+ this.clearLines(this.renderedLineCount);
271
+ this.renderedLineCount = 0;
272
+ } else {
273
+ // Clear the minimal indicator line
274
+ process.stdout.write(ANSI.clearLine() + ANSI.cursorLineStart());
275
+ }
276
+ }
277
+
278
+ /**
279
+ * Render the final collapsed/expanded thinking block.
280
+ * Uses gray/dim color to distinguish from normal output.
281
+ */
282
+ private renderFinal(): void {
283
+ if (!this.state || !this.state.text.trim()) return;
284
+
285
+ const text = this.state.text.trim();
286
+ const elapsed = this.formatDuration(this.state.startTime);
287
+ const charCount = text.length;
288
+ const lineCount = text.split('\n').length;
289
+
290
+ if (!this.options.expanded) {
291
+ // Collapsed: show single summary line in gray
292
+ // This is the default - minimal visual impact
293
+ console.log(
294
+ chalk.gray.dim(' └─ ') +
295
+ chalk.gray.dim(this.options.icon + ' ') +
296
+ chalk.gray.dim.italic('thinking') +
297
+ chalk.gray.dim(` (${charCount} chars, ${elapsed})`)
298
+ );
299
+ } else {
300
+ // Expanded: show preview lines in gray
301
+ const lines = text.split('\n');
302
+ const previewLines = lines.slice(0, this.options.maxPreviewLines);
303
+ const truncated = lines.length > this.options.maxPreviewLines;
304
+
305
+ console.log(
306
+ chalk.gray.dim(' ┌─ ') +
307
+ chalk.gray.dim(this.options.icon + ' ') +
308
+ chalk.gray.dim.italic(`thinking (${elapsed})`)
309
+ );
310
+
311
+ for (const line of previewLines) {
312
+ const truncatedLine = line.slice(0, this.options.maxLineLength);
313
+ // Gray/dim styling for thinking content
314
+ console.log(chalk.gray.dim(' │ ') + chalk.gray.dim(truncatedLine));
315
+ }
316
+
317
+ if (truncated) {
318
+ console.log(chalk.gray.dim(` └─ ... (${lines.length - this.options.maxPreviewLines} more lines)`));
319
+ } else {
320
+ console.log(chalk.gray.dim(' └─ end'));
321
+ }
322
+ }
323
+ }
324
+
325
+ // ========================================================================
326
+ // Helper methods
327
+ // ========================================================================
328
+
329
+ /**
330
+ * Clear N lines from current cursor position upward.
331
+ */
332
+ private clearLines(count: number): void {
333
+ if (count <= 0) return;
334
+
335
+ process.stdout.write(
336
+ ANSI.cursorUp(count) +
337
+ ANSI.clearLine() +
338
+ '\n'.repeat(count - 1) +
339
+ ANSI.cursorUp(count - 1) +
340
+ ANSI.clearLine() +
341
+ ANSI.cursorLineStart()
342
+ );
343
+ }
344
+
345
+ /**
346
+ * Calculate how many terminal lines a text will occupy.
347
+ */
348
+ private calculateTextLines(text: string, width: number): number {
349
+ if (!text) return 0;
350
+ const lines = text.split('\n');
351
+ let totalLines = 0;
352
+ for (const line of lines) {
353
+ if (line.length === 0) {
354
+ totalLines += 1;
355
+ } else {
356
+ totalLines += Math.ceil(line.length / width);
357
+ }
358
+ }
359
+ return Math.max(1, totalLines);
360
+ }
361
+
362
+ /**
363
+ * Wrap text to lines respecting terminal width.
364
+ */
365
+ private wrapTextToLines(text: string, width: number): string[] {
366
+ if (!text) return [];
367
+
368
+ const result: string[] = [];
369
+ const paragraphs = text.split('\n');
370
+
371
+ for (const paragraph of paragraphs) {
372
+ if (paragraph.length === 0) {
373
+ result.push('');
374
+ continue;
375
+ }
376
+ for (let i = 0; i < paragraph.length; i += width) {
377
+ result.push(paragraph.slice(i, i + width));
378
+ }
379
+ }
380
+
381
+ return result;
382
+ }
383
+
384
+ /**
385
+ * Format duration in human-readable form.
386
+ */
387
+ private formatDuration(startTime: number): string {
388
+ const ms = Date.now() - startTime;
389
+ if (ms < 1000) return `${ms}ms`;
390
+ if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;
391
+ return `${Math.floor(ms / 60000)}m ${Math.floor((ms % 60000) / 1000)}s`;
392
+ }
393
+ }
394
+
395
+ // ============================================================================
396
+ // Factory function
397
+ // ============================================================================
398
+
399
+ export function createThinkingBlockRenderer(options?: ThinkingBlockOptions): ThinkingBlockRenderer {
400
+ return new ThinkingBlockRenderer(options);
401
+ }
@@ -0,0 +1,27 @@
1
+ // ============================================================================
2
+ // UI Components Export
3
+ // ============================================================================
4
+
5
+ // Ink-based components
6
+ export {
7
+ Spinner,
8
+ StatusBar,
9
+ InputBox,
10
+ MessageList,
11
+ ToolCallPanel,
12
+ ThinkingBlock,
13
+ ProgressBar,
14
+ ConfirmDialog,
15
+ SelectList,
16
+ Toast,
17
+ Colors,
18
+ } from './InkComponents.js';
19
+
20
+ export { NovaInkApp } from './NovaInkApp.js';
21
+ export { InkAppRunner, createInkApp } from './InkAppRunner.js';
22
+ export type { InkAppOptions } from './InkAppRunner.js';
23
+
24
+ // Legacy components
25
+ export { ThinkingBlockRenderer } from './ThinkingBlockRenderer.js';
26
+ export { ProgressBar as ProgressBarLegacy } from './ProgressBar.js';
27
+ export { ConfirmDialog as ConfirmDialogLegacy } from './ConfirmDialog.js';