wave-code 0.14.4 → 0.15.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 (116) hide show
  1. package/dist/acp/agent.d.ts.map +1 -1
  2. package/dist/acp/agent.js +10 -0
  3. package/dist/components/BackgroundTaskManager.d.ts.map +1 -1
  4. package/dist/components/BackgroundTaskManager.js +17 -53
  5. package/dist/components/CommandSelector.d.ts.map +1 -1
  6. package/dist/components/CommandSelector.js +38 -28
  7. package/dist/components/ConfirmationSelector.d.ts.map +1 -1
  8. package/dist/components/ConfirmationSelector.js +18 -196
  9. package/dist/components/DiscoverView.d.ts.map +1 -1
  10. package/dist/components/DiscoverView.js +19 -11
  11. package/dist/components/FileSelector.d.ts.map +1 -1
  12. package/dist/components/FileSelector.js +29 -25
  13. package/dist/components/HelpView.d.ts.map +1 -1
  14. package/dist/components/HelpView.js +24 -28
  15. package/dist/components/HistorySearch.d.ts.map +1 -1
  16. package/dist/components/HistorySearch.js +26 -28
  17. package/dist/components/InputBox.d.ts.map +1 -1
  18. package/dist/components/InputBox.js +14 -6
  19. package/dist/components/InstalledView.d.ts.map +1 -1
  20. package/dist/components/InstalledView.js +19 -11
  21. package/dist/components/MarketplaceDetail.d.ts.map +1 -1
  22. package/dist/components/MarketplaceDetail.js +35 -16
  23. package/dist/components/MarketplaceView.d.ts.map +1 -1
  24. package/dist/components/MarketplaceView.js +21 -12
  25. package/dist/components/McpManager.d.ts.map +1 -1
  26. package/dist/components/McpManager.js +27 -46
  27. package/dist/components/ModelSelector.d.ts.map +1 -1
  28. package/dist/components/ModelSelector.js +27 -17
  29. package/dist/components/PluginDetail.d.ts.map +1 -1
  30. package/dist/components/PluginDetail.js +39 -26
  31. package/dist/components/RewindCommand.d.ts.map +1 -1
  32. package/dist/components/RewindCommand.js +27 -21
  33. package/dist/components/SessionSelector.d.ts.map +1 -1
  34. package/dist/components/SessionSelector.js +21 -16
  35. package/dist/components/StatusLine.d.ts.map +1 -1
  36. package/dist/components/StatusLine.js +5 -1
  37. package/dist/components/WorktreeExitPrompt.d.ts.map +1 -1
  38. package/dist/components/WorktreeExitPrompt.js +21 -12
  39. package/dist/contexts/useChat.d.ts +0 -1
  40. package/dist/contexts/useChat.d.ts.map +1 -1
  41. package/dist/contexts/useChat.js +13 -20
  42. package/dist/hooks/useInputManager.d.ts +5 -19
  43. package/dist/hooks/useInputManager.d.ts.map +1 -1
  44. package/dist/hooks/useInputManager.js +222 -79
  45. package/dist/managers/inputHandlers.d.ts +1 -1
  46. package/dist/managers/inputHandlers.d.ts.map +1 -1
  47. package/dist/managers/inputHandlers.js +3 -6
  48. package/dist/managers/inputReducer.d.ts +46 -4
  49. package/dist/managers/inputReducer.d.ts.map +1 -1
  50. package/dist/managers/inputReducer.js +519 -4
  51. package/dist/print-cli.d.ts.map +1 -1
  52. package/dist/print-cli.js +21 -9
  53. package/dist/reducers/backgroundTaskManagerReducer.d.ts +20 -0
  54. package/dist/reducers/backgroundTaskManagerReducer.d.ts.map +1 -1
  55. package/dist/reducers/backgroundTaskManagerReducer.js +83 -0
  56. package/dist/reducers/confirmationReducer.d.ts +11 -0
  57. package/dist/reducers/confirmationReducer.d.ts.map +1 -1
  58. package/dist/reducers/confirmationReducer.js +146 -0
  59. package/dist/reducers/helpSelectorReducer.d.ts +26 -0
  60. package/dist/reducers/helpSelectorReducer.d.ts.map +1 -0
  61. package/dist/reducers/helpSelectorReducer.js +52 -0
  62. package/dist/reducers/mcpManagerReducer.d.ts +22 -0
  63. package/dist/reducers/mcpManagerReducer.d.ts.map +1 -1
  64. package/dist/reducers/mcpManagerReducer.js +57 -0
  65. package/dist/reducers/pluginDetailReducer.d.ts +8 -0
  66. package/dist/reducers/pluginDetailReducer.d.ts.map +1 -1
  67. package/dist/reducers/pluginDetailReducer.js +32 -0
  68. package/dist/reducers/questionReducer.d.ts +21 -0
  69. package/dist/reducers/questionReducer.d.ts.map +1 -1
  70. package/dist/reducers/questionReducer.js +177 -0
  71. package/dist/reducers/rewindSelectorReducer.d.ts +22 -0
  72. package/dist/reducers/rewindSelectorReducer.d.ts.map +1 -0
  73. package/dist/reducers/rewindSelectorReducer.js +37 -0
  74. package/dist/reducers/selectorReducer.d.ts +22 -0
  75. package/dist/reducers/selectorReducer.d.ts.map +1 -0
  76. package/dist/reducers/selectorReducer.js +40 -0
  77. package/dist/utils/inputUtils.d.ts +18 -0
  78. package/dist/utils/inputUtils.d.ts.map +1 -0
  79. package/dist/utils/inputUtils.js +67 -0
  80. package/dist/utils/worktree.d.ts.map +1 -1
  81. package/dist/utils/worktree.js +1 -2
  82. package/package.json +2 -2
  83. package/src/acp/agent.ts +10 -0
  84. package/src/components/BackgroundTaskManager.tsx +19 -58
  85. package/src/components/CommandSelector.tsx +53 -44
  86. package/src/components/ConfirmationSelector.tsx +18 -202
  87. package/src/components/DiscoverView.tsx +21 -11
  88. package/src/components/FileSelector.tsx +40 -35
  89. package/src/components/HelpView.tsx +37 -43
  90. package/src/components/HistorySearch.tsx +29 -36
  91. package/src/components/InputBox.tsx +14 -7
  92. package/src/components/InstalledView.tsx +21 -11
  93. package/src/components/MarketplaceDetail.tsx +48 -24
  94. package/src/components/MarketplaceView.tsx +25 -10
  95. package/src/components/McpManager.tsx +29 -54
  96. package/src/components/ModelSelector.tsx +30 -23
  97. package/src/components/PluginDetail.tsx +50 -27
  98. package/src/components/RewindCommand.tsx +38 -32
  99. package/src/components/SessionSelector.tsx +23 -20
  100. package/src/components/StatusLine.tsx +10 -1
  101. package/src/components/WorktreeExitPrompt.tsx +23 -13
  102. package/src/contexts/useChat.tsx +13 -31
  103. package/src/hooks/useInputManager.ts +273 -141
  104. package/src/managers/inputHandlers.ts +1 -6
  105. package/src/managers/inputReducer.ts +675 -7
  106. package/src/print-cli.ts +20 -11
  107. package/src/reducers/backgroundTaskManagerReducer.ts +110 -1
  108. package/src/reducers/confirmationReducer.ts +173 -1
  109. package/src/reducers/helpSelectorReducer.ts +77 -0
  110. package/src/reducers/mcpManagerReducer.ts +82 -1
  111. package/src/reducers/pluginDetailReducer.ts +42 -1
  112. package/src/reducers/questionReducer.ts +204 -0
  113. package/src/reducers/rewindSelectorReducer.ts +54 -0
  114. package/src/reducers/selectorReducer.ts +57 -0
  115. package/src/utils/inputUtils.ts +94 -0
  116. package/src/utils/worktree.ts +1 -7
