@stackwright-pro/mcp 0.2.0-alpha.105 → 0.2.0-alpha.106

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/server.mjs CHANGED
@@ -1999,9 +1999,9 @@ function registerOrchestrationTools(server2) {
1999
1999
 
2000
2000
  // src/tools/pipeline.ts
2001
2001
  import { z as z13 } from "zod";
2002
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync5, mkdirSync as mkdirSync4, lstatSync as lstatSync6 } from "fs";
2002
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, existsSync as existsSync6, mkdirSync as mkdirSync4, lstatSync as lstatSync6 } from "fs";
2003
2003
  import { lockSync } from "proper-lockfile";
2004
- import { join as join5 } from "path";
2004
+ import { join as join6 } from "path";
2005
2005
  import { createHash as createHash3 } from "crypto";
2006
2006
 
2007
2007
  // src/artifact-signing.ts
@@ -2342,6 +2342,208 @@ function registerArtifactSigningTools(server2) {
2342
2342
  );
2343
2343
  }
2344
2344
 
2345
+ // src/tools/validate-pulse-content.ts
2346
+ import { load as yamlLoad } from "js-yaml";
2347
+ import { readFileSync as readFileSync4, readdirSync as readdirSync2, statSync, existsSync as existsSync5 } from "fs";
2348
+ import { join as join4, relative } from "path";
2349
+ import { PULSE_CONTENT_SCHEMAS, ColumnSchema } from "@stackwright-pro/pulse";
2350
+ function getAllowedProps(schema) {
2351
+ let inner = schema;
2352
+ while (inner && typeof inner.unwrap === "function") {
2353
+ inner = inner.unwrap();
2354
+ }
2355
+ if (inner && typeof inner === "object" && inner.shape && typeof inner.shape === "object") {
2356
+ return Object.keys(inner.shape);
2357
+ }
2358
+ return [];
2359
+ }
2360
+ var COLUMN_ALLOWED_PROPS = getAllowedProps(ColumnSchema);
2361
+ function walk(node, path5, relativeFilePath, violations, counter) {
2362
+ if (node === null || node === void 0 || typeof node !== "object") return;
2363
+ if (Array.isArray(node)) {
2364
+ for (let i = 0; i < node.length; i++) {
2365
+ walk(node[i], `${path5}[${i}]`, relativeFilePath, violations, counter);
2366
+ }
2367
+ return;
2368
+ }
2369
+ const obj = node;
2370
+ const contentType = typeof obj["type"] === "string" ? obj["type"] : null;
2371
+ if (contentType !== null && contentType in PULSE_CONTENT_SCHEMAS) {
2372
+ counter.checked++;
2373
+ const schema = PULSE_CONTENT_SCHEMAS[contentType];
2374
+ const result = schema.safeParse(obj);
2375
+ if (!result.success) {
2376
+ violations.push({
2377
+ file: relativeFilePath,
2378
+ yamlPath: path5,
2379
+ contentType,
2380
+ issues: result.error.issues.map((issue) => ({
2381
+ path: issue.path.map(String),
2382
+ message: issue.message
2383
+ })),
2384
+ emittedProps: Object.keys(obj).filter((k) => k !== "type"),
2385
+ allowedProps: getAllowedProps(schema),
2386
+ rawItem: obj
2387
+ });
2388
+ }
2389
+ }
2390
+ for (const [key, child] of Object.entries(obj)) {
2391
+ walk(child, `${path5}.${key}`, relativeFilePath, violations, counter);
2392
+ }
2393
+ }
2394
+ function findContentYamlFiles(dir) {
2395
+ const results = [];
2396
+ if (!existsSync5(dir)) return results;
2397
+ for (const entry of readdirSync2(dir)) {
2398
+ const fullPath = join4(dir, entry);
2399
+ const stat = statSync(fullPath);
2400
+ if (stat.isDirectory()) {
2401
+ results.push(...findContentYamlFiles(fullPath));
2402
+ } else if (entry === "content.yml") {
2403
+ results.push(fullPath);
2404
+ }
2405
+ }
2406
+ return results;
2407
+ }
2408
+ function validatePulseContent(projectRoot) {
2409
+ const pagesDir = join4(projectRoot, "pages");
2410
+ const contentFiles = findContentYamlFiles(pagesDir);
2411
+ const violations = [];
2412
+ const counter = { checked: 0 };
2413
+ for (const absolutePath of contentFiles) {
2414
+ const relPath = relative(projectRoot, absolutePath);
2415
+ let parsed;
2416
+ try {
2417
+ const raw = readFileSync4(absolutePath, "utf-8");
2418
+ parsed = yamlLoad(raw);
2419
+ } catch {
2420
+ continue;
2421
+ }
2422
+ walk(parsed, "content", relPath, violations, counter);
2423
+ }
2424
+ return {
2425
+ valid: violations.length === 0,
2426
+ filesScanned: contentFiles.length,
2427
+ itemsChecked: counter.checked,
2428
+ violations
2429
+ };
2430
+ }
2431
+ function unknownProps(violation) {
2432
+ const allowed = new Set(violation.allowedProps);
2433
+ return violation.emittedProps.filter((p) => !allowed.has(p));
2434
+ }
2435
+ function extractColumnViolations(violation) {
2436
+ if (violation.contentType !== "data_table_pulse") return [];
2437
+ const byColumn = /* @__PURE__ */ new Map();
2438
+ for (const issue of violation.issues) {
2439
+ if (issue.path[0] === "columns" && typeof issue.path[1] === "string") {
2440
+ const colIdx = parseInt(issue.path[1], 10);
2441
+ if (!isNaN(colIdx)) {
2442
+ const missing = issue.path[2] ?? issue.message;
2443
+ if (!byColumn.has(colIdx)) byColumn.set(colIdx, []);
2444
+ byColumn.get(colIdx).push(String(missing));
2445
+ }
2446
+ }
2447
+ }
2448
+ if (byColumn.size === 0) return [];
2449
+ const rawColumns = Array.isArray(violation.rawItem["columns"]) ? violation.rawItem["columns"] : [];
2450
+ const columnAllowed = new Set(COLUMN_ALLOWED_PROPS);
2451
+ const summaries = [];
2452
+ for (const [colIdx, missingProps] of byColumn.entries()) {
2453
+ const rawCol = rawColumns[colIdx] ?? {};
2454
+ const emitted = Object.keys(rawCol);
2455
+ const unknown = emitted.filter((p) => !columnAllowed.has(p));
2456
+ summaries.push({
2457
+ index: colIdx,
2458
+ emittedProps: emitted,
2459
+ unknownProps: unknown,
2460
+ missingProps: [...new Set(missingProps)]
2461
+ });
2462
+ }
2463
+ return summaries.sort((a, b) => a.index - b.index);
2464
+ }
2465
+ function swapSuggestion(unknownPropNames, missingPropNames, itemType) {
2466
+ if (unknownPropNames.length !== 1 || missingPropNames.length !== 1) return null;
2467
+ const level = itemType === "column" ? "column property" : "property";
2468
+ return ` Fix: rename ${level} \`${unknownPropNames[0]}\` \u2192 \`${missingPropNames[0]}\``;
2469
+ }
2470
+ function formatPulseRetryPrompt(result) {
2471
+ if (result.valid) return "";
2472
+ const byFile = /* @__PURE__ */ new Map();
2473
+ for (const v of result.violations) {
2474
+ if (!byFile.has(v.file)) byFile.set(v.file, []);
2475
+ byFile.get(v.file).push(v);
2476
+ }
2477
+ const fileCount = byFile.size;
2478
+ const totalViolations = result.violations.length;
2479
+ const lines = [
2480
+ `Pulse schema validation failed: ${totalViolations} violation(s) across ${fileCount} content.yml file(s).`,
2481
+ "",
2482
+ "You MUST fix ALL violations and re-call stackwright_pro_validate_artifact.",
2483
+ 'Do NOT invent new prop names. Only use props listed in "Schema allows".',
2484
+ ""
2485
+ ];
2486
+ for (const [file, violations] of byFile.entries()) {
2487
+ lines.push(`${file} \u2014 ${violations.length} violation(s):`);
2488
+ for (const v of violations) {
2489
+ lines.push(` ${v.yamlPath} (${v.contentType}):`);
2490
+ const unknown = unknownProps(v);
2491
+ const colViolations = extractColumnViolations(v);
2492
+ if (colViolations.length > 0) {
2493
+ for (const col of colViolations) {
2494
+ lines.push(` columns[${col.index}]:`);
2495
+ if (col.unknownProps.length > 0) {
2496
+ lines.push(
2497
+ ` Unknown prop(s) used: ${col.unknownProps.map((p) => `\`${p}\``).join(", ")}`
2498
+ );
2499
+ }
2500
+ if (col.missingProps.length > 0) {
2501
+ lines.push(
2502
+ ` Missing required prop(s): ${col.missingProps.map((p) => `\`${p}\``).join(", ")}`
2503
+ );
2504
+ }
2505
+ const suggestion = swapSuggestion(col.unknownProps, col.missingProps, "column");
2506
+ if (suggestion) lines.push(suggestion);
2507
+ }
2508
+ lines.push(
2509
+ ` Schema allows ONLY these column props: ${COLUMN_ALLOWED_PROPS.join(", ")}.`
2510
+ );
2511
+ lines.push(
2512
+ ` Do NOT use: \`label\` (use \`header\`), \`renderAs\` (use \`type\`), \`severityMap\` (use \`colorMap\`), \`width\` (not supported).`
2513
+ );
2514
+ } else if (unknown.length > 0 || v.issues.length > 0) {
2515
+ const missingFromIssues = v.issues.filter((i) => i.path.length === 1 && !i.path[0].match(/^\d+$/)).map((i) => i.path[0]).filter(Boolean);
2516
+ if (unknown.length > 0) {
2517
+ lines.push(` Unknown prop(s) used: ${unknown.map((p) => `\`${p}\``).join(", ")}`);
2518
+ }
2519
+ if (missingFromIssues.length > 0) {
2520
+ lines.push(
2521
+ ` Missing required prop(s): ${[...new Set(missingFromIssues)].map((p) => `\`${p}\``).join(", ")}`
2522
+ );
2523
+ }
2524
+ const suggestion = swapSuggestion(unknown, [...new Set(missingFromIssues)], "top");
2525
+ if (suggestion) lines.push(suggestion);
2526
+ lines.push(` Schema allows ONLY these props: ${v.allowedProps.join(", ")}.`);
2527
+ if (v.contentType === "metric_card_pulse") {
2528
+ lines.push(
2529
+ ` Do NOT use: \`valueField\`, \`valuePath\`, \`dataField\`, \`value\`. The correct prop is \`field\` (dot-path into the collection).`
2530
+ );
2531
+ }
2532
+ } else {
2533
+ for (const issue of v.issues) {
2534
+ lines.push(` ${issue.path.join(".")}: ${issue.message}`);
2535
+ }
2536
+ lines.push(` Schema allows ONLY these props: ${v.allowedProps.join(", ")}.`);
2537
+ }
2538
+ }
2539
+ lines.push("");
2540
+ }
2541
+ lines.push(
2542
+ "After fixing ALL violations above, re-call stackwright_pro_validate_artifact with the corrected artifact."
2543
+ );
2544
+ return lines.join("\n");
2545
+ }
2546
+
2345
2547
  // src/tools/pipeline.ts
