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 +141 -177
- package/dist/mcp.js +115 -151
- package/dist/web/assets/index-WpgHkzXv.js +97 -0
- package/dist/web/index.html +1 -1
- package/package.json +1 -1
- package/dist/web/assets/index-DLRxgF4c.js +0 -97
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
|
|
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
|
|
34
|
+
import { join as join10, dirname as dirname3 } from "path";
|
|
35
35
|
import { fileURLToPath } from "url";
|
|
36
|
-
import { existsSync as
|
|
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
|
|
60
|
-
import { existsSync as
|
|
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
|
|
64
|
-
import { join as
|
|
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
|
|
737
|
-
import { mkdirSync as
|
|
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
|
|
743
|
-
import { mkdirSync as
|
|
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(
|
|
746
|
-
const
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
const
|
|
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('
|
|
723
|
+
await page.goto('${url}', { waitUntil: 'networkidle', timeout: 20000 });
|
|
759
724
|
await page.waitForTimeout(2000);
|
|
760
725
|
} catch(e) {
|
|
761
|
-
await page.goto('
|
|
726
|
+
await page.goto('${url}', { waitUntil: 'domcontentloaded', timeout: 10000 });
|
|
762
727
|
await page.waitForTimeout(3000);
|
|
763
728
|
}
|
|
764
|
-
|
|
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
|
|
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 =
|
|
772
|
+
function buildScreenshotPrompt(port, diff, screenshotDir, stashId, componentPath) {
|
|
773
|
+
const outputPath = join5(screenshotDir, `${stashId}.png`);
|
|
808
774
|
return [
|
|
809
|
-
"You are a screenshot
|
|
810
|
-
"
|
|
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
|
-
|
|
784
|
+
`## App: http://localhost:${port}`,
|
|
785
|
+
componentPath ? `## Target component file: ${componentPath}` : "",
|
|
813
786
|
"",
|
|
814
|
-
"## Git diff of changes
|
|
787
|
+
"## Git diff of changes:",
|
|
815
788
|
"```",
|
|
816
789
|
diff,
|
|
817
790
|
"```",
|
|
818
791
|
"",
|
|
819
|
-
"##
|
|
820
|
-
"
|
|
821
|
-
"
|
|
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
|
-
"
|
|
824
|
-
"```",
|
|
825
|
-
"mcp__playwright__browser_run_code",
|
|
826
|
-
"```",
|
|
798
|
+
"Then use Playwright tools to navigate there and capture it:",
|
|
827
799
|
"",
|
|
828
|
-
"**
|
|
829
|
-
"
|
|
830
|
-
`
|
|
831
|
-
`
|
|
832
|
-
`
|
|
833
|
-
`
|
|
834
|
-
`
|
|
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
|
-
"
|
|
843
|
-
|
|
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
|
-
|
|
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": "
|
|
851
|
-
' "route": "/the-
|
|
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 =
|
|
891
|
-
if (!
|
|
892
|
-
|
|
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 =
|
|
935
|
-
if (
|
|
936
|
-
logger.info("smart-screenshot", `AI saved screenshot for ${stashId}
|
|
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 (!
|
|
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
|
-
|
|
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 =
|
|
1045
|
-
if (
|
|
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
|
|
1416
|
-
import { join as
|
|
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 =
|
|
2016
|
-
if (
|
|
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
|
|
2369
|
-
import { join as
|
|
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
|
|
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 (!
|
|
2387
|
-
|
|
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 (!
|
|
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 (
|
|
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
|
|
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 =
|
|
2672
|
-
if (!
|
|
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 =
|
|
2725
|
-
const monorepoWebDir =
|
|
2726
|
-
const webDistDir =
|
|
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 =
|
|
2729
|
-
if (
|
|
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 =
|
|
2749
|
-
if (
|
|
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
|
|
2811
|
-
import { join as
|
|
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 =
|
|
2814
|
-
if (!
|
|
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 (
|
|
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
|
|
3069
|
-
import { dirname as dirname4, join as
|
|
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 =
|
|
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:
|
|
3057
|
+
configPath: join12(home, ".claude.json"),
|
|
3094
3058
|
serversKey: "mcpServers",
|
|
3095
3059
|
format: "standard",
|
|
3096
|
-
detect: () =>
|
|
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:
|
|
3065
|
+
configPath: join12(appSupport, "Claude", "claude_desktop_config.json"),
|
|
3102
3066
|
serversKey: "mcpServers",
|
|
3103
3067
|
format: "standard",
|
|
3104
|
-
detect: () =>
|
|
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:
|
|
3073
|
+
configPath: join12(appSupport, "Code", "User", "mcp.json"),
|
|
3110
3074
|
serversKey: "servers",
|
|
3111
3075
|
format: "standard",
|
|
3112
|
-
detect: () =>
|
|
3076
|
+
detect: () => existsSync12(join12(appSupport, "Code", "User"))
|
|
3113
3077
|
},
|
|
3114
3078
|
{
|
|
3115
3079
|
id: "cursor",
|
|
3116
3080
|
name: "Cursor",
|
|
3117
|
-
configPath:
|
|
3081
|
+
configPath: join12(home, ".cursor", "mcp.json"),
|
|
3118
3082
|
serversKey: "mcpServers",
|
|
3119
3083
|
format: "standard",
|
|
3120
|
-
detect: () =>
|
|
3084
|
+
detect: () => existsSync12(join12(home, ".cursor"))
|
|
3121
3085
|
},
|
|
3122
3086
|
{
|
|
3123
3087
|
id: "windsurf",
|
|
3124
3088
|
name: "Windsurf",
|
|
3125
|
-
configPath:
|
|
3089
|
+
configPath: join12(home, ".codeium", "windsurf", "mcp_config.json"),
|
|
3126
3090
|
serversKey: "mcpServers",
|
|
3127
3091
|
format: "standard",
|
|
3128
|
-
detect: () =>
|
|
3092
|
+
detect: () => existsSync12(join12(home, ".codeium", "windsurf"))
|
|
3129
3093
|
},
|
|
3130
3094
|
{
|
|
3131
3095
|
id: "zed",
|
|
3132
3096
|
name: "Zed",
|
|
3133
|
-
configPath:
|
|
3097
|
+
configPath: join12(appSupport, "Zed", "settings.json"),
|
|
3134
3098
|
serversKey: "context_servers",
|
|
3135
3099
|
format: "zed",
|
|
3136
|
-
detect: () =>
|
|
3100
|
+
detect: () => existsSync12(join12(appSupport, "Zed"))
|
|
3137
3101
|
}
|
|
3138
3102
|
];
|
|
3139
3103
|
}
|
|
3140
3104
|
function readJsonFile(path) {
|
|
3141
|
-
if (!
|
|
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
|
-
|
|
3154
|
-
|
|
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
|
|
3293
|
-
import { tmpdir
|
|
3294
|
-
import { join as
|
|
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 =
|
|
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 =
|
|
3378
|
-
|
|
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 =
|
|
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");
|