wave-code 0.0.6 → 0.0.8

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 (77) 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 +11 -0
  13. package/dist/components/Confirmation.d.ts.map +1 -0
  14. package/dist/components/Confirmation.js +148 -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/MemoryDisplay.js +1 -1
  23. package/dist/components/MessageItem.d.ts +1 -2
  24. package/dist/components/MessageItem.d.ts.map +1 -1
  25. package/dist/components/MessageItem.js +3 -3
  26. package/dist/components/MessageList.d.ts.map +1 -1
  27. package/dist/components/MessageList.js +2 -2
  28. package/dist/components/ReasoningDisplay.d.ts +8 -0
  29. package/dist/components/ReasoningDisplay.d.ts.map +1 -0
  30. package/dist/components/ReasoningDisplay.js +10 -0
  31. package/dist/components/ToolResultDisplay.d.ts.map +1 -1
  32. package/dist/components/ToolResultDisplay.js +2 -1
  33. package/dist/contexts/useChat.d.ts +13 -1
  34. package/dist/contexts/useChat.d.ts.map +1 -1
  35. package/dist/contexts/useChat.js +117 -15
  36. package/dist/hooks/useInputManager.d.ts +3 -0
  37. package/dist/hooks/useInputManager.d.ts.map +1 -1
  38. package/dist/hooks/useInputManager.js +17 -0
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +22 -4
  41. package/dist/managers/InputManager.d.ts +8 -0
  42. package/dist/managers/InputManager.d.ts.map +1 -1
  43. package/dist/managers/InputManager.js +33 -2
  44. package/dist/print-cli.d.ts +1 -0
  45. package/dist/print-cli.d.ts.map +1 -1
  46. package/dist/print-cli.js +36 -3
  47. package/dist/utils/toolParameterTransforms.d.ts +23 -0
  48. package/dist/utils/toolParameterTransforms.d.ts.map +1 -0
  49. package/dist/utils/toolParameterTransforms.js +77 -0
  50. package/package.json +6 -5
  51. package/src/cli.tsx +3 -1
  52. package/src/components/App.tsx +7 -3
  53. package/src/components/BashHistorySelector.tsx +26 -3
  54. package/src/components/ChatInterface.tsx +29 -15
  55. package/src/components/Confirmation.tsx +253 -0
  56. package/src/components/DiffDisplay.tsx +300 -0
  57. package/src/components/FileSelector.tsx +2 -4
  58. package/src/components/InputBox.tsx +37 -14
  59. package/src/components/MemoryDisplay.tsx +1 -1
  60. package/src/components/MessageItem.tsx +4 -12
  61. package/src/components/MessageList.tsx +0 -2
  62. package/src/components/ReasoningDisplay.tsx +33 -0
  63. package/src/components/ToolResultDisplay.tsx +4 -0
  64. package/src/contexts/useChat.tsx +178 -14
  65. package/src/hooks/useInputManager.ts +19 -0
  66. package/src/index.ts +34 -4
  67. package/src/managers/InputManager.ts +46 -2
  68. package/src/print-cli.ts +42 -2
  69. package/src/utils/toolParameterTransforms.ts +104 -0
  70. package/dist/components/DiffViewer.d.ts +0 -9
  71. package/dist/components/DiffViewer.d.ts.map +0 -1
  72. package/dist/components/DiffViewer.js +0 -221
  73. package/dist/utils/fileSearch.d.ts +0 -20
  74. package/dist/utils/fileSearch.d.ts.map +0 -1
  75. package/dist/utils/fileSearch.js +0 -102
  76. package/src/components/DiffViewer.tsx +0 -323
  77. package/src/utils/fileSearch.ts +0 -133
@@ -1,4 +1,4 @@
1
- import { searchFiles as searchFilesUtil } from "../utils/fileSearch.js";
1
+ import { searchFiles as searchFilesUtil, } from "wave-agent-sdk";
2
2
  import { readClipboardImage } from "../utils/clipboard.js";
