stashes 0.1.19 → 0.1.21
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 +213 -31
- package/dist/commands/setup.d.ts +13 -0
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +3 -3
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/update.d.ts +2 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +115 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +173 -28
- package/dist/web/assets/{index-_bKtJYAZ.js → index-CMBT005S.js} +1 -1
- package/dist/web/index.html +1 -1
- package/package.json +1 -1
package/dist/mcp.js
CHANGED
|
@@ -538,6 +538,7 @@ function killAiProcess(id) {
|
|
|
538
538
|
}
|
|
539
539
|
return false;
|
|
540
540
|
}
|
|
541
|
+
var toolNameMap = new Map;
|
|
541
542
|
async function* parseClaudeStream(proc) {
|
|
542
543
|
const stdout = proc.stdout;
|
|
543
544
|
if (!stdout || typeof stdout === "number") {
|
|
@@ -574,15 +575,58 @@ async function* parseClaudeStream(proc) {
|
|
|
574
575
|
} else if (block.type === "thinking" && block.thinking) {
|
|
575
576
|
yield { type: "thinking", content: block.thinking };
|
|
576
577
|
} else if (block.type === "tool_use") {
|
|
577
|
-
|
|
578
|
-
|
|
578
|
+
const toolId = block.id;
|
|
579
|
+
const toolName = block.name;
|
|
580
|
+
toolNameMap.set(toolId, toolName);
|
|
581
|
+
logger.debug("claude", `tool_use: ${toolName}`);
|
|
582
|
+
yield {
|
|
583
|
+
type: "tool_use",
|
|
584
|
+
content: JSON.stringify({ tool: toolName, input: block.input }),
|
|
585
|
+
toolName,
|
|
586
|
+
toolInput: block.input,
|
|
587
|
+
toolUseId: toolId
|
|
588
|
+
};
|
|
579
589
|
}
|
|
580
590
|
}
|
|
581
591
|
}
|
|
592
|
+
if (parsed.type === "user" && parsed.message) {
|
|
593
|
+
const message = parsed.message;
|
|
594
|
+
for (const block of message.content || []) {
|
|
595
|
+
if (block.type === "tool_result") {
|
|
596
|
+
const toolUseId = block.tool_use_id;
|
|
597
|
+
const isError = block.is_error || false;
|
|
598
|
+
const toolName = toolNameMap.get(toolUseId) || "unknown";
|
|
599
|
+
let resultContent;
|
|
600
|
+
if (typeof block.content === "string") {
|
|
601
|
+
resultContent = block.content;
|
|
602
|
+
} else if (Array.isArray(block.content)) {
|
|
603
|
+
resultContent = block.content.filter((c) => c.type === "text").map((c) => c.text).join(`
|
|
604
|
+
`);
|
|
605
|
+
} else {
|
|
606
|
+
resultContent = "";
|
|
607
|
+
}
|
|
608
|
+
yield {
|
|
609
|
+
type: "tool_result",
|
|
610
|
+
content: resultContent.substring(0, 500),
|
|
611
|
+
toolName,
|
|
612
|
+
toolUseId,
|
|
613
|
+
isError
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
if (parsed.type === "result") {
|
|
619
|
+
yield {
|
|
620
|
+
type: "result",
|
|
621
|
+
content: "",
|
|
622
|
+
resultSubtype: parsed.subtype
|
|
623
|
+
};
|
|
624
|
+
}
|
|
582
625
|
}
|
|
583
626
|
}
|
|
584
627
|
} finally {
|
|
585
628
|
logger.debug("claude", `stream ended`, { totalChunks: chunkCount });
|
|
629
|
+
toolNameMap.clear();
|
|
586
630
|
reader.releaseLock();
|
|
587
631
|
}
|
|
588
632
|
}
|
|
@@ -964,6 +1008,34 @@ async function remove(projectPath, stashId) {
|
|
|
964
1008
|
} catch {}
|
|
965
1009
|
logger.info("manage", `removed stash: ${stashId}`);
|
|
966
1010
|
}
|
|
1011
|
+
async function show(projectPath, stashId) {
|
|
1012
|
+
const persistence = new PersistenceService(projectPath);
|
|
1013
|
+
let found;
|
|
1014
|
+
for (const project of persistence.listProjects()) {
|
|
1015
|
+
found = persistence.getStash(project.id, stashId);
|
|
1016
|
+
if (found)
|
|
1017
|
+
break;
|
|
1018
|
+
}
|
|
1019
|
+
if (!found)
|
|
1020
|
+
return null;
|
|
1021
|
+
const git = simpleGit4(projectPath);
|
|
1022
|
+
const branch = found.branch || `stashes/${stashId}`;
|
|
1023
|
+
let diff = "";
|
|
1024
|
+
let files = [];
|
|
1025
|
+
try {
|
|
1026
|
+
const base = await git.raw(["merge-base", "HEAD", branch]);
|
|
1027
|
+
diff = await git.raw(["diff", base.trim(), branch, "--stat"]);
|
|
1028
|
+
const fullDiff = await git.raw(["diff", base.trim(), branch]);
|
|
1029
|
+
diff = fullDiff.length > 8000 ? fullDiff.substring(0, 8000) + `
|
|
1030
|
+
... (truncated)` : fullDiff;
|
|
1031
|
+
const fileList = await git.raw(["diff", "--name-only", base.trim(), branch]);
|
|
1032
|
+
files = fileList.trim().split(`
|
|
1033
|
+
`).filter(Boolean);
|
|
1034
|
+
} catch {
|
|
1035
|
+
diff = "(branch not found or no diff available)";
|
|
1036
|
+
}
|
|
1037
|
+
return { stash: found, diff, files };
|
|
1038
|
+
}
|
|
967
1039
|
// ../mcp/src/tools/generate.ts
|
|
968
1040
|
var generateParams = {
|
|
969
1041
|
prompt: z.string().describe('What UI changes to generate (e.g. "make the hero section bolder")'),
|
|
@@ -1088,10 +1160,48 @@ async function handleVary(args, projectPath) {
|
|
|
1088
1160
|
};
|
|
1089
1161
|
}
|
|
1090
1162
|
|
|
1091
|
-
// ../mcp/src/tools/
|
|
1163
|
+
// ../mcp/src/tools/show.ts
|
|
1092
1164
|
import { z as z4 } from "zod";
|
|
1165
|
+
var showParams = {
|
|
1166
|
+
stashId: z4.string().describe('The stash ID to inspect (e.g. "stash_a1b2c3d4")')
|
|
1167
|
+
};
|
|
1168
|
+
async function handleShow(args, projectPath) {
|
|
1169
|
+
const { stashId } = args;
|
|
1170
|
+
initLogFile(projectPath);
|
|
1171
|
+
const result = await show(projectPath, stashId);
|
|
1172
|
+
if (!result) {
|
|
1173
|
+
return {
|
|
1174
|
+
content: [{
|
|
1175
|
+
type: "text",
|
|
1176
|
+
text: `No stash found with ID "${stashId}".`
|
|
1177
|
+
}]
|
|
1178
|
+
};
|
|
1179
|
+
}
|
|
1180
|
+
const { stash, diff, files } = result;
|
|
1181
|
+
return {
|
|
1182
|
+
content: [{
|
|
1183
|
+
type: "text",
|
|
1184
|
+
text: JSON.stringify({
|
|
1185
|
+
id: stash.id,
|
|
1186
|
+
number: stash.number,
|
|
1187
|
+
prompt: stash.prompt,
|
|
1188
|
+
status: stash.status,
|
|
1189
|
+
branch: stash.branch,
|
|
1190
|
+
componentPath: stash.componentPath ?? null,
|
|
1191
|
+
screenshotPath: stash.screenshotUrl,
|
|
1192
|
+
relatedTo: stash.relatedTo,
|
|
1193
|
+
createdAt: stash.createdAt,
|
|
1194
|
+
filesChanged: files,
|
|
1195
|
+
diff
|
|
1196
|
+
}, null, 2)
|
|
1197
|
+
}]
|
|
1198
|
+
};
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
// ../mcp/src/tools/remove.ts
|
|
1202
|
+
import { z as z5 } from "zod";
|
|
1093
1203
|
var removeParams = {
|
|
1094
|
-
stashId:
|
|
1204
|
+
stashId: z5.string().describe("The stash ID to remove")
|
|
1095
1205
|
};
|
|
1096
1206
|
async function handleRemove(args, projectPath) {
|
|
1097
1207
|
const { stashId } = args;
|
|
@@ -1482,9 +1592,12 @@ ${refs.join(`
|
|
|
1482
1592
|
}
|
|
1483
1593
|
const chatPrompt = [
|
|
1484
1594
|
"You are helping the user explore UI design variations for their project.",
|
|
1485
|
-
"You have access to stashes MCP tools to generate, list, browse, vary, and apply stashes.",
|
|
1595
|
+
"You have access to stashes MCP tools to generate, list, show, browse, vary, and apply stashes.",
|
|
1486
1596
|
"If the user asks you to generate, create, or make variations, use the stashes_generate tool.",
|
|
1487
1597
|
"If the user asks to vary an existing stash, use the stashes_vary tool.",
|
|
1598
|
+
"If the user asks about what a stash changed, its diff, or its contents, use stashes_show to inspect it.",
|
|
1599
|
+
'IMPORTANT: NEVER call stashes_apply unless the user explicitly asks to "apply" or "merge" a stash.',
|
|
1600
|
+
'IMPORTANT: NEVER call stashes_remove unless the user explicitly asks to "delete" or "remove" a stash.',
|
|
1488
1601
|
"Otherwise, respond conversationally about their project and stashes.",
|
|
1489
1602
|
"",
|
|
1490
1603
|
component ? `Component: ${component.name}` : "",
|
|
@@ -1500,11 +1613,27 @@ ${sourceCode.substring(0, 3000)}
|
|
|
1500
1613
|
].filter(Boolean).join(`
|
|
1501
1614
|
`);
|
|
1502
1615
|
const aiProcess = startAiProcess("chat", chatPrompt, this.projectPath);
|
|
1503
|
-
let
|
|
1616
|
+
let thinkingBuf = "";
|
|
1617
|
+
let textBuf = "";
|
|
1618
|
+
const pendingMessages = [];
|
|
1619
|
+
const now = new Date().toISOString();
|
|
1620
|
+
function flushThinking() {
|
|
1621
|
+
if (!thinkingBuf)
|
|
1622
|
+
return;
|
|
1623
|
+
pendingMessages.push({ id: crypto.randomUUID(), role: "assistant", content: thinkingBuf, type: "thinking", createdAt: now });
|
|
1624
|
+
thinkingBuf = "";
|
|
1625
|
+
}
|
|
1626
|
+
function flushText() {
|
|
1627
|
+
if (!textBuf)
|
|
1628
|
+
return;
|
|
1629
|
+
pendingMessages.push({ id: crypto.randomUUID(), role: "assistant", content: textBuf, type: "text", createdAt: now });
|
|
1630
|
+
textBuf = "";
|
|
1631
|
+
}
|
|
1504
1632
|
try {
|
|
1505
1633
|
for await (const chunk of parseClaudeStream(aiProcess.process)) {
|
|
1506
1634
|
if (chunk.type === "text") {
|
|
1507
|
-
|
|
1635
|
+
flushThinking();
|
|
1636
|
+
textBuf += chunk.content;
|
|
1508
1637
|
this.broadcast({
|
|
1509
1638
|
type: "ai_stream",
|
|
1510
1639
|
content: chunk.content,
|
|
@@ -1512,6 +1641,7 @@ ${sourceCode.substring(0, 3000)}
|
|
|
1512
1641
|
source: "chat"
|
|
1513
1642
|
});
|
|
1514
1643
|
} else if (chunk.type === "thinking") {
|
|
1644
|
+
thinkingBuf += chunk.content;
|
|
1515
1645
|
this.broadcast({
|
|
1516
1646
|
type: "ai_stream",
|
|
1517
1647
|
content: chunk.content,
|
|
@@ -1519,13 +1649,25 @@ ${sourceCode.substring(0, 3000)}
|
|
|
1519
1649
|
source: "chat"
|
|
1520
1650
|
});
|
|
1521
1651
|
} else if (chunk.type === "tool_use") {
|
|
1652
|
+
flushThinking();
|
|
1653
|
+
flushText();
|
|
1522
1654
|
let toolName = "unknown";
|
|
1523
1655
|
let toolParams = {};
|
|
1524
1656
|
try {
|
|
1525
1657
|
const parsed = JSON.parse(chunk.content);
|
|
1526
|
-
toolName = parsed.tool
|
|
1527
|
-
toolParams = parsed.input
|
|
1658
|
+
toolName = parsed.tool ?? "unknown";
|
|
1659
|
+
toolParams = parsed.input ?? {};
|
|
1528
1660
|
} catch {}
|
|
1661
|
+
pendingMessages.push({
|
|
1662
|
+
id: crypto.randomUUID(),
|
|
1663
|
+
role: "assistant",
|
|
1664
|
+
content: chunk.content,
|
|
1665
|
+
type: "tool_start",
|
|
1666
|
+
toolName,
|
|
1667
|
+
toolParams,
|
|
1668
|
+
toolStatus: "running",
|
|
1669
|
+
createdAt: now
|
|
1670
|
+
});
|
|
1529
1671
|
this.broadcast({
|
|
1530
1672
|
type: "ai_stream",
|
|
1531
1673
|
content: chunk.content,
|
|
@@ -1536,35 +1678,37 @@ ${sourceCode.substring(0, 3000)}
|
|
|
1536
1678
|
toolStatus: "running"
|
|
1537
1679
|
});
|
|
1538
1680
|
} else if (chunk.type === "tool_result") {
|
|
1539
|
-
let
|
|
1540
|
-
let
|
|
1681
|
+
let toolResult = chunk.content;
|
|
1682
|
+
let isError = false;
|
|
1541
1683
|
try {
|
|
1542
1684
|
const parsed = JSON.parse(chunk.content);
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
} catch {
|
|
1546
|
-
|
|
1547
|
-
|
|
1685
|
+
toolResult = parsed.result ?? chunk.content;
|
|
1686
|
+
isError = !!parsed.is_error;
|
|
1687
|
+
} catch {}
|
|
1688
|
+
pendingMessages.push({
|
|
1689
|
+
id: crypto.randomUUID(),
|
|
1690
|
+
role: "assistant",
|
|
1691
|
+
content: chunk.content,
|
|
1692
|
+
type: "tool_end",
|
|
1693
|
+
toolStatus: isError ? "error" : "completed",
|
|
1694
|
+
toolResult: toolResult.substring(0, 300),
|
|
1695
|
+
createdAt: now
|
|
1696
|
+
});
|
|
1548
1697
|
this.broadcast({
|
|
1549
1698
|
type: "ai_stream",
|
|
1550
1699
|
content: chunk.content,
|
|
1551
1700
|
streamType: "tool_end",
|
|
1552
1701
|
source: "chat",
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
toolResult
|
|
1702
|
+
toolStatus: isError ? "error" : "completed",
|
|
1703
|
+
toolResult: toolResult.substring(0, 300)
|
|
1556
1704
|
});
|
|
1557
1705
|
}
|
|
1558
1706
|
}
|
|
1559
1707
|
await aiProcess.process.exited;
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
content: fullResponse,
|
|
1565
|
-
type: "text",
|
|
1566
|
-
createdAt: new Date().toISOString()
|
|
1567
|
-
});
|
|
1708
|
+
flushThinking();
|
|
1709
|
+
flushText();
|
|
1710
|
+
for (const msg of pendingMessages) {
|
|
1711
|
+
this.persistence.saveChatMessage(projectId, chatId, msg);
|
|
1568
1712
|
}
|
|
1569
1713
|
} catch (err) {
|
|
1570
1714
|
this.broadcast({
|
|
@@ -2196,7 +2340,8 @@ var server = new McpServer({
|
|
|
2196
2340
|
});
|
|
2197
2341
|
server.tool("stashes_generate", "Generate multiple AI-powered UI design explorations (stashes) for a given prompt. Each stash applies a different creative direction.", generateParams, async (args) => handleGenerate(args, projectPath));
|
|
2198
2342
|
server.tool("stashes_list", "List all existing stashes in the current project. Shows ID, prompt, status, branch, and screenshot path.", listParams, async (args) => handleList(args, projectPath));
|
|
2199
|
-
server.tool("
|
|
2343
|
+
server.tool("stashes_show", "Show detailed information about a specific stash including its git diff, changed files, prompt, and metadata. Use this to inspect what a stash changed without applying it.", showParams, async (args) => handleShow(args, projectPath));
|
|
2344
|
+
server.tool("stashes_apply", "Merge a stash branch into the current git branch. Applies the AI-generated UI changes and cleans up all worktrees. ONLY use when the user explicitly asks to apply or merge.", applyParams, async (args) => handleApply(args, projectPath));
|
|
2200
2345
|
server.tool("stashes_vary", "Create a variation of an existing stash by applying additional changes on top of it.", varyParams, async (args) => handleVary(args, projectPath));
|
|
2201
2346
|
server.tool("stashes_remove", "Delete a stash \u2014 removes its persistence entry and git branch.", removeParams, async (args) => handleRemove(args, projectPath));
|
|
2202
2347
|
server.tool("stashes_browse", "Start the Stashes web server and open the browser for interactive browsing of generated stashes.", browseParams, async (args) => handleBrowse(args, projectPath));
|