stashes 0.1.56 → 0.1.58

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
@@ -20,7 +20,7 @@ var __require = import.meta.require;
20
20
 
21
21
  // src/index.ts
22
22
  import { readFileSync as readFileSync10 } from "fs";
23
- import { join as join15, dirname as dirname6 } from "path";
23
+ import { join as join14, dirname as dirname6 } from "path";
24
24
  import { fileURLToPath as fileURLToPath3 } from "url";
25
25
  import { Command } from "commander";
26
26
 
@@ -31,9 +31,9 @@ import open from "open";
31
31
  // ../server/dist/index.js
32
32
  import { Hono as Hono2 } from "hono";
33
33
  import { cors } from "hono/cors";
34
- import { join as join11, dirname as dirname3 } from "path";
34
+ import { join as join10, dirname as dirname3 } from "path";
35
35
  import { fileURLToPath } from "url";
36
- import { existsSync as existsSync11, readFileSync as readFileSync6 } from "fs";
36
+ import { existsSync as existsSync10, readFileSync as readFileSync6 } from "fs";
37
37
  // ../shared/dist/constants/index.js
38
38
  var STASHES_PORT = 4000;
39
39
  var DEFAULT_STASH_COUNT = 3;
@@ -56,12 +56,12 @@ var DEFAULT_DIRECTIVES = [
56
56
  ];
57
57
  // ../server/dist/routes/api.js
58
58
  import { Hono } from "hono";
59
- import { join as join10, basename } from "path";
60
- import { existsSync as existsSync10, readFileSync as readFileSync5 } from "fs";
59
+ import { join as join9, basename } from "path";
60
+ import { existsSync as existsSync9, readFileSync as readFileSync5 } from "fs";
61
61
 
62
62
  // ../core/dist/generation.js
63
- import { readFileSync as readFileSync2, existsSync as existsSync7 } from "fs";
64
- import { join as join7 } from "path";
63
+ import { readFileSync as readFileSync2, existsSync as existsSync6 } from "fs";
64
+ import { join as join6 } from "path";
65
65
  var {spawn: spawn3 } = globalThis.Bun;
66
66
  import simpleGit3 from "simple-git";
67
67
 
