stashes 0.2.2 → 0.2.4

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.
Files changed (3) hide show
  1. package/dist/cli.js +115 -24
  2. package/dist/mcp.js +115 -24
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -62,13 +62,14 @@ import { existsSync as existsSync9, readFileSync as readFileSync5 } from "fs";
62
62
  // ../core/dist/generation.js
63
63
  import { readFileSync as readFileSync2, existsSync as existsSync6 } from "fs";
64
64
  import { join as join6 } from "path";
65
- var {spawn: spawn2 } = globalThis.Bun;
65
+ var {spawn: spawn3 } = globalThis.Bun;
66
66
  import simpleGit3 from "simple-git";
67
67
 
68
68
  // ../core/dist/worktree.js
69
69
  import simpleGit from "simple-git";
70
70
  import { join as join2 } from "path";
71
71
  import { existsSync as existsSync2, rmSync, symlinkSync } from "fs";
72
+ var {spawn } = globalThis.Bun;
72
73
 
73
74
  // ../core/dist/logger.js
74
75
  import { appendFileSync, mkdirSync, existsSync } from "fs";
@@ -128,6 +129,32 @@ var logger = {
128
129
  };
129
130
 
130
131
  // ../core/dist/worktree.js
132
+ async function prepareWorktreeDeps(worktreePath, projectPath) {
133
+ let pm = "npm";
134
+ if (existsSync2(join2(projectPath, "pnpm-lock.yaml")))
135
+ pm = "pnpm";
136
+ else if (existsSync2(join2(projectPath, "bun.lock")) || existsSync2(join2(projectPath, "bun.lockb")))
137
+ pm = "bun";
138
+ else if (existsSync2(join2(projectPath, "yarn.lock")))
139
+ pm = "yarn";
140
+ if (pm === "npm")
141
+ return;
142
+ const cmd = pm === "pnpm" ? ["pnpm", "install", "--frozen-lockfile", "--prefer-offline"] : pm === "bun" ? ["bun", "install", "--frozen-lockfile"] : ["yarn", "install", "--frozen-lockfile"];
143
+ logger.info("worktree", `installing deps (${pm})`, { worktreePath: worktreePath.split("/").slice(-2).join("/") });
144
+ const proc = spawn({
145
+ cmd,
146
+ cwd: worktreePath,
147
+ stdin: "ignore",
148
+ stdout: "pipe",
149
+ stderr: "pipe",
150
+ env: { ...process.env }
151
+ });
152
+ const exitCode = await proc.exited;
153
+ if (exitCode !== 0) {
154
+ const stderr = await new Response(proc.stderr).text();
155
+ logger.warn("worktree", `${pm} install exited ${exitCode}`, { stderr: stderr.substring(0, 300) });
156
+ }
157
+ }
131
158
  var PREVIEW_PORT = STASH_PORT_START;
132
159
 
