stashes 0.1.53 → 0.1.54
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 +76 -62
- package/dist/mcp.js +76 -62
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -804,33 +804,49 @@ ${truncatedDiff}`;
|
|
|
804
804
|
}
|
|
805
805
|
}
|
|
806
806
|
function buildScreenshotPrompt(port, diff, screenshotDir, stashId) {
|
|
807
|
+
const outputPath = join6(screenshotDir, `${stashId}.png`);
|
|
807
808
|
return [
|
|
808
|
-
"You are a screenshot assistant. Take
|
|
809
|
+
"You are a screenshot assistant. Take a screenshot of a running web app.",
|
|
809
810
|
"Be fast \u2014 you have a strict time limit.",
|
|
810
811
|
"",
|
|
811
|
-
|
|
812
|
+
`The app is running at: http://localhost:${port}`,
|
|
812
813
|
"",
|
|
813
|
-
"## Git diff of changes:",
|
|
814
|
+
"## Git diff of changes (tells you what was modified):",
|
|
814
815
|
"```",
|
|
815
816
|
diff,
|
|
816
817
|
"```",
|
|
817
818
|
"",
|
|
818
|
-
"##
|
|
819
|
-
|
|
820
|
-
"
|
|
821
|
-
"3. If the change is on a different route, navigate to it",
|
|
822
|
-
"4. If the change is behind an interaction (tab click, modal open), perform it with mcp__playwright__browser_click",
|
|
823
|
-
`5. Call mcp__playwright__browser_take_screenshot to capture the page`,
|
|
824
|
-
"6. Respond with the JSON below",
|
|
819
|
+
"## Instructions",
|
|
820
|
+
"Use mcp__playwright__browser_run_code to navigate AND screenshot in a single call.",
|
|
821
|
+
"From the diff, determine the route/section that shows the changes.",
|
|
825
822
|
"",
|
|
826
|
-
|
|
827
|
-
|
|
823
|
+
"**Call this tool:**",
|
|
824
|
+
"```",
|
|
825
|
+
"mcp__playwright__browser_run_code",
|
|
826
|
+
"```",
|
|
827
|
+
"",
|
|
828
|
+
"**With this code parameter** (adjust the URL path and scroll if needed):",
|
|
829
|
+
"```javascript",
|
|
830
|
+
`async (page) => {`,
|
|
831
|
+
` await page.setViewportSize({ width: 1280, height: 720 });`,
|
|
832
|
+
` await page.goto('http://localhost:${port}', { waitUntil: 'networkidle', timeout: 15000 });`,
|
|
833
|
+
` // If the changed component is further down, scroll to it:`,
|
|
834
|
+
` // await page.evaluate(() => window.scrollTo(0, 500));`,
|
|
835
|
+
` // await page.waitForTimeout(500);`,
|
|
836
|
+
` await page.waitForTimeout(2000);`,
|
|
837
|
+
` await page.screenshot({ path: '${outputPath.replace(/'/g, "\\'")}', type: 'png' });`,
|
|
838
|
+
` return 'saved';`,
|
|
839
|
+
`}`,
|
|
840
|
+
"```",
|
|
841
|
+
"",
|
|
842
|
+
"If the diff shows changes to a component that requires scrolling or navigation,",
|
|
843
|
+
"adjust the code: change the URL path, add scrollTo, or click a tab first.",
|
|
828
844
|
"",
|
|
829
|
-
|
|
845
|
+
`After the tool call succeeds, respond with ONLY this JSON:`,
|
|
830
846
|
"{",
|
|
831
847
|
' "screenshots": [',
|
|
832
848
|
" {",
|
|
833
|
-
` "path": "${
|
|
849
|
+
` "path": "${outputPath}",`,
|
|
834
850
|
' "label": "Short description of what is shown",',
|
|
835
851
|
' "route": "/the-url-path",',
|
|
836
852
|
' "isPrimary": true',
|
|
@@ -1124,8 +1140,53 @@ async function generate(opts) {
|
|
|
1124
1140
|
return;
|
|
1125
1141
|
}
|
|
1126
1142
|
const generatedStash = { ...stash, status: "screenshotting" };
|
|
1127
|
-
completedStashes.push(generatedStash);
|
|
1128
1143
|
persistence.saveStash(generatedStash);
|
|
1144
|
+
emit(onProgress, { type: "screenshotting", stashId });
|
|
1145
|
+
try {
|
|
1146
|
+
const port = await allocatePort();
|
|
1147
|
+
const screenshotWorktree = await worktreeManager.createForGeneration(`screenshot-${stashId}`);
|
|
1148
|
+
const screenshotGit = simpleGit3(screenshotWorktree.path);
|
|
1149
|
+
await screenshotGit.checkout(["--detach", stash.branch]);
|
|
1150
|
+
const devServer = spawn3({
|
|
1151
|
+
cmd: ["npm", "run", "dev"],
|
|
1152
|
+
cwd: screenshotWorktree.path,
|
|
1153
|
+
stdin: "ignore",
|
|
1154
|
+
stdout: "pipe",
|
|
1155
|
+
stderr: "pipe",
|
|
1156
|
+
env: { ...process.env, PORT: String(port), BROWSER: "none" }
|
|
1157
|
+
});
|
|
1158
|
+
try {
|
|
1159
|
+
await waitForPort(port, 60000);
|
|
1160
|
+
await new Promise((r) => setTimeout(r, 2000));
|
|
1161
|
+
const mainGit = simpleGit3(projectPath);
|
|
1162
|
+
const parentBranch = (await mainGit.revparse(["HEAD"])).trim();
|
|
1163
|
+
const { primary, screenshots } = await captureSmartScreenshots({
|
|
1164
|
+
projectPath,
|
|
1165
|
+
stashId,
|
|
1166
|
+
stashBranch: stash.branch,
|
|
1167
|
+
parentBranch,
|
|
1168
|
+
worktreePath: screenshotWorktree.path,
|
|
1169
|
+
port,
|
|
1170
|
+
model: opts.screenshotModel,
|
|
1171
|
+
timeout: opts.screenshotTimeout
|
|
1172
|
+
});
|
|
1173
|
+
const readyStash = { ...generatedStash, status: "ready", screenshotUrl: primary, screenshots };
|
|
1174
|
+
completedStashes.push(readyStash);
|
|
1175
|
+
persistence.saveStash(readyStash);
|
|
1176
|
+
emit(onProgress, { type: "ready", stashId, screenshotPath: primary });
|
|
1177
|
+
} finally {
|
|
1178
|
+
devServer.kill();
|
|
1179
|
+
}
|
|
1180
|
+
await worktreeManager.removeGeneration(`screenshot-${stashId}`);
|
|
1181
|
+
} catch (err) {
|
|
1182
|
+
logger.error("generation", `screenshot failed for ${stashId}`, {
|
|
1183
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1184
|
+
});
|
|
1185
|
+
const readyStash = { ...generatedStash, status: "ready" };
|
|
1186
|
+
completedStashes.push(readyStash);
|
|
1187
|
+
persistence.saveStash(readyStash);
|
|
1188
|
+
emit(onProgress, { type: "ready", stashId, screenshotPath: "" });
|
|
1189
|
+
}
|
|
1129
1190
|
} catch (error) {
|
|
1130
1191
|
const errorMsg = error instanceof Error ? error.message : "Unknown error";
|
|
1131
1192
|
persistence.saveStash({ ...stash, status: "error", error: errorMsg });
|
|
@@ -1135,53 +1196,6 @@ async function generate(opts) {
|
|
|
1135
1196
|
}
|
|
1136
1197
|
});
|
|
1137
1198
|
await Promise.all(stashPromises);
|
|
1138
|
-
for (const stash of completedStashes) {
|
|
1139
|
-
emit(onProgress, { type: "screenshotting", stashId: stash.id });
|
|
1140
|
-
try {
|
|
1141
|
-
const port = await allocatePort();
|
|
1142
|
-
const worktree = await worktreeManager.createForGeneration(`screenshot-${stash.id}`);
|
|
1143
|
-
const screenshotGit = simpleGit3(worktree.path);
|
|
1144
|
-
await screenshotGit.checkout(["--detach", stash.branch]);
|
|
1145
|
-
const devServer = spawn3({
|
|
1146
|
-
cmd: ["npm", "run", "dev"],
|
|
1147
|
-
cwd: worktree.path,
|
|
1148
|
-
stdin: "ignore",
|
|
1149
|
-
stdout: "pipe",
|
|
1150
|
-
stderr: "pipe",
|
|
1151
|
-
env: { ...process.env, PORT: String(port), BROWSER: "none" }
|
|
1152
|
-
});
|
|
1153
|
-
try {
|
|
1154
|
-
await waitForPort(port, 60000);
|
|
1155
|
-
await new Promise((r) => setTimeout(r, 2000));
|
|
1156
|
-
const mainGit = simpleGit3(projectPath);
|
|
1157
|
-
const parentBranch = (await mainGit.revparse(["HEAD"])).trim();
|
|
1158
|
-
const { primary, screenshots } = await captureSmartScreenshots({
|
|
1159
|
-
projectPath,
|
|
1160
|
-
stashId: stash.id,
|
|
1161
|
-
stashBranch: stash.branch,
|
|
1162
|
-
parentBranch,
|
|
1163
|
-
worktreePath: worktree.path,
|
|
1164
|
-
port,
|
|
1165
|
-
model: opts.screenshotModel,
|
|
1166
|
-
timeout: opts.screenshotTimeout
|
|
1167
|
-
});
|
|
1168
|
-
const updatedStash = { ...stash, status: "ready", screenshotUrl: primary, screenshots };
|
|
1169
|
-
persistence.saveStash(updatedStash);
|
|
1170
|
-
const idx = completedStashes.indexOf(stash);
|
|
1171
|
-
completedStashes[idx] = updatedStash;
|
|
1172
|
-
emit(onProgress, { type: "ready", stashId: stash.id, screenshotPath: primary });
|
|
1173
|
-
} finally {
|
|
1174
|
-
devServer.kill();
|
|
1175
|
-
}
|
|
1176
|
-
await worktreeManager.removeGeneration(`screenshot-${stash.id}`);
|
|
1177
|
-
} catch (err) {
|
|
1178
|
-
logger.error("generation", `screenshot failed for ${stash.id}`, {
|
|
1179
|
-
error: err instanceof Error ? err.message : String(err)
|
|
1180
|
-
});
|
|
1181
|
-
persistence.saveStash({ ...stash, status: "ready" });
|
|
1182
|
-
emit(onProgress, { type: "ready", stashId: stash.id, screenshotPath: "" });
|
|
1183
|
-
}
|
|
1184
|
-
}
|
|
1185
1199
|
return completedStashes;
|
|
1186
1200
|
}
|
|
1187
1201
|
// ../core/dist/vary.js
|
package/dist/mcp.js
CHANGED
|
@@ -789,33 +789,49 @@ ${truncatedDiff}`;
|
|
|
789
789
|
}
|
|
790
790
|
}
|
|
791
791
|
function buildScreenshotPrompt(port, diff, screenshotDir, stashId) {
|
|
792
|
+
const outputPath = join6(screenshotDir, `${stashId}.png`);
|
|
792
793
|
return [
|
|
793
|
-
"You are a screenshot assistant. Take
|
|
794
|
+
"You are a screenshot assistant. Take a screenshot of a running web app.",
|
|
794
795
|
"Be fast \u2014 you have a strict time limit.",
|
|
795
796
|
"",
|
|
796
|
-
|
|
797
|
+
`The app is running at: http://localhost:${port}`,
|
|
797
798
|
"",
|
|
798
|
-
"## Git diff of changes:",
|
|
799
|
+
"## Git diff of changes (tells you what was modified):",
|
|
799
800
|
"```",
|
|
800
801
|
diff,
|
|
801
802
|
"```",
|
|
802
803
|
"",
|
|
803
|
-
"##
|
|
804
|
-
|
|
805
|
-
"
|
|
806
|
-
"3. If the change is on a different route, navigate to it",
|
|
807
|
-
"4. If the change is behind an interaction (tab click, modal open), perform it with mcp__playwright__browser_click",
|
|
808
|
-
`5. Call mcp__playwright__browser_take_screenshot to capture the page`,
|
|
809
|
-
"6. Respond with the JSON below",
|
|
804
|
+
"## Instructions",
|
|
805
|
+
"Use mcp__playwright__browser_run_code to navigate AND screenshot in a single call.",
|
|
806
|
+
"From the diff, determine the route/section that shows the changes.",
|
|
810
807
|
"",
|
|
811
|
-
|
|
812
|
-
|
|
808
|
+
"**Call this tool:**",
|
|
809
|
+
"```",
|
|
810
|
+
"mcp__playwright__browser_run_code",
|
|
811
|
+
"```",
|
|
812
|
+
"",
|
|
813
|
+
"**With this code parameter** (adjust the URL path and scroll if needed):",
|
|
814
|
+
"```javascript",
|
|
815
|
+
`async (page) => {`,
|
|
816
|
+
` await page.setViewportSize({ width: 1280, height: 720 });`,
|
|
817
|
+
` await page.goto('http://localhost:${port}', { waitUntil: 'networkidle', timeout: 15000 });`,
|
|
818
|
+
` // If the changed component is further down, scroll to it:`,
|
|
819
|
+
` // await page.evaluate(() => window.scrollTo(0, 500));`,
|
|
820
|
+
` // await page.waitForTimeout(500);`,
|
|
821
|
+
` await page.waitForTimeout(2000);`,
|
|
822
|
+
` await page.screenshot({ path: '${outputPath.replace(/'/g, "\\'")}', type: 'png' });`,
|
|
823
|
+
` return 'saved';`,
|
|
824
|
+
`}`,
|
|
825
|
+
"```",
|
|
826
|
+
"",
|
|
827
|
+
"If the diff shows changes to a component that requires scrolling or navigation,",
|
|
828
|
+
"adjust the code: change the URL path, add scrollTo, or click a tab first.",
|
|
813
829
|
"",
|
|
814
|
-
|
|
830
|
+
`After the tool call succeeds, respond with ONLY this JSON:`,
|
|
815
831
|
"{",
|
|
816
832
|
' "screenshots": [',
|
|
817
833
|
" {",
|
|
818
|
-
` "path": "${
|
|
834
|
+
` "path": "${outputPath}",`,
|
|
819
835
|
' "label": "Short description of what is shown",',
|
|
820
836
|
' "route": "/the-url-path",',
|
|
821
837
|
' "isPrimary": true',
|
|
@@ -1109,8 +1125,53 @@ async function generate(opts) {
|
|
|
1109
1125
|
return;
|
|
1110
1126
|
}
|
|
1111
1127
|
const generatedStash = { ...stash, status: "screenshotting" };
|
|
1112
|
-
completedStashes.push(generatedStash);
|
|
1113
1128
|
persistence.saveStash(generatedStash);
|
|
1129
|
+
emit(onProgress, { type: "screenshotting", stashId });
|
|
1130
|
+
try {
|
|
1131
|
+
const port = await allocatePort();
|
|
1132
|
+
const screenshotWorktree = await worktreeManager.createForGeneration(`screenshot-${stashId}`);
|
|
1133
|
+
const screenshotGit = simpleGit3(screenshotWorktree.path);
|
|
1134
|
+
await screenshotGit.checkout(["--detach", stash.branch]);
|
|
1135
|
+
const devServer = spawn3({
|
|
1136
|
+
cmd: ["npm", "run", "dev"],
|
|
1137
|
+
cwd: screenshotWorktree.path,
|
|
1138
|
+
stdin: "ignore",
|
|
1139
|
+
stdout: "pipe",
|
|
1140
|
+
stderr: "pipe",
|
|
1141
|
+
env: { ...process.env, PORT: String(port), BROWSER: "none" }
|
|
1142
|
+
});
|
|
1143
|
+
try {
|
|
1144
|
+
await waitForPort(port, 60000);
|
|
1145
|
+
await new Promise((r) => setTimeout(r, 2000));
|
|
1146
|
+
const mainGit = simpleGit3(projectPath);
|
|
1147
|
+
const parentBranch = (await mainGit.revparse(["HEAD"])).trim();
|
|
1148
|
+
const { primary, screenshots } = await captureSmartScreenshots({
|
|
1149
|
+
projectPath,
|
|
1150
|
+
stashId,
|
|
1151
|
+
stashBranch: stash.branch,
|
|
1152
|
+
parentBranch,
|
|
1153
|
+
worktreePath: screenshotWorktree.path,
|
|
1154
|
+
port,
|
|
1155
|
+
model: opts.screenshotModel,
|
|
1156
|
+
timeout: opts.screenshotTimeout
|
|
1157
|
+
});
|
|
1158
|
+
const readyStash = { ...generatedStash, status: "ready", screenshotUrl: primary, screenshots };
|
|
1159
|
+
completedStashes.push(readyStash);
|
|
1160
|
+
persistence.saveStash(readyStash);
|
|
1161
|
+
emit(onProgress, { type: "ready", stashId, screenshotPath: primary });
|
|
1162
|
+
} finally {
|
|
1163
|
+
devServer.kill();
|
|
1164
|
+
}
|
|
1165
|
+
await worktreeManager.removeGeneration(`screenshot-${stashId}`);
|
|
1166
|
+
} catch (err) {
|
|
1167
|
+
logger.error("generation", `screenshot failed for ${stashId}`, {
|
|
1168
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1169
|
+
});
|
|
1170
|
+
const readyStash = { ...generatedStash, status: "ready" };
|
|
1171
|
+
completedStashes.push(readyStash);
|
|
1172
|
+
persistence.saveStash(readyStash);
|
|
1173
|
+
emit(onProgress, { type: "ready", stashId, screenshotPath: "" });
|
|
1174
|
+
}
|
|
1114
1175
|
} catch (error) {
|
|
1115
1176
|
const errorMsg = error instanceof Error ? error.message : "Unknown error";
|
|
1116
1177
|
persistence.saveStash({ ...stash, status: "error", error: errorMsg });
|
|
@@ -1120,53 +1181,6 @@ async function generate(opts) {
|
|
|
1120
1181
|
}
|
|
1121
1182
|
});
|
|
1122
1183
|
await Promise.all(stashPromises);
|
|
1123
|
-
for (const stash of completedStashes) {
|
|
1124
|
-
emit(onProgress, { type: "screenshotting", stashId: stash.id });
|
|
1125
|
-
try {
|
|
1126
|
-
const port = await allocatePort();
|
|
1127
|
-
const worktree = await worktreeManager.createForGeneration(`screenshot-${stash.id}`);
|
|
1128
|
-
const screenshotGit = simpleGit3(worktree.path);
|
|
1129
|
-
await screenshotGit.checkout(["--detach", stash.branch]);
|
|
1130
|
-
const devServer = spawn3({
|
|
1131
|
-
cmd: ["npm", "run", "dev"],
|
|
1132
|
-
cwd: worktree.path,
|
|
1133
|
-
stdin: "ignore",
|
|
1134
|
-
stdout: "pipe",
|
|
1135
|
-
stderr: "pipe",
|
|
1136
|
-
env: { ...process.env, PORT: String(port), BROWSER: "none" }
|
|
1137
|
-
});
|
|
1138
|
-
try {
|
|
1139
|
-
await waitForPort(port, 60000);
|
|
1140
|
-
await new Promise((r) => setTimeout(r, 2000));
|
|
1141
|
-
const mainGit = simpleGit3(projectPath);
|
|
1142
|
-
const parentBranch = (await mainGit.revparse(["HEAD"])).trim();
|
|
1143
|
-
const { primary, screenshots } = await captureSmartScreenshots({
|
|
1144
|
-
projectPath,
|
|
1145
|
-
stashId: stash.id,
|
|
1146
|
-
stashBranch: stash.branch,
|
|
1147
|
-
parentBranch,
|
|
1148
|
-
worktreePath: worktree.path,
|
|
1149
|
-
port,
|
|
1150
|
-
model: opts.screenshotModel,
|
|
1151
|
-
timeout: opts.screenshotTimeout
|
|
1152
|
-
});
|
|
1153
|
-
const updatedStash = { ...stash, status: "ready", screenshotUrl: primary, screenshots };
|
|
1154
|
-
persistence.saveStash(updatedStash);
|
|
1155
|
-
const idx = completedStashes.indexOf(stash);
|
|
1156
|
-
completedStashes[idx] = updatedStash;
|
|
1157
|
-
emit(onProgress, { type: "ready", stashId: stash.id, screenshotPath: primary });
|
|
1158
|
-
} finally {
|
|
1159
|
-
devServer.kill();
|
|
1160
|
-
}
|
|
1161
|
-
await worktreeManager.removeGeneration(`screenshot-${stash.id}`);
|
|
1162
|
-
} catch (err) {
|
|
1163
|
-
logger.error("generation", `screenshot failed for ${stash.id}`, {
|
|
1164
|
-
error: err instanceof Error ? err.message : String(err)
|
|
1165
|
-
});
|
|
1166
|
-
persistence.saveStash({ ...stash, status: "ready" });
|
|
1167
|
-
emit(onProgress, { type: "ready", stashId: stash.id, screenshotPath: "" });
|
|
1168
|
-
}
|
|
1169
|
-
}
|
|
1170
1184
|
return completedStashes;
|
|
1171
1185
|
}
|
|
1172
1186
|
// ../core/dist/vary.js
|