@@ -544,65 +544,19 @@ class PersistenceService {
544
544
 
545
545
  // ../core/dist/ai-process.js
546
546
  var {spawn } = globalThis.Bun;
547
- import { writeFileSync as writeFileSync2, existsSync as existsSync4, mkdirSync as mkdirSync3 } from "fs";
548
- import { join as join4 } from "path";
549
- import { tmpdir } from "os";
550
547
  var CLAUDE_BIN = "/opt/homebrew/bin/claude";
551
- function getPlaywrightMcpConfigPath() {
552
- const configDir = join4(tmpdir(), "stashes-mcp");
553
- const configPath = join4(configDir, "playwright.json");
554
- if (!existsSync4(configPath)) {
555
- mkdirSync3(configDir, { recursive: true });
556
- writeFileSync2(configPath, JSON.stringify({
557
- mcpServers: {
558
- playwright: { command: "npx", args: ["@playwright/mcp@latest"] }
559
- }
560
- }), "utf-8");
561
- }
562
- return configPath;
563
- }
564
- var OVERHEAD_TOOLS = [
565
- "Agent",
566
- "TodoWrite",
567
- "TaskCreate",
568
- "TaskUpdate",
569
- "TaskList",
570
- "TaskGet",
571
- "Skill",
572
- "ToolSearch",
573
- "EnterPlanMode",
574
- "ExitPlanMode",
575
- "WebSearch",
576
- "WebFetch",
577
- "NotebookEdit",
578
- "mcp__UseAI__*",
579
- "mcp__stashes__*",
580
- "mcp__plugin_drills*",
581
- "mcp__plugin_coverit*"
582
- ];
583
548
  var processes = new Map;
584
549
  function startAiProcess(idOrOpts, prompt, cwd, resumeSessionId, model) {
585
550
  const opts = typeof idOrOpts === "string" ? { id: idOrOpts, prompt, cwd, resumeSessionId, model } : idOrOpts;
586
- const restricted = opts.tools !== undefined;
587
551
  killAiProcess(opts.id);
588
552
  logger.info("claude", `spawning process: ${opts.id}`, {
589
553
  cwd: opts.cwd,
590
554
  promptLength: opts.prompt.length,
591
555
  promptPreview: opts.prompt.substring(0, 100),
592
556
  resumeSessionId: opts.resumeSessionId,
593
- model: opts.model,
594
- restricted,
595
- tools: restricted ? opts.tools.join(",") || "none" : "all"
557
+ model: opts.model
596
558
  });
597
559
  const cmd = [CLAUDE_BIN, "-p", opts.prompt, "--output-format=stream-json", "--verbose", "--dangerously-skip-permissions"];
598
- if (restricted) {
599
- cmd.push("--tools", opts.tools.length > 0 ? opts.tools.join(",") : "");
600
- cmd.push("--disallowedTools", OVERHEAD_TOOLS.join(","));
601
- cmd.push("--strict-mcp-config");
602
- if (opts.mcpConfigPath) {
603
- cmd.push("--mcp-config", opts.mcpConfigPath);
604
- }
605
- }
606
560
  if (opts.resumeSessionId) {
607
561
  cmd.push("--resume", opts.resumeSessionId);
608
562
  }
@@ -733,35 +687,47 @@ async function* parseClaudeStream(proc) {
733
687
  }
734
688
 
735
689
  // ../core/dist/smart-screenshot.js
736
- import { join as join6 } from "path";
737
- import { mkdirSync as mkdirSync5, existsSync as existsSync6 } from "fs";
690
+ import { join as join5 } from "path";
691
+ import { mkdirSync as mkdirSync4, existsSync as existsSync5 } from "fs";
738
692
  import simpleGit2 from "simple-git";
739
693
 
740
694
  // ../core/dist/screenshot.js
741
695
  var {spawn: spawn2 } = globalThis.Bun;
742
- import { join as join5 } from "path";
743
- import { mkdirSync as mkdirSync4, existsSync as existsSync5 } from "fs";
696
+ import { join as join4 } from "path";
697
+ import { mkdirSync as mkdirSync3, existsSync as existsSync4 } from "fs";
744
698
  var SCREENSHOTS_DIR = ".stashes/screenshots";
745
- async function captureScreenshot(port, projectPath, stashId) {
746
- const screenshotsDir = join5(projectPath, SCREENSHOTS_DIR);
747
- if (!existsSync5(screenshotsDir)) {
748
- mkdirSync4(screenshotsDir, { recursive: true });
749
- }
750
- const filename = `${stashId}.png`;
751
- const outputPath = join5(screenshotsDir, filename);
699
+ async function captureScreenshot(portOrOpts, projectPath, stashId) {
700
+ const opts = typeof portOrOpts === "number" ? { port: portOrOpts, projectPath, stashId } : portOrOpts;
701
+ const screenshotsDir = join4(opts.projectPath, SCREENSHOTS_DIR);
702
+ if (!existsSync4(screenshotsDir)) {
703
+ mkdirSync3(screenshotsDir, { recursive: true });
704
+ }
705
+ const filename = `${opts.stashId}.png`;
706
+ const outputPath = join4(screenshotsDir, filename);
707
+ const url = `http://localhost:${opts.port}${opts.route || "/"}`;
708
+ const scrollCode = opts.scrollToSelector ? `
709
+ try {
710
+ const el = await page.waitForSelector('${opts.scrollToSelector.replace(/'/g, "\\'")}', { timeout: 5000 });
711
+ if (el) {
712
+ await el.scrollIntoViewIfNeeded();
713
+ await page.waitForTimeout(500);
714
+ }
715
+ } catch(e) { /* selector not found, capture viewport */ }
716
+ ` : "";
752
717
  const playwrightScript = `
753
718
  const { chromium } = require('playwright');
754
719
  (async () => {
755
720
  const browser = await chromium.launch({ headless: true });
756
721
  const page = await browser.newPage({ viewport: { width: 1280, height: 720 } });
757
722
  try {
758
- await page.goto('http://localhost:${port}', { waitUntil: 'networkidle', timeout: 20000 });
723
+ await page.goto('${url}', { waitUntil: 'networkidle', timeout: 20000 });
759
724
  await page.waitForTimeout(2000);
760
725
  } catch(e) {
761
- await page.goto('http://localhost:${port}', { waitUntil: 'domcontentloaded', timeout: 10000 });
726
+ await page.goto('${url}', { waitUntil: 'domcontentloaded', timeout: 10000 });
762
727
  await page.waitForTimeout(3000);
763
728
  }
764
- await page.screenshot({ path: '${outputPath.replace(/'/g, "\\'")}', type: 'png' });
729
+ ${scrollCode}
730
+ await page.screenshot({ path: '${outputPath.replace(/'/g, "\\'")}', type: 'png'${opts.fullPage ? ", fullPage: true" : ""} });
765
731
  await browser.close();
766
732
  })();
767
733
  `;
@@ -797,62 +763,67 @@ ${diffStat}
797
763
  ## Partial diff (truncated):
798
764
  ${truncatedDiff}`;
799
765
  } catch (err) {
800
- logger.warn("smart-screenshot", `git diff failed, falling back`, {
766
+ logger.warn("smart-screenshot", `git diff failed`, {
801
767
  error: err instanceof Error ? err.message : String(err)
802
768
  });
803
769
  return "";
804
770
  }
805
771
  }
806
- function buildScreenshotPrompt(port, diff, screenshotDir, stashId) {
807
- const outputPath = join6(screenshotDir, `${stashId}.png`);
772
+ function buildScreenshotPrompt(port, diff, screenshotDir, stashId, componentPath) {
773
+ const outputPath = join5(screenshotDir, `${stashId}.png`);
808
774
  return [
809
- "You are a screenshot assistant. Take a screenshot of a running web app.",
810
- "Be fast \u2014 you have a strict time limit.",
775
+ "## You are a screenshot agent. Your goal: capture a screenshot showing the UI changes described in the diff below.",
776
+ "",
777
+ "## RULES",
778
+ "- ONLY use Playwright MCP tools (prefixed with mcp__plugin_playwright_playwright__)",
779
+ "- Do NOT call: ToolSearch, Skill, Agent, TodoWrite, TaskCreate, TaskUpdate,",
780
+ " mcp__UseAI__*, mcp__stashes__*, mcp__plugin_drills__*, mcp__plugin_coverit__*,",
781
+ " EnterPlanMode, ExitPlanMode, WebSearch, WebFetch, LSP, Read, Write, Edit, Glob, Grep",
782
+ "- You have a strict time budget. Be fast and decisive.",
811
783
  "",
812
- `The app is running at: http://localhost:${port}`,
784
+ `## App: http://localhost:${port}`,
785
+ componentPath ? `## Target component file: ${componentPath}` : "",
813
786
  "",
814
- "## Git diff of changes (tells you what was modified):",
787
+ "## Git diff of changes:",
815
788
  "```",
816
789
  diff,
817
790
  "```",
818
791
  "",
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.",
792
+ "## Your task",
793
+ "Study the diff above to understand:",
794
+ "1. WHAT changed (component, styles, layout, content)",
795
+ "2. WHERE it lives in the app (which route? which section of the page? is it behind a tab/modal/click?)",
796
+ "3. WHAT STATE is needed to see it (does a button need to be clicked? a tab selected? a form filled?)",
822
797
  "",
823
- "**Call this tool:**",
824
- "```",
825
- "mcp__playwright__browser_run_code",
826
- "```",
798
+ "Then use Playwright tools to navigate there and capture it:",
827
799
  "",
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
- "```",
800
+ "**Available Playwright tools:**",
801
+ "- `mcp__plugin_playwright_playwright__browser_navigate` \u2014 go to a URL",
802
+ "- `mcp__plugin_playwright_playwright__browser_snapshot` \u2014 get the page accessibility tree (use this to find elements)",
803
+ "- `mcp__plugin_playwright_playwright__browser_click` \u2014 click an element (use ref from snapshot)",
804
+ "- `mcp__plugin_playwright_playwright__browser_hover` \u2014 hover over an element",
805
+ "- `mcp__plugin_playwright_playwright__browser_evaluate` \u2014 run JS on the page (e.g. scroll, trigger state)",
806
+ "- `mcp__plugin_playwright_playwright__browser_take_screenshot` \u2014 capture the screenshot",
841
807
  "",
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.",
808
+ "**Typical flow:**",
809
+ `1. Navigate to http://localhost:${port} (or a sub-route if the diff indicates one)`,
810
+ "2. Take a snapshot to understand the page layout",
811
+ "3. If the changed component is not visible: scroll to it, click a tab, open a modal \u2014 whatever is needed",
812
+ "4. Take the screenshot once the changed component is visible",
844
813
  "",
845
- `After the tool call succeeds, respond with ONLY this JSON:`,
814
+ `## Output \u2014 after taking the screenshot, respond with ONLY this JSON:`,
815
+ "```json",
846
816
  "{",
847
817
  ' "screenshots": [',
848
818
  " {",
849
819
  ` "path": "${outputPath}",`,
850
- ' "label": "Short description of what is shown",',
851
- ' "route": "/the-url-path",',
820
+ ' "label": "Brief description of what the screenshot shows",',
821
+ ' "route": "/the-route-you-navigated-to",',
852
822
  ' "isPrimary": true',
853
823
  " }",
854
824
  " ]",
855
- "}"
825
+ "}",
826
+ "```"
856
827
  ].join(`
857
828
  `);
858
829
  }
@@ -886,10 +857,10 @@ async function fallbackScreenshot(port, projectPath, stashId) {
886
857
  }
887
858
  }
888
859
  async function captureSmartScreenshots(opts) {
889
- const { projectPath, stashId, stashBranch, parentBranch, worktreePath, port, model = "sonnet", timeout = DEFAULT_TIMEOUT } = opts;
890
- const screenshotDir = join6(projectPath, SCREENSHOTS_DIR2);
891
- if (!existsSync6(screenshotDir)) {
892
- mkdirSync5(screenshotDir, { recursive: true });
860
+ const { projectPath, stashId, stashBranch, parentBranch, worktreePath, port, model = "sonnet", timeout = DEFAULT_TIMEOUT, componentPath } = opts;
861
+ const screenshotDir = join5(projectPath, SCREENSHOTS_DIR2);
862
+ if (!existsSync5(screenshotDir)) {
863
+ mkdirSync4(screenshotDir, { recursive: true });
893
864
  }
894
865
  const diff = await getStashDiff(worktreePath, parentBranch);
895
866
  if (!diff) {
@@ -897,15 +868,13 @@ async function captureSmartScreenshots(opts) {
897
868
  return fallbackScreenshot(port, projectPath, stashId);
898
869
  }
899
870
  const processId = `screenshot-ai-${stashId}`;
900
- const prompt = buildScreenshotPrompt(port, diff, screenshotDir, stashId);
871
+ const prompt = buildScreenshotPrompt(port, diff, screenshotDir, stashId, componentPath);
901
872
  const modelFlag = model === "sonnet" ? "sonnet" : "haiku";
902
873
  const aiProcess = startAiProcess({
903
874
  id: processId,
904
875
  prompt,
905
876
  cwd: worktreePath,
906
- model: modelFlag,
907
- tools: [],
908
- mcpConfigPath: getPlaywrightMcpConfigPath()
877
+ model: modelFlag
909
878
  });
910
879
  let textOutput = "";
911
880
  let timedOut = false;
@@ -931,9 +900,9 @@ async function captureSmartScreenshots(opts) {
931
900
  }
932
901
  const result = parseAiResult(textOutput);
933
902
  if (!result || !result.screenshots || result.screenshots.length === 0) {
934
- const expectedPath = join6(screenshotDir, `${stashId}.png`);
935
- if (existsSync6(expectedPath)) {
936
- logger.info("smart-screenshot", `AI saved screenshot for ${stashId} via browser_run_code (no JSON response)`);
903
+ const expectedPath = join5(screenshotDir, `${stashId}.png`);
904
+ if (existsSync5(expectedPath)) {
905
+ logger.info("smart-screenshot", `AI saved screenshot for ${stashId} (no JSON response, but file exists)`);
937
906
  const url = `/api/screenshots/${stashId}.png`;
938
907
  return {
939
908
  primary: url,
@@ -947,18 +916,12 @@ async function captureSmartScreenshots(opts) {
947
916
  let primaryUrl = "";
948
917
  for (const shot of result.screenshots) {
949
918
  const filename = shot.path.split("/").pop() || "";
950
- if (!existsSync6(shot.path)) {
919
+ if (!existsSync5(shot.path)) {
951
920
  logger.warn("smart-screenshot", `Screenshot file not found: ${shot.path}`);
952
921
  continue;
953
922
  }
954
923
  const url = `/api/screenshots/${filename}`;
955
- const screenshot = {
956
- url,
957
- label: shot.label,
958
- route: shot.route,
959
- isPrimary: shot.isPrimary
960
- };
961
- screenshots.push(screenshot);
924
+ screenshots.push({ url, label: shot.label, route: shot.route, isPrimary: shot.isPrimary });
962
925
  if (shot.isPrimary)
963
926
  primaryUrl = url;
964
927
  }
@@ -1041,8 +1004,8 @@ async function generate(opts) {
1041
1004
  const selectedDirectives = directives.slice(0, count);
1042
1005
  let sourceCode = "";
1043
1006
  if (component?.filePath) {
1044
- const sourceFile = join7(projectPath, component.filePath);
1045
- if (existsSync7(sourceFile)) {
1007
+ const sourceFile = join6(projectPath, component.filePath);
1008
+ if (existsSync6(sourceFile)) {
1046
1009
  sourceCode = readFileSync2(sourceFile, "utf-8");
1047
1010
  }
1048
1011
  }
@@ -1177,7 +1140,8 @@ async function generate(opts) {
1177
1140
  worktreePath: screenshotWorktree.path,
1178
1141
  port,
1179
1142
  model: opts.screenshotModel,
1180
- timeout: opts.screenshotTimeout
1143
+ timeout: opts.screenshotTimeout,
1144
+ componentPath: stash.componentPath ?? undefined
1181
1145
  });
1182
1146
  const readyStash = { ...generatedStash, status: "ready", screenshotUrl: primary, screenshots };
1183
1147
  completedStashes.push(readyStash);
@@ -1343,7 +1307,8 @@ async function vary(opts) {
1343
1307
  worktreePath: screenshotWorktree.path,
1344
1308
  port,
1345
1309
  model: opts.screenshotModel,
1346
- timeout: opts.screenshotTimeout
1310
+ timeout: opts.screenshotTimeout,
1311
+ componentPath: stash.componentPath ?? undefined
1347
1312
  });
1348
1313
  screenshotPath = result.primary;
1349
1314
  screenshots = [...result.screenshots];
@@ -1412,8 +1377,8 @@ async function cleanup(projectPath) {
1412
1377
  logger.info("manage", "cleanup complete");
1413
1378
  }
1414
1379
  // ../server/dist/services/stash-service.js
1415
- import { readFileSync as readFileSync3, existsSync as existsSync8 } from "fs";
1416
- import { join as join8 } from "path";
1380
+ import { readFileSync as readFileSync3, existsSync as existsSync7 } from "fs";
1381
+ import { join as join7 } from "path";
1417
1382
 
1418
1383
  // ../server/dist/services/app-proxy.js
1419
1384
  import { spawn as spawn5 } from "child_process";
@@ -1949,8 +1914,7 @@ class StashService {
1949
1914
  id: "resolve-component",
1950
1915
  prompt,
1951
1916
  cwd: this.projectPath,
1952
- model: "claude-haiku-4-5-20251001",
1953
- tools: ["Read", "Grep", "Glob", "Bash"]
1917
+ model: "claude-haiku-4-5-20251001"
1954
1918
  });
1955
1919
  let resolvedPath = "";
1956
1920
  try {
@@ -2012,8 +1976,8 @@ class StashService {
2012
1976
  let sourceCode = "";
2013
1977
  const filePath = component?.filePath || "";
2014
1978
  if (filePath && filePath !== "auto-detect") {
2015
- const sourceFile = join8(this.projectPath, filePath);
2016
- if (existsSync8(sourceFile)) {
1979
+ const sourceFile = join7(this.projectPath, filePath);
1980
+ if (existsSync7(sourceFile)) {
2017
1981
  sourceCode = readFileSync3(sourceFile, "utf-8");
2018
1982
  }
2019
1983
  }
@@ -2365,8 +2329,8 @@ ${refDescriptions.join(`
2365
2329
  }
2366
2330
 
2367
2331
  // ../server/dist/services/activity-store.js
2368
- import { appendFileSync as appendFileSync2, readFileSync as readFileSync4, existsSync as existsSync9, mkdirSync as mkdirSync6, rmSync as rmSync3 } from "fs";
2369
- import { join as join9, dirname as dirname2 } from "path";
2332
+ import { appendFileSync as appendFileSync2, readFileSync as readFileSync4, existsSync as existsSync8, mkdirSync as mkdirSync5, rmSync as rmSync3 } from "fs";
2333
+ import { join as join8, dirname as dirname2 } from "path";
2370
2334
 
2371
2335
  class ActivityStore {
2372
2336
  cache = new Map;
@@ -2375,7 +2339,7 @@ class ActivityStore {
2375
2339
  this.projectPath = projectPath;
2376
2340
  }
2377
2341
  jsonlPath(stashId) {
2378
- return join9(this.projectPath, ".stashes", "activity", `${stashId}.jsonl`);
2342
+ return join8(this.projectPath, ".stashes", "activity", `${stashId}.jsonl`);
2379
2343
  }
2380
2344
  append(event) {
2381
2345
  const existing = this.cache.get(event.stashId) ?? [];
@@ -2383,8 +2347,8 @@ class ActivityStore {
2383
2347
  this.cache.set(event.stashId, existing);
2384
2348
  const filePath = this.jsonlPath(event.stashId);
2385
2349
  const dir = dirname2(filePath);
2386
- if (!existsSync9(dir))
2387
- mkdirSync6(dir, { recursive: true });
2350
+ if (!existsSync8(dir))
2351
+ mkdirSync5(dir, { recursive: true });
2388
2352
  appendFileSync2(filePath, JSON.stringify(event) + `
2389
2353
  `, "utf-8");
2390
2354
  }
@@ -2393,7 +2357,7 @@ class ActivityStore {
2393
2357
  if (cached && cached.length > 0)
2394
2358
  return cached;
2395
2359
  const filePath = this.jsonlPath(stashId);
2396
- if (!existsSync9(filePath))
2360
+ if (!existsSync8(filePath))
2397
2361
  return [];
2398
2362
  const lines = readFileSync4(filePath, "utf-8").trim().split(`
2399
2363
  `).filter(Boolean);
@@ -2419,14 +2383,14 @@ class ActivityStore {
2419
2383
  clear(stashId) {
2420
2384
  this.cache.delete(stashId);
2421
2385
  const filePath = this.jsonlPath(stashId);
2422
- if (existsSync9(filePath)) {
2386
+ if (existsSync8(filePath)) {
2423
2387
  rmSync3(filePath);
2424
2388
  }
2425
2389
  }
2426
2390
  has(stashId) {
2427
2391
  if (this.cache.has(stashId) && (this.cache.get(stashId)?.length ?? 0) > 0)
2428
2392
  return true;
2429
- return existsSync9(this.jsonlPath(stashId));
2393
+ return existsSync8(this.jsonlPath(stashId));
2430
2394
  }
2431
2395
  }
2432
2396
 
@@ -2668,8 +2632,8 @@ app.get("/dev-server-status", async (c) => {
2668
2632
  });
2669
2633
  app.get("/screenshots/:filename", (c) => {
2670
2634
  const filename = c.req.param("filename");
2671
- const filePath = join10(serverState.projectPath, ".stashes", "screenshots", filename);
2672
- if (!existsSync10(filePath))
2635
+ const filePath = join9(serverState.projectPath, ".stashes", "screenshots", filename);
2636
+ if (!existsSync9(filePath))
2673
2637
  return c.json({ error: "Not found" }, 404);
2674
2638
  const content = readFileSync5(filePath);
2675
2639
  return new Response(content, {
@@ -2721,12 +2685,12 @@ app2.route("/api", apiRoutes);
2721
2685
  app2.get("/*", async (c) => {
2722
2686
  const path = c.req.path;
2723
2687
  const selfDir = dirname3(fileURLToPath(import.meta.url));
2724
- const bundledWebDir = join11(selfDir, "web");
2725
- const monorepoWebDir = join11(selfDir, "../../web/dist");
2726
- const webDistDir = existsSync11(join11(bundledWebDir, "index.html")) ? bundledWebDir : monorepoWebDir;
2688
+ const bundledWebDir = join10(selfDir, "web");
2689
+ const monorepoWebDir = join10(selfDir, "../../web/dist");
2690
+ const webDistDir = existsSync10(join10(bundledWebDir, "index.html")) ? bundledWebDir : monorepoWebDir;
2727
2691
  const requestPath = path === "/" ? "/index.html" : path;
2728
- const filePath = join11(webDistDir, requestPath);
2729
- if (existsSync11(filePath) && !filePath.includes("..")) {
2692
+ const filePath = join10(webDistDir, requestPath);
2693
+ if (existsSync10(filePath) && !filePath.includes("..")) {
2730
2694
  const content = readFileSync6(filePath);
2731
2695
  const ext = filePath.split(".").pop() || "";
2732
2696
  const contentTypes = {
@@ -2745,8 +2709,8 @@ app2.get("/*", async (c) => {
2745
2709
  headers: { "content-type": contentTypes[ext] || "application/octet-stream" }
2746
2710
  });
2747
2711
  }
2748
- const indexPath = join11(webDistDir, "index.html");
2749
- if (existsSync11(indexPath)) {
2712
+ const indexPath = join10(webDistDir, "index.html");
2713
+ if (existsSync10(indexPath)) {
2750
2714
  const html = readFileSync6(indexPath, "utf-8");
2751
2715
  return new Response(html, {
2752
2716
  headers: { "content-type": "text/html; charset=utf-8" }
@@ -2807,11 +2771,11 @@ async function startServer(projectPath, userDevPort, requestedPort = STASHES_POR
2807
2771
  }
2808
2772
 
2809
2773
  // ../server/dist/services/detector.js
2810
- import { existsSync as existsSync12, readFileSync as readFileSync7 } from "fs";
2811
- import { join as join12 } from "path";
2774
+ import { existsSync as existsSync11, readFileSync as readFileSync7 } from "fs";
2775
+ import { join as join11 } from "path";
2812
2776
  function detectFramework(projectPath) {
2813
- const packageJsonPath = join12(projectPath, "package.json");
2814
- if (!existsSync12(packageJsonPath)) {
2777
+ const packageJsonPath = join11(projectPath, "package.json");
2778
+ if (!existsSync11(packageJsonPath)) {
2815
2779
  return {
2816
2780
  framework: "unknown",
2817
2781
  devCommand: "npm run dev",
@@ -2873,7 +2837,7 @@ function getDevCommand(packageJson, fallback) {
2873
2837
  }
2874
2838
  function findConfig(projectPath, candidates) {
2875
2839
  for (const candidate of candidates) {
2876
- if (existsSync12(join12(projectPath, candidate))) {
2840
+ if (existsSync11(join11(projectPath, candidate))) {
2877
2841
  return candidate;
2878
2842
  }
2879
2843
  }
@@ -3065,8 +3029,8 @@ Cleaning up all stashes and worktrees...`);
3065
3029
  }
3066
3030
 
3067
3031
  // src/commands/setup.ts
3068
- import { existsSync as existsSync13, readFileSync as readFileSync8, writeFileSync as writeFileSync3, mkdirSync as mkdirSync7 } from "fs";
3069
- import { dirname as dirname4, join as join13 } from "path";
3032
+ import { existsSync as existsSync12, readFileSync as readFileSync8, writeFileSync as writeFileSync2, mkdirSync as mkdirSync6 } from "fs";
3033
+ import { dirname as dirname4, join as join12 } from "path";
3070
3034
  import { homedir } from "os";
3071
3035
  import * as p from "@clack/prompts";
3072
3036
  import pc from "picocolors";
@@ -3085,60 +3049,60 @@ var MCP_ENTRY_ZED = {
3085
3049
  };
3086
3050
  function buildToolDefinitions() {
3087
3051
  const home = homedir();
3088
- const appSupport = join13(home, "Library", "Application Support");
3052
+ const appSupport = join12(home, "Library", "Application Support");
3089
3053
  return [
3090
3054
  {
3091
3055
  id: "claude-code",
3092
3056
  name: "Claude Code",
3093
- configPath: join13(home, ".claude.json"),
3057
+ configPath: join12(home, ".claude.json"),
3094
3058
  serversKey: "mcpServers",
3095
3059
  format: "standard",
3096
- detect: () => existsSync13(join13(home, ".claude.json")) || existsSync13(join13(home, ".claude"))
3060
+ detect: () => existsSync12(join12(home, ".claude.json")) || existsSync12(join12(home, ".claude"))
3097
3061
  },
3098
3062
  {
3099
3063
  id: "claude-desktop",
3100
3064
  name: "Claude Desktop",
3101
- configPath: join13(appSupport, "Claude", "claude_desktop_config.json"),
3065
+ configPath: join12(appSupport, "Claude", "claude_desktop_config.json"),
3102
3066
  serversKey: "mcpServers",
3103
3067
  format: "standard",
3104
- detect: () => existsSync13(join13(appSupport, "Claude")) || existsSync13("/Applications/Claude.app")
3068
+ detect: () => existsSync12(join12(appSupport, "Claude")) || existsSync12("/Applications/Claude.app")
3105
3069
  },
3106
3070
  {
3107
3071
  id: "vscode",
3108
3072
  name: "VS Code",
3109
- configPath: join13(appSupport, "Code", "User", "mcp.json"),
3073
+ configPath: join12(appSupport, "Code", "User", "mcp.json"),
3110
3074
  serversKey: "servers",
3111
3075
  format: "standard",
3112
- detect: () => existsSync13(join13(appSupport, "Code", "User"))
3076
+ detect: () => existsSync12(join12(appSupport, "Code", "User"))
3113
3077
  },
3114
3078
  {
3115
3079
  id: "cursor",
3116
3080
  name: "Cursor",
3117
- configPath: join13(home, ".cursor", "mcp.json"),
3081
+ configPath: join12(home, ".cursor", "mcp.json"),
3118
3082
  serversKey: "mcpServers",
3119
3083
  format: "standard",
3120
- detect: () => existsSync13(join13(home, ".cursor"))
3084
+ detect: () => existsSync12(join12(home, ".cursor"))
3121
3085
  },
3122
3086
  {
3123
3087
  id: "windsurf",
3124
3088
  name: "Windsurf",
3125
- configPath: join13(home, ".codeium", "windsurf", "mcp_config.json"),
3089
+ configPath: join12(home, ".codeium", "windsurf", "mcp_config.json"),
3126
3090
  serversKey: "mcpServers",
3127
3091
  format: "standard",
3128
- detect: () => existsSync13(join13(home, ".codeium", "windsurf"))
3092
+ detect: () => existsSync12(join12(home, ".codeium", "windsurf"))
3129
3093
  },
3130
3094
  {
3131
3095
  id: "zed",
3132
3096
  name: "Zed",
3133
- configPath: join13(appSupport, "Zed", "settings.json"),
3097
+ configPath: join12(appSupport, "Zed", "settings.json"),
3134
3098
  serversKey: "context_servers",
3135
3099
  format: "zed",
3136
- detect: () => existsSync13(join13(appSupport, "Zed"))
3100
+ detect: () => existsSync12(join12(appSupport, "Zed"))
3137
3101
  }
3138
3102
  ];
3139
3103
  }
3140
3104
  function readJsonFile(path) {
3141
- if (!existsSync13(path))
3105
+ if (!existsSync12(path))
3142
3106
  return {};
3143
3107
  try {
3144
3108
  const raw = readFileSync8(path, "utf-8").trim();
@@ -3150,8 +3114,8 @@ function readJsonFile(path) {
3150
3114
  }
3151
3115
  }
3152
3116
  function writeJsonFile(path, data) {
3153
- mkdirSync7(dirname4(path), { recursive: true });
3154
- writeFileSync3(path, JSON.stringify(data, null, 2) + `
3117
+ mkdirSync6(dirname4(path), { recursive: true });
3118
+ writeFileSync2(path, JSON.stringify(data, null, 2) + `
3155
3119
  `);
3156
3120
  }
3157
3121
  function isConfigured(tool) {
@@ -3289,15 +3253,15 @@ async function setupCommand(options) {
3289
3253
 
3290
3254
  // src/commands/update.ts
3291
3255
  import { execFileSync, execSync } from "child_process";
3292
- import { writeFileSync as writeFileSync4, unlinkSync, chmodSync, readFileSync as readFileSync9 } from "fs";
3293
- import { tmpdir as tmpdir2 } from "os";
3294
- import { join as join14, dirname as dirname5 } from "path";
3256
+ import { writeFileSync as writeFileSync3, unlinkSync, chmodSync, readFileSync as readFileSync9 } from "fs";
3257
+ import { tmpdir } from "os";
3258
+ import { join as join13, dirname as dirname5 } from "path";
3295
3259
  import { fileURLToPath as fileURLToPath2 } from "url";
3296
3260
  import * as p2 from "@clack/prompts";
3297
3261
  import pc2 from "picocolors";
3298
3262
  function getCurrentVersion() {
3299
3263
  const selfDir = dirname5(fileURLToPath2(import.meta.url));
3300
- const pkgPath = join14(selfDir, "..", "package.json");
3264
+ const pkgPath = join13(selfDir, "..", "package.json");
3301
3265
  return JSON.parse(readFileSync9(pkgPath, "utf-8")).version;
3302
3266
  }
3303
3267
  function fetchLatestVersion() {
@@ -3374,8 +3338,8 @@ async function updateCommand() {
3374
3338
  }
3375
3339
  s.stop(`Removed from ${configuredTools.length} tool${configuredTools.length === 1 ? "" : "s"}`);
3376
3340
  }
3377
- const scriptPath = join14(tmpdir2(), `stashes-update-${Date.now()}.sh`);
3378
- writeFileSync4(scriptPath, buildUpdateScript(), "utf-8");
3341
+ const scriptPath = join13(tmpdir(), `stashes-update-${Date.now()}.sh`);
3342
+ writeFileSync3(scriptPath, buildUpdateScript(), "utf-8");
3379
3343
  chmodSync(scriptPath, 493);
3380
3344
  try {
3381
3345
  execFileSync("bash", [scriptPath], { stdio: "inherit" });
@@ -3393,7 +3357,7 @@ Update failed. Try manually:`);
3393
3357
 
3394
3358
  // src/index.ts
3395
3359
  var selfDir = dirname6(fileURLToPath3(import.meta.url));
3396
- var pkgPath = join15(selfDir, "..", "package.json");
3360
+ var pkgPath = join14(selfDir, "..", "package.json");
3397
3361
  var version = JSON.parse(readFileSync10(pkgPath, "utf-8")).version;
3398
3362
  var program = new Command;
3399
3363
  program.name("stashes").description("Generate AI-powered UI design explorations in your project").version(version, "-v, --version");