stashes 0.1.57 → 0.1.59

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
@@ -545,6 +545,31 @@ class PersistenceService {
545
545
  // ../core/dist/ai-process.js
546
546
  var {spawn } = globalThis.Bun;
547
547
  var CLAUDE_BIN = "/opt/homebrew/bin/claude";
548
+ var OVERHEAD_TOOLS = [
549
+ "Agent",
550
+ "TodoWrite",
551
+ "TaskCreate",
552
+ "TaskUpdate",
553
+ "TaskList",
554
+ "TaskGet",
555
+ "Skill",
556
+ "ToolSearch",
557
+ "EnterPlanMode",
558
+ "ExitPlanMode",
559
+ "WebSearch",
560
+ "WebFetch",
561
+ "NotebookEdit",
562
+ "LSP",
563
+ "Read",
564
+ "Write",
565
+ "Edit",
566
+ "Glob",
567
+ "Grep",
568
+ "mcp__UseAI__*",
569
+ "mcp__stashes__*",
570
+ "mcp__plugin_drills*",
571
+ "mcp__plugin_coverit*"
572
+ ];
548
573
  var processes = new Map;
549
574
  function startAiProcess(idOrOpts, prompt, cwd, resumeSessionId, model) {
550
575
  const opts = typeof idOrOpts === "string" ? { id: idOrOpts, prompt, cwd, resumeSessionId, model } : idOrOpts;
@@ -557,6 +582,9 @@ function startAiProcess(idOrOpts, prompt, cwd, resumeSessionId, model) {
557
582
  model: opts.model
558
583
  });
559
584
  const cmd = [CLAUDE_BIN, "-p", opts.prompt, "--output-format=stream-json", "--verbose", "--dangerously-skip-permissions"];
585
+ if (opts.disableOverheadTools) {
586
+ cmd.push("--disallowedTools", OVERHEAD_TOOLS.join(","));
587
+ }
560
588
  if (opts.resumeSessionId) {
561
589
  cmd.push("--resume", opts.resumeSessionId);
562
590
  }
@@ -769,50 +797,61 @@ ${truncatedDiff}`;
769
797
  return "";
770
798
  }
771
799
  }
772
- function buildScreenshotPrompt(port, diff, screenshotDir, stashId) {
800
+ function buildScreenshotPrompt(port, diff, screenshotDir, stashId, componentPath) {
773
801
  const outputPath = join5(screenshotDir, `${stashId}.png`);
774
802
  return [
775
- "## CRITICAL: You are an automated screenshot subprocess. Follow these rules exactly.",
803
+ "## You are a screenshot agent. Your goal: capture a screenshot showing the UI changes described in the diff below.",
776
804
  "",
777
- "FORBIDDEN \u2014 do NOT call any of these tools:",
778
- "- ToolSearch, Skill, Agent, TodoWrite, TaskCreate, TaskUpdate, TaskList",
779
- "- mcp__UseAI__useai_start, mcp__UseAI__useai_end, mcp__UseAI__useai_heartbeat",
780
- "- mcp__stashes__*, mcp__plugin_drills__*, mcp__plugin_coverit__*",
781
- "- EnterPlanMode, ExitPlanMode, WebSearch, WebFetch, LSP",
782
- "- Read, Write, Edit, Glob, Grep (you do NOT need to read code)",
805
+ "## RULES",
806
+ "- ONLY use Playwright MCP tools (prefixed with mcp__plugin_playwright_playwright__)",
807
+ "- Do NOT call: ToolSearch, Skill, Agent, TodoWrite, TaskCreate, TaskUpdate,",
808
+ " mcp__UseAI__*, mcp__stashes__*, mcp__plugin_drills__*, mcp__plugin_coverit__*,",
809
+ " EnterPlanMode, ExitPlanMode, WebSearch, WebFetch, LSP, Read, Write, Edit, Glob, Grep",
810
+ "- You have a strict time budget. Be fast and decisive.",
783
811
  "",
784
- "YOUR ONLY JOB: navigate to the app and take a screenshot using Playwright.",
812
+ `## App: http://localhost:${port}`,
813
+ componentPath ? `## Target component file: ${componentPath}` : "",
785
814
  "",
786
- `## App URL: http://localhost:${port}`,
787
- "",
788
- "## Git diff (shows what changed \u2014 use this to decide where to navigate):",
815
+ "## Git diff of changes:",
789
816
  "```",
790
817
  diff,
791
818
  "```",
792
819
  "",
793
- "## Steps (do these in order, nothing else):",
794
- `1. Navigate: call mcp__plugin_playwright_playwright__browser_navigate with url "http://localhost:${port}"`,
795
- "2. Wait 3 seconds for the page to render",
796
- "3. If the diff shows changes below the fold, scroll down using mcp__plugin_playwright_playwright__browser_evaluate",
797
- `4. Take screenshot: call mcp__plugin_playwright_playwright__browser_take_screenshot with type "png"`,
798
- ` The screenshot will be saved automatically.`,
799
- "5. Output ONLY this JSON (replace the screenshot path with the actual path returned):",
820
+ "## Your task",
821
+ "Study the diff above to understand:",
822
+ "1. WHAT changed (component, styles, layout, content)",
823
+ "2. WHERE it lives in the app (which route? which section of the page? is it behind a tab/modal/click?)",
824
+ "3. WHAT STATE is needed to see it (does a button need to be clicked? a tab selected? a form filled?)",
825
+ "",
826
+ "Then use Playwright tools to navigate there and capture it:",
800
827
  "",
828
+ "**Available Playwright tools:**",
829
+ "- `mcp__plugin_playwright_playwright__browser_navigate` \u2014 go to a URL",
830
+ "- `mcp__plugin_playwright_playwright__browser_snapshot` \u2014 get the page accessibility tree (use this to find elements)",
831
+ "- `mcp__plugin_playwright_playwright__browser_click` \u2014 click an element (use ref from snapshot)",
832
+ "- `mcp__plugin_playwright_playwright__browser_hover` \u2014 hover over an element",
833
+ "- `mcp__plugin_playwright_playwright__browser_evaluate` \u2014 run JS on the page (e.g. scroll, trigger state)",
834
+ "- `mcp__plugin_playwright_playwright__browser_take_screenshot` \u2014 capture the screenshot",
835
+ "",
836
+ "**Typical flow:**",
837
+ `1. Navigate to http://localhost:${port} (or a sub-route if the diff indicates one)`,
838
+ "2. Take a snapshot to understand the page layout",
839
+ "3. If the changed component is not visible: scroll to it, click a tab, open a modal \u2014 whatever is needed",
840
+ "4. Take the screenshot once the changed component is visible",
841
+ "",
842
+ `## Output \u2014 after taking the screenshot, respond with ONLY this JSON:`,
801
843
  "```json",
802
844
  "{",
803
845
  ' "screenshots": [',
804
846
  " {",
805
847
  ` "path": "${outputPath}",`,
806
- ' "label": "Description of what is shown",',
807
- ' "route": "/",',
848
+ ' "label": "Brief description of what the screenshot shows",',
849
+ ' "route": "/the-route-you-navigated-to",',
808
850
  ' "isPrimary": true',
809
851
  " }",
810
852
  " ]",
811
853
  "}",
812
- "```",
813
- "",
814
- "REMEMBER: Do NOT call ToolSearch, Skill, UseAI, Read, or any tool not listed above.",
815
- "You have a strict time limit. Just navigate, screenshot, output JSON, done."
854
+ "```"
816
855
  ].join(`
817
856
  `);