@@ -1,3 +1,4 @@
1
+ import { Key } from "ink";
1
2
  import type { PermissionDecision } from "wave-agent-sdk";
2
3
 
3
4
  export interface QuestionSavedState {
@@ -45,6 +46,22 @@ export type QuestionAction =
45
46
  options: Array<{ label: string }>;
46
47
  multiSelect?: boolean;
47
48
  }>;
49
+ }
50
+ | { type: "CLEAR_DECISION" }
51
+ | {
52
+ type: "HANDLE_KEY";
53
+ input: string;
54
+ key: Key;
55
+ currentQuestion: {
56
+ question: string;
57
+ options: Array<{ label: string }>;
58
+ multiSelect?: boolean;
59
+ };
60
+ questions: Array<{
61
+ question: string;
62
+ options: Array<{ label: string }>;
63
+ multiSelect?: boolean;
64
+ }>;
48
65
  };
49
66
 
50
67
  function buildAnswerFromSavedState(
@@ -245,6 +262,193 @@ export function questionReducer(
245
262
  };
246
263
  }
247
264
  }
265
+ case "CLEAR_DECISION":
266
+ return { ...state, decision: null };
267
+ case "HANDLE_KEY": {
268
+ const { input, key, currentQuestion, questions } = action;
269
+ if (!currentQuestion) return state;
270
+
271
+ const options = [...currentQuestion.options, { label: "Other" }];
272
+ const isMultiSelect = currentQuestion.multiSelect;
273
+
274
+ if (key.return) {
275
+ const isOtherFocused = state.selectedOptionIndex === options.length - 1;
276
+ let answer = "";
277
+ if (isMultiSelect) {
278
+ const selectedLabels = Array.from(state.selectedOptionIndices)
279
+ .filter((i) => i < currentQuestion.options.length)
280
+ .map((i) => currentQuestion.options[i].label);
281
+ const isOtherChecked = state.selectedOptionIndices.has(
282
+ options.length - 1,
283
+ );
284
+ if (isOtherChecked && state.otherText.trim()) {
285
+ selectedLabels.push(state.otherText.trim());
286
+ }
287
+ answer = selectedLabels.join(", ");
288
+ } else {
289
+ if (isOtherFocused) {
290
+ answer = state.otherText.trim();
291
+ } else {
292
+ answer = options[state.selectedOptionIndex].label;
293
+ }
294
+ }
295
+ if (!answer) return state;
296
+
297
+ const newAnswers = {
298
+ ...state.userAnswers,
299
+ [currentQuestion.question]: answer,
300
+ };
301
+
302
+ if (state.currentQuestionIndex < questions.length - 1) {
303
+ const nextIndex = state.currentQuestionIndex + 1;
304
+ const savedStates = {
305
+ ...state.savedStates,
306
+ [state.currentQuestionIndex]: {
307
+ selectedOptionIndex: state.selectedOptionIndex,
308
+ selectedOptionIndices: state.selectedOptionIndices,
309
+ otherText: state.otherText,
310
+ otherCursorPosition: state.otherCursorPosition,
311
+ },
312
+ };
313
+ const nextState = savedStates[nextIndex] || {
314
+ selectedOptionIndex: 0,
315
+ selectedOptionIndices: new Set<number>(),
316
+ otherText: "",
317
+ otherCursorPosition: 0,
318
+ };
319
+ return {
320
+ ...state,
321
+ currentQuestionIndex: nextIndex,
322
+ ...nextState,
323
+ userAnswers: newAnswers,
324
+ savedStates,
325
+ };
326
+ } else {
327
+ const finalAnswers = { ...newAnswers };
328
+ for (const [idxStr, s] of Object.entries(state.savedStates)) {
329
+ const idx = parseInt(idxStr);
330
+ const q = questions[idx];
331
+ if (q && !finalAnswers[q.question]) {
332
+ const a = buildAnswerFromSavedState(q, s);
333
+ if (a) finalAnswers[q.question] = a;
334
+ }
335
+ }
336
+ const allAnswered = questions.every((q) => finalAnswers[q.question]);
337
+ if (!allAnswered) return state;
338
+ return {
339
+ ...state,
340
+ userAnswers: finalAnswers,
341
+ decision: {
342
+ behavior: "allow",
343
+ message: JSON.stringify(finalAnswers),
344
+ },
345
+ };
346
+ }
347
+ }
348
+
349
+ if (input === " ") {
350
+ const isOtherFocused = state.selectedOptionIndex === options.length - 1;
351
+ if (
352
+ isMultiSelect &&
353
+ (!isOtherFocused ||
354
+ !state.selectedOptionIndices.has(state.selectedOptionIndex))
355
+ ) {
356
+ const nextIndices = new Set(state.selectedOptionIndices);
357
+ if (nextIndices.has(state.selectedOptionIndex))
358
+ nextIndices.delete(state.selectedOptionIndex);
359
+ else nextIndices.add(state.selectedOptionIndex);
360
+ return { ...state, selectedOptionIndices: nextIndices };
361
+ }
362
+ }
363
+
364
+ if (key.upArrow) {
365
+ return {
366
+ ...state,
367
+ selectedOptionIndex: Math.max(0, state.selectedOptionIndex - 1),
368
+ };
369
+ }
370
+ if (key.downArrow) {
371
+ return {
372
+ ...state,
373
+ selectedOptionIndex: Math.min(
374
+ options.length - 1,
375
+ state.selectedOptionIndex + 1,
376
+ ),
377
+ };
378
+ }
379
+
380
+ if (key.tab) {
381
+ const direction = key.shift ? -1 : 1;
382
+ let nextIndex = state.currentQuestionIndex + direction;
383
+ if (nextIndex < 0) nextIndex = questions.length - 1;
384
+ if (nextIndex >= questions.length) nextIndex = 0;
385
+ if (nextIndex === state.currentQuestionIndex) return state;
386
+ const savedStates = {
387
+ ...state.savedStates,
388
+ [state.currentQuestionIndex]: {
389
+ selectedOptionIndex: state.selectedOptionIndex,
390
+ selectedOptionIndices: state.selectedOptionIndices,
391
+ otherText: state.otherText,
392
+ otherCursorPosition: state.otherCursorPosition,
393
+ },
394
+ };
395
+ const nextState = savedStates[nextIndex] || {
396
+ selectedOptionIndex: 0,
397
+ selectedOptionIndices: new Set<number>(),
398
+ otherText: "",
399
+ otherCursorPosition: 0,
400
+ };
401
+ return {
402
+ ...state,
403
+ currentQuestionIndex: nextIndex,
404
+ ...nextState,
405
+ savedStates,
406
+ };
407
+ }
408
+
409
+ if (state.selectedOptionIndex === options.length - 1) {
410
+ if (key.leftArrow) {
411
+ return {
412
+ ...state,
413
+ otherCursorPosition: Math.max(0, state.otherCursorPosition - 1),
414
+ };
415
+ }
416
+ if (key.rightArrow) {
417
+ return {
418
+ ...state,
419
+ otherCursorPosition: Math.min(
420
+ state.otherText.length,
421
+ state.otherCursorPosition + 1,
422
+ ),
423
+ };
424
+ }
425
+ if (key.backspace || key.delete) {
426
+ if (state.otherCursorPosition > 0) {
427
+ return {
428
+ ...state,
429
+ otherText:
430
+ state.otherText.slice(0, state.otherCursorPosition - 1) +
431
+ state.otherText.slice(state.otherCursorPosition),
432
+ otherCursorPosition: state.otherCursorPosition - 1,
433
+ };
434
+ }
435
+ return state;
436
+ }
437
+ if (input && !key.ctrl && !key.meta) {
438
+ const newText =
439
+ state.otherText.slice(0, state.otherCursorPosition) +
440
+ input +
441
+ state.otherText.slice(state.otherCursorPosition);
442
+ return {
443
+ ...state,
444
+ otherText: newText,
445
+ otherCursorPosition: state.otherCursorPosition + input.length,
446
+ };
447
+ }
448
+ }
449
+
450
+ return state;
451
+ }
248
452
  default:
