wave-code 1.0.10 → 1.1.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.
- package/bin/wave-code.js +8 -2
- package/dist/bundle/wave.mjs +3231 -0
- package/package.json +24 -25
- package/src/cli.tsx +6 -1
- package/src/commands/update.ts +2 -7
- package/src/components/App.tsx +3 -7
- package/src/components/BtwDisplay.tsx +10 -1
- package/src/components/ChatInterface.tsx +21 -52
- package/src/components/ConfirmationDetails.tsx +144 -37
- package/src/components/DiffDisplay.tsx +335 -301
- package/src/components/InputBox.tsx +17 -2
- package/src/components/LoadingIndicator.tsx +16 -2
- package/src/components/MessageBlockItem.tsx +2 -4
- package/src/components/MessageList.tsx +75 -14
- package/src/components/ReasoningDisplay.tsx +2 -4
- package/src/components/SessionSelector.tsx +82 -11
- package/src/components/SkillsManager.tsx +312 -0
- package/src/components/StatusCommand.tsx +2 -9
- package/src/components/ToolDisplay.tsx +2 -4
- package/src/constants/commands.ts +6 -0
- package/src/contexts/useChat.tsx +245 -301
- package/src/daemon/commands.ts +57 -27
- package/src/daemon/jsonRpcClient.ts +1 -1
- package/src/daemon/socketClient.ts +2 -2
- package/src/hooks/useInputManager.ts +7 -1
- package/src/index.ts +57 -10
- package/src/managers/inputHandlers.ts +2 -0
- package/src/managers/inputReducer.ts +195 -104
- package/src/print-cli.ts +25 -17
- package/src/reducers/selectorReducer.ts +10 -0
- package/src/reducers/skillsManagerReducer.ts +91 -0
- package/src/session-selector-cli.tsx +269 -28
- package/src/stdio/agentBridge.ts +30 -21
- package/src/stdio/daemonServer.ts +3 -1
- package/src/stdio/protocol.ts +2 -0
- package/src/utils/clipboard.ts +21 -0
- package/src/utils/highlightUtils.ts +46 -1
- package/src/utils/readPackageJson.ts +29 -0
- package/src/utils/streamingText.ts +11 -0
- package/src/utils/worktree.ts +191 -43
- package/dist/cli.d.ts +0 -6
- package/dist/cli.js +0 -53
- package/dist/commands/plugin/disable.d.ts +0 -5
- package/dist/commands/plugin/disable.js +0 -15
- package/dist/commands/plugin/enable.d.ts +0 -5
- package/dist/commands/plugin/enable.js +0 -15
- package/dist/commands/plugin/install.d.ts +0 -5
- package/dist/commands/plugin/install.js +0 -21
- package/dist/commands/plugin/list.d.ts +0 -1
- package/dist/commands/plugin/list.js +0 -31
- package/dist/commands/plugin/marketplace.d.ts +0 -10
- package/dist/commands/plugin/marketplace.js +0 -90
- package/dist/commands/plugin/uninstall.d.ts +0 -3
- package/dist/commands/plugin/uninstall.js +0 -16
- package/dist/commands/plugin/update.d.ts +0 -3
- package/dist/commands/plugin/update.js +0 -15
- package/dist/commands/update.d.ts +0 -1
- package/dist/commands/update.js +0 -97
- package/dist/components/AgentsManager.d.ts +0 -7
- package/dist/components/AgentsManager.js +0 -109
- package/dist/components/App.d.ts +0 -9
- package/dist/components/App.js +0 -76
- package/dist/components/BackgroundTaskManager.d.ts +0 -5
- package/dist/components/BackgroundTaskManager.js +0 -99
- package/dist/components/BangDisplay.d.ts +0 -8
- package/dist/components/BangDisplay.js +0 -17
- package/dist/components/BtwDisplay.d.ts +0 -7
- package/dist/components/BtwDisplay.js +0 -19
- package/dist/components/ChatInterface.d.ts +0 -2
- package/dist/components/ChatInterface.js +0 -61
- package/dist/components/CommandSelector.d.ts +0 -10
- package/dist/components/CommandSelector.js +0 -66
- package/dist/components/CompactDisplay.d.ts +0 -8
- package/dist/components/CompactDisplay.js +0 -13
- package/dist/components/ConfirmationDetails.d.ts +0 -9
- package/dist/components/ConfirmationDetails.js +0 -45
- package/dist/components/ConfirmationSelector.d.ts +0 -13
- package/dist/components/ConfirmationSelector.js +0 -127
- package/dist/components/DiffDisplay.d.ts +0 -8
- package/dist/components/DiffDisplay.js +0 -201
- package/dist/components/DiscoverView.d.ts +0 -2
- package/dist/components/DiscoverView.js +0 -37
- package/dist/components/FileSelector.d.ts +0 -11
- package/dist/components/FileSelector.js +0 -50
- package/dist/components/HelpView.d.ts +0 -7
- package/dist/components/HelpView.js +0 -74
- package/dist/components/HistorySearch.d.ts +0 -8
- package/dist/components/HistorySearch.js +0 -66
- package/dist/components/InputBox.d.ts +0 -24
- package/dist/components/InputBox.js +0 -149
- package/dist/components/InstalledView.d.ts +0 -2
- package/dist/components/InstalledView.js +0 -45
- package/dist/components/LoadingIndicator.d.ts +0 -10
- package/dist/components/LoadingIndicator.js +0 -6
- package/dist/components/LoginCommand.d.ts +0 -5
- package/dist/components/LoginCommand.js +0 -153
- package/dist/components/Markdown.d.ts +0 -5
- package/dist/components/Markdown.js +0 -127
- package/dist/components/MarketplaceAddForm.d.ts +0 -2
- package/dist/components/MarketplaceAddForm.js +0 -55
- package/dist/components/MarketplaceDetail.d.ts +0 -2
- package/dist/components/MarketplaceDetail.js +0 -74
- package/dist/components/MarketplaceList.d.ts +0 -8
- package/dist/components/MarketplaceList.js +0 -17
- package/dist/components/MarketplaceView.d.ts +0 -2
- package/dist/components/MarketplaceView.js +0 -41
- package/dist/components/McpManager.d.ts +0 -9
- package/dist/components/McpManager.js +0 -88
- package/dist/components/MessageBlockItem.d.ts +0 -8
- package/dist/components/MessageBlockItem.js +0 -15
- package/dist/components/MessageList.d.ts +0 -10
- package/dist/components/MessageList.js +0 -80
- package/dist/components/ModelSelector.d.ts +0 -8
- package/dist/components/ModelSelector.js +0 -45
- package/dist/components/Notifications.d.ts +0 -7
- package/dist/components/Notifications.js +0 -9
- package/dist/components/PlanDisplay.d.ts +0 -7
- package/dist/components/PlanDisplay.js +0 -6
- package/dist/components/PluginDetail.d.ts +0 -2
- package/dist/components/PluginDetail.js +0 -87
- package/dist/components/PluginList.d.ts +0 -13
- package/dist/components/PluginList.js +0 -15
- package/dist/components/PluginManagerShell.d.ts +0 -6
- package/dist/components/PluginManagerShell.js +0 -92
- package/dist/components/PluginManagerTypes.d.ts +0 -35
- package/dist/components/PluginManagerTypes.js +0 -1
- package/dist/components/QueuedMessageList.d.ts +0 -2
- package/dist/components/QueuedMessageList.js +0 -18
- package/dist/components/ReasoningDisplay.d.ts +0 -8
- package/dist/components/ReasoningDisplay.js +0 -13
- package/dist/components/RewindCommand.d.ts +0 -12
- package/dist/components/RewindCommand.js +0 -78
- package/dist/components/SessionSelector.d.ts +0 -12
- package/dist/components/SessionSelector.js +0 -47
- package/dist/components/StatusCommand.d.ts +0 -5
- package/dist/components/StatusCommand.js +0 -28
- package/dist/components/StatusLine.d.ts +0 -6
- package/dist/components/StatusLine.js +0 -9
- package/dist/components/TaskList.d.ts +0 -5
- package/dist/components/TaskList.js +0 -197
- package/dist/components/TaskNotificationMessage.d.ts +0 -5
- package/dist/components/TaskNotificationMessage.js +0 -12
- package/dist/components/ToolDisplay.d.ts +0 -9
- package/dist/components/ToolDisplay.js +0 -55
- package/dist/components/WorkflowManager.d.ts +0 -5
- package/dist/components/WorkflowManager.js +0 -102
- package/dist/components/WorktreeExitPrompt.d.ts +0 -12
- package/dist/components/WorktreeExitPrompt.js +0 -35
- package/dist/constants/commands.d.ts +0 -2
- package/dist/constants/commands.js +0 -92
- package/dist/contexts/PluginManagerContext.d.ts +0 -3
- package/dist/contexts/PluginManagerContext.js +0 -9
- package/dist/contexts/useAppConfig.d.ts +0 -10
- package/dist/contexts/useAppConfig.js +0 -13
- package/dist/contexts/useChat.d.ts +0 -101
- package/dist/contexts/useChat.js +0 -993
- package/dist/contracts/status.d.ts +0 -7
- package/dist/contracts/status.js +0 -1
- package/dist/daemon/commands.d.ts +0 -49
- package/dist/daemon/commands.js +0 -341
- package/dist/daemon/jsonRpcClient.d.ts +0 -38
- package/dist/daemon/jsonRpcClient.js +0 -129
- package/dist/daemon/socketClient.d.ts +0 -13
- package/dist/daemon/socketClient.js +0 -26
- package/dist/daemon-cli.d.ts +0 -10
- package/dist/daemon-cli.js +0 -15
- package/dist/hooks/useInputManager.d.ts +0 -75
- package/dist/hooks/useInputManager.js +0 -583
- package/dist/hooks/usePluginManager.d.ts +0 -4
- package/dist/hooks/usePluginManager.js +0 -265
- package/dist/hooks/useTasks.d.ts +0 -1
- package/dist/hooks/useTasks.js +0 -5
- package/dist/index.d.ts +0 -4
- package/dist/index.js +0 -447
- package/dist/managers/inputHandlers.d.ts +0 -30
- package/dist/managers/inputHandlers.js +0 -625
- package/dist/managers/inputReducer.d.ts +0 -271
- package/dist/managers/inputReducer.js +0 -1121
- package/dist/plugin-manager-cli.d.ts +0 -5
- package/dist/plugin-manager-cli.js +0 -12
- package/dist/print-cli.d.ts +0 -10
- package/dist/print-cli.js +0 -209
- package/dist/reducers/agentsManagerReducer.d.ts +0 -26
- package/dist/reducers/agentsManagerReducer.js +0 -54
- package/dist/reducers/backgroundTaskManagerReducer.d.ts +0 -62
- package/dist/reducers/backgroundTaskManagerReducer.js +0 -106
- package/dist/reducers/confirmationReducer.d.ts +0 -37
- package/dist/reducers/confirmationReducer.js +0 -193
- package/dist/reducers/helpSelectorReducer.d.ts +0 -25
- package/dist/reducers/helpSelectorReducer.js +0 -52
- package/dist/reducers/marketplaceAddFormReducer.d.ts +0 -24
- package/dist/reducers/marketplaceAddFormReducer.js +0 -58
- package/dist/reducers/mcpManagerReducer.d.ts +0 -37
- package/dist/reducers/mcpManagerReducer.js +0 -74
- package/dist/reducers/pluginDetailReducer.d.ts +0 -32
- package/dist/reducers/pluginDetailReducer.js +0 -70
- package/dist/reducers/questionReducer.d.ts +0 -82
- package/dist/reducers/questionReducer.js +0 -375
- package/dist/reducers/rewindSelectorReducer.d.ts +0 -21
- package/dist/reducers/rewindSelectorReducer.js +0 -37
- package/dist/reducers/selectorReducer.d.ts +0 -23
- package/dist/reducers/selectorReducer.js +0 -42
- package/dist/reducers/workflowManagerReducer.d.ts +0 -40
- package/dist/reducers/workflowManagerReducer.js +0 -100
- package/dist/session-selector-cli.d.ts +0 -3
- package/dist/session-selector-cli.js +0 -25
- package/dist/stdio/agentBridge.d.ts +0 -139
- package/dist/stdio/agentBridge.js +0 -999
- package/dist/stdio/daemonServer.d.ts +0 -67
- package/dist/stdio/daemonServer.js +0 -191
- package/dist/stdio/index.d.ts +0 -5
- package/dist/stdio/index.js +0 -5
- package/dist/stdio/jsonRpcConnection.d.ts +0 -39
- package/dist/stdio/jsonRpcConnection.js +0 -135
- package/dist/stdio/protocol.d.ts +0 -41
- package/dist/stdio/protocol.js +0 -26
- package/dist/stdio/stdioServer.d.ts +0 -36
- package/dist/stdio/stdioServer.js +0 -51
- package/dist/stdio-cli.d.ts +0 -8
- package/dist/stdio-cli.js +0 -15
- package/dist/types.d.ts +0 -20
- package/dist/types.js +0 -1
- package/dist/utils/bracketedPaste.d.ts +0 -39
- package/dist/utils/bracketedPaste.js +0 -122
- package/dist/utils/clipboard.d.ts +0 -21
- package/dist/utils/clipboard.js +0 -347
- package/dist/utils/constants.d.ts +0 -16
- package/dist/utils/constants.js +0 -18
- package/dist/utils/highlightUtils.d.ts +0 -1
- package/dist/utils/highlightUtils.js +0 -93
- package/dist/utils/inputUtils.d.ts +0 -17
- package/dist/utils/inputUtils.js +0 -67
- package/dist/utils/logger.d.ts +0 -71
- package/dist/utils/logger.js +0 -259
- package/dist/utils/markdownTable.d.ts +0 -34
- package/dist/utils/markdownTable.js +0 -302
- package/dist/utils/rewindCheckpoints.d.ts +0 -8
- package/dist/utils/rewindCheckpoints.js +0 -17
- package/dist/utils/throttle.d.ts +0 -14
- package/dist/utils/throttle.js +0 -55
- package/dist/utils/toolParameterTransforms.d.ts +0 -18
- package/dist/utils/toolParameterTransforms.js +0 -73
- package/dist/utils/usageSummary.d.ts +0 -28
- package/dist/utils/usageSummary.js +0 -121
- package/dist/utils/version.d.ts +0 -1
- package/dist/utils/version.js +0 -4
- package/dist/utils/worktree.d.ts +0 -35
- package/dist/utils/worktree.js +0 -219
|
@@ -138,6 +138,7 @@ export interface InputState {
|
|
|
138
138
|
showPluginManager: boolean;
|
|
139
139
|
showModelSelector: boolean;
|
|
140
140
|
showWorkflowManager: boolean;
|
|
141
|
+
showSkillsManager: boolean;
|
|
141
142
|
permissionMode: PermissionMode;
|
|
142
143
|
selectorJustUsed: boolean;
|
|
143
144
|
history: PromptEntry[];
|
|
@@ -176,6 +177,7 @@ export const initialState: InputState = {
|
|
|
176
177
|
showPluginManager: false,
|
|
177
178
|
showModelSelector: false,
|
|
178
179
|
showWorkflowManager: false,
|
|
180
|
+
showSkillsManager: false,
|
|
179
181
|
permissionMode: "default",
|
|
180
182
|
selectorJustUsed: false,
|
|
181
183
|
history: [],
|
|
@@ -255,6 +257,53 @@ function insertTextWithPlaceholder(
|
|
|
255
257
|
return newState;
|
|
256
258
|
}
|
|
257
259
|
|
|
260
|
+
/**
|
|
261
|
+
* Delete a placeholder token ([LongText#N] / [Image #N]) as a whole block
|
|
262
|
+
* when the cursor sits at the token's end, aligned with Claude Code's
|
|
263
|
+
* Cursor.deleteTokenBefore (Cursor.ts:937-969): the token must be preceded
|
|
264
|
+
* by start-of-input or whitespace and followed by whitespace or EOL, so a
|
|
265
|
+
* mid-token cursor or a token glued to following text falls back to
|
|
266
|
+
* one-character deletion. Also drops the deleted token's longTextMap entry.
|
|
267
|
+
* Returns null when no token is at the cursor position.
|
|
268
|
+
*/
|
|
269
|
+
function deleteTokenBefore(
|
|
270
|
+
inputText: string,
|
|
271
|
+
cursorPosition: number,
|
|
272
|
+
longTextMap: Record<string, string>,
|
|
273
|
+
): {
|
|
274
|
+
inputText: string;
|
|
275
|
+
cursorPosition: number;
|
|
276
|
+
longTextMap: Record<string, string>;
|
|
277
|
+
} | null {
|
|
278
|
+
if (cursorPosition <= 0) {
|
|
279
|
+
return null;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Word-boundary guard (aligned with CC): only trigger when the char after
|
|
283
|
+
// the cursor is whitespace or the end of the string.
|
|
284
|
+
const charAfter = inputText[cursorPosition];
|
|
285
|
+
if (charAfter !== undefined && !/\s/.test(charAfter)) {
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const textBefore = inputText.slice(0, cursorPosition);
|
|
290
|
+
const tokenMatch = textBefore.match(/(^|\s)\[(LongText#\d+|Image #\d+)\]$/);
|
|
291
|
+
if (!tokenMatch) {
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const tokenStart = tokenMatch.index! + tokenMatch[1]!.length;
|
|
296
|
+
const token = tokenMatch[0].slice(tokenMatch[1]!.length);
|
|
297
|
+
const newLongTextMap = { ...longTextMap };
|
|
298
|
+
delete newLongTextMap[token];
|
|
299
|
+
|
|
300
|
+
return {
|
|
301
|
+
inputText: inputText.slice(0, tokenStart) + inputText.slice(cursorPosition),
|
|
302
|
+
cursorPosition: tokenStart,
|
|
303
|
+
longTextMap: newLongTextMap,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
258
307
|
/**
|
|
259
308
|
* Submit the current input text: extract [Image #N] references, route /btw
|
|
260
309
|
* and CLI-internal slash commands, otherwise send as a message. Returns null
|
|
@@ -410,6 +459,7 @@ export type InputAction =
|
|
|
410
459
|
| { type: "SET_SHOW_PLUGIN_MANAGER"; payload: boolean }
|
|
411
460
|
| { type: "SET_SHOW_MODEL_SELECTOR"; payload: boolean }
|
|
412
461
|
| { type: "SET_SHOW_WORKFLOW_MANAGER"; payload: boolean }
|
|
462
|
+
| { type: "SET_SHOW_SKILLS_MANAGER"; payload: boolean }
|
|
413
463
|
| { type: "SET_PERMISSION_MODE"; payload: PermissionMode }
|
|
414
464
|
| { type: "SET_SELECTOR_JUST_USED"; payload: boolean }
|
|
415
465
|
| { type: "INSERT_TEXT_WITH_PLACEHOLDER"; payload: string }
|
|
@@ -640,6 +690,12 @@ export function inputReducer(
|
|
|
640
690
|
showWorkflowManager: action.payload,
|
|
641
691
|
selectorJustUsed: !action.payload ? true : state.selectorJustUsed,
|
|
642
692
|
};
|
|
693
|
+
case "SET_SHOW_SKILLS_MANAGER":
|
|
694
|
+
return {
|
|
695
|
+
...state,
|
|
696
|
+
showSkillsManager: action.payload,
|
|
697
|
+
selectorJustUsed: !action.payload ? true : state.selectorJustUsed,
|
|
698
|
+
};
|
|
643
699
|
case "SET_PERMISSION_MODE":
|
|
644
700
|
return { ...state, permissionMode: action.payload };
|
|
645
701
|
case "SET_SELECTOR_JUST_USED":
|
|
@@ -823,7 +879,9 @@ export function inputReducer(
|
|
|
823
879
|
// chunk (e.g. "\x7f\x7f") that ink cannot parse into a key event, so it
|
|
824
880
|
// arrives as raw input and would otherwise be treated as a paste and
|
|
825
881
|
// inserted literally. Treat each DEL as a synchronous backspace instead
|
|
826
|
-
// (aligned with Claude Code Issue #1853).
|
|
882
|
+
// (aligned with Claude Code Issue #1853). Each DEL deletes a placeholder
|
|
883
|
+
// token as a whole block first, falling back to one character (aligned
|
|
884
|
+
// with Claude Code's useTextInput.ts:442-465).
|
|
827
885
|
if (!key.backspace && !key.delete && input.includes("\x7f")) {
|
|
828
886
|
const delCount = (input.match(/\x7f/g) || []).length;
|
|
829
887
|
|
|
@@ -834,74 +892,92 @@ export function inputReducer(
|
|
|
834
892
|
};
|
|
835
893
|
}
|
|
836
894
|
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
const newInputText =
|
|
843
|
-
state.inputText.substring(0, newCursorPosition) +
|
|
844
|
-
state.inputText.substring(state.cursorPosition);
|
|
845
|
-
|
|
846
|
-
const newState = {
|
|
847
|
-
...state,
|
|
848
|
-
inputText: newInputText,
|
|
849
|
-
cursorPosition: newCursorPosition,
|
|
850
|
-
historyIndex: -1,
|
|
851
|
-
};
|
|
852
|
-
|
|
853
|
-
// Deactivate selectors if their trigger character was deleted
|
|
854
|
-
if (
|
|
855
|
-
newState.showFileSelector &&
|
|
856
|
-
newCursorPosition <= newState.atPosition
|
|
857
|
-
) {
|
|
858
|
-
newState.showFileSelector = false;
|
|
859
|
-
newState.atPosition = -1;
|
|
860
|
-
newState.fileSearchQuery = "";
|
|
861
|
-
newState.isFileSearching = false;
|
|
862
|
-
}
|
|
863
|
-
if (
|
|
864
|
-
newState.showCommandSelector &&
|
|
865
|
-
newCursorPosition <= newState.slashPosition
|
|
866
|
-
) {
|
|
867
|
-
newState.showCommandSelector = false;
|
|
868
|
-
newState.slashPosition = -1;
|
|
869
|
-
newState.commandSearchQuery = "";
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
// Reactivate selectors if cursor is within a trigger word
|
|
873
|
-
const atPos = getAtSelectorPosition(newInputText, newCursorPosition);
|
|
874
|
-
if (atPos !== -1 && !state.showFileSelector) {
|
|
875
|
-
newState.showFileSelector = true;
|
|
876
|
-
newState.atPosition = atPos;
|
|
877
|
-
newState.isFileSearching = true;
|
|
878
|
-
}
|
|
879
|
-
const slashPos = getSlashSelectorPosition(
|
|
895
|
+
let newInputText = state.inputText;
|
|
896
|
+
let newCursorPosition = state.cursorPosition;
|
|
897
|
+
let newLongTextMap = state.longTextMap;
|
|
898
|
+
for (let i = 0; i < delCount; i++) {
|
|
899
|
+
const tokenDeletion = deleteTokenBefore(
|
|
880
900
|
newInputText,
|
|
881
901
|
newCursorPosition,
|
|
902
|
+
newLongTextMap,
|
|
882
903
|
);
|
|
883
|
-
if (
|
|
884
|
-
|
|
885
|
-
|
|
904
|
+
if (tokenDeletion) {
|
|
905
|
+
newInputText = tokenDeletion.inputText;
|
|
906
|
+
newCursorPosition = tokenDeletion.cursorPosition;
|
|
907
|
+
newLongTextMap = tokenDeletion.longTextMap;
|
|
908
|
+
} else if (newCursorPosition > 0) {
|
|
909
|
+
newInputText =
|
|
910
|
+
newInputText.substring(0, newCursorPosition - 1) +
|
|
911
|
+
newInputText.substring(newCursorPosition);
|
|
912
|
+
newCursorPosition -= 1;
|
|
886
913
|
}
|
|
914
|
+
}
|
|
887
915
|
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
}
|
|
895
|
-
if (newState.showCommandSelector && newState.slashPosition >= 0) {
|
|
896
|
-
newState.commandSearchQuery = newInputText.substring(
|
|
897
|
-
newState.slashPosition + 1,
|
|
898
|
-
newCursorPosition,
|
|
899
|
-
);
|
|
900
|
-
}
|
|
916
|
+
if (
|
|
917
|
+
newInputText === state.inputText &&
|
|
918
|
+
newCursorPosition === state.cursorPosition
|
|
919
|
+
) {
|
|
920
|
+
return state;
|
|
921
|
+
}
|
|
901
922
|
|
|
902
|
-
|
|
923
|
+
const newState = {
|
|
924
|
+
...state,
|
|
925
|
+
inputText: newInputText,
|
|
926
|
+
cursorPosition: newCursorPosition,
|
|
927
|
+
longTextMap: newLongTextMap,
|
|
928
|
+
historyIndex: -1,
|
|
929
|
+
};
|
|
930
|
+
|
|
931
|
+
// Deactivate selectors if their trigger character was deleted
|
|
932
|
+
if (
|
|
933
|
+
newState.showFileSelector &&
|
|
934
|
+
newCursorPosition <= newState.atPosition
|
|
935
|
+
) {
|
|
936
|
+
newState.showFileSelector = false;
|
|
937
|
+
newState.atPosition = -1;
|
|
938
|
+
newState.fileSearchQuery = "";
|
|
939
|
+
newState.isFileSearching = false;
|
|
903
940
|
}
|
|
904
|
-
|
|
941
|
+
if (
|
|
942
|
+
newState.showCommandSelector &&
|
|
943
|
+
newCursorPosition <= newState.slashPosition
|
|
944
|
+
) {
|
|
945
|
+
newState.showCommandSelector = false;
|
|
946
|
+
newState.slashPosition = -1;
|
|
947
|
+
newState.commandSearchQuery = "";
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
// Reactivate selectors if cursor is within a trigger word
|
|
951
|
+
const atPos = getAtSelectorPosition(newInputText, newCursorPosition);
|
|
952
|
+
if (atPos !== -1 && !state.showFileSelector) {
|
|
953
|
+
newState.showFileSelector = true;
|
|
954
|
+
newState.atPosition = atPos;
|
|
955
|
+
newState.isFileSearching = true;
|
|
956
|
+
}
|
|
957
|
+
const slashPos = getSlashSelectorPosition(
|
|
958
|
+
newInputText,
|
|
959
|
+
newCursorPosition,
|
|
960
|
+
);
|
|
961
|
+
if (slashPos !== -1 && !state.showCommandSelector) {
|
|
962
|
+
newState.showCommandSelector = true;
|
|
963
|
+
newState.slashPosition = slashPos;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
// Update queries
|
|
967
|
+
if (newState.showFileSelector && newState.atPosition >= 0) {
|
|
968
|
+
newState.fileSearchQuery = newInputText.substring(
|
|
969
|
+
newState.atPosition + 1,
|
|
970
|
+
newCursorPosition,
|
|
971
|
+
);
|
|
972
|
+
}
|
|
973
|
+
if (newState.showCommandSelector && newState.slashPosition >= 0) {
|
|
974
|
+
newState.commandSearchQuery = newInputText.substring(
|
|
975
|
+
newState.slashPosition + 1,
|
|
976
|
+
newCursorPosition,
|
|
977
|
+
);
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
return newState;
|
|
905
981
|
}
|
|
906
982
|
|
|
907
983
|
// 1. /btw overlay handling (active while a question is displayed, or
|
|
@@ -1425,54 +1501,69 @@ export function inputReducer(
|
|
|
1425
1501
|
|
|
1426
1502
|
// 8. Backspace / Delete (Normal Mode)
|
|
1427
1503
|
if (key.backspace || key.delete) {
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1504
|
+
// Placeholder token deletion first (aligned with Claude Code's
|
|
1505
|
+
// deleteTokenBefore, Cursor.ts:937-969): backspace at the end of a
|
|
1506
|
+
// [LongText#N] / [Image #N] token removes the whole token in one
|
|
1507
|
+
// press (and drops its longTextMap entry); otherwise delete one
|
|
1508
|
+
// character. Selector backspace (section 5) stays character-wise —
|
|
1509
|
+
// @mention and /command words are intentionally not tokenized.
|
|
1510
|
+
const tokenDeletion = deleteTokenBefore(
|
|
1511
|
+
state.inputText,
|
|
1512
|
+
state.cursorPosition,
|
|
1513
|
+
state.longTextMap,
|
|
1514
|
+
);
|
|
1515
|
+
if (!tokenDeletion && state.cursorPosition <= 0) {
|
|
1516
|
+
return state;
|
|
1517
|
+
}
|
|
1518
|
+
const newInputText = tokenDeletion
|
|
1519
|
+
? tokenDeletion.inputText
|
|
1520
|
+
: state.inputText.substring(0, state.cursorPosition - 1) +
|
|
1521
|
+
state.inputText.substring(state.cursorPosition);
|
|
1522
|
+
const newCursorPosition = tokenDeletion
|
|
1523
|
+
? tokenDeletion.cursorPosition
|
|
1524
|
+
: state.cursorPosition - 1;
|
|
1436
1525
|
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1526
|
+
const newState = {
|
|
1527
|
+
...state,
|
|
1528
|
+
inputText: newInputText,
|
|
1529
|
+
cursorPosition: newCursorPosition,
|
|
1530
|
+
longTextMap: tokenDeletion
|
|
1531
|
+
? tokenDeletion.longTextMap
|
|
1532
|
+
: state.longTextMap,
|
|
1533
|
+
historyIndex: -1,
|
|
1534
|
+
};
|
|
1443
1535
|
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1536
|
+
// Reactivate selectors if cursor is within word
|
|
1537
|
+
const atPos = getAtSelectorPosition(newInputText, newCursorPosition);
|
|
1538
|
+
if (atPos !== -1 && !state.showFileSelector) {
|
|
1539
|
+
newState.showFileSelector = true;
|
|
1540
|
+
newState.atPosition = atPos;
|
|
1541
|
+
newState.isFileSearching = true;
|
|
1542
|
+
}
|
|
1451
1543
|
|
|
1452
|
-
|
|
1453
|
-
|
|
1544
|
+
const slashPos = getSlashSelectorPosition(
|
|
1545
|
+
newInputText,
|
|
1546
|
+
newCursorPosition,
|
|
1547
|
+
);
|
|
1548
|
+
if (slashPos !== -1 && !state.showCommandSelector) {
|
|
1549
|
+
newState.showCommandSelector = true;
|
|
1550
|
+
newState.slashPosition = slashPos;
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
// Update queries
|
|
1554
|
+
if (newState.showFileSelector && newState.atPosition >= 0) {
|
|
1555
|
+
newState.fileSearchQuery = newInputText.substring(
|
|
1556
|
+
newState.atPosition + 1,
|
|
1557
|
+
newCursorPosition,
|
|
1558
|
+
);
|
|
1559
|
+
}
|
|
1560
|
+
if (newState.showCommandSelector && newState.slashPosition >= 0) {
|
|
1561
|
+
newState.commandSearchQuery = newInputText.substring(
|
|
1562
|
+
newState.slashPosition + 1,
|
|
1454
1563
|
newCursorPosition,
|
|
1455
1564
|
);
|
|
1456
|
-
if (slashPos !== -1 && !state.showCommandSelector) {
|
|
1457
|
-
newState.showCommandSelector = true;
|
|
1458
|
-
newState.slashPosition = slashPos;
|
|
1459
|
-
}
|
|
1460
|
-
|
|
1461
|
-
// Update queries
|
|
1462
|
-
if (newState.showFileSelector && newState.atPosition >= 0) {
|
|
1463
|
-
newState.fileSearchQuery = newInputText.substring(
|
|
1464
|
-
newState.atPosition + 1,
|
|
1465
|
-
newCursorPosition,
|
|
1466
|
-
);
|
|
1467
|
-
}
|
|
1468
|
-
if (newState.showCommandSelector && newState.slashPosition >= 0) {
|
|
1469
|
-
newState.commandSearchQuery = newInputText.substring(
|
|
1470
|
-
newState.slashPosition + 1,
|
|
1471
|
-
newCursorPosition,
|
|
1472
|
-
);
|
|
1473
|
-
}
|
|
1474
|
-
return newState;
|
|
1475
1565
|
}
|
|
1566
|
+
return newState;
|
|
1476
1567
|
}
|
|
1477
1568
|
|
|
1478
1569
|
// 9. Cursor Movement (Normal Mode)
|
package/src/print-cli.ts
CHANGED
|
@@ -147,6 +147,7 @@ export async function startPrintCli(options: PrintCliOptions): Promise<void> {
|
|
|
147
147
|
worktreeName: worktreeSession.name,
|
|
148
148
|
isNew: worktreeSession.isNew,
|
|
149
149
|
repoRoot: worktreeSession.repoRoot,
|
|
150
|
+
hookBased: worktreeSession.hookBased,
|
|
150
151
|
};
|
|
151
152
|
agent.setWorktreeSession(session);
|
|
152
153
|
}
|
|
@@ -186,8 +187,7 @@ export async function startPrintCli(options: PrintCliOptions): Promise<void> {
|
|
|
186
187
|
// Display timing information
|
|
187
188
|
displayTimingInfo(startTime, showStats);
|
|
188
189
|
|
|
189
|
-
//
|
|
190
|
-
// decide whether the worktree is clean enough to remove
|
|
190
|
+
// Decide whether the worktree is clean enough to remove
|
|
191
191
|
let cleanWorktree = false;
|
|
192
192
|
if (worktreeSession) {
|
|
193
193
|
const cwd = workdir || worktreeSession.path;
|
|
@@ -196,9 +196,7 @@ export async function startPrintCli(options: PrintCliOptions): Promise<void> {
|
|
|
196
196
|
const hasCommits = hasNewCommits(cwd, baseBranch);
|
|
197
197
|
cleanWorktree = !hasChanges && !hasCommits;
|
|
198
198
|
|
|
199
|
-
if (cleanWorktree) {
|
|
200
|
-
await agent.triggerWorktreeRemoveHook(worktreeSession.path);
|
|
201
|
-
} else {
|
|
199
|
+
if (!cleanWorktree) {
|
|
202
200
|
process.stdout.write(
|
|
203
201
|
`\n⚠️ Worktree '${worktreeSession.name}' has changes or commits. Keeping it at: ${worktreeSession.path}\n`,
|
|
204
202
|
);
|
|
@@ -211,11 +209,17 @@ export async function startPrintCli(options: PrintCliOptions): Promise<void> {
|
|
|
211
209
|
// Handle worktree cleanup for print mode (git removal stays after destroy)
|
|
212
210
|
if (worktreeSession && cleanWorktree) {
|
|
213
211
|
try {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
212
|
+
// Hook-based worktrees are owned by the hook; the git-root
|
|
213
|
+
// containment check does not apply to them
|
|
214
|
+
if (!worktreeSession.hookBased) {
|
|
215
|
+
validateWorktreeRemovalPath(
|
|
216
|
+
worktreeSession.path,
|
|
217
|
+
worktreeSession.repoRoot,
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
process.stdout.write("\nDeleting worktree ...\n");
|
|
218
221
|
await removeWorktree(worktreeSession);
|
|
222
|
+
process.stdout.write("Done.\n");
|
|
219
223
|
} catch (error) {
|
|
220
224
|
// Never block print-mode exit on worktree cleanup failures
|
|
221
225
|
process.stdout.write(
|
|
@@ -242,7 +246,7 @@ export async function startPrintCli(options: PrintCliOptions): Promise<void> {
|
|
|
242
246
|
// Display timing information even on error
|
|
243
247
|
displayTimingInfo(startTime, showStats);
|
|
244
248
|
|
|
245
|
-
//
|
|
249
|
+
// Decide whether the worktree is clean enough to remove
|
|
246
250
|
let cleanWorktree = false;
|
|
247
251
|
if (worktreeSession) {
|
|
248
252
|
const cwd = workdir || worktreeSession.path;
|
|
@@ -251,9 +255,7 @@ export async function startPrintCli(options: PrintCliOptions): Promise<void> {
|
|
|
251
255
|
const hasCommits = hasNewCommits(cwd, baseBranch);
|
|
252
256
|
cleanWorktree = !hasChanges && !hasCommits;
|
|
253
257
|
|
|
254
|
-
if (cleanWorktree) {
|
|
255
|
-
await agent.triggerWorktreeRemoveHook(worktreeSession.path);
|
|
256
|
-
} else {
|
|
258
|
+
if (!cleanWorktree) {
|
|
257
259
|
process.stdout.write(
|
|
258
260
|
`\n⚠️ Worktree '${worktreeSession.name}' has changes or commits. Keeping it at: ${worktreeSession.path}\n`,
|
|
259
261
|
);
|
|
@@ -265,11 +267,17 @@ export async function startPrintCli(options: PrintCliOptions): Promise<void> {
|
|
|
265
267
|
// Handle worktree cleanup for print mode even on error
|
|
266
268
|
if (worktreeSession && cleanWorktree) {
|
|
267
269
|
try {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
270
|
+
// Hook-based worktrees are owned by the hook; the git-root
|
|
271
|
+
// containment check does not apply to them
|
|
272
|
+
if (!worktreeSession.hookBased) {
|
|
273
|
+
validateWorktreeRemovalPath(
|
|
274
|
+
worktreeSession.path,
|
|
275
|
+
worktreeSession.repoRoot,
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
process.stdout.write("\nDeleting worktree ...\n");
|
|
272
279
|
await removeWorktree(worktreeSession);
|
|
280
|
+
process.stdout.write("Done.\n");
|
|
273
281
|
} catch (error) {
|
|
274
282
|
process.stdout.write(
|
|
275
283
|
`\n⚠️ Skipping worktree removal: ${(error as Error).message}\n`,
|
|
@@ -4,6 +4,10 @@ export interface SelectorState<T = unknown> {
|
|
|
4
4
|
selectedIndex: number;
|
|
5
5
|
pendingDecision: "select" | "insert" | "cancel" | null;
|
|
6
6
|
items: T[];
|
|
7
|
+
/** View state: show sessions from all projects (Ctrl+A toggle). */
|
|
8
|
+
showAllProjects?: boolean;
|
|
9
|
+
/** View state: show sessions from all same-repo worktrees (Ctrl+W toggle). */
|
|
10
|
+
showAllWorktrees?: boolean;
|
|
7
11
|
}
|
|
8
12
|
|
|
9
13
|
export type SelectorAction<T = unknown> =
|
|
@@ -12,6 +16,8 @@ export type SelectorAction<T = unknown> =
|
|
|
12
16
|
| { type: "RESET_INDEX" }
|
|
13
17
|
| { type: "SET_ITEMS"; items: T[] }
|
|
14
18
|
| { type: "HANDLE_KEY"; key: Key; hasInsert: boolean }
|
|
19
|
+
| { type: "TOGGLE_ALL_PROJECTS" }
|
|
20
|
+
| { type: "TOGGLE_ALL_WORKTREES" }
|
|
15
21
|
| { type: "CLEAR_DECISION" };
|
|
16
22
|
|
|
17
23
|
export function selectorReducer<T = unknown>(
|
|
@@ -33,6 +39,10 @@ export function selectorReducer<T = unknown>(
|
|
|
33
39
|
return { ...state, selectedIndex: 0 };
|
|
34
40
|
case "SET_ITEMS":
|
|
35
41
|
return { ...state, items: action.items, selectedIndex: 0 };
|
|
42
|
+
case "TOGGLE_ALL_PROJECTS":
|
|
43
|
+
return { ...state, showAllProjects: !state.showAllProjects };
|
|
44
|
+
case "TOGGLE_ALL_WORKTREES":
|
|
45
|
+
return { ...state, showAllWorktrees: !state.showAllWorktrees };
|
|
36
46
|
case "HANDLE_KEY":
|
|
37
47
|
if (action.key.upArrow) {
|
|
38
48
|
return {
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { Key } from "ink";
|
|
2
|
+
|
|
3
|
+
export type PendingEffect = { type: "CANCEL" };
|
|
4
|
+
|
|
5
|
+
export interface SkillsManagerState {
|
|
6
|
+
selectedIndex: number;
|
|
7
|
+
viewMode: "list" | "detail";
|
|
8
|
+
pendingEffect: PendingEffect | null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type SkillsManagerAction =
|
|
12
|
+
| { type: "MOVE_UP" }
|
|
13
|
+
| { type: "MOVE_DOWN"; itemCount: number }
|
|
14
|
+
| { type: "SET_VIEW_MODE"; viewMode: "list" | "detail" }
|
|
15
|
+
| {
|
|
16
|
+
type: "HANDLE_KEY";
|
|
17
|
+
input: string;
|
|
18
|
+
key: Key;
|
|
19
|
+
itemCount: number;
|
|
20
|
+
}
|
|
21
|
+
| { type: "CLEAR_PENDING_EFFECT" };
|
|
22
|
+
|
|
23
|
+
export function skillsManagerReducer(
|
|
24
|
+
state: SkillsManagerState,
|
|
25
|
+
action: SkillsManagerAction,
|
|
26
|
+
): SkillsManagerState {
|
|
27
|
+
switch (action.type) {
|
|
28
|
+
case "MOVE_UP":
|
|
29
|
+
return {
|
|
30
|
+
...state,
|
|
31
|
+
selectedIndex: Math.max(0, state.selectedIndex - 1),
|
|
32
|
+
};
|
|
33
|
+
case "MOVE_DOWN":
|
|
34
|
+
return {
|
|
35
|
+
...state,
|
|
36
|
+
selectedIndex: Math.min(
|
|
37
|
+
Math.max(0, action.itemCount - 1),
|
|
38
|
+
state.selectedIndex + 1,
|
|
39
|
+
),
|
|
40
|
+
};
|
|
41
|
+
case "SET_VIEW_MODE":
|
|
42
|
+
return { ...state, viewMode: action.viewMode };
|
|
43
|
+
case "HANDLE_KEY": {
|
|
44
|
+
const { key, itemCount } = action;
|
|
45
|
+
|
|
46
|
+
if (key.return) {
|
|
47
|
+
if (state.viewMode === "list") {
|
|
48
|
+
return { ...state, viewMode: "detail" };
|
|
49
|
+
}
|
|
50
|
+
// Aligned with AgentsManager detail view: Enter returns to the list.
|
|
51
|
+
return { ...state, viewMode: "list" };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (key.escape) {
|
|
55
|
+
if (state.viewMode === "detail") {
|
|
56
|
+
return { ...state, viewMode: "list" };
|
|
57
|
+
}
|
|
58
|
+
return { ...state, pendingEffect: { type: "CANCEL" } };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Detail view does not respond to arrow keys (aligned with
|
|
62
|
+
// AgentsManager, which only Esc/Enter back to the list).
|
|
63
|
+
if (state.viewMode === "detail") {
|
|
64
|
+
return state;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (key.upArrow) {
|
|
68
|
+
return {
|
|
69
|
+
...state,
|
|
70
|
+
selectedIndex: Math.max(0, state.selectedIndex - 1),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (key.downArrow) {
|
|
75
|
+
return {
|
|
76
|
+
...state,
|
|
77
|
+
selectedIndex: Math.min(
|
|
78
|
+
Math.max(0, itemCount - 1),
|
|
79
|
+
state.selectedIndex + 1,
|
|
80
|
+
),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return state;
|
|
85
|
+
}
|
|
86
|
+
case "CLEAR_PENDING_EFFECT":
|
|
87
|
+
return { ...state, pendingEffect: null };
|
|
88
|
+
default:
|
|
89
|
+
return state;
|
|
90
|
+
}
|
|
91
|
+
}
|