stashes 0.2.4 → 0.2.6

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 +177 -7
  2. package/dist/mcp.js +177 -7
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -139,7 +139,7 @@ async function prepareWorktreeDeps(worktreePath, projectPath) {
139
139
  pm = "yarn";
140
140
  if (pm === "npm")
141
141
  return;
142
- const cmd = pm === "pnpm" ? ["pnpm", "install", "--frozen-lockfile", "--prefer-offline"] : pm === "bun" ? ["bun", "install", "--frozen-lockfile"] : ["yarn", "install", "--frozen-lockfile"];
142
+ const cmd = pm === "pnpm" ? ["pnpm", "install", "--force", "--prefer-offline"] : pm === "bun" ? ["bun", "install", "--frozen-lockfile"] : ["yarn", "install", "--frozen-lockfile"];
143
143
  logger.info("worktree", `installing deps (${pm})`, { worktreePath: worktreePath.split("/").slice(-2).join("/") });
144
144
  const proc = spawn({
145
145
  cmd,
@@ -1090,8 +1090,11 @@ async function generate(opts) {
1090
1090
  id: stashId,
1091
1091
  prompt: stashPrompt,
1092
1092
  cwd: worktree.path,
1093
- persistSession: false
1093
+ persistSession: true
1094
1094
  })) {
1095
+ if (chunk.type === "session_id" && chunk.sessionId) {
1096
+ persistence.saveStash({ ...stash, sessionId: chunk.sessionId });
1097
+ }
1095
1098
  emit(onProgress, {
1096
1099
  type: "ai_stream",
1097
1100
  stashId,
@@ -1104,12 +1107,36 @@ async function generate(opts) {
1104
1107
  const filePath = chunk.toolInput?.file_path ?? chunk.toolInput?.path ?? chunk.toolInput?.command ?? undefined;
1105
1108
  const lines = chunk.toolInput?.content ? chunk.toolInput.content.split(`
1106
1109
  `).length : undefined;
1110
+ let diff;
1111
+ if (chunk.toolName === "Write") {
1112
+ const writeContent = chunk.toolInput?.content;
1113
+ if (writeContent) {
1114
+ diff = writeContent.substring(0, 5000);
1115
+ }
1116
+ } else if (chunk.toolName === "Edit") {
1117
+ const oldStr = chunk.toolInput?.old_string;
1118
+ const newStr = chunk.toolInput?.new_string;
1119
+ if (oldStr && newStr) {
1120
+ const oldLines = oldStr.split(`
1121
+ `).map((l) => `-${l}`).join(`
1122
+ `);
1123
+ const newLines = newStr.split(`
1124
+ `).map((l) => `+${l}`).join(`
1125
+ `);
1126
+ diff = `--- old
1127
+ +++ new
1128
+ @@ @@
1129
+ ${oldLines}
1130
+ ${newLines}`.substring(0, 5000);
1131
+ }
1132
+ }
1107
1133
  emit(onProgress, {
1108
1134
  type: "activity",
1109
1135
  stashId,
1110
1136
  action: chunk.toolName,
1111
1137
  file: filePath,
1112
1138
  lines,
1139
+ diff,
1113
1140
  timestamp: Date.now()
1114
1141
  });
1115
1142
  }
@@ -1164,7 +1191,7 @@ async function generate(opts) {
1164
1191
  await screenshotGit.checkout(["--detach", stash.branch]);
1165
1192
  await prepareWorktreeDeps(screenshotWorktree.path, projectPath);
1166
1193
  const devServer = spawn3({
1167
- cmd: ["npm", "run", "dev", "--", "--port", String(port)],
1194
+ cmd: ["npm", "run", "dev", "--", "--", "--port", String(port)],
1168
1195
  cwd: screenshotWorktree.path,
1169
1196
  stdin: "ignore",
1170
1197
  stdout: "pipe",
@@ -1292,14 +1319,73 @@ ${context}` : `The user wants to vary the current UI. Apply this change: ${promp
1292
1319
  id: stashId,
1293
1320
  prompt: varyPrompt,
1294
1321
  cwd: worktree.path,
1295
- persistSession: false
1322
+ persistSession: true
1296
1323
  })) {
1324
+ if (chunk.type === "session_id" && chunk.sessionId) {
1325
+ persistence.saveStash({ ...stash, sessionId: chunk.sessionId });
1326
+ }
1297
1327
  emit2(onProgress, {
1298
1328
  type: "ai_stream",
1299
1329
  stashId,
1300
1330
  content: chunk.content,
1301
1331
  streamType: chunk.type
1302
1332
  });
1333
+ if (chunk.type === "tool_use" && chunk.toolName) {
1334
+ const knownTools = ["Read", "Write", "Edit", "Glob", "Grep", "Bash"];
1335
+ if (knownTools.includes(chunk.toolName)) {
1336
+ const filePath = chunk.toolInput?.file_path ?? chunk.toolInput?.path ?? chunk.toolInput?.command ?? undefined;
1337
+ const lines = chunk.toolInput?.content ? chunk.toolInput.content.split(`
1338
+ `).length : undefined;
1339
+ let diff;
1340
+ if (chunk.toolName === "Write") {
1341
+ const writeContent = chunk.toolInput?.content;
1342
+ if (writeContent) {
1343
+ diff = writeContent.substring(0, 5000);
1344
+ }
1345
+ } else if (chunk.toolName === "Edit") {
1346
+ const oldStr = chunk.toolInput?.old_string;
1347
+ const newStr = chunk.toolInput?.new_string;
1348
+ if (oldStr && newStr) {
1349
+ const oldLines = oldStr.split(`
1350
+ `).map((l) => `-${l}`).join(`
1351
+ `);
1352
+ const newLines = newStr.split(`
1353
+ `).map((l) => `+${l}`).join(`
1354
+ `);
1355
+ diff = `--- old
1356
+ +++ new
1357
+ @@ @@
1358
+ ${oldLines}
1359
+ ${newLines}`.substring(0, 5000);
1360
+ }
1361
+ }
1362
+ emit2(onProgress, {
1363
+ type: "activity",
1364
+ stashId,
1365
+ action: chunk.toolName,
1366
+ file: filePath,
1367
+ lines,
1368
+ diff,
1369
+ timestamp: Date.now()
1370
+ });
1371
+ }
1372
+ } else if (chunk.type === "thinking") {
1373
+ emit2(onProgress, {
1374
+ type: "activity",
1375
+ stashId,
1376
+ action: "thinking",
1377
+ content: chunk.content.substring(0, 200),
1378
+ timestamp: Date.now()
1379
+ });
1380
+ } else if (chunk.type === "text") {
1381
+ emit2(onProgress, {
1382
+ type: "activity",
1383
+ stashId,
1384
+ action: "text",
1385
+ content: chunk.content.substring(0, 200),
1386
+ timestamp: Date.now()
1387
+ });
1388
+ }
1303
1389
  }
1304
1390
  const wtGit = simpleGit4(worktree.path);
1305
1391
  let hasChanges = false;
@@ -1336,7 +1422,7 @@ ${context}` : `The user wants to vary the current UI. Apply this change: ${promp
1336
1422
  await screenshotGit.checkout(["--detach", stash.branch]);
1337
1423
  await prepareWorktreeDeps(screenshotWorktree.path, projectPath);
1338
1424
  const devServer = spawn4({
1339
- cmd: ["npm", "run", "dev", "--", "--port", String(port)],
1425
+ cmd: ["npm", "run", "dev", "--", "--", "--port", String(port)],
1340
1426
  cwd: screenshotWorktree.path,
1341
1427
  stdin: "ignore",
1342
1428
  stdout: "pipe",
@@ -1750,7 +1836,7 @@ class PreviewPool {
1750
1836
  const worktreePath = await this.worktreeManager.createPreviewForPool(stashId);
1751
1837
  await prepareWorktreeDeps(worktreePath, this.worktreeManager.getProjectPath());
1752
1838
  const process2 = Bun.spawn({
1753
- cmd: ["npm", "run", "dev", "--", "--port", String(devPort)],
1839
+ cmd: ["npm", "run", "dev", "--", "--", "--port", String(devPort)],
1754
1840
  cwd: worktreePath,
1755
1841
  stdin: "ignore",
1756
1842
  stdout: "pipe",
@@ -2401,7 +2487,6 @@ ${sourceCode.substring(0, 3000)}
2401
2487
  for (const s of allStashes) {
2402
2488
  if (this.activityStore.has(s.id)) {
2403
2489
  stashActivity[s.id] = this.activityStore.getSnapshot(s.id);
2404
- this.activityStore.clear(s.id);
2405
2490
  }
2406
2491
  }
2407
2492
  if (Object.keys(stashActivity).length === 0)
@@ -2559,6 +2644,7 @@ ${sourceCode.substring(0, 3000)}
2559
2644
  file: event.file,
2560
2645
  lines: event.lines,
2561
2646
  content: event.content,
2647
+ diff: event.diff,
2562
2648
  timestamp: event.timestamp
2563
2649
  };
2564
2650
  this.activityStore.append(activityEvent);
@@ -2630,6 +2716,61 @@ ${refDescriptions.join(`
2630
2716
  await this.previewPool.stop(stashId);
2631
2717
  await remove(this.projectPath, stashId);
2632
2718
  }
2719
+ async continueStash(projectId, stashId, message) {
2720
+ const stash = this.persistence.getStash(projectId, stashId);
2721
+ if (!stash?.sessionId) {
2722
+ this.broadcast({ type: "stash:error", stashId, error: "No session to continue" });
2723
+ return;
2724
+ }
2725
+ const worktree = await this.worktreeManager.createPreviewForPool(stashId);
2726
+ try {
2727
+ for await (const chunk of runAgentQuery({
2728
+ id: `continue-${stashId}`,
2729
+ prompt: message,
2730
+ cwd: worktree,
2731
+ resumeSessionId: stash.sessionId,
2732
+ persistSession: true
2733
+ })) {
2734
+ if (chunk.type === "session_id" && chunk.sessionId) {
2735
+ this.persistence.saveStash({ ...stash, sessionId: chunk.sessionId });
2736
+ }
2737
+ if (chunk.type === "tool_use" && chunk.toolName) {
2738
+ const knownTools = ["Read", "Write", "Edit", "Glob", "Grep", "Bash"];
2739
+ if (knownTools.includes(chunk.toolName)) {
2740
+ const filePath = chunk.toolInput?.file_path ?? chunk.toolInput?.path ?? chunk.toolInput?.command ?? undefined;
2741
+ const activityEvent = {
2742
+ stashId,
2743
+ action: chunk.toolName,
2744
+ file: filePath,
2745
+ timestamp: Date.now()
2746
+ };
2747
+ this.activityStore.append(activityEvent);
2748
+ this.broadcast({ type: "stash:activity", stashId, event: activityEvent });
2749
+ }
2750
+ }
2751
+ if (chunk.type === "text") {
2752
+ this.broadcast({
2753
+ type: "ai_stream",
2754
+ content: chunk.content,
2755
+ streamType: "text",
2756
+ source: `stash-${stashId}`
2757
+ });
2758
+ }
2759
+ }
2760
+ const simpleGit6 = (await import("simple-git")).default;
2761
+ const git = simpleGit6(worktree);
2762
+ await git.add("-A");
2763
+ const status = await git.status();
2764
+ if (status.staged.length > 0) {
2765
+ await git.commit(`stashes: continue ${stashId}`);
2766
+ this.broadcast({ type: "stash:status", stashId, status: "screenshotting" });
2767
+ }
2768
+ } catch (err) {
2769
+ this.broadcast({ type: "stash:error", stashId, error: err instanceof Error ? err.message : String(err) });
2770
+ } finally {
2771
+ killAgentQuery(`continue-${stashId}`);
2772
+ }
2773
+ }
2633
2774
  }
2634
2775
 
2635
2776
  // ../server/dist/services/activity-store.js
@@ -2809,6 +2950,15 @@ function createWebSocketHandler(projectPath, userDevPort, appProxyPort, stashPor
2809
2950
  case "delete_stash":
2810
2951
  await stashService.deleteStash(event.stashId);
2811
2952
  break;
2953
+ case "continue_stash": {
2954
+ const stash = persistence.getStash(event.projectId, event.stashId);
2955
+ if (!stash?.sessionId) {
2956
+ broadcast({ type: "stash:error", stashId: event.stashId, error: "No session to continue \u2014 stash was generated without session persistence" });
2957
+ break;
2958
+ }
2959
+ await stashService.continueStash(event.projectId, event.stashId, event.message);
2960
+ break;
2961
+ }
2812
2962
  }
2813
2963
  } catch (err) {
2814
2964
  const errorMsg = err instanceof Error ? err.message : String(err);
@@ -2959,6 +3109,26 @@ app.get("/stash-activity/:stashId", (c) => {
2959
3109
  const events = store.getEvents(stashId);
2960
3110
  return c.json({ data: events });
2961
3111
  });
3112
+ app.get("/stash-session/:stashId", async (c) => {
3113
+ const stashId = c.req.param("stashId");
3114
+ const persistence2 = getPersistence();
3115
+ let stash = null;
3116
+ for (const project of persistence2.listProjects()) {
3117
+ stash = persistence2.getStash(project.id, stashId);
3118
+ if (stash)
3119
+ break;
3120
+ }
3121
+ if (!stash?.sessionId) {
3122
+ return c.json({ data: null, error: "No session found for this stash" }, 404);
3123
+ }
3124
+ try {
3125
+ const { getSessionMessages } = await import("@anthropic-ai/claude-agent-sdk");
3126
+ const messages = await getSessionMessages(stash.sessionId, { limit: 100 });
3127
+ return c.json({ data: messages });
3128
+ } catch (err) {
3129
+ return c.json({ data: null, error: "Failed to retrieve session messages" }, 500);
3130
+ }
3131
+ });
2962
3132
  function ensureProject(persistence2) {
2963
3133
  const projects = persistence2.listProjects();
2964
3134
  if (projects.length > 0)
package/dist/mcp.js CHANGED
@@ -124,7 +124,7 @@ async function prepareWorktreeDeps(worktreePath, projectPath) {
124
124
  pm = "yarn";
125
125
  if (pm === "npm")
126
126
  return;
127
- const cmd = pm === "pnpm" ? ["pnpm", "install", "--frozen-lockfile", "--prefer-offline"] : pm === "bun" ? ["bun", "install", "--frozen-lockfile"] : ["yarn", "install", "--frozen-lockfile"];
127
+ const cmd = pm === "pnpm" ? ["pnpm", "install", "--force", "--prefer-offline"] : pm === "bun" ? ["bun", "install", "--frozen-lockfile"] : ["yarn", "install", "--frozen-lockfile"];
128
128
  logger.info("worktree", `installing deps (${pm})`, { worktreePath: worktreePath.split("/").slice(-2).join("/") });
129
129
  const proc = spawn({
130
130
  cmd,
@@ -1075,8 +1075,11 @@ async function generate(opts) {
1075
1075
  id: stashId,
1076
1076
  prompt: stashPrompt,
1077
1077
  cwd: worktree.path,
1078
- persistSession: false
1078
+ persistSession: true
1079
1079
  })) {
1080
+ if (chunk.type === "session_id" && chunk.sessionId) {
1081
+ persistence.saveStash({ ...stash, sessionId: chunk.sessionId });
1082
+ }
1080
1083
  emit(onProgress, {
1081
1084
  type: "ai_stream",
1082
1085
  stashId,
@@ -1089,12 +1092,36 @@ async function generate(opts) {
1089
1092
  const filePath = chunk.toolInput?.file_path ?? chunk.toolInput?.path ?? chunk.toolInput?.command ?? undefined;
1090
1093
  const lines = chunk.toolInput?.content ? chunk.toolInput.content.split(`
1091
1094
  `).length : undefined;
1095
+ let diff;
1096
+ if (chunk.toolName === "Write") {
1097
+ const writeContent = chunk.toolInput?.content;
1098
+ if (writeContent) {
1099
+ diff = writeContent.substring(0, 5000);
1100
+ }
1101
+ } else if (chunk.toolName === "Edit") {
1102
+ const oldStr = chunk.toolInput?.old_string;
1103
+ const newStr = chunk.toolInput?.new_string;
1104
+ if (oldStr && newStr) {
1105
+ const oldLines = oldStr.split(`
1106
+ `).map((l) => `-${l}`).join(`
1107
+ `);
1108
+ const newLines = newStr.split(`
1109
+ `).map((l) => `+${l}`).join(`
1110
+ `);
1111
+ diff = `--- old
1112
+ +++ new
1113
+ @@ @@
1114
+ ${oldLines}
1115
+ ${newLines}`.substring(0, 5000);
1116
+ }
1117
+ }
1092
1118
  emit(onProgress, {
1093
1119
  type: "activity",
1094
1120
  stashId,
1095
1121
  action: chunk.toolName,
1096
1122
  file: filePath,
1097
1123
  lines,
1124
+ diff,
1098
1125
  timestamp: Date.now()
1099
1126
  });
1100
1127
  }
@@ -1149,7 +1176,7 @@ async function generate(opts) {
1149
1176
  await screenshotGit.checkout(["--detach", stash.branch]);
1150
1177
  await prepareWorktreeDeps(screenshotWorktree.path, projectPath);
1151
1178
  const devServer = spawn3({
1152
- cmd: ["npm", "run", "dev", "--", "--port", String(port)],
1179
+ cmd: ["npm", "run", "dev", "--", "--", "--port", String(port)],
1153
1180
  cwd: screenshotWorktree.path,
1154
1181
  stdin: "ignore",
1155
1182
  stdout: "pipe",
@@ -1277,14 +1304,73 @@ ${context}` : `The user wants to vary the current UI. Apply this change: ${promp
1277
1304
  id: stashId,
1278
1305
  prompt: varyPrompt,
1279
1306
  cwd: worktree.path,
1280
- persistSession: false
1307
+ persistSession: true
1281
1308
  })) {
1309
+ if (chunk.type === "session_id" && chunk.sessionId) {
1310
+ persistence.saveStash({ ...stash, sessionId: chunk.sessionId });
1311
+ }
1282
1312
  emit2(onProgress, {
1283
1313
  type: "ai_stream",
1284
1314
  stashId,
1285
1315
  content: chunk.content,
1286
1316
  streamType: chunk.type
1287
1317
  });
1318
+ if (chunk.type === "tool_use" && chunk.toolName) {
1319
+ const knownTools = ["Read", "Write", "Edit", "Glob", "Grep", "Bash"];
1320
+ if (knownTools.includes(chunk.toolName)) {
1321
+ const filePath = chunk.toolInput?.file_path ?? chunk.toolInput?.path ?? chunk.toolInput?.command ?? undefined;
1322
+ const lines = chunk.toolInput?.content ? chunk.toolInput.content.split(`
1323
+ `).length : undefined;
1324
+ let diff;
1325
+ if (chunk.toolName === "Write") {
1326
+ const writeContent = chunk.toolInput?.content;
1327
+ if (writeContent) {
1328
+ diff = writeContent.substring(0, 5000);
1329
+ }
1330
+ } else if (chunk.toolName === "Edit") {
1331
+ const oldStr = chunk.toolInput?.old_string;
1332
+ const newStr = chunk.toolInput?.new_string;
1333
+ if (oldStr && newStr) {
1334
+ const oldLines = oldStr.split(`
1335
+ `).map((l) => `-${l}`).join(`
1336
+ `);
1337
+ const newLines = newStr.split(`
1338
+ `).map((l) => `+${l}`).join(`
1339
+ `);
1340
+ diff = `--- old
1341
+ +++ new
1342
+ @@ @@
1343
+ ${oldLines}
1344
+ ${newLines}`.substring(0, 5000);
1345
+ }
1346
+ }
1347
+ emit2(onProgress, {
1348
+ type: "activity",
1349
+ stashId,
1350
+ action: chunk.toolName,
1351
+ file: filePath,
1352
+ lines,
1353
+ diff,
1354
+ timestamp: Date.now()
1355
+ });
1356
+ }
1357
+ } else if (chunk.type === "thinking") {
1358
+ emit2(onProgress, {
1359
+ type: "activity",
1360
+ stashId,
1361
+ action: "thinking",
1362
+ content: chunk.content.substring(0, 200),
1363
+ timestamp: Date.now()
1364
+ });
1365
+ } else if (chunk.type === "text") {
1366
+ emit2(onProgress, {
1367
+ type: "activity",
1368
+ stashId,
1369
+ action: "text",
1370
+ content: chunk.content.substring(0, 200),
1371
+ timestamp: Date.now()
1372
+ });
1373
+ }
1288
1374
  }
1289
1375
  const wtGit = simpleGit4(worktree.path);
1290
1376
  let hasChanges = false;
@@ -1321,7 +1407,7 @@ ${context}` : `The user wants to vary the current UI. Apply this change: ${promp
1321
1407
  await screenshotGit.checkout(["--detach", stash.branch]);
1322
1408
  await prepareWorktreeDeps(screenshotWorktree.path, projectPath);
1323
1409
  const devServer = spawn4({
1324
- cmd: ["npm", "run", "dev", "--", "--port", String(port)],
1410
+ cmd: ["npm", "run", "dev", "--", "--", "--port", String(port)],
1325
1411
  cwd: screenshotWorktree.path,
1326
1412
  stdin: "ignore",
1327
1413
  stdout: "pipe",
@@ -1974,7 +2060,7 @@ class PreviewPool {
1974
2060
  const worktreePath = await this.worktreeManager.createPreviewForPool(stashId);
1975
2061
  await prepareWorktreeDeps(worktreePath, this.worktreeManager.getProjectPath());
1976
2062
  const process2 = Bun.spawn({
1977
- cmd: ["npm", "run", "dev", "--", "--port", String(devPort)],
2063
+ cmd: ["npm", "run", "dev", "--", "--", "--port", String(devPort)],
1978
2064
  cwd: worktreePath,
1979
2065
  stdin: "ignore",
1980
2066
  stdout: "pipe",
@@ -2625,7 +2711,6 @@ ${sourceCode.substring(0, 3000)}
2625
2711
  for (const s of allStashes) {
2626
2712
  if (this.activityStore.has(s.id)) {
2627
2713
  stashActivity[s.id] = this.activityStore.getSnapshot(s.id);
2628
- this.activityStore.clear(s.id);
2629
2714
  }
2630
2715
  }
2631
2716
  if (Object.keys(stashActivity).length === 0)
@@ -2783,6 +2868,7 @@ ${sourceCode.substring(0, 3000)}
2783
2868
  file: event.file,
2784
2869
  lines: event.lines,
2785
2870
  content: event.content,
2871
+ diff: event.diff,
2786
2872
  timestamp: event.timestamp
2787
2873
  };
2788
2874
  this.activityStore.append(activityEvent);
@@ -2854,6 +2940,61 @@ ${refDescriptions.join(`
2854
2940
  await this.previewPool.stop(stashId);
2855
2941
  await remove(this.projectPath, stashId);
2856
2942
  }
2943
+ async continueStash(projectId, stashId, message) {
2944
+ const stash = this.persistence.getStash(projectId, stashId);
2945
+ if (!stash?.sessionId) {
2946
+ this.broadcast({ type: "stash:error", stashId, error: "No session to continue" });
2947
+ return;
2948
+ }
2949
+ const worktree = await this.worktreeManager.createPreviewForPool(stashId);
2950
+ try {
2951
+ for await (const chunk of runAgentQuery({
2952
+ id: `continue-${stashId}`,
2953
+ prompt: message,
2954
+ cwd: worktree,
2955
+ resumeSessionId: stash.sessionId,
2956
+ persistSession: true
2957
+ })) {
2958
+ if (chunk.type === "session_id" && chunk.sessionId) {
2959
+ this.persistence.saveStash({ ...stash, sessionId: chunk.sessionId });
2960
+ }
2961
+ if (chunk.type === "tool_use" && chunk.toolName) {
2962
+ const knownTools = ["Read", "Write", "Edit", "Glob", "Grep", "Bash"];
2963
+ if (knownTools.includes(chunk.toolName)) {
2964
+ const filePath = chunk.toolInput?.file_path ?? chunk.toolInput?.path ?? chunk.toolInput?.command ?? undefined;
2965
+ const activityEvent = {
2966
+ stashId,
2967
+ action: chunk.toolName,
2968
+ file: filePath,
2969
+ timestamp: Date.now()
2970
+ };
2971
+ this.activityStore.append(activityEvent);
2972
+ this.broadcast({ type: "stash:activity", stashId, event: activityEvent });
2973
+ }
2974
+ }
2975
+ if (chunk.type === "text") {
2976
+ this.broadcast({
2977
+ type: "ai_stream",
2978
+ content: chunk.content,
2979
+ streamType: "text",
2980
+ source: `stash-${stashId}`
2981
+ });
2982
+ }
2983
+ }
2984
+ const simpleGit6 = (await import("simple-git")).default;
2985
+ const git = simpleGit6(worktree);
2986
+ await git.add("-A");
2987
+ const status = await git.status();
2988
+ if (status.staged.length > 0) {
2989
+ await git.commit(`stashes: continue ${stashId}`);
2990
+ this.broadcast({ type: "stash:status", stashId, status: "screenshotting" });
2991
+ }
2992
+ } catch (err) {
2993
+ this.broadcast({ type: "stash:error", stashId, error: err instanceof Error ? err.message : String(err) });
2994
+ } finally {
2995
+ killAgentQuery(`continue-${stashId}`);
2996
+ }
2997
+ }
2857
2998
  }
2858
2999
 
2859
3000
  // ../server/dist/services/activity-store.js
@@ -3033,6 +3174,15 @@ function createWebSocketHandler(projectPath, userDevPort, appProxyPort, stashPor
3033
3174
  case "delete_stash":
3034
3175
  await stashService.deleteStash(event.stashId);
3035
3176
  break;
3177
+ case "continue_stash": {
3178
+ const stash = persistence.getStash(event.projectId, event.stashId);
3179
+ if (!stash?.sessionId) {
3180
+ broadcast({ type: "stash:error", stashId: event.stashId, error: "No session to continue \u2014 stash was generated without session persistence" });
3181
+ break;
3182
+ }
3183
+ await stashService.continueStash(event.projectId, event.stashId, event.message);
3184
+ break;
3185
+ }
3036
3186
  }
3037
3187
  } catch (err) {
3038
3188
  const errorMsg = err instanceof Error ? err.message : String(err);
@@ -3183,6 +3333,26 @@ app.get("/stash-activity/:stashId", (c) => {
3183
3333
  const events = store.getEvents(stashId);
3184
3334
  return c.json({ data: events });
3185
3335
  });
3336
+ app.get("/stash-session/:stashId", async (c) => {
3337
+ const stashId = c.req.param("stashId");
3338
+ const persistence2 = getPersistence();
3339
+ let stash = null;
3340
+ for (const project of persistence2.listProjects()) {
3341
+ stash = persistence2.getStash(project.id, stashId);
3342
+ if (stash)
3343
+ break;
3344
+ }
3345
+ if (!stash?.sessionId) {
3346
+ return c.json({ data: null, error: "No session found for this stash" }, 404);
3347
+ }
3348
+ try {
3349
+ const { getSessionMessages } = await import("@anthropic-ai/claude-agent-sdk");
3350
+ const messages = await getSessionMessages(stash.sessionId, { limit: 100 });
3351
+ return c.json({ data: messages });
3352
+ } catch (err) {
3353
+ return c.json({ data: null, error: "Failed to retrieve session messages" }, 500);
3354
+ }
3355
+ });
3186
3356
  function ensureProject(persistence2) {
3187
3357
  const projects = persistence2.listProjects();
3188
3358
  if (projects.length > 0)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stashes",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "type": "module",
5
5
  "description": "Generate AI-powered UI design explorations in your project",
6
6
  "keywords": [