@stackwright-pro/mcp 0.2.0-alpha.66 → 0.2.0-alpha.68
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 +364 -31
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +373 -31
- package/dist/server.mjs.map +1 -1
- package/package.json +1 -1
package/dist/server.mjs
CHANGED
|
@@ -1307,6 +1307,13 @@ function answersToManifestFormat(answers, questions) {
|
|
|
1307
1307
|
return result;
|
|
1308
1308
|
}
|
|
1309
1309
|
|
|
1310
|
+
// src/validation.ts
|
|
1311
|
+
var SAFE_PHASE = /^[a-z][a-z0-9-]{0,30}$/;
|
|
1312
|
+
var SAFE_QUESTION_ID = /^[a-z0-9][a-z0-9-]{0,63}$/i;
|
|
1313
|
+
function isValidPhase(phase) {
|
|
1314
|
+
return SAFE_PHASE.test(phase);
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1310
1317
|
// src/tools/questions.ts
|
|
1311
1318
|
var ManifestQuestionSchema = z8.object({
|
|
1312
1319
|
id: z8.string(),
|
|
@@ -1341,7 +1348,6 @@ function registerQuestionTools(server2) {
|
|
|
1341
1348
|
},
|
|
1342
1349
|
async ({ phase, questions, answers }) => {
|
|
1343
1350
|
let resolvedQuestions;
|
|
1344
|
-
const SAFE_PHASE = /^[a-z][a-z0-9-]{0,30}$/;
|
|
1345
1351
|
if (!SAFE_PHASE.test(phase)) {
|
|
1346
1352
|
return {
|
|
1347
1353
|
content: [
|
|
@@ -1443,7 +1449,6 @@ function registerQuestionTools(server2) {
|
|
|
1443
1449
|
"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.",
|
|
1444
1450
|
{ phase: z8.string().describe('Phase name e.g. "designer", "api", "auth"') },
|
|
1445
1451
|
async ({ phase }) => {
|
|
1446
|
-
const SAFE_PHASE = /^[a-z][a-z0-9-]{0,30}$/;
|
|
1447
1452
|
if (!SAFE_PHASE.test(phase)) {
|
|
1448
1453
|
return {
|
|
1449
1454
|
content: [
|
|
@@ -1508,7 +1513,6 @@ function registerQuestionTools(server2) {
|
|
|
1508
1513
|
answer: z8.string().describe("The user's free-text answer")
|
|
1509
1514
|
},
|
|
1510
1515
|
async ({ phase, questionId, answer }) => {
|
|
1511
|
-
const SAFE_PHASE = /^[a-z][a-z0-9-]{0,30}$/;
|
|
1512
1516
|
if (!SAFE_PHASE.test(phase)) {
|
|
1513
1517
|
return {
|
|
1514
1518
|
content: [
|
|
@@ -1517,7 +1521,7 @@ function registerQuestionTools(server2) {
|
|
|
1517
1521
|
isError: true
|
|
1518
1522
|
};
|
|
1519
1523
|
}
|
|
1520
|
-
if (
|
|
1524
|
+
if (!SAFE_QUESTION_ID.test(questionId)) {
|
|
1521
1525
|
return {
|
|
1522
1526
|
content: [
|
|
1523
1527
|
{ type: "text", text: JSON.stringify({ error: "Invalid questionId" }) }
|
|
@@ -1670,14 +1674,61 @@ function handleSaveManifest(input) {
|
|
|
1670
1674
|
}
|
|
1671
1675
|
}
|
|
1672
1676
|
function handleSavePhaseAnswers(input) {
|
|
1677
|
+
if (!isValidPhase(input.phase)) {
|
|
1678
|
+
return {
|
|
1679
|
+
text: JSON.stringify({
|
|
1680
|
+
success: false,
|
|
1681
|
+
error: `Invalid phase name: "${input.phase.slice(0, 50)}". Must be lowercase alphanumeric with hyphens, max 31 chars.`
|
|
1682
|
+
}),
|
|
1683
|
+
isError: true
|
|
1684
|
+
};
|
|
1685
|
+
}
|
|
1686
|
+
for (let i = 0; i < input.rawAnswers.length; i++) {
|
|
1687
|
+
const a = input.rawAnswers[i];
|
|
1688
|
+
if (!a.question_header || a.question_header.trim().length === 0) {
|
|
1689
|
+
return {
|
|
1690
|
+
text: JSON.stringify({
|
|
1691
|
+
success: false,
|
|
1692
|
+
error: `rawAnswers[${i}].question_header is empty \u2014 every answer must have a non-empty question_header.`
|
|
1693
|
+
}),
|
|
1694
|
+
isError: true
|
|
1695
|
+
};
|
|
1696
|
+
}
|
|
1697
|
+
if (!a.selected_options || a.selected_options.length === 0) {
|
|
1698
|
+
return {
|
|
1699
|
+
text: JSON.stringify({
|
|
1700
|
+
success: false,
|
|
1701
|
+
error: `rawAnswers[${i}].selected_options is empty for question_header "${a.question_header}" \u2014 every answer must have at least one selected option.`
|
|
1702
|
+
}),
|
|
1703
|
+
isError: true
|
|
1704
|
+
};
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
if (input.questions && input.questions.length > 0 && input.rawAnswers.length === 0) {
|
|
1708
|
+
return {
|
|
1709
|
+
text: JSON.stringify({
|
|
1710
|
+
success: false,
|
|
1711
|
+
error: `${input.questions.length} questions provided but rawAnswers is empty \u2014 this is a malformed payload. If the phase truly has no questions, omit the questions parameter.`
|
|
1712
|
+
}),
|
|
1713
|
+
isError: true
|
|
1714
|
+
};
|
|
1715
|
+
}
|
|
1673
1716
|
const cwd = input._cwd ?? process.cwd();
|
|
1674
1717
|
const dir = join2(cwd, ".stackwright", "answers");
|
|
1675
1718
|
const filePath = join2(dir, `${input.phase}.json`);
|
|
1676
1719
|
try {
|
|
1677
1720
|
mkdirSync2(dir, { recursive: true });
|
|
1721
|
+
const warnings = [];
|
|
1678
1722
|
let answers;
|
|
1679
1723
|
if (input.questions && input.questions.length > 0) {
|
|
1680
|
-
|
|
1724
|
+
try {
|
|
1725
|
+
answers = answersToManifestFormat(input.rawAnswers, input.questions);
|
|
1726
|
+
} catch (mapErr) {
|
|
1727
|
+
answers = {};
|
|
1728
|
+
warnings.push(
|
|
1729
|
+
`Reverse-mapping threw (${mapErr instanceof Error ? mapErr.message : String(mapErr)}) \u2014 falling back to direct key mapping.`
|
|
1730
|
+
);
|
|
1731
|
+
}
|
|
1681
1732
|
if (Object.keys(answers).length === 0 && input.rawAnswers.length > 0) {
|
|
1682
1733
|
answers = Object.fromEntries(
|
|
1683
1734
|
input.rawAnswers.map((a) => [
|
|
@@ -1685,6 +1736,15 @@ function handleSavePhaseAnswers(input) {
|
|
|
1685
1736
|
a.selected_options.length > 1 ? a.selected_options : a.selected_options[0] ?? ""
|
|
1686
1737
|
])
|
|
1687
1738
|
);
|
|
1739
|
+
warnings.push(
|
|
1740
|
+
`Reverse-mapping produced empty answers despite ${input.rawAnswers.length} rawAnswers \u2014 used direct key mapping as fallback. Ensure question_header values match the manifest header format (e.g. "DESI-1", not descriptive keys).`
|
|
1741
|
+
);
|
|
1742
|
+
}
|
|
1743
|
+
const requiredCount = input.questions.filter((q) => q.required !== false).length;
|
|
1744
|
+
if (input.rawAnswers.length < requiredCount) {
|
|
1745
|
+
warnings.push(
|
|
1746
|
+
`Only ${input.rawAnswers.length} answers provided for ${requiredCount} required questions (${input.questions.length} total) \u2014 answers may be incomplete.`
|
|
1747
|
+
);
|
|
1688
1748
|
}
|
|
1689
1749
|
} else {
|
|
1690
1750
|
answers = Object.fromEntries(
|
|
@@ -1712,7 +1772,8 @@ function handleSavePhaseAnswers(input) {
|
|
|
1712
1772
|
text: JSON.stringify({
|
|
1713
1773
|
success: true,
|
|
1714
1774
|
path: filePath,
|
|
1715
|
-
answersCount: Object.keys(answers).length
|
|
1775
|
+
answersCount: Object.keys(answers).length,
|
|
1776
|
+
...warnings.length > 0 ? { warnings } : {}
|
|
1716
1777
|
}),
|
|
1717
1778
|
isError: false
|
|
1718
1779
|
};
|
|
@@ -1859,15 +1920,31 @@ function registerOrchestrationTools(server2) {
|
|
|
1859
1920
|
rawAnswers: jsonCoerce(
|
|
1860
1921
|
z9.array(
|
|
1861
1922
|
z9.object({
|
|
1862
|
-
question_header: z9.string(),
|
|
1863
|
-
selected_options: z9.array(z9.string()),
|
|
1923
|
+
question_header: z9.string().min(1, "question_header must not be empty"),
|
|
1924
|
+
selected_options: z9.array(z9.string()).min(1, "selected_options must have at least one entry"),
|
|
1864
1925
|
other_text: z9.string().nullable().optional()
|
|
1865
1926
|
})
|
|
1866
1927
|
)
|
|
1867
1928
|
).describe(
|
|
1868
1929
|
"Answers as returned by ask_user_question \u2014 pass the native array, not a JSON string"
|
|
1869
1930
|
),
|
|
1870
|
-
questions: jsonCoerce(
|
|
1931
|
+
questions: jsonCoerce(
|
|
1932
|
+
z9.array(
|
|
1933
|
+
z9.object({
|
|
1934
|
+
id: z9.string(),
|
|
1935
|
+
question: z9.string(),
|
|
1936
|
+
type: z9.string(),
|
|
1937
|
+
options: z9.array(z9.object({ label: z9.string(), value: z9.string() })).optional(),
|
|
1938
|
+
required: z9.boolean().optional(),
|
|
1939
|
+
default: z9.union([z9.string(), z9.boolean(), z9.array(z9.string())]).optional(),
|
|
1940
|
+
help: z9.string().optional(),
|
|
1941
|
+
dependsOn: z9.object({
|
|
1942
|
+
questionId: z9.string(),
|
|
1943
|
+
value: z9.union([z9.string(), z9.array(z9.string())])
|
|
1944
|
+
}).optional()
|
|
1945
|
+
}).passthrough()
|
|
1946
|
+
).optional()
|
|
1947
|
+
).describe(
|
|
1871
1948
|
"Original manifest questions for label\u2192value reverse-mapping \u2014 pass the native array, not a JSON string"
|
|
1872
1949
|
)
|
|
1873
1950
|
},
|
|
@@ -2327,7 +2404,7 @@ var PHASE_TO_OTTER2 = {
|
|
|
2327
2404
|
polish: "stackwright-pro-polish-otter",
|
|
2328
2405
|
geo: "stackwright-pro-geo-otter"
|
|
2329
2406
|
};
|
|
2330
|
-
function
|
|
2407
|
+
function isValidPhase2(phase) {
|
|
2331
2408
|
return PHASE_ORDER.includes(phase);
|
|
2332
2409
|
}
|
|
2333
2410
|
function defaultPhaseStatus() {
|
|
@@ -2423,7 +2500,7 @@ function handleGetPipelineState(_cwd) {
|
|
|
2423
2500
|
}
|
|
2424
2501
|
function handleSetPipelineState(input) {
|
|
2425
2502
|
const cwd = input._cwd ?? process.cwd();
|
|
2426
|
-
if (input.phase && !
|
|
2503
|
+
if (input.phase && !isValidPhase2(input.phase)) {
|
|
2427
2504
|
return {
|
|
2428
2505
|
text: JSON.stringify({
|
|
2429
2506
|
error: true,
|
|
@@ -2442,6 +2519,28 @@ function handleSetPipelineState(input) {
|
|
|
2442
2519
|
isError: true
|
|
2443
2520
|
};
|
|
2444
2521
|
}
|
|
2522
|
+
if (input.updates && input.updates.length > 0) {
|
|
2523
|
+
for (const update of input.updates) {
|
|
2524
|
+
if (!isValidPhase2(update.phase)) {
|
|
2525
|
+
return {
|
|
2526
|
+
text: JSON.stringify({
|
|
2527
|
+
error: true,
|
|
2528
|
+
message: `Invalid phase in batch update: ${update.phase}. Valid phases are: ${PHASE_ORDER.join(", ")}`
|
|
2529
|
+
}),
|
|
2530
|
+
isError: true
|
|
2531
|
+
};
|
|
2532
|
+
}
|
|
2533
|
+
if (!VALID_FIELDS.includes(update.field)) {
|
|
2534
|
+
return {
|
|
2535
|
+
text: JSON.stringify({
|
|
2536
|
+
error: true,
|
|
2537
|
+
message: `Invalid field in batch update: ${update.field}. Valid fields are: ${VALID_FIELDS.join(", ")}`
|
|
2538
|
+
}),
|
|
2539
|
+
isError: true
|
|
2540
|
+
};
|
|
2541
|
+
}
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2445
2544
|
try {
|
|
2446
2545
|
const state = readState(cwd);
|
|
2447
2546
|
if (input.status) {
|
|
@@ -2461,6 +2560,16 @@ function handleSetPipelineState(input) {
|
|
|
2461
2560
|
}
|
|
2462
2561
|
state.currentPhase = phase;
|
|
2463
2562
|
}
|
|
2563
|
+
if (input.updates && input.updates.length > 0) {
|
|
2564
|
+
for (const update of input.updates) {
|
|
2565
|
+
if (!state.phases[update.phase]) {
|
|
2566
|
+
state.phases[update.phase] = defaultPhaseStatus();
|
|
2567
|
+
}
|
|
2568
|
+
const batchPhaseState = state.phases[update.phase];
|
|
2569
|
+
batchPhaseState[update.field] = update.value;
|
|
2570
|
+
state.currentPhase = update.phase;
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2464
2573
|
writeState(cwd, state);
|
|
2465
2574
|
return { text: JSON.stringify(state), isError: false };
|
|
2466
2575
|
} catch (err) {
|
|
@@ -2470,7 +2579,7 @@ function handleSetPipelineState(input) {
|
|
|
2470
2579
|
function handleCheckExecutionReady(_cwd, phase) {
|
|
2471
2580
|
const cwd = _cwd ?? process.cwd();
|
|
2472
2581
|
if (phase) {
|
|
2473
|
-
if (!
|
|
2582
|
+
if (!isValidPhase2(phase)) {
|
|
2474
2583
|
return {
|
|
2475
2584
|
text: JSON.stringify({
|
|
2476
2585
|
error: true,
|
|
@@ -2618,7 +2727,7 @@ function handleListArtifacts(_cwd) {
|
|
|
2618
2727
|
function handleWritePhaseQuestions(input) {
|
|
2619
2728
|
const cwd = input._cwd ?? process.cwd();
|
|
2620
2729
|
const { phase, responseText } = input;
|
|
2621
|
-
if (!
|
|
2730
|
+
if (!isValidPhase2(phase)) {
|
|
2622
2731
|
return {
|
|
2623
2732
|
text: JSON.stringify({ error: true, message: `Unknown phase: ${phase}` }),
|
|
2624
2733
|
isError: true
|
|
@@ -2676,7 +2785,7 @@ function handleWritePhaseQuestions(input) {
|
|
|
2676
2785
|
function handleBuildSpecialistPrompt(input) {
|
|
2677
2786
|
const cwd = input._cwd ?? process.cwd();
|
|
2678
2787
|
const { phase } = input;
|
|
2679
|
-
if (!
|
|
2788
|
+
if (!isValidPhase2(phase)) {
|
|
2680
2789
|
return {
|
|
2681
2790
|
text: JSON.stringify({ error: true, message: `Unknown phase: ${phase}` }),
|
|
2682
2791
|
isError: true
|
|
@@ -2769,6 +2878,18 @@ ${JSON.stringify(content, null, 2)}`
|
|
|
2769
2878
|
if (buildContextText) {
|
|
2770
2879
|
parts.push("BUILD_CONTEXT:", buildContextText, "");
|
|
2771
2880
|
}
|
|
2881
|
+
if (phase === "auth") {
|
|
2882
|
+
const initContextPath = join4(cwd, ".stackwright", "init-context.json");
|
|
2883
|
+
if (existsSync5(initContextPath)) {
|
|
2884
|
+
try {
|
|
2885
|
+
const initContext = JSON.parse(safeReadSync(initContextPath));
|
|
2886
|
+
if (initContext.devOnly === true || initContext.nonInteractive === true) {
|
|
2887
|
+
answers["devOnly"] = true;
|
|
2888
|
+
}
|
|
2889
|
+
} catch {
|
|
2890
|
+
}
|
|
2891
|
+
}
|
|
2892
|
+
}
|
|
2772
2893
|
parts.push("ANSWERS:", JSON.stringify(answers, null, 2));
|
|
2773
2894
|
if (artifactSections.length > 0) {
|
|
2774
2895
|
parts.push("", "UPSTREAM ARTIFACTS:", "", ...artifactSections);
|
|
@@ -3051,6 +3172,12 @@ var PHASE_ARTIFACT_SCHEMA = {
|
|
|
3051
3172
|
protectedRoutes: ["/dashboard/:path*", "/procurement/:path*"],
|
|
3052
3173
|
auditEnabled: true,
|
|
3053
3174
|
auditRetentionDays: 90
|
|
3175
|
+
},
|
|
3176
|
+
// devScripts is OPTIONAL — only present when devOnly: true was used
|
|
3177
|
+
// Polish otter and foreman MUST check devScripts.written before referencing scripts
|
|
3178
|
+
devScripts: {
|
|
3179
|
+
written: true,
|
|
3180
|
+
scripts: { "dev:admin": "MOCK_USER=admin next dev" }
|
|
3054
3181
|
}
|
|
3055
3182
|
},
|
|
3056
3183
|
null,
|
|
@@ -3062,7 +3189,12 @@ var PHASE_ARTIFACT_SCHEMA = {
|
|
|
3062
3189
|
generatedBy: "stackwright-pro-polish-otter",
|
|
3063
3190
|
landingPage: { slug: "", rewritten: true },
|
|
3064
3191
|
navigation: { itemCount: 5, items: ["/dashboard", "/equipment", "/workflows"] },
|
|
3065
|
-
gettingStarted: "<rewritten|redirected|not-found>"
|
|
3192
|
+
gettingStarted: "<rewritten|redirected|not-found>",
|
|
3193
|
+
scaffoldCleanup: {
|
|
3194
|
+
deleted: ["content/posts/getting-started.yaml"],
|
|
3195
|
+
rewritten: ["app/not-found.tsx"],
|
|
3196
|
+
skipped: []
|
|
3197
|
+
}
|
|
3066
3198
|
},
|
|
3067
3199
|
null,
|
|
3068
3200
|
2
|
|
@@ -3071,7 +3203,7 @@ var PHASE_ARTIFACT_SCHEMA = {
|
|
|
3071
3203
|
function handleValidateArtifact(input) {
|
|
3072
3204
|
const cwd = input._cwd ?? process.cwd();
|
|
3073
3205
|
const { phase, responseText, artifact: directArtifact } = input;
|
|
3074
|
-
if (!
|
|
3206
|
+
if (!isValidPhase2(phase)) {
|
|
3075
3207
|
return {
|
|
3076
3208
|
text: JSON.stringify({ error: true, message: `Unknown phase: ${phase}` }),
|
|
3077
3209
|
isError: true
|
|
@@ -3219,7 +3351,16 @@ function registerPipelineTools(server2) {
|
|
|
3219
3351
|
status: z11.enum(["setup", "questions", "execution", "done"]).optional().describe("Top-level status override"),
|
|
3220
3352
|
incrementRetry: boolCoerce(z11.boolean().optional()).describe(
|
|
3221
3353
|
"Bump retryCount by 1 \u2014 must be a JSON boolean"
|
|
3222
|
-
)
|
|
3354
|
+
),
|
|
3355
|
+
updates: jsonCoerce(
|
|
3356
|
+
z11.array(
|
|
3357
|
+
z11.object({
|
|
3358
|
+
phase: z11.string(),
|
|
3359
|
+
field: z11.enum(["questionsCollected", "answered", "executed", "artifactWritten"]),
|
|
3360
|
+
value: z11.boolean()
|
|
3361
|
+
})
|
|
3362
|
+
).optional()
|
|
3363
|
+
).describe("Batch updates \u2014 apply multiple field changes in one atomic read\u2192modify\u2192write")
|
|
3223
3364
|
},
|
|
3224
3365
|
async (args) => res(
|
|
3225
3366
|
handleSetPipelineState({
|
|
@@ -3227,7 +3368,8 @@ function registerPipelineTools(server2) {
|
|
|
3227
3368
|
...args.field != null ? { field: args.field } : {},
|
|
3228
3369
|
...args.value != null ? { value: args.value } : {},
|
|
3229
3370
|
...args.status != null ? { status: args.status } : {},
|
|
3230
|
-
...args.incrementRetry != null ? { incrementRetry: args.incrementRetry } : {}
|
|
3371
|
+
...args.incrementRetry != null ? { incrementRetry: args.incrementRetry } : {},
|
|
3372
|
+
...args.updates != null ? { updates: args.updates } : {}
|
|
3231
3373
|
})
|
|
3232
3374
|
)
|
|
3233
3375
|
);
|
|
@@ -3263,7 +3405,6 @@ function registerPipelineTools(server2) {
|
|
|
3263
3405
|
},
|
|
3264
3406
|
async ({ phase, responseText, questions }) => {
|
|
3265
3407
|
if (phase && questions && Array.isArray(questions)) {
|
|
3266
|
-
const SAFE_PHASE = /^[a-z][a-z0-9-]{0,30}$/;
|
|
3267
3408
|
if (!SAFE_PHASE.test(phase)) {
|
|
3268
3409
|
return {
|
|
3269
3410
|
content: [
|
|
@@ -3395,7 +3536,8 @@ var OTTER_WRITE_ALLOWLISTS = {
|
|
|
3395
3536
|
},
|
|
3396
3537
|
{ prefix: "pages/", suffix: "/content.yml", description: "Landing page content" },
|
|
3397
3538
|
{ prefix: "pages/", suffix: "/content.yaml", description: "Landing page content" },
|
|
3398
|
-
{ prefix: ".stackwright/artifacts/", suffix: ".json", description: "Polish artifact" }
|
|
3539
|
+
{ prefix: ".stackwright/artifacts/", suffix: ".json", description: "Polish artifact" },
|
|
3540
|
+
{ prefix: "README.md", suffix: "", description: "Project README rewrite" }
|
|
3399
3541
|
],
|
|
3400
3542
|
"stackwright-services-otter": [
|
|
3401
3543
|
{ prefix: ".stackwright/artifacts/", suffix: ".json", description: "Services config artifact" },
|
|
@@ -4368,7 +4510,21 @@ async function configureAuthHandler(params, cwd) {
|
|
|
4368
4510
|
rbacDefaultRole: defaultRole,
|
|
4369
4511
|
protectedRoutesCount: protectedRoutes.length,
|
|
4370
4512
|
filesWritten,
|
|
4371
|
-
securityWarning
|
|
4513
|
+
securityWarning,
|
|
4514
|
+
...devOnly ? {
|
|
4515
|
+
devScripts: {
|
|
4516
|
+
written: filesWritten.includes("package.json"),
|
|
4517
|
+
scripts: filesWritten.includes("package.json") ? Object.fromEntries(
|
|
4518
|
+
roles.map((r) => {
|
|
4519
|
+
const k = deriveDevKey(r);
|
|
4520
|
+
return [`dev:${k}`, `MOCK_USER=${k} next dev`];
|
|
4521
|
+
})
|
|
4522
|
+
) : {},
|
|
4523
|
+
...filesWritten.includes("package.json") ? {} : {
|
|
4524
|
+
warning: "package.json not found \u2014 dev scripts were not written. Run the auth phase again after scaffolding creates package.json."
|
|
4525
|
+
}
|
|
4526
|
+
}
|
|
4527
|
+
} : {}
|
|
4372
4528
|
})
|
|
4373
4529
|
}
|
|
4374
4530
|
]
|
|
@@ -4429,7 +4585,7 @@ var _checksums = /* @__PURE__ */ new Map([
|
|
|
4429
4585
|
],
|
|
4430
4586
|
[
|
|
4431
4587
|
"stackwright-pro-auth-otter.json",
|
|
4432
|
-
"
|
|
4588
|
+
"c3be215f05cc48ec51ed75541184b89a8ac123463b80fe02f2adb6c1b5163853"
|
|
4433
4589
|
],
|
|
4434
4590
|
[
|
|
4435
4591
|
"stackwright-pro-dashboard-otter.json",
|
|
@@ -4445,35 +4601,35 @@ var _checksums = /* @__PURE__ */ new Map([
|
|
|
4445
4601
|
],
|
|
4446
4602
|
[
|
|
4447
4603
|
"stackwright-pro-domain-expert-otter.json",
|
|
4448
|
-
"
|
|
4604
|
+
"41e3a5838f05f6a51ed272860dd2d5b1df1f20bfc847eb8f39be109b89738e99"
|
|
4449
4605
|
],
|
|
4450
4606
|
[
|
|
4451
4607
|
"stackwright-pro-foreman-otter.json",
|
|
4452
|
-
"
|
|
4608
|
+
"5f18e37ee3f2064c62a2d01dfc541285f27e5762d0b1011e2a37a48af96c74c8"
|
|
4453
4609
|
],
|
|
4454
4610
|
[
|
|
4455
4611
|
"stackwright-pro-geo-otter.json",
|
|
4456
|
-
"
|
|
4612
|
+
"ff1191af8108cc70c09afe6a99b008dcb46f71e20f884da1ee424e431868a1b6"
|
|
4457
4613
|
],
|
|
4458
4614
|
[
|
|
4459
4615
|
"stackwright-pro-page-otter.json",
|
|
4460
|
-
"
|
|
4616
|
+
"1dcdf866be76d18f3aa2a9e56b8bf57a3d43477b43c410eb6d1feac0e7683658"
|
|
4461
4617
|
],
|
|
4462
4618
|
[
|
|
4463
4619
|
"stackwright-pro-polish-otter.json",
|
|
4464
|
-
"
|
|
4620
|
+
"72a52901b4781c9f0be97024ba6e19415cdfbd92369d9274aa3abe7efd208bc8"
|
|
4465
4621
|
],
|
|
4466
4622
|
[
|
|
4467
4623
|
"stackwright-pro-theme-otter.json",
|
|
4468
|
-
"
|
|
4624
|
+
"e8530a1146abc57eb31d2f036ba057d5eae7d4a2d9dca00e415d1c352ef4c5b5"
|
|
4469
4625
|
],
|
|
4470
4626
|
[
|
|
4471
4627
|
"stackwright-pro-workflow-otter.json",
|
|
4472
|
-
"
|
|
4628
|
+
"e02c1a5f492dd6b11f68e293d92f5f661dbb22e57570dcf133a9d0ffc7a8be7d"
|
|
4473
4629
|
],
|
|
4474
4630
|
[
|
|
4475
4631
|
"stackwright-services-otter.json",
|
|
4476
|
-
"
|
|
4632
|
+
"b8a252d6a2c5090138899925d3ea95bf3339b6e49adc276a1f5f7304b4ae5134"
|
|
4477
4633
|
]
|
|
4478
4634
|
]);
|
|
4479
4635
|
Object.freeze(_checksums);
|
|
@@ -5183,6 +5339,191 @@ function registerTypeSchemasTool(server2) {
|
|
|
5183
5339
|
);
|
|
5184
5340
|
}
|
|
5185
5341
|
|
|
5342
|
+
// src/tools/scaffold-cleanup.ts
|
|
5343
|
+
import {
|
|
5344
|
+
existsSync as existsSync9,
|
|
5345
|
+
lstatSync as lstatSync8,
|
|
5346
|
+
unlinkSync as unlinkSync2,
|
|
5347
|
+
readFileSync as readFileSync9,
|
|
5348
|
+
writeFileSync as writeFileSync7,
|
|
5349
|
+
readdirSync as readdirSync3,
|
|
5350
|
+
rmdirSync,
|
|
5351
|
+
mkdirSync as mkdirSync7
|
|
5352
|
+
} from "fs";
|
|
5353
|
+
import { join as join9, dirname as dirname2 } from "path";
|
|
5354
|
+
var SCAFFOLD_FILES_TO_DELETE = [
|
|
5355
|
+
"content/posts/getting-started.yaml",
|
|
5356
|
+
"content/posts/hello-world.yaml"
|
|
5357
|
+
];
|
|
5358
|
+
var SCAFFOLD_DIRS_TO_PRUNE = [
|
|
5359
|
+
"content/posts"
|
|
5360
|
+
// Don't prune 'content' — user may have other content directories
|
|
5361
|
+
];
|
|
5362
|
+
var NOT_FOUND_PATH = "app/not-found.tsx";
|
|
5363
|
+
var FALLBACK_COLORS = {
|
|
5364
|
+
background: "#ffffff",
|
|
5365
|
+
foreground: "#171717",
|
|
5366
|
+
primary: "#525252",
|
|
5367
|
+
mutedForeground: "#737373"
|
|
5368
|
+
};
|
|
5369
|
+
function readThemeColors(cwd) {
|
|
5370
|
+
const tokenPath = join9(cwd, ".stackwright", "artifacts", "theme-tokens.json");
|
|
5371
|
+
if (!existsSync9(tokenPath)) return { ...FALLBACK_COLORS };
|
|
5372
|
+
const stat = lstatSync8(tokenPath);
|
|
5373
|
+
if (stat.isSymbolicLink()) return { ...FALLBACK_COLORS };
|
|
5374
|
+
try {
|
|
5375
|
+
const raw = JSON.parse(readFileSync9(tokenPath, "utf-8"));
|
|
5376
|
+
const css = raw?.cssVariables ?? {};
|
|
5377
|
+
const toHsl = (hslValue) => {
|
|
5378
|
+
if (!hslValue || typeof hslValue !== "string") return null;
|
|
5379
|
+
return `hsl(${hslValue})`;
|
|
5380
|
+
};
|
|
5381
|
+
return {
|
|
5382
|
+
background: toHsl(css["--background"]) ?? FALLBACK_COLORS.background,
|
|
5383
|
+
foreground: toHsl(css["--foreground"]) ?? FALLBACK_COLORS.foreground,
|
|
5384
|
+
primary: toHsl(css["--primary"]) ?? FALLBACK_COLORS.primary,
|
|
5385
|
+
mutedForeground: toHsl(css["--muted-foreground"]) ?? FALLBACK_COLORS.mutedForeground
|
|
5386
|
+
};
|
|
5387
|
+
} catch {
|
|
5388
|
+
return { ...FALLBACK_COLORS };
|
|
5389
|
+
}
|
|
5390
|
+
}
|
|
5391
|
+
function generateNotFoundPage(colors) {
|
|
5392
|
+
return `export default function NotFound() {
|
|
5393
|
+
return (
|
|
5394
|
+
<div
|
|
5395
|
+
style={{
|
|
5396
|
+
display: 'flex',
|
|
5397
|
+
flexDirection: 'column',
|
|
5398
|
+
alignItems: 'center',
|
|
5399
|
+
justifyContent: 'center',
|
|
5400
|
+
minHeight: '100vh',
|
|
5401
|
+
backgroundColor: '${colors.background}',
|
|
5402
|
+
color: '${colors.foreground}',
|
|
5403
|
+
fontFamily: 'system-ui, -apple-system, sans-serif',
|
|
5404
|
+
padding: '2rem',
|
|
5405
|
+
textAlign: 'center',
|
|
5406
|
+
}}
|
|
5407
|
+
>
|
|
5408
|
+
<h1 style={{ fontSize: '4rem', fontWeight: 700, margin: 0, color: '${colors.primary}' }}>
|
|
5409
|
+
404
|
|
5410
|
+
</h1>
|
|
5411
|
+
<p style={{ fontSize: '1.25rem', color: '${colors.mutedForeground}', marginTop: '0.5rem' }}>
|
|
5412
|
+
Page not found
|
|
5413
|
+
</p>
|
|
5414
|
+
<a
|
|
5415
|
+
href="/"
|
|
5416
|
+
style={{
|
|
5417
|
+
marginTop: '2rem',
|
|
5418
|
+
padding: '0.75rem 1.5rem',
|
|
5419
|
+
backgroundColor: '${colors.primary}',
|
|
5420
|
+
color: '${colors.background}',
|
|
5421
|
+
borderRadius: '0.5rem',
|
|
5422
|
+
textDecoration: 'none',
|
|
5423
|
+
fontSize: '0.875rem',
|
|
5424
|
+
fontWeight: 500,
|
|
5425
|
+
}}
|
|
5426
|
+
>
|
|
5427
|
+
Go Home
|
|
5428
|
+
</a>
|
|
5429
|
+
</div>
|
|
5430
|
+
);
|
|
5431
|
+
}
|
|
5432
|
+
`;
|
|
5433
|
+
}
|
|
5434
|
+
function isSafePath(fullPath) {
|
|
5435
|
+
if (!existsSync9(fullPath)) return true;
|
|
5436
|
+
const stat = lstatSync8(fullPath);
|
|
5437
|
+
return !stat.isSymbolicLink();
|
|
5438
|
+
}
|
|
5439
|
+
function isDirEmpty(dirPath) {
|
|
5440
|
+
if (!existsSync9(dirPath)) return false;
|
|
5441
|
+
const stat = lstatSync8(dirPath);
|
|
5442
|
+
if (!stat.isDirectory()) return false;
|
|
5443
|
+
return readdirSync3(dirPath).length === 0;
|
|
5444
|
+
}
|
|
5445
|
+
function handleCleanupScaffold(_cwd) {
|
|
5446
|
+
const cwd = _cwd ?? process.cwd();
|
|
5447
|
+
const result = {
|
|
5448
|
+
deleted: [],
|
|
5449
|
+
rewritten: [],
|
|
5450
|
+
skipped: [],
|
|
5451
|
+
errors: [],
|
|
5452
|
+
prunedDirs: []
|
|
5453
|
+
};
|
|
5454
|
+
for (const relPath of SCAFFOLD_FILES_TO_DELETE) {
|
|
5455
|
+
const fullPath = join9(cwd, relPath);
|
|
5456
|
+
if (!existsSync9(fullPath)) {
|
|
5457
|
+
result.skipped.push(relPath);
|
|
5458
|
+
continue;
|
|
5459
|
+
}
|
|
5460
|
+
if (!isSafePath(fullPath)) {
|
|
5461
|
+
result.errors.push(`Refusing to delete symlink: ${relPath}`);
|
|
5462
|
+
continue;
|
|
5463
|
+
}
|
|
5464
|
+
try {
|
|
5465
|
+
unlinkSync2(fullPath);
|
|
5466
|
+
result.deleted.push(relPath);
|
|
5467
|
+
} catch (err) {
|
|
5468
|
+
result.errors.push(`Failed to delete ${relPath}: ${String(err)}`);
|
|
5469
|
+
}
|
|
5470
|
+
}
|
|
5471
|
+
for (const relDir of SCAFFOLD_DIRS_TO_PRUNE) {
|
|
5472
|
+
const fullDir = join9(cwd, relDir);
|
|
5473
|
+
if (!existsSync9(fullDir)) continue;
|
|
5474
|
+
if (!isSafePath(fullDir)) {
|
|
5475
|
+
result.errors.push(`Refusing to prune symlink directory: ${relDir}`);
|
|
5476
|
+
continue;
|
|
5477
|
+
}
|
|
5478
|
+
if (isDirEmpty(fullDir)) {
|
|
5479
|
+
try {
|
|
5480
|
+
rmdirSync(fullDir);
|
|
5481
|
+
result.prunedDirs.push(relDir);
|
|
5482
|
+
} catch (err) {
|
|
5483
|
+
result.errors.push(`Failed to prune directory ${relDir}: ${String(err)}`);
|
|
5484
|
+
}
|
|
5485
|
+
}
|
|
5486
|
+
}
|
|
5487
|
+
{
|
|
5488
|
+
const fullPath = join9(cwd, NOT_FOUND_PATH);
|
|
5489
|
+
if (!existsSync9(fullPath)) {
|
|
5490
|
+
result.skipped.push(NOT_FOUND_PATH);
|
|
5491
|
+
} else if (!isSafePath(fullPath)) {
|
|
5492
|
+
result.errors.push(`Refusing to rewrite symlink: ${NOT_FOUND_PATH}`);
|
|
5493
|
+
} else {
|
|
5494
|
+
try {
|
|
5495
|
+
const colors = readThemeColors(cwd);
|
|
5496
|
+
const content = generateNotFoundPage(colors);
|
|
5497
|
+
const dir = dirname2(fullPath);
|
|
5498
|
+
mkdirSync7(dir, { recursive: true });
|
|
5499
|
+
writeFileSync7(fullPath, content, "utf-8");
|
|
5500
|
+
result.rewritten.push(NOT_FOUND_PATH);
|
|
5501
|
+
} catch (err) {
|
|
5502
|
+
result.errors.push(`Failed to rewrite ${NOT_FOUND_PATH}: ${String(err)}`);
|
|
5503
|
+
}
|
|
5504
|
+
}
|
|
5505
|
+
}
|
|
5506
|
+
return {
|
|
5507
|
+
text: JSON.stringify(result),
|
|
5508
|
+
isError: false
|
|
5509
|
+
};
|
|
5510
|
+
}
|
|
5511
|
+
function registerScaffoldCleanupTools(server2) {
|
|
5512
|
+
const DESC = "Deterministic scaffold remnant cleanup \u2014 removes stale OSS scaffold files after raft run.";
|
|
5513
|
+
server2.tool(
|
|
5514
|
+
"stackwright_pro_cleanup_scaffold",
|
|
5515
|
+
`Remove/rewrite stale scaffold files (blog posts, hardcoded not-found page) after the raft pipeline completes. ${DESC}`,
|
|
5516
|
+
{},
|
|
5517
|
+
async () => {
|
|
5518
|
+
const r = handleCleanupScaffold();
|
|
5519
|
+
return {
|
|
5520
|
+
content: [{ type: "text", text: r.text }],
|
|
5521
|
+
isError: r.isError
|
|
5522
|
+
};
|
|
5523
|
+
}
|
|
5524
|
+
);
|
|
5525
|
+
}
|
|
5526
|
+
|
|
5186
5527
|
// package.json
|
|
5187
5528
|
var package_default = {
|
|
5188
5529
|
dependencies: {
|
|
@@ -5206,7 +5547,7 @@ var package_default = {
|
|
|
5206
5547
|
"test:coverage": "vitest run --coverage"
|
|
5207
5548
|
},
|
|
5208
5549
|
name: "@stackwright-pro/mcp",
|
|
5209
|
-
version: "0.2.0-alpha.
|
|
5550
|
+
version: "0.2.0-alpha.68",
|
|
5210
5551
|
description: "MCP tools for Stackwright Pro - Data Explorer, Security, ISR, and Dashboard generation",
|
|
5211
5552
|
license: "SEE LICENSE IN LICENSE",
|
|
5212
5553
|
main: "./dist/server.js",
|
|
@@ -5260,6 +5601,7 @@ registerIntegrityTools(server);
|
|
|
5260
5601
|
registerArtifactSigningTools(server);
|
|
5261
5602
|
registerDomainTools(server);
|
|
5262
5603
|
registerTypeSchemasTool(server);
|
|
5604
|
+
registerScaffoldCleanupTools(server);
|
|
5263
5605
|
async function main() {
|
|
5264
5606
|
const transport = new StdioServerTransport();
|
|
5265
5607
|
try {
|