stashes 0.1.21 → 0.1.22

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 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: [CLAUDE_BIN, "-p", prompt, "--output-format=stream-json", "--verbose", "--dangerously-skip-permissions"],
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 chatPrompt = [
1398
- "You are helping the user explore UI design variations for their project.",
1399
- "You have access to stashes MCP tools to generate, list, show, browse, vary, and apply stashes.",
1400
- "If the user asks you to generate, create, or make variations, use the stashes_generate tool.",
1401
- "If the user asks to vary an existing stash, use the stashes_vary tool.",
1402
- "If the user asks about what a stash changed, its diff, or its contents, use stashes_show to inspect it.",
1403
- 'IMPORTANT: NEVER call stashes_apply unless the user explicitly asks to "apply" or "merge" a stash.',
1404
- 'IMPORTANT: NEVER call stashes_remove unless the user explicitly asks to "delete" or "remove" a stash.',
1405
- "Otherwise, respond conversationally about their project and stashes.",
1406
- "",
1407
- component ? `Component: ${component.name}` : "",
1408
- filePath !== "auto-detect" ? `File: ${filePath}` : "",
1409
- sourceCode ? `
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
- stashContext,
1415
- "",
1416
- `User: ${message}`
1417
- ].filter(Boolean).join(`
1450
+ stashContext,
1451
+ "",
1452
+ `User: ${message}`
1453
+ ].filter(Boolean).join(`
1418
1454
  `);
1419
- const aiProcess = startAiProcess("chat", chatPrompt, this.projectPath);
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;
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: [CLAUDE_BIN, "-p", prompt, "--output-format=stream-json", "--verbose", "--dangerously-skip-permissions"],
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 chatPrompt = [
1594
- "You are helping the user explore UI design variations for their project.",
1595
- "You have access to stashes MCP tools to generate, list, show, browse, vary, and apply stashes.",
1596
- "If the user asks you to generate, create, or make variations, use the stashes_generate tool.",
1597
- "If the user asks to vary an existing stash, use the stashes_vary tool.",
1598
- "If the user asks about what a stash changed, its diff, or its contents, use stashes_show to inspect it.",
1599
- 'IMPORTANT: NEVER call stashes_apply unless the user explicitly asks to "apply" or "merge" a stash.',
1600
- 'IMPORTANT: NEVER call stashes_remove unless the user explicitly asks to "delete" or "remove" a stash.',
1601
- "Otherwise, respond conversationally about their project and stashes.",
1602
- "",
1603
- component ? `Component: ${component.name}` : "",
1604
- filePath !== "auto-detect" ? `File: ${filePath}` : "",
1605
- sourceCode ? `
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
- stashContext,
1611
- "",
1612
- `User: ${message}`
1613
- ].filter(Boolean).join(`
1646
+ stashContext,
1647
+ "",
1648
+ `User: ${message}`
1649
+ ].filter(Boolean).join(`
1614
1650
  `);
1615
- const aiProcess = startAiProcess("chat", chatPrompt, this.projectPath);
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;