stashes 0.1.56 → 0.1.57

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 +125 -174
  2. package/dist/mcp.js +99 -148
  3. package/package.json +1 -1
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,56 @@ ${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
772
  function buildScreenshotPrompt(port, diff, screenshotDir, stashId) {
807
- const outputPath = join6(screenshotDir, `${stashId}.png`);
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
+ "## CRITICAL: You are an automated screenshot subprocess. Follow these rules exactly.",
811
776
  "",
812
- `The app is running at: http://localhost:${port}`,
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)",
813
783
  "",
814
- "## Git diff of changes (tells you what was modified):",
815
- "```",
816
- diff,
817
- "```",
784
+ "YOUR ONLY JOB: navigate to the app and take a screenshot using Playwright.",
818
785
  "",
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.",
786
+ `## App URL: http://localhost:${port}`,
822
787
  "",
823
- "**Call this tool:**",
824
- "```",
825
- "mcp__playwright__browser_run_code",
788
+ "## Git diff (shows what changed \u2014 use this to decide where to navigate):",
826
789
  "```",
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
- `}`,
790
+ diff,
840
791
  "```",
841
792
  "",
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.",
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):",
844
800
  "",
845
- `After the tool call succeeds, respond with ONLY this JSON:`,
801
+ "```json",
846
802
  "{",
847
803
  ' "screenshots": [',
848
804
  " {",
849
805
  ` "path": "${outputPath}",`,
850
- ' "label": "Short description of what is shown",',
851
- ' "route": "/the-url-path",',
806
+ ' "label": "Description of what is shown",',
807
+ ' "route": "/",',
852
808
  ' "isPrimary": true',
853
809
  " }",
854
810
  " ]",
855
- "}"
811
+ "}",
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."
856
816
  ].join(`
857
817
  `);
858
818
  }
@@ -887,9 +847,9 @@ async function fallbackScreenshot(port, projectPath, stashId) {
887
847
  }
888
848
  async function captureSmartScreenshots(opts) {
889
849
  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 });
850
+ const screenshotDir = join5(projectPath, SCREENSHOTS_DIR2);
851
+ if (!existsSync5(screenshotDir)) {
852
+ mkdirSync4(screenshotDir, { recursive: true });
893
853
  }
894
854
  const diff = await getStashDiff(worktreePath, parentBranch);
895
855
  if (!diff) {
@@ -903,9 +863,7 @@ async function captureSmartScreenshots(opts) {
903
863
  id: processId,
904
864
  prompt,
905
865
  cwd: worktreePath,
906
- model: modelFlag,
907
- tools: [],
908
- mcpConfigPath: getPlaywrightMcpConfigPath()
866
+ model: modelFlag
909
867
  });
910
868
  let textOutput = "";
911
869
  let timedOut = false;
@@ -931,9 +889,9 @@ async function captureSmartScreenshots(opts) {
931
889
  }
932
890
  const result = parseAiResult(textOutput);
933
891
  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)`);
892
+ const expectedPath = join5(screenshotDir, `${stashId}.png`);
893
+ if (existsSync5(expectedPath)) {
894
+ logger.info("smart-screenshot", `AI saved screenshot for ${stashId} (no JSON response, but file exists)`);
937
895
  const url = `/api/screenshots/${stashId}.png`;
938
896
  return {
939
897
  primary: url,
@@ -947,18 +905,12 @@ async function captureSmartScreenshots(opts) {
947
905
  let primaryUrl = "";
948
906
  for (const shot of result.screenshots) {
949
907
  const filename = shot.path.split("/").pop() || "";
950
- if (!existsSync6(shot.path)) {
908
+ if (!existsSync5(shot.path)) {
951
909
  logger.warn("smart-screenshot", `Screenshot file not found: ${shot.path}`);
952
910
  continue;
953
911
  }
954
912
  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);
913
+ screenshots.push({ url, label: shot.label, route: shot.route, isPrimary: shot.isPrimary });
962
914
  if (shot.isPrimary)
963
915
  primaryUrl = url;
964
916
  }
@@ -1041,8 +993,8 @@ async function generate(opts) {
1041
993
  const selectedDirectives = directives.slice(0, count);
1042
994
  let sourceCode = "";
1043
995
  if (component?.filePath) {
1044
- const sourceFile = join7(projectPath, component.filePath);
1045
- if (existsSync7(sourceFile)) {
996
+ const sourceFile = join6(projectPath, component.filePath);
997
+ if (existsSync6(sourceFile)) {
1046
998
  sourceCode = readFileSync2(sourceFile, "utf-8");
1047
999
  }
1048
1000
  }
@@ -1412,8 +1364,8 @@ async function cleanup(projectPath) {
1412
1364
  logger.info("manage", "cleanup complete");
1413
1365
  }
1414
1366
  // ../server/dist/services/stash-service.js
1415
- import { readFileSync as readFileSync3, existsSync as existsSync8 } from "fs";
1416
- import { join as join8 } from "path";
1367
+ import { readFileSync as readFileSync3, existsSync as existsSync7 } from "fs";
1368
+ import { join as join7 } from "path";
1417
1369
 
1418
1370
  // ../server/dist/services/app-proxy.js
1419
1371
  import { spawn as spawn5 } from "child_process";
@@ -1949,8 +1901,7 @@ class StashService {
1949
1901
  id: "resolve-component",
1950
1902
  prompt,
1951
1903
  cwd: this.projectPath,
1952
- model: "claude-haiku-4-5-20251001",
1953
- tools: ["Read", "Grep", "Glob", "Bash"]
1904
+ model: "claude-haiku-4-5-20251001"
1954
1905
  });
1955
1906
  let resolvedPath = "";
1956
1907
  try {
@@ -2012,8 +1963,8 @@ class StashService {
2012
1963
  let sourceCode = "";
2013
1964
  const filePath = component?.filePath || "";
2014
1965
  if (filePath && filePath !== "auto-detect") {
2015
- const sourceFile = join8(this.projectPath, filePath);
2016
- if (existsSync8(sourceFile)) {
1966
+ const sourceFile = join7(this.projectPath, filePath);
1967
+ if (existsSync7(sourceFile)) {
2017
1968
  sourceCode = readFileSync3(sourceFile, "utf-8");
2018
1969
  }
2019
1970
  }
@@ -2365,8 +2316,8 @@ ${refDescriptions.join(`
2365
2316
  }
