stashes 0.1.21 → 0.1.23
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 +79 -37
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +26 -19
- package/dist/commands/update.js.map +1 -1
- package/dist/mcp.js +62 -21
- package/dist/web/assets/{index-CMBT005S.js → index-CrqSrNLl.js} +25 -25
- package/dist/web/assets/index-Yo4820v-.css +1 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/web/assets/index-CXnspw1A.css +0 -1
package/dist/cli.js
CHANGED
|
@@ -622,15 +622,20 @@ class PersistenceService {
|
|
|
622
622
|
var {spawn } = globalThis.Bun;
|
|
623
623
|
var CLAUDE_BIN = "/opt/homebrew/bin/claude";
|
|
624
624
|
var processes = new Map;
|
|
625
|
-
function startAiProcess(id, prompt, cwd) {
|
|
625
|
+
function startAiProcess(id, prompt, cwd, resumeSessionId) {
|
|
626
626
|
killAiProcess(id);
|
|
627
627
|
logger.info("claude", `spawning process: ${id}`, {
|
|
628
628
|
cwd,
|
|
629
629
|
promptLength: prompt.length,
|
|
630
|
-
promptPreview: prompt.substring(0, 100)
|
|
630
|
+
promptPreview: prompt.substring(0, 100),
|
|
631
|
+
resumeSessionId
|
|
631
632
|
});
|
|
633
|
+
const cmd = [CLAUDE_BIN, "-p", prompt, "--output-format=stream-json", "--verbose", "--dangerously-skip-permissions"];
|
|
634
|
+
if (resumeSessionId) {
|
|
635
|
+
cmd.push("--resume", resumeSessionId);
|
|
636
|
+
}
|
|
632
637
|
const proc = spawn({
|
|
633
|
-
cmd
|
|
638
|
+
cmd,
|
|
634
639
|
stdin: "ignore",
|
|
635
640
|
stdout: "pipe",
|
|
636
641
|
stderr: "pipe",
|
|
@@ -684,6 +689,9 @@ async function* parseClaudeStream(proc) {
|
|
|
684
689
|
} catch {
|
|
685
690
|
continue;
|
|
686
691
|
}
|
|
692
|
+
if (parsed.type === "system" && parsed.subtype === "init" && parsed.session_id) {
|
|
693
|
+
yield { type: "session_id", content: "", sessionId: parsed.session_id };
|
|
694
|
+
}
|
|
687
695
|
if (parsed.type === "assistant" && parsed.message) {
|
|
688
696
|
const message = parsed.message;
|
|
689
697
|
for (const block of message.content || []) {
|
|
@@ -1323,6 +1331,9 @@ class StashService {
|
|
|
1323
1331
|
broadcast;
|
|
1324
1332
|
previewPool;
|
|
1325
1333
|
selectedComponent = null;
|
|
1334
|
+
messageQueue = [];
|
|
1335
|
+
isProcessingMessage = false;
|
|
1336
|
+
chatSessions = new Map;
|
|
1326
1337
|
constructor(projectPath, worktreeManager, persistence, broadcast) {
|
|
1327
1338
|
this.projectPath = projectPath;
|
|
1328
1339
|
this.worktreeManager = worktreeManager;
|
|
@@ -1375,6 +1386,20 @@ class StashService {
|
|
|
1375
1386
|
}
|
|
1376
1387
|
}
|
|
1377
1388
|
async message(projectId, chatId, message, referenceStashIds, componentContext) {
|
|
1389
|
+
this.messageQueue.push({ projectId, chatId, message, referenceStashIds, componentContext });
|
|
1390
|
+
if (!this.isProcessingMessage) {
|
|
1391
|
+
await this.processMessageQueue();
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
async processMessageQueue() {
|
|
1395
|
+
this.isProcessingMessage = true;
|
|
1396
|
+
while (this.messageQueue.length > 0) {
|
|
1397
|
+
const msg = this.messageQueue.shift();
|
|
1398
|
+
await this.processMessage(msg.projectId, msg.chatId, msg.message, msg.referenceStashIds, msg.componentContext);
|
|
1399
|
+
}
|
|
1400
|
+
this.isProcessingMessage = false;
|
|
1401
|
+
}
|
|
1402
|
+
async processMessage(projectId, chatId, message, referenceStashIds, componentContext) {
|
|
1378
1403
|
const component = componentContext ? { name: componentContext.name, filePath: this.selectedComponent?.filePath || "" } : this.selectedComponent;
|
|
1379
1404
|
let sourceCode = "";
|
|
1380
1405
|
const filePath = component?.filePath || "";
|
|
@@ -1394,29 +1419,41 @@ ${refs.join(`
|
|
|
1394
1419
|
`)}`;
|
|
1395
1420
|
}
|
|
1396
1421
|
}
|
|
1397
|
-
const
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1422
|
+
const existingSessionId = this.chatSessions.get(chatId);
|
|
1423
|
+
let chatPrompt;
|
|
1424
|
+
if (existingSessionId) {
|
|
1425
|
+
const parts = [message];
|
|
1426
|
+
if (stashContext)
|
|
1427
|
+
parts.push(stashContext);
|
|
1428
|
+
if (component && !existingSessionId)
|
|
1429
|
+
parts.push(`Component: ${component.name}`);
|
|
1430
|
+
chatPrompt = parts.join(`
|
|
1431
|
+
`);
|
|
1432
|
+
} else {
|
|
1433
|
+
chatPrompt = [
|
|
1434
|
+
"You are helping the user explore UI design variations for their project.",
|
|
1435
|
+
"You have access to stashes MCP tools to generate, list, show, browse, vary, and apply stashes.",
|
|
1436
|
+
"If the user asks you to generate, create, or make variations, use the stashes_generate tool.",
|
|
1437
|
+
"If the user asks to vary an existing stash, use the stashes_vary tool.",
|
|
1438
|
+
"If the user asks about what a stash changed, its diff, or its contents, use stashes_show to inspect it.",
|
|
1439
|
+
'IMPORTANT: NEVER call stashes_apply unless the user explicitly asks to "apply" or "merge" a stash.',
|
|
1440
|
+
'IMPORTANT: NEVER call stashes_remove unless the user explicitly asks to "delete" or "remove" a stash.',
|
|
1441
|
+
"Otherwise, respond conversationally about their project and stashes.",
|
|
1442
|
+
"",
|
|
1443
|
+
component ? `Component: ${component.name}` : "",
|
|
1444
|
+
filePath !== "auto-detect" ? `File: ${filePath}` : "",
|
|
1445
|
+
sourceCode ? `
|
|
1410
1446
|
Source:
|
|
1411
1447
|
\`\`\`
|
|
1412
1448
|
${sourceCode.substring(0, 3000)}
|
|
1413
1449
|
\`\`\`` : "",
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1450
|
+
stashContext,
|
|
1451
|
+
"",
|
|
1452
|
+
`User: ${message}`
|
|
1453
|
+
].filter(Boolean).join(`
|
|
1418
1454
|
`);
|
|
1419
|
-
|
|
1455
|
+
}
|
|
1456
|
+
const aiProcess = startAiProcess("chat", chatPrompt, this.projectPath, existingSessionId);
|
|
1420
1457
|
let thinkingBuf = "";
|
|
1421
1458
|
let textBuf = "";
|
|
1422
1459
|
const pendingMessages = [];
|
|
@@ -1435,6 +1472,10 @@ ${sourceCode.substring(0, 3000)}
|
|
|
1435
1472
|
}
|
|
1436
1473
|
try {
|
|
1437
1474
|
for await (const chunk of parseClaudeStream(aiProcess.process)) {
|
|
1475
|
+
if (chunk.type === "session_id" && chunk.sessionId) {
|
|
1476
|
+
this.chatSessions.set(chatId, chunk.sessionId);
|
|
1477
|
+
continue;
|
|
1478
|
+
}
|
|
1438
1479
|
if (chunk.type === "text") {
|
|
1439
1480
|
flushThinking();
|
|
1440
1481
|
textBuf += chunk.content;
|
|
@@ -2523,8 +2564,8 @@ async function setupCommand(options) {
|
|
|
2523
2564
|
}
|
|
2524
2565
|
|
|
2525
2566
|
// src/commands/update.ts
|
|
2526
|
-
import {
|
|
2527
|
-
import { writeFileSync as writeFileSync3, chmodSync, readFileSync as readFileSync8 } from "fs";
|
|
2567
|
+
import { execFileSync, execSync } from "child_process";
|
|
2568
|
+
import { writeFileSync as writeFileSync3, unlinkSync, chmodSync, readFileSync as readFileSync8 } from "fs";
|
|
2528
2569
|
import { tmpdir } from "os";
|
|
2529
2570
|
import { join as join11, dirname as dirname4 } from "path";
|
|
2530
2571
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
@@ -2545,7 +2586,7 @@ function fetchLatestVersion() {
|
|
|
2545
2586
|
return null;
|
|
2546
2587
|
}
|
|
2547
2588
|
}
|
|
2548
|
-
function buildUpdateScript(
|
|
2589
|
+
function buildUpdateScript() {
|
|
2549
2590
|
return [
|
|
2550
2591
|
"#!/bin/bash",
|
|
2551
2592
|
"set -e",
|
|
@@ -2561,12 +2602,8 @@ function buildUpdateScript(scriptPath) {
|
|
|
2561
2602
|
'echo "Re-registering with AI tools..."',
|
|
2562
2603
|
"stashes setup",
|
|
2563
2604
|
"",
|
|
2564
|
-
"NEW_VERSION=$(stashes --version)",
|
|
2565
2605
|
'echo ""',
|
|
2566
|
-
'echo "\u2713
|
|
2567
|
-
"",
|
|
2568
|
-
"# Self-cleanup",
|
|
2569
|
-
`rm -f "${scriptPath}"`
|
|
2606
|
+
'echo "\u2713 Update complete!"'
|
|
2570
2607
|
].join(`
|
|
2571
2608
|
`);
|
|
2572
2609
|
}
|
|
@@ -2614,15 +2651,20 @@ async function updateCommand() {
|
|
|
2614
2651
|
s.stop(`Removed from ${configuredTools.length} tool${configuredTools.length === 1 ? "" : "s"}`);
|
|
2615
2652
|
}
|
|
2616
2653
|
const scriptPath = join11(tmpdir(), `stashes-update-${Date.now()}.sh`);
|
|
2617
|
-
writeFileSync3(scriptPath, buildUpdateScript(
|
|
2654
|
+
writeFileSync3(scriptPath, buildUpdateScript(), "utf-8");
|
|
2618
2655
|
chmodSync(scriptPath, 493);
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2656
|
+
try {
|
|
2657
|
+
execFileSync("bash", [scriptPath], { stdio: "inherit" });
|
|
2658
|
+
} catch {
|
|
2659
|
+
console.error(`
|
|
2660
|
+
Update failed. Try manually:`);
|
|
2661
|
+
console.error(" npm install -g stashes@latest && stashes setup");
|
|
2662
|
+
process.exitCode = 1;
|
|
2663
|
+
} finally {
|
|
2664
|
+
try {
|
|
2665
|
+
unlinkSync(scriptPath);
|
|
2666
|
+
} catch {}
|
|
2667
|
+
}
|
|
2626
2668
|
}
|
|
2627
2669
|
|
|
2628
2670
|
// src/index.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"AAmDA,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAmFnD"}
|
package/dist/commands/update.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { writeFileSync, chmodSync, readFileSync } from 'node:fs';
|
|
1
|
+
import { execFileSync, execSync } from 'node:child_process';
|
|
2
|
+
import { writeFileSync, unlinkSync, chmodSync, readFileSync } from 'node:fs';
|
|
3
3
|
import { tmpdir } from 'node:os';
|
|
4
4
|
import { join, dirname } from 'node:path';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
@@ -23,7 +23,7 @@ function fetchLatestVersion() {
|
|
|
23
23
|
return null;
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
|
-
function buildUpdateScript(
|
|
26
|
+
function buildUpdateScript() {
|
|
27
27
|
return [
|
|
28
28
|
'#!/bin/bash',
|
|
29
29
|
'set -e',
|
|
@@ -39,12 +39,8 @@ function buildUpdateScript(scriptPath) {
|
|
|
39
39
|
'echo "Re-registering with AI tools..."',
|
|
40
40
|
'stashes setup',
|
|
41
41
|
'',
|
|
42
|
-
'NEW_VERSION=$(stashes --version)',
|
|
43
42
|
'echo ""',
|
|
44
|
-
'echo "✓
|
|
45
|
-
'',
|
|
46
|
-
'# Self-cleanup',
|
|
47
|
-
`rm -f "${scriptPath}"`,
|
|
43
|
+
'echo "✓ Update complete!"',
|
|
48
44
|
].join('\n');
|
|
49
45
|
}
|
|
50
46
|
// ── Command ─────────────────────────────────────────────────────────────────────
|
|
@@ -98,18 +94,29 @@ export async function updateCommand() {
|
|
|
98
94
|
}
|
|
99
95
|
s.stop(`Removed from ${configuredTools.length} tool${configuredTools.length === 1 ? '' : 's'}`);
|
|
100
96
|
}
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
//
|
|
97
|
+
// Run uninstall → install → setup via a synchronous shell script.
|
|
98
|
+
// Safe on Unix: npm uninstall deletes the binary on disk, but the OS keeps
|
|
99
|
+
// the inode alive for this running process (all code is already in memory).
|
|
100
|
+
// The parent blocks until the script finishes, then exits cleanly — no
|
|
101
|
+
// shell prompt interleaving, no dangling background process.
|
|
104
102
|
const scriptPath = join(tmpdir(), `stashes-update-${Date.now()}.sh`);
|
|
105
|
-
writeFileSync(scriptPath, buildUpdateScript(
|
|
103
|
+
writeFileSync(scriptPath, buildUpdateScript(), 'utf-8');
|
|
106
104
|
chmodSync(scriptPath, 0o755);
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
105
|
+
try {
|
|
106
|
+
execFileSync('bash', [scriptPath], { stdio: 'inherit' });
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
console.error('\nUpdate failed. Try manually:');
|
|
110
|
+
console.error(' npm install -g stashes@latest && stashes setup');
|
|
111
|
+
process.exitCode = 1;
|
|
112
|
+
}
|
|
113
|
+
finally {
|
|
114
|
+
try {
|
|
115
|
+
unlinkSync(scriptPath);
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// Script may already be gone
|
|
119
|
+
}
|
|
120
|
+
}
|
|
114
121
|
}
|
|
115
122
|
//# sourceMappingURL=update.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update.js","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"update.js","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,KAAK,CAAC,MAAM,gBAAgB,CAAC;AACpC,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,oBAAoB,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE3E,mFAAmF;AAEnF,SAAS,iBAAiB;IACxB,MAAM,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACxD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC;IACpD,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,OAAiB,CAAC;AACtE,CAAC;AAED,SAAS,kBAAkB;IACzB,IAAI,CAAC;QACH,OAAO,QAAQ,CAAC,0BAA0B,EAAE;YAC1C,QAAQ,EAAE,OAAO;YACjB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;SAChC,CAAC,CAAC,IAAI,EAAE,CAAC;IACZ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,iBAAiB;IACxB,OAAO;QACL,aAAa;QACb,QAAQ;QACR,EAAE;QACF,SAAS;QACT,wCAAwC;QACxC,8CAA8C;QAC9C,EAAE;QACF,qCAAqC;QACrC,+BAA+B;QAC/B,EAAE;QACF,SAAS;QACT,wCAAwC;QACxC,eAAe;QACf,EAAE;QACF,SAAS;QACT,2BAA2B;KAC5B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,mFAAmF;AAEnF,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC;IAEpD,MAAM,cAAc,GAAG,iBAAiB,EAAE,CAAC;IAE3C,yCAAyC;IACzC,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC;IACtB,CAAC,CAAC,KAAK,CAAC,yBAAyB,CAAC,CAAC;IACnC,MAAM,aAAa,GAAG,kBAAkB,EAAE,CAAC;IAE3C,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,CAAC,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;QACtC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,mEAAmE,CAAC,CAAC;QACjF,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QAC3B,OAAO;IACT,CAAC;IAED,CAAC,CAAC,IAAI,CAAC,mBAAmB,EAAE,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;IAErD,IAAI,cAAc,KAAK,aAAa,EAAE,CAAC;QACrC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,iCAAiC,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;QAC1E,CAAC,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;QAC9B,OAAO;IACT,CAAC;IAED,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;IAExE,oBAAoB;IACpB,MAAM,aAAa,GAAG,MAAM,CAAC,CAAC,OAAO,CAAC;QACpC,OAAO,EAAE,sBAAsB;KAChC,CAAC,CAAC;IAEH,IAAI,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;QAChD,CAAC,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;QAC9B,OAAO;IACT,CAAC;IAED,+EAA+E;IAC/E,MAAM,KAAK,GAAG,oBAAoB,EAAE,CAAC;IACrC,MAAM,eAAe,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;QACzC,IAAI,CAAC;YACH,OAAO,CAAC,CAAC,MAAM,EAAE,IAAI,YAAY,CAAC,CAAC,CAAC,CAAC;QACvC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/B,CAAC,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;QACzC,KAAK,MAAM,IAAI,IAAI,eAAe,EAAE,CAAC;YACnC,IAAI,CAAC;gBACH,SAAS,CAAC,IAAI,CAAC,CAAC;YAClB,CAAC;YAAC,MAAM,CAAC;gBACP,8CAA8C;YAChD,CAAC;QACH,CAAC;QACD,CAAC,CAAC,IAAI,CACJ,gBAAgB,eAAe,CAAC,MAAM,QAAQ,eAAe,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CACxF,CAAC;IACJ,CAAC;IAED,kEAAkE;IAClE,2EAA2E;IAC3E,4EAA4E;IAC5E,uEAAuE;IACvE,6DAA6D;IAC7D,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,kBAAkB,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACrE,aAAa,CAAC,UAAU,EAAE,iBAAiB,EAAE,EAAE,OAAO,CAAC,CAAC;IACxD,SAAS,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAE7B,IAAI,CAAC;QACH,YAAY,CAAC,MAAM,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IAC3D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC;QAChD,OAAO,CAAC,KAAK,CAAC,kDAAkD,CAAC,CAAC;QAClE,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvB,CAAC;YAAS,CAAC;QACT,IAAI,CAAC;YACH,UAAU,CAAC,UAAU,CAAC,CAAC;QACzB,CAAC;QAAC,MAAM,CAAC;YACP,6BAA6B;QAC/B,CAAC;IACH,CAAC;AACH,CAAC"}
|
package/dist/mcp.js
CHANGED
|
@@ -504,15 +504,20 @@ class PersistenceService {
|
|
|
504
504
|
var {spawn } = globalThis.Bun;
|
|
505
505
|
var CLAUDE_BIN = "/opt/homebrew/bin/claude";
|
|
506
506
|
var processes = new Map;
|
|
507
|
-
function startAiProcess(id, prompt, cwd) {
|
|
507
|
+
function startAiProcess(id, prompt, cwd, resumeSessionId) {
|
|
508
508
|
killAiProcess(id);
|
|
509
509
|
logger.info("claude", `spawning process: ${id}`, {
|
|
510
510
|
cwd,
|
|
511
511
|
promptLength: prompt.length,
|
|
512
|
-
promptPreview: prompt.substring(0, 100)
|
|
512
|
+
promptPreview: prompt.substring(0, 100),
|
|
513
|
+
resumeSessionId
|
|
513
514
|
});
|
|
515
|
+
const cmd = [CLAUDE_BIN, "-p", prompt, "--output-format=stream-json", "--verbose", "--dangerously-skip-permissions"];
|
|
516
|
+
if (resumeSessionId) {
|
|
517
|
+
cmd.push("--resume", resumeSessionId);
|
|
518
|
+
}
|
|
514
519
|
const proc = spawn({
|
|
515
|
-
cmd
|
|
520
|
+
cmd,
|
|
516
521
|
stdin: "ignore",
|
|
517
522
|
stdout: "pipe",
|
|
518
523
|
stderr: "pipe",
|
|
@@ -566,6 +571,9 @@ async function* parseClaudeStream(proc) {
|
|
|
566
571
|
} catch {
|
|
567
572
|
continue;
|
|
568
573
|
}
|
|
574
|
+
if (parsed.type === "system" && parsed.subtype === "init" && parsed.session_id) {
|
|
575
|
+
yield { type: "session_id", content: "", sessionId: parsed.session_id };
|
|
576
|
+
}
|
|
569
577
|
if (parsed.type === "assistant" && parsed.message) {
|
|
570
578
|
const message = parsed.message;
|
|
571
579
|
for (const block of message.content || []) {
|
|
@@ -1519,6 +1527,9 @@ class StashService {
|
|
|
1519
1527
|
broadcast;
|
|
1520
1528
|
previewPool;
|
|
1521
1529
|
selectedComponent = null;
|
|
1530
|
+
messageQueue = [];
|
|
1531
|
+
isProcessingMessage = false;
|
|
1532
|
+
chatSessions = new Map;
|
|
1522
1533
|
constructor(projectPath, worktreeManager, persistence, broadcast) {
|
|
1523
1534
|
this.projectPath = projectPath;
|
|
1524
1535
|
this.worktreeManager = worktreeManager;
|
|
@@ -1571,6 +1582,20 @@ class StashService {
|
|
|
1571
1582
|
}
|
|
1572
1583
|
}
|
|
1573
1584
|
async message(projectId, chatId, message, referenceStashIds, componentContext) {
|
|
1585
|
+
this.messageQueue.push({ projectId, chatId, message, referenceStashIds, componentContext });
|
|
1586
|
+
if (!this.isProcessingMessage) {
|
|
1587
|
+
await this.processMessageQueue();
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
async processMessageQueue() {
|
|
1591
|
+
this.isProcessingMessage = true;
|
|
1592
|
+
while (this.messageQueue.length > 0) {
|
|
1593
|
+
const msg = this.messageQueue.shift();
|
|
1594
|
+
await this.processMessage(msg.projectId, msg.chatId, msg.message, msg.referenceStashIds, msg.componentContext);
|
|
1595
|
+
}
|
|
1596
|
+
this.isProcessingMessage = false;
|
|
1597
|
+
}
|
|
1598
|
+
async processMessage(projectId, chatId, message, referenceStashIds, componentContext) {
|
|
1574
1599
|
const component = componentContext ? { name: componentContext.name, filePath: this.selectedComponent?.filePath || "" } : this.selectedComponent;
|
|
1575
1600
|
let sourceCode = "";
|
|
1576
1601
|
const filePath = component?.filePath || "";
|
|
@@ -1590,29 +1615,41 @@ ${refs.join(`
|
|
|
1590
1615
|
`)}`;
|
|
1591
1616
|
}
|
|
1592
1617
|
}
|
|
1593
|
-
const
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1618
|
+
const existingSessionId = this.chatSessions.get(chatId);
|
|
1619
|
+
let chatPrompt;
|
|
1620
|
+
if (existingSessionId) {
|
|
1621
|
+
const parts = [message];
|
|
1622
|
+
if (stashContext)
|
|
1623
|
+
parts.push(stashContext);
|
|
1624
|
+
if (component && !existingSessionId)
|
|
1625
|
+
parts.push(`Component: ${component.name}`);
|
|
1626
|
+
chatPrompt = parts.join(`
|
|
1627
|
+
`);
|
|
1628
|
+
} else {
|
|
1629
|
+
chatPrompt = [
|
|
1630
|
+
"You are helping the user explore UI design variations for their project.",
|
|
1631
|
+
"You have access to stashes MCP tools to generate, list, show, browse, vary, and apply stashes.",
|
|
1632
|
+
"If the user asks you to generate, create, or make variations, use the stashes_generate tool.",
|
|
1633
|
+
"If the user asks to vary an existing stash, use the stashes_vary tool.",
|
|
1634
|
+
"If the user asks about what a stash changed, its diff, or its contents, use stashes_show to inspect it.",
|
|
1635
|
+
'IMPORTANT: NEVER call stashes_apply unless the user explicitly asks to "apply" or "merge" a stash.',
|
|
1636
|
+
'IMPORTANT: NEVER call stashes_remove unless the user explicitly asks to "delete" or "remove" a stash.',
|
|
1637
|
+
"Otherwise, respond conversationally about their project and stashes.",
|
|
1638
|
+
"",
|
|
1639
|
+
component ? `Component: ${component.name}` : "",
|
|
1640
|
+
filePath !== "auto-detect" ? `File: ${filePath}` : "",
|
|
1641
|
+
sourceCode ? `
|
|
1606
1642
|
Source:
|
|
1607
1643
|
\`\`\`
|
|
1608
1644
|
${sourceCode.substring(0, 3000)}
|
|
1609
1645
|
\`\`\`` : "",
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1646
|
+
stashContext,
|
|
1647
|
+
"",
|
|
1648
|
+
`User: ${message}`
|
|
1649
|
+
].filter(Boolean).join(`
|
|
1614
1650
|
`);
|
|
1615
|
-
|
|
1651
|
+
}
|
|
1652
|
+
const aiProcess = startAiProcess("chat", chatPrompt, this.projectPath, existingSessionId);
|
|
1616
1653
|
let thinkingBuf = "";
|
|
1617
1654
|
let textBuf = "";
|
|
1618
1655
|
const pendingMessages = [];
|
|
@@ -1631,6 +1668,10 @@ ${sourceCode.substring(0, 3000)}
|
|
|
1631
1668
|
}
|
|
1632
1669
|
try {
|
|
1633
1670
|
for await (const chunk of parseClaudeStream(aiProcess.process)) {
|
|
1671
|
+
if (chunk.type === "session_id" && chunk.sessionId) {
|
|
1672
|
+
this.chatSessions.set(chatId, chunk.sessionId);
|
|
1673
|
+
continue;
|
|
1674
|
+
}
|
|
1634
1675
|
if (chunk.type === "text") {
|
|
1635
1676
|
flushThinking();
|
|
1636
1677
|
textBuf += chunk.content;
|