@ebowwa/coder 0.7.64 → 0.7.66
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.
- package/dist/index.js +36233 -32
- package/dist/interfaces/ui/terminal/cli/index.js +34318 -158
- package/dist/interfaces/ui/terminal/native/README.md +53 -0
- package/dist/interfaces/ui/terminal/native/claude_code_native.darwin-x64.node +0 -0
- package/dist/interfaces/ui/terminal/native/claude_code_native.dylib +0 -0
- package/dist/interfaces/ui/terminal/native/index.d.ts +0 -0
- package/dist/interfaces/ui/terminal/native/index.darwin-arm64.node +0 -0
- package/dist/interfaces/ui/terminal/native/index.js +43 -0
- package/dist/interfaces/ui/terminal/native/index.node +0 -0
- package/dist/interfaces/ui/terminal/native/package.json +34 -0
- package/dist/native/README.md +53 -0
- package/dist/native/claude_code_native.darwin-x64.node +0 -0
- package/dist/native/claude_code_native.dylib +0 -0
- package/dist/native/index.d.ts +0 -480
- package/dist/native/index.darwin-arm64.node +0 -0
- package/dist/native/index.js +43 -1625
- package/dist/native/index.node +0 -0
- package/dist/native/package.json +34 -0
- package/native/index.darwin-arm64.node +0 -0
- package/native/index.js +33 -19
- package/package.json +3 -2
- package/packages/src/core/agent-loop/__tests__/compaction.test.ts +17 -14
- package/packages/src/core/agent-loop/compaction.ts +6 -2
- package/packages/src/core/agent-loop/index.ts +2 -0
- package/packages/src/core/agent-loop/loop-state.ts +1 -1
- package/packages/src/core/agent-loop/turn-executor.ts +4 -0
- package/packages/src/core/agent-loop/types.ts +4 -0
- package/packages/src/core/api-client-impl.ts +377 -176
- package/packages/src/core/cognitive-security/hooks.ts +2 -1
- package/packages/src/core/config/todo +7 -0
- package/packages/src/core/context/__tests__/integration.test.ts +334 -0
- package/packages/src/core/context/compaction.ts +170 -0
- package/packages/src/core/context/constants.ts +58 -0
- package/packages/src/core/context/extraction.ts +85 -0
- package/packages/src/core/context/index.ts +66 -0
- package/packages/src/core/context/summarization.ts +251 -0
- package/packages/src/core/context/token-estimation.ts +98 -0
- package/packages/src/core/context/types.ts +59 -0
- package/packages/src/core/models.ts +81 -4
- package/packages/src/core/normalizers/todo +5 -1
- package/packages/src/core/providers/README.md +230 -0
- package/packages/src/core/providers/__tests__/providers.test.ts +135 -0
- package/packages/src/core/providers/index.ts +419 -0
- package/packages/src/core/providers/types.ts +132 -0
- package/packages/src/core/retry.ts +10 -0
- package/packages/src/ecosystem/tools/index.ts +174 -0
- package/packages/src/index.ts +23 -2
- package/packages/src/interfaces/ui/index.ts +17 -20
- package/packages/src/interfaces/ui/spinner.ts +2 -2
- package/packages/src/interfaces/ui/terminal/bridge/index.ts +370 -0
- package/packages/src/interfaces/ui/terminal/bridge/ipc.ts +829 -0
- package/packages/src/interfaces/ui/terminal/bridge/screen-export.ts +968 -0
- package/packages/src/interfaces/ui/terminal/bridge/types.ts +226 -0
- package/packages/src/interfaces/ui/terminal/bridge/useBridge.ts +210 -0
- package/packages/src/interfaces/ui/terminal/cli/bootstrap.ts +132 -0
- package/packages/src/interfaces/ui/terminal/cli/index.ts +200 -13
- package/packages/src/interfaces/ui/terminal/cli/interactive/index.ts +110 -0
- package/packages/src/interfaces/ui/terminal/cli/interactive/input-handler.ts +402 -0
- package/packages/src/interfaces/ui/terminal/cli/interactive/interactive-runner.ts +820 -0
- package/packages/src/interfaces/ui/terminal/cli/interactive/message-store.ts +299 -0
- package/packages/src/interfaces/ui/terminal/cli/interactive/types.ts +274 -0
- package/packages/src/interfaces/ui/terminal/shared/index.ts +13 -0
- package/packages/src/interfaces/ui/terminal/shared/query.ts +9 -3
- package/packages/src/interfaces/ui/terminal/shared/setup.ts +5 -1
- package/packages/src/interfaces/ui/terminal/shared/spinner-frames.ts +73 -0
- package/packages/src/interfaces/ui/terminal/shared/status-line.ts +10 -2
- package/packages/src/native/index.ts +404 -27
- package/packages/src/native/tui_v2_types.ts +39 -0
- package/packages/src/teammates/coordination.test.ts +279 -0
- package/packages/src/teammates/coordination.ts +646 -0
- package/packages/src/teammates/index.ts +95 -25
- package/packages/src/teammates/integration.test.ts +272 -0
- package/packages/src/teammates/runner.test.ts +235 -0
- package/packages/src/teammates/runner.ts +750 -0
- package/packages/src/teammates/schemas.ts +673 -0
- package/packages/src/types/index.ts +1 -0
- package/packages/src/core/context-compaction.ts +0 -578
- package/packages/src/interfaces/ui/Screenshot 2026-03-02 at 9.23.10/342/200/257PM.png +0 -0
- package/packages/src/interfaces/ui/Screenshot 2026-03-03 at 10.55.11/342/200/257AM.png +0 -0
- package/packages/src/interfaces/ui/terminal/tui/HelpPanel.tsx +0 -262
- package/packages/src/interfaces/ui/terminal/tui/InputContext.tsx +0 -232
- package/packages/src/interfaces/ui/terminal/tui/InputField.tsx +0 -62
- package/packages/src/interfaces/ui/terminal/tui/InteractiveTUI.tsx +0 -537
- package/packages/src/interfaces/ui/terminal/tui/MessageArea.tsx +0 -107
- package/packages/src/interfaces/ui/terminal/tui/MessageStore.tsx +0 -240
- package/packages/src/interfaces/ui/terminal/tui/StatusBar.tsx +0 -54
- package/packages/src/interfaces/ui/terminal/tui/commands.ts +0 -438
- package/packages/src/interfaces/ui/terminal/tui/components/InteractiveElements.tsx +0 -584
- package/packages/src/interfaces/ui/terminal/tui/components/MultilineInput.tsx +0 -614
- package/packages/src/interfaces/ui/terminal/tui/components/PaneManager.tsx +0 -333
- package/packages/src/interfaces/ui/terminal/tui/components/Sidebar.tsx +0 -604
- package/packages/src/interfaces/ui/terminal/tui/components/index.ts +0 -118
- package/packages/src/interfaces/ui/terminal/tui/console.ts +0 -49
- package/packages/src/interfaces/ui/terminal/tui/index.ts +0 -90
- package/packages/src/interfaces/ui/terminal/tui/run.tsx +0 -42
- package/packages/src/interfaces/ui/terminal/tui/spinner.ts +0 -69
- package/packages/src/interfaces/ui/terminal/tui/tui-app.tsx +0 -390
- package/packages/src/interfaces/ui/terminal/tui/tui-footer.ts +0 -422
- package/packages/src/interfaces/ui/terminal/tui/types.ts +0 -186
- package/packages/src/interfaces/ui/terminal/tui/useInputHandler.ts +0 -104
- package/packages/src/interfaces/ui/terminal/tui/useNativeInput.ts +0 -239
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spinner Frames
|
|
3
|
+
* Single source of truth for spinner animation frames
|
|
4
|
+
*
|
|
5
|
+
* These are pure data arrays with no dependencies, suitable for any UI context.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Default braille spinner frames
|
|
10
|
+
*/
|
|
11
|
+
export const spinnerFrames: string[] = [
|
|
12
|
+
"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏",
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Dot-based spinner frames (works in more terminals)
|
|
17
|
+
*/
|
|
18
|
+
export const dotSpinnerFrames: string[] = [
|
|
19
|
+
"⠁", "⠈", "⠐", "⠠", "⢀", "⣀", "⣄", "⣤", "⣦", "⣶", "⣷", "⣸", "⣼", "⣾", "⣽", "⣻",
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* ASCII-only spinner frames (maximum compatibility)
|
|
24
|
+
*/
|
|
25
|
+
export const asciiSpinnerFrames: string[] = [
|
|
26
|
+
"|", "/", "-", "\\",
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Arrow spinner frames
|
|
31
|
+
*/
|
|
32
|
+
export const arrowSpinnerFrames: string[] = [
|
|
33
|
+
"→", "↘", "↓", "↙", "←", "↖", "↑", "↗",
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Simple dot progress frames
|
|
38
|
+
*/
|
|
39
|
+
export const simpleDotFrames: string[] = [
|
|
40
|
+
". ", ".. ", "...", " ..", " .", " ",
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Tool activity spinner frames
|
|
45
|
+
*/
|
|
46
|
+
export const toolSpinnerFrames: string[] = [
|
|
47
|
+
"⚙ ", "⚙⚙", " ⚙",
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Get next frame in sequence
|
|
52
|
+
*/
|
|
53
|
+
export function nextFrame(frames: string[], index: number): string {
|
|
54
|
+
return frames[index % frames.length] ?? frames[0] ?? "";
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Create a spinner iterator
|
|
59
|
+
*/
|
|
60
|
+
export function* createSpinnerIterator(frames: string[]): Generator<string, never, never> {
|
|
61
|
+
let index = 0;
|
|
62
|
+
while (true) {
|
|
63
|
+
yield frames[index % frames.length] ?? frames[0] ?? "";
|
|
64
|
+
index++;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Get frame at specific index
|
|
70
|
+
*/
|
|
71
|
+
export function getFrame(frames: string[], index: number): string {
|
|
72
|
+
return frames[index % frames.length] ?? frames[0] ?? "";
|
|
73
|
+
}
|
|
@@ -203,6 +203,7 @@ export function renderStatusLine(options: StatusLineOptions): string {
|
|
|
203
203
|
model,
|
|
204
204
|
permissionMode,
|
|
205
205
|
isLoading = false,
|
|
206
|
+
verbose = false,
|
|
206
207
|
} = options;
|
|
207
208
|
|
|
208
209
|
const contextInfo = calculateContextInfo(tokensUsed, model);
|
|
@@ -223,6 +224,11 @@ export function renderStatusLine(options: StatusLineOptions): string {
|
|
|
223
224
|
// 2. Add permission mode
|
|
224
225
|
parts.push(formatPermissionMode(permissionMode));
|
|
225
226
|
|
|
227
|
+
// 3. Add version in verbose mode
|
|
228
|
+
if (verbose) {
|
|
229
|
+
parts.push(`currentVersion: ${VERSION}`);
|
|
230
|
+
}
|
|
231
|
+
|
|
226
232
|
// Join with comma-space
|
|
227
233
|
let statusLine = parts.join(", ");
|
|
228
234
|
|
|
@@ -238,13 +244,15 @@ export function renderStatusLine(options: StatusLineOptions): string {
|
|
|
238
244
|
* Render a compact status line for narrow displays
|
|
239
245
|
*/
|
|
240
246
|
export function renderCompactStatusLine(options: StatusLineOptions): string {
|
|
241
|
-
const { tokensUsed, model, isLoading = false } = options;
|
|
247
|
+
const { tokensUsed, model, permissionMode, isLoading = false } = options;
|
|
242
248
|
const contextInfo = calculateContextInfo(tokensUsed, model);
|
|
249
|
+
const permDisplay = getPermissionModeDisplay(permissionMode);
|
|
243
250
|
|
|
244
|
-
// Compact: "8% |
|
|
251
|
+
// Compact: "8% | 5.0k | bypass"
|
|
245
252
|
const parts = [
|
|
246
253
|
formatContextPercent(contextInfo.percentRemaining),
|
|
247
254
|
contextInfo.tokenDisplay.split(" ")[0] ?? "0", // Just the number
|
|
255
|
+
permDisplay.label.split(" ")[0] ?? permDisplay.label, // First word of label (e.g., "bypass" from "bypass permissions")
|
|
248
256
|
];
|
|
249
257
|
|
|
250
258
|
let line = parts.join(chalk.dim(" | "));
|
|
@@ -70,9 +70,138 @@ export interface MultiEditPreviewEntry {
|
|
|
70
70
|
replacementCount: number;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
// ===== Grep Types =====
|
|
74
|
+
|
|
75
|
+
/** Options for grep search operations */
|
|
76
|
+
export interface GrepQueryOptions {
|
|
77
|
+
/** Case insensitive matching */
|
|
78
|
+
caseInsensitive?: boolean;
|
|
79
|
+
/** Maximum number of results to return */
|
|
80
|
+
maxResults?: number;
|
|
81
|
+
/** Glob patterns for files to include */
|
|
82
|
+
includePatterns?: string[];
|
|
83
|
+
/** Glob patterns for files/dirs to exclude */
|
|
84
|
+
excludePatterns?: string[];
|
|
85
|
+
/** Treat pattern as literal string (no regex) */
|
|
86
|
+
literal?: boolean;
|
|
87
|
+
/** Match whole words only */
|
|
88
|
+
wholeWord?: boolean;
|
|
89
|
+
/** Invert match (show non-matching lines) */
|
|
90
|
+
invert?: boolean;
|
|
91
|
+
/** Enable multiline matching */
|
|
92
|
+
multiline?: boolean;
|
|
93
|
+
/** Number of context lines (sets both before and after) */
|
|
94
|
+
contextLines?: number;
|
|
95
|
+
/** Number of lines to show before match */
|
|
96
|
+
contextBefore?: number;
|
|
97
|
+
/** Number of lines to show after match */
|
|
98
|
+
contextAfter?: number;
|
|
99
|
+
/** Maximum directory depth */
|
|
100
|
+
maxDepth?: number;
|
|
101
|
+
/** Follow symbolic links */
|
|
102
|
+
followSymlinks?: boolean;
|
|
103
|
+
/** Skip hidden files and directories */
|
|
104
|
+
skipHidden?: boolean;
|
|
105
|
+
/** Respect .gitignore rules */
|
|
106
|
+
respectGitignore?: boolean;
|
|
107
|
+
/** Skip binary files */
|
|
108
|
+
skipBinary?: boolean;
|
|
109
|
+
/** Maximum file size in bytes */
|
|
110
|
+
maxFilesize?: number;
|
|
111
|
+
/** File extensions to include */
|
|
112
|
+
extensions?: string[];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** A single grep match result */
|
|
116
|
+
export interface GrepMatch {
|
|
117
|
+
/** File path containing the match */
|
|
118
|
+
path: string;
|
|
119
|
+
/** 1-based line number */
|
|
120
|
+
line: number;
|
|
121
|
+
/** 1-based column number */
|
|
122
|
+
column: number;
|
|
123
|
+
/** The matched line content */
|
|
124
|
+
content: string;
|
|
125
|
+
/** Length of the match in bytes */
|
|
126
|
+
matchLength: number;
|
|
127
|
+
/** Lines before the match (for context) */
|
|
128
|
+
contextBefore: string[];
|
|
129
|
+
/** Lines after the match (for context) */
|
|
130
|
+
contextAfter: string[];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Result of a grep search operation */
|
|
134
|
+
export interface GrepResult {
|
|
135
|
+
/** Total number of matches found */
|
|
136
|
+
totalCount: number;
|
|
137
|
+
/** All matches found */
|
|
138
|
+
matches: GrepMatch[];
|
|
139
|
+
/** Number of files searched */
|
|
140
|
+
filesSearched: number;
|
|
141
|
+
/** Number of files skipped */
|
|
142
|
+
filesSkipped: number;
|
|
143
|
+
/** Time spent searching in milliseconds */
|
|
144
|
+
durationMs: number;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Count result per file */
|
|
148
|
+
export interface GrepCountResult {
|
|
149
|
+
/** File path */
|
|
150
|
+
path: string;
|
|
151
|
+
/** Number of matches in this file */
|
|
152
|
+
count: number;
|
|
153
|
+
/** Number of lines in this file */
|
|
154
|
+
lineCount: number;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** Result for files-with-matches mode */
|
|
158
|
+
export interface GrepFilesResult {
|
|
159
|
+
/** List of file paths containing matches */
|
|
160
|
+
files: string[];
|
|
161
|
+
/** Total number of files with matches */
|
|
162
|
+
totalMatches: number;
|
|
163
|
+
}
|
|
164
|
+
|
|
73
165
|
// ===== Quant Types =====
|
|
74
166
|
|
|
75
167
|
/** OHLCV candlestick data */
|
|
168
|
+
// ===== TUI v2 Types =====
|
|
169
|
+
|
|
170
|
+
export interface RenderMessage {
|
|
171
|
+
role: string;
|
|
172
|
+
content: string;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export interface RenderState {
|
|
176
|
+
messages: RenderMessage[];
|
|
177
|
+
inputValue: string;
|
|
178
|
+
cursorPos: number;
|
|
179
|
+
statusText: string;
|
|
180
|
+
isLoading: boolean;
|
|
181
|
+
streamingText: string;
|
|
182
|
+
model: string;
|
|
183
|
+
showHelp: boolean;
|
|
184
|
+
helpText: string;
|
|
185
|
+
searchMode: boolean;
|
|
186
|
+
searchQuery: string;
|
|
187
|
+
searchResults: SearchResult[];
|
|
188
|
+
searchSelected: number;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export interface InputEvent {
|
|
192
|
+
eventType: "key" | "resize" | "none";
|
|
193
|
+
key?: string;
|
|
194
|
+
modifiers?: string;
|
|
195
|
+
newWidth?: number;
|
|
196
|
+
newHeight?: number;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export interface SearchResult {
|
|
200
|
+
filePath: string;
|
|
201
|
+
lineNumber: number;
|
|
202
|
+
content: string;
|
|
203
|
+
}
|
|
204
|
+
|
|
76
205
|
export interface OHLCV {
|
|
77
206
|
timestamp: number;
|
|
78
207
|
open: number;
|
|
@@ -246,6 +375,9 @@ export interface NativeModule {
|
|
|
246
375
|
/** Highlight a diff with ANSI colors */
|
|
247
376
|
highlight_diff: (oldText: string, newText: string, options?: DiffOptions) => HighlightDiffResult;
|
|
248
377
|
|
|
378
|
+
// ===== Grep Functions =====
|
|
379
|
+
|
|
380
|
+
/** Legacy search_files (deprecated, use grep_search instead) */
|
|
249
381
|
search_files: (
|
|
250
382
|
pattern: string,
|
|
251
383
|
path: string,
|
|
@@ -267,6 +399,17 @@ export interface NativeModule {
|
|
|
267
399
|
files_searched: number;
|
|
268
400
|
};
|
|
269
401
|
|
|
402
|
+
/** Search for pattern in files with full options */
|
|
403
|
+
grep_search: (pattern: string, path: string, options?: GrepQueryOptions) => Promise<GrepResult>;
|
|
404
|
+
|
|
405
|
+
/** Count matches per file */
|
|
406
|
+
grep_count: (pattern: string, path: string, options?: GrepQueryOptions) => Promise<GrepCountResult[]>;
|
|
407
|
+
|
|
408
|
+
/** List files containing matches */
|
|
409
|
+
grep_files: (pattern: string, path: string, options?: GrepQueryOptions) => Promise<GrepFilesResult>;
|
|
410
|
+
|
|
411
|
+
// ===== Token Functions =====
|
|
412
|
+
|
|
270
413
|
count_tokens: (text: string) => number;
|
|
271
414
|
|
|
272
415
|
calculate_diff: (
|
|
@@ -539,6 +682,11 @@ export function loadNative(): NativeModule {
|
|
|
539
682
|
preview_multi_edits: native.previewMultiEdits || native.preview_multi_edits,
|
|
540
683
|
apply_multi_edits: native.applyMultiEdits || native.apply_multi_edits,
|
|
541
684
|
|
|
685
|
+
// Grep functions (NAPI exports camelCase for async functions)
|
|
686
|
+
grep_search: native.grepSearch,
|
|
687
|
+
grep_count: native.grepCount,
|
|
688
|
+
grep_files: native.grepFiles,
|
|
689
|
+
|
|
542
690
|
// Cognitive Security - Action Module (NAPI exports camelCase)
|
|
543
691
|
classify_operation: native.classifyOperation,
|
|
544
692
|
get_action_types: native.getActionTypes,
|
|
@@ -985,9 +1133,42 @@ function getFallbackModule(): NativeModule {
|
|
|
985
1133
|
};
|
|
986
1134
|
},
|
|
987
1135
|
|
|
988
|
-
count_tokens: (text) => {
|
|
989
|
-
//
|
|
990
|
-
|
|
1136
|
+
count_tokens: (text: string): number => {
|
|
1137
|
+
// Sophisticated fallback matching Rust implementation
|
|
1138
|
+
if (!text || text.length === 0) return 0;
|
|
1139
|
+
|
|
1140
|
+
const bytes = text.length;
|
|
1141
|
+
|
|
1142
|
+
// Count whitespace
|
|
1143
|
+
const whitespace = text.split('').filter(c => /\s/.test(c)).length;
|
|
1144
|
+
|
|
1145
|
+
// Count punctuation
|
|
1146
|
+
const punctuation = text.split('').filter(c => /[^\w\s]/.test(c)).length;
|
|
1147
|
+
|
|
1148
|
+
// Detect if this is code (heuristic)
|
|
1149
|
+
const isCode = text.includes('{') ||
|
|
1150
|
+
text.includes('}') ||
|
|
1151
|
+
text.includes(';') ||
|
|
1152
|
+
text.includes('fn ') ||
|
|
1153
|
+
text.includes('function') ||
|
|
1154
|
+
text.includes('const ') ||
|
|
1155
|
+
text.includes('let ') ||
|
|
1156
|
+
text.includes('import ');
|
|
1157
|
+
|
|
1158
|
+
// Base estimate: ~4 characters per token
|
|
1159
|
+
const baseEstimate = Math.floor(bytes / 4);
|
|
1160
|
+
|
|
1161
|
+
// Adjust for code (typically more tokens)
|
|
1162
|
+
const codeAdjustment = isCode ? Math.floor(bytes / 10) : 0;
|
|
1163
|
+
|
|
1164
|
+
// Adjust for whitespace (tokens are often split on whitespace)
|
|
1165
|
+
const whitespaceAdjustment = Math.floor(whitespace * 0.2);
|
|
1166
|
+
|
|
1167
|
+
// Adjust for punctuation
|
|
1168
|
+
const punctuationAdjustment = Math.floor(punctuation * 0.5);
|
|
1169
|
+
|
|
1170
|
+
// Combine estimates, minimum 1
|
|
1171
|
+
return Math.max(1, baseEstimate + codeAdjustment + whitespaceAdjustment + punctuationAdjustment);
|
|
991
1172
|
},
|
|
992
1173
|
|
|
993
1174
|
calculate_diff: (oldText, newText) => {
|
|
@@ -1025,34 +1206,102 @@ function getFallbackModule(): NativeModule {
|
|
|
1025
1206
|
}
|
|
1026
1207
|
|
|
1027
1208
|
switch (strategy) {
|
|
1028
|
-
case "truncate":
|
|
1209
|
+
case "truncate": {
|
|
1210
|
+
// Smart truncation: find paragraph or sentence boundary
|
|
1211
|
+
const half = Math.floor(maxChars / 2);
|
|
1212
|
+
const searchStart = Math.max(0, half - 100);
|
|
1213
|
+
const searchEnd = Math.min(content.length, half + 100);
|
|
1214
|
+
|
|
1215
|
+
// Try to find paragraph break
|
|
1216
|
+
let breakPoint = half;
|
|
1217
|
+
for (let i = searchEnd; i >= searchStart; i--) {
|
|
1218
|
+
if (content[i] === '\n' && content[i + 1] === '\n') {
|
|
1219
|
+
breakPoint = i;
|
|
1220
|
+
break;
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1029
1224
|
return (
|
|
1030
|
-
content.slice(0,
|
|
1031
|
-
"\n\n... [truncated] ...\n\n" +
|
|
1032
|
-
content.slice(-
|
|
1225
|
+
content.slice(0, breakPoint) +
|
|
1226
|
+
"\n\n... [content truncated: " + Math.floor((content.length - maxChars) / charsPerToken) + " tokens omitted] ...\n\n" +
|
|
1227
|
+
content.slice(-half)
|
|
1033
1228
|
);
|
|
1034
|
-
|
|
1035
|
-
|
|
1229
|
+
}
|
|
1230
|
+
case "summarize": {
|
|
1231
|
+
// Return beginning, key sections, and end
|
|
1232
|
+
const lines = content.split("\n");
|
|
1233
|
+
const firstCount = Math.min(20, Math.floor(lines.length / 4));
|
|
1234
|
+
const lastCount = Math.min(20, Math.floor(lines.length / 4));
|
|
1036
1235
|
const quarter = Math.floor(maxChars / 4);
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1236
|
+
|
|
1237
|
+
let result = "=== BEGINNING ===\n";
|
|
1238
|
+
result += lines.slice(0, firstCount).join("\n").slice(0, quarter) + "\n\n";
|
|
1239
|
+
|
|
1240
|
+
result += "=== KEY LINES ===\n";
|
|
1241
|
+
const importantLines = lines.filter(line => {
|
|
1242
|
+
const t = line.trim();
|
|
1243
|
+
return t.startsWith('#') ||
|
|
1244
|
+
t.startsWith('function') ||
|
|
1245
|
+
t.startsWith('const') ||
|
|
1246
|
+
t.startsWith('class') ||
|
|
1247
|
+
t.startsWith('export') ||
|
|
1248
|
+
t.startsWith('import') ||
|
|
1249
|
+
t.startsWith('async ') ||
|
|
1250
|
+
t.startsWith('pub fn') ||
|
|
1251
|
+
t.startsWith('fn ');
|
|
1252
|
+
});
|
|
1253
|
+
result += importantLines.slice(0, 20).join("\n").slice(0, quarter) + "\n\n";
|
|
1254
|
+
|
|
1255
|
+
result += "=== END ===\n";
|
|
1256
|
+
result += lines.slice(-lastCount).join("\n").slice(0, quarter);
|
|
1257
|
+
|
|
1258
|
+
return result;
|
|
1259
|
+
}
|
|
1260
|
+
case "extract": {
|
|
1261
|
+
// Extract structure (headings, declarations, etc.)
|
|
1045
1262
|
const lines = content.split("\n");
|
|
1046
|
-
const
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1263
|
+
const result: string[] = [];
|
|
1264
|
+
let currentChars = 0;
|
|
1265
|
+
let inCodeBlock = false;
|
|
1266
|
+
|
|
1267
|
+
for (const line of lines) {
|
|
1268
|
+
// Track code blocks
|
|
1269
|
+
if (line.trim().startsWith('```')) {
|
|
1270
|
+
inCodeBlock = !inCodeBlock;
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
const trimmed = line.trim();
|
|
1274
|
+
|
|
1275
|
+
// Include important lines
|
|
1276
|
+
const isImportant =
|
|
1277
|
+
trimmed.startsWith('#') ||
|
|
1278
|
+
trimmed.startsWith('##') ||
|
|
1279
|
+
trimmed.startsWith('###') ||
|
|
1280
|
+
trimmed.startsWith('function ') ||
|
|
1281
|
+
trimmed.startsWith('const ') ||
|
|
1282
|
+
trimmed.startsWith('let ') ||
|
|
1283
|
+
trimmed.startsWith('class ') ||
|
|
1284
|
+
trimmed.startsWith('interface ') ||
|
|
1285
|
+
trimmed.startsWith('type ') ||
|
|
1286
|
+
trimmed.startsWith('export ') ||
|
|
1287
|
+
trimmed.startsWith('import ') ||
|
|
1288
|
+
trimmed.startsWith('async ') ||
|
|
1289
|
+
trimmed.startsWith('pub fn') ||
|
|
1290
|
+
trimmed.startsWith('fn ') ||
|
|
1291
|
+
trimmed.startsWith('struct ') ||
|
|
1292
|
+
trimmed.startsWith('impl ') ||
|
|
1293
|
+
trimmed.startsWith('// TODO') ||
|
|
1294
|
+
trimmed.startsWith('// FIXME') ||
|
|
1295
|
+
trimmed.startsWith('// NOTE');
|
|
1296
|
+
|
|
1297
|
+
if (isImportant && currentChars + line.length < maxChars) {
|
|
1298
|
+
result.push(line);
|
|
1299
|
+
currentChars += line.length + 1;
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
return result.length > 0 ? result.join("\n") : content.slice(0, maxChars);
|
|
1304
|
+
}
|
|
1056
1305
|
default:
|
|
1057
1306
|
return content.slice(0, maxChars);
|
|
1058
1307
|
}
|
|
@@ -1862,6 +2111,17 @@ function getFallbackModule(): NativeModule {
|
|
|
1862
2111
|
throw new Error("Native TUI not available in fallback mode. Build the Rust native module.");
|
|
1863
2112
|
},
|
|
1864
2113
|
is_native_tui_available: () => false,
|
|
2114
|
+
|
|
2115
|
+
// Grep functions (fallback - use Grep tool instead)
|
|
2116
|
+
grep_search: async (_pattern: string, _path: string, _options?: GrepQueryOptions): Promise<GrepResult> => {
|
|
2117
|
+
throw new Error("grep_search not available in fallback mode. Build the Rust native module or use the Grep tool.");
|
|
2118
|
+
},
|
|
2119
|
+
grep_count: async (_pattern: string, _path: string, _options?: GrepQueryOptions): Promise<GrepCountResult[]> => {
|
|
2120
|
+
throw new Error("grep_count not available in fallback mode. Build the Rust native module or use the Grep tool.");
|
|
2121
|
+
},
|
|
2122
|
+
grep_files: async (_pattern: string, _path: string, _options?: GrepQueryOptions): Promise<GrepFilesResult> => {
|
|
2123
|
+
throw new Error("grep_files not available in fallback mode. Build the Rust native module or use the Grep tool.");
|
|
2124
|
+
},
|
|
1865
2125
|
};
|
|
1866
2126
|
}
|
|
1867
2127
|
|
|
@@ -2343,3 +2603,120 @@ export function quantCorrelation(x: number[], y: number[]): number {
|
|
|
2343
2603
|
return quant.quant_correlation(ptrX, ptrY, x.length);
|
|
2344
2604
|
}
|
|
2345
2605
|
|
|
2606
|
+
|
|
2607
|
+
|
|
2608
|
+
|
|
2609
|
+
// ===== TUI v2 Native Renderer Re-export =====
|
|
2610
|
+
// Re-export the NativeRenderer class directly from the native module (the .node binary)
|
|
2611
|
+
// Use lazy loading to avoid circular dependency issues during module initialization
|
|
2612
|
+
|
|
2613
|
+
import * as path from "path";
|
|
2614
|
+
|
|
2615
|
+
let _NativeRendererClass: any = null;
|
|
2616
|
+
|
|
2617
|
+
/**
|
|
2618
|
+
* Find the native module directory at runtime
|
|
2619
|
+
* Works both in development and when bundled
|
|
2620
|
+
*/
|
|
2621
|
+
function findNativeDir(): string | null {
|
|
2622
|
+
// Try multiple possible locations for the native directory
|
|
2623
|
+
const possiblePaths = [
|
|
2624
|
+
// When running from dist/ (bundled)
|
|
2625
|
+
path.resolve(__dirname, "..", "..", "..", "native"),
|
|
2626
|
+
// When running from dist/native/ (native loader location)
|
|
2627
|
+
path.resolve(__dirname, "..", "..", "native"),
|
|
2628
|
+
// When running from packages/src/native/ (development)
|
|
2629
|
+
path.resolve(__dirname, "..", "..", "..", "..", "..", "native"),
|
|
2630
|
+
// Try from current working directory
|
|
2631
|
+
path.resolve(process.cwd(), "native"),
|
|
2632
|
+
// Try from dist directory relative to executable
|
|
2633
|
+
path.resolve(path.dirname(process.execPath), "..", "native"),
|
|
2634
|
+
];
|
|
2635
|
+
|
|
2636
|
+
for (const p of possiblePaths) {
|
|
2637
|
+
try {
|
|
2638
|
+
const files = require("fs").readdirSync(p);
|
|
2639
|
+
if (files.some((f: string) => f.endsWith(".node"))) {
|
|
2640
|
+
return p;
|
|
2641
|
+
}
|
|
2642
|
+
} catch {
|
|
2643
|
+
// Directory doesn't exist or can't be read, try next
|
|
2644
|
+
}
|
|
2645
|
+
}
|
|
2646
|
+
|
|
2647
|
+
return null;
|
|
2648
|
+
}
|
|
2649
|
+
|
|
2650
|
+
/**
|
|
2651
|
+
* Get the NativeRenderer class (lazy-loaded to avoid circular dependencies)
|
|
2652
|
+
*/
|
|
2653
|
+
function getNativeRendererClass(): any {
|
|
2654
|
+
if (!_NativeRendererClass) {
|
|
2655
|
+
const { platform, arch } = process;
|
|
2656
|
+
|
|
2657
|
+
// Find the native directory
|
|
2658
|
+
const nativeDir = findNativeDir();
|
|
2659
|
+
if (!nativeDir) {
|
|
2660
|
+
throw new Error("Could not find native module directory");
|
|
2661
|
+
}
|
|
2662
|
+
|
|
2663
|
+
// Determine the correct native module file based on platform
|
|
2664
|
+
let nativeFile: string;
|
|
2665
|
+
if (platform === "darwin" && arch === "arm64") {
|
|
2666
|
+
nativeFile = "index.darwin-arm64.node";
|
|
2667
|
+
} else if (platform === "darwin" && arch === "x64") {
|
|
2668
|
+
nativeFile = "claude_code_native.darwin-x64.node";
|
|
2669
|
+
} else if (platform === "linux" && arch === "x64") {
|
|
2670
|
+
nativeFile = "claude_code_native.linux-x64-gnu.node";
|
|
2671
|
+
} else if (platform === "win32" && arch === "x64") {
|
|
2672
|
+
nativeFile = "claude_code_native.win32-x64-msvc.node";
|
|
2673
|
+
} else {
|
|
2674
|
+
throw new Error(`Unsupported platform: ${platform}-${arch}`);
|
|
2675
|
+
}
|
|
2676
|
+
|
|
2677
|
+
const nativePath = path.join(nativeDir, nativeFile);
|
|
2678
|
+
const nativeModule = require(nativePath);
|
|
2679
|
+
_NativeRendererClass = nativeModule.NativeRenderer;
|
|
2680
|
+
}
|
|
2681
|
+
return _NativeRendererClass;
|
|
2682
|
+
}
|
|
2683
|
+
|
|
2684
|
+
/**
|
|
2685
|
+
* Lazy-loading wrapper for the NativeRenderer class
|
|
2686
|
+
* This allows us to defer loading the native module until it's actually needed
|
|
2687
|
+
*/
|
|
2688
|
+
export const NativeRenderer = new Proxy(
|
|
2689
|
+
// Use a function as the target so it can be called with `new`
|
|
2690
|
+
function NativeRenderer(this: any, ...args: any[]) {
|
|
2691
|
+
const ActualClass = getNativeRendererClass();
|
|
2692
|
+
const instance = new ActualClass(...args);
|
|
2693
|
+
// Copy instance to `this` if called with new
|
|
2694
|
+
Object.assign(this, instance);
|
|
2695
|
+
return instance;
|
|
2696
|
+
} as any,
|
|
2697
|
+
{
|
|
2698
|
+
// Proxy property access to the actual class
|
|
2699
|
+
get(_target, prop) {
|
|
2700
|
+
const ActualClass = getNativeRendererClass();
|
|
2701
|
+
return Reflect.get(ActualClass, prop, ActualClass);
|
|
2702
|
+
},
|
|
2703
|
+
// Proxy static method calls
|
|
2704
|
+
apply(_target, _thisArg, args) {
|
|
2705
|
+
const ActualClass = getNativeRendererClass();
|
|
2706
|
+
return ActualClass(...args);
|
|
2707
|
+
},
|
|
2708
|
+
}
|
|
2709
|
+
) as any;
|
|
2710
|
+
|
|
2711
|
+
// Type alias for the NativeRenderer instance type
|
|
2712
|
+
// Use typeof since NativeRenderer is a value (Proxy)
|
|
2713
|
+
export type NativeRendererType = typeof NativeRenderer;
|
|
2714
|
+
|
|
2715
|
+
// Note: RenderState, RenderMessage, InputEvent, SearchResult types are already
|
|
2716
|
+
// defined as interfaces in this file (lines 78-107).
|
|
2717
|
+
// Type aliases for the v2 TUI using the already-defined types
|
|
2718
|
+
export type NativeRenderState = RenderState;
|
|
2719
|
+
export type NativeRenderMessage = RenderMessage;
|
|
2720
|
+
export type NativeInputEvent = InputEvent;
|
|
2721
|
+
export type NativeSearchResult = SearchResult;
|
|
2722
|
+
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TUI v2 Type definitions
|
|
3
|
+
* Re-exported from the native module for TypeScript consumption
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface RenderMessage {
|
|
7
|
+
role: string;
|
|
8
|
+
content: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface RenderState {
|
|
12
|
+
messages: RenderMessage[];
|
|
13
|
+
input_value: string;
|
|
14
|
+
cursor_pos: number;
|
|
15
|
+
status_text: string;
|
|
16
|
+
is_loading: boolean;
|
|
17
|
+
streaming_text: string;
|
|
18
|
+
model: string;
|
|
19
|
+
show_help: boolean;
|
|
20
|
+
help_text: string;
|
|
21
|
+
search_mode: boolean;
|
|
22
|
+
search_query: string;
|
|
23
|
+
search_results: SearchResult[];
|
|
24
|
+
search_selected: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface InputEvent {
|
|
28
|
+
event_type: "key" | "resize" | "none";
|
|
29
|
+
key?: string;
|
|
30
|
+
modifiers?: string;
|
|
31
|
+
new_width?: number;
|
|
32
|
+
new_height?: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface SearchResult {
|
|
36
|
+
file_path: string;
|
|
37
|
+
line_number: number;
|
|
38
|
+
content: string;
|
|
39
|
+
}
|