wave-code 0.8.0 → 0.8.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.
@@ -1 +1 @@
1
- {"version":3,"file":"CommandSelector.d.ts","sourceRoot":"","sources":["../../src/components/CommandSelector.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAExC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AA0CnD,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,CAAC,EAAE,YAAY,EAAE,CAAC;CAC3B;AAED,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CA+I1D,CAAC"}
1
+ {"version":3,"file":"CommandSelector.d.ts","sourceRoot":"","sources":["../../src/components/CommandSelector.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAExC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAGnD,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,CAAC,EAAE,YAAY,EAAE,CAAC;CAC3B;AAED,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CA+I1D,CAAC"}
@@ -1,44 +1,7 @@
1
1
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
2
  import React, { useState } from "react";
3
3
  import { Box, Text, useInput } from "ink";
4
- const AVAILABLE_COMMANDS = [
5
- {
6
- id: "clear",
7
- name: "clear",
8
- description: "Clear the chat session and terminal",
9
- handler: () => { }, // Handler here won't be used, actual processing is in the hook
10
- },
11
- {
12
- id: "tasks",
13
- name: "tasks",
14
- description: "View and manage background tasks (shells and subagents)",
15
- handler: () => { }, // Handler here won't be used, actual processing is in the hook
16
- },
17
- {
18
- id: "mcp",
19
- name: "mcp",
20
- description: "View and manage MCP servers",
21
- handler: () => { }, // Handler here won't be used, actual processing is in the hook
22
- },
23
- {
24
- id: "rewind",
25
- name: "rewind",
26
- description: "Revert conversation and file changes to a previous checkpoint",
27
- handler: () => { }, // Handler here won't be used, actual processing is in the hook
28
- },
29
- {
30
- id: "help",
31
- name: "help",
32
- description: "Show help and key bindings",
33
- handler: () => { }, // Handler here won't be used, actual processing is in the hook
34
- },
35
- {
36
- id: "status",
37
- name: "status",
38
- description: "Show agent status and configuration",
39
- handler: () => { }, // Handler here won't be used, actual processing is in the hook
40
- },
41
- ];
4
+ import { AVAILABLE_COMMANDS } from "../constants/commands.js";
42
5
  export const CommandSelector = ({ searchQuery, onSelect, onInsert, onCancel, commands = [], // Default to empty array
43
6
  }) => {
44
7
  const MAX_VISIBLE_ITEMS = 3;
@@ -1 +1 @@
1
- {"version":3,"file":"ConfirmationSelector.d.ts","sourceRoot":"","sources":["../../src/components/ConfirmationSelector.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4C,MAAM,OAAO,CAAC;AAEjE,OAAO,KAAK,EAAE,kBAAkB,EAAwB,MAAM,gBAAgB,CAAC;AAgB/E,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,CAAC,QAAQ,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACnD,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7C;AASD,eAAO,MAAM,oBAAoB,EAAE,KAAK,CAAC,EAAE,CAAC,yBAAyB,CAmmBpE,CAAC"}
1
+ {"version":3,"file":"ConfirmationSelector.d.ts","sourceRoot":"","sources":["../../src/components/ConfirmationSelector.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAuD,MAAM,OAAO,CAAC;AAE5E,OAAO,KAAK,EAAE,kBAAkB,EAAwB,MAAM,gBAAgB,CAAC;AAgB/E,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,CAAC,QAAQ,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACnD,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7C;AASD,eAAO,MAAM,oBAAoB,EAAE,KAAK,CAAC,EAAE,CAAC,yBAAyB,CA6mBpE,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useLayoutEffect, useRef, useState } from "react";
2
+ import { useEffect, useLayoutEffect, useRef, useState } from "react";
3
3
  import { Box, Text, useInput, useStdout, measureElement } from "ink";
4
4
  import { BASH_TOOL_NAME, EXIT_PLAN_MODE_TOOL_NAME, ASK_USER_QUESTION_TOOL_NAME, } from "wave-agent-sdk";
5
5
  const getHeaderColor = (header) => {
@@ -34,6 +34,14 @@ export const ConfirmationSelector = ({ toolName, toolInput, suggestedPrefix, hid
34
34
  otherCursorPosition: 0,
35
35
  savedStates: {},
36
36
  });
37
+ const pendingDecisionRef = useRef(null);
38
+ useEffect(() => {
39
+ if (pendingDecisionRef.current) {
40
+ const decision = pendingDecisionRef.current;
41
+ pendingDecisionRef.current = null;
42
+ onDecision(decision);
43
+ }
44
+ });
37
45
  const questions = toolInput?.questions || [];
38
46
  const currentQuestion = questions[questionState.currentQuestionIndex];
39
47
  const getAutoOptionText = () => {
@@ -147,10 +155,10 @@ export const ConfirmationSelector = ({ toolName, toolInput, suggestedPrefix, hid
147
155
  const allAnswered = questions.every((q) => finalAnswers[q.question]);
148
156
  if (!allAnswered)
149
157
  return prev;
150
- onDecision({
158
+ pendingDecisionRef.current = {
151
159
  behavior: "allow",
152
160
  message: JSON.stringify(finalAnswers),
153
- });
161
+ };
154
162
  return {
155
163
  ...prev,
156
164
  userAnswers: finalAnswers,
@@ -1,6 +1,8 @@
1
1
  import React from "react";
2
+ import type { SlashCommand } from "wave-agent-sdk";
2
3
  export interface HelpViewProps {
3
4
  onCancel: () => void;
5
+ commands?: SlashCommand[];
4
6
  }
5
7
  export declare const HelpView: React.FC<HelpViewProps>;
6
8
  //# sourceMappingURL=HelpView.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"HelpView.d.ts","sourceRoot":"","sources":["../../src/components/HelpView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,aAAa,CAqD5C,CAAC"}
1
+ {"version":3,"file":"HelpView.d.ts","sourceRoot":"","sources":["../../src/components/HelpView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAExC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAGnD,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,CAAC,EAAE,YAAY,EAAE,CAAC;CAC3B;AAED,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,aAAa,CAmK5C,CAAC"}
@@ -1,9 +1,40 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from "react";
2
3
  import { Box, Text, useInput } from "ink";
3
- export const HelpView = ({ onCancel }) => {
4
+ import { AVAILABLE_COMMANDS } from "../constants/commands.js";
5
+ export const HelpView = ({ onCancel, commands = [], }) => {
6
+ const [activeTab, setActiveTab] = useState("general");
7
+ const [selectedIndex, setSelectedIndex] = useState(0);
8
+ const MAX_VISIBLE_ITEMS = 10;
9
+ const tabs = [
10
+ "general",
11
+ "commands",
12
+ ];
13
+ if (commands.length > 0) {
14
+ tabs.push("custom-commands");
15
+ }
4
16
  useInput((_, key) => {
5
- if (key.escape || key.return) {
17
+ if (key.escape) {
6
18
  onCancel();
19
+ return;
20
+ }
21
+ if (key.tab) {
22
+ setActiveTab((prev) => {
23
+ const currentIndex = tabs.indexOf(prev);
24
+ const nextIndex = (currentIndex + 1) % tabs.length;
25
+ return tabs[nextIndex];
26
+ });
27
+ setSelectedIndex(0);
28
+ return;
29
+ }
30
+ if (activeTab === "commands" || activeTab === "custom-commands") {
31
+ const currentCommands = activeTab === "commands" ? AVAILABLE_COMMANDS : commands;
32
+ if (key.upArrow) {
33
+ setSelectedIndex((prev) => Math.max(0, prev - 1));
34
+ }
35
+ else if (key.downArrow) {
36
+ setSelectedIndex((prev) => Math.min(currentCommands.length - 1, prev + 1));
37
+ }
7
38
  }
8
39
  });
9
40
  const helpItems = [
@@ -15,12 +46,25 @@ export const HelpView = ({ onCancel }) => {
15
46
  { key: "Ctrl+B", description: "Background current task" },
16
47
  { key: "Ctrl+V", description: "Paste image" },
17
48
  { key: "Shift+Tab", description: "Cycle permission mode" },
18
- { key: "/status", description: "Show agent status and configuration" },
19
- { key: "/clear", description: "Clear the chat session and terminal" },
20
49
  {
21
50
  key: "Esc",
22
51
  description: "Interrupt AI or command / Cancel selector / Close help",
23
52
  },
24
53
  ];
25
- return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "cyan", borderLeft: false, borderRight: false, paddingX: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: "cyan", bold: true, underline: true, children: "Help & Key Bindings" }) }), helpItems.map((item, index) => (_jsxs(Box, { children: [_jsx(Box, { width: 20, children: _jsx(Text, { color: "yellow", children: item.key }) }), _jsx(Text, { color: "white", children: item.description })] }, index))), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Press Esc or Enter to close" }) })] }));
54
+ // Calculate visible window for commands
55
+ const currentCommands = activeTab === "commands" ? AVAILABLE_COMMANDS : commands;
56
+ const startIndex = Math.max(0, Math.min(selectedIndex - Math.floor(MAX_VISIBLE_ITEMS / 2), Math.max(0, currentCommands.length - MAX_VISIBLE_ITEMS)));
57
+ const visibleCommands = currentCommands.slice(startIndex, startIndex + MAX_VISIBLE_ITEMS);
58
+ const footerText = [
59
+ "Tab switch",
60
+ activeTab !== "general" && "↑↓ navigate",
61
+ "Esc close",
62
+ ]
63
+ .filter(Boolean)
64
+ .join(" • ");
65
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "cyan", borderLeft: false, borderRight: false, paddingX: 1, width: "100%", children: [_jsxs(Box, { marginBottom: 1, gap: 2, children: [_jsx(Text, { color: activeTab === "general" ? "cyan" : "gray", bold: true, underline: activeTab === "general", children: "General" }), _jsx(Text, { color: activeTab === "commands" ? "cyan" : "gray", bold: true, underline: activeTab === "commands", children: "Commands" }), commands.length > 0 && (_jsx(Text, { color: activeTab === "custom-commands" ? "cyan" : "gray", bold: true, underline: activeTab === "custom-commands", children: "Custom Commands" }))] }), activeTab === "general" ? (_jsx(Box, { flexDirection: "column", children: helpItems.map((item, index) => (_jsxs(Box, { children: [_jsx(Box, { width: 20, children: _jsx(Text, { color: "yellow", children: item.key }) }), _jsx(Text, { color: "white", children: item.description })] }, index))) })) : (_jsx(Box, { flexDirection: "column", children: visibleCommands.map((command, index) => {
66
+ const actualIndex = startIndex + index;
67
+ const isSelected = actualIndex === selectedIndex;
68
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: isSelected ? "black" : "white", backgroundColor: isSelected ? "cyan" : undefined, children: [isSelected ? "▶ " : " ", "/", command.id] }), isSelected && (_jsx(Box, { marginLeft: 4, children: _jsx(Text, { color: "gray", dimColor: true, children: command.description }) }))] }, command.id));
69
+ }) })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: footerText }) })] }));
26
70
  };