818
857
  }
@@ -846,7 +885,7 @@ async function fallbackScreenshot(port, projectPath, stashId) {
846
885
  }
847
886
  }
848
887
  async function captureSmartScreenshots(opts) {
849
- const { projectPath, stashId, stashBranch, parentBranch, worktreePath, port, model = "sonnet", timeout = DEFAULT_TIMEOUT } = opts;
888
+ const { projectPath, stashId, stashBranch, parentBranch, worktreePath, port, model = "sonnet", timeout = DEFAULT_TIMEOUT, componentPath } = opts;
850
889
  const screenshotDir = join5(projectPath, SCREENSHOTS_DIR2);
851
890
  if (!existsSync5(screenshotDir)) {
852
891
  mkdirSync4(screenshotDir, { recursive: true });
@@ -857,13 +896,14 @@ async function captureSmartScreenshots(opts) {
857
896
  return fallbackScreenshot(port, projectPath, stashId);
858
897
  }
859
898
  const processId = `screenshot-ai-${stashId}`;
860
- const prompt = buildScreenshotPrompt(port, diff, screenshotDir, stashId);
899
+ const prompt = buildScreenshotPrompt(port, diff, screenshotDir, stashId, componentPath);
861
900
  const modelFlag = model === "sonnet" ? "sonnet" : "haiku";
862
901
  const aiProcess = startAiProcess({
863
902
  id: processId,
864
903
  prompt,
865
904
  cwd: worktreePath,
866
- model: modelFlag
905
+ model: modelFlag,
906
+ disableOverheadTools: true
867
907
  });
868
908
  let textOutput = "";
869
909
  let timedOut = false;
@@ -1129,7 +1169,8 @@ async function generate(opts) {
1129
1169
  worktreePath: screenshotWorktree.path,
1130
1170
  port,
1131
1171
  model: opts.screenshotModel,
1132
- timeout: opts.screenshotTimeout
1172
+ timeout: opts.screenshotTimeout,
1173
+ componentPath: stash.componentPath ?? undefined
1133
1174
  });
1134
1175
  const readyStash = { ...generatedStash, status: "ready", screenshotUrl: primary, screenshots };
1135
1176
  completedStashes.push(readyStash);
@@ -1295,7 +1336,8 @@ async function vary(opts) {
1295
1336
  worktreePath: screenshotWorktree.path,
1296
1337
  port,
1297
1338
  model: opts.screenshotModel,
1298
- timeout: opts.screenshotTimeout
1339
+ timeout: opts.screenshotTimeout,
1340
+ componentPath: stash.componentPath ?? undefined
1299
1341
  });