2346
2548
  import { WorkflowFileSchema, authConfigSchema as authConfigSchema3 } from "@stackwright-pro/types";
2347
2549
 
@@ -2357,7 +2559,7 @@ import {
2357
2559
 
2358
2560
  // src/tools/validate-yaml-fragment.ts
2359
2561
  import { z as z11 } from "zod";
2360
- import { load as yamlLoad } from "js-yaml";
2562
+ import { load as yamlLoad2 } from "js-yaml";
2361
2563
  import {
2362
2564
  WorkflowStepSchema,
2363
2565
  WorkflowDefinitionSchema,
@@ -2465,7 +2667,7 @@ function handleValidateYamlFragment(input) {
2465
2667
  const name = schemaName;
2466
2668
  let parsed;
2467
2669
  try {
2468
- parsed = yamlLoad(yaml3);
2670
+ parsed = yamlLoad2(yaml3);
2469
2671
  } catch (err) {
2470
2672
  return {
2471
2673
  valid: false,
@@ -2692,8 +2894,8 @@ function registerGetSchemaTool(server2) {
2692
2894
  }
2693
2895
 
2694
2896
  // src/tools/pipeline-graph.ts
2695
- import { readFileSync as readFileSync4, readdirSync as readdirSync2, lstatSync as lstatSync5 } from "fs";
2696
- import { join as join4 } from "path";
2897
+ import { readFileSync as readFileSync5, readdirSync as readdirSync3, lstatSync as lstatSync5 } from "fs";
2898
+ import { join as join5 } from "path";
2697
2899
  var MANIFEST_NAME_TO_PHASE = {
2698
2900
  "stackwright-pro-designer-otter": "designer",
2699
2901
  "stackwright-pro-theme-otter": "theme",
@@ -2722,8 +2924,8 @@ var OTTER_SEARCH_PATHS = [
2722
2924
  ];
2723
2925
  function resolveOtterDirFromCwd() {
2724
2926
  const cwd = process.cwd();
2725
- for (const relative of OTTER_SEARCH_PATHS) {
2726
- const candidate = join4(cwd, relative);
2927
+ for (const relative2 of OTTER_SEARCH_PATHS) {
2928
+ const candidate = join5(cwd, relative2);
2727
2929
  try {
2728
2930
  lstatSync5(candidate);
2729
2931
  return candidate;
@@ -2735,14 +2937,14 @@ function resolveOtterDirFromCwd() {
2735
2937
  );
2736
2938
  }
2737
2939
  function loadOtterManifestsFromDir(otterDir) {
2738
- const entries = readdirSync2(otterDir);
2940
+ const entries = readdirSync3(otterDir);
2739
2941
  const manifests = [];
2740
2942
  for (const filename of entries) {
2741
2943
  if (!filename.endsWith("-otter.json")) continue;
2742
- const filePath = join4(otterDir, filename);
2944
+ const filePath = join5(otterDir, filename);
2743
2945
  if (lstatSync5(filePath).isSymbolicLink()) continue;
2744
2946
  try {
2745
- const raw = readFileSync4(filePath, "utf-8");
2947
+ const raw = readFileSync5(filePath, "utf-8");
2746
2948
  const manifest = JSON.parse(raw);
2747
2949
  manifests.push(manifest);
2748
2950
  } catch (err) {
@@ -2847,19 +3049,19 @@ var DISTRIBUTED_NAMESPACES = ["@stackwright-pro", "@stackwright"];
2847
3049
  function loadDistributedOtterManifests(cwd) {
2848
3050
  const results = [];
2849
3051
  for (const namespace of DISTRIBUTED_NAMESPACES) {
2850
- const nsDir = join4(cwd, "node_modules", namespace);
3052
+ const nsDir = join5(cwd, "node_modules", namespace);
2851
3053
  let pkgDirNames;
2852
3054
  try {
2853
- pkgDirNames = readdirSync2(nsDir);
3055
+ pkgDirNames = readdirSync3(nsDir);
2854
3056
  } catch {
2855
3057
  continue;
2856
3058
  }
2857
3059
  for (const pkgDirName of pkgDirNames) {
2858
- const pkgDir = join4(nsDir, pkgDirName);
3060
+ const pkgDir = join5(nsDir, pkgDirName);
2859
3061
  const packageName = `${namespace}/${pkgDirName}`;
2860
3062
  let otterRelPaths;
2861
3063
  try {
2862
- const raw = readFileSync4(join4(pkgDir, "package.json"), "utf-8");
3064
+ const raw = readFileSync5(join5(pkgDir, "package.json"), "utf-8");
2863
3065
  const pkgJson = JSON.parse(raw);
2864
3066
  const declared = pkgJson.stackwright?.otters;
2865
3067
  if (!Array.isArray(declared) || declared.length === 0) continue;
@@ -2868,9 +3070,9 @@ function loadDistributedOtterManifests(cwd) {
2868
3070
  continue;
2869
3071
  }
2870
3072
  for (const relPath of otterRelPaths) {
2871
- const otterFilePath = join4(pkgDir, relPath);
3073
+ const otterFilePath = join5(pkgDir, relPath);
2872
3074
  try {
2873
- const raw = readFileSync4(otterFilePath, "utf-8");
3075
+ const raw = readFileSync5(otterFilePath, "utf-8");
2874
3076
  const manifest = JSON.parse(raw);
2875
3077
  results.push({ manifest, packageName });
2876
3078
  } catch (err) {
@@ -2911,6 +3113,7 @@ function loadPipelineGraph() {
2911
3113
 
2912
3114
  // src/tools/pipeline.ts
2913
3115
  import { emit } from "@stackwright-pro/telemetry";
3116
+ var PULSE_CONTENT_PHASES = /* @__PURE__ */ new Set(["pages", "dashboard", "geo", "workflow"]);
2914
3117
  var PHASE_ORDER = [
2915
3118
  "designer",
2916
3119
  "theme",
@@ -2987,11 +3190,11 @@ function createDefaultState() {
2987
3190
  };
2988
3191
  }
2989
3192
  function statePath(cwd) {
2990
- return join5(cwd, ".stackwright", "pipeline-state.json");
3193
+ return join6(cwd, ".stackwright", "pipeline-state.json");
2991
3194
  }
2992
3195
  function readState(cwd) {
2993
3196
  const p = statePath(cwd);
2994
- if (!existsSync5(p)) return createDefaultState();
3197
+ if (!existsSync6(p)) return createDefaultState();
2995
3198
  const raw = JSON.parse(safeReadSync(p));
2996
3199
  if (typeof raw !== "object" || raw === null || raw.version !== "1.0") {
2997
3200
  return createDefaultState();
@@ -2999,7 +3202,7 @@ function readState(cwd) {
2999
3202
  return raw;
3000
3203
  }
3001
3204
  function safeWriteSync(filePath, content) {
3002
- if (existsSync5(filePath)) {
3205
+ if (existsSync6(filePath)) {
3003
3206
  const stat = lstatSync6(filePath);
3004
3207
  if (stat.isSymbolicLink()) {
3005
3208
  throw new Error(`Refusing to write to symlink: ${filePath}`);
@@ -3008,25 +3211,25 @@ function safeWriteSync(filePath, content) {
3008
3211
  writeFileSync4(filePath, content);
3009
3212
  }
3010
3213
  function safeReadSync(filePath) {
3011
- if (existsSync5(filePath)) {
3214
+ if (existsSync6(filePath)) {
3012
3215
  const stat = lstatSync6(filePath);
3013
3216
  if (stat.isSymbolicLink()) {
3014
3217
  throw new Error(`Refusing to read symlink: ${filePath}`);
3015
3218
  }
3016
3219
  }
3017
- return readFileSync5(filePath, "utf-8");
3220
+ return readFileSync6(filePath, "utf-8");
3018
3221
  }
3019
3222
  function writeState(cwd, state) {
3020
- const dir = join5(cwd, ".stackwright");
3223
+ const dir = join6(cwd, ".stackwright");
3021
3224
  mkdirSync4(dir, { recursive: true });
3022
3225
  state.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3023
3226
  safeWriteSync(statePath(cwd), JSON.stringify(state, null, 2) + "\n");
3024
3227
  }
3025
3228
  function updateStateAtomic(cwd, mutator) {
3026
- const dir = join5(cwd, ".stackwright");
3229
+ const dir = join6(cwd, ".stackwright");
3027
3230
  mkdirSync4(dir, { recursive: true });
3028
3231
  const path5 = statePath(cwd);
3029
- if (!existsSync5(path5)) {
3232
+ if (!existsSync6(path5)) {
3030
3233
  safeWriteSync(path5, JSON.stringify(createDefaultState(), null, 2) + "\n");
3031
3234
  }
3032
3235
  const release = lockSync(path5, {
@@ -3060,8 +3263,8 @@ function handleGetPipelineState(_cwd) {
3060
3263
  const cwd = _cwd ?? process.cwd();
3061
3264
  try {
3062
3265
  const state = readState(cwd);
3063
- const keyPath = join5(cwd, ".stackwright", "pipeline-keys.json");
3064
- if (!existsSync5(keyPath)) {
3266
+ const keyPath = join6(cwd, ".stackwright", "pipeline-keys.json");
3267
+ if (!existsSync6(keyPath)) {
3065
3268
  try {
3066
3269
  const { fingerprint } = initPipelineKeys(cwd);
3067
3270
  state["signingKeyFingerprint"] = fingerprint;
@@ -3192,8 +3395,8 @@ function handleCheckExecutionReady(_cwd, phase) {
3192
3395
  isError: true
3193
3396
  };
3194
3397
  }
3195
- const answerFile = join5(cwd, ".stackwright", "answers", `${phase}.json`);
3196
- if (!existsSync5(answerFile)) {
3398
+ const answerFile = join6(cwd, ".stackwright", "answers", `${phase}.json`);
3399
+ if (!existsSync6(answerFile)) {
3197
3400
  return {
3198
3401
  text: JSON.stringify({ ready: false, phase, reason: "Answer file not found" }),
3199
3402
  isError: false
@@ -3217,12 +3420,12 @@ function handleCheckExecutionReady(_cwd, phase) {
3217
3420
  }
3218
3421
  }
3219
3422
  try {
3220
- const answersDir = join5(cwd, ".stackwright", "answers");
3423
+ const answersDir = join6(cwd, ".stackwright", "answers");
3221
3424
  const answeredPhases = [];
3222
3425
  const missingPhases = [];
3223
3426
  for (const phase2 of PHASE_ORDER) {
3224
- const answerFile = join5(answersDir, `${phase2}.json`);
3225
- if (existsSync5(answerFile)) {
3427
+ const answerFile = join6(answersDir, `${phase2}.json`);
3428
+ if (existsSync6(answerFile)) {
3226
3429
  try {
3227
3430
  const raw = safeReadSync(answerFile);
3228
3431
  const parsed = JSON.parse(raw);
@@ -3291,7 +3494,7 @@ function handleGetReadyPhases(_cwd) {
3291
3494
  function handleListArtifacts(_cwd) {
3292
3495
  const cwd = _cwd ?? process.cwd();
3293
3496
  try {
3294
- const artifactsDir = join5(cwd, ".stackwright", "artifacts");
3497
+ const artifactsDir = join6(cwd, ".stackwright", "artifacts");
3295
3498
  let manifest = null;
3296
3499
  try {
3297
3500
  manifest = loadSignatureManifest(cwd);
@@ -3301,8 +3504,8 @@ function handleListArtifacts(_cwd) {
3301
3504
  let completedCount = 0;
3302
3505
  for (const phase of PHASE_ORDER) {
3303
3506
  const expectedFile = PHASE_ARTIFACT[phase];
3304
- const fullPath = join5(artifactsDir, expectedFile);
3305
- const exists = existsSync5(fullPath);
3507
+ const fullPath = join6(artifactsDir, expectedFile);
3508
+ const exists = existsSync6(fullPath);
3306
3509
  if (exists) completedCount++;
3307
3510
  let signed = false;
3308
3511
  let signatureValid = null;
@@ -3355,9 +3558,9 @@ function handleWritePhaseQuestions(input) {
3355
3558
  }
3356
3559
  } catch {
3357
3560
  }
3358
- const questionsDir = join5(cwd, ".stackwright", "questions");
3561
+ const questionsDir = join6(cwd, ".stackwright", "questions");
3359
3562
  mkdirSync4(questionsDir, { recursive: true });
3360
- const filePath = join5(questionsDir, `${phase}.json`);
3563
+ const filePath = join6(questionsDir, `${phase}.json`);
3361
3564
  const payload = {
3362
3565
  version: "1.0",
3363
3566
  phase,
@@ -3397,14 +3600,14 @@ function handleBuildSpecialistPrompt(input) {
3397
3600
  };
3398
3601
  }
3399
3602
  try {
3400
- const answersPath = join5(cwd, ".stackwright", "answers", `${phase}.json`);
3603
+ const answersPath = join6(cwd, ".stackwright", "answers", `${phase}.json`);
3401
3604
  let answers = {};
3402
- if (existsSync5(answersPath)) {
3605
+ if (existsSync6(answersPath)) {
3403
3606
  answers = JSON.parse(safeReadSync(answersPath));
3404
3607
  }
3405
3608
  let buildContextText = "";
3406
- const buildContextPath = join5(cwd, ".stackwright", "build-context.json");
3407
- if (existsSync5(buildContextPath)) {
3609
+ const buildContextPath = join6(cwd, ".stackwright", "build-context.json");
3610
+ if (existsSync6(buildContextPath)) {
3408
3611
  try {
3409
3612
  const bcRaw = JSON.parse(safeReadSync(buildContextPath));
3410
3613
  if (typeof bcRaw.buildContext === "string" && bcRaw.buildContext.trim().length > 0) {
@@ -3419,8 +3622,8 @@ function handleBuildSpecialistPrompt(input) {
3419
3622
  const missingDependencies = [];
3420
3623
  for (const dep of deps) {
3421
3624
  const artifactFile = PHASE_ARTIFACT[dep];
3422
- const artifactPath = join5(cwd, ".stackwright", "artifacts", artifactFile);
3423
- if (existsSync5(artifactPath)) {
3625
+ const artifactPath = join6(cwd, ".stackwright", "artifacts", artifactFile);
3626
+ if (existsSync6(artifactPath)) {
3424
3627
  const rawContent = safeReadSync(artifactPath);
3425
3628
  const rawBytes = Buffer.from(rawContent, "utf-8");
3426
3629
  const content = JSON.parse(rawContent);
@@ -3485,8 +3688,8 @@ ${JSON.stringify(content, null, 2)}`
3485
3688
  parts.push("BUILD_CONTEXT:", buildContextText, "");
3486
3689
  }
3487
3690
  if (phase === "auth") {
3488
- const initContextPath = join5(cwd, ".stackwright", "init-context.json");
3489
- if (existsSync5(initContextPath)) {
3691
+ const initContextPath = join6(cwd, ".stackwright", "init-context.json");
3692
+ if (existsSync6(initContextPath)) {
3490
3693
  try {
3491
3694
  const initContext = JSON.parse(safeReadSync(initContextPath));
3492
3695
  if (initContext.devOnly === true || initContext.nonInteractive === true) {
@@ -3991,13 +4194,13 @@ function _validateArtifactInner(input) {
3991
4194
  // to a flow or state-machine in this services-config artifact.
3992
4195
  // Skips gracefully when wizard-otter didn't run (Pro-only install) or declared no hooks.
3993
4196
  services: (artifact2) => {
3994
- const workflowArtifactPath = join5(cwd, ".stackwright", "artifacts", "workflow-config.json");
3995
- if (!existsSync5(workflowArtifactPath)) {
4197
+ const workflowArtifactPath = join6(cwd, ".stackwright", "artifacts", "workflow-config.json");
4198
+ if (!existsSync6(workflowArtifactPath)) {
3996
4199
  return { success: true };
3997
4200
  }
3998
4201
  let workflowArtifact;
3999
4202
  try {
4000
- workflowArtifact = JSON.parse(readFileSync5(workflowArtifactPath, "utf-8"));
4203
+ workflowArtifact = JSON.parse(readFileSync6(workflowArtifactPath, "utf-8"));
4001
4204
  } catch {
4002
4205
  return { success: true };
4003
4206
  }
@@ -4067,11 +4270,23 @@ function _validateArtifactInner(input) {
4067
4270
  return { text: JSON.stringify(result), isError: false };
4068
4271
  }
4069
4272
  }
4273
+ if (PULSE_CONTENT_PHASES.has(phase)) {
4274
+ const pulseResult = validatePulseContent(cwd);
4275
+ if (!pulseResult.valid) {
4276
+ const result = {
4277
+ valid: false,
4278
+ phase,
4279
+ violation: "schema-mismatch",
4280
+ retryPrompt: formatPulseRetryPrompt(pulseResult)
4281
+ };
4282
+ return { text: JSON.stringify(result), isError: false };
4283
+ }
4284
+ }
4070
4285
  try {
4071
- const artifactsDir = join5(cwd, ".stackwright", "artifacts");
4286
+ const artifactsDir = join6(cwd, ".stackwright", "artifacts");
4072
4287
  mkdirSync4(artifactsDir, { recursive: true });
4073
4288
  const artifactFile = phase === "api" && input.integrationName ? `api-config-${input.integrationName}.json` : PHASE_ARTIFACT[phase];
4074
- const artifactPath = join5(artifactsDir, artifactFile);
4289
+ const artifactPath = join6(artifactsDir, artifactFile);
4075
4290
  const serialized = JSON.stringify(artifact, null, 2) + "\n";
4076
4291
  const artifactBytes = Buffer.from(serialized, "utf-8");
4077
4292
  safeWriteSync(artifactPath, serialized);
@@ -4290,9 +4505,9 @@ function registerPipelineTools(server2) {
4290
4505
  isError: true
4291
4506
  };
4292
4507
  }
4293
- const questionsDir = join5(process.cwd(), ".stackwright", "questions");
4508
+ const questionsDir = join6(process.cwd(), ".stackwright", "questions");
4294
4509
  mkdirSync4(questionsDir, { recursive: true });
4295
- const outPath = join5(questionsDir, `${phase}.json`);
4510
+ const outPath = join6(questionsDir, `${phase}.json`);
4296
4511
  writeFileSync4(
4297
4512
  outPath,
4298
4513
  JSON.stringify({ phase, questions, writtenAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)
@@ -4380,8 +4595,8 @@ function registerPipelineTools(server2) {
4380
4595
 
4381
4596
  // src/tools/safe-write.ts
4382
4597
  import { z as z14 } from "zod";
4383
- import { writeFileSync as writeFileSync5, readFileSync as readFileSync6, existsSync as existsSync6, mkdirSync as mkdirSync5, lstatSync as lstatSync7 } from "fs";
4384
- import { normalize, isAbsolute, dirname, join as join6 } from "path";
4598
+ import { writeFileSync as writeFileSync5, readFileSync as readFileSync7, existsSync as existsSync7, mkdirSync as mkdirSync5, lstatSync as lstatSync7 } from "fs";
4599
+ import { normalize, isAbsolute, dirname, join as join7 } from "path";
4385
4600
  import { emit as emit2 } from "@stackwright-pro/telemetry";
4386
4601
  var OTTER_WRITE_ALLOWLISTS = {
4387
4602
  "stackwright-pro-designer-otter": [
@@ -4563,21 +4778,21 @@ function extractContentTypes(yamlContent) {
4563
4778
  return types.length > 0 ? types : ["unknown"];
4564
4779
  }
4565
4780
  function readPageRegistry(cwd) {
4566
- const regPath = join6(cwd, PAGE_REGISTRY_FILE);
4567
- if (!existsSync6(regPath)) return {};
4781
+ const regPath = join7(cwd, PAGE_REGISTRY_FILE);
4782
+ if (!existsSync7(regPath)) return {};
4568
4783
  try {
4569
4784
  const stat = lstatSync7(regPath);
4570
4785
  if (stat.isSymbolicLink()) return {};
4571
- return JSON.parse(readFileSync6(regPath, "utf-8"));
4786
+ return JSON.parse(readFileSync7(regPath, "utf-8"));
4572
4787
  } catch {
4573
4788
  return {};
4574
4789
  }
4575
4790
  }
4576
4791
  function writePageRegistry(cwd, registry) {
4577
- const dir = join6(cwd, ".stackwright");
4792
+ const dir = join7(cwd, ".stackwright");
4578
4793
  mkdirSync5(dir, { recursive: true });
4579
- const regPath = join6(cwd, PAGE_REGISTRY_FILE);
4580
- if (existsSync6(regPath)) {
4794
+ const regPath = join7(cwd, PAGE_REGISTRY_FILE);
4795
+ if (existsSync7(regPath)) {
4581
4796
  const stat = lstatSync7(regPath);
4582
4797
  if (stat.isSymbolicLink()) {
4583
4798
  throw new Error("Refusing to write page registry through symlink");
@@ -4749,8 +4964,8 @@ function _safeWriteInner(input) {
4749
4964
  return { text: JSON.stringify(result), isError: true };
4750
4965
  }
4751
4966
  const normalized = normalize(filePath);
4752
- const fullPath = join6(cwd, normalized);
4753
- if (existsSync6(fullPath)) {
4967
+ const fullPath = join7(cwd, normalized);
4968
+ if (existsSync7(fullPath)) {
4754
4969
  try {
4755
4970
  const stat = lstatSync7(fullPath);
4756
4971
  if (stat.isSymbolicLink()) {
@@ -4880,8 +5095,8 @@ function registerSafeWriteTools(server2) {
4880
5095
 
4881
5096
  // src/tools/auth.ts
4882
5097
  import { z as z15 } from "zod";
4883
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, existsSync as existsSync7, mkdirSync as mkdirSync6 } from "fs";
4884
- import { join as join7 } from "path";
5098
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync8, mkdirSync as mkdirSync6 } from "fs";
5099
+ import { join as join8 } from "path";
4885
5100
  function buildHierarchy(roles) {
4886
5101
  const h = {};
4887
5102
  for (let i = 0; i < roles.length - 1; i++) {
@@ -4908,9 +5123,9 @@ ${indent} requiredRole: ${r.requiredRole}`).join("\n");
4908
5123
  }
4909
5124
  function detectNextMajorVersion(cwd) {
4910
5125
  try {
4911
- const pkgPath = join7(cwd, "package.json");
4912
- if (!existsSync7(pkgPath)) return null;
4913
- const pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
5126
+ const pkgPath = join8(cwd, "package.json");
5127
+ if (!existsSync8(pkgPath)) return null;
5128
+ const pkg = JSON.parse(readFileSync8(pkgPath, "utf8"));
4914
5129
  const nextVersion = pkg.dependencies?.next ?? pkg.devDependencies?.next;
4915
5130
  if (!nextVersion) return null;
4916
5131
  const match = nextVersion.match(/(\d+)/);
@@ -5574,7 +5789,7 @@ async function configureAuthHandler(params, cwd) {
5574
5789
  auditRetentionDays,
5575
5790
  normalizedRoutes
5576
5791
  );
5577
- writeFileSync6(join7(cwd, conventionFile), authFileContent, "utf8");
5792
+ writeFileSync6(join8(cwd, conventionFile), authFileContent, "utf8");
5578
5793
  filesWritten.push(conventionFile);
5579
5794
  } catch (err) {
5580
5795
  const msg = err instanceof Error ? err.message : String(err);
@@ -5594,9 +5809,9 @@ async function configureAuthHandler(params, cwd) {
5594
5809
  if (!devOnly) {
5595
5810
  try {
5596
5811
  const envBlock = generateEnvBlock(effectiveMethod, params);
5597
- const envPath = join7(cwd, ".env.example");
5598
- if (existsSync7(envPath)) {
5599
- const existing = readFileSync7(envPath, "utf8");
5812
+ const envPath = join8(cwd, ".env.example");
5813
+ if (existsSync8(envPath)) {
5814
+ const existing = readFileSync8(envPath, "utf8");
5600
5815
  writeFileSync6(envPath, existing.trimEnd() + "\n\n" + envBlock, "utf8");
5601
5816
  } else {
5602
5817
  writeFileSync6(envPath, envBlock, "utf8");
@@ -5617,10 +5832,10 @@ async function configureAuthHandler(params, cwd) {
5617
5832
  }
5618
5833
  if (devOnly) {
5619
5834
  try {
5620
- const mockAuthDir = join7(cwd, "lib");
5835
+ const mockAuthDir = join8(cwd, "lib");
5621
5836
  mkdirSync6(mockAuthDir, { recursive: true });
5622
5837
  const mockAuthContent = generateMockAuthContent(roles, mockUsers);
5623
- writeFileSync6(join7(mockAuthDir, "mock-auth.ts"), mockAuthContent, "utf8");
5838
+ writeFileSync6(join8(mockAuthDir, "mock-auth.ts"), mockAuthContent, "utf8");
5624
5839
  filesWritten.push("lib/mock-auth.ts");
5625
5840
  } catch (err) {
5626
5841
  const msg = err instanceof Error ? err.message : String(err);
@@ -5638,9 +5853,9 @@ async function configureAuthHandler(params, cwd) {
5638
5853
  };
5639
5854
  }
5640
5855
  try {
5641
- const pkgPath = join7(cwd, "package.json");
5642
- if (existsSync7(pkgPath)) {
5643
- const existingPkg = readFileSync7(pkgPath, "utf8");
5856
+ const pkgPath = join8(cwd, "package.json");
5857
+ if (existsSync8(pkgPath)) {
5858
+ const existingPkg = readFileSync8(pkgPath, "utf8");
5644
5859
  const updatedPkg = updatePackageJsonScripts(existingPkg, roles, mockUsers);
5645
5860
  writeFileSync6(pkgPath, updatedPkg, "utf8");
5646
5861
  filesWritten.push("package.json");
@@ -5683,7 +5898,7 @@ async function configureAuthHandler(params, cwd) {
5683
5898
  normalizedRoutes,
5684
5899
  useProxy
5685
5900
  );
5686
- writeFileSync6(join7(cwd, "stackwright.auth.yml"), authYaml, "utf8");
5901
+ writeFileSync6(join8(cwd, "stackwright.auth.yml"), authYaml, "utf8");
5687
5902
  filesWritten.push("stackwright.auth.yml");
5688
5903
  } catch (err) {
5689
5904
  const msg = err instanceof Error ? err.message : String(err);
@@ -5796,8 +6011,8 @@ function registerAuthTools(server2) {
5796
6011
 
5797
6012
  // src/integrity.ts
5798
6013
  import { createHash as createHash4, timingSafeEqual as timingSafeEqual2 } from "crypto";
5799
- import { readFileSync as readFileSync8, readdirSync as readdirSync3, lstatSync as lstatSync8 } from "fs";
5800
- import { join as join8, basename } from "path";
6014
+ import { readFileSync as readFileSync9, readdirSync as readdirSync4, lstatSync as lstatSync8 } from "fs";
6015
+ import { join as join9, basename } from "path";
5801
6016
  var _checksums = /* @__PURE__ */ new Map([
5802
6017
  [
5803
6018
  "stackwright-pro-api-otter.json",
@@ -5905,7 +6120,7 @@ function verifyOtterFile(filePath) {
5905
6120
  }
5906
6121
  let raw;
5907
6122
  try {
5908
- raw = readFileSync8(filePath);
6123
+ raw = readFileSync9(filePath);
5909
6124
  } catch (err) {
5910
6125
  const msg = err instanceof Error ? err.message : String(err);
5911
6126
  return { verified: false, filename, error: `Cannot read file: ${msg}` };
@@ -5955,7 +6170,7 @@ function verifyAllOtters(otterDir) {
5955
6170
  const unknown = [];
5956
6171
  let entries;
5957
6172
  try {
5958
- entries = readdirSync3(otterDir);
6173
+ entries = readdirSync4(otterDir);
5959
6174
  } catch (err) {
5960
6175
  const msg = err instanceof Error ? err.message : String(err);
5961
6176
  return {
@@ -5966,7 +6181,7 @@ function verifyAllOtters(otterDir) {
5966
6181
  }
5967
6182
  const otterFiles = entries.filter((f) => f.endsWith("-otter.json"));
5968
6183
  for (const filename of otterFiles) {
5969
- const filePath = join8(otterDir, filename);
6184
+ const filePath = join9(otterDir, filename);
5970
6185
  try {
5971
6186
  if (lstatSync8(filePath).isSymbolicLink()) {
5972
6187
  failed.push({ filename, error: "Skipped: symlink" });
@@ -5993,8 +6208,8 @@ function verifyAllOtters(otterDir) {
5993
6208
  var DEFAULT_SEARCH_PATHS = ["node_modules/@stackwright-pro/otters/src/", "packages/otters/src/"];
5994
6209
  function resolveOtterDir() {
5995
6210
  const cwd = process.cwd();
5996
- for (const relative of DEFAULT_SEARCH_PATHS) {
5997
- const candidate = join8(cwd, relative);
6211
+ for (const relative2 of DEFAULT_SEARCH_PATHS) {
6212
+ const candidate = join9(cwd, relative2);
5998
6213
  try {
5999
6214
  lstatSync8(candidate);
6000
6215
  return candidate;
@@ -6075,13 +6290,13 @@ function registerIntegrityTools(server2) {
6075
6290
 
6076
6291
  // src/tools/domain.ts
6077
6292
  import { z as z16 } from "zod";
6078
- import { readFileSync as readFileSync9, existsSync as existsSync8 } from "fs";
6079
- import { join as join9 } from "path";
6293
+ import { readFileSync as readFileSync10, existsSync as existsSync9 } from "fs";
6294
+ import { join as join10 } from "path";
6080
6295
  function handleListCollections(input) {
6081
6296
  const cwd = input._cwd ?? process.cwd();
6082
6297
  const sources = [
6083
6298
  {
6084
- path: join9(cwd, ".stackwright", "artifacts", "data-config.json"),
6299
+ path: join10(cwd, ".stackwright", "artifacts", "data-config.json"),
6085
6300
  source: "data-config.json",
6086
6301
  parse: (raw) => {
6087
6302
  const parsed = JSON.parse(raw);
@@ -6092,15 +6307,15 @@ function handleListCollections(input) {
6092
6307
  }
6093
6308
  },
6094
6309
  {
6095
- path: join9(cwd, "stackwright.yml"),
6310
+ path: join10(cwd, "stackwright.yml"),
6096
6311
  source: "stackwright.yml",
6097
6312
  parse: extractCollectionsFromYaml
6098
6313
  }
6099
6314
  ];
6100
6315
  for (const { path: path5, source, parse } of sources) {
6101
- if (!existsSync8(path5)) continue;
6316
+ if (!existsSync9(path5)) continue;
6102
6317
  try {
6103
- const collections = parse(readFileSync9(path5, "utf8"));
6318
+ const collections = parse(readFileSync10(path5, "utf8"));
6104
6319
  return {
6105
6320
  text: JSON.stringify({ collections, source, collectionCount: collections.length }),
6106
6321
  isError: false
@@ -6251,8 +6466,8 @@ function handleValidateWorkflow(input) {
6251
6466
  if (input.workflow && Object.keys(input.workflow).length > 0) {
6252
6467
  raw = input.workflow;
6253
6468
  } else {
6254
- const artifactPath = join9(cwd, ".stackwright", "artifacts", "workflow-config.json");
6255
- if (!existsSync8(artifactPath)) {
6469
+ const artifactPath = join10(cwd, ".stackwright", "artifacts", "workflow-config.json");
6470
+ if (!existsSync9(artifactPath)) {
6256
6471
  return fail([
6257
6472
  {
6258
6473
  code: "NO_WORKFLOW",
@@ -6261,7 +6476,7 @@ function handleValidateWorkflow(input) {
6261
6476
  ]);
6262
6477
  }
6263
6478
  try {
6264
- raw = JSON.parse(readFileSync9(artifactPath, "utf8"));
6479
+ raw = JSON.parse(readFileSync10(artifactPath, "utf8"));
6265
6480
  } catch (err) {
6266
6481
  return fail([{ code: "INVALID_JSON", message: `Failed to parse workflow artifact: ${err}` }]);
6267
6482
  }
@@ -6573,7 +6788,7 @@ import * as fs2 from "fs";
6573
6788
  import * as os from "os";
6574
6789
  import * as path3 from "path";
6575
6790
  import { z as z18 } from "zod";
6576
- import { load as yamlLoad2, dump as yamlDump } from "js-yaml";
6791
+ import { load as yamlLoad3, dump as yamlDump } from "js-yaml";
6577
6792
  import { writePage, resolvePagesDir } from "@stackwright/cli";
6578
6793
  import { isProContentType, isOssTypeWithProExtension } from "@stackwright-pro/types";
6579
6794
  var BCP47_RE = /^[a-z]{2}(-[A-Z]{2})?$/;
@@ -6627,7 +6842,7 @@ function handleProWritePage(input) {
6627
6842
  }
6628
6843
  let parsed;
6629
6844
  try {
6630
- parsed = yamlLoad2(content);
6845
+ parsed = yamlLoad3(content);
6631
6846
  } catch (err) {
6632
6847
  return {
6633
6848
  isError: true,
@@ -6699,16 +6914,16 @@ function registerProPageTools(server2) {
6699
6914
 
6700
6915
  // src/tools/scaffold-cleanup.ts
6701
6916
  import {
6702
- existsSync as existsSync10,
6917
+ existsSync as existsSync11,
6703
6918
  lstatSync as lstatSync9,
6704
6919
  unlinkSync as unlinkSync2,
6705
- readFileSync as readFileSync10,
6920
+ readFileSync as readFileSync11,
6706
6921
  writeFileSync as writeFileSync8,
6707
- readdirSync as readdirSync4,
6922
+ readdirSync as readdirSync5,
6708
6923
  rmdirSync,
6709
6924
  mkdirSync as mkdirSync8
6710
6925
  } from "fs";
6711
- import { join as join11, dirname as dirname3 } from "path";
6926
+ import { join as join12, dirname as dirname3 } from "path";
6712
6927
  var SCAFFOLD_FILES_TO_DELETE = [
6713
6928
  "content/posts/getting-started.yaml",
6714
6929
  "content/posts/hello-world.yaml"
@@ -6733,12 +6948,12 @@ var FALLBACK_COLORS = {
6733
6948
  mutedForeground: "#737373"
6734
6949
  };
6735
6950
  function readThemeColors(cwd) {
6736
- const tokenPath = join11(cwd, ".stackwright", "artifacts", "theme-tokens.json");
6737
- if (!existsSync10(tokenPath)) return { ...FALLBACK_COLORS };
6951
+ const tokenPath = join12(cwd, ".stackwright", "artifacts", "theme-tokens.json");
6952
+ if (!existsSync11(tokenPath)) return { ...FALLBACK_COLORS };
6738
6953
  const stat = lstatSync9(tokenPath);
6739
6954
  if (stat.isSymbolicLink()) return { ...FALLBACK_COLORS };
6740
6955
  try {
6741
- const raw = JSON.parse(readFileSync10(tokenPath, "utf-8"));
6956
+ const raw = JSON.parse(readFileSync11(tokenPath, "utf-8"));
6742
6957
  const css = raw?.cssVariables ?? {};
6743
6958
  const toHsl = (hslValue) => {
6744
6959
  if (!hslValue || typeof hslValue !== "string") return null;
@@ -6798,15 +7013,15 @@ function generateNotFoundPage(colors) {
6798
7013
  `;
6799
7014
  }
6800
7015
  function isSafePath(fullPath) {
6801
- if (!existsSync10(fullPath)) return true;
7016
+ if (!existsSync11(fullPath)) return true;
6802
7017
  const stat = lstatSync9(fullPath);
6803
7018
  return !stat.isSymbolicLink();
6804
7019
  }
6805
7020
  function isDirEmpty(dirPath) {
6806
- if (!existsSync10(dirPath)) return false;
7021
+ if (!existsSync11(dirPath)) return false;
6807
7022
  const stat = lstatSync9(dirPath);
6808
7023
  if (!stat.isDirectory()) return false;
6809
- return readdirSync4(dirPath).length === 0;
7024
+ return readdirSync5(dirPath).length === 0;
6810
7025
  }
6811
7026
  function handleCleanupScaffold(_cwd) {
6812
7027
  const cwd = _cwd ?? process.cwd();
@@ -6819,8 +7034,8 @@ function handleCleanupScaffold(_cwd) {
6819
7034
  cleanupWarnings: []
6820
7035
  };
6821
7036
  for (const relPath of SCAFFOLD_FILES_TO_DELETE) {
6822
- const fullPath = join11(cwd, relPath);
6823
- if (!existsSync10(fullPath)) {
7037
+ const fullPath = join12(cwd, relPath);
7038
+ if (!existsSync11(fullPath)) {
6824
7039
  result.skipped.push(relPath);
6825
7040
  continue;
6826
7041
  }
@@ -6837,13 +7052,13 @@ function handleCleanupScaffold(_cwd) {
6837
7052
  }
6838
7053
  {
6839
7054
  const relPath = "pages/getting-started/content.yml";
6840
- const fullPath = join11(cwd, relPath);
6841
- if (!existsSync10(fullPath)) {
7055
+ const fullPath = join12(cwd, relPath);
7056
+ if (!existsSync11(fullPath)) {
6842
7057
  result.skipped.push(relPath);
6843
7058
  } else if (!isSafePath(fullPath)) {
6844
7059
  result.errors.push(`Refusing to delete symlink: ${relPath}`);
6845
7060
  } else {
6846
- const content = readFileSync10(fullPath, "utf-8");
7061
+ const content = readFileSync11(fullPath, "utf-8");
6847
7062
  const isScaffoldDefault = GETTING_STARTED_SCAFFOLD_FINGERPRINTS.some(
6848
7063
  (marker) => content.includes(marker)
6849
7064
  );
@@ -6863,16 +7078,16 @@ function handleCleanupScaffold(_cwd) {
6863
7078
  }
6864
7079
  {
6865
7080
  const relPath = `content/posts/${POSTS_COLLECTION_FILE}`;
6866
- const fullPath = join11(cwd, relPath);
6867
- const postsDir = join11(cwd, "content/posts");
6868
- if (!existsSync10(fullPath)) {
7081
+ const fullPath = join12(cwd, relPath);
7082
+ const postsDir = join12(cwd, "content/posts");
7083
+ if (!existsSync11(fullPath)) {
6869
7084
  result.skipped.push(relPath);
6870
7085
  } else if (!isSafePath(fullPath)) {
6871
7086
  result.errors.push(`Refusing to delete symlink: ${relPath}`);
6872
- } else if (!existsSync10(postsDir)) {
7087
+ } else if (!existsSync11(postsDir)) {
6873
7088
  result.skipped.push(relPath);
6874
7089
  } else {
6875
- const userPostFiles = readdirSync4(postsDir).filter(
7090
+ const userPostFiles = readdirSync5(postsDir).filter(
6876
7091
  (f) => f !== POSTS_COLLECTION_FILE && POST_FILE_EXTENSIONS.some((ext) => f.endsWith(ext))
6877
7092
  );
6878
7093
  if (userPostFiles.length === 0) {
@@ -6890,8 +7105,8 @@ function handleCleanupScaffold(_cwd) {
6890
7105
  }
6891
7106
  }
6892
7107
  for (const relDir of SCAFFOLD_DIRS_TO_PRUNE) {
6893
- const fullDir = join11(cwd, relDir);
6894
- if (!existsSync10(fullDir)) continue;
7108
+ const fullDir = join12(cwd, relDir);
7109
+ if (!existsSync11(fullDir)) continue;
6895
7110
  if (!isSafePath(fullDir)) {
6896
7111
  result.errors.push(`Refusing to prune symlink directory: ${relDir}`);
6897
7112
  continue;
@@ -6906,8 +7121,8 @@ function handleCleanupScaffold(_cwd) {
6906
7121
  }
6907
7122
  }
6908
7123
  {
6909
- const fullPath = join11(cwd, NOT_FOUND_PATH);
6910
- if (!existsSync10(fullPath)) {
7124
+ const fullPath = join12(cwd, NOT_FOUND_PATH);
7125
+ if (!existsSync11(fullPath)) {
6911
7126
  result.skipped.push(NOT_FOUND_PATH);
6912
7127
  } else if (!isSafePath(fullPath)) {
6913
7128
  result.errors.push(`Refusing to rewrite symlink: ${NOT_FOUND_PATH}`);
@@ -7428,14 +7643,14 @@ ${results.join("\n")}`;
7428
7643
 
7429
7644
  // src/tools/strip-legacy-integrations.ts
7430
7645
  import { z as z21 } from "zod";
7431
- import { existsSync as existsSync11, readFileSync as readFileSync11, writeFileSync as writeFileSync9 } from "fs";
7432
- import { join as join12 } from "path";
7646
+ import { existsSync as existsSync12, readFileSync as readFileSync12, writeFileSync as writeFileSync9 } from "fs";
7647
+ import { join as join13 } from "path";
7433
7648
  function handleStripLegacyIntegrations(input) {
7434
7649
  const { projectRoot } = input;
7435
- const ymlPath = join12(projectRoot, "stackwright.yml");
7436
- const yamlPath = join12(projectRoot, "stackwright.yaml");
7437
- if (!existsSync11(ymlPath)) {
7438
- if (existsSync11(yamlPath)) {
7650
+ const ymlPath = join13(projectRoot, "stackwright.yml");
7651
+ const yamlPath = join13(projectRoot, "stackwright.yaml");
7652
+ if (!existsSync12(ymlPath)) {
7653
+ if (existsSync12(yamlPath)) {
7439
7654
  return {
7440
7655
  changed: false,
7441
7656
  removedEntries: 0,
@@ -7448,7 +7663,7 @@ function handleStripLegacyIntegrations(input) {
7448
7663
  message: "stackwright.yml not found \u2014 no-op."
7449
7664
  };
7450
7665
  }
7451
- const raw = readFileSync11(ymlPath, "utf-8");
7666
+ const raw = readFileSync12(ymlPath, "utf-8");
7452
7667
  const isCRLF = raw.includes("\r\n");
7453
7668
  const content = isCRLF ? raw.replace(/\r\n/g, "\n") : raw;
7454
7669
  const lines = content.split("\n");
@@ -7503,15 +7718,15 @@ function registerStripLegacyIntegrationsTool(server2) {
7503
7718
 
7504
7719
  // src/tools/emitters.ts
7505
7720
  import { z as z22 } from "zod";
7506
- import { readFileSync as readFileSync12, existsSync as existsSync12, readdirSync as readdirSync5 } from "fs";
7507
- import { join as join13 } from "path";
7721
+ import { readFileSync as readFileSync13, existsSync as existsSync13, readdirSync as readdirSync6 } from "fs";
7722
+ import { join as join14 } from "path";
7508
7723
  import yaml from "js-yaml";
7509
7724
  import { emitEnvLocal, emitProvidersFile, emitMockPorts } from "@stackwright-pro/emitters";
7510
7725
  function readIntegrations(cwd) {
7511
- const filePath = join13(cwd, "stackwright.integrations.yml");
7512
- if (!existsSync12(filePath)) return [];
7726
+ const filePath = join14(cwd, "stackwright.integrations.yml");
7727
+ if (!existsSync13(filePath)) return [];
7513
7728
  try {
7514
- const raw = yaml.load(readFileSync12(filePath, "utf-8"));
7729
+ const raw = yaml.load(readFileSync13(filePath, "utf-8"));
7515
7730
  if (!raw?.integrations || !Array.isArray(raw.integrations)) return [];
7516
7731
  return raw.integrations.filter((i) => typeof i.name === "string").map((i) => ({
7517
7732
  name: i.name,
@@ -7522,15 +7737,15 @@ function readIntegrations(cwd) {
7522
7737
  }
7523
7738
  }
7524
7739
  function readServiceTokenRefs(cwd) {
7525
- const servicesDir = join13(cwd, "services");
7526
- if (!existsSync12(servicesDir)) return [];
7740
+ const servicesDir = join14(cwd, "services");
7741
+ if (!existsSync13(servicesDir)) return [];
7527
7742
  try {
7528
- const files = readdirSync5(servicesDir).filter((f) => f.endsWith(".yml") || f.endsWith(".yaml"));
7743
+ const files = readdirSync6(servicesDir).filter((f) => f.endsWith(".yml") || f.endsWith(".yaml"));
7529
7744
  const refs = [];
7530
7745
  for (const file of files) {
7531
7746
  try {
7532
7747
  const raw = yaml.load(
7533
- readFileSync12(join13(servicesDir, file), "utf-8")
7748
+ readFileSync13(join14(servicesDir, file), "utf-8")
7534
7749
  );
7535
7750
  if (raw?.tokenEnv && typeof raw.tokenEnv === "string") {
7536
7751
  refs.push({
@@ -7680,8 +7895,8 @@ This is the third concrete instance of the swp-zf9y deterministic emitter patter
7680
7895
 
7681
7896
  // src/tools/list-specs.ts
7682
7897
  import { z as z23 } from "zod";
7683
- import { readdirSync as readdirSync6, readFileSync as readFileSync13, statSync, existsSync as existsSync13 } from "fs";
7684
- import { join as join14, extname, basename as basename2 } from "path";
7898
+ import { readdirSync as readdirSync7, readFileSync as readFileSync14, statSync as statSync2, existsSync as existsSync14 } from "fs";
7899
+ import { join as join15, extname, basename as basename2 } from "path";
7685
7900
  var STRIP_SUFFIXES = ["-openapi", "-asyncapi", "-api", "-spec"];
7686
7901
  var ALLOWED_EXTS = /* @__PURE__ */ new Set([".yaml", ".yml", ".json"]);
7687
7902
  function deriveIntegrationName(fileName) {
@@ -7703,21 +7918,21 @@ function detectFormat(snippet) {
7703
7918
  }
7704
7919
  function handleListSpecs(input) {
7705
7920
  const { projectRoot } = input;
7706
- const specsDir = join14(projectRoot, "specs");
7921
+ const specsDir = join15(projectRoot, "specs");
7707
7922
  const empty = { projectRoot, specs: [], totalCount: 0 };
7708
- if (!existsSync13(specsDir)) return empty;
7923
+ if (!existsSync14(specsDir)) return empty;
7709
7924
  let entries;
7710
7925
  try {
7711
- entries = readdirSync6(specsDir);
7926
+ entries = readdirSync7(specsDir);
7712
7927
  } catch {
7713
7928
  return empty;
7714
7929
  }
7715
7930
  const specs = [];
7716
7931
  for (const entry of entries) {
7717
- const fullPath = join14(specsDir, entry);
7932
+ const fullPath = join15(specsDir, entry);
7718
7933
  let stat;
7719
7934
  try {
7720
- stat = statSync(fullPath);
7935
+ stat = statSync2(fullPath);
7721
7936
  } catch {
7722
7937
  continue;
7723
7938
  }
@@ -7725,7 +7940,7 @@ function handleListSpecs(input) {
7725
7940
  if (!ALLOWED_EXTS.has(extname(entry).toLowerCase())) continue;
7726
7941
  let snippet = "";
7727
7942
  try {
7728
- snippet = readFileSync13(fullPath, "utf-8").slice(0, 2048);
7943
+ snippet = readFileSync14(fullPath, "utf-8").slice(0, 2048);
7729
7944
  } catch {
7730
7945
  }
7731
7946
  specs.push({
@@ -7757,9 +7972,9 @@ function registerListSpecsTool(server2) {
7757
7972
 
7758
7973
  // src/tools/consolidate-integrations.ts
7759
7974
  import { z as z24 } from "zod";
7760
- import { readdirSync as readdirSync7, readFileSync as readFileSync14, writeFileSync as writeFileSync10, existsSync as existsSync14, mkdirSync as mkdirSync9 } from "fs";
7975
+ import { readdirSync as readdirSync8, readFileSync as readFileSync15, writeFileSync as writeFileSync10, existsSync as existsSync15, mkdirSync as mkdirSync9 } from "fs";
7761
7976
  import { lockSync as lockSync2 } from "proper-lockfile";
7762
- import { join as join15, basename as basename3 } from "path";
7977
+ import { join as join16, basename as basename3 } from "path";
7763
7978
  import yaml2 from "js-yaml";
7764
7979
  var BASE_MOCK_PORT = 4011;
7765
7980
  var STRIP_SUFFIXES2 = ["-openapi", "-asyncapi", "-api", "-spec"];
@@ -7772,13 +7987,13 @@ function deriveNameFromFilename(filename) {
7772
7987
  }
7773
7988
  function handleConsolidateIntegrations(input) {
7774
7989
  const { projectRoot } = input;
7775
- const artifactsDir = join15(projectRoot, ".stackwright", "artifacts");
7776
- if (!existsSync14(artifactsDir)) {
7990
+ const artifactsDir = join16(projectRoot, ".stackwright", "artifacts");
7991
+ if (!existsSync15(artifactsDir)) {
7777
7992
  return { written: false, integrationCount: 0, reason: "no per-spec artifacts found" };
7778
7993
  }
7779
7994
  let entries;
7780
7995
  try {
7781
- entries = readdirSync7(artifactsDir).filter(
7996
+ entries = readdirSync8(artifactsDir).filter(
7782
7997
  (f) => f.startsWith("api-config-") && f.endsWith(".json") && f !== "api-config.json"
7783
7998
  );
7784
7999
  } catch {
@@ -7792,7 +8007,7 @@ function handleConsolidateIntegrations(input) {
7792
8007
  entries.forEach((filename, index) => {
7793
8008
  let artifact = {};
7794
8009
  try {
7795
- const raw = readFileSync14(join15(artifactsDir, filename), "utf-8");
8010
+ const raw = readFileSync15(join16(artifactsDir, filename), "utf-8");
7796
8011
  artifact = JSON.parse(raw);
7797
8012
  } catch {
7798
8013
  }
@@ -7818,19 +8033,19 @@ function handleConsolidateIntegrations(input) {
7818
8033
  });
7819
8034
  const integrations = Array.from(integrationsByName.values());
7820
8035
  const dedupedCount = entries.length - integrations.length;
7821
- const integrationsYmlPath = join15(projectRoot, "stackwright.integrations.yml");
8036
+ const integrationsYmlPath = join16(projectRoot, "stackwright.integrations.yml");
7822
8037
  let existing = {};
7823
- if (existsSync14(integrationsYmlPath)) {
8038
+ if (existsSync15(integrationsYmlPath)) {
7824
8039
  try {
7825
- const raw = readFileSync14(integrationsYmlPath, "utf-8");
8040
+ const raw = readFileSync15(integrationsYmlPath, "utf-8");
7826
8041
  existing = yaml2.load(raw) ?? {};
7827
8042
  } catch {
7828
8043
  existing = {};
7829
8044
  }
7830
8045
  }
7831
8046
  const merged = { ...existing, integrations };
7832
- mkdirSync9(join15(projectRoot), { recursive: true });
7833
- if (!existsSync14(integrationsYmlPath)) {
8047
+ mkdirSync9(join16(projectRoot), { recursive: true });
8048
+ if (!existsSync15(integrationsYmlPath)) {
7834
8049
  writeFileSync10(integrationsYmlPath, "", "utf-8");
7835
8050
  }
7836
8051
  const release = lockSync2(integrationsYmlPath, {
@@ -7923,7 +8138,7 @@ var package_default = {
7923
8138
  "test:coverage": "vitest run --coverage"
7924
8139
  },
7925
8140
  name: "@stackwright-pro/mcp",
7926
- version: "0.2.0-alpha.105",
8141
+ version: "0.2.0-alpha.106",
7927
8142
  description: "MCP tools for Stackwright Pro - Data Explorer, Security, ISR, and Dashboard generation",
7928
8143
  license: "SEE LICENSE IN LICENSE",
7929
8144
  main: "./dist/server.js",