@@ -1 +1 @@
1
- {"version":3,"file":"InputBox.d.ts","sourceRoot":"","sources":["../../src/components/InputBox.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoB,MAAM,OAAO,CAAC;AAczC,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEpE,eAAO,MAAM,sBAAsB,mDACe,CAAC;AAEnD,eAAO,MAAM,6BAA6B,QAGzC,CAAC;AAEF,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,CACZ,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,KAC/C,IAAI,CAAC;IACV,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAE1B,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;IAC/B,gBAAgB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5D,mBAAmB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAE/D,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC;IAC/B,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;CAClD;AAED,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,aAAa,CAgO5C,CAAC"}
1
+ {"version":3,"file":"InputBox.d.ts","sourceRoot":"","sources":["../../src/components/InputBox.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoB,MAAM,OAAO,CAAC;AAczC,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEpE,eAAO,MAAM,sBAAsB,mDACe,CAAC;AAEnD,eAAO,MAAM,6BAA6B,QAGzC,CAAC;AAEF,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,CACZ,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,KAC/C,IAAI,CAAC;IACV,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAE1B,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;IAC/B,gBAAgB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5D,mBAAmB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAE/D,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC;IAC/B,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;CAClD;AAED,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,aAAa,CAkO5C,CAAC"}
@@ -78,7 +78,7 @@ export const InputBox = ({ isLoading = false, isCommandRunning = false, sendMess
78
78
  return (_jsx(RewindCommand, { messages: messages, onSelect: handleRewindSelectWithClose, onCancel: handleRewindCancel, getFullMessageThread: getFullMessageThread }));
79
79
  }
80
80
  if (showHelp) {
81
- return _jsx(HelpView, { onCancel: () => setShowHelp(false) });
81
+ return (_jsx(HelpView, { onCancel: () => setShowHelp(false), commands: slashCommands }));
82
82
  }
83
83
  if (showStatusCommand) {
84
84
  return _jsx(StatusCommand, { onCancel: () => setShowStatusCommand(false) });
@@ -0,0 +1,3 @@
1
+ import type { SlashCommand } from "wave-agent-sdk";
2
+ export declare const AVAILABLE_COMMANDS: SlashCommand[];
3
+ //# sourceMappingURL=commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/constants/commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD,eAAO,MAAM,kBAAkB,EAAE,YAAY,EAsC5C,CAAC"}
@@ -0,0 +1,38 @@
1
+ export const AVAILABLE_COMMANDS = [
2
+ {
3
+ id: "clear",
4
+ name: "clear",
5
+ description: "Clear the chat session and terminal",
6
+ handler: () => { }, // Handler here won't be used, actual processing is in the hook
7
+ },
8
+ {
9
+ id: "tasks",
10
+ name: "tasks",
11
+ description: "View and manage background tasks (shells and subagents)",
12
+ handler: () => { }, // Handler here won't be used, actual processing is in the hook
13
+ },
14
+ {
15
+ id: "mcp",
16
+ name: "mcp",
17
+ description: "View and manage MCP servers",
18
+ handler: () => { }, // Handler here won't be used, actual processing is in the hook
19
+ },
20
+ {
21
+ id: "rewind",
22
+ name: "rewind",
23
+ description: "Revert conversation and file changes to a previous checkpoint",
24
+ handler: () => { }, // Handler here won't be used, actual processing is in the hook
25
+ },
26
+ {
27
+ id: "help",
28
+ name: "help",
29
+ description: "Show help and key bindings",
30
+ handler: () => { }, // Handler here won't be used, actual processing is in the hook
31
+ },
32
+ {
33
+ id: "status",
34
+ name: "status",
35
+ description: "Show agent status and configuration",
36
+ handler: () => { }, // Handler here won't be used, actual processing is in the hook
37
+ },
38
+ ];
@@ -1 +1 @@
1
- {"version":3,"file":"highlightUtils.d.ts","sourceRoot":"","sources":["../../src/utils/highlightUtils.ts"],"names":[],"mappings":"AA6DA,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAcvE"}
1
+ {"version":3,"file":"highlightUtils.d.ts","sourceRoot":"","sources":["../../src/utils/highlightUtils.ts"],"names":[],"mappings":"AA6DA,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAsCvE"}
@@ -1,48 +1,48 @@
1
- import hljs from 'highlight.js';
2
- import { parse, HTMLElement, TextNode } from 'node-html-parser';
3
- import chalk from 'chalk';
1
+ import hljs from "highlight.js";
2
+ import { parse, HTMLElement, TextNode } from "node-html-parser";
3
+ import chalk from "chalk";
4
4
  const theme = {
5
- 'hljs-keyword': chalk.blue,
6
- 'hljs-built_in': chalk.cyan,
7
- 'hljs-type': chalk.cyan,
8
- 'hljs-literal': chalk.magenta,
9
- 'hljs-number': chalk.magenta,
10
- 'hljs-operator': chalk.white,
11
- 'hljs-punctuation': chalk.white,
12
- 'hljs-property': chalk.yellow,
13
- 'hljs-attr': chalk.yellow,
14
- 'hljs-variable': chalk.white,
15
- 'hljs-template-variable': chalk.white,
16
- 'hljs-string': chalk.green,
17
- 'hljs-char': chalk.green,
18
- 'hljs-comment': chalk.gray,
19
- 'hljs-doctag': chalk.gray,
20
- 'hljs-function': chalk.yellow,
21
- 'hljs-title': chalk.yellow,
22
- 'hljs-params': chalk.white,
23
- 'hljs-tag': chalk.blue,
24
- 'hljs-name': chalk.blue,
25
- 'hljs-selector-tag': chalk.blue,
26
- 'hljs-selector-id': chalk.blue,
27
- 'hljs-selector-class': chalk.blue,
28
- 'hljs-selector-attr': chalk.blue,
29
- 'hljs-selector-pseudo': chalk.blue,
30
- 'hljs-subst': chalk.white,
31
- 'hljs-section': chalk.blue.bold,
32
- 'hljs-bullet': chalk.magenta,
33
- 'hljs-emphasis': chalk.italic,
34
- 'hljs-strong': chalk.bold,
35
- 'hljs-addition': chalk.green,
36
- 'hljs-deletion': chalk.red,
37
- 'hljs-link': chalk.blue.underline,
5
+ "hljs-keyword": chalk.blue,
6
+ "hljs-built_in": chalk.cyan,
7
+ "hljs-type": chalk.cyan,
8
+ "hljs-literal": chalk.magenta,
9
+ "hljs-number": chalk.magenta,
10
+ "hljs-operator": chalk.white,
11
+ "hljs-punctuation": chalk.white,
12
+ "hljs-property": chalk.yellow,
13
+ "hljs-attr": chalk.yellow,
14
+ "hljs-variable": chalk.white,
15
+ "hljs-template-variable": chalk.white,
16
+ "hljs-string": chalk.green,
17
+ "hljs-char": chalk.green,
18
+ "hljs-comment": chalk.gray,
19
+ "hljs-doctag": chalk.gray,
20
+ "hljs-function": chalk.yellow,
21
+ "hljs-title": chalk.yellow,
22
+ "hljs-params": chalk.white,
23
+ "hljs-tag": chalk.blue,
24
+ "hljs-name": chalk.blue,
25
+ "hljs-selector-tag": chalk.blue,
26
+ "hljs-selector-id": chalk.blue,
27
+ "hljs-selector-class": chalk.blue,
28
+ "hljs-selector-attr": chalk.blue,
29
+ "hljs-selector-pseudo": chalk.blue,
30
+ "hljs-subst": chalk.white,
31
+ "hljs-section": chalk.blue.bold,
32
+ "hljs-bullet": chalk.magenta,
33
+ "hljs-emphasis": chalk.italic,
34
+ "hljs-strong": chalk.bold,
35
+ "hljs-addition": chalk.green,
36
+ "hljs-deletion": chalk.red,
37
+ "hljs-link": chalk.blue.underline,
38
38
  };
39
39
  function nodeToAnsi(node) {
40
40
  if (node instanceof TextNode) {
41
41
  return node.text;
42
42
  }
43
43
  if (node instanceof HTMLElement) {
44
- const content = node.childNodes.map(nodeToAnsi).join('');
45
- const classes = node.getAttribute('class')?.split(/\s+/) || [];
44
+ const content = node.childNodes.map(nodeToAnsi).join("");
45
+ const classes = node.getAttribute("class")?.split(/\s+/) || [];
46
46
  for (const className of classes) {
47
47
  if (theme[className]) {
48
48
  return theme[className](content);
@@ -50,18 +50,42 @@ function nodeToAnsi(node) {
50
50
  }
51
51
  return content;
52
52
  }
53
- return '';
53
+ return "";
54
54
  }
55
55
  export function highlightToAnsi(code, language) {
56
56
  if (!code) {
57
- return '';
57
+ return "";
58
58
  }
59
59
  try {
60
60
  const highlighted = language
61
61
  ? hljs.highlight(code, { language }).value
62
- : hljs.highlightAuto(code).value;
62
+ : hljs.highlightAuto(code, [
63
+ "javascript",
64
+ "typescript",
65
+ "bash",
66
+ "json",
67
+ "markdown",
68
+ "python",
69
+ "yaml",
70
+ "html",
71
+ "css",
72
+ "sql",
73
+ "xml",
74
+ "rust",
75
+ "go",
76
+ "java",
77
+ "cpp",
78
+ "c",
79
+ "csharp",
80
+ "php",
81
+ "ruby",
82
+ "swift",
83
+ "kotlin",
84
+ "toml",
85
+ "ini",
86
+ ]).value;
63
87
  const root = parse(highlighted);
64
- return root.childNodes.map(nodeToAnsi).join('');
88
+ return root.childNodes.map(nodeToAnsi).join("");
65
89
  }
66
90
  catch {
67
91
  return code;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wave-code",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "CLI-based code assistant powered by AI, built with React and Ink",
5
5
  "repository": {
6
6
  "type": "git",
@@ -39,7 +39,7 @@
39
39
  "react": "^19.2.4",
40
40
  "react-dom": "19.2.4",
41
41
  "yargs": "^17.7.2",
42
- "wave-agent-sdk": "0.8.0"
42
+ "wave-agent-sdk": "0.8.1"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@types/react": "^19.1.8",
@@ -1,46 +1,7 @@
1
1
  import React, { useState } from "react";
2
2
  import { Box, Text, useInput } from "ink";
3
3
  import type { SlashCommand } from "wave-agent-sdk";
4
-
5
- const AVAILABLE_COMMANDS: SlashCommand[] = [
6
- {
7
- id: "clear",
8
- name: "clear",
9
- description: "Clear the chat session and terminal",
10
- handler: () => {}, // Handler here won't be used, actual processing is in the hook
11
- },
12
- {
13
- id: "tasks",
14
- name: "tasks",
15
- description: "View and manage background tasks (shells and subagents)",
16
- handler: () => {}, // Handler here won't be used, actual processing is in the hook
17
- },
18
- {
19
- id: "mcp",
20
- name: "mcp",
21
- description: "View and manage MCP servers",
22
- handler: () => {}, // Handler here won't be used, actual processing is in the hook
23
- },
24
- {
25
- id: "rewind",
26
- name: "rewind",
27
- description:
28
- "Revert conversation and file changes to a previous checkpoint",
29
- handler: () => {}, // Handler here won't be used, actual processing is in the hook
30
- },
31
- {
32
- id: "help",
33
- name: "help",
34
- description: "Show help and key bindings",
35
- handler: () => {}, // Handler here won't be used, actual processing is in the hook
36
- },
37
- {
38
- id: "status",
39
- name: "status",
40
- description: "Show agent status and configuration",
41
- handler: () => {}, // Handler here won't be used, actual processing is in the hook
42
- },
43
- ];
4
+ import { AVAILABLE_COMMANDS } from "../constants/commands.js";
44
5
 
45
6
  export interface CommandSelectorProps {
46
7
  searchQuery: string;
@@ -1,4 +1,4 @@
1
- import React, { useLayoutEffect, useRef, useState } from "react";
1
+ import React, { useEffect, useLayoutEffect, useRef, useState } from "react";
2
2
  import { Box, Text, useInput, useStdout, measureElement } from "ink";
3
3
  import type { PermissionDecision, AskUserQuestionInput } from "wave-agent-sdk";
4
4
  import {
@@ -81,6 +81,16 @@ export const ConfirmationSelector: React.FC<ConfirmationSelectorProps> = ({
81
81
  >,
82
82
  });
83
83
 
84
+ const pendingDecisionRef = useRef<PermissionDecision | null>(null);
85
+
86
+ useEffect(() => {
87
+ if (pendingDecisionRef.current) {
88
+ const decision = pendingDecisionRef.current;
89
+ pendingDecisionRef.current = null;
90
+ onDecision(decision);
91
+ }
92
+ });
93
+
84
94
  const questions =
85
95
  (toolInput as unknown as AskUserQuestionInput)?.questions || [];
86
96
  const currentQuestion = questions[questionState.currentQuestionIndex];
@@ -204,10 +214,10 @@ export const ConfirmationSelector: React.FC<ConfirmationSelectorProps> = ({
204
214
  );
205
215
  if (!allAnswered) return prev;
206
216
 
207
- onDecision({
217
+ pendingDecisionRef.current = {
208
218
  behavior: "allow",
209
219
  message: JSON.stringify(finalAnswers),
210
- });
220
+ };
211
221
  return {
212
222
  ...prev,
213
223
  userAnswers: finalAnswers,
@@ -1,14 +1,57 @@
1
- import React from "react";
1
+ import React, { useState } from "react";
2
2
  import { Box, Text, useInput } from "ink";
3
+ import type { SlashCommand } from "wave-agent-sdk";
4
+ import { AVAILABLE_COMMANDS } from "../constants/commands.js";
3
5
 
4
6
  export interface HelpViewProps {
5
7
  onCancel: () => void;
8
+ commands?: SlashCommand[];
6
9
  }
7
10
 
8
- export const HelpView: React.FC<HelpViewProps> = ({ onCancel }) => {
11
+ export const HelpView: React.FC<HelpViewProps> = ({
12
+ onCancel,
13
+ commands = [],
14
+ }) => {
15
+ const [activeTab, setActiveTab] = useState<
16
+ "general" | "commands" | "custom-commands"
17
+ >("general");
18
+ const [selectedIndex, setSelectedIndex] = useState(0);
19
+ const MAX_VISIBLE_ITEMS = 10;
20
+
21
+ const tabs: ("general" | "commands" | "custom-commands")[] = [
22
+ "general",
23
+ "commands",
24
+ ];
25
+ if (commands.length > 0) {
26
+ tabs.push("custom-commands");
27
+ }
28
+
9
29
  useInput((_, key) => {
10
- if (key.escape || key.return) {
30
+ if (key.escape) {
11
31
  onCancel();
32
+ return;
33
+ }
34
+
35
+ if (key.tab) {
36
+ setActiveTab((prev) => {
37
+ const currentIndex = tabs.indexOf(prev);
38
+ const nextIndex = (currentIndex + 1) % tabs.length;
39
+ return tabs[nextIndex];
40
+ });
41
+ setSelectedIndex(0);
42
+ return;
43
+ }
44
+
45
+ if (activeTab === "commands" || activeTab === "custom-commands") {
46
+ const currentCommands =
47
+ activeTab === "commands" ? AVAILABLE_COMMANDS : commands;
48
+ if (key.upArrow) {
49
+ setSelectedIndex((prev) => Math.max(0, prev - 1));
50
+ } else if (key.downArrow) {
51
+ setSelectedIndex((prev) =>
52
+ Math.min(currentCommands.length - 1, prev + 1),
53
+ );
54
+ }
12
55
  }
13
56
  });
14
57
 
@@ -21,14 +64,35 @@ export const HelpView: React.FC<HelpViewProps> = ({ onCancel }) => {
21
64
  { key: "Ctrl+B", description: "Background current task" },
22
65
  { key: "Ctrl+V", description: "Paste image" },
23
66
  { key: "Shift+Tab", description: "Cycle permission mode" },
24
- { key: "/status", description: "Show agent status and configuration" },
25
- { key: "/clear", description: "Clear the chat session and terminal" },
26
67
  {
27
68
  key: "Esc",
28
69
  description: "Interrupt AI or command / Cancel selector / Close help",
29
70
  },
30
71
  ];
31
72
 
73
+ // Calculate visible window for commands
74
+ const currentCommands =
75
+ activeTab === "commands" ? AVAILABLE_COMMANDS : commands;
76
+ const startIndex = Math.max(
77
+ 0,
78
+ Math.min(
79
+ selectedIndex - Math.floor(MAX_VISIBLE_ITEMS / 2),
80
+ Math.max(0, currentCommands.length - MAX_VISIBLE_ITEMS),
81
+ ),
82
+ );
83
+ const visibleCommands = currentCommands.slice(
84
+ startIndex,
85
+ startIndex + MAX_VISIBLE_ITEMS,
86
+ );
87
+
88
+ const footerText = [
89
+ "Tab switch",
90
+ activeTab !== "general" && "↑↓ navigate",
91
+ "Esc close",
92
+ ]
93
+ .filter(Boolean)
94
+ .join(" • ");
95
+
32
96
  return (
33
97
  <Box
34
98
  flexDirection="column"
@@ -37,24 +101,73 @@ export const HelpView: React.FC<HelpViewProps> = ({ onCancel }) => {
37
101
  borderLeft={false}
38
102
  borderRight={false}
39
103
  paddingX={1}
104
+ width="100%"
40
105
  >
41
- <Box marginBottom={1}>
42
- <Text color="cyan" bold underline>
43
- Help & Key Bindings
106
+ <Box marginBottom={1} gap={2}>
107
+ <Text
108
+ color={activeTab === "general" ? "cyan" : "gray"}
109
+ bold
110
+ underline={activeTab === "general"}
111
+ >
112
+ General
113
+ </Text>
114
+ <Text
115
+ color={activeTab === "commands" ? "cyan" : "gray"}
116
+ bold
117
+ underline={activeTab === "commands"}
118
+ >
119
+ Commands
44
120
  </Text>
121
+ {commands.length > 0 && (
122
+ <Text
123
+ color={activeTab === "custom-commands" ? "cyan" : "gray"}
124
+ bold
125
+ underline={activeTab === "custom-commands"}
126
+ >
127
+ Custom Commands
128
+ </Text>
129
+ )}
45
130
  </Box>
46
131
 
47
- {helpItems.map((item, index) => (
48
- <Box key={index}>
49
- <Box width={20}>
50
- <Text color="yellow">{item.key}</Text>
51
- </Box>
52
- <Text color="white">{item.description}</Text>
132
+ {activeTab === "general" ? (
133
+ <Box flexDirection="column">
134
+ {helpItems.map((item, index) => (
135
+ <Box key={index}>
136
+ <Box width={20}>
137
+ <Text color="yellow">{item.key}</Text>
138
+ </Box>
139
+ <Text color="white">{item.description}</Text>
140
+ </Box>
141
+ ))}
142
+ </Box>
143
+ ) : (
144
+ <Box flexDirection="column">
145
+ {visibleCommands.map((command, index) => {
146
+ const actualIndex = startIndex + index;
147
+ const isSelected = actualIndex === selectedIndex;
148
+ return (
149
+ <Box key={command.id} flexDirection="column">
150
+ <Text
151
+ color={isSelected ? "black" : "white"}
152
+ backgroundColor={isSelected ? "cyan" : undefined}
153
+ >
154
+ {isSelected ? "▶ " : " "}/{command.id}
155
+ </Text>
156
+ {isSelected && (
157
+ <Box marginLeft={4}>
158
+ <Text color="gray" dimColor>
159
+ {command.description}
160
+ </Text>
161
+ </Box>
162
+ )}
163
+ </Box>
164
+ );
165
+ })}
53
166
  </Box>
54
- ))}
167
+ )}
55
168
 
56
169
  <Box marginTop={1}>
57
- <Text dimColor>Press Esc or Enter to close</Text>
170
+ <Text dimColor>{footerText}</Text>
58
171
  </Box>
59
172
  </Box>
60
173
  );
@@ -174,7 +174,9 @@ export const InputBox: React.FC<InputBoxProps> = ({
174
174
  }
175
175
 
176
176
  if (showHelp) {
177
- return <HelpView onCancel={() => setShowHelp(false)} />;
177
+ return (
178
+ <HelpView onCancel={() => setShowHelp(false)} commands={slashCommands} />
179
+ );
178
180
  }
179
181
 
180
182
  if (showStatusCommand) {
@@ -0,0 +1,41 @@
1
+ import type { SlashCommand } from "wave-agent-sdk";
2
+
3
+ export const AVAILABLE_COMMANDS: SlashCommand[] = [
4
+ {
5
+ id: "clear",
6
+ name: "clear",
7
+ description: "Clear the chat session and terminal",
8
+ handler: () => {}, // Handler here won't be used, actual processing is in the hook
9
+ },
10
+ {
11
+ id: "tasks",
12
+ name: "tasks",
13
+ description: "View and manage background tasks (shells and subagents)",
14
+ handler: () => {}, // Handler here won't be used, actual processing is in the hook
15
+ },
16
+ {
17
+ id: "mcp",
18
+ name: "mcp",
19
+ description: "View and manage MCP servers",
20
+ handler: () => {}, // Handler here won't be used, actual processing is in the hook
21
+ },
22
+ {
23
+ id: "rewind",
24
+ name: "rewind",
25
+ description:
26
+ "Revert conversation and file changes to a previous checkpoint",
27
+ handler: () => {}, // Handler here won't be used, actual processing is in the hook
28
+ },
29
+ {
30
+ id: "help",
31
+ name: "help",
32
+ description: "Show help and key bindings",
33
+ handler: () => {}, // Handler here won't be used, actual processing is in the hook
34
+ },
35
+ {
36
+ id: "status",
37
+ name: "status",
38
+ description: "Show agent status and configuration",
39
+ handler: () => {}, // Handler here won't be used, actual processing is in the hook
40
+ },
41
+ ];
@@ -1,41 +1,41 @@
1
- import hljs from 'highlight.js';
2
- import { parse, Node, HTMLElement, TextNode } from 'node-html-parser';
3
- import chalk from 'chalk';
1
+ import hljs from "highlight.js";
2
+ import { parse, Node, HTMLElement, TextNode } from "node-html-parser";
3
+ import chalk from "chalk";
4
4
 
5
5
  const theme: Record<string, (text: string) => string> = {
6
- 'hljs-keyword': chalk.blue,
7
- 'hljs-built_in': chalk.cyan,
8
- 'hljs-type': chalk.cyan,
9
- 'hljs-literal': chalk.magenta,
10
- 'hljs-number': chalk.magenta,
11
- 'hljs-operator': chalk.white,
12
- 'hljs-punctuation': chalk.white,
13
- 'hljs-property': chalk.yellow,
14
- 'hljs-attr': chalk.yellow,
15
- 'hljs-variable': chalk.white,
16
- 'hljs-template-variable': chalk.white,
17
- 'hljs-string': chalk.green,
18
- 'hljs-char': chalk.green,
19
- 'hljs-comment': chalk.gray,
20
- 'hljs-doctag': chalk.gray,
21
- 'hljs-function': chalk.yellow,
22
- 'hljs-title': chalk.yellow,
23
- 'hljs-params': chalk.white,
24
- 'hljs-tag': chalk.blue,
25
- 'hljs-name': chalk.blue,
26
- 'hljs-selector-tag': chalk.blue,
27
- 'hljs-selector-id': chalk.blue,
28
- 'hljs-selector-class': chalk.blue,
29
- 'hljs-selector-attr': chalk.blue,
30
- 'hljs-selector-pseudo': chalk.blue,
31
- 'hljs-subst': chalk.white,
32
- 'hljs-section': chalk.blue.bold,
33
- 'hljs-bullet': chalk.magenta,
34
- 'hljs-emphasis': chalk.italic,
35
- 'hljs-strong': chalk.bold,
36
- 'hljs-addition': chalk.green,
37
- 'hljs-deletion': chalk.red,
38
- 'hljs-link': chalk.blue.underline,
6
+ "hljs-keyword": chalk.blue,
7
+ "hljs-built_in": chalk.cyan,
8
+ "hljs-type": chalk.cyan,
9
+ "hljs-literal": chalk.magenta,
10
+ "hljs-number": chalk.magenta,
11
+ "hljs-operator": chalk.white,
12
+ "hljs-punctuation": chalk.white,
13
+ "hljs-property": chalk.yellow,
14
+ "hljs-attr": chalk.yellow,
15
+ "hljs-variable": chalk.white,
16
+ "hljs-template-variable": chalk.white,
17
+ "hljs-string": chalk.green,
18
+ "hljs-char": chalk.green,
19
+ "hljs-comment": chalk.gray,
20
+ "hljs-doctag": chalk.gray,
21
+ "hljs-function": chalk.yellow,
22
+ "hljs-title": chalk.yellow,
23
+ "hljs-params": chalk.white,
24
+ "hljs-tag": chalk.blue,
25
+ "hljs-name": chalk.blue,
26
+ "hljs-selector-tag": chalk.blue,
27
+ "hljs-selector-id": chalk.blue,
28
+ "hljs-selector-class": chalk.blue,
29
+ "hljs-selector-attr": chalk.blue,
30
+ "hljs-selector-pseudo": chalk.blue,
31
+ "hljs-subst": chalk.white,
32
+ "hljs-section": chalk.blue.bold,
33
+ "hljs-bullet": chalk.magenta,
34
+ "hljs-emphasis": chalk.italic,
35
+ "hljs-strong": chalk.bold,
36
+ "hljs-addition": chalk.green,
37
+ "hljs-deletion": chalk.red,
38
+ "hljs-link": chalk.blue.underline,
39
39
  };
40
40
 
41
41
  function nodeToAnsi(node: Node): string {
@@ -44,8 +44,8 @@ function nodeToAnsi(node: Node): string {
44
44
  }
45
45
 
46
46
  if (node instanceof HTMLElement) {
47
- const content = node.childNodes.map(nodeToAnsi).join('');
48
- const classes = node.getAttribute('class')?.split(/\s+/) || [];
47
+ const content = node.childNodes.map(nodeToAnsi).join("");
48
+ const classes = node.getAttribute("class")?.split(/\s+/) || [];
49
49
 
50
50
  for (const className of classes) {
51
51
  if (theme[className]) {
@@ -56,20 +56,44 @@ function nodeToAnsi(node: Node): string {
56
56
  return content;
57
57
  }
58
58
 
59
- return '';
59
+ return "";
60
60
  }
61
61
 
62
62
  export function highlightToAnsi(code: string, language?: string): string {
63
63
  if (!code) {
64
- return '';
64
+ return "";
65
65
  }
66
66
  try {
67
67
  const highlighted = language
68
68
  ? hljs.highlight(code, { language }).value
69
- : hljs.highlightAuto(code).value;
69
+ : hljs.highlightAuto(code, [
70
+ "javascript",
71
+ "typescript",
72
+ "bash",
73
+ "json",
74
+ "markdown",
75
+ "python",
76
+ "yaml",
77
+ "html",
78
+ "css",
79
+ "sql",
80
+ "xml",
81
+ "rust",
82
+ "go",
83
+ "java",
84
+ "cpp",
85
+ "c",
86
+ "csharp",
87
+ "php",
88
+ "ruby",
89
+ "swift",
90
+ "kotlin",
91
+ "toml",
92
+ "ini",
93
+ ]).value;
70
94
 
71
95
  const root = parse(highlighted);
72
- return root.childNodes.map(nodeToAnsi).join('');
96
+ return root.childNodes.map(nodeToAnsi).join("");
73
97
  } catch {
74
98
  return code;
75
99
  }