1300
1342
  screenshotPath = result.primary;
1301
1343
  screenshots = [...result.screenshots];
package/dist/mcp.js CHANGED
@@ -530,6 +530,31 @@ class PersistenceService {
530
530
  // ../core/dist/ai-process.js
531
531
  var {spawn } = globalThis.Bun;
532
532
  var CLAUDE_BIN = "/opt/homebrew/bin/claude";
533
+ var OVERHEAD_TOOLS = [
534
+ "Agent",
535
+ "TodoWrite",
536
+ "TaskCreate",
537
+ "TaskUpdate",
538
+ "TaskList",
539
+ "TaskGet",
540
+ "Skill",
541
+ "ToolSearch",
542
+ "EnterPlanMode",
543
+ "ExitPlanMode",
544
+ "WebSearch",
545
+ "WebFetch",
546
+ "NotebookEdit",
547
+ "LSP",
548
+ "Read",
549
+ "Write",
550
+ "Edit",
551
+ "Glob",
552
+ "Grep",
553
+ "mcp__UseAI__*",
554
+ "mcp__stashes__*",
555
+ "mcp__plugin_drills*",
556
+ "mcp__plugin_coverit*"
557
+ ];
533
558
  var processes = new Map;
534
559
  function startAiProcess(idOrOpts, prompt, cwd, resumeSessionId, model) {
535
560
  const opts = typeof idOrOpts === "string" ? { id: idOrOpts, prompt, cwd, resumeSessionId, model } : idOrOpts;
@@ -542,6 +567,9 @@ function startAiProcess(idOrOpts, prompt, cwd, resumeSessionId, model) {
542
567
  model: opts.model
543
568
  });
544
569
  const cmd = [CLAUDE_BIN, "-p", opts.prompt, "--output-format=stream-json", "--verbose", "--dangerously-skip-permissions"];
570
+ if (opts.disableOverheadTools) {
571
+ cmd.push("--disallowedTools", OVERHEAD_TOOLS.join(","));
572
+ }
545
573
  if (opts.resumeSessionId) {
546
574
  cmd.push("--resume", opts.resumeSessionId);
547
575
  }
@@ -754,50 +782,61 @@ ${truncatedDiff}`;
754
782
  return "";
755
783
  }
756
784
  }
757
- function buildScreenshotPrompt(port, diff, screenshotDir, stashId) {
785
+ function buildScreenshotPrompt(port, diff, screenshotDir, stashId, componentPath) {
758
786
  const outputPath = join5(screenshotDir, `${stashId}.png`);
759
787
  return [
760
- "## CRITICAL: You are an automated screenshot subprocess. Follow these rules exactly.",
788
+ "## You are a screenshot agent. Your goal: capture a screenshot showing the UI changes described in the diff below.",
761
789
  "",
762
- "FORBIDDEN \u2014 do NOT call any of these tools:",
763
- "- ToolSearch, Skill, Agent, TodoWrite, TaskCreate, TaskUpdate, TaskList",
764
- "- mcp__UseAI__useai_start, mcp__UseAI__useai_end, mcp__UseAI__useai_heartbeat",
765
- "- mcp__stashes__*, mcp__plugin_drills__*, mcp__plugin_coverit__*",
766
- "- EnterPlanMode, ExitPlanMode, WebSearch, WebFetch, LSP",
767
- "- Read, Write, Edit, Glob, Grep (you do NOT need to read code)",
790
+ "## RULES",
791
+ "- ONLY use Playwright MCP tools (prefixed with mcp__plugin_playwright_playwright__)",
792
+ "- Do NOT call: ToolSearch, Skill, Agent, TodoWrite, TaskCreate, TaskUpdate,",
793
+ " mcp__UseAI__*, mcp__stashes__*, mcp__plugin_drills__*, mcp__plugin_coverit__*,",
794
+ " EnterPlanMode, ExitPlanMode, WebSearch, WebFetch, LSP, Read, Write, Edit, Glob, Grep",
795
+ "- You have a strict time budget. Be fast and decisive.",
768
796
  "",
769
- "YOUR ONLY JOB: navigate to the app and take a screenshot using Playwright.",
797
+ `## App: http://localhost:${port}`,
798
+ componentPath ? `## Target component file: ${componentPath}` : "",
770
799
  "",
771
- `## App URL: http://localhost:${port}`,
772
- "",
773
- "## Git diff (shows what changed \u2014 use this to decide where to navigate):",
800
+ "## Git diff of changes:",
774
801
  "```",
775
802
  diff,
776
803
  "```",
777
804
  "",
778
- "## Steps (do these in order, nothing else):",
779
- `1. Navigate: call mcp__plugin_playwright_playwright__browser_navigate with url "http://localhost:${port}"`,
780
- "2. Wait 3 seconds for the page to render",
781
- "3. If the diff shows changes below the fold, scroll down using mcp__plugin_playwright_playwright__browser_evaluate",
782
- `4. Take screenshot: call mcp__plugin_playwright_playwright__browser_take_screenshot with type "png"`,
783
- ` The screenshot will be saved automatically.`,
784
- "5. Output ONLY this JSON (replace the screenshot path with the actual path returned):",
805
+ "## Your task",
806
+ "Study the diff above to understand:",
807
+ "1. WHAT changed (component, styles, layout, content)",
808
+ "2. WHERE it lives in the app (which route? which section of the page? is it behind a tab/modal/click?)",
809
+ "3. WHAT STATE is needed to see it (does a button need to be clicked? a tab selected? a form filled?)",
810
+ "",
811
+ "Then use Playwright tools to navigate there and capture it:",
785
812
  "",
813
+ "**Available Playwright tools:**",
814
+ "- `mcp__plugin_playwright_playwright__browser_navigate` \u2014 go to a URL",
815
+ "- `mcp__plugin_playwright_playwright__browser_snapshot` \u2014 get the page accessibility tree (use this to find elements)",
816
+ "- `mcp__plugin_playwright_playwright__browser_click` \u2014 click an element (use ref from snapshot)",
817
+ "- `mcp__plugin_playwright_playwright__browser_hover` \u2014 hover over an element",
818
+ "- `mcp__plugin_playwright_playwright__browser_evaluate` \u2014 run JS on the page (e.g. scroll, trigger state)",
819
+ "- `mcp__plugin_playwright_playwright__browser_take_screenshot` \u2014 capture the screenshot",
820
+ "",
821
+ "**Typical flow:**",
822
+ `1. Navigate to http://localhost:${port} (or a sub-route if the diff indicates one)`,
823
+ "2. Take a snapshot to understand the page layout",
824
+ "3. If the changed component is not visible: scroll to it, click a tab, open a modal \u2014 whatever is needed",
825
+ "4. Take the screenshot once the changed component is visible",
826
+ "",
827
+ `## Output \u2014 after taking the screenshot, respond with ONLY this JSON:`,
786
828
  "```json",
787
829
  "{",
788
830
  ' "screenshots": [',
789
831
  " {",
790
832
  ` "path": "${outputPath}",`,
791
- ' "label": "Description of what is shown",',
792
- ' "route": "/",',
833
+ ' "label": "Brief description of what the screenshot shows",',
834
+ ' "route": "/the-route-you-navigated-to",',
793
835
  ' "isPrimary": true',
794
836
  " }",
795
837
  " ]",
796
838
  "}",
797
- "```",
798
- "",
799
- "REMEMBER: Do NOT call ToolSearch, Skill, UseAI, Read, or any tool not listed above.",
800
- "You have a strict time limit. Just navigate, screenshot, output JSON, done."
839
+ "```"
801
840
  ].join(`
802
841
  `);
