stashes 0.1.29 → 0.1.31
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
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(["
|
|
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(["
|
|
1264
|
+
await screenshotGit.checkout(["--detach", stash.branch]);
|
|
1259
1265
|
const devServer = spawn4({
|
|
1260
1266
|
cmd: ["npm", "run", "dev"],
|
|
1261
1267
|
cwd: screenshotWorktree.path,
|
|
@@ -1612,6 +1618,7 @@ class StashService {
|
|
|
1612
1618
|
this.isProcessingMessage = false;
|
|
1613
1619
|
}
|
|
1614
1620
|
async processMessage(projectId, chatId, message, referenceStashIds, componentContext) {
|
|
1621
|
+
const preExistingStashIds = new Set(this.persistence.listStashes(projectId).map((s) => s.id));
|
|
1615
1622
|
const component = componentContext ? { name: componentContext.name, filePath: this.selectedComponent?.filePath || "" } : this.selectedComponent;
|
|
1616
1623
|
let sourceCode = "";
|
|
1617
1624
|
const filePath = component?.filePath || "";
|
|
@@ -1770,7 +1777,7 @@ ${sourceCode.substring(0, 3000)}
|
|
|
1770
1777
|
await aiProcess.process.exited;
|
|
1771
1778
|
flushThinking();
|
|
1772
1779
|
flushText();
|
|
1773
|
-
this.syncStashesFromDisk(projectId, chatId);
|
|
1780
|
+
this.syncStashesFromDisk(projectId, chatId, preExistingStashIds);
|
|
1774
1781
|
} catch (err) {
|
|
1775
1782
|
this.broadcast({
|
|
1776
1783
|
type: "ai_stream",
|
|
@@ -1783,12 +1790,11 @@ ${sourceCode.substring(0, 3000)}
|
|
|
1783
1790
|
killAiProcess("chat");
|
|
1784
1791
|
}
|
|
1785
1792
|
}
|
|
1786
|
-
syncStashesFromDisk(projectId, chatId) {
|
|
1793
|
+
syncStashesFromDisk(projectId, chatId, preExistingIds) {
|
|
1787
1794
|
const diskStashes = this.persistence.listStashes(projectId);
|
|
1788
1795
|
for (const stash of diskStashes) {
|
|
1789
|
-
if (chatId && !stash.originChatId && !
|
|
1790
|
-
|
|
1791
|
-
this.persistence.saveStash(updated);
|
|
1796
|
+
if (chatId && !stash.originChatId && preExistingIds && !preExistingIds.has(stash.id)) {
|
|
1797
|
+
this.persistence.saveStash({ ...stash, originChatId: chatId });
|
|
1792
1798
|
}
|
|
1793
1799
|
this.broadcast({
|
|
1794
1800
|
type: "stash:status",
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(["
|
|
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(["
|
|
1146
|
+
await screenshotGit.checkout(["--detach", stash.branch]);
|
|
1141
1147
|
const devServer = spawn4({
|
|
1142
1148
|
cmd: ["npm", "run", "dev"],
|
|
1143
1149
|
cwd: screenshotWorktree.path,
|
|
@@ -1808,6 +1814,7 @@ class StashService {
|
|
|
1808
1814
|
this.isProcessingMessage = false;
|
|
1809
1815
|
}
|
|
1810
1816
|
async processMessage(projectId, chatId, message, referenceStashIds, componentContext) {
|
|
1817
|
+
const preExistingStashIds = new Set(this.persistence.listStashes(projectId).map((s) => s.id));
|
|
1811
1818
|
const component = componentContext ? { name: componentContext.name, filePath: this.selectedComponent?.filePath || "" } : this.selectedComponent;
|
|
1812
1819
|
let sourceCode = "";
|
|
1813
1820
|
const filePath = component?.filePath || "";
|
|
@@ -1966,7 +1973,7 @@ ${sourceCode.substring(0, 3000)}
|
|
|
1966
1973
|
await aiProcess.process.exited;
|
|
1967
1974
|
flushThinking();
|
|
1968
1975
|
flushText();
|
|
1969
|
-
this.syncStashesFromDisk(projectId, chatId);
|
|
1976
|
+
this.syncStashesFromDisk(projectId, chatId, preExistingStashIds);
|
|
1970
1977
|
} catch (err) {
|
|
1971
1978
|
this.broadcast({
|
|
1972
1979
|
type: "ai_stream",
|
|
@@ -1979,12 +1986,11 @@ ${sourceCode.substring(0, 3000)}
|
|
|
1979
1986
|
killAiProcess("chat");
|
|
1980
1987
|
}
|
|
1981
1988
|
}
|
|
1982
|
-
syncStashesFromDisk(projectId, chatId) {
|
|
1989
|
+
syncStashesFromDisk(projectId, chatId, preExistingIds) {
|
|
1983
1990
|
const diskStashes = this.persistence.listStashes(projectId);
|
|
1984
1991
|
for (const stash of diskStashes) {
|
|
1985
|
-
if (chatId && !stash.originChatId && !
|
|
1986
|
-
|
|
1987
|
-
this.persistence.saveStash(updated);
|
|
1992
|
+
if (chatId && !stash.originChatId && preExistingIds && !preExistingIds.has(stash.id)) {
|
|
1993
|
+
this.persistence.saveStash({ ...stash, originChatId: chatId });
|
|
1988
1994
|
}
|
|
1989
1995
|
this.broadcast({
|
|
1990
1996
|
type: "stash:status",
|