3
3
  export class InputManager {
4
4
  constructor(callbacks = {}) {
@@ -40,13 +40,19 @@ export class InputManager {
40
40
  // Additional UI state
41
41
  this.showBashManager = false;
42
42
  this.showMcpManager = false;
43
+ // Permission mode state
44
+ this.permissionMode = "default";
43
45
  // Flag to prevent handleInput conflicts when selector selection occurs
44
46
  this.selectorJustUsed = false;
45
47
  this.callbacks = callbacks;
48
+ this.logger = callbacks.logger;
46
49
  }
47
50
  // Update callbacks
48
51
  updateCallbacks(callbacks) {
49
52
  this.callbacks = { ...this.callbacks, ...callbacks };
53
+ if (callbacks.logger) {
54
+ this.logger = callbacks.logger;
55
+ }
50
56
  }
51
57
  // Core input methods
52
58
  getInputText() {
@@ -619,6 +625,25 @@ export class InputManager {
619
625
  this.showMcpManager = show;
620
626
  this.callbacks.onMcpManagerStateChange?.(show);
621
627
  }
628
+ // Permission mode methods
629
+ getPermissionMode() {
630
+ return this.permissionMode;
631
+ }
632
+ setPermissionMode(mode) {
633
+ this.permissionMode = mode;
634
+ }
635
+ cyclePermissionMode() {
636
+ const modes = ["default", "acceptEdits"];
637
+ const currentIndex = modes.indexOf(this.permissionMode);
638
+ const nextIndex = currentIndex === -1 ? 0 : (currentIndex + 1) % modes.length;
639
+ const nextMode = modes[nextIndex];
640
+ this.logger?.debug("Cycling permission mode", {
641
+ from: this.permissionMode,
642
+ to: nextMode,
643
+ });
644
+ this.permissionMode = nextMode;
645
+ this.callbacks.onPermissionModeChange?.(this.permissionMode);
646
+ }
622
647
  // Handle submit logic
623
648
  async handleSubmit(attachedImages, isLoading = false, isCommandRunning = false) {
624
649
  // Prevent submission during loading or command execution
@@ -655,7 +680,7 @@ export class InputManager {
655
680
  }
656
681
  // Handle selector input (when any selector is active)
657
682
  handleSelectorInput(input, key) {
658
- if (key.backspace || key.delete) {
683
+ if (key.backspace || (key.delete && !this.showBashHistorySelector)) {
659
684
  if (this.cursorPosition > 0) {
660
685
  this.deleteCharAtCursor((newInput, newCursorPosition) => {
661
686
  // Check for special character deletion
@@ -793,6 +818,12 @@ export class InputManager {
793
818
  this.callbacks.onAbortMessage?.();
794
819
  return true;
795
820
  }
821
+ // Handle Shift+Tab for permission mode cycling
822
+ if (key.tab && key.shift) {
823
+ this.logger?.debug("Shift+Tab detected, cycling permission mode");
824
+ this.cyclePermissionMode();
825
+ return true;
826
+ }
796
827
  // Check if any selector is active
797
828
  if (this.showFileSelector ||
798
829
  this.showCommandSelector ||
@@ -3,6 +3,7 @@ export interface PrintCliOptions {
3
3
  continueLastSession?: boolean;
4
4
  message?: string;
5
5
  showStats?: boolean;
6
+ bypassPermissions?: boolean;
6
7
  }
7
8
  export declare function startPrintCli(options: PrintCliOptions): Promise<void>;
8
9
  //# sourceMappingURL=print-cli.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"print-cli.d.ts","sourceRoot":"","sources":["../src/print-cli.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,eAAe;IAC9B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAgBD,wBAAsB,aAAa,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAuI3E"}
1
+ {"version":3,"file":"print-cli.d.ts","sourceRoot":"","sources":["../src/print-cli.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,eAAe;IAC9B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAgBD,wBAAsB,aAAa,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CA8K3E"}
package/dist/print-cli.js CHANGED
@@ -13,7 +13,7 @@ function displayTimingInfo(startTime, showStats) {
13
13
  }
14
14
  export async function startPrintCli(options) {
15
15
  const startTime = new Date();
16
- const { restoreSessionId, continueLastSession, message, showStats = false, } = options;
16
+ const { restoreSessionId, continueLastSession, message, showStats = false, bypassPermissions, } = options;
17
17
  if ((!message || message.trim() === "") &&
18
18
  !continueLastSession &&
19
19
  !restoreSessionId) {
@@ -21,13 +21,30 @@ export async function startPrintCli(options) {
21
21
  process.exit(1);
22
22
  }
23
23
  let agent;
24
+ let isReasoning = false;
25
+ let isContent = false;
26
+ const subagentReasoningStates = new Map();
27
+ const subagentContentStates = new Map();
24
28
  // Setup callbacks for agent
25
29
  const callbacks = {
26
30
  onAssistantMessageAdded: () => {
31
+ isReasoning = false;
32
+ isContent = false;
27
33
  // Assistant message started - no content to output yet
28
34
  process.stdout.write("\n");
29
35
  },
36
+ onAssistantReasoningUpdated: (chunk) => {
37
+ if (!isReasoning) {
38
+ process.stdout.write("💭 Reasoning:\n");
39
+ isReasoning = true;
40
+ }
41
+ process.stdout.write(chunk);
42
+ },
30
43
  onAssistantContentUpdated: (chunk) => {
44
+ if (isReasoning && !isContent) {
45
+ process.stdout.write("\n\n📝 Response:\n");
46
+ isContent = true;
47
+ }
31
48
  // FR-001: Stream content updates for real-time display - output only the new chunk
32
49
  process.stdout.write(chunk);
33
50
  },
@@ -59,11 +76,25 @@ export async function startPrintCli(options) {
59
76
  process.stdout.write(` ${statusIcon} Subagent status: ${status}\n`);
60
77
  },
61
78
  // Subagent message callbacks
62
- onSubagentAssistantMessageAdded: () => {
79
+ onSubagentAssistantMessageAdded: (subagentId) => {
80
+ subagentReasoningStates.set(subagentId, false);
81
+ subagentContentStates.set(subagentId, false);
63
82
  // Subagent assistant message started - add indentation
64
83
  process.stdout.write("\n ");
65
84
  },
66
- onSubagentAssistantContentUpdated: (_subagentId, chunk) => {
85
+ onSubagentAssistantReasoningUpdated: (subagentId, chunk) => {
86
+ if (!subagentReasoningStates.get(subagentId)) {
87
+ process.stdout.write("💭 Reasoning: ");
88
+ subagentReasoningStates.set(subagentId, true);
89
+ }
90
+ process.stdout.write(chunk);
91
+ },
92
+ onSubagentAssistantContentUpdated: (subagentId, chunk) => {
93
+ if (subagentReasoningStates.get(subagentId) &&
94
+ !subagentContentStates.get(subagentId)) {
95
+ process.stdout.write("\n 📝 Response: ");
96
+ subagentContentStates.set(subagentId, true);
97
+ }
67
98
  // Stream subagent content with indentation - output only the new chunk
68
99
  process.stdout.write(chunk);
69
100
  },
@@ -83,6 +114,8 @@ export async function startPrintCli(options) {
83
114
  callbacks,
84
115
  restoreSessionId,
85
116
  continueLastSession,
117
+ permissionMode: bypassPermissions ? "bypassPermissions" : undefined,
118
+ // 保持流式模式以获得更好的命令行用户体验
86
119
  });
87
120
  // Send message if provided and not empty
88
121
  if (message && message.trim() !== "") {
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Tool parameter transformation utilities for UI rendering
3
+ * Forces type judgment based on tool name using type assertions
4
+ */
5
+ import { type Change, type WriteToolParameters, type EditToolParameters, type MultiEditToolParameters } from "wave-agent-sdk";
6
+ /**
7
+ * Transform Write tool parameters to changes
8
+ */
9
+ export declare function transformWriteParameters(parameters: WriteToolParameters): Change[];
10
+ /**
11
+ * Transform Edit tool parameters to changes
12
+ */
13
+ export declare function transformEditParameters(parameters: EditToolParameters): Change[];
14
+ /**
15
+ * Transform MultiEdit tool parameters to changes
16
+ */
17
+ export declare function transformMultiEditParameters(parameters: MultiEditToolParameters): Change[];
18
+ /**
19
+ * Transform tool block parameters into standardized Change[] array for diff display
20
+ * Forces type judgment based on tool name using type assertions
21
+ */
22
+ export declare function transformToolBlockToChanges(toolName: string, parameters: string): Change[];
23
+ //# sourceMappingURL=toolParameterTransforms.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolParameterTransforms.d.ts","sourceRoot":"","sources":["../../src/utils/toolParameterTransforms.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,KAAK,MAAM,EACX,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,EAC7B,MAAM,gBAAgB,CAAC;AAmBxB;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,mBAAmB,GAC9B,MAAM,EAAE,CAOV;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,kBAAkB,GAC7B,MAAM,EAAE,CAOV;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAC1C,UAAU,EAAE,uBAAuB,GAClC,MAAM,EAAE,CAKV;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,MAAM,EAAE,CA2BV"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Tool parameter transformation utilities for UI rendering
3
+ * Forces type judgment based on tool name using type assertions
4
+ */
5
+ import { logger } from "./logger.js";
6
+ /**
7
+ * Parse tool block parameters
8
+ */
9
+ function parseToolParameters(parameters) {
10
+ if (!parameters) {
11
+ return {};
12
+ }
13
+ try {
14
+ return JSON.parse(parameters);
15
+ }
16
+ catch (error) {
17
+ logger.warn("Failed to parse tool parameters:", error);
18
+ return {};
19
+ }
20
+ }
21
+ /**
22
+ * Transform Write tool parameters to changes
23
+ */
24
+ export function transformWriteParameters(parameters) {
25
+ return [
26
+ {
27
+ oldContent: "", // No previous content for write operations
28
+ newContent: parameters.content,
29
+ },
30
+ ];
31
+ }
32
+ /**
33
+ * Transform Edit tool parameters to changes
34
+ */
35
+ export function transformEditParameters(parameters) {
36
+ return [
37
+ {
38
+ oldContent: parameters.old_string,
39
+ newContent: parameters.new_string,
40
+ },
41
+ ];
42
+ }
43
+ /**
44
+ * Transform MultiEdit tool parameters to changes
45
+ */
46
+ export function transformMultiEditParameters(parameters) {
47
+ return parameters.edits.map((edit) => ({
48
+ oldContent: edit.old_string,
49
+ newContent: edit.new_string,
50
+ }));
51
+ }
52
+ /**
53
+ * Transform tool block parameters into standardized Change[] array for diff display
54
+ * Forces type judgment based on tool name using type assertions
55
+ */
56
+ export function transformToolBlockToChanges(toolName, parameters) {
57
+ try {
58
+ if (!toolName) {
59
+ return [];
60
+ }
61
+ const parsedParams = parseToolParameters(parameters);
62
+ switch (toolName) {
63
+ case "Write":
64
+ return transformWriteParameters(parsedParams);
65
+ case "Edit":
66
+ return transformEditParameters(parsedParams);
67
+ case "MultiEdit":
68
+ return transformMultiEditParameters(parsedParams);
69
+ default:
70
+ return [];
71
+ }
72
+ }
73
+ catch (error) {
74
+ logger.warn("Failed to transform tool block to changes:", error);
75
+ return [];
76
+ }
77
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wave-code",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "CLI-based code assistant powered by AI, built with React and Ink",
5
5
  "keywords": [
6
6
  "ai",
@@ -26,13 +26,13 @@
26
26
  "dependencies": {
27
27
  "chalk": "^5.6.2",
28
28
  "diff": "^8.0.2",
29
- "glob": "^11.0.3",
29
+ "glob": "^13.0.0",
30
30
  "ink": "^6.5.1",
31
31
  "marked": "^11.2.0",
32
32
  "marked-terminal": "^7.3.0",
33
33
  "react": "^19.1.0",
34
34
  "yargs": "^17.7.2",
35
- "wave-agent-sdk": "0.0.8"
35
+ "wave-agent-sdk": "0.0.10"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@types/marked-terminal": "^6.1.1",
@@ -41,7 +41,7 @@
41
41
  "eslint-plugin-react": "^7.37.5",
42
42
  "eslint-plugin-react-hooks": "^5.2.0",
43
43
  "ink-testing-library": "^4.0.0",
44
- "rimraf": "^6.0.1",
44
+ "rimraf": "^6.1.2",
45
45
  "tsc-alias": "^1.8.16",
46
46
  "tsx": "^4.20.4",
47
47
  "vitest": "^3.2.4"
@@ -50,10 +50,11 @@
50
50
  "react": ">=18.0.0"
51
51
  },
52
52
  "engines": {
53
- "node": ">=16.0.0"
53
+ "node": ">=22.0.0"
54
54
  },
55
55
  "license": "MIT",
56
56
  "scripts": {
57
+ "wave": "tsx src/index.ts",
57
58
  "build": "rimraf dist && tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
58
59
  "type-check": "tsc --noEmit --incremental",
59
60
  "watch": "tsc -p tsconfig.build.json --watch & tsc-alias -p tsconfig.build.json --watch",
package/src/cli.tsx CHANGED
@@ -6,10 +6,11 @@ import { cleanupLogs } from "./utils/logger.js";
6
6
  export interface CliOptions {
7
7
  restoreSessionId?: string;
8
8
  continueLastSession?: boolean;
9
+ bypassPermissions?: boolean;
9
10
  }
10
11
 
11
12
  export async function startCli(options: CliOptions): Promise<void> {
12
- const { restoreSessionId, continueLastSession } = options;
13
+ const { restoreSessionId, continueLastSession, bypassPermissions } = options;
13
14
 
14
15
  // Continue with ink-based UI for normal mode
15
16
  // Global cleanup tracker
@@ -63,6 +64,7 @@ export async function startCli(options: CliOptions): Promise<void> {
63
64
  <App
64
65
  restoreSessionId={restoreSessionId}
65
66
  continueLastSession={continueLastSession}
67
+ bypassPermissions={bypassPermissions}
66
68
  />,
67
69
  );
68
70
 
@@ -6,11 +6,14 @@ import { AppProvider } from "../contexts/useAppConfig.js";
6
6
  interface AppProps {
7
7
  restoreSessionId?: string;
8
8
  continueLastSession?: boolean;
9
+ bypassPermissions?: boolean;
9
10
  }
10
11
 
11
- const AppWithProviders: React.FC = () => {
12
+ const AppWithProviders: React.FC<{ bypassPermissions?: boolean }> = ({
13
+ bypassPermissions,
14
+ }) => {
12
15
  return (
13
- <ChatProvider>
16
+ <ChatProvider bypassPermissions={bypassPermissions}>
14
17
  <ChatInterface />
15
18
  </ChatProvider>
16
19
  );
@@ -19,13 +22,14 @@ const AppWithProviders: React.FC = () => {
19
22
  export const App: React.FC<AppProps> = ({
20
23
  restoreSessionId,
21
24
  continueLastSession,
25
+ bypassPermissions,
22
26
  }) => {
23
27
  return (
24
28
  <AppProvider
25
29
  restoreSessionId={restoreSessionId}
26
30
  continueLastSession={continueLastSession}
27
31
  >
28
- <AppWithProviders />
32
+ <AppWithProviders bypassPermissions={bypassPermissions} />
29
33
  </AppProvider>
30
34
  );
31
35
  };
@@ -1,6 +1,10 @@
1
1
  import React, { useState, useEffect } from "react";
2
2
  import { Box, Text, useInput } from "ink";
3
- import { searchBashHistory, type BashHistoryEntry } from "wave-agent-sdk";
3
+ import {
4
+ searchBashHistory,
5
+ deleteBashCommandFromHistory,
6
+ type BashHistoryEntry,
7
+ } from "wave-agent-sdk";
4
8
  import { logger } from "../utils/logger.js";
5
9
 
6
10
  export interface BashHistorySelectorProps {
@@ -20,6 +24,7 @@ export const BashHistorySelector: React.FC<BashHistorySelectorProps> = ({
20
24
  }) => {
21
25
  const [selectedIndex, setSelectedIndex] = useState(0);
22
26
  const [commands, setCommands] = useState<BashHistoryEntry[]>([]);
27
+ const [refreshCounter, setRefreshCounter] = useState(0);
23
28
 
24
29
  // Search bash history
25
30
  useEffect(() => {
@@ -30,8 +35,9 @@ export const BashHistorySelector: React.FC<BashHistorySelectorProps> = ({
30
35
  searchQuery,
31
36
  workdir,
32
37
  resultCount: results.length,
38
+ refreshCounter,
33
39
  });
34
- }, [searchQuery, workdir]);
40
+ }, [searchQuery, workdir, refreshCounter]);
35
41
 
36
42
  useInput((input, key) => {
37
43
  logger.debug("BashHistorySelector useInput:", {
@@ -77,6 +83,22 @@ export const BashHistorySelector: React.FC<BashHistorySelectorProps> = ({
77
83
  setSelectedIndex(Math.min(commands.length - 1, selectedIndex + 1));
78
84
  return;
79
85
  }
86
+
87
+ if (key.delete) {
88
+ if (commands.length > 0 && selectedIndex < commands.length) {
89
+ const selectedCommand = commands[selectedIndex];
90
+ deleteBashCommandFromHistory(
91
+ selectedCommand.command,
92
+ selectedCommand.workdir,
93
+ );
94
+ setRefreshCounter((prev) => prev + 1);
95
+ // Adjust selectedIndex if we deleted the last item
96
+ if (selectedIndex >= commands.length - 1 && selectedIndex > 0) {
97
+ setSelectedIndex(selectedIndex - 1);
98
+ }
99
+ }
100
+ return;
101
+ }
80
102
  });
81
103
 
82
104
  if (commands.length === 0) {
@@ -155,7 +177,8 @@ export const BashHistorySelector: React.FC<BashHistorySelectorProps> = ({
155
177
 
156
178
  <Box>
157
179
  <Text dimColor>
158
- Use ↑↓ to navigate, Enter to execute, Tab to insert, Escape to cancel
180
+ Use ↑↓ to navigate, Enter to execute, Tab to insert, Delete to remove,
181
+ Escape to cancel
159
182
  </Text>
160
183
  </Box>
161
184
  </Box>
@@ -2,6 +2,7 @@ import React from "react";
2
2
  import { Box } from "ink";
3
3
  import { MessageList } from "./MessageList.js";
4
4
  import { InputBox } from "./InputBox.js";
5
+ import { Confirmation } from "./Confirmation.js";
5
6
  import { useChat } from "../contexts/useChat.js";
6
7
 
7
8
  export const ChatInterface: React.FC = () => {
@@ -22,6 +23,10 @@ export const ChatInterface: React.FC = () => {
22
23
  latestTotalTokens,
23
24
  slashCommands,
24
25
  hasSlashCommand,
26
+ isConfirmationVisible,
27
+ confirmingTool,
28
+ handleConfirmationDecision,
29
+ handleConfirmationCancel,
25
30
  } = useChat();
26
31
 
27
32
  if (!sessionId) return null;
@@ -38,21 +43,30 @@ export const ChatInterface: React.FC = () => {
38
43
  key={String(isExpanded) + sessionId}
39
44
  />
40
45
 
41
- {!isExpanded && (
42
- <InputBox
43
- isLoading={isLoading}
44
- isCommandRunning={isCommandRunning}
45
- userInputHistory={userInputHistory}
46
- sendMessage={sendMessage}
47
- abortMessage={abortMessage}
48
- saveMemory={saveMemory}
49
- mcpServers={mcpServers}
50
- connectMcpServer={connectMcpServer}
51
- disconnectMcpServer={disconnectMcpServer}
52
- slashCommands={slashCommands}
53
- hasSlashCommand={hasSlashCommand}
54
- />
55
- )}
46
+ {!isExpanded &&
47
+ (isConfirmationVisible ? (
48
+ <Confirmation
49
+ toolName={confirmingTool!.name}
50
+ toolInput={confirmingTool!.input}
51
+ onDecision={handleConfirmationDecision}
52
+ onCancel={handleConfirmationCancel}
53
+ onAbort={abortMessage}
54
+ />
55
+ ) : (
56
+ <InputBox
57
+ isLoading={isLoading}
58
+ isCommandRunning={isCommandRunning}
59
+ userInputHistory={userInputHistory}
60
+ sendMessage={sendMessage}
61
+ abortMessage={abortMessage}
62
+ saveMemory={saveMemory}
63
+ mcpServers={mcpServers}
64
+ connectMcpServer={connectMcpServer}
65
+ disconnectMcpServer={disconnectMcpServer}
66
+ slashCommands={slashCommands}
67
+ hasSlashCommand={hasSlashCommand}
68
+ />
69
+ ))}
56
70
  </Box>
57
71
  );
58
72
  };