stashes 0.1.29 → 0.1.30

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 +27 -22
  2. package/dist/mcp.js +27 -22
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -247,6 +247,10 @@ class WorktreeManager {
247
247
  const { readdirSync } = await import("fs");
248
248
  const entries = readdirSync(worktreesDir);
249
249
  for (const entry of entries) {
250
+ if (entry.startsWith("screenshot-")) {
251
+ logger.info("worktree", `skipping active screenshot worktree: ${entry}`);
252
+ continue;
253
+ }
250
254
  const worktreePath = join3(worktreesDir, entry);
251
255
  logger.info("worktree", `cleaning up stale worktree: ${entry}`);
252
256
  try {
@@ -896,6 +900,20 @@ function parseAiResult(text) {
896
900
  return null;
897
901
  }
898
902
  }
903
+ async function fallbackScreenshot(port, projectPath, stashId) {
904
+ try {
905
+ const url = await captureScreenshot(port, projectPath, stashId);
906
+ return {
907
+ primary: url,
908
+ screenshots: [{ url, label: "Homepage", route: "/", isPrimary: true }]
909
+ };
910
+ } catch (err) {
911
+ logger.error("smart-screenshot", `Fallback screenshot also failed for ${stashId}`, {
912
+ error: err instanceof Error ? err.message : String(err)
913
+ });
914
+ return { primary: "", screenshots: [] };
915
+ }
916
+ }
899
917
  async function captureSmartScreenshots(opts) {
900
918
  const { projectPath, stashId, stashBranch, parentBranch, worktreePath, port, model = "haiku", timeout = DEFAULT_TIMEOUT } = opts;
901
919
  const screenshotDir = join6(projectPath, SCREENSHOTS_DIR2);
@@ -905,11 +923,7 @@ async function captureSmartScreenshots(opts) {
905
923
  const diff = await getStashDiff(worktreePath, parentBranch);
906
924
  if (!diff) {
907
925
  logger.info("smart-screenshot", `No diff found for ${stashId}, using simple screenshot`);
908
- const url = await captureScreenshot(port, projectPath, stashId);
909
- return {
910
- primary: url,
911
- screenshots: [{ url, label: "Homepage", route: "/", isPrimary: true }]
912
- };
926
+ return fallbackScreenshot(port, projectPath, stashId);
913
927
  }
914
928
  const processId = `screenshot-ai-${stashId}`;
915
929
  const prompt = buildScreenshotPrompt(port, diff, screenshotDir, stashId);
@@ -939,12 +953,8 @@ async function captureSmartScreenshots(opts) {
939
953
  }
940
954
  const result = parseAiResult(textOutput);
941
955
  if (!result || !result.screenshots || result.screenshots.length === 0) {
942
- logger.info("smart-screenshot", `AI returned no screenshots for ${stashId}, falling back`);
943
- const url = await captureScreenshot(port, projectPath, stashId);
944
- return {
945
- primary: url,
946
- screenshots: [{ url, label: "Homepage", route: "/", isPrimary: true }]
947
- };
956
+ logger.info("smart-screenshot", `AI returned no screenshots for ${stashId} (timedOut=${timedOut}), falling back`);
957
+ return fallbackScreenshot(port, projectPath, stashId);
948
958
  }
949
959
  const screenshots = [];
950
960
  let primaryUrl = "";
@@ -967,11 +977,7 @@ async function captureSmartScreenshots(opts) {
967
977
  }
968
978
  if (screenshots.length === 0) {
969
979
  logger.info("smart-screenshot", `No valid screenshots for ${stashId}, falling back`);
970
- const url = await captureScreenshot(port, projectPath, stashId);
971
- return {
972
- primary: url,
973
- screenshots: [{ url, label: "Homepage", route: "/", isPrimary: true }]
974
- };
980
+ return fallbackScreenshot(port, projectPath, stashId);
975
981
  }
976
982
  if (!primaryUrl) {
977
983
  primaryUrl = screenshots[0].url;
@@ -1120,7 +1126,7 @@ async function generate(opts) {
1120
1126
  const port = await allocatePort();
1121
1127
  const worktree = await worktreeManager.createForGeneration(`screenshot-${stash.id}`);
1122
1128
  const screenshotGit = simpleGit3(worktree.path);
1123
- await screenshotGit.checkout(["-f", stash.branch]);
1129
+ await screenshotGit.checkout(["--detach", stash.branch]);
1124
1130
  const devServer = spawn3({
1125
1131
  cmd: ["npm", "run", "dev"],
1126
1132
  cwd: worktree.path,
@@ -1255,7 +1261,7 @@ async function vary(opts) {
1255
1261
  const port = await allocatePort2();
1256
1262
  const screenshotWorktree = await worktreeManager.createForGeneration(`screenshot-${stashId}`);
1257
1263
  const screenshotGit = simpleGit4(screenshotWorktree.path);
1258
- await screenshotGit.checkout(["-f", stash.branch]);
1264
+ await screenshotGit.checkout(["--detach", stash.branch]);
1259
1265
  const devServer = spawn4({
1260
1266
  cmd: ["npm", "run", "dev"],
1261
1267
  cwd: screenshotWorktree.path,
@@ -1786,9 +1792,8 @@ ${sourceCode.substring(0, 3000)}
1786
1792
  syncStashesFromDisk(projectId, chatId) {
1787
1793
  const diskStashes = this.persistence.listStashes(projectId);
1788
1794
  for (const stash of diskStashes) {
1789
- if (chatId && !stash.originChatId && !this.knownStashIds.has(stash.id)) {
1790
- const updated = { ...stash, originChatId: chatId };
1791
- this.persistence.saveStash(updated);
1795
+ if (chatId && !stash.originChatId) {
1796
+ this.persistence.saveStash({ ...stash, originChatId: chatId });
1792
1797
  }
1793
1798
  this.broadcast({
1794
1799
  type: "stash:status",
@@ -1819,7 +1824,7 @@ ${sourceCode.substring(0, 3000)}
1819
1824
  for (const stash of stashes) {
1820
1825
  const prev = lastStatus.get(stash.id);
1821
1826
  if (!prev || prev !== stash.status) {
1822
- if (chatId && !stash.originChatId && !prev) {
1827
+ if (chatId && !stash.originChatId) {
1823
1828
  this.persistence.saveStash({ ...stash, originChatId: chatId });
1824
1829
  }
1825
1830
  this.broadcast({
package/dist/mcp.js CHANGED
@@ -129,6 +129,10 @@ class WorktreeManager {
129
129
  const { readdirSync } = await import("fs");
130
130
  const entries = readdirSync(worktreesDir);
131
131
  for (const entry of entries) {
132
+ if (entry.startsWith("screenshot-")) {
133
+ logger.info("worktree", `skipping active screenshot worktree: ${entry}`);
134
+ continue;
135
+ }
132
136
  const worktreePath = join2(worktreesDir, entry);
133
137
  logger.info("worktree", `cleaning up stale worktree: ${entry}`);
134
138
  try {
@@ -778,6 +782,20 @@ function parseAiResult(text) {
778
782
  return null;
779
783
  }
780
784
  }
785
+ async function fallbackScreenshot(port, projectPath, stashId) {
786
+ try {
787
+ const url = await captureScreenshot(port, projectPath, stashId);
788
+ return {
789
+ primary: url,
790
+ screenshots: [{ url, label: "Homepage", route: "/", isPrimary: true }]
791
+ };
792
+ } catch (err) {
793
+ logger.error("smart-screenshot", `Fallback screenshot also failed for ${stashId}`, {
794
+ error: err instanceof Error ? err.message : String(err)
795
+ });
796
+ return { primary: "", screenshots: [] };
797
+ }
798
+ }
781
799
  async function captureSmartScreenshots(opts) {
782
800
  const { projectPath, stashId, stashBranch, parentBranch, worktreePath, port, model = "haiku", timeout = DEFAULT_TIMEOUT } = opts;
783
801
  const screenshotDir = join5(projectPath, SCREENSHOTS_DIR2);
@@ -787,11 +805,7 @@ async function captureSmartScreenshots(opts) {
787
805
  const diff = await getStashDiff(worktreePath, parentBranch);
788
806
  if (!diff) {
789
807
  logger.info("smart-screenshot", `No diff found for ${stashId}, using simple screenshot`);
790
- const url = await captureScreenshot(port, projectPath, stashId);
791
- return {
792
- primary: url,
793
- screenshots: [{ url, label: "Homepage", route: "/", isPrimary: true }]
794
- };
808
+ return fallbackScreenshot(port, projectPath, stashId);
795
809
  }
796
810
  const processId = `screenshot-ai-${stashId}`;
797
811
  const prompt = buildScreenshotPrompt(port, diff, screenshotDir, stashId);
@@ -821,12 +835,8 @@ async function captureSmartScreenshots(opts) {
821
835
  }
822
836
  const result = parseAiResult(textOutput);
823
837
  if (!result || !result.screenshots || result.screenshots.length === 0) {
824
- logger.info("smart-screenshot", `AI returned no screenshots for ${stashId}, falling back`);
825
- const url = await captureScreenshot(port, projectPath, stashId);
826
- return {
827
- primary: url,
828
- screenshots: [{ url, label: "Homepage", route: "/", isPrimary: true }]
829
- };
838
+ logger.info("smart-screenshot", `AI returned no screenshots for ${stashId} (timedOut=${timedOut}), falling back`);
839
+ return fallbackScreenshot(port, projectPath, stashId);
830
840
  }
831
841
  const screenshots = [];
832
842
  let primaryUrl = "";
@@ -849,11 +859,7 @@ async function captureSmartScreenshots(opts) {
849
859
  }
850
860
  if (screenshots.length === 0) {
851
861
  logger.info("smart-screenshot", `No valid screenshots for ${stashId}, falling back`);
852
- const url = await captureScreenshot(port, projectPath, stashId);
853
- return {
854
- primary: url,
855
- screenshots: [{ url, label: "Homepage", route: "/", isPrimary: true }]
856
- };
862
+ return fallbackScreenshot(port, projectPath, stashId);
857
863
  }
858
864
  if (!primaryUrl) {
859
865
  primaryUrl = screenshots[0].url;
@@ -1002,7 +1008,7 @@ async function generate(opts) {
1002
1008
  const port = await allocatePort();
1003
1009
  const worktree = await worktreeManager.createForGeneration(`screenshot-${stash.id}`);
1004
1010
  const screenshotGit = simpleGit3(worktree.path);
1005
- await screenshotGit.checkout(["-f", stash.branch]);
1011
+ await screenshotGit.checkout(["--detach", stash.branch]);
1006
1012
  const devServer = spawn3({
1007
1013
  cmd: ["npm", "run", "dev"],
1008
1014
  cwd: worktree.path,
@@ -1137,7 +1143,7 @@ async function vary(opts) {
1137
1143
  const port = await allocatePort2();
1138
1144
  const screenshotWorktree = await worktreeManager.createForGeneration(`screenshot-${stashId}`);
1139
1145
  const screenshotGit = simpleGit4(screenshotWorktree.path);
1140
- await screenshotGit.checkout(["-f", stash.branch]);
1146
+ await screenshotGit.checkout(["--detach", stash.branch]);
1141
1147
  const devServer = spawn4({
1142
1148
  cmd: ["npm", "run", "dev"],
1143
1149
  cwd: screenshotWorktree.path,
@@ -1982,9 +1988,8 @@ ${sourceCode.substring(0, 3000)}
1982
1988
  syncStashesFromDisk(projectId, chatId) {
1983
1989
  const diskStashes = this.persistence.listStashes(projectId);
1984
1990
  for (const stash of diskStashes) {
1985
- if (chatId && !stash.originChatId && !this.knownStashIds.has(stash.id)) {
1986
- const updated = { ...stash, originChatId: chatId };
1987
- this.persistence.saveStash(updated);
1991
+ if (chatId && !stash.originChatId) {
1992
+ this.persistence.saveStash({ ...stash, originChatId: chatId });
1988
1993
  }
1989
1994
  this.broadcast({
1990
1995
  type: "stash:status",
@@ -2015,7 +2020,7 @@ ${sourceCode.substring(0, 3000)}
2015
2020
  for (const stash of stashes) {
2016
2021
  const prev = lastStatus.get(stash.id);
2017
2022
  if (!prev || prev !== stash.status) {
2018
- if (chatId && !stash.originChatId && !prev) {
2023
+ if (chatId && !stash.originChatId) {
2019
2024
  this.persistence.saveStash({ ...stash, originChatId: chatId });
2020
2025
  }
2021
2026
  this.broadcast({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stashes",
3
- "version": "0.1.29",
3
+ "version": "0.1.30",
4
4
  "type": "module",
5
5
  "description": "Generate AI-powered UI design explorations in your project",
6
6
  "keywords": [