803
842
  }
@@ -831,7 +870,7 @@ async function fallbackScreenshot(port, projectPath, stashId) {
831
870
  }
832
871
  }
833
872
  async function captureSmartScreenshots(opts) {
834
- const { projectPath, stashId, stashBranch, parentBranch, worktreePath, port, model = "sonnet", timeout = DEFAULT_TIMEOUT } = opts;
873
+ const { projectPath, stashId, stashBranch, parentBranch, worktreePath, port, model = "sonnet", timeout = DEFAULT_TIMEOUT, componentPath } = opts;
835
874
  const screenshotDir = join5(projectPath, SCREENSHOTS_DIR2);
836
875
  if (!existsSync5(screenshotDir)) {
837
876
  mkdirSync4(screenshotDir, { recursive: true });
@@ -842,13 +881,14 @@ async function captureSmartScreenshots(opts) {
842
881
  return fallbackScreenshot(port, projectPath, stashId);
843
882
  }
844
883
  const processId = `screenshot-ai-${stashId}`;
845
- const prompt = buildScreenshotPrompt(port, diff, screenshotDir, stashId);
884
+ const prompt = buildScreenshotPrompt(port, diff, screenshotDir, stashId, componentPath);
846
885
  const modelFlag = model === "sonnet" ? "sonnet" : "haiku";
847
886
  const aiProcess = startAiProcess({
848
887
  id: processId,
849
888
  prompt,
850
889
  cwd: worktreePath,
851
- model: modelFlag
890
+ model: modelFlag,
891
+ disableOverheadTools: true
852
892
  });
853
893
  let textOutput = "";
854
894
  let timedOut = false;
@@ -1114,7 +1154,8 @@ async function generate(opts) {
1114
1154
  worktreePath: screenshotWorktree.path,
1115
1155
  port,
1116
1156
  model: opts.screenshotModel,
1117
- timeout: opts.screenshotTimeout
1157
+ timeout: opts.screenshotTimeout,
1158
+ componentPath: stash.componentPath ?? undefined
1118
1159
  });
1119
1160
  const readyStash = { ...generatedStash, status: "ready", screenshotUrl: primary, screenshots };
1120
1161
  completedStashes.push(readyStash);
@@ -1280,7 +1321,8 @@ async function vary(opts) {
1280
1321
  worktreePath: screenshotWorktree.path,
1281
1322
  port,
1282
1323
  model: opts.screenshotModel,
1283
- timeout: opts.screenshotTimeout
1324
+ timeout: opts.screenshotTimeout,
1325
+ componentPath: stash.componentPath ?? undefined
1284
1326
  });
1285
1327
  screenshotPath = result.primary;
1286
1328
  screenshots = [...result.screenshots];