wave-code 0.0.6 → 0.0.10

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 (80) hide show
  1. package/README.md +1 -1
  2. package/dist/cli.d.ts +1 -0
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +2 -2
  5. package/dist/components/App.d.ts +1 -0
  6. package/dist/components/App.d.ts.map +1 -1
  7. package/dist/components/App.js +4 -4
  8. package/dist/components/BashHistorySelector.d.ts.map +1 -1
  9. package/dist/components/BashHistorySelector.js +17 -3
  10. package/dist/components/ChatInterface.d.ts.map +1 -1
  11. package/dist/components/ChatInterface.js +4 -2
  12. package/dist/components/Confirmation.d.ts +13 -0
  13. package/dist/components/Confirmation.d.ts.map +1 -0
  14. package/dist/components/Confirmation.js +172 -0
  15. package/dist/components/DiffDisplay.d.ts +8 -0
  16. package/dist/components/DiffDisplay.d.ts.map +1 -0
  17. package/dist/components/DiffDisplay.js +168 -0
  18. package/dist/components/FileSelector.d.ts +2 -4
  19. package/dist/components/FileSelector.d.ts.map +1 -1
  20. package/dist/components/InputBox.d.ts.map +1 -1
  21. package/dist/components/InputBox.js +10 -1
  22. package/dist/components/Markdown.d.ts.map +1 -1
  23. package/dist/components/Markdown.js +115 -16
  24. package/dist/components/MemoryDisplay.js +1 -1
  25. package/dist/components/MessageItem.d.ts +1 -2
  26. package/dist/components/MessageItem.d.ts.map +1 -1
  27. package/dist/components/MessageItem.js +3 -3
  28. package/dist/components/MessageList.d.ts.map +1 -1
  29. package/dist/components/MessageList.js +2 -2
  30. package/dist/components/ReasoningDisplay.d.ts +8 -0
  31. package/dist/components/ReasoningDisplay.d.ts.map +1 -0
  32. package/dist/components/ReasoningDisplay.js +10 -0
  33. package/dist/components/ToolResultDisplay.d.ts.map +1 -1
  34. package/dist/components/ToolResultDisplay.js +2 -1
  35. package/dist/contexts/useChat.d.ts +15 -1
  36. package/dist/contexts/useChat.d.ts.map +1 -1
  37. package/dist/contexts/useChat.js +124 -15
  38. package/dist/hooks/useInputManager.d.ts +3 -0
  39. package/dist/hooks/useInputManager.d.ts.map +1 -1
  40. package/dist/hooks/useInputManager.js +17 -0
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +22 -4
  43. package/dist/managers/InputManager.d.ts +8 -0
  44. package/dist/managers/InputManager.d.ts.map +1 -1
  45. package/dist/managers/InputManager.js +33 -2
  46. package/dist/print-cli.d.ts +1 -0
  47. package/dist/print-cli.d.ts.map +1 -1
  48. package/dist/print-cli.js +36 -3
  49. package/dist/utils/toolParameterTransforms.d.ts +23 -0
  50. package/dist/utils/toolParameterTransforms.d.ts.map +1 -0
  51. package/dist/utils/toolParameterTransforms.js +77 -0
  52. package/package.json +10 -8
  53. package/src/cli.tsx +3 -1
  54. package/src/components/App.tsx +7 -3
  55. package/src/components/BashHistorySelector.tsx +26 -3
  56. package/src/components/ChatInterface.tsx +31 -15
  57. package/src/components/Confirmation.tsx +286 -0
  58. package/src/components/DiffDisplay.tsx +300 -0
  59. package/src/components/FileSelector.tsx +2 -4
  60. package/src/components/InputBox.tsx +37 -14
  61. package/src/components/Markdown.tsx +329 -16
  62. package/src/components/MemoryDisplay.tsx +1 -1
  63. package/src/components/MessageItem.tsx +4 -12
  64. package/src/components/MessageList.tsx +0 -2
  65. package/src/components/ReasoningDisplay.tsx +33 -0
  66. package/src/components/ToolResultDisplay.tsx +4 -0
  67. package/src/contexts/useChat.tsx +206 -14
  68. package/src/hooks/useInputManager.ts +19 -0
  69. package/src/index.ts +34 -4
  70. package/src/managers/InputManager.ts +46 -2
  71. package/src/print-cli.ts +42 -2
  72. package/src/utils/toolParameterTransforms.ts +104 -0
  73. package/dist/components/DiffViewer.d.ts +0 -9
  74. package/dist/components/DiffViewer.d.ts.map +0 -1
  75. package/dist/components/DiffViewer.js +0 -221
  76. package/dist/utils/fileSearch.d.ts +0 -20
  77. package/dist/utils/fileSearch.d.ts.map +0 -1
  78. package/dist/utils/fileSearch.js +0 -102
  79. package/src/components/DiffViewer.tsx +0 -323
  80. package/src/utils/fileSearch.ts +0 -133