249
453
  return state;
250
454
  }
@@ -0,0 +1,54 @@
1
+ import { Key } from "ink";
2
+
3
+ export interface SelectorState {
4
+ selectedIndex: number;
5
+ pendingDecision: "select" | "cancel" | null;
6
+ }
7
+
8
+ export type SelectorAction =
9
+ | { type: "MOVE_UP" }
10
+ | { type: "MOVE_DOWN"; maxIndex: number }
11
+ | { type: "RESET_INDEX"; index: number }
12
+ | { type: "HANDLE_KEY"; key: Key; maxIndex: number }
13
+ | { type: "CLEAR_DECISION" };
14
+
15
+ export function rewindSelectorReducer(
16
+ state: SelectorState,
17
+ action: SelectorAction,
18
+ ): SelectorState {
19
+ switch (action.type) {
20
+ case "MOVE_UP":
21
+ return { ...state, selectedIndex: Math.max(0, state.selectedIndex - 1) };
22
+ case "MOVE_DOWN":
23
+ return {
24
+ ...state,
25
+ selectedIndex: Math.min(action.maxIndex, state.selectedIndex + 1),
26
+ };
27
+ case "RESET_INDEX":
28
+ return { ...state, selectedIndex: action.index };
29
+ case "HANDLE_KEY":
30
+ if (action.key.upArrow) {
31
+ return {
32
+ ...state,
33
+ selectedIndex: Math.max(0, state.selectedIndex - 1),
34
+ };
35
+ }
36
+ if (action.key.downArrow) {
37
+ return {
38
+ ...state,
39
+ selectedIndex: Math.min(action.maxIndex, state.selectedIndex + 1),
40
+ };
41
+ }
42
+ if (action.key.return) {
43
+ return { ...state, pendingDecision: "select" };
44
+ }
45
+ if (action.key.escape) {
46
+ return { ...state, pendingDecision: "cancel" };
47
+ }
48
+ return state;
49
+ case "CLEAR_DECISION":
50
+ return { ...state, pendingDecision: null };
51
+ default:
52
+ return state;
53
+ }
54
+ }
@@ -0,0 +1,57 @@
1
+ import { Key } from "ink";
2
+
3
+ export interface SelectorState {
4
+ selectedIndex: number;
5
+ pendingDecision: "select" | "insert" | "cancel" | null;
6
+ }
7
+
8
+ export type SelectorAction =
9
+ | { type: "MOVE_UP" }
10
+ | { type: "MOVE_DOWN"; maxIndex: number }
11
+ | { type: "RESET_INDEX" }
12
+ | { type: "HANDLE_KEY"; key: Key; maxIndex: number; hasInsert: boolean }
13
+ | { type: "CLEAR_DECISION" };
14
+
15
+ export function selectorReducer(
16
+ state: SelectorState,
17
+ action: SelectorAction,
18
+ ): SelectorState {
19
+ switch (action.type) {
20
+ case "MOVE_UP":
21
+ return { ...state, selectedIndex: Math.max(0, state.selectedIndex - 1) };
22
+ case "MOVE_DOWN":
23
+ return {
24
+ ...state,
25
+ selectedIndex: Math.min(action.maxIndex, state.selectedIndex + 1),
26
+ };
27
+ case "RESET_INDEX":
28
+ return { ...state, selectedIndex: 0 };
29
+ case "HANDLE_KEY":
30
+ if (action.key.upArrow) {
31
+ return {
32
+ ...state,
33
+ selectedIndex: Math.max(0, state.selectedIndex - 1),
34
+ };
35
+ }
36
+ if (action.key.downArrow) {
37
+ return {
38
+ ...state,
39
+ selectedIndex: Math.min(action.maxIndex, state.selectedIndex + 1),
40
+ };
41
+ }
42
+ if (action.key.return) {
43
+ return { ...state, pendingDecision: "select" };
44
+ }
45
+ if (action.key.tab && action.hasInsert) {
46
+ return { ...state, pendingDecision: "insert" };
47
+ }
48
+ if (action.key.escape) {
49
+ return { ...state, pendingDecision: "cancel" };
50
+ }
51
+ return state;
52
+ case "CLEAR_DECISION":
53
+ return { ...state, pendingDecision: null };
54
+ default:
55
+ return state;
56
+ }
57
+ }
@@ -0,0 +1,94 @@
1
+ export const expandLongTextPlaceholders = (
2
+ text: string,
3
+ longTextMap: Record<string, string>,
4
+ ): string => {
5
+ let expandedText = text;
6
+ const longTextRegex = /\[LongText#(\d+)\]/g;
7
+ const matches = [...text.matchAll(longTextRegex)];
8
+
9
+ for (const match of matches) {
10
+ const placeholder = match[0];
11
+ const originalText = longTextMap[placeholder];
12
+ if (originalText) {
13
+ expandedText = expandedText.replace(placeholder, originalText);
14
+ }
15
+ }
16
+
17
+ return expandedText;
18
+ };
19
+
20
+ export const getAtSelectorPosition = (
21
+ text: string,
22
+ cursorPosition: number,
23
+ ): number => {
24
+ let i = cursorPosition - 1;
25
+ while (i >= 0 && !/\s/.test(text[i])) {
26
+ if (text[i] === "@") {
27
+ if (i === 0 || /\s/.test(text[i - 1])) {
28
+ return i;
29
+ }
30
+ break;
31
+ }
32
+ i--;
33
+ }
34
+ return -1;
35
+ };
36
+
37
+ export const getSlashSelectorPosition = (
38
+ text: string,
39
+ cursorPosition: number,
40
+ ): number => {
41
+ let i = cursorPosition - 1;
42
+ while (i >= 0 && !/\s/.test(text[i])) {
43
+ if (text[i] === "/") {
44
+ if (i === 0 || /\s/.test(text[i - 1])) {
45
+ return i;
46
+ }
47
+ break;
48
+ }
49
+ i--;
50
+ }
51
+ return -1;
52
+ };
53
+
54
+ export const getWordEnd = (text: string, startPos: number): number => {
55
+ let i = startPos;
56
+ while (i < text.length && !/\s/.test(text[i])) {
57
+ i++;
58
+ }
59
+ return i;
60
+ };
61
+
62
+ export const SELECTOR_TRIGGERS = [
63
+ {
64
+ char: "@",
65
+ type: "ACTIVATE_FILE_SELECTOR" as const,
66
+ shouldActivate: (char: string, pos: number, text: string) =>
67
+ char === "@" && (pos === 1 || /\s/.test(text[pos - 2])),
68
+ },
69
+ {
70
+ char: "/",
71
+ type: "ACTIVATE_COMMAND_SELECTOR" as const,
72
+ shouldActivate: (
73
+ char: string,
74
+ pos: number,
75
+ text: string,
76
+ showFileSelector: boolean,
77
+ ) =>
78
+ char === "/" &&
79
+ !showFileSelector &&
80
+ (pos === 1 || /\s/.test(text[pos - 2])),
81
+ },
82
+ ] as const;
83
+
84
+ export const getProjectedState = (
85
+ inputText: string,
86
+ cursorPosition: number,
87
+ char: string,
88
+ ) => {
89
+ const beforeCursor = inputText.substring(0, cursorPosition);
90
+ const afterCursor = inputText.substring(cursorPosition);
91
+ const newInputText = beforeCursor + char + afterCursor;
92
+ const newCursorPosition = cursorPosition + char.length;
93
+ return { newInputText, newCursorPosition };
94
+ };
@@ -21,13 +21,7 @@ export interface WorktreeSession {
21
21
  */
22
22
  export function createWorktree(name: string, cwd: string): WorktreeSession {
23
23
  const repoRoot = getGitMainRepoRoot(cwd);
24
- const projectName = path.basename(repoRoot);
25
- const worktreePath = path.join(
26
- repoRoot,
27
- "..",
28
- `${projectName}.worktrees`,
29
- name,
30
- );
24
+ const worktreePath = path.join(repoRoot, ".wave", "worktrees", name);
31
25
  const branchName = `worktree-${name}`;
32
26
  const baseBranch = getDefaultRemoteBranch(cwd);
33
27