stashes 0.1.11 → 0.1.13
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/cli.js +213 -46
- package/dist/mcp.js +212 -45
- package/dist/web/assets/index-7ZfF6u6K.js +96 -0
- package/dist/web/assets/index-CGKcu-ru.css +1 -0
- package/dist/web/index.html +3 -3
- package/package.json +1 -1
- package/dist/web/assets/index-CGt4isEz.js +0 -62
- package/dist/web/assets/index-YiFrDapH.css +0 -1
package/dist/mcp.js
CHANGED
|
@@ -347,7 +347,7 @@ class WorktreeManager {
|
|
|
347
347
|
}
|
|
348
348
|
|
|
349
349
|
// ../core/dist/persistence.js
|
|
350
|
-
import { readFileSync, writeFileSync, mkdirSync as mkdirSync2, existsSync as existsSync3, rmSync as rmSync2 } from "fs";
|
|
350
|
+
import { readFileSync, writeFileSync, mkdirSync as mkdirSync2, existsSync as existsSync3, rmSync as rmSync2, readdirSync } from "fs";
|
|
351
351
|
import { join as join3, dirname } from "path";
|
|
352
352
|
var STASHES_DIR = ".stashes";
|
|
353
353
|
function ensureDir(dirPath) {
|
|
@@ -424,14 +424,64 @@ class PersistenceService {
|
|
|
424
424
|
const filePath = join3(this.basePath, "projects", projectId, "stashes.json");
|
|
425
425
|
writeJson(filePath, stashes);
|
|
426
426
|
}
|
|
427
|
-
|
|
428
|
-
const
|
|
429
|
-
|
|
427
|
+
listChats(projectId) {
|
|
428
|
+
const dir = join3(this.basePath, "projects", projectId, "chats");
|
|
429
|
+
if (!existsSync3(dir))
|
|
430
|
+
return [];
|
|
431
|
+
const files = readdirSync(dir).filter((f) => f.endsWith(".json"));
|
|
432
|
+
return files.map((f) => {
|
|
433
|
+
const data = readJson(join3(dir, f), null);
|
|
434
|
+
return data?.chat;
|
|
435
|
+
}).filter(Boolean).sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
436
|
+
}
|
|
437
|
+
getChat(projectId, chatId) {
|
|
438
|
+
const filePath = join3(this.basePath, "projects", projectId, "chats", `${chatId}.json`);
|
|
439
|
+
const data = readJson(filePath, null);
|
|
440
|
+
return data?.chat;
|
|
441
|
+
}
|
|
442
|
+
saveChat(chat) {
|
|
443
|
+
const filePath = join3(this.basePath, "projects", chat.projectId, "chats", `${chat.id}.json`);
|
|
444
|
+
const existing = readJson(filePath, { chat, messages: [] });
|
|
445
|
+
writeJson(filePath, { ...existing, chat });
|
|
446
|
+
}
|
|
447
|
+
deleteChat(projectId, chatId) {
|
|
448
|
+
const filePath = join3(this.basePath, "projects", projectId, "chats", `${chatId}.json`);
|
|
449
|
+
if (existsSync3(filePath)) {
|
|
450
|
+
rmSync2(filePath);
|
|
451
|
+
}
|
|
430
452
|
}
|
|
431
|
-
|
|
432
|
-
const
|
|
433
|
-
const
|
|
434
|
-
|
|
453
|
+
getChatMessages(projectId, chatId) {
|
|
454
|
+
const filePath = join3(this.basePath, "projects", projectId, "chats", `${chatId}.json`);
|
|
455
|
+
const data = readJson(filePath, { messages: [] });
|
|
456
|
+
return data.messages;
|
|
457
|
+
}
|
|
458
|
+
saveChatMessage(projectId, chatId, message) {
|
|
459
|
+
const filePath = join3(this.basePath, "projects", projectId, "chats", `${chatId}.json`);
|
|
460
|
+
const data = readJson(filePath, { chat: this.getChat(projectId, chatId), messages: [] });
|
|
461
|
+
writeJson(filePath, { ...data, messages: [...data.messages, message] });
|
|
462
|
+
}
|
|
463
|
+
migrateOldChat(projectId) {
|
|
464
|
+
const oldPath = join3(this.basePath, "projects", projectId, "chat.json");
|
|
465
|
+
if (!existsSync3(oldPath))
|
|
466
|
+
return null;
|
|
467
|
+
const messages = readJson(oldPath, []);
|
|
468
|
+
if (messages.length === 0) {
|
|
469
|
+
rmSync2(oldPath);
|
|
470
|
+
return null;
|
|
471
|
+
}
|
|
472
|
+
const chatId = `chat_${crypto.randomUUID().substring(0, 8)}`;
|
|
473
|
+
const chat = {
|
|
474
|
+
id: chatId,
|
|
475
|
+
projectId,
|
|
476
|
+
title: "Initial conversation",
|
|
477
|
+
createdAt: messages[0].createdAt,
|
|
478
|
+
updatedAt: messages[messages.length - 1].createdAt
|
|
479
|
+
};
|
|
480
|
+
const filePath = join3(this.basePath, "projects", projectId, "chats", `${chatId}.json`);
|
|
481
|
+
writeJson(filePath, { chat, messages });
|
|
482
|
+
rmSync2(oldPath);
|
|
483
|
+
logger.info("persistence", `migrated old chat.json \u2192 ${chatId}`);
|
|
484
|
+
return chatId;
|
|
435
485
|
}
|
|
436
486
|
ensureGitignore(projectPath) {
|
|
437
487
|
const gitignorePath = join3(projectPath, ".gitignore");
|
|
@@ -657,7 +707,7 @@ async function allocatePort() {
|
|
|
657
707
|
throw new Error("No available ports in range 4010-4030");
|
|
658
708
|
}
|
|
659
709
|
async function generate(opts) {
|
|
660
|
-
const { projectPath, projectId, prompt, component, count = DEFAULT_STASH_COUNT, directives = DEFAULT_DIRECTIVES, onProgress } = opts;
|
|
710
|
+
const { projectPath, projectId, chatId, prompt, component, count = DEFAULT_STASH_COUNT, directives = DEFAULT_DIRECTIVES, onProgress } = opts;
|
|
661
711
|
const worktreeManager = new WorktreeManager(projectPath);
|
|
662
712
|
const persistence = new PersistenceService(projectPath);
|
|
663
713
|
const selectedDirectives = directives.slice(0, count);
|
|
@@ -669,12 +719,17 @@ async function generate(opts) {
|
|
|
669
719
|
}
|
|
670
720
|
}
|
|
671
721
|
const completedStashes = [];
|
|
672
|
-
const
|
|
722
|
+
const existingStashes = persistence.listStashes(projectId);
|
|
723
|
+
const maxNumber = existingStashes.reduce((max, s) => Math.max(max, s.number ?? 0), 0);
|
|
724
|
+
const stashPromises = selectedDirectives.map(async (directive, idx) => {
|
|
673
725
|
const stashId = `stash_${crypto.randomUUID().substring(0, 8)}`;
|
|
726
|
+
const stashNumber = maxNumber + idx + 1;
|
|
674
727
|
const worktree = await worktreeManager.createForGeneration(stashId);
|
|
675
728
|
const stash = {
|
|
676
729
|
id: stashId,
|
|
730
|
+
number: stashNumber,
|
|
677
731
|
projectId,
|
|
732
|
+
originChatId: chatId,
|
|
678
733
|
prompt,
|
|
679
734
|
componentPath: component?.filePath,
|
|
680
735
|
branch: worktree.branch,
|
|
@@ -687,7 +742,7 @@ async function generate(opts) {
|
|
|
687
742
|
createdAt: new Date().toISOString()
|
|
688
743
|
};
|
|
689
744
|
persistence.saveStash(stash);
|
|
690
|
-
emit(onProgress, { type: "generating", stashId });
|
|
745
|
+
emit(onProgress, { type: "generating", stashId, number: stashNumber });
|
|
691
746
|
let stashPrompt;
|
|
692
747
|
if (component?.filePath) {
|
|
693
748
|
stashPrompt = buildStashPrompt({ name: component.exportName || component.filePath, filePath: component.filePath, domSelector: "" }, sourceCode, prompt, directive);
|
|
@@ -776,7 +831,7 @@ async function allocatePort2() {
|
|
|
776
831
|
throw new Error("No available ports in range 4010-4030");
|
|
777
832
|
}
|
|
778
833
|
async function vary(opts) {
|
|
779
|
-
const { projectPath, sourceStashId, prompt, onProgress } = opts;
|
|
834
|
+
const { projectPath, sourceStashId, chatId, prompt, onProgress } = opts;
|
|
780
835
|
const persistence = new PersistenceService(projectPath);
|
|
781
836
|
const worktreeManager = new WorktreeManager(projectPath);
|
|
782
837
|
let sourceStash;
|
|
@@ -792,10 +847,14 @@ async function vary(opts) {
|
|
|
792
847
|
if (!sourceStash)
|
|
793
848
|
throw new Error(`Source stash ${sourceStashId} not found`);
|
|
794
849
|
const stashId = `stash_${crypto.randomUUID().substring(0, 8)}`;
|
|
850
|
+
const existingStashes = persistence.listStashes(projectId);
|
|
851
|
+
const stashNumber = existingStashes.reduce((max, s) => Math.max(max, s.number ?? 0), 0) + 1;
|
|
795
852
|
const worktree = await worktreeManager.createForVary(stashId, sourceStash.branch);
|
|
796
853
|
const stash = {
|
|
797
854
|
id: stashId,
|
|
855
|
+
number: stashNumber,
|
|
798
856
|
projectId,
|
|
857
|
+
originChatId: chatId,
|
|
799
858
|
prompt,
|
|
800
859
|
componentPath: sourceStash.componentPath,
|
|
801
860
|
branch: worktree.branch,
|
|
@@ -808,7 +867,7 @@ async function vary(opts) {
|
|
|
808
867
|
createdAt: new Date().toISOString()
|
|
809
868
|
};
|
|
810
869
|
persistence.saveStash(stash);
|
|
811
|
-
emit2(onProgress, { type: "generating", stashId });
|
|
870
|
+
emit2(onProgress, { type: "generating", stashId, number: stashNumber });
|
|
812
871
|
const varyPrompt = `The user wants to vary the current UI. Apply this change: ${prompt}`;
|
|
813
872
|
const aiProcess = startAiProcess(stashId, varyPrompt, worktree.path);
|
|
814
873
|
try {
|
|
@@ -1052,7 +1111,7 @@ import { existsSync as existsSync8, readFileSync as readFileSync5 } from "fs";
|
|
|
1052
1111
|
|
|
1053
1112
|
// ../server/dist/routes/api.js
|
|
1054
1113
|
import { Hono } from "hono";
|
|
1055
|
-
import { join as join6 } from "path";
|
|
1114
|
+
import { join as join6, basename } from "path";
|
|
1056
1115
|
import { existsSync as existsSync6, readFileSync as readFileSync3 } from "fs";
|
|
1057
1116
|
var app = new Hono;
|
|
1058
1117
|
app.get("/health", (c) => c.json({ status: "ok", service: "stashes" }));
|
|
@@ -1084,14 +1143,54 @@ app.get("/projects/:id", (c) => {
|
|
|
1084
1143
|
if (!project)
|
|
1085
1144
|
return c.json({ error: "Project not found" }, 404);
|
|
1086
1145
|
const stashes = persistence.listStashes(project.id);
|
|
1087
|
-
const
|
|
1088
|
-
return c.json({ data: { ...project, stashes,
|
|
1146
|
+
const chats = persistence.listChats(project.id);
|
|
1147
|
+
return c.json({ data: { ...project, stashes, chats } });
|
|
1089
1148
|
});
|
|
1090
1149
|
app.delete("/projects/:id", (c) => {
|
|
1091
1150
|
const id = c.req.param("id");
|
|
1092
1151
|
getPersistence().deleteProject(id);
|
|
1093
1152
|
return c.json({ data: { deleted: id } });
|
|
1094
1153
|
});
|
|
1154
|
+
app.get("/chats", (c) => {
|
|
1155
|
+
const persistence = getPersistence();
|
|
1156
|
+
const project = ensureProject(persistence);
|
|
1157
|
+
const chats = persistence.listChats(project.id);
|
|
1158
|
+
const stashes = persistence.listStashes(project.id);
|
|
1159
|
+
return c.json({ data: { project, chats, stashCount: stashes.length } });
|
|
1160
|
+
});
|
|
1161
|
+
app.post("/chats", async (c) => {
|
|
1162
|
+
const persistence = getPersistence();
|
|
1163
|
+
const project = ensureProject(persistence);
|
|
1164
|
+
const { title } = await c.req.json();
|
|
1165
|
+
const chatCount = persistence.listChats(project.id).length;
|
|
1166
|
+
const chat = {
|
|
1167
|
+
id: `chat_${crypto.randomUUID().substring(0, 8)}`,
|
|
1168
|
+
projectId: project.id,
|
|
1169
|
+
title: title?.trim() || `Chat ${chatCount + 1}`,
|
|
1170
|
+
createdAt: new Date().toISOString(),
|
|
1171
|
+
updatedAt: new Date().toISOString()
|
|
1172
|
+
};
|
|
1173
|
+
persistence.saveChat(chat);
|
|
1174
|
+
return c.json({ data: chat }, 201);
|
|
1175
|
+
});
|
|
1176
|
+
app.get("/chats/:chatId", (c) => {
|
|
1177
|
+
const persistence = getPersistence();
|
|
1178
|
+
const project = ensureProject(persistence);
|
|
1179
|
+
const chatId = c.req.param("chatId");
|
|
1180
|
+
const chat = persistence.getChat(project.id, chatId);
|
|
1181
|
+
if (!chat)
|
|
1182
|
+
return c.json({ error: "Chat not found" }, 404);
|
|
1183
|
+
const messages = persistence.getChatMessages(project.id, chatId);
|
|
1184
|
+
const stashes = persistence.listStashes(project.id).filter((s) => s.originChatId === chatId);
|
|
1185
|
+
return c.json({ data: { ...chat, messages, stashes } });
|
|
1186
|
+
});
|
|
1187
|
+
app.delete("/chats/:chatId", (c) => {
|
|
1188
|
+
const persistence = getPersistence();
|
|
1189
|
+
const project = ensureProject(persistence);
|
|
1190
|
+
const chatId = c.req.param("chatId");
|
|
1191
|
+
persistence.deleteChat(project.id, chatId);
|
|
1192
|
+
return c.json({ data: { deleted: chatId } });
|
|
1193
|
+
});
|
|
1095
1194
|
app.get("/screenshots/:filename", (c) => {
|
|
1096
1195
|
const filename = c.req.param("filename");
|
|
1097
1196
|
const filePath = join6(serverState.projectPath, ".stashes", "screenshots", filename);
|
|
@@ -1102,6 +1201,20 @@ app.get("/screenshots/:filename", (c) => {
|
|
|
1102
1201
|
headers: { "content-type": "image/png", "cache-control": "no-cache" }
|
|
1103
1202
|
});
|
|
1104
1203
|
});
|
|
1204
|
+
function ensureProject(persistence) {
|
|
1205
|
+
const projects = persistence.listProjects();
|
|
1206
|
+
if (projects.length > 0)
|
|
1207
|
+
return projects[0];
|
|
1208
|
+
const project = {
|
|
1209
|
+
id: `proj_${crypto.randomUUID().substring(0, 8)}`,
|
|
1210
|
+
name: basename(serverState.projectPath),
|
|
1211
|
+
createdAt: new Date().toISOString(),
|
|
1212
|
+
updatedAt: new Date().toISOString()
|
|
1213
|
+
};
|
|
1214
|
+
persistence.saveProject(project);
|
|
1215
|
+
persistence.migrateOldChat(project.id);
|
|
1216
|
+
return project;
|
|
1217
|
+
}
|
|
1105
1218
|
var apiRoutes = app;
|
|
1106
1219
|
|
|
1107
1220
|
// ../server/dist/services/stash-service.js
|
|
@@ -1347,8 +1460,8 @@ class StashService {
|
|
|
1347
1460
|
});
|
|
1348
1461
|
}
|
|
1349
1462
|
}
|
|
1350
|
-
async
|
|
1351
|
-
const component = this.selectedComponent;
|
|
1463
|
+
async message(projectId, chatId, message, referenceStashIds, componentContext) {
|
|
1464
|
+
const component = componentContext ? { name: componentContext.name, filePath: this.selectedComponent?.filePath || "" } : this.selectedComponent;
|
|
1352
1465
|
let sourceCode = "";
|
|
1353
1466
|
const filePath = component?.filePath || "";
|
|
1354
1467
|
if (filePath && filePath !== "auto-detect") {
|
|
@@ -1368,8 +1481,11 @@ ${refs.join(`
|
|
|
1368
1481
|
}
|
|
1369
1482
|
}
|
|
1370
1483
|
const chatPrompt = [
|
|
1371
|
-
"
|
|
1372
|
-
"
|
|
1484
|
+
"You are helping the user explore UI design variations for their project.",
|
|
1485
|
+
"You have access to stashes MCP tools to generate, list, browse, vary, and apply stashes.",
|
|
1486
|
+
"If the user asks you to generate, create, or make variations, use the stashes_generate tool.",
|
|
1487
|
+
"If the user asks to vary an existing stash, use the stashes_vary tool.",
|
|
1488
|
+
"Otherwise, respond conversationally about their project and stashes.",
|
|
1373
1489
|
"",
|
|
1374
1490
|
component ? `Component: ${component.name}` : "",
|
|
1375
1491
|
filePath !== "auto-detect" ? `File: ${filePath}` : "",
|
|
@@ -1380,7 +1496,7 @@ ${sourceCode.substring(0, 3000)}
|
|
|
1380
1496
|
\`\`\`` : "",
|
|
1381
1497
|
stashContext,
|
|
1382
1498
|
"",
|
|
1383
|
-
`User
|
|
1499
|
+
`User: ${message}`
|
|
1384
1500
|
].filter(Boolean).join(`
|
|
1385
1501
|
`);
|
|
1386
1502
|
const aiProcess = startAiProcess("chat", chatPrompt, this.projectPath);
|
|
@@ -1389,12 +1505,60 @@ ${sourceCode.substring(0, 3000)}
|
|
|
1389
1505
|
for await (const chunk of parseClaudeStream(aiProcess.process)) {
|
|
1390
1506
|
if (chunk.type === "text") {
|
|
1391
1507
|
fullResponse += chunk.content;
|
|
1392
|
-
this.broadcast({
|
|
1508
|
+
this.broadcast({
|
|
1509
|
+
type: "ai_stream",
|
|
1510
|
+
content: chunk.content,
|
|
1511
|
+
streamType: "text",
|
|
1512
|
+
source: "chat"
|
|
1513
|
+
});
|
|
1514
|
+
} else if (chunk.type === "thinking") {
|
|
1515
|
+
this.broadcast({
|
|
1516
|
+
type: "ai_stream",
|
|
1517
|
+
content: chunk.content,
|
|
1518
|
+
streamType: "thinking",
|
|
1519
|
+
source: "chat"
|
|
1520
|
+
});
|
|
1521
|
+
} else if (chunk.type === "tool_use") {
|
|
1522
|
+
let toolName = "unknown";
|
|
1523
|
+
let toolParams = {};
|
|
1524
|
+
try {
|
|
1525
|
+
const parsed = JSON.parse(chunk.content);
|
|
1526
|
+
toolName = parsed.tool || parsed.name || "unknown";
|
|
1527
|
+
toolParams = parsed.input || parsed.params || {};
|
|
1528
|
+
} catch {}
|
|
1529
|
+
this.broadcast({
|
|
1530
|
+
type: "ai_stream",
|
|
1531
|
+
content: chunk.content,
|
|
1532
|
+
streamType: "tool_start",
|
|
1533
|
+
source: "chat",
|
|
1534
|
+
toolName,
|
|
1535
|
+
toolParams,
|
|
1536
|
+
toolStatus: "running"
|
|
1537
|
+
});
|
|
1538
|
+
} else if (chunk.type === "tool_result") {
|
|
1539
|
+
let toolName = "unknown";
|
|
1540
|
+
let toolResult = "";
|
|
1541
|
+
try {
|
|
1542
|
+
const parsed = JSON.parse(chunk.content);
|
|
1543
|
+
toolName = parsed.tool || parsed.name || "unknown";
|
|
1544
|
+
toolResult = typeof parsed.result === "string" ? parsed.result.substring(0, 200) : JSON.stringify(parsed.result).substring(0, 200);
|
|
1545
|
+
} catch {
|
|
1546
|
+
toolResult = chunk.content.substring(0, 200);
|
|
1547
|
+
}
|
|
1548
|
+
this.broadcast({
|
|
1549
|
+
type: "ai_stream",
|
|
1550
|
+
content: chunk.content,
|
|
1551
|
+
streamType: "tool_end",
|
|
1552
|
+
source: "chat",
|
|
1553
|
+
toolName,
|
|
1554
|
+
toolStatus: "completed",
|
|
1555
|
+
toolResult
|
|
1556
|
+
});
|
|
1393
1557
|
}
|
|
1394
1558
|
}
|
|
1395
1559
|
await aiProcess.process.exited;
|
|
1396
1560
|
if (fullResponse) {
|
|
1397
|
-
this.persistence.saveChatMessage(projectId, {
|
|
1561
|
+
this.persistence.saveChatMessage(projectId, chatId, {
|
|
1398
1562
|
id: crypto.randomUUID(),
|
|
1399
1563
|
role: "assistant",
|
|
1400
1564
|
content: fullResponse,
|
|
@@ -1405,7 +1569,7 @@ ${sourceCode.substring(0, 3000)}
|
|
|
1405
1569
|
} catch (err) {
|
|
1406
1570
|
this.broadcast({
|
|
1407
1571
|
type: "ai_stream",
|
|
1408
|
-
content: `
|
|
1572
|
+
content: `Error: ${err instanceof Error ? err.message : String(err)}`,
|
|
1409
1573
|
streamType: "text",
|
|
1410
1574
|
source: "chat"
|
|
1411
1575
|
});
|
|
@@ -1418,7 +1582,12 @@ ${sourceCode.substring(0, 3000)}
|
|
|
1418
1582
|
case "generating":
|
|
1419
1583
|
case "screenshotting":
|
|
1420
1584
|
case "ready":
|
|
1421
|
-
this.broadcast({
|
|
1585
|
+
this.broadcast({
|
|
1586
|
+
type: "stash:status",
|
|
1587
|
+
stashId: event.stashId,
|
|
1588
|
+
status: event.type === "ready" ? "ready" : event.type,
|
|
1589
|
+
..."number" in event ? { number: event.number } : {}
|
|
1590
|
+
});
|
|
1422
1591
|
if (event.type === "ready" && "screenshotPath" in event && event.screenshotPath) {
|
|
1423
1592
|
this.broadcast({ type: "stash:screenshot", stashId: event.stashId, url: event.screenshotPath });
|
|
1424
1593
|
}
|
|
@@ -1426,9 +1595,11 @@ ${sourceCode.substring(0, 3000)}
|
|
|
1426
1595
|
case "error":
|
|
1427
1596
|
this.broadcast({ type: "stash:error", stashId: event.stashId, error: event.error });
|
|
1428
1597
|
break;
|
|
1429
|
-
case "ai_stream":
|
|
1430
|
-
|
|
1598
|
+
case "ai_stream": {
|
|
1599
|
+
const streamType = event.streamType === "tool_use" ? "tool_start" : event.streamType;
|
|
1600
|
+
this.broadcast({ type: "ai_stream", content: event.content, streamType });
|
|
1431
1601
|
break;
|
|
1602
|
+
}
|
|
1432
1603
|
}
|
|
1433
1604
|
}
|
|
1434
1605
|
async generate(projectId, prompt, stashCount = DEFAULT_STASH_COUNT, referenceStashIds) {
|
|
@@ -1503,7 +1674,14 @@ function createWebSocketHandler(projectPath, userDevPort, appProxyPort) {
|
|
|
1503
1674
|
open(ws) {
|
|
1504
1675
|
clients.add(ws);
|
|
1505
1676
|
logger.info("ws", "client connected", { total: clients.size });
|
|
1506
|
-
|
|
1677
|
+
const project = ensureProject(persistence);
|
|
1678
|
+
ws.send(JSON.stringify({
|
|
1679
|
+
type: "server_ready",
|
|
1680
|
+
port: userDevPort,
|
|
1681
|
+
appProxyPort,
|
|
1682
|
+
projectId: project.id,
|
|
1683
|
+
projectName: project.name
|
|
1684
|
+
}));
|
|
1507
1685
|
},
|
|
1508
1686
|
async message(ws, message) {
|
|
1509
1687
|
const raw = typeof message === "string" ? message : new TextDecoder().decode(message);
|
|
@@ -1522,28 +1700,17 @@ function createWebSocketHandler(projectPath, userDevPort, appProxyPort) {
|
|
|
1522
1700
|
case "select_component":
|
|
1523
1701
|
stashService.setSelectedComponent(event.component);
|
|
1524
1702
|
break;
|
|
1525
|
-
case "
|
|
1526
|
-
persistence.saveChatMessage(event.projectId, {
|
|
1703
|
+
case "message":
|
|
1704
|
+
persistence.saveChatMessage(event.projectId, event.chatId, {
|
|
1527
1705
|
id: crypto.randomUUID(),
|
|
1528
1706
|
role: "user",
|
|
1529
1707
|
content: event.message,
|
|
1530
1708
|
type: "text",
|
|
1531
|
-
createdAt: new Date().toISOString()
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
break;
|
|
1535
|
-
case "generate":
|
|
1536
|
-
persistence.saveChatMessage(event.projectId, {
|
|
1537
|
-
id: crypto.randomUUID(),
|
|
1538
|
-
role: "user",
|
|
1539
|
-
content: event.prompt,
|
|
1540
|
-
type: "text",
|
|
1541
|
-
createdAt: new Date().toISOString()
|
|
1709
|
+
createdAt: new Date().toISOString(),
|
|
1710
|
+
referenceStashIds: event.referenceStashIds,
|
|
1711
|
+
componentContext: event.componentContext
|
|
1542
1712
|
});
|
|
1543
|
-
await stashService.
|
|
1544
|
-
break;
|
|
1545
|
-
case "vary":
|
|
1546
|
-
await stashService.vary(event.sourceStashId, event.prompt);
|
|
1713
|
+
await stashService.message(event.projectId, event.chatId, event.message, event.referenceStashIds, event.componentContext);
|
|
1547
1714
|
break;
|
|
1548
1715
|
case "interact":
|
|
1549
1716
|
await stashService.switchPreview(event.stashId, event.sortedStashIds);
|