133
160
  class WorktreeManager {
@@ -140,6 +167,9 @@ class WorktreeManager {
140
167
  this.git = simpleGit(projectPath);
141
168
  this.cleanupStaleWorktrees();
142
169
  }
170
+ getProjectPath() {
171
+ return this.projectPath;
172
+ }
143
173
  async cleanupStaleWorktrees() {
144
174
  const worktreesDir = join2(this.projectPath, ".stashes", "worktrees");
145
175
  if (!existsSync2(worktreesDir))
@@ -704,7 +734,7 @@ import { mkdirSync as mkdirSync4, existsSync as existsSync5 } from "fs";
704
734
  import simpleGit2 from "simple-git";
705
735
 
706
736
  // ../core/dist/screenshot.js
707
- var {spawn } = globalThis.Bun;
737
+ var {spawn: spawn2 } = globalThis.Bun;
708
738
  import { join as join4 } from "path";
709
739
  import { mkdirSync as mkdirSync3, existsSync as existsSync4 } from "fs";
710
740
  var SCREENSHOTS_DIR = ".stashes/screenshots";
@@ -743,7 +773,7 @@ async function captureScreenshot(portOrOpts, projectPath, stashId) {
743
773
  await browser.close();
744
774
  })();
745
775
  `;
746
- const proc = spawn({
776
+ const proc = spawn2({
747
777
  cmd: ["node", "-e", playwrightScript],
748
778
  stdin: "ignore",
749
779
  stdout: "pipe",
@@ -1132,8 +1162,9 @@ async function generate(opts) {
1132
1162
  const screenshotWorktree = await worktreeManager.createForGeneration(`screenshot-${stashId}`);
1133
1163
  const screenshotGit = simpleGit3(screenshotWorktree.path);
1134
1164
  await screenshotGit.checkout(["--detach", stash.branch]);
1135
- const devServer = spawn2({
1136
- cmd: ["npm", "run", "dev"],
1165
+ await prepareWorktreeDeps(screenshotWorktree.path, projectPath);
1166
+ const devServer = spawn3({
1167
+ cmd: ["npm", "run", "dev", "--", "--port", String(port)],
1137
1168
  cwd: screenshotWorktree.path,
1138
1169
  stdin: "ignore",
1139
1170
  stdout: "pipe",
@@ -1185,7 +1216,7 @@ async function generate(opts) {
1185
1216
  return completedStashes;
1186
1217
  }
1187
1218
  // ../core/dist/vary.js
1188
- var {spawn: spawn3 } = globalThis.Bun;
1219
+ var {spawn: spawn4 } = globalThis.Bun;
1189
1220
  import simpleGit4 from "simple-git";
1190
1221
  function emit2(onProgress, event) {
1191
1222
  if (onProgress)
@@ -1303,8 +1334,9 @@ ${context}` : `The user wants to vary the current UI. Apply this change: ${promp
1303
1334
  const screenshotWorktree = await worktreeManager.createForGeneration(`screenshot-${stashId}`);
1304
1335
  const screenshotGit = simpleGit4(screenshotWorktree.path);
1305
1336
  await screenshotGit.checkout(["--detach", stash.branch]);
1306
- const devServer = spawn3({
1307
- cmd: ["npm", "run", "dev"],
1337
+ await prepareWorktreeDeps(screenshotWorktree.path, projectPath);
1338
+ const devServer = spawn4({
1339
+ cmd: ["npm", "run", "dev", "--", "--port", String(port)],
1308
1340
  cwd: screenshotWorktree.path,
1309
1341
  stdin: "ignore",
1310
1342
  stdout: "pipe",
@@ -1398,7 +1430,7 @@ import { query as query2, tool, createSdkMcpServer } from "@anthropic-ai/claude-
1398
1430
  import { z } from "zod/v4";
1399
1431
 
1400
1432
  // ../server/dist/services/app-proxy.js
1401
- import { spawn as spawn4 } from "child_process";
1433
+ import { spawn as spawn5 } from "child_process";
1402
1434
  function startAppProxy(userDevPort, proxyPort, injectOverlay) {
1403
1435
  const overlayScript = injectOverlay("", userDevPort, proxyPort);
1404
1436
  const overlayEscaped = JSON.stringify(overlayScript);
@@ -1473,7 +1505,7 @@ server.listen(${proxyPort}, () => {
1473
1505
  if (process.send) process.send('ready');
1474
1506
  });
1475
1507
  `;
1476
- const child = spawn4("node", ["-e", proxyScript], {
1508
+ const child = spawn5("node", ["-e", proxyScript], {
1477
1509
  stdio: ["ignore", "inherit", "inherit", "ipc"]
1478
1510
  });
1479
1511
  child.on("error", (err) => {
@@ -1716,8 +1748,9 @@ class PreviewPool {
1716
1748
  this.usedPorts.add(proxyPort);
1717
1749
  const devPort = proxyPort + DEV_PORT_OFFSET;
1718
1750
  const worktreePath = await this.worktreeManager.createPreviewForPool(stashId);
1751
+ await prepareWorktreeDeps(worktreePath, this.worktreeManager.getProjectPath());
1719
1752
  const process2 = Bun.spawn({
1720
- cmd: ["npm", "run", "dev"],
1753
+ cmd: ["npm", "run", "dev", "--", "--port", String(devPort)],
1721
1754
  cwd: worktreePath,
1722
1755
  stdin: "ignore",
1723
1756
  stdout: "pipe",
@@ -2218,11 +2251,44 @@ ${sourceCode.substring(0, 3000)}
2218
2251
  mcpServers: {
2219
2252
  "stashes-chat": stashServer
2220
2253
  },
2254
+ disallowedTools: [
2255
+ "Write",
2256
+ "Edit",
2257
+ "Bash",
2258
+ "NotebookEdit",
2259
+ "LSP",
2260
+ "Agent",
2261
+ "TodoWrite",
2262
+ "TaskCreate",
2263
+ "TaskUpdate",
2264
+ "TaskList",
2265
+ "TaskGet",
2266
+ "Skill",
2267
+ "ToolSearch",
2268
+ "EnterPlanMode",
2269
+ "ExitPlanMode",
2270
+ "WebSearch",
2271
+ "WebFetch"
2272
+ ],
2221
2273
  permissionMode: "bypassPermissions",
2222
2274
  allowDangerouslySkipPermissions: true,
2223
2275
  includePartialMessages: true
2224
2276
  }
2225
2277
  });
2278
+ const VISIBLE_TOOLS = new Set([
2279
+ "generate_stashes",
2280
+ "vary_stash",
2281
+ "show_stash",
2282
+ "stashes_generate",
2283
+ "stashes_vary",
2284
+ "stashes_show",
2285
+ "stashes_list",
2286
+ "stashes_apply",
2287
+ "stashes_remove",
2288
+ "stashes_browse"
2289
+ ]);
2290
+ const hiddenToolIds = new Set;
2291
+ let insideHiddenTool = false;
2226
2292
  for await (const sdkMessage of queryIter) {
2227
2293
  if (sdkMessage.type === "system" && "subtype" in sdkMessage && sdkMessage.subtype === "init") {
2228
2294
  const sessionId = "session_id" in sdkMessage ? sdkMessage.session_id : "";
@@ -2236,14 +2302,16 @@ ${sourceCode.substring(0, 3000)}
2236
2302
  if (event.type === "content_block_delta") {
2237
2303
  const delta = event.delta;
2238
2304
  if (delta.type === "text_delta" && delta.text) {
2239
- flushThinking();
2240
- textBuf += delta.text;
2241
- this.broadcast({
2242
- type: "ai_stream",
2243
- content: delta.text,
2244
- streamType: "text",
2245
- source: "chat"
2246
- });
2305
+ if (!insideHiddenTool) {
2306
+ flushThinking();
2307
+ textBuf += delta.text;
2308
+ this.broadcast({
2309
+ type: "ai_stream",
2310
+ content: delta.text,
2311
+ streamType: "text",
2312
+ source: "chat"
2313
+ });
2314
+ }
2247
2315
  } else if (delta.type === "thinking_delta" && delta.thinking) {
2248
2316
  thinkingBuf += delta.thinking;
2249
2317
  this.broadcast({
@@ -2260,12 +2328,19 @@ ${sourceCode.substring(0, 3000)}
2260
2328
  const msg = sdkMessage.message;
2261
2329
  for (const block of msg.content || []) {
2262
2330
  if (block.type === "tool_use") {
2263
- flushThinking();
2264
- flushText();
2265
2331
  const toolId = block.id;
2266
2332
  const toolName = block.name;
2267
2333
  const toolParams = block.input ?? {};
2268
2334
  toolNameMap.set(toolId, toolName);
2335
+ const isVisible = VISIBLE_TOOLS.has(toolName);
2336
+ if (!isVisible) {
2337
+ hiddenToolIds.add(toolId);
2338
+ insideHiddenTool = true;
2339
+ continue;
2340
+ }
2341
+ insideHiddenTool = false;
2342
+ flushThinking();
2343
+ flushText();
2269
2344
  save({
2270
2345
  id: crypto.randomUUID(),
2271
2346
  role: "assistant",
@@ -2301,6 +2376,12 @@ ${sourceCode.substring(0, 3000)}
2301
2376
  const toolUseId = b.tool_use_id;
2302
2377
  const isError = b.is_error || false;
2303
2378
  const endToolName = toolNameMap.get(toolUseId) || "unknown";
2379
+ if (hiddenToolIds.has(toolUseId)) {
2380
+ hiddenToolIds.delete(toolUseId);
2381
+ insideHiddenTool = false;
2382
+ continue;
2383
+ }
2384
+ insideHiddenTool = false;
2304
2385
  if (endToolName.includes("generate_stashes") || endToolName.includes("stashes_generate") || endToolName.includes("vary_stash") || endToolName.includes("stashes_vary")) {
2305
2386
  this.stopStashPoll();
2306
2387
  }
@@ -2469,9 +2550,19 @@ ${sourceCode.substring(0, 3000)}
2469
2550
  case "error":
2470
2551
  this.broadcast({ type: "stash:error", stashId: event.stashId, error: event.error });
2471
2552
  break;
2472
- case "ai_stream": {
2473
- const streamType = event.streamType === "tool_use" ? "tool_start" : event.streamType;
2474
- this.broadcast({ type: "ai_stream", content: event.content, streamType });
2553
+ case "ai_stream":
2554
+ break;
2555
+ case "activity": {
2556
+ const activityEvent = {
2557
+ stashId: event.stashId,
2558
+ action: event.action,
2559
+ file: event.file,
2560
+ lines: event.lines,
2561
+ content: event.content,
2562
+ timestamp: event.timestamp
2563
+ };
2564
+ this.activityStore.append(activityEvent);
2565
+ this.broadcast({ type: "stash:activity", stashId: event.stashId, event: activityEvent });
2475
2566
  break;
2476
2567
  }
2477
2568
  }
package/dist/mcp.js CHANGED
@@ -28,7 +28,7 @@ import { z } from "zod";
28
28
  // ../core/dist/generation.js
29
29
  import { readFileSync as readFileSync2, existsSync as existsSync6 } from "fs";
30
30
  import { join as join6 } from "path";
31
- var {spawn: spawn2 } = globalThis.Bun;
31
+ var {spawn: spawn3 } = globalThis.Bun;
32
32
  import simpleGit3 from "simple-git";
33
33
  // ../shared/dist/constants/index.js
34
34
  var STASHES_PORT = 4000;
@@ -54,6 +54,7 @@ var DEFAULT_DIRECTIVES = [
54
54
  import simpleGit from "simple-git";
55
55
  import { join as join2 } from "path";
56
56
  import { existsSync as existsSync2, rmSync, symlinkSync } from "fs";
57
+ var {spawn } = globalThis.Bun;
57
58
 
58
59
  // ../core/dist/logger.js
59
60
  import { appendFileSync, mkdirSync, existsSync } from "fs";
@@ -113,6 +114,32 @@ var logger = {
113
114
  };
114
115
 
115
116
  // ../core/dist/worktree.js
117
+ async function prepareWorktreeDeps(worktreePath, projectPath) {
118
+ let pm = "npm";
119
+ if (existsSync2(join2(projectPath, "pnpm-lock.yaml")))
120
+ pm = "pnpm";
121
+ else if (existsSync2(join2(projectPath, "bun.lock")) || existsSync2(join2(projectPath, "bun.lockb")))
122
+ pm = "bun";
123
+ else if (existsSync2(join2(projectPath, "yarn.lock")))
124
+ pm = "yarn";
125
+ if (pm === "npm")
126
+ return;
127
+ const cmd = pm === "pnpm" ? ["pnpm", "install", "--frozen-lockfile", "--prefer-offline"] : pm === "bun" ? ["bun", "install", "--frozen-lockfile"] : ["yarn", "install", "--frozen-lockfile"];
128
+ logger.info("worktree", `installing deps (${pm})`, { worktreePath: worktreePath.split("/").slice(-2).join("/") });
129
+ const proc = spawn({
130
+ cmd,
131
+ cwd: worktreePath,
132
+ stdin: "ignore",
133
+ stdout: "pipe",
134
+ stderr: "pipe",
135
+ env: { ...process.env }
136
+ });
137
+ const exitCode = await proc.exited;
138
+ if (exitCode !== 0) {
139
+ const stderr = await new Response(proc.stderr).text();
140
+ logger.warn("worktree", `${pm} install exited ${exitCode}`, { stderr: stderr.substring(0, 300) });
141
+ }
142
+ }
116
143
  var PREVIEW_PORT = STASH_PORT_START;
117
144
 
118
145
  class WorktreeManager {
@@ -125,6 +152,9 @@ class WorktreeManager {
125
152
  this.git = simpleGit(projectPath);
126
153
  this.cleanupStaleWorktrees();
127
154
  }
155
+ getProjectPath() {
156
+ return this.projectPath;
157
+ }
128
158
  async cleanupStaleWorktrees() {
129
159
  const worktreesDir = join2(this.projectPath, ".stashes", "worktrees");
130
160
  if (!existsSync2(worktreesDir))
@@ -689,7 +719,7 @@ import { mkdirSync as mkdirSync4, existsSync as existsSync5 } from "fs";
689
719
  import simpleGit2 from "simple-git";
690
720
 
691
721
  // ../core/dist/screenshot.js
692
- var {spawn } = globalThis.Bun;
722
+ var {spawn: spawn2 } = globalThis.Bun;
693
723
  import { join as join4 } from "path";
694
724
  import { mkdirSync as mkdirSync3, existsSync as existsSync4 } from "fs";
695
725
  var SCREENSHOTS_DIR = ".stashes/screenshots";
@@ -728,7 +758,7 @@ async function captureScreenshot(portOrOpts, projectPath, stashId) {
728
758
  await browser.close();
729
759
  })();
730
760
  `;
731
- const proc = spawn({
761
+ const proc = spawn2({
732
762
  cmd: ["node", "-e", playwrightScript],
733
763
  stdin: "ignore",
734
764
  stdout: "pipe",
@@ -1117,8 +1147,9 @@ async function generate(opts) {
1117
1147
  const screenshotWorktree = await worktreeManager.createForGeneration(`screenshot-${stashId}`);
1118
1148
  const screenshotGit = simpleGit3(screenshotWorktree.path);
1119
1149
  await screenshotGit.checkout(["--detach", stash.branch]);
1120
- const devServer = spawn2({
1121
- cmd: ["npm", "run", "dev"],
1150
+ await prepareWorktreeDeps(screenshotWorktree.path, projectPath);
1151
+ const devServer = spawn3({
1152
+ cmd: ["npm", "run", "dev", "--", "--port", String(port)],
1122
1153
  cwd: screenshotWorktree.path,
1123
1154
  stdin: "ignore",
1124
1155
  stdout: "pipe",
@@ -1170,7 +1201,7 @@ async function generate(opts) {
1170
1201
  return completedStashes;
1171
1202
  }
1172
1203
  // ../core/dist/vary.js
1173
- var {spawn: spawn3 } = globalThis.Bun;
1204
+ var {spawn: spawn4 } = globalThis.Bun;
1174
1205
  import simpleGit4 from "simple-git";
1175
1206
  function emit2(onProgress, event) {
1176
1207
  if (onProgress)
@@ -1288,8 +1319,9 @@ ${context}` : `The user wants to vary the current UI. Apply this change: ${promp
1288
1319
  const screenshotWorktree = await worktreeManager.createForGeneration(`screenshot-${stashId}`);
1289
1320
  const screenshotGit = simpleGit4(screenshotWorktree.path);
1290
1321
  await screenshotGit.checkout(["--detach", stash.branch]);
1291
- const devServer = spawn3({
1292
- cmd: ["npm", "run", "dev"],
1322
+ await prepareWorktreeDeps(screenshotWorktree.path, projectPath);
1323
+ const devServer = spawn4({
1324
+ cmd: ["npm", "run", "dev", "--", "--port", String(port)],
1293
1325
  cwd: screenshotWorktree.path,
1294
1326
  stdin: "ignore",
1295
1327
  stdout: "pipe",
@@ -1622,7 +1654,7 @@ import { query as query2, tool, createSdkMcpServer } from "@anthropic-ai/claude-
1622
1654
  import { z as z6 } from "zod/v4";
1623
1655
 
1624
1656
  // ../server/dist/services/app-proxy.js
1625
- import { spawn as spawn4 } from "child_process";
1657
+ import { spawn as spawn5 } from "child_process";
1626
1658
  function startAppProxy(userDevPort, proxyPort, injectOverlay) {
1627
1659
  const overlayScript = injectOverlay("", userDevPort, proxyPort);
1628
1660
  const overlayEscaped = JSON.stringify(overlayScript);
@@ -1697,7 +1729,7 @@ server.listen(${proxyPort}, () => {
1697
1729
  if (process.send) process.send('ready');
1698
1730
  });
1699
1731
  `;
1700
- const child = spawn4("node", ["-e", proxyScript], {
1732
+ const child = spawn5("node", ["-e", proxyScript], {
1701
1733
  stdio: ["ignore", "inherit", "inherit", "ipc"]
1702
1734
  });
1703
1735
  child.on("error", (err) => {
@@ -1940,8 +1972,9 @@ class PreviewPool {
1940
1972
  this.usedPorts.add(proxyPort);
1941
1973
  const devPort = proxyPort + DEV_PORT_OFFSET;
1942
1974
  const worktreePath = await this.worktreeManager.createPreviewForPool(stashId);
1975
+ await prepareWorktreeDeps(worktreePath, this.worktreeManager.getProjectPath());
1943
1976
  const process2 = Bun.spawn({
1944
- cmd: ["npm", "run", "dev"],
1977
+ cmd: ["npm", "run", "dev", "--", "--port", String(devPort)],
1945
1978
  cwd: worktreePath,
1946
1979
  stdin: "ignore",
1947
1980
  stdout: "pipe",
@@ -2442,11 +2475,44 @@ ${sourceCode.substring(0, 3000)}
2442
2475
  mcpServers: {
2443
2476
  "stashes-chat": stashServer
2444
2477
  },
2478
+ disallowedTools: [
2479
+ "Write",
2480
+ "Edit",
2481
+ "Bash",
2482
+ "NotebookEdit",
2483
+ "LSP",
2484
+ "Agent",
2485
+ "TodoWrite",
2486
+ "TaskCreate",
2487
+ "TaskUpdate",
2488
+ "TaskList",
2489
+ "TaskGet",
2490
+ "Skill",
2491
+ "ToolSearch",
2492
+ "EnterPlanMode",
2493
+ "ExitPlanMode",
2494
+ "WebSearch",
2495
+ "WebFetch"
2496
+ ],
2445
2497
  permissionMode: "bypassPermissions",
2446
2498
  allowDangerouslySkipPermissions: true,
2447
2499
  includePartialMessages: true
2448
2500
  }
2449
2501
  });
2502
+ const VISIBLE_TOOLS = new Set([
2503
+ "generate_stashes",
2504
+ "vary_stash",
2505
+ "show_stash",
2506
+ "stashes_generate",
2507
+ "stashes_vary",
2508
+ "stashes_show",
2509
+ "stashes_list",
2510
+ "stashes_apply",
2511
+ "stashes_remove",
2512
+ "stashes_browse"
2513
+ ]);
2514
+ const hiddenToolIds = new Set;
2515
+ let insideHiddenTool = false;
2450
2516
  for await (const sdkMessage of queryIter) {
2451
2517
  if (sdkMessage.type === "system" && "subtype" in sdkMessage && sdkMessage.subtype === "init") {
2452
2518
  const sessionId = "session_id" in sdkMessage ? sdkMessage.session_id : "";
@@ -2460,14 +2526,16 @@ ${sourceCode.substring(0, 3000)}
2460
2526
  if (event.type === "content_block_delta") {
2461
2527
  const delta = event.delta;
2462
2528
  if (delta.type === "text_delta" && delta.text) {
2463
- flushThinking();
2464
- textBuf += delta.text;
2465
- this.broadcast({
2466
- type: "ai_stream",
2467
- content: delta.text,
2468
- streamType: "text",
2469
- source: "chat"
2470
- });
2529
+ if (!insideHiddenTool) {
2530
+ flushThinking();
2531
+ textBuf += delta.text;
2532
+ this.broadcast({
2533
+ type: "ai_stream",
2534
+ content: delta.text,
2535
+ streamType: "text",
2536
+ source: "chat"
2537
+ });
2538
+ }
2471
2539
  } else if (delta.type === "thinking_delta" && delta.thinking) {
2472
2540
  thinkingBuf += delta.thinking;
2473
2541
  this.broadcast({
@@ -2484,12 +2552,19 @@ ${sourceCode.substring(0, 3000)}
2484
2552
  const msg = sdkMessage.message;
2485
2553
  for (const block of msg.content || []) {
2486
2554
  if (block.type === "tool_use") {
2487
- flushThinking();
2488
- flushText();
2489
2555
  const toolId = block.id;
2490
2556
  const toolName = block.name;
2491
2557
  const toolParams = block.input ?? {};
2492
2558
  toolNameMap.set(toolId, toolName);
2559
+ const isVisible = VISIBLE_TOOLS.has(toolName);
2560
+ if (!isVisible) {
2561
+ hiddenToolIds.add(toolId);
2562
+ insideHiddenTool = true;
2563
+ continue;
2564
+ }
2565
+ insideHiddenTool = false;
2566
+ flushThinking();
2567
+ flushText();
2493
2568
  save({
2494
2569
  id: crypto.randomUUID(),
2495
2570
  role: "assistant",
@@ -2525,6 +2600,12 @@ ${sourceCode.substring(0, 3000)}
2525
2600
  const toolUseId = b.tool_use_id;
2526
2601
  const isError = b.is_error || false;
2527
2602
  const endToolName = toolNameMap.get(toolUseId) || "unknown";
2603
+ if (hiddenToolIds.has(toolUseId)) {
2604
+ hiddenToolIds.delete(toolUseId);
2605
+ insideHiddenTool = false;
2606
+ continue;
2607
+ }
2608
+ insideHiddenTool = false;
2528
2609
  if (endToolName.includes("generate_stashes") || endToolName.includes("stashes_generate") || endToolName.includes("vary_stash") || endToolName.includes("stashes_vary")) {
2529
2610
  this.stopStashPoll();
2530
2611
  }
@@ -2693,9 +2774,19 @@ ${sourceCode.substring(0, 3000)}
2693
2774
  case "error":
2694
2775
  this.broadcast({ type: "stash:error", stashId: event.stashId, error: event.error });
2695
2776
  break;
2696
- case "ai_stream": {
2697
- const streamType = event.streamType === "tool_use" ? "tool_start" : event.streamType;
2698
- this.broadcast({ type: "ai_stream", content: event.content, streamType });
2777
+ case "ai_stream":
2778
+ break;
2779
+ case "activity": {
2780
+ const activityEvent = {
2781
+ stashId: event.stashId,
2782
+ action: event.action,
2783
+ file: event.file,
2784
+ lines: event.lines,
2785
+ content: event.content,
2786
+ timestamp: event.timestamp
2787
+ };
2788
+ this.activityStore.append(activityEvent);
2789
+ this.broadcast({ type: "stash:activity", stashId: event.stashId, event: activityEvent });
2699
2790
  break;
2700
2791
  }
2701
2792
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stashes",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "type": "module",
5
5
  "description": "Generate AI-powered UI design explorations in your project",
6
6
  "keywords": [