@@ -1,28 +1,341 @@
1
1
  import React, { useMemo } from "react";
2
- import { Text } from "ink";
3
- import { marked } from "marked";
4
- import TerminalRenderer from "marked-terminal";
2
+ import { Box, Text, useStdout } from "ink";
3
+ import { marked, type Token, type Tokens } from "marked";
5
4
 
6
5
  export interface MarkdownProps {
7
6
  children: string;
8
7
  }
9
8
 
10
- // Markdown component using marked-terminal with proper unescape option
11
- export const Markdown = React.memo(({ children }: MarkdownProps) => {
12
- const result = useMemo(() => {
13
- // Configure marked with TerminalRenderer using default options
14
- marked.setOptions({
15
- renderer: new TerminalRenderer({
16
- // Use official unescape option to handle HTML entities
17
- unescape: true,
18
- }),
9
+ const unescapeHtml = (html: string) => {
10
+ return html
11
+ .replace(/&/g, "&")
12
+ .replace(/&lt;/g, "<")
13
+ .replace(/&gt;/g, ">")
14
+ .replace(/&quot;/g, '"')
15
+ .replace(/&#39;/g, "'");
16
+ };
17
+
18
+ const InlineRenderer = ({ tokens }: { tokens: Token[] }) => {
19
+ return (
20
+ <>
21
+ {tokens.map((token, index) => {
22
+ switch (token.type) {
23
+ case "text": {
24
+ const t = token as Tokens.Text;
25
+ if (t.tokens) {
26
+ return <InlineRenderer key={index} tokens={t.tokens} />;
27
+ }
28
+ return <Text key={index}>{unescapeHtml(t.text)}</Text>;
29
+ }
30
+ case "strong":
31
+ return (
32
+ <Text key={index} bold>
33
+ {token.tokens ? (
34
+ <InlineRenderer tokens={token.tokens} />
35
+ ) : (
36
+ unescapeHtml((token as Tokens.Strong).text)
37
+ )}
38
+ </Text>
39
+ );
40
+ case "em":
41
+ return (
42
+ <Text key={index} italic>
43
+ {token.tokens ? (
44
+ <InlineRenderer tokens={token.tokens} />
45
+ ) : (
46
+ unescapeHtml((token as Tokens.Em).text)
47
+ )}
48
+ </Text>
49
+ );
50
+ case "codespan":
51
+ return (
52
+ <Text key={index} color="yellow">
53
+ {unescapeHtml((token as Tokens.Codespan).text)}
54
+ </Text>
55
+ );
56
+ case "link":
57
+ return (
58
+ <Text key={index} color="blue" underline>
59
+ {token.tokens ? (
60
+ <InlineRenderer tokens={token.tokens} />
61
+ ) : (
62
+ unescapeHtml((token as Tokens.Link).text)
63
+ )}
64
+ </Text>
65
+ );
66
+ case "br":
67
+ return <Text key={index}>{"\n"}</Text>;
68
+ case "del":
69
+ return (
70
+ <Text key={index} strikethrough>
71
+ {token.tokens ? (
72
+ <InlineRenderer tokens={token.tokens} />
73
+ ) : (
74
+ unescapeHtml((token as Tokens.Del).text)
75
+ )}
76
+ </Text>
77
+ );
78
+ default:
79
+ return <Text key={index}>{token.raw}</Text>;
80
+ }
81
+ })}
82
+ </>
83
+ );
84
+ };
85
+
86
+ const TableRenderer = ({ token }: { token: Tokens.Table }) => {
87
+ const { stdout } = useStdout();
88
+ const terminalWidth = (stdout?.columns || 80) - 2;
89
+
90
+ const columnWidths = useMemo(() => {
91
+ const numCols = token.header.length;
92
+ const minWidth = 5;
93
+ const maxColWidth = 40;
94
+ const widths = token.header.map((h) =>
95
+ Math.min(maxColWidth, Math.max(minWidth, h.text.length)),
96
+ );
97
+
98
+ token.rows.forEach((row) => {
99
+ row.forEach((cell, i) => {
100
+ widths[i] = Math.min(
101
+ maxColWidth,
102
+ Math.max(widths[i] || minWidth, cell.text.length),
103
+ );
104
+ });
19
105
  });
20
106
 
21
- const output = marked(children);
22
- return typeof output === "string" ? output.trim() : "";
23
- }, [children]);
107
+ const paddedWidths = widths.map((w) => w + 2);
108
+ const totalWidth = paddedWidths.reduce((a, b) => a + b, 0) + numCols + 1;
109
+
110
+ if (totalWidth <= terminalWidth) {
111
+ return paddedWidths;
112
+ }
113
+
114
+ // If table is too wide, scale down columns proportionally
115
+ const availableWidth = terminalWidth - numCols - 1;
116
+ const scaleFactor = availableWidth / (totalWidth - numCols - 1);
117
+ return paddedWidths.map((w) =>
118
+ Math.max(minWidth, Math.floor(w * scaleFactor)),
119
+ );
120
+ }, [token, terminalWidth]);
121
+
122
+ return (
123
+ <Box
124
+ flexDirection="column"
125
+ marginBottom={1}
126
+ borderStyle="single"
127
+ borderColor="gray"
128
+ width={columnWidths.reduce((a, b) => a + b, 0) + token.header.length + 1}
129
+ >
130
+ {/* Header */}
131
+ <Box
132
+ flexDirection="row"
133
+ borderStyle="single"
134
+ borderBottom
135
+ borderTop={false}
136
+ borderLeft={false}
137
+ borderRight={false}
138
+ borderColor="gray"
139
+ >
140
+ {token.header.map((cell, i) => (
141
+ <Box
142
+ key={i}
143
+ width={columnWidths[i]}
144
+ paddingX={1}
145
+ borderStyle="single"
146
+ borderLeft={i > 0}
147
+ borderRight={false}
148
+ borderTop={false}
149
+ borderBottom={false}
150
+ borderColor="gray"
151
+ >
152
+ <Text bold wrap="wrap">
153
+ <InlineRenderer tokens={cell.tokens} />
154
+ </Text>
155
+ </Box>
156
+ ))}
157
+ </Box>
158
+ {/* Rows */}
159
+ {token.rows.map((row, rowIndex) => (
160
+ <Box key={rowIndex} flexDirection="row">
161
+ {row.map((cell, i) => (
162
+ <Box
163
+ key={i}
164
+ width={columnWidths[i]}
165
+ paddingX={1}
166
+ borderStyle="single"
167
+ borderLeft={i > 0}
168
+ borderRight={false}
169
+ borderTop={false}
170
+ borderBottom={false}
171
+ borderColor="gray"
172
+ >
173
+ <Text wrap="wrap">
174
+ <InlineRenderer tokens={cell.tokens} />
175
+ </Text>
176
+ </Box>
177
+ ))}
178
+ </Box>
179
+ ))}
180
+ </Box>
181
+ );
182
+ };
183
+
184
+ const BlockRenderer = ({ tokens }: { tokens: Token[] }) => {
185
+ return (
186
+ <>
187
+ {tokens.map((token, index) => {
188
+ switch (token.type) {
189
+ case "heading": {
190
+ const t = token as Tokens.Heading;
191
+ return (
192
+ <Box key={index} marginBottom={1} flexDirection="column">
193
+ <Text bold color="cyan">
194
+ {"#".repeat(t.depth)} <InlineRenderer tokens={t.tokens} />
195
+ </Text>
196
+ </Box>
197
+ );
198
+ }
199
+ case "paragraph": {
200
+ const t = token as Tokens.Paragraph;
201
+ return (
202
+ <Box
203
+ key={index}
204
+ marginBottom={1}
205
+ flexDirection="row"
206
+ flexWrap="wrap"
207
+ >
208
+ <InlineRenderer tokens={t.tokens} />
209
+ </Box>
210
+ );
211
+ }
212
+ case "code": {
213
+ const t = token as Tokens.Code;
214
+ if (t.lang !== undefined) {
215
+ const lines = token.raw.replace(/\n$/, "").split("\n");
216
+ const opening = lines[0];
217
+ const closing = lines[lines.length - 1];
218
+ const content = lines.slice(1, -1).join("\n");
219
+ return (
220
+ <Box
221
+ key={index}
222
+ flexDirection="column"
223
+ paddingX={1}
224
+ marginBottom={1}
225
+ >
226
+ <Text color="gray">{opening}</Text>
227
+ {content && <Text>{content}</Text>}
228
+ <Text color="gray">{closing}</Text>
229
+ </Box>
230
+ );
231
+ }
232
+ return (
233
+ <Box
234
+ key={index}
235
+ flexDirection="column"
236
+ paddingX={1}
237
+ marginBottom={1}
238
+ >
239
+ <Text>{t.text}</Text>
240
+ </Box>
241
+ );
242
+ }
243
+ case "list": {
244
+ const t = token as Tokens.List;
245
+ return (
246
+ <Box
247
+ key={index}
248
+ flexDirection="column"
249
+ marginBottom={1}
250
+ paddingLeft={2}
251
+ >
252
+ {t.items.map((item, i) => {
253
+ const start = t.start || 1;
254
+ return (
255
+ <Box key={i} flexDirection="row">
256
+ <Text color="gray">
257
+ {t.ordered ? `${start + i}. ` : "• "}
258
+ </Text>
259
+ <Box flexDirection="column" flexGrow={1}>
260
+ {item.tokens.map((itemToken, itemIndex) => {
261
+ if (itemToken.type === "text") {
262
+ const it = itemToken as Tokens.Text;
263
+ return (
264
+ <Box
265
+ key={itemIndex}
266
+ flexDirection="row"
267
+ flexWrap="wrap"
268
+ >
269
+ <InlineRenderer
270
+ tokens={it.tokens || [itemToken]}
271
+ />
272
+ </Box>
273
+ );
274
+ }
275
+ return (
276
+ <BlockRenderer
277
+ key={itemIndex}
278
+ tokens={[itemToken]}
279
+ />
280
+ );
281
+ })}
282
+ </Box>
283
+ </Box>
284
+ );
285
+ })}
286
+ </Box>
287
+ );
288
+ }
289
+ case "blockquote": {
290
+ const t = token as Tokens.Blockquote;
291
+ return (
292
+ <Box
293
+ key={index}
294
+ flexDirection="column"
295
+ paddingLeft={2}
296
+ borderStyle="single"
297
+ borderLeft
298
+ borderRight={false}
299
+ borderTop={false}
300
+ borderBottom={false}
301
+ borderColor="gray"
302
+ marginBottom={1}
303
+ >
304
+ <BlockRenderer tokens={t.tokens} />
305
+ </Box>
306
+ );
307
+ }
308
+ case "hr":
309
+ return (
310
+ <Box key={index} marginBottom={1}>
311
+ <Text color="gray">{"─".repeat(20)}</Text>
312
+ </Box>
313
+ );
314
+ case "table":
315
+ return <TableRenderer key={index} token={token as Tokens.Table} />;
316
+ case "space":
317
+ return null;
318
+ default:
319
+ return (
320
+ <Box key={index} marginBottom={1}>
321
+ <Text>{token.raw}</Text>
322
+ </Box>
323
+ );
324
+ }
325
+ })}
326
+ </>
327
+ );
328
+ };
329
+
330
+ // Markdown component using custom Ink-based renderer
331
+ export const Markdown = React.memo(({ children }: MarkdownProps) => {
332
+ const tokens = useMemo(() => marked.lexer(children), [children]);
24
333
 
25
- return <Text>{result}</Text>;
334
+ return (
335
+ <Box flexDirection="column">
336
+ <BlockRenderer tokens={tokens} />
337
+ </Box>
338
+ );
26
339
  });
27
340
 
28
341
  // Add display name for debugging
@@ -25,7 +25,7 @@ export const MemoryDisplay: React.FC<MemoryDisplayProps> = ({ block }) => {
25
25
  if (!isSuccess) return null;
26
26
 
27
27
  if (memoryType === "user") {
28
- return `Memory saved to ${storagePath || "user-memory.md"}`;
28
+ return `Memory saved to ${storagePath || "AGENTS.md"}`;
29
29
  } else {
30
30
  return `Memory saved to ${storagePath || "AGENTS.md"}`;
31
31
  }
@@ -2,26 +2,24 @@ import React from "react";
2
2
  import { Box, Text } from "ink";
3
3
  import type { Message } from "wave-agent-sdk";
4
4
  import { MessageSource } from "wave-agent-sdk";
5
- import { DiffViewer } from "./DiffViewer.js";
6
5
  import { CommandOutputDisplay } from "./CommandOutputDisplay.js";
7
6
  import { ToolResultDisplay } from "./ToolResultDisplay.js";
8
7
  import { MemoryDisplay } from "./MemoryDisplay.js";
9
8
  import { CompressDisplay } from "./CompressDisplay.js";
10
9
  import { SubagentBlock } from "./SubagentBlock.js";
10
+ import { ReasoningDisplay } from "./ReasoningDisplay.js";
11
11
  import { Markdown } from "./Markdown.js";
12
12
 
13
13
  export interface MessageItemProps {
14
14
  message: Message;
15
15
  isExpanded: boolean;
16
16
  shouldShowHeader: boolean;
17
- isStatic?: boolean;
18
17
  }
19
18
 
20
19
  export const MessageItem = ({
21
20
  message,
22
21
  isExpanded,
23
22
  shouldShowHeader,
24
- isStatic = true,
25
23
  }: MessageItemProps) => {
26
24
  if (message.blocks.length === 0) return null;
27
25
  return (
@@ -49,11 +47,7 @@ export const MessageItem = ({
49
47
  🔗{" "}
50
48
  </Text>
51
49
  )}
52
- {isStatic ? (
53
- <Markdown>{block.content}</Markdown>
54
- ) : (
55
- <Text>{block.content.split("\n").slice(-10).join("\n")}</Text>
56
- )}
50
+ <Markdown>{block.content}</Markdown>
57
51
  </Box>
58
52
  )}
59
53
 
@@ -63,10 +57,6 @@ export const MessageItem = ({
63
57
  </Box>
64
58
  )}
65
59
 
66
- {block.type === "diff" && (
67
- <DiffViewer block={block} isStatic={isStatic} />
68
- )}
69
-
70
60
  {block.type === "command_output" && (
71
61
  <CommandOutputDisplay block={block} isExpanded={isExpanded} />
72
62
  )}
@@ -96,6 +86,8 @@ export const MessageItem = ({
96
86
  )}
97
87
 
98
88
  {block.type === "subagent" && <SubagentBlock block={block} />}
89
+
90
+ {block.type === "reasoning" && <ReasoningDisplay block={block} />}
99
91
  </Box>
100
92
  ))}
101
93
  </Box>
@@ -75,7 +75,6 @@ export const MessageList = React.memo(
75
75
  message={message}
76
76
  shouldShowHeader={previousMessage?.role !== message.role}
77
77
  isExpanded={isExpanded}
78
- isStatic={true}
79
78
  />
80
79
  );
81
80
  }}
@@ -92,7 +91,6 @@ export const MessageList = React.memo(
92
91
  message={message}
93
92
  shouldShowHeader={previousMessage?.role !== message.role}
94
93
  isExpanded={isExpanded}
95
- isStatic={false}
96
94
  />
97
95
  </Box>
98
96
  );
@@ -0,0 +1,33 @@
1
+ import React from "react";
2
+ import { Box } from "ink";
3
+ import type { ReasoningBlock } from "wave-agent-sdk";
4
+ import { Markdown } from "./Markdown.js";
5
+
6
+ interface ReasoningDisplayProps {
7
+ block: ReasoningBlock;
8
+ }
9
+
10
+ export const ReasoningDisplay: React.FC<ReasoningDisplayProps> = ({
11
+ block,
12
+ }) => {
13
+ const { content } = block;
14
+
15
+ if (!content || !content.trim()) {
16
+ return null;
17
+ }
18
+
19
+ return (
20
+ <Box
21
+ borderRight={false}
22
+ borderTop={false}
23
+ borderBottom={false}
24
+ borderStyle="classic"
25
+ borderColor="blue"
26
+ paddingLeft={1}
27
+ >
28
+ <Box flexDirection="column">
29
+ <Markdown>{content}</Markdown>
30
+ </Box>
31
+ </Box>
32
+ );
33
+ };
@@ -1,6 +1,7 @@
1
1
  import React from "react";
2
2
  import { Box, Text } from "ink";
3
3
  import type { ToolBlock } from "wave-agent-sdk";
4
+ import { DiffDisplay } from "./DiffDisplay.js";
4
5
 
5
6
  interface ToolResultDisplayProps {
6
7
  block: ToolBlock;
@@ -133,6 +134,9 @@ export const ToolResultDisplay: React.FC<ToolResultDisplayProps> = ({
133
134
  </Text>
134
135
  </Box>
135
136
  )}
137
+
138
+ {/* Diff display - handled by DiffDisplay component */}
139
+ <DiffDisplay toolBlock={block} />
136
140
  </Box>
137
141
  );
138
142
  };