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/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
|
|
30
|
-
import { join as
|
|
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
|
|
722
|
-
import { mkdirSync as
|
|
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
|
|
728
|
-
import { mkdirSync as
|
|
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(
|
|
731
|
-
const
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
const
|
|
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('
|
|
708
|
+
await page.goto('${url}', { waitUntil: 'networkidle', timeout: 20000 });
|
|
744
709
|
await page.waitForTimeout(2000);
|
|
745
710
|
} catch(e) {
|
|
746
|
-
await page.goto('
|
|
711
|
+
await page.goto('${url}', { waitUntil: 'domcontentloaded', timeout: 10000 });
|
|
747
712
|
await page.waitForTimeout(3000);
|
|
748
713
|
}
|
|
749
|
-
|
|
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,67 @@ ${diffStat}
|
|
|
782
748
|
## Partial diff (truncated):
|
|
783
749
|
${truncatedDiff}`;
|
|
784
750
|
} catch (err) {
|
|
785
|
-
logger.warn("smart-screenshot", `git diff failed
|
|
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
|
-
function buildScreenshotPrompt(port, diff, screenshotDir, stashId) {
|
|
792
|
-
const outputPath =
|
|
757
|
+
function buildScreenshotPrompt(port, diff, screenshotDir, stashId, componentPath) {
|
|
758
|
+
const outputPath = join5(screenshotDir, `${stashId}.png`);
|
|
793
759
|
return [
|
|
794
|
-
"You are a screenshot
|
|
795
|
-
"Be fast \u2014 you have a strict time limit.",
|
|
760
|
+
"## You are a screenshot agent. Your goal: capture a screenshot showing the UI changes described in the diff below.",
|
|
796
761
|
"",
|
|
797
|
-
|
|
762
|
+
"## RULES",
|
|
763
|
+
"- ONLY use Playwright MCP tools (prefixed with mcp__plugin_playwright_playwright__)",
|
|
764
|
+
"- Do NOT call: ToolSearch, Skill, Agent, TodoWrite, TaskCreate, TaskUpdate,",
|
|
765
|
+
" mcp__UseAI__*, mcp__stashes__*, mcp__plugin_drills__*, mcp__plugin_coverit__*,",
|
|
766
|
+
" EnterPlanMode, ExitPlanMode, WebSearch, WebFetch, LSP, Read, Write, Edit, Glob, Grep",
|
|
767
|
+
"- You have a strict time budget. Be fast and decisive.",
|
|
798
768
|
"",
|
|
799
|
-
|
|
769
|
+
`## App: http://localhost:${port}`,
|
|
770
|
+
componentPath ? `## Target component file: ${componentPath}` : "",
|
|
771
|
+
"",
|
|
772
|
+
"## Git diff of changes:",
|
|
800
773
|
"```",
|
|
801
774
|
diff,
|
|
802
775
|
"```",
|
|
803
776
|
"",
|
|
804
|
-
"##
|
|
805
|
-
"
|
|
806
|
-
"
|
|
777
|
+
"## Your task",
|
|
778
|
+
"Study the diff above to understand:",
|
|
779
|
+
"1. WHAT changed (component, styles, layout, content)",
|
|
780
|
+
"2. WHERE it lives in the app (which route? which section of the page? is it behind a tab/modal/click?)",
|
|
781
|
+
"3. WHAT STATE is needed to see it (does a button need to be clicked? a tab selected? a form filled?)",
|
|
807
782
|
"",
|
|
808
|
-
"
|
|
809
|
-
"```",
|
|
810
|
-
"mcp__playwright__browser_run_code",
|
|
811
|
-
"```",
|
|
783
|
+
"Then use Playwright tools to navigate there and capture it:",
|
|
812
784
|
"",
|
|
813
|
-
"**
|
|
814
|
-
"
|
|
815
|
-
`
|
|
816
|
-
`
|
|
817
|
-
`
|
|
818
|
-
`
|
|
819
|
-
`
|
|
820
|
-
` // await page.waitForTimeout(500);`,
|
|
821
|
-
` await page.waitForTimeout(2000);`,
|
|
822
|
-
` await page.screenshot({ path: '${outputPath.replace(/'/g, "\\'")}', type: 'png' });`,
|
|
823
|
-
` return 'saved';`,
|
|
824
|
-
`}`,
|
|
825
|
-
"```",
|
|
785
|
+
"**Available Playwright tools:**",
|
|
786
|
+
"- `mcp__plugin_playwright_playwright__browser_navigate` \u2014 go to a URL",
|
|
787
|
+
"- `mcp__plugin_playwright_playwright__browser_snapshot` \u2014 get the page accessibility tree (use this to find elements)",
|
|
788
|
+
"- `mcp__plugin_playwright_playwright__browser_click` \u2014 click an element (use ref from snapshot)",
|
|
789
|
+
"- `mcp__plugin_playwright_playwright__browser_hover` \u2014 hover over an element",
|
|
790
|
+
"- `mcp__plugin_playwright_playwright__browser_evaluate` \u2014 run JS on the page (e.g. scroll, trigger state)",
|
|
791
|
+
"- `mcp__plugin_playwright_playwright__browser_take_screenshot` \u2014 capture the screenshot",
|
|
826
792
|
"",
|
|
827
|
-
"
|
|
828
|
-
|
|
793
|
+
"**Typical flow:**",
|
|
794
|
+
`1. Navigate to http://localhost:${port} (or a sub-route if the diff indicates one)`,
|
|
795
|
+
"2. Take a snapshot to understand the page layout",
|
|
796
|
+
"3. If the changed component is not visible: scroll to it, click a tab, open a modal \u2014 whatever is needed",
|
|
797
|
+
"4. Take the screenshot once the changed component is visible",
|
|
829
798
|
"",
|
|
830
|
-
|
|
799
|
+
`## Output \u2014 after taking the screenshot, respond with ONLY this JSON:`,
|
|
800
|
+
"```json",
|
|
831
801
|
"{",
|
|
832
802
|
' "screenshots": [',
|
|
833
803
|
" {",
|
|
834
804
|
` "path": "${outputPath}",`,
|
|
835
|
-
' "label": "
|
|
836
|
-
' "route": "/the-
|
|
805
|
+
' "label": "Brief description of what the screenshot shows",',
|
|
806
|
+
' "route": "/the-route-you-navigated-to",',
|
|
837
807
|
' "isPrimary": true',
|
|
838
808
|
" }",
|
|
839
809
|
" ]",
|
|
840
|
-
"}"
|
|
810
|
+
"}",
|
|
811
|
+
"```"
|
|
841
812
|
].join(`
|
|
842
813
|
`);
|
|
843
814
|
}
|
|
@@ -871,10 +842,10 @@ async function fallbackScreenshot(port, projectPath, stashId) {
|
|
|
871
842
|
}
|
|
872
843
|
}
|
|
873
844
|
async function captureSmartScreenshots(opts) {
|
|
874
|
-
const { projectPath, stashId, stashBranch, parentBranch, worktreePath, port, model = "sonnet", timeout = DEFAULT_TIMEOUT } = opts;
|
|
875
|
-
const screenshotDir =
|
|
876
|
-
if (!
|
|
877
|
-
|
|
845
|
+
const { projectPath, stashId, stashBranch, parentBranch, worktreePath, port, model = "sonnet", timeout = DEFAULT_TIMEOUT, componentPath } = opts;
|
|
846
|
+
const screenshotDir = join5(projectPath, SCREENSHOTS_DIR2);
|
|
847
|
+
if (!existsSync5(screenshotDir)) {
|
|
848
|
+
mkdirSync4(screenshotDir, { recursive: true });
|
|
878
849
|
}
|
|
879
850
|
const diff = await getStashDiff(worktreePath, parentBranch);
|
|
880
851
|
if (!diff) {
|
|
@@ -882,15 +853,13 @@ async function captureSmartScreenshots(opts) {
|
|
|
882
853
|
return fallbackScreenshot(port, projectPath, stashId);
|
|
883
854
|
}
|
|
884
855
|
const processId = `screenshot-ai-${stashId}`;
|
|
885
|
-
const prompt = buildScreenshotPrompt(port, diff, screenshotDir, stashId);
|
|
856
|
+
const prompt = buildScreenshotPrompt(port, diff, screenshotDir, stashId, componentPath);
|
|
886
857
|
const modelFlag = model === "sonnet" ? "sonnet" : "haiku";
|
|
887
858
|
const aiProcess = startAiProcess({
|
|
888
859
|
id: processId,
|
|
889
860
|
prompt,
|
|
890
861
|
cwd: worktreePath,
|
|
891
|
-
model: modelFlag
|
|
892
|
-
tools: [],
|
|
893
|
-
mcpConfigPath: getPlaywrightMcpConfigPath()
|
|
862
|
+
model: modelFlag
|
|
894
863
|
});
|
|
895
864
|
let textOutput = "";
|
|
896
865
|
let timedOut = false;
|
|
@@ -916,9 +885,9 @@ async function captureSmartScreenshots(opts) {
|
|
|
916
885
|
}
|
|
917
886
|
const result = parseAiResult(textOutput);
|
|
918
887
|
if (!result || !result.screenshots || result.screenshots.length === 0) {
|
|
919
|
-
const expectedPath =
|
|
920
|
-
if (
|
|
921
|
-
logger.info("smart-screenshot", `AI saved screenshot for ${stashId}
|
|
888
|
+
const expectedPath = join5(screenshotDir, `${stashId}.png`);
|
|
889
|
+
if (existsSync5(expectedPath)) {
|
|
890
|
+
logger.info("smart-screenshot", `AI saved screenshot for ${stashId} (no JSON response, but file exists)`);
|
|
922
891
|
const url = `/api/screenshots/${stashId}.png`;
|
|
923
892
|
return {
|
|
924
893
|
primary: url,
|
|
@@ -932,18 +901,12 @@ async function captureSmartScreenshots(opts) {
|
|
|
932
901
|
let primaryUrl = "";
|
|
933
902
|
for (const shot of result.screenshots) {
|
|
934
903
|
const filename = shot.path.split("/").pop() || "";
|
|
935
|
-
if (!
|
|
904
|
+
if (!existsSync5(shot.path)) {
|
|
936
905
|
logger.warn("smart-screenshot", `Screenshot file not found: ${shot.path}`);
|
|
937
906
|
continue;
|
|
938
907
|
}
|
|
939
908
|
const url = `/api/screenshots/${filename}`;
|
|
940
|
-
|
|
941
|
-
url,
|
|
942
|
-
label: shot.label,
|
|
943
|
-
route: shot.route,
|
|
944
|
-
isPrimary: shot.isPrimary
|
|
945
|
-
};
|
|
946
|
-
screenshots.push(screenshot);
|
|
909
|
+
screenshots.push({ url, label: shot.label, route: shot.route, isPrimary: shot.isPrimary });
|
|
947
910
|
if (shot.isPrimary)
|
|
948
911
|
primaryUrl = url;
|
|
949
912
|
}
|
|
@@ -1026,8 +989,8 @@ async function generate(opts) {
|
|
|
1026
989
|
const selectedDirectives = directives.slice(0, count);
|
|
1027
990
|
let sourceCode = "";
|
|
1028
991
|
if (component?.filePath) {
|
|
1029
|
-
const sourceFile =
|
|
1030
|
-
if (
|
|
992
|
+
const sourceFile = join6(projectPath, component.filePath);
|
|
993
|
+
if (existsSync6(sourceFile)) {
|
|
1031
994
|
sourceCode = readFileSync2(sourceFile, "utf-8");
|
|
1032
995
|
}
|
|
1033
996
|
}
|
|
@@ -1162,7 +1125,8 @@ async function generate(opts) {
|
|
|
1162
1125
|
worktreePath: screenshotWorktree.path,
|
|
1163
1126
|
port,
|
|
1164
1127
|
model: opts.screenshotModel,
|
|
1165
|
-
timeout: opts.screenshotTimeout
|
|
1128
|
+
timeout: opts.screenshotTimeout,
|
|
1129
|
+
componentPath: stash.componentPath ?? undefined
|
|
1166
1130
|
});
|
|
1167
1131
|
const readyStash = { ...generatedStash, status: "ready", screenshotUrl: primary, screenshots };
|
|
1168
1132
|
completedStashes.push(readyStash);
|
|
@@ -1328,7 +1292,8 @@ async function vary(opts) {
|
|
|
1328
1292
|
worktreePath: screenshotWorktree.path,
|
|
1329
1293
|
port,
|
|
1330
1294
|
model: opts.screenshotModel,
|
|
1331
|
-
timeout: opts.screenshotTimeout
|
|
1295
|
+
timeout: opts.screenshotTimeout,
|
|
1296
|
+
componentPath: stash.componentPath ?? undefined
|
|
1332
1297
|
});
|
|
1333
1298
|
screenshotPath = result.primary;
|
|
1334
1299
|
screenshots = [...result.screenshots];
|
|
@@ -1626,18 +1591,18 @@ async function handleRemove(args, projectPath) {
|
|
|
1626
1591
|
// ../server/dist/index.js
|
|
1627
1592
|
import { Hono as Hono2 } from "hono";
|
|
1628
1593
|
import { cors } from "hono/cors";
|
|
1629
|
-
import { join as
|
|
1594
|
+
import { join as join10, dirname as dirname3 } from "path";
|
|
1630
1595
|
import { fileURLToPath } from "url";
|
|
1631
|
-
import { existsSync as
|
|
1596
|
+
import { existsSync as existsSync10, readFileSync as readFileSync6 } from "fs";
|
|
1632
1597
|
|
|
1633
1598
|
// ../server/dist/routes/api.js
|
|
1634
1599
|
import { Hono } from "hono";
|
|
1635
|
-
import { join as
|
|
1636
|
-
import { existsSync as
|
|
1600
|
+
import { join as join9, basename } from "path";
|
|
1601
|
+
import { existsSync as existsSync9, readFileSync as readFileSync5 } from "fs";
|
|
1637
1602
|
|
|
1638
1603
|
// ../server/dist/services/stash-service.js
|
|
1639
|
-
import { readFileSync as readFileSync3, existsSync as
|
|
1640
|
-
import { join as
|
|
1604
|
+
import { readFileSync as readFileSync3, existsSync as existsSync7 } from "fs";
|
|
1605
|
+
import { join as join7 } from "path";
|
|
1641
1606
|
|
|
1642
1607
|
// ../server/dist/services/app-proxy.js
|
|
1643
1608
|
import { spawn as spawn5 } from "child_process";
|
|
@@ -2173,8 +2138,7 @@ class StashService {
|
|
|
2173
2138
|
id: "resolve-component",
|
|
2174
2139
|
prompt,
|
|
2175
2140
|
cwd: this.projectPath,
|
|
2176
|
-
model: "claude-haiku-4-5-20251001"
|
|
2177
|
-
tools: ["Read", "Grep", "Glob", "Bash"]
|
|
2141
|
+
model: "claude-haiku-4-5-20251001"
|
|
2178
2142
|
});
|
|
2179
2143
|
let resolvedPath = "";
|
|
2180
2144
|
try {
|
|
@@ -2236,8 +2200,8 @@ class StashService {
|
|
|
2236
2200
|
let sourceCode = "";
|
|
2237
2201
|
const filePath = component?.filePath || "";
|
|
2238
2202
|
if (filePath && filePath !== "auto-detect") {
|
|
2239
|
-
const sourceFile =
|
|
2240
|
-
if (
|
|
2203
|
+
const sourceFile = join7(this.projectPath, filePath);
|
|
2204
|
+
if (existsSync7(sourceFile)) {
|
|
2241
2205
|
sourceCode = readFileSync3(sourceFile, "utf-8");
|
|
2242
2206
|
}
|
|
2243
2207
|
}
|
|
@@ -2589,8 +2553,8 @@ ${refDescriptions.join(`
|
|
|
2589
2553
|
}
|
|
2590
2554
|
|
|
2591
2555
|
// ../server/dist/services/activity-store.js
|
|
2592
|
-
import { appendFileSync as appendFileSync2, readFileSync as readFileSync4, existsSync as
|
|
2593
|
-
import { join as
|
|
2556
|
+
import { appendFileSync as appendFileSync2, readFileSync as readFileSync4, existsSync as existsSync8, mkdirSync as mkdirSync5, rmSync as rmSync3 } from "fs";
|
|
2557
|
+
import { join as join8, dirname as dirname2 } from "path";
|
|
2594
2558
|
|
|
2595
2559
|
class ActivityStore {
|
|
2596
2560
|
cache = new Map;
|
|
@@ -2599,7 +2563,7 @@ class ActivityStore {
|
|
|
2599
2563
|
this.projectPath = projectPath;
|
|
2600
2564
|
}
|
|
2601
2565
|
jsonlPath(stashId) {
|
|
2602
|
-
return
|
|
2566
|
+
return join8(this.projectPath, ".stashes", "activity", `${stashId}.jsonl`);
|
|
2603
2567
|
}
|
|
2604
2568
|
append(event) {
|
|
2605
2569
|
const existing = this.cache.get(event.stashId) ?? [];
|
|
@@ -2607,8 +2571,8 @@ class ActivityStore {
|
|
|
2607
2571
|
this.cache.set(event.stashId, existing);
|
|
2608
2572
|
const filePath = this.jsonlPath(event.stashId);
|
|
2609
2573
|
const dir = dirname2(filePath);
|
|
2610
|
-
if (!
|
|
2611
|
-
|
|
2574
|
+
if (!existsSync8(dir))
|
|
2575
|
+
mkdirSync5(dir, { recursive: true });
|
|
2612
2576
|
appendFileSync2(filePath, JSON.stringify(event) + `
|
|
2613
2577
|
`, "utf-8");
|
|
2614
2578
|
}
|
|
@@ -2617,7 +2581,7 @@ class ActivityStore {
|
|
|
2617
2581
|
if (cached && cached.length > 0)
|
|
2618
2582
|
return cached;
|
|
2619
2583
|
const filePath = this.jsonlPath(stashId);
|
|
2620
|
-
if (!
|
|
2584
|
+
if (!existsSync8(filePath))
|
|
2621
2585
|
return [];
|
|
2622
2586
|
const lines = readFileSync4(filePath, "utf-8").trim().split(`
|
|
2623
2587
|
`).filter(Boolean);
|
|
@@ -2643,14 +2607,14 @@ class ActivityStore {
|
|
|
2643
2607
|
clear(stashId) {
|
|
2644
2608
|
this.cache.delete(stashId);
|
|
2645
2609
|
const filePath = this.jsonlPath(stashId);
|
|
2646
|
-
if (
|
|
2610
|
+
if (existsSync8(filePath)) {
|
|
2647
2611
|
rmSync3(filePath);
|
|
2648
2612
|
}
|
|
2649
2613
|
}
|
|
2650
2614
|
has(stashId) {
|
|
2651
2615
|
if (this.cache.has(stashId) && (this.cache.get(stashId)?.length ?? 0) > 0)
|
|
2652
2616
|
return true;
|
|
2653
|
-
return
|
|
2617
|
+
return existsSync8(this.jsonlPath(stashId));
|
|
2654
2618
|
}
|
|
2655
2619
|
}
|
|
2656
2620
|
|
|
@@ -2892,8 +2856,8 @@ app.get("/dev-server-status", async (c) => {
|
|
|
2892
2856
|
});
|
|
2893
2857
|
app.get("/screenshots/:filename", (c) => {
|
|
2894
2858
|
const filename = c.req.param("filename");
|
|
2895
|
-
const filePath =
|
|
2896
|
-
if (!
|
|
2859
|
+
const filePath = join9(serverState.projectPath, ".stashes", "screenshots", filename);
|
|
2860
|
+
if (!existsSync9(filePath))
|
|
2897
2861
|
return c.json({ error: "Not found" }, 404);
|
|
2898
2862
|
const content = readFileSync5(filePath);
|
|
2899
2863
|
return new Response(content, {
|
|
@@ -2945,12 +2909,12 @@ app2.route("/api", apiRoutes);
|
|
|
2945
2909
|
app2.get("/*", async (c) => {
|
|
2946
2910
|
const path = c.req.path;
|
|
2947
2911
|
const selfDir = dirname3(fileURLToPath(import.meta.url));
|
|
2948
|
-
const bundledWebDir =
|
|
2949
|
-
const monorepoWebDir =
|
|
2950
|
-
const webDistDir =
|
|
2912
|
+
const bundledWebDir = join10(selfDir, "web");
|
|
2913
|
+
const monorepoWebDir = join10(selfDir, "../../web/dist");
|
|
2914
|
+
const webDistDir = existsSync10(join10(bundledWebDir, "index.html")) ? bundledWebDir : monorepoWebDir;
|
|
2951
2915
|
const requestPath = path === "/" ? "/index.html" : path;
|
|
2952
|
-
const filePath =
|
|
2953
|
-
if (
|
|
2916
|
+
const filePath = join10(webDistDir, requestPath);
|
|
2917
|
+
if (existsSync10(filePath) && !filePath.includes("..")) {
|
|
2954
2918
|
const content = readFileSync6(filePath);
|
|
2955
2919
|
const ext = filePath.split(".").pop() || "";
|
|
2956
2920
|
const contentTypes = {
|
|
@@ -2969,8 +2933,8 @@ app2.get("/*", async (c) => {
|
|
|
2969
2933
|
headers: { "content-type": contentTypes[ext] || "application/octet-stream" }
|
|
2970
2934
|
});
|
|
2971
2935
|
}
|
|
2972
|
-
const indexPath =
|
|
2973
|
-
if (
|
|
2936
|
+
const indexPath = join10(webDistDir, "index.html");
|
|
2937
|
+
if (existsSync10(indexPath)) {
|
|
2974
2938
|
const html = readFileSync6(indexPath, "utf-8");
|
|
2975
2939
|
return new Response(html, {
|
|
2976
2940
|
headers: { "content-type": "text/html; charset=utf-8" }
|
|
@@ -3034,11 +2998,11 @@ async function startServer(projectPath, userDevPort, requestedPort = STASHES_POR
|
|
|
3034
2998
|
import open from "open";
|
|
3035
2999
|
|
|
3036
3000
|
// ../server/dist/services/detector.js
|
|
3037
|
-
import { existsSync as
|
|
3038
|
-
import { join as
|
|
3001
|
+
import { existsSync as existsSync11, readFileSync as readFileSync7 } from "fs";
|
|
3002
|
+
import { join as join11 } from "path";
|
|
3039
3003
|
function detectFramework(projectPath) {
|
|
3040
|
-
const packageJsonPath =
|
|
3041
|
-
if (!
|
|
3004
|
+
const packageJsonPath = join11(projectPath, "package.json");
|
|
3005
|
+
if (!existsSync11(packageJsonPath)) {
|
|
3042
3006
|
return {
|
|
3043
3007
|
framework: "unknown",
|
|
3044
3008
|
devCommand: "npm run dev",
|
|
@@ -3100,7 +3064,7 @@ function getDevCommand(packageJson, fallback) {
|
|
|
3100
3064
|
}
|
|
3101
3065
|
function findConfig(projectPath, candidates) {
|
|
3102
3066
|
for (const candidate of candidates) {
|
|
3103
|
-
if (
|
|
3067
|
+
if (existsSync11(join11(projectPath, candidate))) {
|
|
3104
3068
|
return candidate;
|
|
3105
3069
|
}
|
|
3106
3070
|
}
|