wave-code 0.19.9 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/dist/cli.js +20 -1
  2. package/dist/components/App.js +7 -0
  3. package/dist/components/BtwDisplay.js +13 -3
  4. package/dist/components/ChatInterface.js +21 -6
  5. package/dist/components/HelpView.js +6 -0
  6. package/dist/components/InputBox.d.ts +1 -2
  7. package/dist/components/InputBox.js +16 -5
  8. package/dist/components/LoadingIndicator.d.ts +1 -2
  9. package/dist/components/LoadingIndicator.js +2 -2
  10. package/dist/components/Markdown.js +13 -16
  11. package/dist/components/MessageList.d.ts +2 -1
  12. package/dist/components/MessageList.js +2 -2
  13. package/dist/components/RewindCommand.js +4 -2
  14. package/dist/components/StatusLine.d.ts +0 -2
  15. package/dist/components/StatusLine.js +6 -6
  16. package/dist/components/TaskList.js +2 -1
  17. package/dist/components/ToolDisplay.d.ts +1 -0
  18. package/dist/components/ToolDisplay.js +17 -9
  19. package/dist/constants/commands.js +0 -6
  20. package/dist/contexts/useChat.d.ts +3 -5
  21. package/dist/contexts/useChat.js +242 -82
  22. package/dist/daemon-cli.d.ts +10 -0
  23. package/dist/daemon-cli.js +15 -0
  24. package/dist/hooks/useInputManager.d.ts +1 -0
  25. package/dist/hooks/useInputManager.js +120 -40
  26. package/dist/index.js +10 -0
  27. package/dist/managers/inputHandlers.js +55 -30
  28. package/dist/managers/inputReducer.d.ts +22 -22
  29. package/dist/managers/inputReducer.js +361 -177
  30. package/dist/print-cli.js +36 -10
  31. package/dist/stdio/agentBridge.d.ts +23 -0
  32. package/dist/stdio/agentBridge.js +151 -18
  33. package/dist/stdio/daemonServer.d.ts +67 -0
  34. package/dist/stdio/daemonServer.js +191 -0
  35. package/dist/stdio/index.d.ts +2 -0
  36. package/dist/stdio/index.js +2 -0
  37. package/dist/stdio/jsonRpcConnection.d.ts +30 -0
  38. package/dist/stdio/jsonRpcConnection.js +127 -0
  39. package/dist/stdio/protocol.d.ts +2 -2
  40. package/dist/stdio/stdioServer.d.ts +2 -7
  41. package/dist/stdio/stdioServer.js +9 -100
  42. package/dist/utils/bracketedPaste.d.ts +39 -0
  43. package/dist/utils/bracketedPaste.js +122 -0
  44. package/dist/utils/markdownTable.d.ts +34 -0
  45. package/dist/utils/markdownTable.js +302 -0
  46. package/dist/utils/rewindCheckpoints.d.ts +8 -0
  47. package/dist/utils/rewindCheckpoints.js +15 -0
  48. package/dist/utils/throttle.d.ts +3 -3
  49. package/dist/utils/worktree.d.ts +8 -0
  50. package/dist/utils/worktree.js +32 -1
  51. package/package.json +4 -2
  52. package/src/cli.tsx +20 -1
  53. package/src/components/App.tsx +5 -0
  54. package/src/components/BtwDisplay.tsx +36 -12
  55. package/src/components/ChatInterface.tsx +26 -11
  56. package/src/components/HelpView.tsx +6 -0
  57. package/src/components/InputBox.tsx +21 -10
  58. package/src/components/LoadingIndicator.tsx +1 -4
  59. package/src/components/Markdown.tsx +15 -18
  60. package/src/components/MessageList.tsx +6 -0
  61. package/src/components/RewindCommand.tsx +4 -2
  62. package/src/components/StatusLine.tsx +0 -10
  63. package/src/components/TaskList.tsx +2 -1
  64. package/src/components/ToolDisplay.tsx +17 -6
  65. package/src/constants/commands.ts +0 -6
  66. package/src/contexts/useChat.tsx +310 -95
  67. package/src/daemon-cli.ts +17 -0
  68. package/src/hooks/useInputManager.ts +135 -43
  69. package/src/index.ts +12 -0
  70. package/src/managers/inputHandlers.ts +55 -32
  71. package/src/managers/inputReducer.ts +442 -214
  72. package/src/print-cli.ts +48 -11
  73. package/src/stdio/agentBridge.ts +213 -18
  74. package/src/stdio/daemonServer.ts +212 -0
  75. package/src/stdio/index.ts +2 -0
  76. package/src/stdio/jsonRpcConnection.ts +160 -0
  77. package/src/stdio/protocol.ts +5 -2
  78. package/src/stdio/stdioServer.ts +14 -120
  79. package/src/utils/bracketedPaste.ts +170 -0
  80. package/src/utils/markdownTable.ts +359 -0
  81. package/src/utils/rewindCheckpoints.ts +15 -0
  82. package/src/utils/throttle.ts +8 -8
  83. package/src/utils/worktree.ts +50 -1
