@stackwright-pro/mcp 0.2.0-alpha.21 → 0.2.0-alpha.23
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/integrity.js +9 -9
- package/dist/integrity.js.map +1 -1
- package/dist/integrity.mjs +9 -9
- package/dist/integrity.mjs.map +1 -1
- package/dist/server.js +313 -33
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +349 -69
- package/dist/server.mjs.map +1 -1
- package/package.json +2 -2
package/dist/server.js
CHANGED
|
@@ -1135,6 +1135,7 @@ function setupPackages(opts) {
|
|
|
1135
1135
|
|
|
1136
1136
|
// src/tools/questions.ts
|
|
1137
1137
|
var import_promises = require("fs/promises");
|
|
1138
|
+
var import_node_fs = require("fs");
|
|
1138
1139
|
var import_node_path = require("path");
|
|
1139
1140
|
var import_zod8 = require("zod");
|
|
1140
1141
|
|
|
@@ -1407,12 +1408,12 @@ function registerQuestionTools(server2) {
|
|
|
1407
1408
|
content: [
|
|
1408
1409
|
{
|
|
1409
1410
|
type: "text",
|
|
1410
|
-
text:
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1411
|
+
text: `Phase "${phase}" has no questions to present. Do NOT call ask_user_question. Call stackwright_pro_save_phase_answers({ phase: "${phase}", rawAnswers: [] }) directly, then proceed to the execution step for this phase.`
|
|
1412
|
+
},
|
|
1413
|
+
{
|
|
1414
|
+
type: "text",
|
|
1415
|
+
// Empty array — second block always present so the foreman's two-block contract holds
|
|
1416
|
+
text: JSON.stringify([])
|
|
1416
1417
|
}
|
|
1417
1418
|
],
|
|
1418
1419
|
isError: false
|
|
@@ -1433,6 +1434,144 @@ function registerQuestionTools(server2) {
|
|
|
1433
1434
|
};
|
|
1434
1435
|
}
|
|
1435
1436
|
);
|
|
1437
|
+
server2.tool(
|
|
1438
|
+
"stackwright_pro_get_next_question",
|
|
1439
|
+
"Returns the next unanswered question for a phase as a plain JSON object \u2014 one question at a time. Returns { done: true } when all questions are answered or the phase has no questions. Call this in a loop: present the question in plain chat, get user reply, call record_answer, repeat.",
|
|
1440
|
+
{ phase: import_zod8.z.string().describe('Phase name e.g. "designer", "api", "auth"') },
|
|
1441
|
+
async ({ phase }) => {
|
|
1442
|
+
const SAFE_PHASE = /^[a-z][a-z0-9-]{0,30}$/;
|
|
1443
|
+
if (!SAFE_PHASE.test(phase)) {
|
|
1444
|
+
return {
|
|
1445
|
+
content: [
|
|
1446
|
+
{
|
|
1447
|
+
type: "text",
|
|
1448
|
+
text: JSON.stringify({ error: `Invalid phase name: "${phase.slice(0, 50)}"` })
|
|
1449
|
+
}
|
|
1450
|
+
],
|
|
1451
|
+
isError: true
|
|
1452
|
+
};
|
|
1453
|
+
}
|
|
1454
|
+
const cwd = process.cwd();
|
|
1455
|
+
const questionsPath = (0, import_node_path.join)(cwd, ".stackwright", "questions", `${phase}.json`);
|
|
1456
|
+
let questions = [];
|
|
1457
|
+
try {
|
|
1458
|
+
const raw = await (0, import_promises.readFile)(questionsPath, "utf-8");
|
|
1459
|
+
const parsed = JSON.parse(raw);
|
|
1460
|
+
questions = parsed.questions ?? [];
|
|
1461
|
+
} catch {
|
|
1462
|
+
return { content: [{ type: "text", text: JSON.stringify({ done: true }) }] };
|
|
1463
|
+
}
|
|
1464
|
+
if (questions.length === 0) {
|
|
1465
|
+
return { content: [{ type: "text", text: JSON.stringify({ done: true }) }] };
|
|
1466
|
+
}
|
|
1467
|
+
const answersPath = (0, import_node_path.join)(cwd, ".stackwright", "answers", `${phase}.json`);
|
|
1468
|
+
let answeredIds = /* @__PURE__ */ new Set();
|
|
1469
|
+
try {
|
|
1470
|
+
const raw = await (0, import_promises.readFile)(answersPath, "utf-8");
|
|
1471
|
+
const parsed = JSON.parse(raw);
|
|
1472
|
+
answeredIds = new Set(Object.keys(parsed.answers ?? {}));
|
|
1473
|
+
} catch {
|
|
1474
|
+
}
|
|
1475
|
+
const next = questions.find((q) => !answeredIds.has(q.id));
|
|
1476
|
+
if (!next) {
|
|
1477
|
+
return { content: [{ type: "text", text: JSON.stringify({ done: true }) }] };
|
|
1478
|
+
}
|
|
1479
|
+
return {
|
|
1480
|
+
content: [
|
|
1481
|
+
{
|
|
1482
|
+
type: "text",
|
|
1483
|
+
text: JSON.stringify({
|
|
1484
|
+
done: false,
|
|
1485
|
+
questionId: next.id,
|
|
1486
|
+
question: next.question,
|
|
1487
|
+
type: next.type,
|
|
1488
|
+
options: next.options ?? null,
|
|
1489
|
+
help: next.help ?? null,
|
|
1490
|
+
index: questions.indexOf(next) + 1,
|
|
1491
|
+
total: questions.length
|
|
1492
|
+
})
|
|
1493
|
+
}
|
|
1494
|
+
]
|
|
1495
|
+
};
|
|
1496
|
+
}
|
|
1497
|
+
);
|
|
1498
|
+
server2.tool(
|
|
1499
|
+
"stackwright_pro_record_answer",
|
|
1500
|
+
"Records a single answer to a phase question. Appends to .stackwright/answers/{phase}.json incrementally. Idempotent \u2014 calling twice for the same questionId overwrites. Call after receiving each user reply in the conversational question loop.",
|
|
1501
|
+
{
|
|
1502
|
+
phase: import_zod8.z.string().describe('Phase name e.g. "designer"'),
|
|
1503
|
+
questionId: import_zod8.z.string().describe('The question ID from get_next_question, e.g. "designer-1"'),
|
|
1504
|
+
answer: import_zod8.z.string().describe("The user's free-text answer")
|
|
1505
|
+
},
|
|
1506
|
+
async ({ phase, questionId, answer }) => {
|
|
1507
|
+
const SAFE_PHASE = /^[a-z][a-z0-9-]{0,30}$/;
|
|
1508
|
+
if (!SAFE_PHASE.test(phase)) {
|
|
1509
|
+
return {
|
|
1510
|
+
content: [
|
|
1511
|
+
{ type: "text", text: JSON.stringify({ error: "Invalid phase name" }) }
|
|
1512
|
+
],
|
|
1513
|
+
isError: true
|
|
1514
|
+
};
|
|
1515
|
+
}
|
|
1516
|
+
if (!/^[a-z0-9][a-z0-9-]{0,63}$/i.test(questionId)) {
|
|
1517
|
+
return {
|
|
1518
|
+
content: [
|
|
1519
|
+
{ type: "text", text: JSON.stringify({ error: "Invalid questionId" }) }
|
|
1520
|
+
],
|
|
1521
|
+
isError: true
|
|
1522
|
+
};
|
|
1523
|
+
}
|
|
1524
|
+
const safeAnswer = answer.slice(0, 2e3);
|
|
1525
|
+
const cwd = process.cwd();
|
|
1526
|
+
const answersDir = (0, import_node_path.join)(cwd, ".stackwright", "answers");
|
|
1527
|
+
const answersPath = (0, import_node_path.join)(answersDir, `${phase}.json`);
|
|
1528
|
+
if ((0, import_node_fs.existsSync)(answersDir) && (0, import_node_fs.lstatSync)(answersDir).isSymbolicLink()) {
|
|
1529
|
+
return {
|
|
1530
|
+
content: [
|
|
1531
|
+
{
|
|
1532
|
+
type: "text",
|
|
1533
|
+
text: JSON.stringify({ error: "answers dir is a symlink \u2014 refusing to write" })
|
|
1534
|
+
}
|
|
1535
|
+
],
|
|
1536
|
+
isError: true
|
|
1537
|
+
};
|
|
1538
|
+
}
|
|
1539
|
+
(0, import_node_fs.mkdirSync)(answersDir, { recursive: true });
|
|
1540
|
+
let existing = {
|
|
1541
|
+
version: "1.0",
|
|
1542
|
+
phase,
|
|
1543
|
+
answers: {}
|
|
1544
|
+
};
|
|
1545
|
+
try {
|
|
1546
|
+
if ((0, import_node_fs.existsSync)(answersPath)) {
|
|
1547
|
+
if ((0, import_node_fs.lstatSync)(answersPath).isSymbolicLink()) {
|
|
1548
|
+
return {
|
|
1549
|
+
content: [
|
|
1550
|
+
{
|
|
1551
|
+
type: "text",
|
|
1552
|
+
text: JSON.stringify({ error: "answers file is a symlink \u2014 refusing to write" })
|
|
1553
|
+
}
|
|
1554
|
+
],
|
|
1555
|
+
isError: true
|
|
1556
|
+
};
|
|
1557
|
+
}
|
|
1558
|
+
const raw = await (0, import_promises.readFile)(answersPath, "utf-8");
|
|
1559
|
+
existing = JSON.parse(raw);
|
|
1560
|
+
}
|
|
1561
|
+
} catch {
|
|
1562
|
+
}
|
|
1563
|
+
existing.answers[questionId] = safeAnswer;
|
|
1564
|
+
existing.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1565
|
+
const tmp = `${answersPath}.tmp`;
|
|
1566
|
+
await (0, import_promises.writeFile)(tmp, JSON.stringify(existing, null, 2), "utf-8");
|
|
1567
|
+
(0, import_node_fs.renameSync)(tmp, answersPath);
|
|
1568
|
+
return {
|
|
1569
|
+
content: [
|
|
1570
|
+
{ type: "text", text: JSON.stringify({ recorded: true, phase, questionId }) }
|
|
1571
|
+
]
|
|
1572
|
+
};
|
|
1573
|
+
}
|
|
1574
|
+
);
|
|
1436
1575
|
}
|
|
1437
1576
|
|
|
1438
1577
|
// src/tools/orchestration.ts
|
|
@@ -1604,7 +1743,57 @@ function handleGetOtterName(input) {
|
|
|
1604
1743
|
isError: false
|
|
1605
1744
|
};
|
|
1606
1745
|
}
|
|
1746
|
+
function handleSaveBuildContext(input) {
|
|
1747
|
+
const cwd = input._cwd ?? process.cwd();
|
|
1748
|
+
const dir = (0, import_path3.join)(cwd, ".stackwright");
|
|
1749
|
+
const filePath = (0, import_path3.join)(dir, "build-context.json");
|
|
1750
|
+
try {
|
|
1751
|
+
(0, import_fs3.mkdirSync)(dir, { recursive: true });
|
|
1752
|
+
if ((0, import_fs3.existsSync)(filePath)) {
|
|
1753
|
+
const stat = (0, import_fs3.lstatSync)(filePath);
|
|
1754
|
+
if (stat.isSymbolicLink()) {
|
|
1755
|
+
return {
|
|
1756
|
+
text: JSON.stringify({
|
|
1757
|
+
success: false,
|
|
1758
|
+
error: `Refusing to write to symlink: ${filePath}`
|
|
1759
|
+
}),
|
|
1760
|
+
isError: true
|
|
1761
|
+
};
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
const payload = {
|
|
1765
|
+
version: "1.0",
|
|
1766
|
+
savedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1767
|
+
buildContext: input.buildContext
|
|
1768
|
+
};
|
|
1769
|
+
(0, import_fs3.writeFileSync)(filePath, JSON.stringify(payload, null, 2) + "\n");
|
|
1770
|
+
return {
|
|
1771
|
+
text: JSON.stringify({ success: true, path: filePath }),
|
|
1772
|
+
isError: false
|
|
1773
|
+
};
|
|
1774
|
+
} catch (err) {
|
|
1775
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1776
|
+
return {
|
|
1777
|
+
text: JSON.stringify({ success: false, error: message }),
|
|
1778
|
+
isError: true
|
|
1779
|
+
};
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1607
1782
|
function registerOrchestrationTools(server2) {
|
|
1783
|
+
server2.tool(
|
|
1784
|
+
"stackwright_pro_save_build_context",
|
|
1785
|
+
`Save the user's initial build description to .stackwright/build-context.json. Call this once at startup after the user answers the opening "what are you building" question. The saved context is automatically prepended to specialist prompts by stackwright_pro_build_specialist_prompt.`,
|
|
1786
|
+
{
|
|
1787
|
+
buildContext: import_zod9.z.string().describe("Free-text description of what the user wants to build")
|
|
1788
|
+
},
|
|
1789
|
+
async ({ buildContext }) => {
|
|
1790
|
+
const { text, isError } = handleSaveBuildContext({ buildContext });
|
|
1791
|
+
return {
|
|
1792
|
+
content: [{ type: "text", text }],
|
|
1793
|
+
isError
|
|
1794
|
+
};
|
|
1795
|
+
}
|
|
1796
|
+
);
|
|
1608
1797
|
server2.tool(
|
|
1609
1798
|
"stackwright_pro_parse_otter_response",
|
|
1610
1799
|
"Parse and validate a specialist otter's QUESTION_COLLECTION_MODE JSON response. Handles JSON extraction from LLM responses (strips markdown, fixes single quotes, trailing commas). Detects the phase from the otter name. Use this immediately after invoke_agent() to get a validated manifest phase object.",
|
|
@@ -1883,28 +2072,62 @@ function handleSetPipelineState(input) {
|
|
|
1883
2072
|
return { text: JSON.stringify({ error: true, message: String(err) }), isError: true };
|
|
1884
2073
|
}
|
|
1885
2074
|
}
|
|
1886
|
-
function handleCheckExecutionReady(_cwd) {
|
|
2075
|
+
function handleCheckExecutionReady(_cwd, phase) {
|
|
1887
2076
|
const cwd = _cwd ?? process.cwd();
|
|
2077
|
+
if (phase) {
|
|
2078
|
+
if (!isValidPhase(phase)) {
|
|
2079
|
+
return {
|
|
2080
|
+
text: JSON.stringify({
|
|
2081
|
+
error: true,
|
|
2082
|
+
message: `Invalid phase: ${phase}. Valid phases are: ${PHASE_ORDER.join(", ")}`
|
|
2083
|
+
}),
|
|
2084
|
+
isError: true
|
|
2085
|
+
};
|
|
2086
|
+
}
|
|
2087
|
+
const answerFile = (0, import_path4.join)(cwd, ".stackwright", "answers", `${phase}.json`);
|
|
2088
|
+
if (!(0, import_fs4.existsSync)(answerFile)) {
|
|
2089
|
+
return {
|
|
2090
|
+
text: JSON.stringify({ ready: false, phase, reason: "Answer file not found" }),
|
|
2091
|
+
isError: false
|
|
2092
|
+
};
|
|
2093
|
+
}
|
|
2094
|
+
try {
|
|
2095
|
+
const raw = safeReadSync(answerFile);
|
|
2096
|
+
const parsed = JSON.parse(raw);
|
|
2097
|
+
if (typeof parsed["version"] !== "string" || typeof parsed["phase"] !== "string" || typeof parsed["answers"] !== "object" || parsed["answers"] === null) {
|
|
2098
|
+
return {
|
|
2099
|
+
text: JSON.stringify({ ready: false, phase, reason: "Answer file is malformed" }),
|
|
2100
|
+
isError: false
|
|
2101
|
+
};
|
|
2102
|
+
}
|
|
2103
|
+
return { text: JSON.stringify({ ready: true, phase }), isError: false };
|
|
2104
|
+
} catch {
|
|
2105
|
+
return {
|
|
2106
|
+
text: JSON.stringify({ ready: false, phase, reason: "Answer file could not be parsed" }),
|
|
2107
|
+
isError: false
|
|
2108
|
+
};
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
1888
2111
|
try {
|
|
1889
2112
|
const answersDir = (0, import_path4.join)(cwd, ".stackwright", "answers");
|
|
1890
2113
|
const answeredPhases = [];
|
|
1891
2114
|
const missingPhases = [];
|
|
1892
|
-
for (const
|
|
1893
|
-
const answerFile = (0, import_path4.join)(answersDir, `${
|
|
2115
|
+
for (const phase2 of PHASE_ORDER) {
|
|
2116
|
+
const answerFile = (0, import_path4.join)(answersDir, `${phase2}.json`);
|
|
1894
2117
|
if ((0, import_fs4.existsSync)(answerFile)) {
|
|
1895
2118
|
try {
|
|
1896
2119
|
const raw = safeReadSync(answerFile);
|
|
1897
2120
|
const parsed = JSON.parse(raw);
|
|
1898
2121
|
if (typeof parsed["version"] !== "string" || typeof parsed["phase"] !== "string" || typeof parsed["answers"] !== "object" || parsed["answers"] === null) {
|
|
1899
|
-
missingPhases.push(
|
|
2122
|
+
missingPhases.push(phase2);
|
|
1900
2123
|
continue;
|
|
1901
2124
|
}
|
|
1902
|
-
answeredPhases.push(
|
|
2125
|
+
answeredPhases.push(phase2);
|
|
1903
2126
|
} catch {
|
|
1904
|
-
missingPhases.push(
|
|
2127
|
+
missingPhases.push(phase2);
|
|
1905
2128
|
}
|
|
1906
2129
|
} else {
|
|
1907
|
-
missingPhases.push(
|
|
2130
|
+
missingPhases.push(phase2);
|
|
1908
2131
|
}
|
|
1909
2132
|
}
|
|
1910
2133
|
return {
|
|
@@ -2014,6 +2237,17 @@ function handleBuildSpecialistPrompt(input) {
|
|
|
2014
2237
|
if ((0, import_fs4.existsSync)(answersPath)) {
|
|
2015
2238
|
answers = JSON.parse(safeReadSync(answersPath));
|
|
2016
2239
|
}
|
|
2240
|
+
let buildContextText = "";
|
|
2241
|
+
const buildContextPath = (0, import_path4.join)(cwd, ".stackwright", "build-context.json");
|
|
2242
|
+
if ((0, import_fs4.existsSync)(buildContextPath)) {
|
|
2243
|
+
try {
|
|
2244
|
+
const bcRaw = JSON.parse(safeReadSync(buildContextPath));
|
|
2245
|
+
if (typeof bcRaw.buildContext === "string" && bcRaw.buildContext.trim().length > 0) {
|
|
2246
|
+
buildContextText = bcRaw.buildContext.trim();
|
|
2247
|
+
}
|
|
2248
|
+
} catch {
|
|
2249
|
+
}
|
|
2250
|
+
}
|
|
2017
2251
|
const deps = PHASE_DEPENDENCIES[phase];
|
|
2018
2252
|
const artifactSections = [];
|
|
2019
2253
|
const missingDependencies = [];
|
|
@@ -2024,13 +2258,14 @@ function handleBuildSpecialistPrompt(input) {
|
|
|
2024
2258
|
const content = JSON.parse(safeReadSync(artifactPath));
|
|
2025
2259
|
const expectedOtter = PHASE_TO_OTTER2[dep];
|
|
2026
2260
|
const artifactOtter = content["generatedBy"];
|
|
2261
|
+
const normalizedOtter = artifactOtter?.replace(/-[a-f0-9]{6}$/, "");
|
|
2027
2262
|
if (!artifactOtter) {
|
|
2028
2263
|
missingDependencies.push(dep);
|
|
2029
2264
|
artifactSections.push(
|
|
2030
2265
|
`[${artifactFile}]:
|
|
2031
2266
|
(integrity check failed: missing generatedBy field)`
|
|
2032
2267
|
);
|
|
2033
|
-
} else if (
|
|
2268
|
+
} else if (normalizedOtter !== expectedOtter) {
|
|
2034
2269
|
missingDependencies.push(dep);
|
|
2035
2270
|
artifactSections.push(
|
|
2036
2271
|
`[${artifactFile}]:
|
|
@@ -2046,7 +2281,11 @@ ${JSON.stringify(content, null, 2)}`);
|
|
|
2046
2281
|
(not yet available)`);
|
|
2047
2282
|
}
|
|
2048
2283
|
}
|
|
2049
|
-
const parts = [
|
|
2284
|
+
const parts = [];
|
|
2285
|
+
if (buildContextText) {
|
|
2286
|
+
parts.push("BUILD_CONTEXT:", buildContextText, "");
|
|
2287
|
+
}
|
|
2288
|
+
parts.push("ANSWERS:", JSON.stringify(answers, null, 2));
|
|
2050
2289
|
if (artifactSections.length > 0) {
|
|
2051
2290
|
parts.push("", "UPSTREAM ARTIFACTS:", "", ...artifactSections);
|
|
2052
2291
|
}
|
|
@@ -2205,9 +2444,11 @@ function registerPipelineTools(server2) {
|
|
|
2205
2444
|
);
|
|
2206
2445
|
server2.tool(
|
|
2207
2446
|
"stackwright_pro_check_execution_ready",
|
|
2208
|
-
`Check all phases have answer files in .stackwright/answers/. ${DESC}`,
|
|
2209
|
-
{
|
|
2210
|
-
|
|
2447
|
+
`Check all phases have answer files in .stackwright/answers/. If phase is provided, check only that phase. ${DESC}`,
|
|
2448
|
+
{
|
|
2449
|
+
phase: import_zod10.z.string().optional().describe("If provided, check only this phase's readiness. Omit to check all phases.")
|
|
2450
|
+
},
|
|
2451
|
+
async ({ phase }) => res(handleCheckExecutionReady(void 0, phase))
|
|
2211
2452
|
);
|
|
2212
2453
|
server2.tool(
|
|
2213
2454
|
"stackwright_pro_list_artifacts",
|
|
@@ -2217,12 +2458,49 @@ function registerPipelineTools(server2) {
|
|
|
2217
2458
|
);
|
|
2218
2459
|
server2.tool(
|
|
2219
2460
|
"stackwright_pro_write_phase_questions",
|
|
2220
|
-
`Parse otter question-collection response \u2192 .stackwright/questions/{phase}.json. ${DESC}`,
|
|
2461
|
+
`Parse otter question-collection response \u2192 .stackwright/questions/{phase}.json. Specialists may also call this directly with a parsed questions array. ${DESC}`,
|
|
2221
2462
|
{
|
|
2222
|
-
phase: import_zod10.z.string().describe('Phase name, e.g. "designer"'),
|
|
2223
|
-
responseText: import_zod10.z.string().describe("Raw LLM response from QUESTION_COLLECTION_MODE")
|
|
2463
|
+
phase: import_zod10.z.string().optional().describe('Phase name, e.g. "designer" (required for direct write)'),
|
|
2464
|
+
responseText: import_zod10.z.string().optional().describe("Raw LLM response from QUESTION_COLLECTION_MODE"),
|
|
2465
|
+
questions: jsonCoerce(import_zod10.z.array(import_zod10.z.any()).optional()).describe(
|
|
2466
|
+
"Questions array for direct specialist write"
|
|
2467
|
+
)
|
|
2224
2468
|
},
|
|
2225
|
-
async ({ phase, responseText }) =>
|
|
2469
|
+
async ({ phase, responseText, questions }) => {
|
|
2470
|
+
if (phase && questions && Array.isArray(questions)) {
|
|
2471
|
+
const SAFE_PHASE = /^[a-z][a-z0-9-]{0,30}$/;
|
|
2472
|
+
if (!SAFE_PHASE.test(phase)) {
|
|
2473
|
+
return {
|
|
2474
|
+
content: [
|
|
2475
|
+
{ type: "text", text: JSON.stringify({ error: `Invalid phase name` }) }
|
|
2476
|
+
],
|
|
2477
|
+
isError: true
|
|
2478
|
+
};
|
|
2479
|
+
}
|
|
2480
|
+
const questionsDir = (0, import_path4.join)(process.cwd(), ".stackwright", "questions");
|
|
2481
|
+
(0, import_fs4.mkdirSync)(questionsDir, { recursive: true });
|
|
2482
|
+
const outPath = (0, import_path4.join)(questionsDir, `${phase}.json`);
|
|
2483
|
+
(0, import_fs4.writeFileSync)(
|
|
2484
|
+
outPath,
|
|
2485
|
+
JSON.stringify({ phase, questions, writtenAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)
|
|
2486
|
+
);
|
|
2487
|
+
return {
|
|
2488
|
+
content: [
|
|
2489
|
+
{
|
|
2490
|
+
type: "text",
|
|
2491
|
+
text: JSON.stringify({
|
|
2492
|
+
written: true,
|
|
2493
|
+
phase,
|
|
2494
|
+
count: questions.length
|
|
2495
|
+
})
|
|
2496
|
+
}
|
|
2497
|
+
]
|
|
2498
|
+
};
|
|
2499
|
+
}
|
|
2500
|
+
return res(
|
|
2501
|
+
handleWritePhaseQuestions({ phase: phase ?? "", responseText: responseText ?? "" })
|
|
2502
|
+
);
|
|
2503
|
+
}
|
|
2226
2504
|
);
|
|
2227
2505
|
server2.tool(
|
|
2228
2506
|
"stackwright_pro_build_specialist_prompt",
|
|
@@ -2477,7 +2755,9 @@ function registerSafeWriteTools(server2) {
|
|
|
2477
2755
|
callerOtter: import_zod11.z.string().describe('The otter agent name requesting the write, e.g. "stackwright-pro-page-otter"'),
|
|
2478
2756
|
filePath: import_zod11.z.string().describe('Relative path from project root, e.g. "pages/dashboard/content.yml"'),
|
|
2479
2757
|
content: import_zod11.z.string().describe("File content to write"),
|
|
2480
|
-
createDirectories: import_zod11.z.boolean().optional().
|
|
2758
|
+
createDirectories: boolCoerce(import_zod11.z.boolean().optional().default(true)).describe(
|
|
2759
|
+
"Create parent directories if they don't exist. Default: true"
|
|
2760
|
+
)
|
|
2481
2761
|
},
|
|
2482
2762
|
async ({ callerOtter, filePath, content, createDirectories }) => {
|
|
2483
2763
|
const result = handleSafeWrite({
|
|
@@ -2891,39 +3171,39 @@ var import_path7 = require("path");
|
|
|
2891
3171
|
var _checksums = /* @__PURE__ */ new Map([
|
|
2892
3172
|
[
|
|
2893
3173
|
"stackwright-pro-api-otter.json",
|
|
2894
|
-
"
|
|
3174
|
+
"0ac26d85a5ad35b072a58965e1d5e090dd5c5f16dc14e68c452c3e99fcbb5510"
|
|
2895
3175
|
],
|
|
2896
3176
|
[
|
|
2897
3177
|
"stackwright-pro-auth-otter.json",
|
|
2898
|
-
"
|
|
3178
|
+
"d789b71f196659d5745ebfca87a7bda60a1bb63cfeccd17b4a273ac1e29bb08d"
|
|
2899
3179
|
],
|
|
2900
3180
|
[
|
|
2901
3181
|
"stackwright-pro-dashboard-otter.json",
|
|
2902
|
-
"
|
|
3182
|
+
"600e8597429c353e5b886f316731be84a86cd8b93617bf046e3cbf390b31a431"
|
|
2903
3183
|
],
|
|
2904
3184
|
[
|
|
2905
3185
|
"stackwright-pro-data-otter.json",
|
|
2906
|
-
"
|
|
3186
|
+
"b2946e3da3b53282c122d150e6db86b0cb89d2edba2a94a7666b26d27051be96"
|
|
2907
3187
|
],
|
|
2908
3188
|
[
|
|
2909
3189
|
"stackwright-pro-designer-otter.json",
|
|
2910
|
-
"
|
|
3190
|
+
"f4dbff5149051c77be1645de5ee12c0bd7d590c687a0b2d86737b915a5a6d5f0"
|
|
2911
3191
|
],
|
|
2912
3192
|
[
|
|
2913
3193
|
"stackwright-pro-foreman-otter.json",
|
|
2914
|
-
"
|
|
3194
|
+
"7464523d7288374dc6efa5c213c825ec0616e00cf4f739d8039eb41df812cbc5"
|
|
2915
3195
|
],
|
|
2916
3196
|
[
|
|
2917
3197
|
"stackwright-pro-page-otter.json",
|
|
2918
|
-
"
|
|
3198
|
+
"12aca7b666b3c85c1d96c700a2da7f209604cb75d0f064995e052711ddafd657"
|
|
2919
3199
|
],
|
|
2920
3200
|
[
|
|
2921
3201
|
"stackwright-pro-theme-otter.json",
|
|
2922
|
-
"
|
|
3202
|
+
"a303ec6c045420f2c916583e3f6efcda469e9610fedfc84a508ed8a8a75866bc"
|
|
2923
3203
|
],
|
|
2924
3204
|
[
|
|
2925
3205
|
"stackwright-pro-workflow-otter.json",
|
|
2926
|
-
"
|
|
3206
|
+
"16da6c109d0b5ee60d0a14e009dbeab02a7bbac3b0947795769da053565b9821"
|
|
2927
3207
|
]
|
|
2928
3208
|
]);
|
|
2929
3209
|
Object.freeze(_checksums);
|
|
@@ -3529,7 +3809,7 @@ var package_default = {
|
|
|
3529
3809
|
"test:coverage": "vitest run --coverage"
|
|
3530
3810
|
},
|
|
3531
3811
|
name: "@stackwright-pro/mcp",
|
|
3532
|
-
version: "0.2.0-alpha.
|
|
3812
|
+
version: "0.2.0-alpha.23",
|
|
3533
3813
|
description: "MCP tools for Stackwright Pro - Data Explorer, Security, ISR, and Dashboard generation",
|
|
3534
3814
|
license: "PROPRIETARY",
|
|
3535
3815
|
main: "./dist/server.js",
|