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 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;
@@ -2523,8 +2564,8 @@ async function setupCommand(options) {
2523
2564
  }
2524
2565
 
2525
2566
  // src/commands/update.ts
2526
- import { spawn as spawn5, execSync } from "child_process";
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(scriptPath) {
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 Updated to stashes v${NEW_VERSION}"',
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(scriptPath), "utf-8");
2654
+ writeFileSync3(scriptPath, buildUpdateScript(), "utf-8");
2618
2655
  chmodSync(scriptPath, 493);
2619
- p2.outro("Handing off to updater...");
2620
- const child = spawn5("bash", [scriptPath], {
2621
- detached: true,
2622
- stdio: "inherit"
2623
- });
2624
- child.unref();
2625
- process.exit(0);
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":"AAuDA,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CA6EnD"}
1
+ {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"AAmDA,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAmFnD"}
@@ -1,5 +1,5 @@
1
- import { spawn, execSync } from 'node:child_process';
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(scriptPath) {
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 "✓ Updated to stashes v${NEW_VERSION}"',
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
- // Write a detached shell script to handle uninstall install → setup.
102
- // This avoids the self-destruct problem: the current Node process exits
103
- // cleanly before npm removes the binary from disk.
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(scriptPath), 'utf-8');
103
+ writeFileSync(scriptPath, buildUpdateScript(), 'utf-8');
106
104
  chmodSync(scriptPath, 0o755);
107
- p.outro('Handing off to updater...');
108
- const child = spawn('bash', [scriptPath], {
109
- detached: true,
110
- stdio: 'inherit',
111
- });
112
- child.unref();
113
- process.exit(0);
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":"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"}
1
+ {"version":3,"file":"update.js","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"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"}
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;