@@ -31,6 +31,7 @@ export const INPUT_PLACEHOLDER_TEXT_PREFIX = INPUT_PLACEHOLDER_TEXT.substring(
31
31
  export interface InputBoxProps {
32
32
  isLoading?: boolean;
33
33
  isCommandRunning?: boolean;
34
+ isCompacting?: boolean;
34
35
  workdir?: string;
35
36
  sendMessage?: (
36
37
  message: string,
@@ -48,12 +49,12 @@ export interface InputBoxProps {
48
49
  // Token usage
49
50
  latestTotalTokens?: number;
50
51
  maxInputTokens?: number;
51
- // Goal state
52
- isGoalActive?: boolean;
53
- goalElapsed?: string;
54
52
  }
55
53
 
56
54
  export const InputBox: React.FC<InputBoxProps> = ({
55
+ isLoading,
56
+ isCommandRunning,
57
+ isCompacting,
57
58
  sendMessage = () => {},
58
59
  abortMessage = () => {},
59
60
  mcpServers = [],
@@ -63,8 +64,6 @@ export const InputBox: React.FC<InputBoxProps> = ({
63
64
  hasSlashCommand = () => false,
64
65
  latestTotalTokens = 0,
65
66
  maxInputTokens = 200000,
66
- isGoalActive,
67
- goalElapsed,
68
67
  }) => {
69
68
  const {
70
69
  permissionMode: chatPermissionMode,
@@ -78,13 +77,13 @@ export const InputBox: React.FC<InputBoxProps> = ({
78
77
  askBtw,
79
78
  clearMessages,
80
79
  compact,
81
- goalCommand,
82
80
  currentModel,
83
81
  configuredModels,
84
82
  setModel,
85
83
  recreateAgent,
86
84
  recallQueuedMessage,
87
85
  queuedMessages,
86
+ setIsBtwActive,
88
87
  } = useChat();
89
88
 
90
89
  // Ref to hold setInputText so queue callbacks can access it before useInputManager returns
@@ -92,6 +91,10 @@ export const InputBox: React.FC<InputBoxProps> = ({
92
91
 
93
92
  const hasQueuedMessages = (queuedMessages?.length ?? 0) > 0;
94
93
 
94
+ // Idle means no AI work in flight. Esc double-press clear only applies when
95
+ // idle; while busy, Esc keeps its abort semantics.
96
+ const isIdle = !(isLoading || isCommandRunning || isCompacting);
97
+
95
98
  const onRecallQueuedMessage = useCallback(() => {
96
99
  const msg = recallQueuedMessage();
97
100
  if (msg) {
@@ -146,6 +149,8 @@ export const InputBox: React.FC<InputBoxProps> = ({
146
149
  setPermissionMode,
147
150
  // BTW state
148
151
  btwState,
152
+ // Esc double-press clear pending
153
+ escClearPending,
149
154
  // Main handler
150
155
  handleInput,
151
156
  // Manager ready state
@@ -157,7 +162,6 @@ export const InputBox: React.FC<InputBoxProps> = ({
157
162
  onAskBtw: askBtw,
158
163
  onClearMessages: clearMessages,
159
164
  onCompact: compact,
160
- onGoalCommand: goalCommand,
161
165
  onHasSlashCommand: hasSlashCommand,
162
166
  onAbortMessage: abortMessage,
163
167
  onBackgroundCurrentTask: backgroundCurrentTask,
@@ -166,6 +170,7 @@ export const InputBox: React.FC<InputBoxProps> = ({
166
170
  workdir: workingDirectory,
167
171
  getFullMessageThread,
168
172
  hasQueuedMessages,
173
+ isIdle,
169
174
  onRecallQueuedMessage,
170
175
  });
171
176
 
@@ -174,6 +179,13 @@ export const InputBox: React.FC<InputBoxProps> = ({
174
179
  setInputTextRef.current = setInputText;
175
180
  }, [setInputText]);
176
181
 
182
+ // Sync the btw overlay's visibility to ChatContext so siblings (TaskList)
183
+ // can hide while the side-question is on display (aligned with Claude Code,
184
+ // which suppresses the expanded task list while a local-jsx command shows).
185
+ useEffect(() => {
186
+ setIsBtwActive(btwState.question !== "" || btwState.answer !== undefined);
187
+ }, [btwState.question, btwState.answer, setIsBtwActive]);
188
+
177
189
  // Sync permission mode from useChat to InputManager
178
190
  useEffect(() => {
179
191
  setPermissionMode(chatPermissionMode);
@@ -331,7 +343,7 @@ export const InputBox: React.FC<InputBoxProps> = ({
331
343
  <WorkflowManager onCancel={() => setShowWorkflowManager(false)} />
332
344
  )}
333
345
 
334
- {btwState.question
346
+ {btwState.question || btwState.answer
335
347
  ? null
336
348
  : showBackgroundTaskManager ||
337
349
  showMcpManager ||
@@ -342,6 +354,7 @@ export const InputBox: React.FC<InputBoxProps> = ({
342
354
  showPluginManager ||
343
355
  showWorkflowManager || (
344
356
  <Box flexDirection="column">
357
+ {escClearPending && <Text color="gray">再次按 Esc 清空输入</Text>}
345
358
  <Box
346
359
  borderStyle="single"
347
360
  borderColor="gray"
@@ -365,8 +378,6 @@ export const InputBox: React.FC<InputBoxProps> = ({
365
378
  <StatusLine
366
379
  permissionMode={permissionMode}
367
380
  isShellCommand={isShellCommand}
368
- isGoalActive={isGoalActive}
369
- goalElapsed={goalElapsed}
370
381
  latestTotalTokens={latestTotalTokens}
371
382
  maxInputTokens={maxInputTokens}
372
383
  />
@@ -5,7 +5,6 @@ export interface LoadingIndicatorProps {
5
5
  isLoading?: boolean;
6
6
  isCommandRunning?: boolean;
7
7
  isCompacting?: boolean;
8
- isGoalEvaluating?: boolean;
9
8
  latestTotalTokens?: number;
10
9
  }
11
10
 
@@ -13,12 +12,11 @@ export const LoadingIndicator = ({
13
12
  isLoading = false,
14
13
  isCommandRunning = false,
15
14
  isCompacting = false,
16
- isGoalEvaluating = false,
17
15
  latestTotalTokens = 0,
18
16
  }: LoadingIndicatorProps) => {
19
17
  return (
20
18
  <Box flexDirection="column">
21
- {isLoading && !isCompacting && !isGoalEvaluating && (
19
+ {isLoading && !isCompacting && (
22
20
  <Box>
23
21
  <Text color="yellow">✻ AI is thinking... </Text>
24
22
  {latestTotalTokens > 0 && (
@@ -51,7 +49,6 @@ export const LoadingIndicator = ({
51
49
  {isCompacting && (
52
50
  <Text color="magenta">✻ Compacting message history...</Text>
53
51
  )}
54
- {isGoalEvaluating && <Text color="cyan">✻ Evaluating goal...</Text>}
55
52
  </Box>
56
53
  );
57
54
  };
@@ -1,8 +1,9 @@
1
1
  import React, { useMemo } from "react";
2
- import { Box, Text } from "ink";
2
+ import { Box, Text, useStdout } from "ink";
3
3
  import { Renderer, marked, type Tokens } from "marked";
4
4
  import chalk from "chalk";
5
5
  import { highlightToAnsi } from "../utils/highlightUtils.js";
6
+ import { renderMarkdownTable } from "../utils/markdownTable.js";
6
7
 
7
8
  export interface MarkdownProps {
8
9
  children: string;
@@ -19,6 +20,10 @@ const unescapeHtml = (html: string) => {
19
20
  };
20
21
 
21
22
  class AnsiRenderer extends Renderer<string> {
23
+ constructor(private readonly columns: number) {
24
+ super();
25
+ }
26
+
22
27
  override code({ text, lang }: Tokens.Code): string {
23
28
  const prefix = lang ? `\`\`\`${lang}` : "```";
24
29
  const suffix = "```";
@@ -82,20 +87,10 @@ class AnsiRenderer extends Renderer<string> {
82
87
  }
83
88
 
84
89
  override table(token: Tokens.Table): string {
85
- const header = token.header.map((cell) => this.tablecell(cell)).join("");
86
- const body = token.rows
87
- .map((row) => row.map((cell) => this.tablecell(cell)).join("") + "\n")
88
- .join("");
89
- return `\n${header}\n${body}\n`;
90
- }
91
-
92
- override tablerow({ text }: Tokens.TableRow): string {
93
- return text + "\n";
94
- }
95
-
96
- override tablecell(token: Tokens.TableCell): string {
97
- const text = token.header ? chalk.bold(token.text) : token.text;
98
- return text + " | ";
90
+ const table = renderMarkdownTable(token, this.columns, (tokens) =>
91
+ this.parser.parseInline(tokens),
92
+ );
93
+ return `\n${table}\n`;
99
94
  }
100
95
 
101
96
  override strong({ tokens }: Tokens.Strong): string {
@@ -140,17 +135,19 @@ class AnsiRenderer extends Renderer<string> {
140
135
  }
141
136
  }
142
137
 
143
- const renderer = new AnsiRenderer();
138
+ const createRenderer = (columns: number) => new AnsiRenderer(columns);
144
139
 
145
140
  // Markdown component using custom ANSI renderer
146
141
  export const Markdown = React.memo(({ children }: MarkdownProps) => {
142
+ const { stdout } = useStdout();
143
+ const columns = stdout?.columns ?? 80;
147
144
  const ansiContent = useMemo(() => {
148
145
  return marked.parse(children, {
149
- renderer,
146
+ renderer: createRenderer(columns),
150
147
  gfm: true,
151
148
  breaks: true,
152
149
  }) as string;
153
- }, [children]);
150
+ }, [children, columns]);
154
151
 
155
152
  return (
156
153
  <Box flexDirection="column">
@@ -13,6 +13,7 @@ export interface MessageListProps {
13
13
  forceStatic?: boolean;
14
14
  version?: string;
15
15
  workdir?: string;
16
+ showLoginHint?: boolean;
16
17
  }
17
18
 
18
19
  export const MessageList = React.memo(
@@ -22,6 +23,7 @@ export const MessageList = React.memo(
22
23
  forceStatic = false,
23
24
  version,
24
25
  workdir,
26
+ showLoginHint = false,
25
27
  }: MessageListProps) => {
26
28
  const maxMessages = isExpanded
27
29
  ? MAX_MESSAGES_EXPANDED
@@ -135,6 +137,10 @@ export const MessageList = React.memo(
135
137
  </Static>
136
138
  )}
137
139
 
140
+ {/* Login hint — rendered in the dynamic area (not Static) so it can
141
+ disappear in real time when the user authenticates. */}
142
+ {showLoginHint && <Text color="gray">Type /login to authenticate</Text>}
143
+
138
144
  {/* Dynamic blocks */}
139
145
  {dynamicBlocks.length > 0 && (
140
146
  <Box flexDirection="column">
@@ -3,6 +3,7 @@ import { Box, Text, useInput } from "ink";
3
3
  import type { Message } from "wave-agent-sdk";
4
4
  import { getMessageContent } from "wave-agent-sdk";
5
5
  import { rewindSelectorReducer } from "../reducers/rewindSelectorReducer.js";
6
+ import { isUserCheckpointMessage } from "../utils/rewindCheckpoints.js";
6
7
 
7
8
  export interface RewindCommandProps {
8
9
  messages: Message[];
@@ -32,10 +33,11 @@ export const RewindCommand: React.FC<RewindCommandProps> = ({
32
33
  }
33
34
  }, [getFullMessageThread]);
34
35
 
35
- // Filter user messages as checkpoints, excluding meta messages
36
+ // Filter user messages as checkpoints, excluding meta messages and
37
+ // system-generated user-role messages (task notifications, hook injections)
36
38
  const checkpoints = messages
37
39
  .map((msg, index) => ({ msg, index }))
38
- .filter(({ msg }) => msg.role === "user" && !msg.isMeta);
40
+ .filter(({ msg }) => isUserCheckpointMessage(msg));
39
41
 
40
42
  const MAX_VISIBLE_ITEMS = 3;
41
43
 
@@ -4,8 +4,6 @@ import { Box, Text } from "ink";
4
4
  export interface StatusLineProps {
5
5
  permissionMode: string;
6
6
  isShellCommand: boolean;
7
- isGoalActive?: boolean;
8
- goalElapsed?: string;
9
7
  latestTotalTokens?: number;
10
8
  maxInputTokens?: number;
11
9
  }
@@ -13,8 +11,6 @@ export interface StatusLineProps {
13
11
  export const StatusLine: React.FC<StatusLineProps> = ({
14
12
  permissionMode,
15
13
  isShellCommand,
16
- isGoalActive,
17
- goalElapsed,
18
14
  latestTotalTokens = 0,
19
15
  maxInputTokens = 200000,
20
16
  }) => {
@@ -34,12 +30,6 @@ export const StatusLine: React.FC<StatusLineProps> = ({
34
30
  </Text>
35
31
  ) : (
36
32
  <Box>
37
- {isGoalActive && (
38
- <Text color="gray">
39
- <Text color="cyan">◎ /goal</Text> active{" "}
40
- {goalElapsed && <Text>({goalElapsed})</Text>} |{" "}
41
- </Text>
42
- )}
43
33
  <Text color="gray">
44
34
  Mode:{" "}
45
35
  <Text
@@ -91,7 +91,7 @@ function getDisplayLimit(rows: number | undefined): number {
91
91
 
92
92
  export const TaskList: React.FC = () => {
93
93
  const tasks = useTasks();
94
- const { isTaskListVisible } = useChat();
94
+ const { isTaskListVisible, isBtwActive } = useChat();
95
95
  const { stdout } = useStdout();
96
96
 
97
97
  const completionTimestampsRef = React.useRef<Map<string, number>>(new Map());
@@ -188,6 +188,7 @@ export const TaskList: React.FC = () => {
188
188
  if (
189
189
  tasks.length === 0 ||
190
190
  !isTaskListVisible ||
191
+ isBtwActive ||
191
192
  autoHidden ||
192
193
  (allCompleted && !hadIncompleteRef.current)
193
194
  ) {
@@ -9,6 +9,22 @@ interface ToolDisplayProps {
9
9
  isExpanded?: boolean;
10
10
  }
11
11
 
12
+ // Status dot color for a tool block. In-flight stages (start/streaming/running)
13
+ // never show red — the outcome isn't known until the tool reaches "end".
14
+ // An explicit error overrides everything.
15
+ export const getToolStatusColor = (
16
+ stage: ToolBlock["stage"],
17
+ success?: boolean,
18
+ error?: string | Error,
19
+ ): string => {
20
+ if (error) return "red";
21
+ if (stage === "start" || stage === "streaming") return "gray";
22
+ if (stage === "running") return "yellow";
23
+ if (success) return "green";
24
+ if (success === false) return "red";
25
+ return "gray"; // Unknown state or no state information
26
+ };
27
+
12
28
  export const ToolDisplay: React.FC<ToolDisplayProps> = ({
13
29
  block,
14
30
  isExpanded = false,
@@ -19,12 +35,7 @@ export const ToolDisplay: React.FC<ToolDisplayProps> = ({
19
35
  // Directly use compactParams
20
36
  // (no change needed as we destructured it above)
21
37
 
22
- const getStatusColor = () => {
23
- if (stage === "running") return "yellow";
24
- if (success) return "green";
25
- if (error || success === false) return "red";
26
- return "gray"; // Unknown state or no state information
27
- };
38
+ const getStatusColor = () => getToolStatusColor(stage, success, error);
28
39
 
29
40
  const hasImages = () => {
30
41
  return block.images && block.images.length > 0;
@@ -80,10 +80,4 @@ export const AVAILABLE_COMMANDS: SlashCommand[] = [
80
80
  description: "Compact conversation history to reduce context usage",
81
81
  handler: () => {},
82
82
  },
83
- {
84
- id: "goal",
85
- name: "goal",
86
- description: "Set, check, or clear an autonomous goal for the session",
87
- handler: () => {},
88
- },
89
83
  ];