@oro.ad/nuxt-claude-devtools 1.0.6 → 1.1.0
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 +149 -13
- package/dist/client/200.html +1 -1
- package/dist/client/404.html +1 -1
- package/dist/client/_nuxt/{Dgh4EhoJ.js → BRCY8pHC.js} +1 -1
- package/dist/client/_nuxt/BVHVIm9H.js +12 -0
- package/dist/client/_nuxt/BZrcCMrf.js +1 -0
- package/dist/client/_nuxt/BbEuL4Z6.js +1 -0
- package/dist/client/_nuxt/BmjlsnUc.js +1 -0
- package/dist/client/_nuxt/D2NL8Xro.js +7 -0
- package/dist/client/_nuxt/D9qGFoJm.js +4 -0
- package/dist/client/_nuxt/DImlDIT-.js +59 -0
- package/dist/client/_nuxt/{B6Pm7LNk.js → DV075BoS.js} +1 -1
- package/dist/client/_nuxt/{BngXb2T5.js → DYNukx3V.js} +1 -1
- package/dist/client/_nuxt/Dbw96V2H.js +7 -0
- package/dist/client/_nuxt/FllXIyfS.js +8 -0
- package/dist/client/_nuxt/MarkdownContent.WwTYmYZK.css +1 -0
- package/dist/client/_nuxt/TvBJGid1.js +1 -0
- package/dist/client/_nuxt/XJ4dJUK2.js +1 -0
- package/dist/client/_nuxt/builds/latest.json +1 -1
- package/dist/client/_nuxt/builds/meta/e8ae4dbb-462d-47a2-9aa2-50bed9498ab2.json +1 -0
- package/dist/client/_nuxt/e7kgpy_n.js +4 -0
- package/dist/client/_nuxt/entry.DwDQaFYc.css +1 -0
- package/dist/client/_nuxt/error-404.2GhCpCfF.css +1 -0
- package/dist/client/_nuxt/error-500.DqdIhFrl.css +1 -0
- package/dist/client/_nuxt/index.Bomb3OYy.css +1 -0
- package/dist/client/agents/index.html +1 -0
- package/dist/client/commands/index.html +1 -0
- package/dist/client/docs/index.html +1 -0
- package/dist/client/index.html +1 -1
- package/dist/client/mcp/index.html +1 -1
- package/dist/client/skills/index.html +1 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +22 -13
- package/dist/runtime/logger.d.ts +5 -0
- package/dist/runtime/logger.js +12 -0
- package/dist/runtime/server/agents-manager.d.ts +31 -0
- package/dist/runtime/server/agents-manager.js +193 -0
- package/dist/runtime/server/claude-session.d.ts +21 -4
- package/dist/runtime/server/claude-session.js +467 -25
- package/dist/runtime/server/commands-manager.d.ts +24 -0
- package/dist/runtime/server/commands-manager.js +132 -0
- package/dist/runtime/server/docs-manager.d.ts +48 -0
- package/dist/runtime/server/docs-manager.js +189 -0
- package/dist/runtime/server/history-manager.d.ts +24 -0
- package/dist/runtime/server/history-manager.js +184 -0
- package/dist/runtime/server/plugins/socket.io.d.ts +2 -0
- package/dist/runtime/server/plugins/socket.io.js +48 -0
- package/dist/runtime/server/skills-manager.d.ts +36 -0
- package/dist/runtime/server/skills-manager.js +210 -0
- package/dist/runtime/types.d.ts +156 -0
- package/dist/runtime/types.js +0 -0
- package/package.json +17 -1
- package/dist/client/_nuxt/BWmwj9se.js +0 -1
- package/dist/client/_nuxt/CRwOrvc3.js +0 -1
- package/dist/client/_nuxt/DMBGVttU.js +0 -1
- package/dist/client/_nuxt/DYOOVyPh.js +0 -1
- package/dist/client/_nuxt/JxSLzYFz.js +0 -4
- package/dist/client/_nuxt/builds/meta/f2b44466-6d7e-4b5c-bf6b-f6c7cf9e0d59.json +0 -1
- package/dist/client/_nuxt/entry.Ci1n7Rlt.css +0 -1
- package/dist/client/_nuxt/error-404.BLrjNXsr.css +0 -1
- package/dist/client/_nuxt/error-500.DLkAwcfL.css +0 -1
|
@@ -1,38 +1,56 @@
|
|
|
1
|
-
import { createServer } from "node:http";
|
|
2
1
|
import { execSync, spawn } from "node:child_process";
|
|
3
|
-
import {
|
|
2
|
+
import { createLogger } from "../logger.js";
|
|
3
|
+
import { AgentsManager } from "./agents-manager.js";
|
|
4
|
+
import { CommandsManager } from "./commands-manager.js";
|
|
5
|
+
import { DocsManager } from "./docs-manager.js";
|
|
6
|
+
import { HistoryManager } from "./history-manager.js";
|
|
7
|
+
import { SkillsManager } from "./skills-manager.js";
|
|
8
|
+
const log = createLogger("session", { timestamp: true });
|
|
4
9
|
function getErrorMessage(error) {
|
|
5
10
|
if (error instanceof Error) return error.message;
|
|
6
11
|
return String(error);
|
|
7
12
|
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
11
|
-
console.log(`[claude-session] [${timestamp}] ${message}`, data !== void 0 ? data : "");
|
|
13
|
+
function generateId() {
|
|
14
|
+
return `msg_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
12
15
|
}
|
|
16
|
+
export const SOCKET_PATH = "/__claude_devtools_socket";
|
|
13
17
|
export class ClaudeSession {
|
|
14
18
|
config;
|
|
15
19
|
io = null;
|
|
16
|
-
httpServer = null;
|
|
17
20
|
isProcessing = false;
|
|
18
21
|
continueSession = false;
|
|
22
|
+
historyManager;
|
|
23
|
+
docsManager;
|
|
24
|
+
commandsManager;
|
|
25
|
+
agentsManager;
|
|
26
|
+
skillsManager;
|
|
27
|
+
// Claude CLI session ID (in-memory only, lost on hot-reload)
|
|
28
|
+
claudeSessionId = null;
|
|
29
|
+
// Stream parsing state
|
|
30
|
+
parseBuffer = "";
|
|
31
|
+
currentContentBlocks = [];
|
|
32
|
+
currentMessageId = "";
|
|
33
|
+
currentModel = "";
|
|
34
|
+
accumulatedText = "";
|
|
19
35
|
constructor(config) {
|
|
20
36
|
this.config = config;
|
|
37
|
+
this.historyManager = new HistoryManager(config.rootDir);
|
|
38
|
+
this.docsManager = new DocsManager(config.rootDir);
|
|
39
|
+
this.commandsManager = new CommandsManager(config.rootDir);
|
|
40
|
+
this.agentsManager = new AgentsManager(config.rootDir);
|
|
41
|
+
this.skillsManager = new SkillsManager(config.rootDir);
|
|
21
42
|
}
|
|
22
|
-
|
|
23
|
-
this.
|
|
24
|
-
|
|
25
|
-
cors: {
|
|
26
|
-
origin: "*",
|
|
27
|
-
methods: ["GET", "POST"]
|
|
28
|
-
}
|
|
29
|
-
});
|
|
43
|
+
attachSocketIO(io) {
|
|
44
|
+
this.io = io;
|
|
45
|
+
log(`Socket.IO attached, setting up event handlers`);
|
|
30
46
|
this.io.on("connection", (socket) => {
|
|
31
47
|
log("Client connected", { socketId: socket.id });
|
|
32
48
|
socket.emit("session:status", {
|
|
33
49
|
active: true,
|
|
34
50
|
processing: this.isProcessing
|
|
35
51
|
});
|
|
52
|
+
const conversation = this.historyManager.getActiveConversation();
|
|
53
|
+
socket.emit("history:loaded", conversation);
|
|
36
54
|
socket.on("message:send", (message) => {
|
|
37
55
|
log("Message received", { length: message.length, preview: message.substring(0, 100) });
|
|
38
56
|
this.sendMessage(message);
|
|
@@ -40,7 +58,37 @@ export class ClaudeSession {
|
|
|
40
58
|
socket.on("session:reset", () => {
|
|
41
59
|
log("Resetting session (new conversation)");
|
|
42
60
|
this.continueSession = false;
|
|
61
|
+
this.claudeSessionId = null;
|
|
62
|
+
const conversation2 = this.historyManager.resetSession();
|
|
43
63
|
this.io?.emit("session:status", { active: true, processing: false });
|
|
64
|
+
this.io?.emit("history:loaded", conversation2);
|
|
65
|
+
});
|
|
66
|
+
socket.on("history:load", () => {
|
|
67
|
+
const conversation2 = this.historyManager.getActiveConversation();
|
|
68
|
+
socket.emit("history:loaded", conversation2);
|
|
69
|
+
});
|
|
70
|
+
socket.on("history:list", () => {
|
|
71
|
+
const conversations = this.historyManager.getConversations();
|
|
72
|
+
socket.emit("history:list", conversations);
|
|
73
|
+
});
|
|
74
|
+
socket.on("history:switch", (id) => {
|
|
75
|
+
const conversation2 = this.historyManager.setActiveConversation(id);
|
|
76
|
+
if (conversation2) {
|
|
77
|
+
this.continueSession = false;
|
|
78
|
+
this.claudeSessionId = conversation2.claudeSessionId || null;
|
|
79
|
+
log("Switched to conversation", {
|
|
80
|
+
id: conversation2.id,
|
|
81
|
+
claudeSessionId: this.claudeSessionId,
|
|
82
|
+
messageCount: conversation2.messages.length
|
|
83
|
+
});
|
|
84
|
+
socket.emit("history:switched", conversation2);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
socket.on("history:delete", (id) => {
|
|
88
|
+
const success = this.historyManager.deleteConversation(id);
|
|
89
|
+
socket.emit("history:deleted", { id, success });
|
|
90
|
+
const conversations = this.historyManager.getConversations();
|
|
91
|
+
socket.emit("history:list", conversations);
|
|
44
92
|
});
|
|
45
93
|
socket.on("mcp:list", () => {
|
|
46
94
|
log("MCP list requested");
|
|
@@ -55,17 +103,349 @@ export class ClaudeSession {
|
|
|
55
103
|
log("MCP remove requested", data);
|
|
56
104
|
this.removeMcpServer(data, socket);
|
|
57
105
|
});
|
|
106
|
+
socket.on("docs:list", () => {
|
|
107
|
+
log("Docs list requested");
|
|
108
|
+
const files = this.docsManager.getDocFiles();
|
|
109
|
+
socket.emit("docs:list", files);
|
|
110
|
+
});
|
|
111
|
+
socket.on("docs:get", (path) => {
|
|
112
|
+
log("Docs get requested", { path });
|
|
113
|
+
const file = this.docsManager.getDocFile(path);
|
|
114
|
+
socket.emit("docs:file", file);
|
|
115
|
+
});
|
|
116
|
+
socket.on("docs:save", (data) => {
|
|
117
|
+
log("Docs save requested", { path: data.path });
|
|
118
|
+
try {
|
|
119
|
+
const file = this.docsManager.saveDocFile(data.path, data.content);
|
|
120
|
+
socket.emit("docs:saved", { success: true, file });
|
|
121
|
+
const files = this.docsManager.getDocFiles();
|
|
122
|
+
socket.emit("docs:list", files);
|
|
123
|
+
} catch (error) {
|
|
124
|
+
socket.emit("docs:saved", { success: false, error: String(error) });
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
socket.on("docs:delete", (path) => {
|
|
128
|
+
log("Docs delete requested", { path });
|
|
129
|
+
const success = this.docsManager.deleteDocFile(path);
|
|
130
|
+
socket.emit("docs:deleted", { path, success });
|
|
131
|
+
const files = this.docsManager.getDocFiles();
|
|
132
|
+
socket.emit("docs:list", files);
|
|
133
|
+
});
|
|
134
|
+
socket.on("claudemd:get", () => {
|
|
135
|
+
log("CLAUDE.md get requested");
|
|
136
|
+
const data = this.docsManager.getClaudeMd();
|
|
137
|
+
socket.emit("claudemd:data", data);
|
|
138
|
+
});
|
|
139
|
+
socket.on("claudemd:save", (content) => {
|
|
140
|
+
log("CLAUDE.md save requested");
|
|
141
|
+
try {
|
|
142
|
+
const data = this.docsManager.saveClaudeMd(content);
|
|
143
|
+
socket.emit("claudemd:saved", { success: true, ...data });
|
|
144
|
+
} catch (error) {
|
|
145
|
+
socket.emit("claudemd:saved", { success: false, error: String(error) });
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
socket.on("llms:list", () => {
|
|
149
|
+
log("LLMS list requested");
|
|
150
|
+
const sources = this.docsManager.getLlmsSources();
|
|
151
|
+
socket.emit("llms:list", sources);
|
|
152
|
+
});
|
|
153
|
+
socket.on("llms:add", (data) => {
|
|
154
|
+
log("LLMS add requested", { url: data.url });
|
|
155
|
+
try {
|
|
156
|
+
const source = this.docsManager.addLlmsSource(data.url, data.title, data.description);
|
|
157
|
+
socket.emit("llms:added", { success: true, source });
|
|
158
|
+
const sources = this.docsManager.getLlmsSources();
|
|
159
|
+
socket.emit("llms:list", sources);
|
|
160
|
+
} catch (error) {
|
|
161
|
+
socket.emit("llms:added", { success: false, error: String(error) });
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
socket.on("llms:remove", (url) => {
|
|
165
|
+
log("LLMS remove requested", { url });
|
|
166
|
+
const success = this.docsManager.removeLlmsSource(url);
|
|
167
|
+
socket.emit("llms:removed", { url, success });
|
|
168
|
+
const sources = this.docsManager.getLlmsSources();
|
|
169
|
+
socket.emit("llms:list", sources);
|
|
170
|
+
});
|
|
171
|
+
socket.on("llms:update", (data) => {
|
|
172
|
+
log("LLMS update requested", { url: data.url });
|
|
173
|
+
const source = this.docsManager.updateLlmsSource(data.url, {
|
|
174
|
+
title: data.title,
|
|
175
|
+
description: data.description
|
|
176
|
+
});
|
|
177
|
+
socket.emit("llms:updated", { success: !!source, source });
|
|
178
|
+
});
|
|
179
|
+
socket.on("commands:list", () => {
|
|
180
|
+
log("Commands list requested");
|
|
181
|
+
const commands = this.commandsManager.getCommands();
|
|
182
|
+
socket.emit("commands:list", commands);
|
|
183
|
+
});
|
|
184
|
+
socket.on("commands:get", (name) => {
|
|
185
|
+
log("Command get requested", { name });
|
|
186
|
+
const command = this.commandsManager.getCommand(name);
|
|
187
|
+
socket.emit("commands:data", command);
|
|
188
|
+
});
|
|
189
|
+
socket.on("commands:save", (data) => {
|
|
190
|
+
log("Command save requested", { name: data.name });
|
|
191
|
+
try {
|
|
192
|
+
const command = this.commandsManager.saveCommand(data.name, data.content, {
|
|
193
|
+
description: data.description,
|
|
194
|
+
allowedTools: data.allowedTools,
|
|
195
|
+
disableModelInvocation: data.disableModelInvocation
|
|
196
|
+
});
|
|
197
|
+
socket.emit("commands:saved", { success: true, command });
|
|
198
|
+
const commands = this.commandsManager.getCommands();
|
|
199
|
+
socket.emit("commands:list", commands);
|
|
200
|
+
} catch (error) {
|
|
201
|
+
socket.emit("commands:saved", { success: false, error: String(error) });
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
socket.on("commands:delete", (name) => {
|
|
205
|
+
log("Command delete requested", { name });
|
|
206
|
+
const success = this.commandsManager.deleteCommand(name);
|
|
207
|
+
socket.emit("commands:deleted", { name, success });
|
|
208
|
+
const commands = this.commandsManager.getCommands();
|
|
209
|
+
socket.emit("commands:list", commands);
|
|
210
|
+
});
|
|
211
|
+
socket.on("agents:list", () => {
|
|
212
|
+
log("Agents list requested");
|
|
213
|
+
const agents = this.agentsManager.getAgents();
|
|
214
|
+
socket.emit("agents:list", agents);
|
|
215
|
+
});
|
|
216
|
+
socket.on("agents:get", (name) => {
|
|
217
|
+
log("Agent get requested", { name });
|
|
218
|
+
const agent = this.agentsManager.getAgent(name);
|
|
219
|
+
socket.emit("agents:data", agent);
|
|
220
|
+
});
|
|
221
|
+
socket.on("agents:save", (data) => {
|
|
222
|
+
log("Agent save requested", { name: data.name });
|
|
223
|
+
try {
|
|
224
|
+
const agent = this.agentsManager.saveAgent({
|
|
225
|
+
name: data.name,
|
|
226
|
+
description: data.description,
|
|
227
|
+
prompt: data.prompt,
|
|
228
|
+
model: data.model,
|
|
229
|
+
tools: data.tools,
|
|
230
|
+
disallowedTools: data.disallowedTools,
|
|
231
|
+
permissionMode: data.permissionMode,
|
|
232
|
+
skills: data.skills
|
|
233
|
+
});
|
|
234
|
+
socket.emit("agents:saved", { success: true, agent });
|
|
235
|
+
const agents = this.agentsManager.getAgents();
|
|
236
|
+
socket.emit("agents:list", agents);
|
|
237
|
+
} catch (error) {
|
|
238
|
+
socket.emit("agents:saved", { success: false, error: String(error) });
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
socket.on("agents:delete", (name) => {
|
|
242
|
+
log("Agent delete requested", { name });
|
|
243
|
+
const success = this.agentsManager.deleteAgent(name);
|
|
244
|
+
socket.emit("agents:deleted", { name, success });
|
|
245
|
+
const agents = this.agentsManager.getAgents();
|
|
246
|
+
socket.emit("agents:list", agents);
|
|
247
|
+
});
|
|
248
|
+
socket.on("skills:list", () => {
|
|
249
|
+
log("Skills list requested");
|
|
250
|
+
const skills = this.skillsManager.getSkills();
|
|
251
|
+
socket.emit("skills:list", skills);
|
|
252
|
+
});
|
|
253
|
+
socket.on("skills:get", (name) => {
|
|
254
|
+
log("Skill get requested", { name });
|
|
255
|
+
const skill = this.skillsManager.getSkill(name);
|
|
256
|
+
socket.emit("skills:get", skill);
|
|
257
|
+
});
|
|
258
|
+
socket.on("skills:save", (data) => {
|
|
259
|
+
log("Skill save requested", { name: data.name });
|
|
260
|
+
try {
|
|
261
|
+
const skill = this.skillsManager.saveSkill({
|
|
262
|
+
name: data.name,
|
|
263
|
+
description: data.description,
|
|
264
|
+
content: data.content,
|
|
265
|
+
argumentHint: data.argumentHint,
|
|
266
|
+
disableModelInvocation: data.disableModelInvocation,
|
|
267
|
+
userInvocable: data.userInvocable,
|
|
268
|
+
allowedTools: data.allowedTools,
|
|
269
|
+
model: data.model,
|
|
270
|
+
context: data.context,
|
|
271
|
+
agent: data.agent
|
|
272
|
+
});
|
|
273
|
+
socket.emit("skills:saved", { success: true, skill });
|
|
274
|
+
const skills = this.skillsManager.getSkills();
|
|
275
|
+
socket.emit("skills:list", skills);
|
|
276
|
+
} catch (error) {
|
|
277
|
+
socket.emit("skills:saved", { success: false, error: getErrorMessage(error) });
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
socket.on("skills:names", () => {
|
|
281
|
+
log("Skill names requested");
|
|
282
|
+
const names = this.skillsManager.getSkillNames();
|
|
283
|
+
socket.emit("skills:names", names);
|
|
284
|
+
});
|
|
285
|
+
socket.on("skills:delete", (name) => {
|
|
286
|
+
log("Skill delete requested", { name });
|
|
287
|
+
const success = this.skillsManager.deleteSkill(name);
|
|
288
|
+
socket.emit("skills:deleted", { name, success });
|
|
289
|
+
const skills = this.skillsManager.getSkills();
|
|
290
|
+
socket.emit("skills:list", skills);
|
|
291
|
+
});
|
|
58
292
|
socket.on("disconnect", () => {
|
|
59
293
|
log("Client disconnected", { socketId: socket.id });
|
|
60
294
|
});
|
|
61
295
|
});
|
|
62
|
-
this.httpServer.listen(SOCKET_PORT, () => {
|
|
63
|
-
log(`Socket.IO server started on port ${SOCKET_PORT}`);
|
|
64
|
-
});
|
|
65
296
|
}
|
|
66
297
|
destroy() {
|
|
67
298
|
this.io?.close();
|
|
68
|
-
|
|
299
|
+
}
|
|
300
|
+
resetStreamState() {
|
|
301
|
+
this.parseBuffer = "";
|
|
302
|
+
this.currentContentBlocks = [];
|
|
303
|
+
this.currentMessageId = generateId();
|
|
304
|
+
this.currentModel = "";
|
|
305
|
+
this.accumulatedText = "";
|
|
306
|
+
}
|
|
307
|
+
buildSystemPrompt() {
|
|
308
|
+
const sections = [];
|
|
309
|
+
const llmsSources = this.docsManager.getLlmsSources();
|
|
310
|
+
if (llmsSources.length > 0) {
|
|
311
|
+
sections.push("=== EXTERNAL DOCUMENTATION SOURCES ===");
|
|
312
|
+
sections.push("The following llms.txt sources are configured for this project.");
|
|
313
|
+
sections.push("You can fetch these URLs to get documentation context when needed:");
|
|
314
|
+
sections.push("");
|
|
315
|
+
for (const source of llmsSources) {
|
|
316
|
+
const title = source.title || source.domain;
|
|
317
|
+
const desc = source.description ? ` - ${source.description}` : "";
|
|
318
|
+
sections.push(`- ${title}${desc}`);
|
|
319
|
+
sections.push(` URL: ${source.url}`);
|
|
320
|
+
}
|
|
321
|
+
sections.push("");
|
|
322
|
+
}
|
|
323
|
+
if (this.historyManager.hasHistoryForRecovery()) {
|
|
324
|
+
const historyPrompt = this.historyManager.formatHistoryForSystemPrompt();
|
|
325
|
+
if (historyPrompt) {
|
|
326
|
+
sections.push(historyPrompt);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
if (sections.length === 0) {
|
|
330
|
+
return null;
|
|
331
|
+
}
|
|
332
|
+
return sections.join("\n");
|
|
333
|
+
}
|
|
334
|
+
parseStreamChunk(data) {
|
|
335
|
+
this.parseBuffer += data;
|
|
336
|
+
const events = [];
|
|
337
|
+
const lines = this.parseBuffer.split("\n");
|
|
338
|
+
this.parseBuffer = lines.pop() || "";
|
|
339
|
+
for (const line of lines) {
|
|
340
|
+
if (line.trim()) {
|
|
341
|
+
try {
|
|
342
|
+
const event = JSON.parse(line);
|
|
343
|
+
events.push(event);
|
|
344
|
+
} catch (e) {
|
|
345
|
+
log("Failed to parse stream event", { line: line.substring(0, 100), error: e });
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return events;
|
|
350
|
+
}
|
|
351
|
+
processStreamEvent(event) {
|
|
352
|
+
switch (event.type) {
|
|
353
|
+
case "system":
|
|
354
|
+
log("System event", { subtype: event.subtype });
|
|
355
|
+
break;
|
|
356
|
+
case "assistant": {
|
|
357
|
+
const assistantEvent = event;
|
|
358
|
+
this.currentMessageId = assistantEvent.message.id;
|
|
359
|
+
this.currentModel = assistantEvent.message.model;
|
|
360
|
+
for (const block of assistantEvent.message.content) {
|
|
361
|
+
if (block.type === "text" && block.text) {
|
|
362
|
+
const textBlock = {
|
|
363
|
+
type: "text",
|
|
364
|
+
text: block.text
|
|
365
|
+
};
|
|
366
|
+
this.currentContentBlocks.push(textBlock);
|
|
367
|
+
this.accumulatedText += block.text;
|
|
368
|
+
this.io?.emit("output:chunk", block.text);
|
|
369
|
+
this.io?.emit("stream:text_delta", {
|
|
370
|
+
index: this.currentContentBlocks.length - 1,
|
|
371
|
+
text: block.text
|
|
372
|
+
});
|
|
373
|
+
} else if (block.type === "tool_use" && block.id && block.name) {
|
|
374
|
+
const toolBlock = {
|
|
375
|
+
type: "tool_use",
|
|
376
|
+
id: block.id,
|
|
377
|
+
name: block.name,
|
|
378
|
+
input: block.input || {}
|
|
379
|
+
};
|
|
380
|
+
this.currentContentBlocks.push(toolBlock);
|
|
381
|
+
this.io?.emit("stream:tool_use", {
|
|
382
|
+
id: block.id,
|
|
383
|
+
name: block.name,
|
|
384
|
+
input: block.input || {}
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
break;
|
|
389
|
+
}
|
|
390
|
+
case "tool_use": {
|
|
391
|
+
const toolEvent = event;
|
|
392
|
+
const toolBlock = {
|
|
393
|
+
type: "tool_use",
|
|
394
|
+
id: toolEvent.tool_use_id,
|
|
395
|
+
name: toolEvent.name,
|
|
396
|
+
input: toolEvent.input
|
|
397
|
+
};
|
|
398
|
+
this.currentContentBlocks.push(toolBlock);
|
|
399
|
+
this.io?.emit("stream:tool_use", {
|
|
400
|
+
id: toolEvent.tool_use_id,
|
|
401
|
+
name: toolEvent.name,
|
|
402
|
+
input: toolEvent.input
|
|
403
|
+
});
|
|
404
|
+
break;
|
|
405
|
+
}
|
|
406
|
+
case "tool_result": {
|
|
407
|
+
const resultEvent = event;
|
|
408
|
+
this.currentContentBlocks.push({
|
|
409
|
+
type: "tool_result",
|
|
410
|
+
tool_use_id: resultEvent.tool_use_id,
|
|
411
|
+
content: resultEvent.content,
|
|
412
|
+
is_error: resultEvent.is_error
|
|
413
|
+
});
|
|
414
|
+
this.io?.emit("stream:tool_result", {
|
|
415
|
+
tool_use_id: resultEvent.tool_use_id,
|
|
416
|
+
name: resultEvent.name,
|
|
417
|
+
content: resultEvent.content,
|
|
418
|
+
is_error: resultEvent.is_error
|
|
419
|
+
});
|
|
420
|
+
break;
|
|
421
|
+
}
|
|
422
|
+
case "result": {
|
|
423
|
+
const resultEvent = event;
|
|
424
|
+
log("Result event", {
|
|
425
|
+
subtype: resultEvent.subtype,
|
|
426
|
+
cost: resultEvent.cost_usd,
|
|
427
|
+
duration: resultEvent.duration_ms,
|
|
428
|
+
session_id: resultEvent.session_id
|
|
429
|
+
});
|
|
430
|
+
if (resultEvent.session_id) {
|
|
431
|
+
this.claudeSessionId = resultEvent.session_id;
|
|
432
|
+
this.historyManager.setClaudeSessionId(resultEvent.session_id);
|
|
433
|
+
log("Saved Claude session ID", { sessionId: resultEvent.session_id });
|
|
434
|
+
}
|
|
435
|
+
this.io?.emit("stream:result", {
|
|
436
|
+
subtype: resultEvent.subtype,
|
|
437
|
+
result: resultEvent.result,
|
|
438
|
+
error: resultEvent.error,
|
|
439
|
+
session_id: resultEvent.session_id,
|
|
440
|
+
cost_usd: resultEvent.cost_usd,
|
|
441
|
+
duration_ms: resultEvent.duration_ms,
|
|
442
|
+
is_error: resultEvent.is_error
|
|
443
|
+
});
|
|
444
|
+
break;
|
|
445
|
+
}
|
|
446
|
+
default:
|
|
447
|
+
log("Unknown event type", { type: event.type });
|
|
448
|
+
}
|
|
69
449
|
}
|
|
70
450
|
sendMessage(message) {
|
|
71
451
|
if (this.isProcessing) {
|
|
@@ -73,15 +453,41 @@ export class ClaudeSession {
|
|
|
73
453
|
return;
|
|
74
454
|
}
|
|
75
455
|
this.isProcessing = true;
|
|
456
|
+
this.resetStreamState();
|
|
76
457
|
this.io?.emit("session:status", { active: true, processing: true });
|
|
458
|
+
const userMessage = {
|
|
459
|
+
id: generateId(),
|
|
460
|
+
role: "user",
|
|
461
|
+
content: message,
|
|
462
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
463
|
+
};
|
|
464
|
+
this.historyManager.addMessage(userMessage);
|
|
465
|
+
this.io?.emit("stream:message_start", {
|
|
466
|
+
id: this.currentMessageId
|
|
467
|
+
});
|
|
77
468
|
const args = [
|
|
78
469
|
...this.config.args,
|
|
79
470
|
"-p",
|
|
80
471
|
message,
|
|
472
|
+
"--output-format",
|
|
473
|
+
"stream-json",
|
|
474
|
+
"--verbose",
|
|
81
475
|
"--dangerously-skip-permissions"
|
|
82
476
|
];
|
|
83
|
-
|
|
84
|
-
|
|
477
|
+
const storedSessionId = this.claudeSessionId || this.historyManager.getClaudeSessionId();
|
|
478
|
+
if (storedSessionId) {
|
|
479
|
+
args.push("--resume", storedSessionId);
|
|
480
|
+
log("Resuming Claude session", { sessionId: storedSessionId });
|
|
481
|
+
} else {
|
|
482
|
+
const systemPrompt = this.buildSystemPrompt();
|
|
483
|
+
if (systemPrompt) {
|
|
484
|
+
args.push("--system-prompt", systemPrompt);
|
|
485
|
+
log("Using system prompt with project context", {
|
|
486
|
+
promptLength: systemPrompt.length
|
|
487
|
+
});
|
|
488
|
+
} else if (this.continueSession) {
|
|
489
|
+
args.push("--continue");
|
|
490
|
+
}
|
|
85
491
|
}
|
|
86
492
|
log("Spawning Claude process", { command: this.config.command, args, cwd: this.config.rootDir });
|
|
87
493
|
const child = spawn(this.config.command, args, {
|
|
@@ -97,7 +503,10 @@ export class ClaudeSession {
|
|
|
97
503
|
child.stdout?.on("data", (data) => {
|
|
98
504
|
const chunk = data.toString();
|
|
99
505
|
log("stdout chunk", { length: chunk.length });
|
|
100
|
-
this.
|
|
506
|
+
const events = this.parseStreamChunk(chunk);
|
|
507
|
+
for (const event of events) {
|
|
508
|
+
this.processStreamEvent(event);
|
|
509
|
+
}
|
|
101
510
|
});
|
|
102
511
|
child.stderr?.on("data", (data) => {
|
|
103
512
|
const chunk = data.toString();
|
|
@@ -109,18 +518,46 @@ export class ClaudeSession {
|
|
|
109
518
|
this.isProcessing = false;
|
|
110
519
|
this.io?.emit("session:status", { active: true, processing: false });
|
|
111
520
|
});
|
|
521
|
+
child.stdin?.on("error", (error) => {
|
|
522
|
+
log("stdin error (process may have exited)", { error: error.message });
|
|
523
|
+
});
|
|
112
524
|
child.on("close", (code) => {
|
|
113
525
|
log("Process closed", { exitCode: code });
|
|
114
526
|
this.isProcessing = false;
|
|
115
527
|
if (code === 0) {
|
|
116
528
|
this.continueSession = true;
|
|
529
|
+
const assistantMessage = {
|
|
530
|
+
id: this.currentMessageId,
|
|
531
|
+
role: "assistant",
|
|
532
|
+
content: this.accumulatedText,
|
|
533
|
+
contentBlocks: this.currentContentBlocks.length > 0 ? [...this.currentContentBlocks] : void 0,
|
|
534
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
535
|
+
model: this.currentModel
|
|
536
|
+
};
|
|
537
|
+
this.historyManager.addMessage(assistantMessage);
|
|
538
|
+
this.io?.emit("stream:message_complete", {
|
|
539
|
+
id: this.currentMessageId,
|
|
540
|
+
model: this.currentModel,
|
|
541
|
+
content: this.accumulatedText,
|
|
542
|
+
contentBlocks: this.currentContentBlocks
|
|
543
|
+
});
|
|
117
544
|
this.io?.emit("output:complete");
|
|
118
545
|
} else {
|
|
119
|
-
|
|
546
|
+
const sessionIdWasUsed = this.claudeSessionId || this.historyManager.getClaudeSessionId();
|
|
547
|
+
if (sessionIdWasUsed) {
|
|
548
|
+
log("Clearing expired Claude session ID", { sessionId: sessionIdWasUsed });
|
|
549
|
+
this.claudeSessionId = null;
|
|
550
|
+
this.historyManager.setClaudeSessionId("");
|
|
551
|
+
}
|
|
552
|
+
this.io?.emit("session:error", `Process exited with code ${code}. Session may have expired - try sending the message again.`);
|
|
120
553
|
}
|
|
121
554
|
this.io?.emit("session:status", { active: true, processing: false });
|
|
122
555
|
});
|
|
123
|
-
|
|
556
|
+
try {
|
|
557
|
+
child.stdin?.end();
|
|
558
|
+
} catch (e) {
|
|
559
|
+
log("Error closing stdin", { error: e });
|
|
560
|
+
}
|
|
124
561
|
}
|
|
125
562
|
getMcpServers() {
|
|
126
563
|
const servers = [];
|
|
@@ -199,15 +636,20 @@ export class ClaudeSession {
|
|
|
199
636
|
}
|
|
200
637
|
}
|
|
201
638
|
let sessionInstance = null;
|
|
202
|
-
export function
|
|
639
|
+
export function initClaudeSession(config) {
|
|
203
640
|
if (!sessionInstance) {
|
|
204
641
|
sessionInstance = new ClaudeSession(config);
|
|
642
|
+
log("Session initialized");
|
|
205
643
|
}
|
|
206
644
|
return sessionInstance;
|
|
207
645
|
}
|
|
646
|
+
export function getClaudeSessionInstance() {
|
|
647
|
+
return sessionInstance;
|
|
648
|
+
}
|
|
208
649
|
export function destroyClaudeSession() {
|
|
209
650
|
if (sessionInstance) {
|
|
210
651
|
sessionInstance.destroy();
|
|
211
652
|
sessionInstance = null;
|
|
653
|
+
log("Session destroyed");
|
|
212
654
|
}
|
|
213
655
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface SlashCommand {
|
|
2
|
+
name: string;
|
|
3
|
+
path: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
allowedTools?: string[];
|
|
6
|
+
disableModelInvocation?: boolean;
|
|
7
|
+
content: string;
|
|
8
|
+
rawContent: string;
|
|
9
|
+
updatedAt: string;
|
|
10
|
+
}
|
|
11
|
+
export declare class CommandsManager {
|
|
12
|
+
private commandsDir;
|
|
13
|
+
constructor(projectPath: string);
|
|
14
|
+
private parseFrontmatter;
|
|
15
|
+
private buildFrontmatter;
|
|
16
|
+
getCommands(): SlashCommand[];
|
|
17
|
+
getCommand(name: string): SlashCommand | null;
|
|
18
|
+
saveCommand(name: string, content: string, options?: {
|
|
19
|
+
description?: string;
|
|
20
|
+
allowedTools?: string[];
|
|
21
|
+
disableModelInvocation?: boolean;
|
|
22
|
+
}): SlashCommand;
|
|
23
|
+
deleteCommand(name: string): boolean;
|
|
24
|
+
}
|