wave-code 0.18.3 → 0.18.5
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/dist/acp/agent.d.ts.map +1 -1
- package/dist/acp/agent.js +13 -0
- package/dist/components/CommandSelector.d.ts.map +1 -1
- package/dist/components/CommandSelector.js +9 -13
- package/dist/components/ConfirmationSelector.d.ts.map +1 -1
- package/dist/components/ConfirmationSelector.js +0 -1
- package/dist/components/DiscoverView.d.ts.map +1 -1
- package/dist/components/DiscoverView.js +10 -6
- package/dist/components/FileSelector.js +8 -8
- package/dist/components/HistorySearch.d.ts.map +1 -1
- package/dist/components/HistorySearch.js +6 -13
- package/dist/components/InstalledView.d.ts.map +1 -1
- package/dist/components/InstalledView.js +10 -6
- package/dist/components/LoginCommand.d.ts.map +1 -1
- package/dist/components/LoginCommand.js +14 -11
- package/dist/components/MarketplaceDetail.d.ts.map +1 -1
- package/dist/components/MarketplaceDetail.js +11 -7
- package/dist/components/MarketplaceView.d.ts.map +1 -1
- package/dist/components/MarketplaceView.js +10 -6
- package/dist/components/ModelSelector.d.ts.map +1 -1
- package/dist/components/ModelSelector.js +9 -15
- package/dist/components/SessionSelector.d.ts +5 -3
- package/dist/components/SessionSelector.d.ts.map +1 -1
- package/dist/components/SessionSelector.js +11 -7
- package/dist/components/WorktreeExitPrompt.d.ts.map +1 -1
- package/dist/components/WorktreeExitPrompt.js +3 -3
- package/dist/reducers/questionReducer.d.ts +0 -7
- package/dist/reducers/questionReducer.d.ts.map +1 -1
- package/dist/reducers/questionReducer.js +2 -1
- package/dist/reducers/selectorReducer.d.ts +7 -5
- package/dist/reducers/selectorReducer.d.ts.map +1 -1
- package/dist/reducers/selectorReducer.js +4 -2
- package/dist/utils/worktree.d.ts.map +1 -1
- package/dist/utils/worktree.js +10 -10
- package/package.json +2 -2
- package/src/acp/agent.ts +14 -0
- package/src/components/CommandSelector.tsx +11 -15
- package/src/components/ConfirmationSelector.tsx +0 -1
- package/src/components/DiscoverView.tsx +22 -7
- package/src/components/FileSelector.tsx +9 -9
- package/src/components/HistorySearch.tsx +7 -14
- package/src/components/InstalledView.tsx +23 -9
- package/src/components/LoginCommand.tsx +33 -36
- package/src/components/MarketplaceDetail.tsx +25 -10
- package/src/components/MarketplaceView.tsx +16 -7
- package/src/components/ModelSelector.tsx +13 -16
- package/src/components/SessionSelector.tsx +19 -9
- package/src/components/WorktreeExitPrompt.tsx +7 -4
- package/src/reducers/questionReducer.ts +2 -6
- package/src/reducers/selectorReducer.ts +20 -10
- package/src/utils/worktree.ts +29 -19
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"questionReducer.d.ts","sourceRoot":"","sources":["../../src/reducers/questionReducer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAC1B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEzD,MAAM,WAAW,kBAAkB;IACjC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,qBAAqB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC5B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,qBAAqB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACnC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAChD,QAAQ,EAAE,kBAAkB,GAAG,IAAI,CAAC;CACrC;AAED,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC5C;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC9C;IACE,IAAI,EAAE,qBAAqB,CAAC;IAC5B,aAAa,EAAE,MAAM,CAAC;CACvB,GACD;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,KAAK,EAAE,OAAO,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,GACjE;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,GAC7D;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,GACnD;IACE,IAAI,EAAE,gBAAgB,CAAC;IACvB,eAAe,EAAE;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,KAAK,CAAC;YAAE,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAClC,WAAW,CAAC,EAAE,OAAO,CAAC;KACvB,CAAC;IACF,OAAO,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClC,aAAa,EAAE,OAAO,CAAC;IACvB,SAAS,EAAE,KAAK,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,KAAK,CAAC;YAAE,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAClC,WAAW,CAAC,EAAE,OAAO,CAAC;KACvB,CAAC,CAAC;CACJ,GACD;IAAE,IAAI,EAAE,gBAAgB,CAAA;CAAE,GAC1B;IACE,IAAI,EAAE,YAAY,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,GAAG,CAAC;IACT,
|
|
1
|
+
{"version":3,"file":"questionReducer.d.ts","sourceRoot":"","sources":["../../src/reducers/questionReducer.ts"],"names":[],"mappings":"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"}
|
|
@@ -192,7 +192,8 @@ export function questionReducer(state, action) {
|
|
|
192
192
|
case "CLEAR_DECISION":
|
|
193
193
|
return { ...state, decision: null };
|
|
194
194
|
case "HANDLE_KEY": {
|
|
195
|
-
const { input, key,
|
|
195
|
+
const { input, key, questions } = action;
|
|
196
|
+
const currentQuestion = questions[state.currentQuestionIndex];
|
|
196
197
|
if (!currentQuestion)
|
|
197
198
|
return state;
|
|
198
199
|
const options = [...currentQuestion.options, { label: "Other" }];
|
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
import { Key } from "ink";
|
|
2
|
-
export interface SelectorState {
|
|
2
|
+
export interface SelectorState<T = unknown> {
|
|
3
3
|
selectedIndex: number;
|
|
4
4
|
pendingDecision: "select" | "insert" | "cancel" | null;
|
|
5
|
+
items: T[];
|
|
5
6
|
}
|
|
6
|
-
export type SelectorAction = {
|
|
7
|
+
export type SelectorAction<T = unknown> = {
|
|
7
8
|
type: "MOVE_UP";
|
|
8
9
|
} | {
|
|
9
10
|
type: "MOVE_DOWN";
|
|
10
|
-
maxIndex: number;
|
|
11
11
|
} | {
|
|
12
12
|
type: "RESET_INDEX";
|
|
13
|
+
} | {
|
|
14
|
+
type: "SET_ITEMS";
|
|
15
|
+
items: T[];
|
|
13
16
|
} | {
|
|
14
17
|
type: "HANDLE_KEY";
|
|
15
18
|
key: Key;
|
|
16
|
-
maxIndex: number;
|
|
17
19
|
hasInsert: boolean;
|
|
18
20
|
} | {
|
|
19
21
|
type: "CLEAR_DECISION";
|
|
20
22
|
};
|
|
21
|
-
export declare function selectorReducer(state: SelectorState
|
|
23
|
+
export declare function selectorReducer<T = unknown>(state: SelectorState<T>, action: SelectorAction<T>): SelectorState<T>;
|
|
22
24
|
//# sourceMappingURL=selectorReducer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"selectorReducer.d.ts","sourceRoot":"","sources":["../../src/reducers/selectorReducer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAE1B,MAAM,WAAW,aAAa;
|
|
1
|
+
{"version":3,"file":"selectorReducer.d.ts","sourceRoot":"","sources":["../../src/reducers/selectorReducer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAE1B,MAAM,WAAW,aAAa,CAAC,CAAC,GAAG,OAAO;IACxC,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,IAAI,CAAC;IACvD,KAAK,EAAE,CAAC,EAAE,CAAC;CACZ;AAED,MAAM,MAAM,cAAc,CAAC,CAAC,GAAG,OAAO,IAClC;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GACrB;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,GACvB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,CAAC,EAAE,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,GAAG,EAAE,GAAG,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,GACpD;IAAE,IAAI,EAAE,gBAAgB,CAAA;CAAE,CAAC;AAE/B,wBAAgB,eAAe,CAAC,CAAC,GAAG,OAAO,EACzC,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,EACvB,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,GACxB,aAAa,CAAC,CAAC,CAAC,CA+ClB"}
|
|
@@ -5,10 +5,12 @@ export function selectorReducer(state, action) {
|
|
|
5
5
|
case "MOVE_DOWN":
|
|
6
6
|
return {
|
|
7
7
|
...state,
|
|
8
|
-
selectedIndex: Math.min(
|
|
8
|
+
selectedIndex: Math.min(state.items.length - 1, state.selectedIndex + 1),
|
|
9
9
|
};
|
|
10
10
|
case "RESET_INDEX":
|
|
11
11
|
return { ...state, selectedIndex: 0 };
|
|
12
|
+
case "SET_ITEMS":
|
|
13
|
+
return { ...state, items: action.items, selectedIndex: 0 };
|
|
12
14
|
case "HANDLE_KEY":
|
|
13
15
|
if (action.key.upArrow) {
|
|
14
16
|
return {
|
|
@@ -19,7 +21,7 @@ export function selectorReducer(state, action) {
|
|
|
19
21
|
if (action.key.downArrow) {
|
|
20
22
|
return {
|
|
21
23
|
...state,
|
|
22
|
-
selectedIndex: Math.min(
|
|
24
|
+
selectedIndex: Math.min(state.items.length - 1, state.selectedIndex + 1),
|
|
23
25
|
};
|
|
24
26
|
}
|
|
25
27
|
if (action.key.return) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worktree.d.ts","sourceRoot":"","sources":["../../src/utils/worktree.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,qBAAqB,EAAE,OAAO,CAAC;IAC/B,aAAa,EAAE,OAAO,CAAC;IACvB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,eAAe,
|
|
1
|
+
{"version":3,"file":"worktree.d.ts","sourceRoot":"","sources":["../../src/utils/worktree.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,qBAAqB,EAAE,OAAO,CAAC;IAC/B,aAAa,EAAE,OAAO,CAAC;IACvB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,eAAe,CAiIzE;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,CAiE7D"}
|
package/dist/utils/worktree.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import { getDefaultRemoteBranch, getGitMainRepoRoot } from "wave-agent-sdk";
|
|
@@ -33,7 +33,7 @@ export function createWorktree(name, cwd) {
|
|
|
33
33
|
}
|
|
34
34
|
try {
|
|
35
35
|
// Create worktree and branch
|
|
36
|
-
|
|
36
|
+
execFileSync("git", ["worktree", "add", "-b", branchName, worktreePath, baseBranch], {
|
|
37
37
|
cwd: repoRoot,
|
|
38
38
|
stdio: ["ignore", "pipe", "pipe"],
|
|
39
39
|
});
|
|
@@ -52,7 +52,7 @@ export function createWorktree(name, cwd) {
|
|
|
52
52
|
if (stderr.includes("already exists")) {
|
|
53
53
|
// If branch already exists, try to add worktree without -b
|
|
54
54
|
try {
|
|
55
|
-
|
|
55
|
+
execFileSync("git", ["worktree", "add", worktreePath, branchName], {
|
|
56
56
|
cwd: repoRoot,
|
|
57
57
|
stdio: ["ignore", "pipe", "pipe"],
|
|
58
58
|
});
|
|
@@ -75,11 +75,11 @@ export function createWorktree(name, cwd) {
|
|
|
75
75
|
// Base branch not fetched yet — try fetching then retrying
|
|
76
76
|
const branchNameOnly = baseBranch.split("/").pop();
|
|
77
77
|
try {
|
|
78
|
-
|
|
78
|
+
execFileSync("git", ["fetch", "origin", branchNameOnly], {
|
|
79
79
|
cwd: repoRoot,
|
|
80
80
|
stdio: ["ignore", "pipe", "pipe"],
|
|
81
81
|
});
|
|
82
|
-
|
|
82
|
+
execFileSync("git", ["worktree", "add", "-b", branchName, worktreePath, baseBranch], {
|
|
83
83
|
cwd: repoRoot,
|
|
84
84
|
stdio: ["ignore", "pipe", "pipe"],
|
|
85
85
|
});
|
|
@@ -96,7 +96,7 @@ export function createWorktree(name, cwd) {
|
|
|
96
96
|
catch {
|
|
97
97
|
// Fetch or retry failed — fall back to HEAD
|
|
98
98
|
try {
|
|
99
|
-
|
|
99
|
+
execFileSync("git", ["worktree", "add", "-b", branchName, worktreePath, "HEAD"], {
|
|
100
100
|
cwd: repoRoot,
|
|
101
101
|
stdio: ["ignore", "pipe", "pipe"],
|
|
102
102
|
});
|
|
@@ -128,7 +128,7 @@ export function removeWorktree(session) {
|
|
|
128
128
|
// Get current branch in worktree before removing it
|
|
129
129
|
let currentBranch;
|
|
130
130
|
try {
|
|
131
|
-
currentBranch =
|
|
131
|
+
currentBranch = execFileSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
132
132
|
cwd: session.path,
|
|
133
133
|
encoding: "utf8",
|
|
134
134
|
stdio: ["ignore", "pipe", "ignore"],
|
|
@@ -138,13 +138,13 @@ export function removeWorktree(session) {
|
|
|
138
138
|
// Ignore errors getting current branch
|
|
139
139
|
}
|
|
140
140
|
// Remove worktree
|
|
141
|
-
|
|
141
|
+
execFileSync("git", ["worktree", "remove", "--force", session.path], {
|
|
142
142
|
cwd: repoRoot,
|
|
143
143
|
stdio: ["ignore", "pipe", "pipe"],
|
|
144
144
|
});
|
|
145
145
|
// Delete original branch
|
|
146
146
|
try {
|
|
147
|
-
|
|
147
|
+
execFileSync("git", ["branch", "-D", session.branch], {
|
|
148
148
|
cwd: repoRoot,
|
|
149
149
|
stdio: ["ignore", "pipe", "pipe"],
|
|
150
150
|
});
|
|
@@ -162,7 +162,7 @@ export function removeWorktree(session) {
|
|
|
162
162
|
currentBranch !== "main" &&
|
|
163
163
|
currentBranch !== "master") {
|
|
164
164
|
try {
|
|
165
|
-
|
|
165
|
+
execFileSync("git", ["branch", "-D", currentBranch], {
|
|
166
166
|
cwd: repoRoot,
|
|
167
167
|
stdio: ["ignore", "pipe", "pipe"],
|
|
168
168
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wave-code",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.5",
|
|
4
4
|
"description": "CLI-based code assistant powered by AI, built with React and Ink",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"semver": "^7.7.4",
|
|
43
43
|
"yargs": "^17.7.2",
|
|
44
44
|
"zod": "^3.23.8",
|
|
45
|
-
"wave-agent-sdk": "0.18.
|
|
45
|
+
"wave-agent-sdk": "0.18.5"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@types/react": "^19.1.8",
|
package/src/acp/agent.ts
CHANGED
|
@@ -245,6 +245,8 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
245
245
|
callbacks.onSessionIdChange?.(newSessionId),
|
|
246
246
|
onLatestTotalTokensChange: (tokens: number) =>
|
|
247
247
|
callbacks.onLatestTotalTokensChange?.(tokens),
|
|
248
|
+
onLoadingChange: (loading: boolean) =>
|
|
249
|
+
callbacks.onLoadingChange?.(loading),
|
|
248
250
|
},
|
|
249
251
|
});
|
|
250
252
|
|
|
@@ -1398,6 +1400,18 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
1398
1400
|
},
|
|
1399
1401
|
});
|
|
1400
1402
|
},
|
|
1403
|
+
onLoadingChange: (loading: boolean) => {
|
|
1404
|
+
if (!loading) {
|
|
1405
|
+
this.connection.sessionUpdate({
|
|
1406
|
+
sessionId: sessionRef.id as AcpSessionId,
|
|
1407
|
+
update: {
|
|
1408
|
+
sessionUpdate: "agent_message_chunk",
|
|
1409
|
+
content: { type: "text", text: "" },
|
|
1410
|
+
_meta: { endOfTurn: true },
|
|
1411
|
+
},
|
|
1412
|
+
});
|
|
1413
|
+
}
|
|
1414
|
+
},
|
|
1401
1415
|
},
|
|
1402
1416
|
sessionRef,
|
|
1403
1417
|
};
|
|
@@ -23,20 +23,16 @@ export const CommandSelector: React.FC<CommandSelectorProps> = ({
|
|
|
23
23
|
commands = [],
|
|
24
24
|
}) => {
|
|
25
25
|
const MAX_VISIBLE_ITEMS = 3;
|
|
26
|
-
const [state, dispatch] = useReducer(selectorReducer
|
|
26
|
+
const [state, dispatch] = useReducer(selectorReducer<SlashCommand>, {
|
|
27
27
|
selectedIndex: 0,
|
|
28
28
|
pendingDecision: null,
|
|
29
|
-
|
|
29
|
+
items: [],
|
|
30
|
+
} as SelectorState<SlashCommand>);
|
|
30
31
|
|
|
31
|
-
const { selectedIndex, pendingDecision } = state;
|
|
32
|
-
|
|
33
|
-
// Reset selected index when search query changes
|
|
34
|
-
useEffect(() => {
|
|
35
|
-
dispatch({ type: "RESET_INDEX" });
|
|
36
|
-
}, [searchQuery]);
|
|
32
|
+
const { selectedIndex, pendingDecision, items: filteredCommands } = state;
|
|
37
33
|
|
|
38
34
|
// Merge agent commands and local commands, memoized to stabilize useEffect dependencies
|
|
39
|
-
const
|
|
35
|
+
const computedCommands = useMemo(() => {
|
|
40
36
|
const allCommands = [...AVAILABLE_COMMANDS, ...commands];
|
|
41
37
|
return allCommands.filter(
|
|
42
38
|
(command) =>
|
|
@@ -45,6 +41,11 @@ export const CommandSelector: React.FC<CommandSelectorProps> = ({
|
|
|
45
41
|
);
|
|
46
42
|
}, [searchQuery, commands]);
|
|
47
43
|
|
|
44
|
+
// Sync computed commands into reducer state (SET_ITEMS also resets index)
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
dispatch({ type: "SET_ITEMS", items: computedCommands });
|
|
47
|
+
}, [computedCommands]);
|
|
48
|
+
|
|
48
49
|
// Handle decisions from reducer
|
|
49
50
|
useEffect(() => {
|
|
50
51
|
if (!pendingDecision) return;
|
|
@@ -91,12 +92,7 @@ export const CommandSelector: React.FC<CommandSelectorProps> = ({
|
|
|
91
92
|
);
|
|
92
93
|
|
|
93
94
|
useInput((input, key) => {
|
|
94
|
-
dispatch({
|
|
95
|
-
type: "HANDLE_KEY",
|
|
96
|
-
key,
|
|
97
|
-
maxIndex: filteredCommands.length - 1,
|
|
98
|
-
hasInsert: !!onInsert,
|
|
99
|
-
});
|
|
95
|
+
dispatch({ type: "HANDLE_KEY", key, hasInsert: !!onInsert });
|
|
100
96
|
});
|
|
101
97
|
|
|
102
98
|
if (filteredCommands.length === 0) {
|
|
@@ -1,37 +1,52 @@
|
|
|
1
1
|
import React, { useReducer, useEffect } from "react";
|
|
2
2
|
import { Box, useInput } from "ink";
|
|
3
|
+
import type { MarketplacePluginEntry } from "wave-agent-sdk";
|
|
3
4
|
import { usePluginManagerContext } from "../contexts/PluginManagerContext.js";
|
|
4
5
|
import { PluginList } from "./PluginList.js";
|
|
5
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
selectorReducer,
|
|
8
|
+
type SelectorState,
|
|
9
|
+
} from "../reducers/selectorReducer.js";
|
|
10
|
+
|
|
11
|
+
type DiscoverablePlugin = MarketplacePluginEntry & {
|
|
12
|
+
marketplace: string;
|
|
13
|
+
installed: boolean;
|
|
14
|
+
version?: string;
|
|
15
|
+
};
|
|
6
16
|
|
|
7
17
|
export const DiscoverView: React.FC = () => {
|
|
8
18
|
const { discoverablePlugins, actions } = usePluginManagerContext();
|
|
9
|
-
const [state, dispatch] = useReducer(selectorReducer
|
|
19
|
+
const [state, dispatch] = useReducer(selectorReducer<DiscoverablePlugin>, {
|
|
10
20
|
selectedIndex: 0,
|
|
11
21
|
pendingDecision: null,
|
|
12
|
-
|
|
22
|
+
items: [],
|
|
23
|
+
} as SelectorState<DiscoverablePlugin>);
|
|
13
24
|
|
|
14
|
-
const { selectedIndex, pendingDecision } = state;
|
|
25
|
+
const { selectedIndex, pendingDecision, items } = state;
|
|
26
|
+
|
|
27
|
+
// Sync plugins into reducer state
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
dispatch({ type: "SET_ITEMS", items: discoverablePlugins });
|
|
30
|
+
}, [discoverablePlugins]);
|
|
15
31
|
|
|
16
32
|
useInput((_input, key) => {
|
|
17
33
|
dispatch({
|
|
18
34
|
type: "HANDLE_KEY",
|
|
19
35
|
key,
|
|
20
|
-
maxIndex: discoverablePlugins.length - 1,
|
|
21
36
|
hasInsert: false,
|
|
22
37
|
});
|
|
23
38
|
});
|
|
24
39
|
|
|
25
40
|
useEffect(() => {
|
|
26
41
|
if (pendingDecision === "select") {
|
|
27
|
-
const plugin =
|
|
42
|
+
const plugin = items[selectedIndex];
|
|
28
43
|
if (plugin) {
|
|
29
44
|
actions.setSelectedId(`${plugin.name}@${plugin.marketplace}`);
|
|
30
45
|
actions.setView("PLUGIN_DETAIL");
|
|
31
46
|
}
|
|
32
47
|
dispatch({ type: "CLEAR_DECISION" });
|
|
33
48
|
}
|
|
34
|
-
}, [pendingDecision, selectedIndex,
|
|
49
|
+
}, [pendingDecision, selectedIndex, items, actions]);
|
|
35
50
|
|
|
36
51
|
return (
|
|
37
52
|
<Box flexDirection="column">
|
|
@@ -23,16 +23,17 @@ export const FileSelector: React.FC<FileSelectorProps> = ({
|
|
|
23
23
|
onSelect,
|
|
24
24
|
onCancel,
|
|
25
25
|
}) => {
|
|
26
|
-
const [state, dispatch] = useReducer(selectorReducer
|
|
26
|
+
const [state, dispatch] = useReducer(selectorReducer<FileItem>, {
|
|
27
27
|
selectedIndex: 0,
|
|
28
28
|
pendingDecision: null,
|
|
29
|
-
|
|
29
|
+
items: [],
|
|
30
|
+
} as SelectorState<FileItem>);
|
|
30
31
|
|
|
31
|
-
const { selectedIndex, pendingDecision } = state;
|
|
32
|
+
const { selectedIndex, pendingDecision, items: filesInState } = state;
|
|
32
33
|
|
|
33
|
-
//
|
|
34
|
+
// Sync files from props into reducer state (SET_ITEMS also resets index)
|
|
34
35
|
useEffect(() => {
|
|
35
|
-
dispatch({ type: "
|
|
36
|
+
dispatch({ type: "SET_ITEMS", items: files });
|
|
36
37
|
}, [files]);
|
|
37
38
|
|
|
38
39
|
// Handle decisions from reducer
|
|
@@ -40,21 +41,20 @@ export const FileSelector: React.FC<FileSelectorProps> = ({
|
|
|
40
41
|
if (!pendingDecision) return;
|
|
41
42
|
|
|
42
43
|
if (pendingDecision === "select" || pendingDecision === "insert") {
|
|
43
|
-
if (
|
|
44
|
-
onSelect(
|
|
44
|
+
if (filesInState.length > 0 && selectedIndex < filesInState.length) {
|
|
45
|
+
onSelect(filesInState[selectedIndex].path);
|
|
45
46
|
}
|
|
46
47
|
} else if (pendingDecision === "cancel") {
|
|
47
48
|
onCancel();
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
dispatch({ type: "CLEAR_DECISION" });
|
|
51
|
-
}, [pendingDecision, selectedIndex,
|
|
52
|
+
}, [pendingDecision, selectedIndex, filesInState, onSelect, onCancel]);
|
|
52
53
|
|
|
53
54
|
useInput((input, key) => {
|
|
54
55
|
dispatch({
|
|
55
56
|
type: "HANDLE_KEY",
|
|
56
57
|
key,
|
|
57
|
-
maxIndex: files.length - 1,
|
|
58
58
|
hasInsert: true, // For FileSelector, Tab is also select
|
|
59
59
|
});
|
|
60
60
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useReducer, useEffect
|
|
1
|
+
import React, { useReducer, useEffect } from "react";
|
|
2
2
|
import { Box, Text, useInput } from "ink";
|
|
3
3
|
import { PromptHistoryManager, type PromptEntry } from "wave-agent-sdk";
|
|
4
4
|
import {
|
|
@@ -18,20 +18,18 @@ export const HistorySearch: React.FC<HistorySearchProps> = ({
|
|
|
18
18
|
onCancel,
|
|
19
19
|
}) => {
|
|
20
20
|
const MAX_VISIBLE_ITEMS = 5;
|
|
21
|
-
const [state, dispatch] = useReducer(selectorReducer
|
|
21
|
+
const [state, dispatch] = useReducer(selectorReducer<PromptEntry>, {
|
|
22
22
|
selectedIndex: 0,
|
|
23
23
|
pendingDecision: null,
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
items: [],
|
|
25
|
+
} as SelectorState<PromptEntry>);
|
|
26
26
|
|
|
27
|
-
const { selectedIndex, pendingDecision } = state;
|
|
27
|
+
const { selectedIndex, pendingDecision, items: entries } = state;
|
|
28
28
|
|
|
29
29
|
useEffect(() => {
|
|
30
30
|
const fetchHistory = async () => {
|
|
31
31
|
const results = await PromptHistoryManager.searchHistory(searchQuery);
|
|
32
|
-
|
|
33
|
-
setEntries(limitedResults);
|
|
34
|
-
dispatch({ type: "RESET_INDEX" });
|
|
32
|
+
dispatch({ type: "SET_ITEMS", items: results.slice(0, 20) });
|
|
35
33
|
};
|
|
36
34
|
fetchHistory();
|
|
37
35
|
}, [searchQuery]);
|
|
@@ -52,12 +50,7 @@ export const HistorySearch: React.FC<HistorySearchProps> = ({
|
|
|
52
50
|
}, [pendingDecision, selectedIndex, entries, onSelect, onCancel]);
|
|
53
51
|
|
|
54
52
|
useInput((input, key) => {
|
|
55
|
-
dispatch({
|
|
56
|
-
type: "HANDLE_KEY",
|
|
57
|
-
key,
|
|
58
|
-
maxIndex: entries.length - 1,
|
|
59
|
-
hasInsert: false,
|
|
60
|
-
});
|
|
53
|
+
dispatch({ type: "HANDLE_KEY", key, hasInsert: false });
|
|
61
54
|
});
|
|
62
55
|
|
|
63
56
|
if (entries.length === 0) {
|
|
@@ -1,36 +1,50 @@
|
|
|
1
1
|
import React, { useReducer, useEffect } from "react";
|
|
2
2
|
import { Box, Text, useInput } from "ink";
|
|
3
|
+
import type { InstalledPlugin } from "wave-agent-sdk";
|
|
3
4
|
import { usePluginManagerContext } from "../contexts/PluginManagerContext.js";
|
|
4
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
selectorReducer,
|
|
7
|
+
type SelectorState,
|
|
8
|
+
} from "../reducers/selectorReducer.js";
|
|
9
|
+
|
|
10
|
+
type InstalledPluginWithEnabled = InstalledPlugin & { enabled: boolean };
|
|
5
11
|
|
|
6
12
|
export const InstalledView: React.FC = () => {
|
|
7
13
|
const { installedPlugins, actions } = usePluginManagerContext();
|
|
8
|
-
const [state, dispatch] = useReducer(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
14
|
+
const [state, dispatch] = useReducer(
|
|
15
|
+
selectorReducer<InstalledPluginWithEnabled>,
|
|
16
|
+
{
|
|
17
|
+
selectedIndex: 0,
|
|
18
|
+
pendingDecision: null,
|
|
19
|
+
items: [],
|
|
20
|
+
} as SelectorState<InstalledPluginWithEnabled>,
|
|
21
|
+
);
|
|
12
22
|
|
|
13
|
-
const { selectedIndex, pendingDecision } = state;
|
|
23
|
+
const { selectedIndex, pendingDecision, items } = state;
|
|
24
|
+
|
|
25
|
+
// Sync plugins into reducer state
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
dispatch({ type: "SET_ITEMS", items: installedPlugins });
|
|
28
|
+
}, [installedPlugins]);
|
|
14
29
|
|
|
15
30
|
useInput((_input, key) => {
|
|
16
31
|
dispatch({
|
|
17
32
|
type: "HANDLE_KEY",
|
|
18
33
|
key,
|
|
19
|
-
maxIndex: installedPlugins.length - 1,
|
|
20
34
|
hasInsert: false,
|
|
21
35
|
});
|
|
22
36
|
});
|
|
23
37
|
|
|
24
38
|
useEffect(() => {
|
|
25
39
|
if (pendingDecision === "select") {
|
|
26
|
-
const plugin =
|
|
40
|
+
const plugin = items[selectedIndex];
|
|
27
41
|
if (plugin) {
|
|
28
42
|
actions.setSelectedId(`${plugin.name}@${plugin.marketplace}`);
|
|
29
43
|
actions.setView("PLUGIN_DETAIL");
|
|
30
44
|
}
|
|
31
45
|
dispatch({ type: "CLEAR_DECISION" });
|
|
32
46
|
}
|
|
33
|
-
}, [pendingDecision, selectedIndex,
|
|
47
|
+
}, [pendingDecision, selectedIndex, items, actions]);
|
|
34
48
|
|
|
35
49
|
if (installedPlugins.length === 0) {
|
|
36
50
|
return (
|
|
@@ -36,49 +36,46 @@ export const LoginCommand: React.FC<LoginCommandProps> = ({ onCancel }) => {
|
|
|
36
36
|
const tokenResolveRef = useRef<((token: string) => void) | null>(null);
|
|
37
37
|
const tokenRejectRef = useRef<((err: Error) => void) | null>(null);
|
|
38
38
|
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
// Single useInput handler — branches on isLoading to avoid the
|
|
40
|
+
// isActive transition race that drops stdin between hook swaps.
|
|
41
|
+
useInput((input, key) => {
|
|
42
|
+
if (!isLoadingRef.current) {
|
|
43
|
+
// Pre-loading mode: Enter starts login, Esc cancels
|
|
42
44
|
if (key.escape) {
|
|
43
45
|
onCancel();
|
|
44
46
|
}
|
|
45
|
-
if (key.return
|
|
47
|
+
if (key.return) {
|
|
46
48
|
handleEnter();
|
|
47
49
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
51
52
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
// Empty: just clear, keep waiting
|
|
66
|
-
setTokenInput("");
|
|
67
|
-
}
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
// Backspace
|
|
71
|
-
if (key.backspace && tokenInput.length > 0) {
|
|
72
|
-
setTokenInput((prev) => prev.slice(0, -1));
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
// Regular character input (single or pasted multi-char)
|
|
76
|
-
if (input && !key.ctrl && !key.meta && !key.return && input.length > 0) {
|
|
77
|
-
setTokenInput((prev) => prev + input);
|
|
53
|
+
// Token input mode: capture keystrokes while loading
|
|
54
|
+
if (key.escape) {
|
|
55
|
+
tokenRejectRef.current?.(new Error("cancelled"));
|
|
56
|
+
onCancel();
|
|
57
|
+
}
|
|
58
|
+
if (key.return) {
|
|
59
|
+
// Submit token
|
|
60
|
+
const trimmed = tokenInput.trim();
|
|
61
|
+
if (trimmed) {
|
|
62
|
+
tokenResolveRef.current?.(trimmed);
|
|
63
|
+
} else {
|
|
64
|
+
// Empty: just clear, keep waiting
|
|
65
|
+
setTokenInput("");
|
|
78
66
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
// Backspace
|
|
70
|
+
if (key.backspace && tokenInput.length > 0) {
|
|
71
|
+
setTokenInput((prev) => prev.slice(0, -1));
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
// Regular character input (single or pasted multi-char)
|
|
75
|
+
if (input && !key.ctrl && !key.meta && !key.return && input.length > 0) {
|
|
76
|
+
setTokenInput((prev) => prev + input);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
82
79
|
|
|
83
80
|
const handleEnter = async () => {
|
|
84
81
|
if (isLoadingRef.current) return;
|
|
@@ -1,16 +1,27 @@
|
|
|
1
1
|
import React, { useReducer, useEffect, useMemo } from "react";
|
|
2
2
|
import { Box, Text, useInput } from "ink";
|
|
3
3
|
import { usePluginManagerContext } from "../contexts/PluginManagerContext.js";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
selectorReducer,
|
|
6
|
+
type SelectorState,
|
|
7
|
+
} from "../reducers/selectorReducer.js";
|
|
5
8
|
|
|
6
9
|
export const MarketplaceDetail: React.FC = () => {
|
|
7
10
|
const { state, marketplaces, actions } = usePluginManagerContext();
|
|
8
|
-
const [selectorState, dispatch] = useReducer(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
const [selectorState, dispatch] = useReducer(
|
|
12
|
+
selectorReducer<{ id: string; label: string }>,
|
|
13
|
+
{
|
|
14
|
+
selectedIndex: 0,
|
|
15
|
+
pendingDecision: null,
|
|
16
|
+
items: [],
|
|
17
|
+
} as SelectorState<{ id: string; label: string }>,
|
|
18
|
+
);
|
|
12
19
|
|
|
13
|
-
const {
|
|
20
|
+
const {
|
|
21
|
+
selectedIndex: selectedActionIndex,
|
|
22
|
+
pendingDecision,
|
|
23
|
+
items: actionsList,
|
|
24
|
+
} = selectorState;
|
|
14
25
|
|
|
15
26
|
const marketplace = marketplaces.find((m) => m.name === state.selectedId);
|
|
16
27
|
|
|
@@ -27,13 +38,17 @@ export const MarketplaceDetail: React.FC = () => {
|
|
|
27
38
|
[marketplace?.autoUpdate],
|
|
28
39
|
);
|
|
29
40
|
|
|
41
|
+
// Sync ACTIONS into reducer state
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
dispatch({ type: "SET_ITEMS", items: [...ACTIONS] });
|
|
44
|
+
}, [ACTIONS]);
|
|
45
|
+
|
|
30
46
|
useInput((_input, key) => {
|
|
31
47
|
if (state.isLoading && !key.escape) return;
|
|
32
48
|
|
|
33
49
|
dispatch({
|
|
34
50
|
type: "HANDLE_KEY",
|
|
35
51
|
key,
|
|
36
|
-
maxIndex: ACTIONS.length - 1,
|
|
37
52
|
hasInsert: false,
|
|
38
53
|
});
|
|
39
54
|
});
|
|
@@ -42,12 +57,12 @@ export const MarketplaceDetail: React.FC = () => {
|
|
|
42
57
|
if (!pendingDecision) return;
|
|
43
58
|
|
|
44
59
|
if (pendingDecision === "select" && marketplace && !state.isLoading) {
|
|
45
|
-
const action =
|
|
60
|
+
const action = actionsList[selectedActionIndex]?.id;
|
|
46
61
|
if (action === "toggle-auto-update") {
|
|
47
62
|
actions.toggleAutoUpdate(marketplace.name, !marketplace.autoUpdate);
|
|
48
63
|
} else if (action === "update") {
|
|
49
64
|
actions.updateMarketplace(marketplace.name);
|
|
50
|
-
} else {
|
|
65
|
+
} else if (action === "remove") {
|
|
51
66
|
actions.removeMarketplace(marketplace.name);
|
|
52
67
|
}
|
|
53
68
|
} else if (pendingDecision === "cancel") {
|
|
@@ -58,10 +73,10 @@ export const MarketplaceDetail: React.FC = () => {
|
|
|
58
73
|
}, [
|
|
59
74
|
pendingDecision,
|
|
60
75
|
selectedActionIndex,
|
|
76
|
+
actionsList,
|
|
61
77
|
marketplace,
|
|
62
78
|
state.isLoading,
|
|
63
79
|
actions,
|
|
64
|
-
ACTIONS,
|
|
65
80
|
]);
|
|
66
81
|
|
|
67
82
|
if (!marketplace) {
|