thepopebot 1.2.76-beta.2 → 1.2.76-beta.21
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/README.md +3 -3
- package/api/CLAUDE.md +11 -4
- package/api/index.js +56 -18
- package/bin/CLAUDE.md +7 -4
- package/bin/cli.js +25 -45
- package/config/CLAUDE.md +23 -4
- package/drizzle/0021_coding_agent_workspace.sql +1 -0
- package/drizzle/0022_organic_apocalypse.sql +16 -0
- package/drizzle/0023_needy_ender_wiggin.sql +1 -0
- package/drizzle/meta/0021_snapshot.json +639 -0
- package/drizzle/meta/0022_snapshot.json +743 -0
- package/drizzle/meta/0023_snapshot.json +750 -0
- package/drizzle/meta/_journal.json +21 -0
- package/lib/CLAUDE.md +2 -2
- package/lib/actions.js +9 -1
- package/lib/ai/CLAUDE.md +72 -57
- package/lib/ai/helper-llm.js +108 -0
- package/lib/ai/index.js +308 -438
- package/lib/ai/line-mappers.js +42 -24
- package/lib/ai/scope.js +26 -0
- package/lib/ai/sdk-adapters/CLAUDE.md +114 -0
- package/lib/ai/sdk-adapters/claude-code.js +120 -8
- package/lib/ai/system-prompt.js +34 -0
- package/lib/ai/workspace-setup.js +19 -35
- package/lib/channels/CLAUDE.md +14 -4
- package/lib/channels/base.js +6 -2
- package/lib/channels/commands/index.js +42 -0
- package/lib/channels/commands/session.js +53 -0
- package/lib/channels/commands/verify.js +18 -0
- package/lib/channels/telegram.js +79 -28
- package/lib/chat/CLAUDE.md +4 -4
- package/lib/chat/actions.js +270 -49
- package/lib/chat/api.js +185 -31
- package/lib/chat/components/CLAUDE.md +6 -2
- package/lib/chat/components/chat-input.js +77 -47
- package/lib/chat/components/chat-input.jsx +77 -40
- package/lib/chat/components/chat-page.js +2 -0
- package/lib/chat/components/chat-page.jsx +3 -0
- package/lib/chat/components/chat.js +62 -14
- package/lib/chat/components/chat.jsx +68 -10
- package/lib/chat/components/code-mode-toggle.js +141 -22
- package/lib/chat/components/code-mode-toggle.jsx +129 -20
- package/lib/chat/components/containers-page.js +58 -40
- package/lib/chat/components/containers-page.jsx +64 -25
- package/lib/chat/components/crons-page.js +17 -3
- package/lib/chat/components/crons-page.jsx +34 -6
- package/lib/chat/components/index.js +2 -2
- package/lib/chat/components/message.js +18 -3
- package/lib/chat/components/message.jsx +18 -3
- package/lib/chat/components/profile-page.js +182 -4
- package/lib/chat/components/profile-page.jsx +196 -1
- package/lib/chat/components/scope-picker.js +21 -0
- package/lib/chat/components/scope-picker.jsx +27 -0
- package/lib/chat/components/settings-chat-page.js +11 -11
- package/lib/chat/components/settings-chat-page.jsx +14 -18
- package/lib/chat/components/settings-coding-agents-page.js +110 -16
- package/lib/chat/components/settings-coding-agents-page.jsx +87 -3
- package/lib/chat/components/settings-github-page.js +5 -0
- package/lib/chat/components/settings-github-page.jsx +5 -0
- package/lib/chat/components/settings-layout.js +3 -3
- package/lib/chat/components/settings-layout.jsx +3 -3
- package/lib/chat/components/settings-secrets-layout.js +1 -2
- package/lib/chat/components/settings-secrets-layout.jsx +1 -2
- package/lib/chat/components/settings-secrets-page.js +180 -75
- package/lib/chat/components/settings-secrets-page.jsx +212 -66
- package/lib/chat/components/triggers-page.js +17 -3
- package/lib/chat/components/triggers-page.jsx +34 -6
- package/lib/chat/components/ui/combobox.js +18 -2
- package/lib/chat/components/ui/combobox.jsx +17 -1
- package/lib/chat/components/ui/dropdown-menu.js +23 -2
- package/lib/chat/components/ui/dropdown-menu.jsx +27 -2
- package/lib/chat/telegram-profile.js +33 -0
- package/lib/cluster/CLAUDE.md +9 -3
- package/lib/code/CLAUDE.md +11 -3
- package/lib/code/actions.js +47 -8
- package/lib/code/terminal-view.js +31 -21
- package/lib/code/terminal-view.jsx +32 -23
- package/lib/config.js +15 -4
- package/lib/containers/CLAUDE.md +16 -6
- package/lib/db/CLAUDE.md +5 -2
- package/lib/db/chats.js +9 -17
- package/lib/db/code-workspaces.js +8 -3
- package/lib/db/config.js +0 -1
- package/lib/db/index.js +12 -0
- package/lib/db/schema.js +24 -1
- package/lib/db/user-channels.js +129 -0
- package/lib/llm-providers.js +8 -0
- package/lib/maintenance.js +31 -21
- package/lib/tools/CLAUDE.md +12 -3
- package/lib/tools/assemblyai.js +17 -0
- package/lib/tools/create-agent-job.js +12 -8
- package/lib/tools/docker.js +34 -10
- package/lib/tools/github.js +34 -0
- package/lib/tools/telegram.js +106 -0
- package/lib/utils/render-md.js +44 -18
- package/package.json +8 -8
- package/setup/CLAUDE.md +11 -5
- package/setup/lib/providers.mjs +2 -1
- package/setup/lib/targets.mjs +13 -16
- package/setup/lib/telegram.mjs +8 -69
- package/templates/.env.example +0 -7
- package/templates/.github/workflows/rebuild-event-handler.yml +1 -1
- package/templates/.gitignore.template +1 -3
- package/templates/CLAUDE.md +1 -1
- package/templates/CLAUDE.md.template +29 -7
- package/templates/agent-job/CLAUDE.md.template +5 -3
- package/templates/agent-job/CRONS.json +16 -0
- package/templates/agent-job/SYSTEM.md +16 -11
- package/templates/agents/CLAUDE.md.template +17 -17
- package/templates/coding-workspace/CLAUDE.md.template +7 -0
- package/templates/data/CLAUDE.md.template +1 -1
- package/templates/docker-compose.custom.yml +1 -0
- package/templates/docker-compose.yml +1 -0
- package/templates/event-handler/CLAUDE.md.template +79 -0
- package/templates/event-handler/TRIGGERS.json +18 -2
- package/templates/skills/CLAUDE.md.template +20 -22
- package/templates/skills/{library/agent-job-secrets → agent-job-secrets}/SKILL.md +2 -2
- package/lib/ai/agent.js +0 -65
- package/lib/ai/async-channel.js +0 -51
- package/lib/ai/model.js +0 -130
- package/lib/ai/tools.js +0 -164
- package/lib/tools/openai.js +0 -37
- package/setup/lib/telegram-verify.mjs +0 -63
- package/setup/setup-telegram.mjs +0 -260
- package/templates/agent-job/SOUL.md +0 -17
- /package/templates/{skills/active/.gitkeep → coding-workspace/SYSTEM.md} +0 -0
- /package/templates/skills/{library/agent-job-secrets → agent-job-secrets}/agent-job-secrets.js +0 -0
- /package/templates/skills/{library/playwright-cli → playwright-cli}/SKILL.md +0 -0
|
@@ -58,6 +58,8 @@ function ChatPage({ session, needsSetup, chatId }) {
|
|
|
58
58
|
const parsed = JSON.parse(msg.content);
|
|
59
59
|
if (parsed?.type === "tool-invocation") {
|
|
60
60
|
parts = [parsed];
|
|
61
|
+
} else if (parsed?.type === "error") {
|
|
62
|
+
parts = [{ type: "data-error", data: { message: parsed.message } }];
|
|
61
63
|
} else {
|
|
62
64
|
parts = [{ type: "text", text: msg.content }];
|
|
63
65
|
}
|
|
@@ -75,6 +75,9 @@ export function ChatPage({ session, needsSetup, chatId }) {
|
|
|
75
75
|
const parsed = JSON.parse(msg.content);
|
|
76
76
|
if (parsed?.type === 'tool-invocation') {
|
|
77
77
|
parts = [parsed];
|
|
78
|
+
} else if (parsed?.type === 'error') {
|
|
79
|
+
// Rehydrate error messages as data-error parts (matches live stream shape)
|
|
80
|
+
parts = [{ type: 'data-error', data: { message: parsed.message } }];
|
|
78
81
|
} else {
|
|
79
82
|
parts = [{ type: 'text', text: msg.content }];
|
|
80
83
|
}
|
|
@@ -8,10 +8,22 @@ import { ChatInput } from "./chat-input.js";
|
|
|
8
8
|
import { ChatHeader } from "./chat-header.js";
|
|
9
9
|
import { Greeting } from "./greeting.js";
|
|
10
10
|
import { RepoBranchPicker, WorkspaceBar } from "./code-mode-toggle.js";
|
|
11
|
+
import { ScopePicker } from "./scope-picker.js";
|
|
11
12
|
import { DiffViewer } from "./diff-viewer.js";
|
|
12
13
|
import { cn } from "../utils.js";
|
|
13
14
|
const fetchRepositories = () => fetch("/code/repositories").then((r) => r.json()).catch(() => []);
|
|
14
15
|
const fetchBranches = (repoFullName) => fetch(`/code/branches?repo=${encodeURIComponent(repoFullName)}`).then((r) => r.json()).catch(() => []);
|
|
16
|
+
const fetchDefaultBranch = (repoFullName) => fetch(`/code/default-branch?repo=${encodeURIComponent(repoFullName)}`).then((r) => r.json()).then(({ branch }) => branch || null).catch(() => null);
|
|
17
|
+
const fetchCreateRepository = (name) => fetch("/code/repositories/create", {
|
|
18
|
+
method: "POST",
|
|
19
|
+
headers: { "Content-Type": "application/json" },
|
|
20
|
+
body: JSON.stringify({ name })
|
|
21
|
+
}).then((r) => {
|
|
22
|
+
if (!r.ok) return r.json().then((d) => {
|
|
23
|
+
throw new Error(d.error || "Failed to create repository");
|
|
24
|
+
});
|
|
25
|
+
return r.json();
|
|
26
|
+
});
|
|
15
27
|
function Chat({ chatId, initialMessages = [], workspace = null, chatMode = null }) {
|
|
16
28
|
const [input, setInput] = useState("");
|
|
17
29
|
const [files, setFiles] = useState([]);
|
|
@@ -20,7 +32,7 @@ function Chat({ chatId, initialMessages = [], workspace = null, chatMode = null
|
|
|
20
32
|
const [codeModeType, setCodeModeType] = useState(() => {
|
|
21
33
|
if (typeof window !== "undefined") {
|
|
22
34
|
const stored = localStorage.getItem(`codeModeType:${chatId}`);
|
|
23
|
-
if (stored === "plan" || stored === "code"
|
|
35
|
+
if (stored === "plan" || stored === "code") return stored;
|
|
24
36
|
}
|
|
25
37
|
return "code";
|
|
26
38
|
});
|
|
@@ -35,13 +47,30 @@ function Chat({ chatId, initialMessages = [], workspace = null, chatMode = null
|
|
|
35
47
|
const [workspaceState, setWorkspaceState] = useState(workspace);
|
|
36
48
|
const [diffStats, setDiffStats] = useState(null);
|
|
37
49
|
const [showDiff, setShowDiff] = useState(false);
|
|
50
|
+
const [availableAgents, setAvailableAgents] = useState(null);
|
|
51
|
+
const [scope, setScope] = useState(workspace?.scope || null);
|
|
52
|
+
const [availableScopes, setAvailableScopes] = useState(null);
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
import("../actions.js").then(({ getAvailableCodingAgents }) => {
|
|
55
|
+
getAvailableCodingAgents().then((agents) => setAvailableAgents(agents)).catch(() => {
|
|
56
|
+
});
|
|
57
|
+
}).catch(() => {
|
|
58
|
+
});
|
|
59
|
+
}, []);
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
if (!codeMode) {
|
|
62
|
+
fetch("/chat/scopes").then((r) => r.json()).then((scopes) => setAvailableScopes(scopes)).catch(() => setAvailableScopes([]));
|
|
63
|
+
}
|
|
64
|
+
}, [codeMode]);
|
|
38
65
|
useEffect(() => {
|
|
39
66
|
fetch("/code/default-repo").then((res) => res.json()).then(({ repo: r }) => {
|
|
40
67
|
if (r) {
|
|
41
68
|
setDefaultRepo(r);
|
|
42
69
|
if (!workspace && !repo && !codeMode) {
|
|
43
70
|
setRepo(r);
|
|
44
|
-
|
|
71
|
+
fetchDefaultBranch(r).then((b) => {
|
|
72
|
+
if (b) setBranch(b);
|
|
73
|
+
});
|
|
45
74
|
}
|
|
46
75
|
}
|
|
47
76
|
}).catch(() => {
|
|
@@ -57,8 +86,8 @@ function Chat({ chatId, initialMessages = [], workspace = null, chatMode = null
|
|
|
57
86
|
window.location.href = `/code/${workspaceState.id}`;
|
|
58
87
|
}
|
|
59
88
|
}, [workspaceState?.containerName]);
|
|
60
|
-
const codeModeRef = useRef({ codeMode, codeModeType, repo, branch, workspaceId: workspaceState?.id });
|
|
61
|
-
codeModeRef.current = { codeMode, codeModeType, repo, branch, workspaceId: workspaceState?.id };
|
|
89
|
+
const codeModeRef = useRef({ codeMode, codeModeType, repo, branch, workspaceId: workspaceState?.id, scope });
|
|
90
|
+
codeModeRef.current = { codeMode, codeModeType, repo, branch, workspaceId: workspaceState?.id, scope };
|
|
62
91
|
const transport = useMemo(
|
|
63
92
|
() => new DefaultChatTransport({
|
|
64
93
|
api: "/stream/chat",
|
|
@@ -68,7 +97,8 @@ function Chat({ chatId, initialMessages = [], workspace = null, chatMode = null
|
|
|
68
97
|
codeModeType: codeModeRef.current.codeModeType,
|
|
69
98
|
repo: codeModeRef.current.repo,
|
|
70
99
|
branch: codeModeRef.current.branch,
|
|
71
|
-
workspaceId: codeModeRef.current.workspaceId
|
|
100
|
+
workspaceId: codeModeRef.current.workspaceId,
|
|
101
|
+
scope: codeModeRef.current.scope || void 0
|
|
72
102
|
})
|
|
73
103
|
}),
|
|
74
104
|
[chatId]
|
|
@@ -164,12 +194,13 @@ function Chat({ chatId, initialMessages = [], workspace = null, chatMode = null
|
|
|
164
194
|
}, [messages, setMessages, sendMessage]);
|
|
165
195
|
const isInteractiveActive = !!workspaceState?.containerName;
|
|
166
196
|
const [togglingMode, setTogglingMode] = useState(false);
|
|
167
|
-
const handleInteractiveToggle = useCallback(async () => {
|
|
197
|
+
const handleInteractiveToggle = useCallback(async (agentOverride) => {
|
|
168
198
|
if (!workspaceState?.id || togglingMode || isInteractiveActive) return;
|
|
169
199
|
setTogglingMode(true);
|
|
170
200
|
try {
|
|
171
201
|
const { startInteractiveMode } = await import("../../code/actions.js");
|
|
172
|
-
const
|
|
202
|
+
const agent = typeof agentOverride === "string" ? agentOverride : void 0;
|
|
203
|
+
const result = await startInteractiveMode(workspaceState.id, agent);
|
|
173
204
|
if (result.containerName) {
|
|
174
205
|
setWorkspaceState((prev) => ({ ...prev, containerName: result.containerName }));
|
|
175
206
|
}
|
|
@@ -184,8 +215,11 @@ function Chat({ chatId, initialMessages = [], workspace = null, chatMode = null
|
|
|
184
215
|
onModeChange: setCodeModeType,
|
|
185
216
|
isInteractiveActive,
|
|
186
217
|
onInteractiveToggle: handleInteractiveToggle,
|
|
187
|
-
togglingMode
|
|
218
|
+
togglingMode,
|
|
219
|
+
availableAgents,
|
|
220
|
+
hasMessages: messages.length > 0
|
|
188
221
|
};
|
|
222
|
+
const defaultPlaceholder = !codeMode && scope ? `Send message to /${scope}` : "Send a message...";
|
|
189
223
|
const handleBranchChange = useCallback((newBranch) => {
|
|
190
224
|
setBranch(newBranch);
|
|
191
225
|
if (workspaceState?.id) {
|
|
@@ -213,7 +247,7 @@ function Chat({ chatId, initialMessages = [], workspace = null, chatMode = null
|
|
|
213
247
|
}
|
|
214
248
|
return null;
|
|
215
249
|
}, [workspaceState?.id]);
|
|
216
|
-
return /* @__PURE__ */ jsxs("div", { className: "flex h-svh flex-col", children: [
|
|
250
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex h-svh flex-col overflow-hidden", children: [
|
|
217
251
|
/* @__PURE__ */ jsx(ChatHeader, { chatId }),
|
|
218
252
|
messages.length === 0 ? /* @__PURE__ */ jsx("div", { className: "flex flex-1 flex-col items-center justify-center px-2.5 md:px-6", children: /* @__PURE__ */ jsxs("div", { className: "w-full max-w-4xl", children: [
|
|
219
253
|
/* @__PURE__ */ jsx(Greeting, { codeMode }),
|
|
@@ -228,6 +262,7 @@ function Chat({ chatId, initialMessages = [], workspace = null, chatMode = null
|
|
|
228
262
|
stop,
|
|
229
263
|
files,
|
|
230
264
|
setFiles,
|
|
265
|
+
placeholder: defaultPlaceholder,
|
|
231
266
|
codeMode,
|
|
232
267
|
codeModeSettings
|
|
233
268
|
}
|
|
@@ -241,7 +276,16 @@ function Chat({ chatId, initialMessages = [], workspace = null, chatMode = null
|
|
|
241
276
|
branch,
|
|
242
277
|
onBranchChange: handleBranchChange,
|
|
243
278
|
getRepositories: fetchRepositories,
|
|
244
|
-
getBranches: fetchBranches
|
|
279
|
+
getBranches: fetchBranches,
|
|
280
|
+
createRepository: fetchCreateRepository
|
|
281
|
+
}
|
|
282
|
+
),
|
|
283
|
+
!codeMode && /* @__PURE__ */ jsx(
|
|
284
|
+
ScopePicker,
|
|
285
|
+
{
|
|
286
|
+
scope,
|
|
287
|
+
onScopeChange: setScope,
|
|
288
|
+
scopes: availableScopes
|
|
245
289
|
}
|
|
246
290
|
),
|
|
247
291
|
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center justify-center gap-3 mt-4", children: /* @__PURE__ */ jsxs(
|
|
@@ -256,7 +300,10 @@ function Chat({ chatId, initialMessages = [], workspace = null, chatMode = null
|
|
|
256
300
|
setBranch("");
|
|
257
301
|
} else if (defaultRepo) {
|
|
258
302
|
setRepo(defaultRepo);
|
|
259
|
-
setBranch("
|
|
303
|
+
setBranch("");
|
|
304
|
+
fetchDefaultBranch(defaultRepo).then((b) => {
|
|
305
|
+
if (b) setBranch(b);
|
|
306
|
+
});
|
|
260
307
|
} else {
|
|
261
308
|
setRepo("");
|
|
262
309
|
setBranch("");
|
|
@@ -319,7 +366,8 @@ function Chat({ chatId, initialMessages = [], workspace = null, chatMode = null
|
|
|
319
366
|
workspace: workspaceState,
|
|
320
367
|
diffStats,
|
|
321
368
|
onDiffStatsRefresh: handleDiffStatsRefresh,
|
|
322
|
-
onShowDiff: () => setShowDiff(true)
|
|
369
|
+
onShowDiff: () => setShowDiff(true),
|
|
370
|
+
chatMode: codeMode ? "code" : "agent"
|
|
323
371
|
}
|
|
324
372
|
) }),
|
|
325
373
|
/* @__PURE__ */ jsx(
|
|
@@ -334,7 +382,7 @@ function Chat({ chatId, initialMessages = [], workspace = null, chatMode = null
|
|
|
334
382
|
files,
|
|
335
383
|
setFiles,
|
|
336
384
|
disabled: isInteractiveActive,
|
|
337
|
-
placeholder: isInteractiveActive ? "Interactive mode is active." :
|
|
385
|
+
placeholder: isInteractiveActive ? "Interactive mode is active." : defaultPlaceholder,
|
|
338
386
|
className: workspaceState ? "rounded-t-none" : void 0,
|
|
339
387
|
codeMode,
|
|
340
388
|
codeModeSettings
|
|
@@ -381,7 +429,7 @@ function Chat({ chatId, initialMessages = [], workspace = null, chatMode = null
|
|
|
381
429
|
files,
|
|
382
430
|
setFiles,
|
|
383
431
|
disabled: isInteractiveActive,
|
|
384
|
-
placeholder: isInteractiveActive ? "Interactive mode is active." :
|
|
432
|
+
placeholder: isInteractiveActive ? "Interactive mode is active." : defaultPlaceholder,
|
|
385
433
|
className: workspaceState ? "rounded-t-none" : void 0,
|
|
386
434
|
codeMode,
|
|
387
435
|
codeModeSettings
|
|
@@ -8,6 +8,7 @@ import { ChatInput } from './chat-input.js';
|
|
|
8
8
|
import { ChatHeader } from './chat-header.js';
|
|
9
9
|
import { Greeting } from './greeting.js';
|
|
10
10
|
import { RepoBranchPicker, WorkspaceBar } from './code-mode-toggle.js';
|
|
11
|
+
import { ScopePicker } from './scope-picker.js';
|
|
11
12
|
import { DiffViewer } from './diff-viewer.js';
|
|
12
13
|
import { cn } from '../utils.js';
|
|
13
14
|
|
|
@@ -21,6 +22,22 @@ const fetchBranches = (repoFullName) =>
|
|
|
21
22
|
.then(r => r.json())
|
|
22
23
|
.catch(() => []);
|
|
23
24
|
|
|
25
|
+
const fetchDefaultBranch = (repoFullName) =>
|
|
26
|
+
fetch(`/code/default-branch?repo=${encodeURIComponent(repoFullName)}`)
|
|
27
|
+
.then(r => r.json())
|
|
28
|
+
.then(({ branch }) => branch || null)
|
|
29
|
+
.catch(() => null);
|
|
30
|
+
|
|
31
|
+
const fetchCreateRepository = (name) =>
|
|
32
|
+
fetch('/code/repositories/create', {
|
|
33
|
+
method: 'POST',
|
|
34
|
+
headers: { 'Content-Type': 'application/json' },
|
|
35
|
+
body: JSON.stringify({ name }),
|
|
36
|
+
}).then(r => {
|
|
37
|
+
if (!r.ok) return r.json().then(d => { throw new Error(d.error || 'Failed to create repository'); });
|
|
38
|
+
return r.json();
|
|
39
|
+
});
|
|
40
|
+
|
|
24
41
|
export function Chat({ chatId, initialMessages = [], workspace = null, chatMode = null }) {
|
|
25
42
|
const [input, setInput] = useState('');
|
|
26
43
|
const [files, setFiles] = useState([]);
|
|
@@ -29,7 +46,7 @@ export function Chat({ chatId, initialMessages = [], workspace = null, chatMode
|
|
|
29
46
|
const [codeModeType, setCodeModeType] = useState(() => {
|
|
30
47
|
if (typeof window !== 'undefined') {
|
|
31
48
|
const stored = localStorage.getItem(`codeModeType:${chatId}`);
|
|
32
|
-
if (stored === 'plan' || stored === 'code'
|
|
49
|
+
if (stored === 'plan' || stored === 'code') return stored;
|
|
33
50
|
}
|
|
34
51
|
return 'code';
|
|
35
52
|
});
|
|
@@ -46,6 +63,26 @@ export function Chat({ chatId, initialMessages = [], workspace = null, chatMode
|
|
|
46
63
|
const [workspaceState, setWorkspaceState] = useState(workspace);
|
|
47
64
|
const [diffStats, setDiffStats] = useState(null);
|
|
48
65
|
const [showDiff, setShowDiff] = useState(false);
|
|
66
|
+
const [availableAgents, setAvailableAgents] = useState(null);
|
|
67
|
+
const [scope, setScope] = useState(workspace?.scope || null);
|
|
68
|
+
const [availableScopes, setAvailableScopes] = useState(null);
|
|
69
|
+
|
|
70
|
+
// Load available coding agents once on mount (for the right-click agent picker)
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
import('../actions.js').then(({ getAvailableCodingAgents }) => {
|
|
73
|
+
getAvailableCodingAgents().then(agents => setAvailableAgents(agents)).catch(() => {});
|
|
74
|
+
}).catch(() => {});
|
|
75
|
+
}, []);
|
|
76
|
+
|
|
77
|
+
// Load available agent scopes for agent mode
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
if (!codeMode) {
|
|
80
|
+
fetch('/chat/scopes')
|
|
81
|
+
.then(r => r.json())
|
|
82
|
+
.then(scopes => setAvailableScopes(scopes))
|
|
83
|
+
.catch(() => setAvailableScopes([]));
|
|
84
|
+
}
|
|
85
|
+
}, [codeMode]);
|
|
49
86
|
|
|
50
87
|
// Fetch default repo for agent mode on mount
|
|
51
88
|
// Uses fetch instead of server action to avoid Next.js page revalidation
|
|
@@ -57,7 +94,7 @@ export function Chat({ chatId, initialMessages = [], workspace = null, chatMode
|
|
|
57
94
|
setDefaultRepo(r);
|
|
58
95
|
if (!workspace && !repo && !codeMode) {
|
|
59
96
|
setRepo(r);
|
|
60
|
-
setBranch(
|
|
97
|
+
fetchDefaultBranch(r).then((b) => { if (b) setBranch(b); });
|
|
61
98
|
}
|
|
62
99
|
}
|
|
63
100
|
}).catch(() => {});
|
|
@@ -75,8 +112,8 @@ export function Chat({ chatId, initialMessages = [], workspace = null, chatMode
|
|
|
75
112
|
}
|
|
76
113
|
}, [workspaceState?.containerName]);
|
|
77
114
|
|
|
78
|
-
const codeModeRef = useRef({ codeMode, codeModeType, repo, branch, workspaceId: workspaceState?.id });
|
|
79
|
-
codeModeRef.current = { codeMode, codeModeType, repo, branch, workspaceId: workspaceState?.id };
|
|
115
|
+
const codeModeRef = useRef({ codeMode, codeModeType, repo, branch, workspaceId: workspaceState?.id, scope });
|
|
116
|
+
codeModeRef.current = { codeMode, codeModeType, repo, branch, workspaceId: workspaceState?.id, scope };
|
|
80
117
|
|
|
81
118
|
const transport = useMemo(
|
|
82
119
|
() =>
|
|
@@ -89,6 +126,7 @@ export function Chat({ chatId, initialMessages = [], workspace = null, chatMode
|
|
|
89
126
|
repo: codeModeRef.current.repo,
|
|
90
127
|
branch: codeModeRef.current.branch,
|
|
91
128
|
workspaceId: codeModeRef.current.workspaceId,
|
|
129
|
+
scope: codeModeRef.current.scope || undefined,
|
|
92
130
|
}),
|
|
93
131
|
}),
|
|
94
132
|
[chatId]
|
|
@@ -211,12 +249,15 @@ export function Chat({ chatId, initialMessages = [], workspace = null, chatMode
|
|
|
211
249
|
const isInteractiveActive = !!workspaceState?.containerName;
|
|
212
250
|
const [togglingMode, setTogglingMode] = useState(false);
|
|
213
251
|
|
|
214
|
-
const handleInteractiveToggle = useCallback(async () => {
|
|
252
|
+
const handleInteractiveToggle = useCallback(async (agentOverride) => {
|
|
215
253
|
if (!workspaceState?.id || togglingMode || isInteractiveActive) return;
|
|
216
254
|
setTogglingMode(true);
|
|
217
255
|
try {
|
|
218
256
|
const { startInteractiveMode } = await import('../../code/actions.js');
|
|
219
|
-
|
|
257
|
+
// agentOverride is a string agent id when coming from the right-click picker,
|
|
258
|
+
// or undefined when coming from a plain left-click (uses global config default)
|
|
259
|
+
const agent = typeof agentOverride === 'string' ? agentOverride : undefined;
|
|
260
|
+
const result = await startInteractiveMode(workspaceState.id, agent);
|
|
220
261
|
if (result.containerName) {
|
|
221
262
|
setWorkspaceState(prev => ({ ...prev, containerName: result.containerName }));
|
|
222
263
|
}
|
|
@@ -233,8 +274,14 @@ export function Chat({ chatId, initialMessages = [], workspace = null, chatMode
|
|
|
233
274
|
isInteractiveActive,
|
|
234
275
|
onInteractiveToggle: handleInteractiveToggle,
|
|
235
276
|
togglingMode,
|
|
277
|
+
availableAgents,
|
|
278
|
+
hasMessages: messages.length > 0,
|
|
236
279
|
};
|
|
237
280
|
|
|
281
|
+
const defaultPlaceholder = !codeMode && scope
|
|
282
|
+
? `Send message to /${scope}`
|
|
283
|
+
: 'Send a message...';
|
|
284
|
+
|
|
238
285
|
const handleBranchChange = useCallback((newBranch) => {
|
|
239
286
|
setBranch(newBranch);
|
|
240
287
|
if (workspaceState?.id) {
|
|
@@ -263,7 +310,7 @@ export function Chat({ chatId, initialMessages = [], workspace = null, chatMode
|
|
|
263
310
|
}, [workspaceState?.id]);
|
|
264
311
|
|
|
265
312
|
return (
|
|
266
|
-
<div className="flex h-svh flex-col">
|
|
313
|
+
<div className="flex h-svh flex-col overflow-hidden">
|
|
267
314
|
<ChatHeader chatId={chatId} />
|
|
268
315
|
{messages.length === 0 ? (
|
|
269
316
|
<div className="flex flex-1 flex-col items-center justify-center px-2.5 md:px-6">
|
|
@@ -283,6 +330,7 @@ export function Chat({ chatId, initialMessages = [], workspace = null, chatMode
|
|
|
283
330
|
stop={stop}
|
|
284
331
|
files={files}
|
|
285
332
|
setFiles={setFiles}
|
|
333
|
+
placeholder={defaultPlaceholder}
|
|
286
334
|
codeMode={codeMode}
|
|
287
335
|
codeModeSettings={codeModeSettings}
|
|
288
336
|
/>
|
|
@@ -296,6 +344,14 @@ export function Chat({ chatId, initialMessages = [], workspace = null, chatMode
|
|
|
296
344
|
onBranchChange={handleBranchChange}
|
|
297
345
|
getRepositories={fetchRepositories}
|
|
298
346
|
getBranches={fetchBranches}
|
|
347
|
+
createRepository={fetchCreateRepository}
|
|
348
|
+
/>
|
|
349
|
+
)}
|
|
350
|
+
{!codeMode && (
|
|
351
|
+
<ScopePicker
|
|
352
|
+
scope={scope}
|
|
353
|
+
onScopeChange={setScope}
|
|
354
|
+
scopes={availableScopes}
|
|
299
355
|
/>
|
|
300
356
|
)}
|
|
301
357
|
<div className="flex flex-wrap items-center justify-center gap-3 mt-4">
|
|
@@ -310,7 +366,8 @@ export function Chat({ chatId, initialMessages = [], workspace = null, chatMode
|
|
|
310
366
|
setBranch('');
|
|
311
367
|
} else if (defaultRepo) {
|
|
312
368
|
setRepo(defaultRepo);
|
|
313
|
-
setBranch('
|
|
369
|
+
setBranch('');
|
|
370
|
+
fetchDefaultBranch(defaultRepo).then((b) => { if (b) setBranch(b); });
|
|
314
371
|
} else {
|
|
315
372
|
setRepo('');
|
|
316
373
|
setBranch('');
|
|
@@ -378,6 +435,7 @@ export function Chat({ chatId, initialMessages = [], workspace = null, chatMode
|
|
|
378
435
|
diffStats={diffStats}
|
|
379
436
|
onDiffStatsRefresh={handleDiffStatsRefresh}
|
|
380
437
|
onShowDiff={() => setShowDiff(true)}
|
|
438
|
+
chatMode={codeMode ? 'code' : 'agent'}
|
|
381
439
|
/>
|
|
382
440
|
</div>
|
|
383
441
|
)}
|
|
@@ -391,7 +449,7 @@ export function Chat({ chatId, initialMessages = [], workspace = null, chatMode
|
|
|
391
449
|
files={files}
|
|
392
450
|
setFiles={setFiles}
|
|
393
451
|
disabled={isInteractiveActive}
|
|
394
|
-
placeholder={isInteractiveActive ? 'Interactive mode is active.' :
|
|
452
|
+
placeholder={isInteractiveActive ? 'Interactive mode is active.' : defaultPlaceholder}
|
|
395
453
|
className={workspaceState ? "rounded-t-none" : undefined}
|
|
396
454
|
codeMode={codeMode}
|
|
397
455
|
codeModeSettings={codeModeSettings}
|
|
@@ -443,7 +501,7 @@ export function Chat({ chatId, initialMessages = [], workspace = null, chatMode
|
|
|
443
501
|
files={files}
|
|
444
502
|
setFiles={setFiles}
|
|
445
503
|
disabled={isInteractiveActive}
|
|
446
|
-
placeholder={isInteractiveActive ? 'Interactive mode is active.' :
|
|
504
|
+
placeholder={isInteractiveActive ? 'Interactive mode is active.' : defaultPlaceholder}
|
|
447
505
|
className={workspaceState ? "rounded-t-none" : undefined}
|
|
448
506
|
codeMode={codeMode}
|
|
449
507
|
codeModeSettings={codeModeSettings}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useState, useEffect, useCallback, useRef } from "react";
|
|
4
4
|
import { createPortal } from "react-dom";
|
|
5
|
-
import { GitBranchIcon, ChevronDownIcon, SpinnerIcon, XIcon } from "./icons.js";
|
|
5
|
+
import { GitBranchIcon, ChevronDownIcon, SpinnerIcon, XIcon, PlusIcon } from "./icons.js";
|
|
6
6
|
import { Combobox } from "./ui/combobox.js";
|
|
7
7
|
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator } from "./ui/dropdown-menu.js";
|
|
8
8
|
import { cn } from "../utils.js";
|
|
@@ -16,13 +16,15 @@ function RepoBranchPicker({
|
|
|
16
16
|
branch,
|
|
17
17
|
onBranchChange,
|
|
18
18
|
getRepositories,
|
|
19
|
-
getBranches
|
|
19
|
+
getBranches,
|
|
20
|
+
createRepository
|
|
20
21
|
}) {
|
|
21
22
|
const [repos, setRepos] = useState([]);
|
|
22
23
|
const [branches, setBranches] = useState([]);
|
|
23
24
|
const [loadingRepos, setLoadingRepos] = useState(false);
|
|
24
25
|
const [loadingBranches, setLoadingBranches] = useState(false);
|
|
25
26
|
const [reposLoaded, setReposLoaded] = useState(false);
|
|
27
|
+
const [showCreateDialog, setShowCreateDialog] = useState(false);
|
|
26
28
|
useEffect(() => {
|
|
27
29
|
setLoadingRepos(true);
|
|
28
30
|
getRepositories().then((data) => {
|
|
@@ -49,6 +51,10 @@ function RepoBranchPicker({
|
|
|
49
51
|
setLoadingBranches(false);
|
|
50
52
|
}).catch(() => setLoadingBranches(false));
|
|
51
53
|
}, [repo]);
|
|
54
|
+
const handleRepoCreated = useCallback((fullName) => {
|
|
55
|
+
setRepos((prev) => [...prev, { full_name: fullName, default_branch: "main" }]);
|
|
56
|
+
onRepoChange(fullName);
|
|
57
|
+
}, [onRepoChange]);
|
|
52
58
|
const repoOptions = repos.map((r) => ({ value: r.full_name, label: r.full_name }));
|
|
53
59
|
const branchOptions = branches.map((b) => ({ value: b.name, label: b.name }));
|
|
54
60
|
return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-center gap-3", children: [
|
|
@@ -60,7 +66,12 @@ function RepoBranchPicker({
|
|
|
60
66
|
onChange: onRepoChange,
|
|
61
67
|
placeholder: "Select repository...",
|
|
62
68
|
loading: loadingRepos,
|
|
63
|
-
highlight: !repo && !loadingRepos
|
|
69
|
+
highlight: !repo && !loadingRepos,
|
|
70
|
+
footerAction: createRepository ? {
|
|
71
|
+
icon: /* @__PURE__ */ jsx(PlusIcon, { size: 14 }),
|
|
72
|
+
label: "Create new repository...",
|
|
73
|
+
onClick: () => setShowCreateDialog(true)
|
|
74
|
+
} : void 0
|
|
64
75
|
}
|
|
65
76
|
) }),
|
|
66
77
|
/* @__PURE__ */ jsx("div", { className: cn("w-full sm:w-auto sm:min-w-[200px] sm:max-w-[200px]", !repo && "opacity-50 pointer-events-none"), children: /* @__PURE__ */ jsx(
|
|
@@ -73,9 +84,102 @@ function RepoBranchPicker({
|
|
|
73
84
|
loading: loadingBranches,
|
|
74
85
|
highlight: !!repo && !branch && !loadingBranches
|
|
75
86
|
}
|
|
76
|
-
) })
|
|
87
|
+
) }),
|
|
88
|
+
showCreateDialog && /* @__PURE__ */ jsx(
|
|
89
|
+
CreateRepoDialog,
|
|
90
|
+
{
|
|
91
|
+
onClose: () => setShowCreateDialog(false),
|
|
92
|
+
onCreate: handleRepoCreated,
|
|
93
|
+
createRepository
|
|
94
|
+
}
|
|
95
|
+
)
|
|
77
96
|
] });
|
|
78
97
|
}
|
|
98
|
+
function CreateRepoDialog({ onClose, onCreate, createRepository }) {
|
|
99
|
+
const [name, setName] = useState("");
|
|
100
|
+
const [creating, setCreating] = useState(false);
|
|
101
|
+
const [error, setError] = useState(null);
|
|
102
|
+
const inputRef = useRef(null);
|
|
103
|
+
useEffect(() => {
|
|
104
|
+
setTimeout(() => inputRef.current?.focus(), 0);
|
|
105
|
+
}, []);
|
|
106
|
+
useEffect(() => {
|
|
107
|
+
const handleEsc = (e) => {
|
|
108
|
+
if (e.key === "Escape") onClose();
|
|
109
|
+
};
|
|
110
|
+
document.addEventListener("keydown", handleEsc);
|
|
111
|
+
return () => document.removeEventListener("keydown", handleEsc);
|
|
112
|
+
}, [onClose]);
|
|
113
|
+
const handleSubmit = async (e) => {
|
|
114
|
+
e.preventDefault();
|
|
115
|
+
const trimmed = name.trim();
|
|
116
|
+
if (!trimmed || creating) return;
|
|
117
|
+
setCreating(true);
|
|
118
|
+
setError(null);
|
|
119
|
+
try {
|
|
120
|
+
const repo = await createRepository(trimmed);
|
|
121
|
+
onCreate(repo.full_name);
|
|
122
|
+
onClose();
|
|
123
|
+
} catch (err) {
|
|
124
|
+
setError(err.message || "Failed to create repository");
|
|
125
|
+
setCreating(false);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
return createPortal(
|
|
129
|
+
/* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
|
|
130
|
+
/* @__PURE__ */ jsx("div", { className: "fixed inset-0 bg-black/50", onClick: onClose }),
|
|
131
|
+
/* @__PURE__ */ jsxs(
|
|
132
|
+
"div",
|
|
133
|
+
{
|
|
134
|
+
className: "relative z-50 w-full max-w-md mx-4 rounded-lg border border-border bg-background p-6 shadow-lg",
|
|
135
|
+
onClick: (e) => e.stopPropagation(),
|
|
136
|
+
children: [
|
|
137
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-4", children: [
|
|
138
|
+
/* @__PURE__ */ jsx("h3", { className: "text-base font-semibold", children: "Create Repository" }),
|
|
139
|
+
/* @__PURE__ */ jsx("button", { onClick: onClose, className: "text-muted-foreground hover:text-foreground transition-colors", children: /* @__PURE__ */ jsx(XIcon, { size: 16 }) })
|
|
140
|
+
] }),
|
|
141
|
+
/* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, children: [
|
|
142
|
+
/* @__PURE__ */ jsx("label", { className: "block text-sm text-muted-foreground mb-1.5", children: "Repository name" }),
|
|
143
|
+
/* @__PURE__ */ jsx(
|
|
144
|
+
"input",
|
|
145
|
+
{
|
|
146
|
+
ref: inputRef,
|
|
147
|
+
type: "text",
|
|
148
|
+
value: name,
|
|
149
|
+
onChange: (e) => setName(e.target.value),
|
|
150
|
+
placeholder: "my-project",
|
|
151
|
+
className: "w-full rounded-md border border-border bg-muted px-3 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/30 focus:border-primary"
|
|
152
|
+
}
|
|
153
|
+
),
|
|
154
|
+
error && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive mt-2", children: error }),
|
|
155
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-5 flex justify-end gap-2", children: [
|
|
156
|
+
/* @__PURE__ */ jsx(
|
|
157
|
+
"button",
|
|
158
|
+
{
|
|
159
|
+
type: "button",
|
|
160
|
+
onClick: onClose,
|
|
161
|
+
className: "px-3 py-1.5 text-sm border border-border text-muted-foreground hover:text-foreground rounded-md transition-colors",
|
|
162
|
+
children: "Cancel"
|
|
163
|
+
}
|
|
164
|
+
),
|
|
165
|
+
/* @__PURE__ */ jsx(
|
|
166
|
+
"button",
|
|
167
|
+
{
|
|
168
|
+
type: "submit",
|
|
169
|
+
disabled: !name.trim() || creating,
|
|
170
|
+
className: "px-3 py-1.5 text-sm font-medium bg-foreground text-background hover:bg-foreground/90 disabled:opacity-50 rounded-md transition-colors",
|
|
171
|
+
children: creating ? "Creating..." : "Create"
|
|
172
|
+
}
|
|
173
|
+
)
|
|
174
|
+
] })
|
|
175
|
+
] })
|
|
176
|
+
]
|
|
177
|
+
}
|
|
178
|
+
)
|
|
179
|
+
] }),
|
|
180
|
+
document.body
|
|
181
|
+
);
|
|
182
|
+
}
|
|
79
183
|
function WorkspaceBar({
|
|
80
184
|
repo,
|
|
81
185
|
branch,
|
|
@@ -84,7 +188,8 @@ function WorkspaceBar({
|
|
|
84
188
|
workspace,
|
|
85
189
|
diffStats,
|
|
86
190
|
onDiffStatsRefresh,
|
|
87
|
-
onShowDiff
|
|
191
|
+
onShowDiff,
|
|
192
|
+
chatMode = "agent"
|
|
88
193
|
}) {
|
|
89
194
|
const [branches, setBranches] = useState([]);
|
|
90
195
|
const [loadingBranches, setLoadingBranches] = useState(false);
|
|
@@ -96,7 +201,7 @@ function WorkspaceBar({
|
|
|
96
201
|
repoName && /* @__PURE__ */ jsx("span", { className: "shrink-0 cursor-default hidden md:inline", title: repo, children: repoName }),
|
|
97
202
|
branch && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
98
203
|
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-muted-foreground/30 hidden md:inline", children: "/" }),
|
|
99
|
-
/* @__PURE__ */ jsx("div", { className: "
|
|
204
|
+
/* @__PURE__ */ jsx("div", { className: "min-w-0", children: /* @__PURE__ */ jsx(
|
|
100
205
|
Combobox,
|
|
101
206
|
{
|
|
102
207
|
options: branches.map((b) => ({ value: b.name, label: b.name })),
|
|
@@ -114,17 +219,17 @@ function WorkspaceBar({
|
|
|
114
219
|
}).finally(() => setLoadingBranches(false));
|
|
115
220
|
}
|
|
116
221
|
},
|
|
117
|
-
triggerClassName: "font-medium text-foreground hover:text-primary hover:bg-accent transition-colors cursor-pointer truncate text-xs rounded px-1 -mx-1",
|
|
118
|
-
triggerLabel: /* @__PURE__ */ jsx("span", {
|
|
222
|
+
triggerClassName: "inline-block max-w-[70px] md:max-w-[160px] text-left font-medium text-foreground hover:text-primary hover:bg-accent transition-colors cursor-pointer truncate text-xs rounded px-1 -mx-1 align-middle",
|
|
223
|
+
triggerLabel: /* @__PURE__ */ jsx("span", { title: branch, children: branch })
|
|
119
224
|
}
|
|
120
225
|
) })
|
|
121
226
|
] }),
|
|
122
|
-
featureBranch && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
227
|
+
featureBranch && featureBranch !== branch && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
123
228
|
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-muted-foreground/50", children: "\u2190" }),
|
|
124
229
|
/* @__PURE__ */ jsx("span", { className: "text-primary truncate min-w-0 cursor-default", title: featureBranch, children: featureBranch })
|
|
125
230
|
] })
|
|
126
231
|
] }),
|
|
127
|
-
workspace?.id && /* @__PURE__ */ jsx(WorkspaceCommandButton, { workspaceId: workspace.id, diffStats, onDiffStatsRefresh, onShowDiff })
|
|
232
|
+
workspace?.id && /* @__PURE__ */ jsx(WorkspaceCommandButton, { workspaceId: workspace.id, diffStats, onDiffStatsRefresh, onShowDiff, chatMode })
|
|
128
233
|
] });
|
|
129
234
|
}
|
|
130
235
|
function CommandOutputDialog({ title, logs, exitCode, running, onClose }) {
|
|
@@ -193,21 +298,43 @@ function CommandOutputDialog({ title, logs, exitCode, running, onClose }) {
|
|
|
193
298
|
);
|
|
194
299
|
}
|
|
195
300
|
const STORAGE_KEY = "thepopebot-workspace-command";
|
|
196
|
-
|
|
301
|
+
const FALLBACK_BY_MODE = { agent: "push", code: "create-pr" };
|
|
302
|
+
function WorkspaceCommandButton({ workspaceId, diffStats, onDiffStatsRefresh, onShowDiff, chatMode = "agent" }) {
|
|
303
|
+
const storageKey = `${STORAGE_KEY}:${chatMode}`;
|
|
197
304
|
const [selectedCommand, setSelectedCommandState] = useState(() => {
|
|
198
305
|
try {
|
|
199
|
-
return localStorage.getItem(
|
|
306
|
+
return localStorage.getItem(storageKey) || FALLBACK_BY_MODE[chatMode] || "create-pr";
|
|
200
307
|
} catch {
|
|
201
|
-
return "create-pr";
|
|
308
|
+
return FALLBACK_BY_MODE[chatMode] || "create-pr";
|
|
202
309
|
}
|
|
203
310
|
});
|
|
204
311
|
const setSelectedCommand = (cmd) => {
|
|
205
312
|
setSelectedCommandState(cmd);
|
|
206
313
|
try {
|
|
207
|
-
localStorage.setItem(
|
|
314
|
+
localStorage.setItem(storageKey, cmd);
|
|
208
315
|
} catch {
|
|
209
316
|
}
|
|
210
317
|
};
|
|
318
|
+
useEffect(() => {
|
|
319
|
+
let stored = null;
|
|
320
|
+
try {
|
|
321
|
+
stored = localStorage.getItem(storageKey);
|
|
322
|
+
} catch {
|
|
323
|
+
}
|
|
324
|
+
if (stored) return;
|
|
325
|
+
let cancelled = false;
|
|
326
|
+
import("../actions.js").then(({ getModeGitActionDefault }) => {
|
|
327
|
+
getModeGitActionDefault(chatMode).then((val) => {
|
|
328
|
+
if (cancelled || !val) return;
|
|
329
|
+
setSelectedCommandState(val);
|
|
330
|
+
}).catch(() => {
|
|
331
|
+
});
|
|
332
|
+
}).catch(() => {
|
|
333
|
+
});
|
|
334
|
+
return () => {
|
|
335
|
+
cancelled = true;
|
|
336
|
+
};
|
|
337
|
+
}, [chatMode, storageKey]);
|
|
211
338
|
const [commandRunning, setCommandRunning] = useState(false);
|
|
212
339
|
const [dialogOpen, setDialogOpen] = useState(false);
|
|
213
340
|
const [commandLogs, setCommandLogs] = useState([]);
|
|
@@ -223,14 +350,6 @@ function WorkspaceCommandButton({ workspaceId, diffStats, onDiffStatsRefresh, on
|
|
|
223
350
|
}, []);
|
|
224
351
|
const handleRun = useCallback(async () => {
|
|
225
352
|
if (commandRunning) return;
|
|
226
|
-
const fresh = await onDiffStatsRefresh?.();
|
|
227
|
-
const stats = fresh || diffStats;
|
|
228
|
-
if (!(stats?.insertions || 0) && !(stats?.deletions || 0)) {
|
|
229
|
-
setDialogOpen(true);
|
|
230
|
-
setCommandLogs([{ stream: "stderr", raw: "You have no changes.", parsed: [{ type: "text", text: "You have no changes." }] }]);
|
|
231
|
-
setCommandExitCode(1);
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
353
|
setCommandRunning(true);
|
|
235
354
|
setDialogOpen(true);
|
|
236
355
|
setCommandLogs([]);
|