2366
2317
 
2367
2318
  // ../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";
2319
+ import { appendFileSync as appendFileSync2, readFileSync as readFileSync4, existsSync as existsSync8, mkdirSync as mkdirSync5, rmSync as rmSync3 } from "fs";
2320
+ import { join as join8, dirname as dirname2 } from "path";
2370
2321
 
2371
2322
  class ActivityStore {
2372
2323
  cache = new Map;
@@ -2375,7 +2326,7 @@ class ActivityStore {
2375
2326
  this.projectPath = projectPath;
2376
2327
  }
2377
2328
  jsonlPath(stashId) {
2378
- return join9(this.projectPath, ".stashes", "activity", `${stashId}.jsonl`);
2329
+ return join8(this.projectPath, ".stashes", "activity", `${stashId}.jsonl`);
2379
2330
  }
2380
2331
  append(event) {
2381
2332
  const existing = this.cache.get(event.stashId) ?? [];
@@ -2383,8 +2334,8 @@ class ActivityStore {
2383
2334
  this.cache.set(event.stashId, existing);
2384
2335
  const filePath = this.jsonlPath(event.stashId);
2385
2336
  const dir = dirname2(filePath);
2386
- if (!existsSync9(dir))
2387
- mkdirSync6(dir, { recursive: true });
2337
+ if (!existsSync8(dir))
2338
+ mkdirSync5(dir, { recursive: true });
2388
2339
  appendFileSync2(filePath, JSON.stringify(event) + `
2389
2340
  `, "utf-8");
2390
2341
  }
@@ -2393,7 +2344,7 @@ class ActivityStore {
2393
2344
  if (cached && cached.length > 0)
2394
2345
  return cached;
2395
2346
  const filePath = this.jsonlPath(stashId);
2396
- if (!existsSync9(filePath))
2347
+ if (!existsSync8(filePath))
2397
2348
  return [];
2398
2349
  const lines = readFileSync4(filePath, "utf-8").trim().split(`
2399
2350
  `).filter(Boolean);
@@ -2419,14 +2370,14 @@ class ActivityStore {
2419
2370
  clear(stashId) {
2420
2371
  this.cache.delete(stashId);
2421
2372
  const filePath = this.jsonlPath(stashId);
2422
- if (existsSync9(filePath)) {
2373
+ if (existsSync8(filePath)) {
2423
2374
  rmSync3(filePath);
2424
2375
  }
2425
2376
  }
2426
2377
  has(stashId) {
2427
2378
  if (this.cache.has(stashId) && (this.cache.get(stashId)?.length ?? 0) > 0)
2428
2379
  return true;
2429
- return existsSync9(this.jsonlPath(stashId));
2380
+ return existsSync8(this.jsonlPath(stashId));
2430
2381
  }
2431
2382
  }
2432
2383
 
@@ -2668,8 +2619,8 @@ app.get("/dev-server-status", async (c) => {
2668
2619
  });
2669
2620
  app.get("/screenshots/:filename", (c) => {
2670
2621
  const filename = c.req.param("filename");
2671
- const filePath = join10(serverState.projectPath, ".stashes", "screenshots", filename);
2672
- if (!existsSync10(filePath))
2622
+ const filePath = join9(serverState.projectPath, ".stashes", "screenshots", filename);
2623
+ if (!existsSync9(filePath))
2673
2624
  return c.json({ error: "Not found" }, 404);
2674
2625
  const content = readFileSync5(filePath);
2675
2626
  return new Response(content, {
@@ -2721,12 +2672,12 @@ app2.route("/api", apiRoutes);
2721
2672
  app2.get("/*", async (c) => {
2722
2673
  const path = c.req.path;
2723
2674
  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;
2675
+ const bundledWebDir = join10(selfDir, "web");
2676
+ const monorepoWebDir = join10(selfDir, "../../web/dist");
2677
+ const webDistDir = existsSync10(join10(bundledWebDir, "index.html")) ? bundledWebDir : monorepoWebDir;
2727
2678
  const requestPath = path === "/" ? "/index.html" : path;
2728
- const filePath = join11(webDistDir, requestPath);
2729
- if (existsSync11(filePath) && !filePath.includes("..")) {
2679
+ const filePath = join10(webDistDir, requestPath);
2680
+ if (existsSync10(filePath) && !filePath.includes("..")) {
2730
2681
  const content = readFileSync6(filePath);
2731
2682
  const ext = filePath.split(".").pop() || "";
2732
2683
  const contentTypes = {
@@ -2745,8 +2696,8 @@ app2.get("/*", async (c) => {
2745
2696
  headers: { "content-type": contentTypes[ext] || "application/octet-stream" }
2746
2697
  });
2747
2698
  }
2748
- const indexPath = join11(webDistDir, "index.html");
2749
- if (existsSync11(indexPath)) {
2699
+ const indexPath = join10(webDistDir, "index.html");
2700
+ if (existsSync10(indexPath)) {
2750
2701
  const html = readFileSync6(indexPath, "utf-8");
2751
2702
  return new Response(html, {
2752
2703
  headers: { "content-type": "text/html; charset=utf-8" }
@@ -2807,11 +2758,11 @@ async function startServer(projectPath, userDevPort, requestedPort = STASHES_POR
2807
2758
  }
2808
2759
 
2809
2760
  // ../server/dist/services/detector.js
2810
- import { existsSync as existsSync12, readFileSync as readFileSync7 } from "fs";
2811
- import { join as join12 } from "path";
2761
+ import { existsSync as existsSync11, readFileSync as readFileSync7 } from "fs";
2762
+ import { join as join11 } from "path";
2812
2763
  function detectFramework(projectPath) {
2813
- const packageJsonPath = join12(projectPath, "package.json");
2814
- if (!existsSync12(packageJsonPath)) {
2764
+ const packageJsonPath = join11(projectPath, "package.json");
2765
+ if (!existsSync11(packageJsonPath)) {
2815
2766
  return {
2816
2767
  framework: "unknown",
2817
2768
  devCommand: "npm run dev",
@@ -2873,7 +2824,7 @@ function getDevCommand(packageJson, fallback) {
2873
2824
  }
2874
2825
  function findConfig(projectPath, candidates) {
2875
2826
  for (const candidate of candidates) {
2876
- if (existsSync12(join12(projectPath, candidate))) {
2827
+ if (existsSync11(join11(projectPath, candidate))) {
2877
2828
  return candidate;
2878
2829
  }
2879
2830
  }
@@ -3065,8 +3016,8 @@ Cleaning up all stashes and worktrees...`);
3065
3016
  }
3066
3017
 
3067
3018
  // 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";
3019
+ import { existsSync as existsSync12, readFileSync as readFileSync8, writeFileSync as writeFileSync2, mkdirSync as mkdirSync6 } from "fs";
3020
+ import { dirname as dirname4, join as join12 } from "path";
3070
3021
  import { homedir } from "os";
3071
3022
  import * as p from "@clack/prompts";
3072
3023
  import pc from "picocolors";
@@ -3085,60 +3036,60 @@ var MCP_ENTRY_ZED = {
3085
3036
  };
3086
3037
  function buildToolDefinitions() {
3087
3038
  const home = homedir();
3088
- const appSupport = join13(home, "Library", "Application Support");
3039
+ const appSupport = join12(home, "Library", "Application Support");
3089
3040
  return [
3090
3041
  {
3091
3042
  id: "claude-code",
3092
3043
  name: "Claude Code",
3093
- configPath: join13(home, ".claude.json"),
3044
+ configPath: join12(home, ".claude.json"),
3094
3045
  serversKey: "mcpServers",
3095
3046
  format: "standard",
3096
- detect: () => existsSync13(join13(home, ".claude.json")) || existsSync13(join13(home, ".claude"))
3047
+ detect: () => existsSync12(join12(home, ".claude.json")) || existsSync12(join12(home, ".claude"))
3097
3048
  },
3098
3049
  {
3099
3050
  id: "claude-desktop",
3100
3051
  name: "Claude Desktop",
3101
- configPath: join13(appSupport, "Claude", "claude_desktop_config.json"),
3052
+ configPath: join12(appSupport, "Claude", "claude_desktop_config.json"),
3102
3053
  serversKey: "mcpServers",
3103
3054
  format: "standard",
3104
- detect: () => existsSync13(join13(appSupport, "Claude")) || existsSync13("/Applications/Claude.app")
3055
+ detect: () => existsSync12(join12(appSupport, "Claude")) || existsSync12("/Applications/Claude.app")
3105
3056
  },
3106
3057
  {
3107
3058
  id: "vscode",
3108
3059
  name: "VS Code",
3109
- configPath: join13(appSupport, "Code", "User", "mcp.json"),
3060
+ configPath: join12(appSupport, "Code", "User", "mcp.json"),
3110
3061
  serversKey: "servers",
3111
3062
  format: "standard",
3112
- detect: () => existsSync13(join13(appSupport, "Code", "User"))
3063
+ detect: () => existsSync12(join12(appSupport, "Code", "User"))
3113
3064
  },
3114
3065
  {
3115
3066
  id: "cursor",
3116
3067
  name: "Cursor",
3117
- configPath: join13(home, ".cursor", "mcp.json"),
3068
+ configPath: join12(home, ".cursor", "mcp.json"),
3118
3069
  serversKey: "mcpServers",
3119
3070
  format: "standard",
3120
- detect: () => existsSync13(join13(home, ".cursor"))
3071
+ detect: () => existsSync12(join12(home, ".cursor"))
3121
3072
  },
3122
3073
  {
3123
3074
  id: "windsurf",
3124
3075
  name: "Windsurf",
3125
- configPath: join13(home, ".codeium", "windsurf", "mcp_config.json"),
3076
+ configPath: join12(home, ".codeium", "windsurf", "mcp_config.json"),
3126
3077
  serversKey: "mcpServers",
3127
3078
  format: "standard",
3128
- detect: () => existsSync13(join13(home, ".codeium", "windsurf"))
3079
+ detect: () => existsSync12(join12(home, ".codeium", "windsurf"))
3129
3080
  },
3130
3081
  {
3131
3082
  id: "zed",
3132
3083
  name: "Zed",
3133
- configPath: join13(appSupport, "Zed", "settings.json"),
3084
+ configPath: join12(appSupport, "Zed", "settings.json"),
3134
3085
  serversKey: "context_servers",
3135
3086
  format: "zed",
3136
- detect: () => existsSync13(join13(appSupport, "Zed"))
3087
+ detect: () => existsSync12(join12(appSupport, "Zed"))
3137
3088
  }
3138
3089
  ];
3139
3090
  }
3140
3091
  function readJsonFile(path) {
3141
- if (!existsSync13(path))
3092
+ if (!existsSync12(path))
3142
3093
  return {};
3143
3094
  try {
3144
3095
  const raw = readFileSync8(path, "utf-8").trim();
@@ -3150,8 +3101,8 @@ function readJsonFile(path) {
3150
3101
  }
3151
3102
  }
3152
3103
  function writeJsonFile(path, data) {
3153
- mkdirSync7(dirname4(path), { recursive: true });
3154
- writeFileSync3(path, JSON.stringify(data, null, 2) + `
3104
+ mkdirSync6(dirname4(path), { recursive: true });
3105
+ writeFileSync2(path, JSON.stringify(data, null, 2) + `
3155
3106
  `);
3156
3107
  }
3157
3108
  function isConfigured(tool) {
@@ -3289,15 +3240,15 @@ async function setupCommand(options) {
3289
3240
 
3290
3241
  // src/commands/update.ts
3291
3242
  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";
3243
+ import { writeFileSync as writeFileSync3, unlinkSync, chmodSync, readFileSync as readFileSync9 } from "fs";
3244
+ import { tmpdir } from "os";
3245
+ import { join as join13, dirname as dirname5 } from "path";
3295
3246
  import { fileURLToPath as fileURLToPath2 } from "url";
3296
3247
  import * as p2 from "@clack/prompts";
3297
3248
  import pc2 from "picocolors";
3298
3249
  function getCurrentVersion() {
3299
3250
  const selfDir = dirname5(fileURLToPath2(import.meta.url));
3300
- const pkgPath = join14(selfDir, "..", "package.json");
3251
+ const pkgPath = join13(selfDir, "..", "package.json");
3301
3252
  return JSON.parse(readFileSync9(pkgPath, "utf-8")).version;
3302
3253
  }
3303
3254
  function fetchLatestVersion() {
@@ -3374,8 +3325,8 @@ async function updateCommand() {
3374
3325
  }
3375
3326
  s.stop(`Removed from ${configuredTools.length} tool${configuredTools.length === 1 ? "" : "s"}`);
3376
3327
  }
3377
- const scriptPath = join14(tmpdir2(), `stashes-update-${Date.now()}.sh`);
3378
- writeFileSync4(scriptPath, buildUpdateScript(), "utf-8");
3328
+ const scriptPath = join13(tmpdir(), `stashes-update-${Date.now()}.sh`);
3329
+ writeFileSync3(scriptPath, buildUpdateScript(), "utf-8");
3379
3330
  chmodSync(scriptPath, 493);
3380
3331
  try {
3381
3332
  execFileSync("bash", [scriptPath], { stdio: "inherit" });
@@ -3393,7 +3344,7 @@ Update failed. Try manually:`);
3393
3344
 
3394
3345
  // src/index.ts
3395
3346
  var selfDir = dirname6(fileURLToPath3(import.meta.url));
3396
- var pkgPath = join15(selfDir, "..", "package.json");
3347
+ var pkgPath = join14(selfDir, "..", "package.json");
3397
3348
  var version = JSON.parse(readFileSync10(pkgPath, "utf-8")).version;
3398
3349
  var program = new Command;
3399
3350
  program.name("stashes").description("Generate AI-powered UI design explorations in your project").version(version, "-v, --version");
package/dist/mcp.js CHANGED
@@ -26,8 +26,8 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
26
26
  import { z } from "zod";
27
27
 
28
28
  // ../core/dist/generation.js
29
- import { readFileSync as readFileSync2, existsSync as existsSync7 } from "fs";
30
- import { join as join7 } from "path";
29
+ import { readFileSync as readFileSync2, existsSync as existsSync6 } from "fs";
30
+ import { join as join6 } from "path";
31
31
  var {spawn: spawn3 } = globalThis.Bun;
32
32
  import simpleGit3 from "simple-git";
33
33
  // ../shared/dist/constants/index.js
@@ -529,65 +529,19 @@ class PersistenceService {
529
529
 
530
530
  // ../core/dist/ai-process.js
531
531
  var {spawn } = globalThis.Bun;
532
- import { writeFileSync as writeFileSync2, existsSync as existsSync4, mkdirSync as mkdirSync3 } from "fs";
533
- import { join as join4 } from "path";
534
- import { tmpdir } from "os";
535
532
  var CLAUDE_BIN = "/opt/homebrew/bin/claude";
536
- function getPlaywrightMcpConfigPath() {
537
- const configDir = join4(tmpdir(), "stashes-mcp");
538
- const configPath = join4(configDir, "playwright.json");
539
- if (!existsSync4(configPath)) {
540
- mkdirSync3(configDir, { recursive: true });
541
- writeFileSync2(configPath, JSON.stringify({
542
- mcpServers: {
543
- playwright: { command: "npx", args: ["@playwright/mcp@latest"] }
544
- }
545
- }), "utf-8");
546
- }
547
- return configPath;
548
- }
549
- var OVERHEAD_TOOLS = [
550
- "Agent",
551
- "TodoWrite",
552
- "TaskCreate",
553
- "TaskUpdate",
554
- "TaskList",
555
- "TaskGet",
556
- "Skill",
557
- "ToolSearch",
558
- "EnterPlanMode",
559
- "ExitPlanMode",
560
- "WebSearch",
561
- "WebFetch",
562
- "NotebookEdit",
563
- "mcp__UseAI__*",
564
- "mcp__stashes__*",
565
- "mcp__plugin_drills*",
566
- "mcp__plugin_coverit*"
567
- ];
568
533
  var processes = new Map;
569
534
  function startAiProcess(idOrOpts, prompt, cwd, resumeSessionId, model) {
570
535
  const opts = typeof idOrOpts === "string" ? { id: idOrOpts, prompt, cwd, resumeSessionId, model } : idOrOpts;
571
- const restricted = opts.tools !== undefined;
572
536
  killAiProcess(opts.id);
573
537
  logger.info("claude", `spawning process: ${opts.id}`, {
574
538
  cwd: opts.cwd,
575
539
  promptLength: opts.prompt.length,
576
540
  promptPreview: opts.prompt.substring(0, 100),
577
541
  resumeSessionId: opts.resumeSessionId,
578
- model: opts.model,
579
- restricted,
580
- tools: restricted ? opts.tools.join(",") || "none" : "all"
542
+ model: opts.model
581
543
  });
582
544
  const cmd = [CLAUDE_BIN, "-p", opts.prompt, "--output-format=stream-json", "--verbose", "--dangerously-skip-permissions"];
583
- if (restricted) {
584
- cmd.push("--tools", opts.tools.length > 0 ? opts.tools.join(",") : "");
585
- cmd.push("--disallowedTools", OVERHEAD_TOOLS.join(","));
586
- cmd.push("--strict-mcp-config");
587
- if (opts.mcpConfigPath) {
588
- cmd.push("--mcp-config", opts.mcpConfigPath);
589
- }
590
- }
591
545
  if (opts.resumeSessionId) {
592
546
  cmd.push("--resume", opts.resumeSessionId);
593
547
  }
@@ -718,35 +672,47 @@ async function* parseClaudeStream(proc) {
718
672
  }
719
673
 
720
674
  // ../core/dist/smart-screenshot.js
721
- import { join as join6 } from "path";
722
- import { mkdirSync as mkdirSync5, existsSync as existsSync6 } from "fs";
675
+ import { join as join5 } from "path";
676
+ import { mkdirSync as mkdirSync4, existsSync as existsSync5 } from "fs";
723
677
  import simpleGit2 from "simple-git";
724
678
 
725
679
  // ../core/dist/screenshot.js
726
680
  var {spawn: spawn2 } = globalThis.Bun;
727
- import { join as join5 } from "path";
728
- import { mkdirSync as mkdirSync4, existsSync as existsSync5 } from "fs";
681
+ import { join as join4 } from "path";
682
+ import { mkdirSync as mkdirSync3, existsSync as existsSync4 } from "fs";
729
683
  var SCREENSHOTS_DIR = ".stashes/screenshots";
730
- async function captureScreenshot(port, projectPath, stashId) {
731
- const screenshotsDir = join5(projectPath, SCREENSHOTS_DIR);
732
- if (!existsSync5(screenshotsDir)) {
733
- mkdirSync4(screenshotsDir, { recursive: true });
734
- }
735
- const filename = `${stashId}.png`;
736
- const outputPath = join5(screenshotsDir, filename);
684
+ async function captureScreenshot(portOrOpts, projectPath, stashId) {
685
+ const opts = typeof portOrOpts === "number" ? { port: portOrOpts, projectPath, stashId } : portOrOpts;
686
+ const screenshotsDir = join4(opts.projectPath, SCREENSHOTS_DIR);
687
+ if (!existsSync4(screenshotsDir)) {
688
+ mkdirSync3(screenshotsDir, { recursive: true });
689
+ }
690
+ const filename = `${opts.stashId}.png`;
691
+ const outputPath = join4(screenshotsDir, filename);
692
+ const url = `http://localhost:${opts.port}${opts.route || "/"}`;
693
+ const scrollCode = opts.scrollToSelector ? `
694
+ try {
695
+ const el = await page.waitForSelector('${opts.scrollToSelector.replace(/'/g, "\\'")}', { timeout: 5000 });
696
+ if (el) {
697
+ await el.scrollIntoViewIfNeeded();
698
+ await page.waitForTimeout(500);
699
+ }
700
+ } catch(e) { /* selector not found, capture viewport */ }
701
+ ` : "";
737
702
  const playwrightScript = `
738
703
  const { chromium } = require('playwright');
739
704
  (async () => {
740
705
  const browser = await chromium.launch({ headless: true });
741
706
  const page = await browser.newPage({ viewport: { width: 1280, height: 720 } });
742
707
  try {
743
- await page.goto('http://localhost:${port}', { waitUntil: 'networkidle', timeout: 20000 });
708
+ await page.goto('${url}', { waitUntil: 'networkidle', timeout: 20000 });
744
709
  await page.waitForTimeout(2000);
745
710
  } catch(e) {
746
- await page.goto('http://localhost:${port}', { waitUntil: 'domcontentloaded', timeout: 10000 });
711
+ await page.goto('${url}', { waitUntil: 'domcontentloaded', timeout: 10000 });
747
712
  await page.waitForTimeout(3000);
748
713
  }
749
- await page.screenshot({ path: '${outputPath.replace(/'/g, "\\'")}', type: 'png' });
714
+ ${scrollCode}
715
+ await page.screenshot({ path: '${outputPath.replace(/'/g, "\\'")}', type: 'png'${opts.fullPage ? ", fullPage: true" : ""} });
750
716
  await browser.close();
751
717
  })();
752
718
  `;
@@ -782,62 +748,56 @@ ${diffStat}
782
748
  ## Partial diff (truncated):
783
749
  ${truncatedDiff}`;
784
750
  } catch (err) {
785
- logger.warn("smart-screenshot", `git diff failed, falling back`, {
751
+ logger.warn("smart-screenshot", `git diff failed`, {
786
752
  error: err instanceof Error ? err.message : String(err)
787
753
  });
788
754
  return "";
789
755
  }
790
756
  }
791
757
  function buildScreenshotPrompt(port, diff, screenshotDir, stashId) {
792
- const outputPath = join6(screenshotDir, `${stashId}.png`);
758
+ const outputPath = join5(screenshotDir, `${stashId}.png`);
793
759
  return [
794
- "You are a screenshot assistant. Take a screenshot of a running web app.",
795
- "Be fast \u2014 you have a strict time limit.",
760
+ "## CRITICAL: You are an automated screenshot subprocess. Follow these rules exactly.",
796
761
  "",
797
- `The app is running at: http://localhost:${port}`,
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)",
798
768
  "",
799
- "## Git diff of changes (tells you what was modified):",
800
- "```",
801
- diff,
802
- "```",
769
+ "YOUR ONLY JOB: navigate to the app and take a screenshot using Playwright.",
803
770
  "",
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.",
771
+ `## App URL: http://localhost:${port}`,
807
772
  "",
808
- "**Call this tool:**",
773
+ "## Git diff (shows what changed \u2014 use this to decide where to navigate):",
809
774
  "```",
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
- `}`,
775
+ diff,
825
776
  "```",
826
777
  "",
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.",
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):",
829
785
  "",
830
- `After the tool call succeeds, respond with ONLY this JSON:`,
786
+ "```json",
831
787
  "{",
832
788
  ' "screenshots": [',
833
789
  " {",
834
790
  ` "path": "${outputPath}",`,
835
- ' "label": "Short description of what is shown",',
836
- ' "route": "/the-url-path",',
791
+ ' "label": "Description of what is shown",',
792
+ ' "route": "/",',
837
793
  ' "isPrimary": true',
838
794
  " }",
839
795
  " ]",
840
- "}"
796
+ "}",
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."
841
801
  ].join(`
842
802
  `);
843
803
  }
@@ -872,9 +832,9 @@ async function fallbackScreenshot(port, projectPath, stashId) {
872
832
  }
873
833
  async function captureSmartScreenshots(opts) {
874
834
  const { projectPath, stashId, stashBranch, parentBranch, worktreePath, port, model = "sonnet", timeout = DEFAULT_TIMEOUT } = opts;
875
- const screenshotDir = join6(projectPath, SCREENSHOTS_DIR2);
876
- if (!existsSync6(screenshotDir)) {
877
- mkdirSync5(screenshotDir, { recursive: true });
835
+ const screenshotDir = join5(projectPath, SCREENSHOTS_DIR2);
836
+ if (!existsSync5(screenshotDir)) {
837
+ mkdirSync4(screenshotDir, { recursive: true });
878
838
  }
879
839
  const diff = await getStashDiff(worktreePath, parentBranch);
880
840
  if (!diff) {
@@ -888,9 +848,7 @@ async function captureSmartScreenshots(opts) {
888
848
  id: processId,
889
849
  prompt,
890
850
  cwd: worktreePath,
891
- model: modelFlag,
892
- tools: [],
893
- mcpConfigPath: getPlaywrightMcpConfigPath()
851
+ model: modelFlag
894
852
  });
895
853
  let textOutput = "";
896
854
  let timedOut = false;
@@ -916,9 +874,9 @@ async function captureSmartScreenshots(opts) {
916
874
  }
917
875
  const result = parseAiResult(textOutput);
918
876
  if (!result || !result.screenshots || result.screenshots.length === 0) {
919
- const expectedPath = join6(screenshotDir, `${stashId}.png`);
920
- if (existsSync6(expectedPath)) {
921
- logger.info("smart-screenshot", `AI saved screenshot for ${stashId} via browser_run_code (no JSON response)`);
877
+ const expectedPath = join5(screenshotDir, `${stashId}.png`);
878
+ if (existsSync5(expectedPath)) {
879
+ logger.info("smart-screenshot", `AI saved screenshot for ${stashId} (no JSON response, but file exists)`);
922
880
  const url = `/api/screenshots/${stashId}.png`;
923
881
  return {
924
882
  primary: url,
@@ -932,18 +890,12 @@ async function captureSmartScreenshots(opts) {
932
890
  let primaryUrl = "";
933
891
  for (const shot of result.screenshots) {
934
892
  const filename = shot.path.split("/").pop() || "";
935
- if (!existsSync6(shot.path)) {
893
+ if (!existsSync5(shot.path)) {
936
894
  logger.warn("smart-screenshot", `Screenshot file not found: ${shot.path}`);
937
895
  continue;
938
896
  }
939
897
  const url = `/api/screenshots/${filename}`;
940
- const screenshot = {
941
- url,
942
- label: shot.label,
943
- route: shot.route,
944
- isPrimary: shot.isPrimary
945
- };
946
- screenshots.push(screenshot);
898
+ screenshots.push({ url, label: shot.label, route: shot.route, isPrimary: shot.isPrimary });
947
899
  if (shot.isPrimary)
948
900
  primaryUrl = url;
949
901
  }
@@ -1026,8 +978,8 @@ async function generate(opts) {
1026
978
  const selectedDirectives = directives.slice(0, count);
1027
979
  let sourceCode = "";
1028
980
  if (component?.filePath) {
1029
- const sourceFile = join7(projectPath, component.filePath);
1030
- if (existsSync7(sourceFile)) {
981
+ const sourceFile = join6(projectPath, component.filePath);
982
+ if (existsSync6(sourceFile)) {
1031
983
  sourceCode = readFileSync2(sourceFile, "utf-8");
1032
984
  }
1033
985
  }
@@ -1626,18 +1578,18 @@ async function handleRemove(args, projectPath) {
1626
1578
  // ../server/dist/index.js
1627
1579
  import { Hono as Hono2 } from "hono";
1628
1580
  import { cors } from "hono/cors";
1629
- import { join as join11, dirname as dirname3 } from "path";
1581
+ import { join as join10, dirname as dirname3 } from "path";
1630
1582
  import { fileURLToPath } from "url";
1631
- import { existsSync as existsSync11, readFileSync as readFileSync6 } from "fs";
1583
+ import { existsSync as existsSync10, readFileSync as readFileSync6 } from "fs";
1632
1584
 
1633
1585
  // ../server/dist/routes/api.js
1634
1586
  import { Hono } from "hono";
1635
- import { join as join10, basename } from "path";
1636
- import { existsSync as existsSync10, readFileSync as readFileSync5 } from "fs";
1587
+ import { join as join9, basename } from "path";
1588
+ import { existsSync as existsSync9, readFileSync as readFileSync5 } from "fs";
1637
1589
 
1638
1590
  // ../server/dist/services/stash-service.js
1639
- import { readFileSync as readFileSync3, existsSync as existsSync8 } from "fs";
1640
- import { join as join8 } from "path";
1591
+ import { readFileSync as readFileSync3, existsSync as existsSync7 } from "fs";
1592
+ import { join as join7 } from "path";
1641
1593
 
1642
1594
  // ../server/dist/services/app-proxy.js
1643
1595
  import { spawn as spawn5 } from "child_process";
@@ -2173,8 +2125,7 @@ class StashService {
2173
2125
  id: "resolve-component",
2174
2126
  prompt,
2175
2127
  cwd: this.projectPath,
2176
- model: "claude-haiku-4-5-20251001",
2177
- tools: ["Read", "Grep", "Glob", "Bash"]
2128
+ model: "claude-haiku-4-5-20251001"
2178
2129
  });
2179
2130
  let resolvedPath = "";
2180
2131
  try {
@@ -2236,8 +2187,8 @@ class StashService {
2236
2187
  let sourceCode = "";
2237
2188
  const filePath = component?.filePath || "";
2238
2189
  if (filePath && filePath !== "auto-detect") {
2239
- const sourceFile = join8(this.projectPath, filePath);
2240
- if (existsSync8(sourceFile)) {
2190
+ const sourceFile = join7(this.projectPath, filePath);
2191
+ if (existsSync7(sourceFile)) {
2241
2192
  sourceCode = readFileSync3(sourceFile, "utf-8");
2242
2193
  }
2243
2194
  }
@@ -2589,8 +2540,8 @@ ${refDescriptions.join(`
2589
2540
  }
2590
2541
 
2591
2542
  // ../server/dist/services/activity-store.js
2592
- import { appendFileSync as appendFileSync2, readFileSync as readFileSync4, existsSync as existsSync9, mkdirSync as mkdirSync6, rmSync as rmSync3 } from "fs";
2593
- import { join as join9, dirname as dirname2 } from "path";
2543
+ import { appendFileSync as appendFileSync2, readFileSync as readFileSync4, existsSync as existsSync8, mkdirSync as mkdirSync5, rmSync as rmSync3 } from "fs";
2544
+ import { join as join8, dirname as dirname2 } from "path";
2594
2545
 
2595
2546
  class ActivityStore {
2596
2547
  cache = new Map;
@@ -2599,7 +2550,7 @@ class ActivityStore {
2599
2550
  this.projectPath = projectPath;
2600
2551
  }
2601
2552
  jsonlPath(stashId) {
2602
- return join9(this.projectPath, ".stashes", "activity", `${stashId}.jsonl`);
2553
+ return join8(this.projectPath, ".stashes", "activity", `${stashId}.jsonl`);
2603
2554
  }
2604
2555
  append(event) {
2605
2556
  const existing = this.cache.get(event.stashId) ?? [];
@@ -2607,8 +2558,8 @@ class ActivityStore {
2607
2558
  this.cache.set(event.stashId, existing);
2608
2559
  const filePath = this.jsonlPath(event.stashId);
2609
2560
  const dir = dirname2(filePath);
2610
- if (!existsSync9(dir))
2611
- mkdirSync6(dir, { recursive: true });
2561
+ if (!existsSync8(dir))
2562
+ mkdirSync5(dir, { recursive: true });
2612
2563
  appendFileSync2(filePath, JSON.stringify(event) + `
2613
2564
  `, "utf-8");
2614
2565
  }
@@ -2617,7 +2568,7 @@ class ActivityStore {
2617
2568
  if (cached && cached.length > 0)
2618
2569
  return cached;
2619
2570
  const filePath = this.jsonlPath(stashId);
2620
- if (!existsSync9(filePath))
2571
+ if (!existsSync8(filePath))
2621
2572
  return [];
2622
2573
  const lines = readFileSync4(filePath, "utf-8").trim().split(`
2623
2574
  `).filter(Boolean);
@@ -2643,14 +2594,14 @@ class ActivityStore {
2643
2594
  clear(stashId) {
2644
2595
  this.cache.delete(stashId);
2645
2596
  const filePath = this.jsonlPath(stashId);
2646
- if (existsSync9(filePath)) {
2597
+ if (existsSync8(filePath)) {
2647
2598
  rmSync3(filePath);
2648
2599
  }
2649
2600
  }
2650
2601
  has(stashId) {
2651
2602
  if (this.cache.has(stashId) && (this.cache.get(stashId)?.length ?? 0) > 0)
2652
2603
  return true;
2653
- return existsSync9(this.jsonlPath(stashId));
2604
+ return existsSync8(this.jsonlPath(stashId));
2654
2605
  }
2655
2606
  }
2656
2607
 
@@ -2892,8 +2843,8 @@ app.get("/dev-server-status", async (c) => {
2892
2843
  });
2893
2844
  app.get("/screenshots/:filename", (c) => {
2894
2845
  const filename = c.req.param("filename");
2895
- const filePath = join10(serverState.projectPath, ".stashes", "screenshots", filename);
2896
- if (!existsSync10(filePath))
2846
+ const filePath = join9(serverState.projectPath, ".stashes", "screenshots", filename);
2847
+ if (!existsSync9(filePath))
2897
2848
  return c.json({ error: "Not found" }, 404);
2898
2849
  const content = readFileSync5(filePath);
2899
2850
  return new Response(content, {
@@ -2945,12 +2896,12 @@ app2.route("/api", apiRoutes);
2945
2896
  app2.get("/*", async (c) => {
2946
2897
  const path = c.req.path;
2947
2898
  const selfDir = dirname3(fileURLToPath(import.meta.url));
2948
- const bundledWebDir = join11(selfDir, "web");
2949
- const monorepoWebDir = join11(selfDir, "../../web/dist");
2950
- const webDistDir = existsSync11(join11(bundledWebDir, "index.html")) ? bundledWebDir : monorepoWebDir;
2899
+ const bundledWebDir = join10(selfDir, "web");
2900
+ const monorepoWebDir = join10(selfDir, "../../web/dist");
2901
+ const webDistDir = existsSync10(join10(bundledWebDir, "index.html")) ? bundledWebDir : monorepoWebDir;
2951
2902
  const requestPath = path === "/" ? "/index.html" : path;
2952
- const filePath = join11(webDistDir, requestPath);
2953
- if (existsSync11(filePath) && !filePath.includes("..")) {
2903
+ const filePath = join10(webDistDir, requestPath);
2904
+ if (existsSync10(filePath) && !filePath.includes("..")) {
2954
2905
  const content = readFileSync6(filePath);
2955
2906
  const ext = filePath.split(".").pop() || "";
2956
2907
  const contentTypes = {
@@ -2969,8 +2920,8 @@ app2.get("/*", async (c) => {
2969
2920
  headers: { "content-type": contentTypes[ext] || "application/octet-stream" }
2970
2921
  });
2971
2922
  }
2972
- const indexPath = join11(webDistDir, "index.html");
2973
- if (existsSync11(indexPath)) {
2923
+ const indexPath = join10(webDistDir, "index.html");
2924
+ if (existsSync10(indexPath)) {
2974
2925
  const html = readFileSync6(indexPath, "utf-8");
2975
2926
  return new Response(html, {
2976
2927
  headers: { "content-type": "text/html; charset=utf-8" }
@@ -3034,11 +2985,11 @@ async function startServer(projectPath, userDevPort, requestedPort = STASHES_POR
3034
2985
  import open from "open";
3035
2986
 
3036
2987
  // ../server/dist/services/detector.js
3037
- import { existsSync as existsSync12, readFileSync as readFileSync7 } from "fs";
3038
- import { join as join12 } from "path";
2988
+ import { existsSync as existsSync11, readFileSync as readFileSync7 } from "fs";
2989
+ import { join as join11 } from "path";
3039
2990
  function detectFramework(projectPath) {
3040
- const packageJsonPath = join12(projectPath, "package.json");
3041
- if (!existsSync12(packageJsonPath)) {
2991
+ const packageJsonPath = join11(projectPath, "package.json");
2992
+ if (!existsSync11(packageJsonPath)) {
3042
2993
  return {
3043
2994
  framework: "unknown",
3044
2995
  devCommand: "npm run dev",
@@ -3100,7 +3051,7 @@ function getDevCommand(packageJson, fallback) {
3100
3051
  }
3101
3052
  function findConfig(projectPath, candidates) {
3102
3053
  for (const candidate of candidates) {
3103
- if (existsSync12(join12(projectPath, candidate))) {
3054
+ if (existsSync11(join11(projectPath, candidate))) {
3104
3055
  return candidate;
3105
3056
  }
3106
3057
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stashes",
3
- "version": "0.1.56",
3
+ "version": "0.1.57",
4
4
  "type": "module",
5
5
  "description": "Generate AI-powered UI design explorations in your project",
6
6
  "keywords": [