@stackwright-pro/mcp 0.2.0-alpha.107 → 0.2.0-alpha.109

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.js CHANGED
@@ -1334,6 +1334,10 @@ function answersToManifestFormat(answers, questions) {
1334
1334
  // src/validation.ts
1335
1335
  var SAFE_PHASE = /^[a-z][a-z0-9-]{0,30}$/;
1336
1336
  var SAFE_QUESTION_ID = /^[a-z0-9][a-z0-9-]{0,63}$/i;
1337
+ var SAFE_SLUG = /^[a-z0-9][a-z0-9-]{0,79}$/;
1338
+ function isValidSlug(slug) {
1339
+ return SAFE_SLUG.test(slug);
1340
+ }
1337
1341
  function isValidPhase(phase) {
1338
1342
  return SAFE_PHASE.test(phase);
1339
1343
  }
@@ -2023,9 +2027,9 @@ function registerOrchestrationTools(server2) {
2023
2027
 
2024
2028
  // src/tools/pipeline.ts
2025
2029
  var import_zod13 = require("zod");
2026
- var import_fs7 = require("fs");
2030
+ var import_fs8 = require("fs");
2027
2031
  var import_proper_lockfile = require("proper-lockfile");
2028
- var import_path7 = require("path");
2032
+ var import_path8 = require("path");
2029
2033
  var import_crypto3 = require("crypto");
2030
2034
 
2031
2035
  // src/artifact-signing.ts
@@ -2552,6 +2556,234 @@ function formatPulseRetryPrompt(result) {
2552
2556
  return lines.join("\n");
2553
2557
  }
2554
2558
 
2559
+ // src/tools/validate-api-integration.ts
2560
+ var CANONICAL_API_AUTH_TYPES = ["apiKey", "bearer", "oauth2", "oidc", "none"];
2561
+ var CANONICAL_SET = new Set(CANONICAL_API_AUTH_TYPES);
2562
+ var VALID_SET_DISPLAY = CANONICAL_API_AUTH_TYPES.map((t) => `\`${t}\``).join(", ");
2563
+ var RENAME_MAP = {
2564
+ // apiKey variants
2565
+ "api-key": "apiKey",
2566
+ api_key: "apiKey",
2567
+ apikey: "apiKey",
2568
+ // lowercase of 'apiKey' / 'APIKEY'
2569
+ // oauth2 variants
2570
+ "oauth-2": "oauth2",
2571
+ oauth_2: "oauth2",
2572
+ // oidc variants
2573
+ "open-id-connect": "oidc",
2574
+ "openid-connect": "oidc",
2575
+ openidconnect: "oidc",
2576
+ // lowercase of 'openIdConnect'
2577
+ openidconnect_: "oidc",
2578
+ // belt-and-suspenders
2579
+ openIdConnect: "oidc",
2580
+ // exact camelCase as emitted by some tooling
2581
+ // bearer variants
2582
+ "bearer-token": "bearer",
2583
+ bearer_token: "bearer"
2584
+ };
2585
+ function renameSuggestion(badLiteral) {
2586
+ return RENAME_MAP[badLiteral] ?? RENAME_MAP[badLiteral.toLowerCase()];
2587
+ }
2588
+ function validateAuthType(authType, yamlPath, artifactPath) {
2589
+ if (authType === void 0 || authType === null) return null;
2590
+ if (typeof authType !== "string") {
2591
+ return [
2592
+ `${artifactPath}: \`${yamlPath}\` must be a string, got \`${typeof authType}\`.`,
2593
+ `Valid values: ${VALID_SET_DISPLAY}.`,
2594
+ "Fix this value and re-call stackwright_pro_validate_artifact."
2595
+ ].join("\n");
2596
+ }
2597
+ if (CANONICAL_SET.has(authType)) return null;
2598
+ const suggestion = renameSuggestion(authType);
2599
+ const lines = [
2600
+ `${artifactPath}: \`${yamlPath}\` has non-canonical value \`"${authType}"\`.`,
2601
+ `Valid values: ${VALID_SET_DISPLAY}.`
2602
+ ];
2603
+ if (suggestion) {
2604
+ lines.push(`Did you mean: \`${suggestion}\`?`);
2605
+ }
2606
+ lines.push("Fix this value and re-call stackwright_pro_validate_artifact.");
2607
+ return lines.join("\n");
2608
+ }
2609
+ function validateApiIntegration(artifact, integrationName) {
2610
+ const artifactFile = integrationName ? `api-config-${integrationName}.json` : "api-config.json";
2611
+ const artifactPath = `.stackwright/artifacts/${artifactFile}`;
2612
+ if (artifact === null || typeof artifact !== "object") {
2613
+ return { valid: true };
2614
+ }
2615
+ const obj = artifact;
2616
+ const errors = [];
2617
+ if ("auth" in obj && obj["auth"] !== null && typeof obj["auth"] === "object") {
2618
+ const auth = obj["auth"];
2619
+ const err = validateAuthType(auth["type"], "auth.type", artifactPath);
2620
+ if (err) errors.push(err);
2621
+ }
2622
+ if ("securitySchemes" in obj && obj["securitySchemes"] !== null && typeof obj["securitySchemes"] === "object" && !Array.isArray(obj["securitySchemes"])) {
2623
+ const schemes = obj["securitySchemes"];
2624
+ for (const [schemeName, scheme] of Object.entries(schemes)) {
2625
+ if (scheme !== null && typeof scheme === "object") {
2626
+ const s = scheme;
2627
+ const err = validateAuthType(s["type"], `securitySchemes.${schemeName}.type`, artifactPath);
2628
+ if (err) errors.push(err);
2629
+ }
2630
+ }
2631
+ }
2632
+ if (errors.length === 0) return { valid: true };
2633
+ const retryPrompt = [
2634
+ `API integration auth validation failed: ${errors.length} violation(s) in ${artifactPath}.`,
2635
+ "",
2636
+ "You MUST fix ALL violations and re-call stackwright_pro_validate_artifact.",
2637
+ "",
2638
+ ...errors.flatMap((e) => [e, ""]),
2639
+ `Canonical auth.type values: ${VALID_SET_DISPLAY}.`,
2640
+ "Do NOT use hyphenated or underscored variants (api-key, oauth-2, bearer-token, etc.)."
2641
+ ].join("\n");
2642
+ return { valid: false, retryPrompt };
2643
+ }
2644
+
2645
+ // src/tools/validate-auth-config.ts
2646
+ var KNOWN_NESTED_STRATEGIES = ["oidc", "pki"];
2647
+ var NESTED_STRATEGY_FIELD_EXAMPLES = {
2648
+ oidc: ["discoveryUrl", "clientId", "clientSecret"],
2649
+ pki: ["profile", "source", "headerPrefix"]
2650
+ };
2651
+ function nestedBlockError(strategy, nestedObj, artifactPath) {
2652
+ const exampleFields = NESTED_STRATEGY_FIELD_EXAMPLES[strategy];
2653
+ const actualKeys = Object.keys(nestedObj).slice(0, 2);
2654
+ const showField0 = actualKeys[0] ?? exampleFields[0];
2655
+ const showField1 = actualKeys[1] ?? exampleFields[1];
2656
+ return [
2657
+ `${artifactPath}: \`authConfig.${strategy}\` must not be a nested provider block.`,
2658
+ `Hoist \`authConfig.${strategy}.*\` fields to \`authConfig.*\`. The canonical AuthConfig`,
2659
+ `schema does not nest provider-specific fields under a named sub-object.`,
2660
+ ``,
2661
+ `Bad (nested block):`,
2662
+ ` "authConfig": {`,
2663
+ ` "type": "${strategy}",`,
2664
+ ` "${strategy}": {`,
2665
+ ` "${showField0}": "...",`,
2666
+ ` "${showField1}": "...",`,
2667
+ ` ...`,
2668
+ ` }`,
2669
+ ` }`,
2670
+ ``,
2671
+ `Correct (hoisted to root of authConfig):`,
2672
+ ` "authConfig": {`,
2673
+ ` "type": "${strategy}",`,
2674
+ ` "${exampleFields[0]}": "...",`,
2675
+ ` "${exampleFields[1]}": "...",`,
2676
+ ` "${exampleFields[2]}": "...",`,
2677
+ ` ...`,
2678
+ ` }`,
2679
+ ``,
2680
+ `Move every key from \`authConfig.${strategy}\` up to \`authConfig\` and remove the \`"${strategy}"\` sub-object.`
2681
+ ].join("\n");
2682
+ }
2683
+ function discriminatorKeyError(methodVal, artifactPath) {
2684
+ return [
2685
+ `${artifactPath}: \`authConfig.method\` is not a valid key in the canonical AuthConfig schema.`,
2686
+ `Rename \`authConfig.method\` \u2192 \`authConfig.type\`. The canonical AuthConfig schema uses`,
2687
+ `\`type\` as the auth-strategy discriminator, matching the api-config \`auth.type\` convention`,
2688
+ `shipped in swp-fimq.`,
2689
+ ``,
2690
+ `Bad:`,
2691
+ ` "authConfig": { "method": "${methodVal}", ... }`,
2692
+ ``,
2693
+ `Correct:`,
2694
+ ` "authConfig": { "type": "${methodVal}", ... }`
2695
+ ].join("\n");
2696
+ }
2697
+ function ambiguousDiscriminatorError(methodVal, typeVal, artifactPath) {
2698
+ return [
2699
+ `${artifactPath}: \`authConfig\` has both \`method: "${methodVal}"\` and \`type: "${typeVal}"\` \u2014 ambiguous discriminator.`,
2700
+ `Remove \`authConfig.method\` and keep only \`authConfig.type\`. The canonical AuthConfig`,
2701
+ `schema uses \`type\` as the sole auth-strategy discriminator.`
2702
+ ].join("\n");
2703
+ }
2704
+ function validateAuthConfig(artifact, artifactPath) {
2705
+ if (artifact === null || typeof artifact !== "object") {
2706
+ return { valid: true };
2707
+ }
2708
+ const obj = artifact;
2709
+ if (!("authConfig" in obj) || obj["authConfig"] === void 0 || obj["authConfig"] === null) {
2710
+ return { valid: true };
2711
+ }
2712
+ if (typeof obj["authConfig"] !== "object" || Array.isArray(obj["authConfig"])) {
2713
+ return { valid: true };
2714
+ }
2715
+ const authConfig = obj["authConfig"];
2716
+ const errors = [];
2717
+ const hasMethod = "method" in authConfig && authConfig["method"] !== void 0;
2718
+ const hasType = "type" in authConfig && authConfig["type"] !== void 0;
2719
+ if (hasMethod && hasType) {
2720
+ errors.push(
2721
+ ambiguousDiscriminatorError(
2722
+ String(authConfig["method"]),
2723
+ String(authConfig["type"]),
2724
+ artifactPath
2725
+ )
2726
+ );
2727
+ } else if (hasMethod && !hasType) {
2728
+ errors.push(discriminatorKeyError(String(authConfig["method"]), artifactPath));
2729
+ }
2730
+ for (const strategy of KNOWN_NESTED_STRATEGIES) {
2731
+ const nested = authConfig[strategy];
2732
+ if (nested !== null && nested !== void 0 && typeof nested === "object" && !Array.isArray(nested)) {
2733
+ errors.push(nestedBlockError(strategy, nested, artifactPath));
2734
+ }
2735
+ }
2736
+ if (errors.length === 0) return { valid: true };
2737
+ const retryPrompt = [
2738
+ `Auth config validation failed: ${errors.length} violation(s) in ${artifactPath}.`,
2739
+ ``,
2740
+ `You MUST fix ALL violations and re-call stackwright_pro_validate_artifact.`,
2741
+ ``,
2742
+ ...errors.flatMap((e) => [e, ""]),
2743
+ `The canonical authConfig shape uses \`type\` (not \`method\`) as the discriminator,`,
2744
+ `with all provider-specific fields hoisted to the root of \`authConfig\`.`,
2745
+ `AuthConfig is a discriminated union on \`type: 'oidc' | 'pki'\`.`
2746
+ ].join("\n");
2747
+ return { valid: false, retryPrompt };
2748
+ }
2749
+
2750
+ // src/tools/auth-manifest-aggregator.ts
2751
+ var import_fs6 = require("fs");
2752
+ var import_path6 = require("path");
2753
+ function extractPatternBaseSlug(pattern) {
2754
+ const stripped = pattern.replace(/^\//, "");
2755
+ if (!stripped) return null;
2756
+ return stripped.split("/")[0] ?? null;
2757
+ }
2758
+ function extractManifestSlugs(artifactsDir) {
2759
+ const slugs = /* @__PURE__ */ new Set();
2760
+ if (!(0, import_fs6.existsSync)(artifactsDir)) return slugs;
2761
+ let entries;
2762
+ try {
2763
+ entries = (0, import_fs6.readdirSync)(artifactsDir);
2764
+ } catch {
2765
+ return slugs;
2766
+ }
2767
+ for (const entry of entries) {
2768
+ if (!entry.endsWith("-manifest.json")) continue;
2769
+ try {
2770
+ const content = JSON.parse((0, import_fs6.readFileSync)((0, import_path6.join)(artifactsDir, entry), "utf-8"));
2771
+ const pages = content.pages;
2772
+ if (!Array.isArray(pages)) continue;
2773
+ for (const page of pages) {
2774
+ if (typeof page.slug !== "string" || !page.slug) continue;
2775
+ const firstSeg = page.slug.split("/")[0];
2776
+ if (firstSeg) slugs.add(firstSeg);
2777
+ if (!page.slug.includes("[") && !page.slug.includes(":")) {
2778
+ slugs.add(page.slug);
2779
+ }
2780
+ }
2781
+ } catch {
2782
+ }
2783
+ }
2784
+ return slugs;
2785
+ }
2786
+
2555
2787
  // src/tools/pipeline.ts
2556
2788
  var import_types3 = require("@stackwright-pro/types");
2557
2789
 
@@ -2890,8 +3122,8 @@ function registerGetSchemaTool(server2) {
2890
3122
  }
2891
3123
 
2892
3124
  // src/tools/pipeline-graph.ts
2893
- var import_fs6 = require("fs");
2894
- var import_path6 = require("path");
3125
+ var import_fs7 = require("fs");
3126
+ var import_path7 = require("path");
2895
3127
  var MANIFEST_NAME_TO_PHASE = {
2896
3128
  "stackwright-pro-designer-otter": "designer",
2897
3129
  "stackwright-pro-theme-otter": "theme",
@@ -2921,9 +3153,9 @@ var OTTER_SEARCH_PATHS = [
2921
3153
  function resolveOtterDirFromCwd() {
2922
3154
  const cwd = process.cwd();
2923
3155
  for (const relative2 of OTTER_SEARCH_PATHS) {
2924
- const candidate = (0, import_path6.join)(cwd, relative2);
3156
+ const candidate = (0, import_path7.join)(cwd, relative2);
2925
3157
  try {
2926
- (0, import_fs6.lstatSync)(candidate);
3158
+ (0, import_fs7.lstatSync)(candidate);
2927
3159
  return candidate;
2928
3160
  } catch {
2929
3161
  }
@@ -2933,14 +3165,14 @@ function resolveOtterDirFromCwd() {
2933
3165
  );
2934
3166
  }
2935
3167
  function loadOtterManifestsFromDir(otterDir) {
2936
- const entries = (0, import_fs6.readdirSync)(otterDir);
3168
+ const entries = (0, import_fs7.readdirSync)(otterDir);
2937
3169
  const manifests = [];
2938
3170
  for (const filename of entries) {
2939
3171
  if (!filename.endsWith("-otter.json")) continue;
2940
- const filePath = (0, import_path6.join)(otterDir, filename);
2941
- if ((0, import_fs6.lstatSync)(filePath).isSymbolicLink()) continue;
3172
+ const filePath = (0, import_path7.join)(otterDir, filename);
3173
+ if ((0, import_fs7.lstatSync)(filePath).isSymbolicLink()) continue;
2942
3174
  try {
2943
- const raw = (0, import_fs6.readFileSync)(filePath, "utf-8");
3175
+ const raw = (0, import_fs7.readFileSync)(filePath, "utf-8");
2944
3176
  const manifest = JSON.parse(raw);
2945
3177
  manifests.push(manifest);
2946
3178
  } catch (err) {
@@ -3045,19 +3277,19 @@ var DISTRIBUTED_NAMESPACES = ["@stackwright-pro", "@stackwright"];
3045
3277
  function loadDistributedOtterManifests(cwd) {
3046
3278
  const results = [];
3047
3279
  for (const namespace of DISTRIBUTED_NAMESPACES) {
3048
- const nsDir = (0, import_path6.join)(cwd, "node_modules", namespace);
3280
+ const nsDir = (0, import_path7.join)(cwd, "node_modules", namespace);
3049
3281
  let pkgDirNames;
3050
3282
  try {
3051
- pkgDirNames = (0, import_fs6.readdirSync)(nsDir);
3283
+ pkgDirNames = (0, import_fs7.readdirSync)(nsDir);
3052
3284
  } catch {
3053
3285
  continue;
3054
3286
  }
3055
3287
  for (const pkgDirName of pkgDirNames) {
3056
- const pkgDir = (0, import_path6.join)(nsDir, pkgDirName);
3288
+ const pkgDir = (0, import_path7.join)(nsDir, pkgDirName);
3057
3289
  const packageName = `${namespace}/${pkgDirName}`;
3058
3290
  let otterRelPaths;
3059
3291
  try {
3060
- const raw = (0, import_fs6.readFileSync)((0, import_path6.join)(pkgDir, "package.json"), "utf-8");
3292
+ const raw = (0, import_fs7.readFileSync)((0, import_path7.join)(pkgDir, "package.json"), "utf-8");
3061
3293
  const pkgJson = JSON.parse(raw);
3062
3294
  const declared = pkgJson.stackwright?.otters;
3063
3295
  if (!Array.isArray(declared) || declared.length === 0) continue;
@@ -3066,9 +3298,9 @@ function loadDistributedOtterManifests(cwd) {
3066
3298
  continue;
3067
3299
  }
3068
3300
  for (const relPath of otterRelPaths) {
3069
- const otterFilePath = (0, import_path6.join)(pkgDir, relPath);
3301
+ const otterFilePath = (0, import_path7.join)(pkgDir, relPath);
3070
3302
  try {
3071
- const raw = (0, import_fs6.readFileSync)(otterFilePath, "utf-8");
3303
+ const raw = (0, import_fs7.readFileSync)(otterFilePath, "utf-8");
3072
3304
  const manifest = JSON.parse(raw);
3073
3305
  results.push({ manifest, packageName });
3074
3306
  } catch (err) {
@@ -3186,11 +3418,11 @@ function createDefaultState() {
3186
3418
  };
3187
3419
  }
3188
3420
  function statePath(cwd) {
3189
- return (0, import_path7.join)(cwd, ".stackwright", "pipeline-state.json");
3421
+ return (0, import_path8.join)(cwd, ".stackwright", "pipeline-state.json");
3190
3422
  }
3191
3423
  function readState(cwd) {
3192
3424
  const p = statePath(cwd);
3193
- if (!(0, import_fs7.existsSync)(p)) return createDefaultState();
3425
+ if (!(0, import_fs8.existsSync)(p)) return createDefaultState();
3194
3426
  const raw = JSON.parse(safeReadSync(p));
3195
3427
  if (typeof raw !== "object" || raw === null || raw.version !== "1.0") {
3196
3428
  return createDefaultState();
@@ -3198,34 +3430,34 @@ function readState(cwd) {
3198
3430
  return raw;
3199
3431
  }
3200
3432
  function safeWriteSync(filePath, content) {
3201
- if ((0, import_fs7.existsSync)(filePath)) {
3202
- const stat = (0, import_fs7.lstatSync)(filePath);
3433
+ if ((0, import_fs8.existsSync)(filePath)) {
3434
+ const stat = (0, import_fs8.lstatSync)(filePath);
3203
3435
  if (stat.isSymbolicLink()) {
3204
3436
  throw new Error(`Refusing to write to symlink: ${filePath}`);
3205
3437
  }
3206
3438
  }
3207
- (0, import_fs7.writeFileSync)(filePath, content);
3439
+ (0, import_fs8.writeFileSync)(filePath, content);
3208
3440
  }
3209
3441
  function safeReadSync(filePath) {
3210
- if ((0, import_fs7.existsSync)(filePath)) {
3211
- const stat = (0, import_fs7.lstatSync)(filePath);
3442
+ if ((0, import_fs8.existsSync)(filePath)) {
3443
+ const stat = (0, import_fs8.lstatSync)(filePath);
3212
3444
  if (stat.isSymbolicLink()) {
3213
3445
  throw new Error(`Refusing to read symlink: ${filePath}`);
3214
3446
  }
3215
3447
  }
3216
- return (0, import_fs7.readFileSync)(filePath, "utf-8");
3448
+ return (0, import_fs8.readFileSync)(filePath, "utf-8");
3217
3449
  }
3218
3450
  function writeState(cwd, state) {
3219
- const dir = (0, import_path7.join)(cwd, ".stackwright");
3220
- (0, import_fs7.mkdirSync)(dir, { recursive: true });
3451
+ const dir = (0, import_path8.join)(cwd, ".stackwright");
3452
+ (0, import_fs8.mkdirSync)(dir, { recursive: true });
3221
3453
  state.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3222
3454
  safeWriteSync(statePath(cwd), JSON.stringify(state, null, 2) + "\n");
3223
3455
  }
3224
3456
  function updateStateAtomic(cwd, mutator) {
3225
- const dir = (0, import_path7.join)(cwd, ".stackwright");
3226
- (0, import_fs7.mkdirSync)(dir, { recursive: true });
3457
+ const dir = (0, import_path8.join)(cwd, ".stackwright");
3458
+ (0, import_fs8.mkdirSync)(dir, { recursive: true });
3227
3459
  const path5 = statePath(cwd);
3228
- if (!(0, import_fs7.existsSync)(path5)) {
3460
+ if (!(0, import_fs8.existsSync)(path5)) {
3229
3461
  safeWriteSync(path5, JSON.stringify(createDefaultState(), null, 2) + "\n");
3230
3462
  }
3231
3463
  const release = (0, import_proper_lockfile.lockSync)(path5, {
@@ -3259,8 +3491,8 @@ function handleGetPipelineState(_cwd) {
3259
3491
  const cwd = _cwd ?? process.cwd();
3260
3492
  try {
3261
3493
  const state = readState(cwd);
3262
- const keyPath = (0, import_path7.join)(cwd, ".stackwright", "pipeline-keys.json");
3263
- if (!(0, import_fs7.existsSync)(keyPath)) {
3494
+ const keyPath = (0, import_path8.join)(cwd, ".stackwright", "pipeline-keys.json");
3495
+ if (!(0, import_fs8.existsSync)(keyPath)) {
3264
3496
  try {
3265
3497
  const { fingerprint } = initPipelineKeys(cwd);
3266
3498
  state["signingKeyFingerprint"] = fingerprint;
@@ -3391,8 +3623,8 @@ function handleCheckExecutionReady(_cwd, phase) {
3391
3623
  isError: true
3392
3624
  };
3393
3625
  }
3394
- const answerFile = (0, import_path7.join)(cwd, ".stackwright", "answers", `${phase}.json`);
3395
- if (!(0, import_fs7.existsSync)(answerFile)) {
3626
+ const answerFile = (0, import_path8.join)(cwd, ".stackwright", "answers", `${phase}.json`);
3627
+ if (!(0, import_fs8.existsSync)(answerFile)) {
3396
3628
  return {
3397
3629
  text: JSON.stringify({ ready: false, phase, reason: "Answer file not found" }),
3398
3630
  isError: false
@@ -3416,12 +3648,12 @@ function handleCheckExecutionReady(_cwd, phase) {
3416
3648
  }
3417
3649
  }
3418
3650
  try {
3419
- const answersDir = (0, import_path7.join)(cwd, ".stackwright", "answers");
3651
+ const answersDir = (0, import_path8.join)(cwd, ".stackwright", "answers");
3420
3652
  const answeredPhases = [];
3421
3653
  const missingPhases = [];
3422
3654
  for (const phase2 of PHASE_ORDER) {
3423
- const answerFile = (0, import_path7.join)(answersDir, `${phase2}.json`);
3424
- if ((0, import_fs7.existsSync)(answerFile)) {
3655
+ const answerFile = (0, import_path8.join)(answersDir, `${phase2}.json`);
3656
+ if ((0, import_fs8.existsSync)(answerFile)) {
3425
3657
  try {
3426
3658
  const raw = safeReadSync(answerFile);
3427
3659
  const parsed = JSON.parse(raw);
@@ -3490,7 +3722,7 @@ function handleGetReadyPhases(_cwd) {
3490
3722
  function handleListArtifacts(_cwd) {
3491
3723
  const cwd = _cwd ?? process.cwd();
3492
3724
  try {
3493
- const artifactsDir = (0, import_path7.join)(cwd, ".stackwright", "artifacts");
3725
+ const artifactsDir = (0, import_path8.join)(cwd, ".stackwright", "artifacts");
3494
3726
  let manifest = null;
3495
3727
  try {
3496
3728
  manifest = loadSignatureManifest(cwd);
@@ -3500,8 +3732,8 @@ function handleListArtifacts(_cwd) {
3500
3732
  let completedCount = 0;
3501
3733
  for (const phase of PHASE_ORDER) {
3502
3734
  const expectedFile = PHASE_ARTIFACT[phase];
3503
- const fullPath = (0, import_path7.join)(artifactsDir, expectedFile);
3504
- const exists = (0, import_fs7.existsSync)(fullPath);
3735
+ const fullPath = (0, import_path8.join)(artifactsDir, expectedFile);
3736
+ const exists = (0, import_fs8.existsSync)(fullPath);
3505
3737
  if (exists) completedCount++;
3506
3738
  let signed = false;
3507
3739
  let signatureValid = null;
@@ -3554,9 +3786,9 @@ function handleWritePhaseQuestions(input) {
3554
3786
  }
3555
3787
  } catch {
3556
3788
  }
3557
- const questionsDir = (0, import_path7.join)(cwd, ".stackwright", "questions");
3558
- (0, import_fs7.mkdirSync)(questionsDir, { recursive: true });
3559
- const filePath = (0, import_path7.join)(questionsDir, `${phase}.json`);
3789
+ const questionsDir = (0, import_path8.join)(cwd, ".stackwright", "questions");
3790
+ (0, import_fs8.mkdirSync)(questionsDir, { recursive: true });
3791
+ const filePath = (0, import_path8.join)(questionsDir, `${phase}.json`);
3560
3792
  const payload = {
3561
3793
  version: "1.0",
3562
3794
  phase,
@@ -3596,14 +3828,14 @@ function handleBuildSpecialistPrompt(input) {
3596
3828
  };
3597
3829
  }
3598
3830
  try {
3599
- const answersPath = (0, import_path7.join)(cwd, ".stackwright", "answers", `${phase}.json`);
3831
+ const answersPath = (0, import_path8.join)(cwd, ".stackwright", "answers", `${phase}.json`);
3600
3832
  let answers = {};
3601
- if ((0, import_fs7.existsSync)(answersPath)) {
3833
+ if ((0, import_fs8.existsSync)(answersPath)) {
3602
3834
  answers = JSON.parse(safeReadSync(answersPath));
3603
3835
  }
3604
3836
  let buildContextText = "";
3605
- const buildContextPath = (0, import_path7.join)(cwd, ".stackwright", "build-context.json");
3606
- if ((0, import_fs7.existsSync)(buildContextPath)) {
3837
+ const buildContextPath = (0, import_path8.join)(cwd, ".stackwright", "build-context.json");
3838
+ if ((0, import_fs8.existsSync)(buildContextPath)) {
3607
3839
  try {
3608
3840
  const bcRaw = JSON.parse(safeReadSync(buildContextPath));
3609
3841
  if (typeof bcRaw.buildContext === "string" && bcRaw.buildContext.trim().length > 0) {
@@ -3618,8 +3850,8 @@ function handleBuildSpecialistPrompt(input) {
3618
3850
  const missingDependencies = [];
3619
3851
  for (const dep of deps) {
3620
3852
  const artifactFile = PHASE_ARTIFACT[dep];
3621
- const artifactPath = (0, import_path7.join)(cwd, ".stackwright", "artifacts", artifactFile);
3622
- if ((0, import_fs7.existsSync)(artifactPath)) {
3853
+ const artifactPath = (0, import_path8.join)(cwd, ".stackwright", "artifacts", artifactFile);
3854
+ if ((0, import_fs8.existsSync)(artifactPath)) {
3623
3855
  const rawContent = safeReadSync(artifactPath);
3624
3856
  const rawBytes = Buffer.from(rawContent, "utf-8");
3625
3857
  const content = JSON.parse(rawContent);
@@ -3684,8 +3916,8 @@ ${JSON.stringify(content, null, 2)}`
3684
3916
  parts.push("BUILD_CONTEXT:", buildContextText, "");
3685
3917
  }
3686
3918
  if (phase === "auth") {
3687
- const initContextPath = (0, import_path7.join)(cwd, ".stackwright", "init-context.json");
3688
- if ((0, import_fs7.existsSync)(initContextPath)) {
3919
+ const initContextPath = (0, import_path8.join)(cwd, ".stackwright", "init-context.json");
3920
+ if ((0, import_fs8.existsSync)(initContextPath)) {
3689
3921
  try {
3690
3922
  const initContext = JSON.parse(safeReadSync(initContextPath));
3691
3923
  if (initContext.devOnly === true || initContext.nonInteractive === true) {
@@ -4136,7 +4368,87 @@ function _validateArtifactInner(input) {
4136
4368
  };
4137
4369
  return { text: JSON.stringify(result), isError: false };
4138
4370
  }
4371
+ if (phase === "auth") {
4372
+ const authArtifactPath = `.stackwright/artifacts/${PHASE_ARTIFACT["auth"]}`;
4373
+ const authShapeResult = validateAuthConfig(artifact, authArtifactPath);
4374
+ if (!authShapeResult.valid) {
4375
+ const result = {
4376
+ valid: false,
4377
+ phase,
4378
+ violation: "schema-mismatch",
4379
+ retryPrompt: authShapeResult.retryPrompt
4380
+ };
4381
+ return { text: JSON.stringify(result), isError: false };
4382
+ }
4383
+ }
4384
+ if (phase === "auth") {
4385
+ const rawUncovered = artifact["uncoveredSlugs"];
4386
+ if (Array.isArray(rawUncovered) && rawUncovered.length > 0) {
4387
+ const otterFromSource = (source) => {
4388
+ if (source === "geo-manifest.json") return "geo-otter";
4389
+ if (source === "dashboard-manifest.json") return "dashboard-otter";
4390
+ if (source === "pages-manifest.json") return "page-otter";
4391
+ return source || "(unknown)";
4392
+ };
4393
+ const slugLines = rawUncovered.map((entry) => {
4394
+ const e = entry ?? {};
4395
+ const source = typeof e["source"] === "string" ? e["source"] : "(unknown)";
4396
+ const slug = typeof e["slug"] === "string" ? e["slug"] : "(unknown)";
4397
+ return ` ${source} \u2192 ${slug} (emitted by ${otterFromSource(source)})`;
4398
+ });
4399
+ const retryPrompt = [
4400
+ "Auth-config cannot be finalized: the following page slugs have no auth declaration in their emitting otter's manifest:",
4401
+ "",
4402
+ ...slugLines,
4403
+ "",
4404
+ "This is a HARD build-gate failure \u2014 auth-otter cannot self-correct because the root cause is upstream.",
4405
+ "",
4406
+ "Fix: update the offending emitter otter to declare `authRequired: true` + `requiredRoles: [...]`",
4407
+ "(and optionally `tier`, `authRationale`) in the manifest entry for each listed slug.",
4408
+ "",
4409
+ "For map pages: geo-otter (see swp-1114).",
4410
+ "For dashboard pages: dashboard-otter (see swp-52lk).",
4411
+ "For page pages: page-otter (already does this \u2014 check for regressions).",
4412
+ "For workflow pages: form-wizard-otter emits routes but auth is derived from the pages-manifest",
4413
+ "workflow-wrapper entry \u2014 check page-otter didn't skip a workflow-wrapper.",
4414
+ "",
4415
+ "After updating the offending manifest, re-run the pipeline from the affected phase."
4416
+ ].join("\n");
4417
+ const result = {
4418
+ valid: false,
4419
+ phase,
4420
+ violation: "uncovered-slugs",
4421
+ retryPrompt
4422
+ };
4423
+ return { text: JSON.stringify(result), isError: false };
4424
+ }
4425
+ }
4139
4426
  const PHASE_ZOD_VALIDATORS = {
4427
+ // 5.0 Theme defaultColorMode hygiene (swp-0ryn / swp-mw97)
4428
+ //
4429
+ // DHL raft runs produced `defaultColorMode: "auto"` (not a valid value) and
4430
+ // `defaultColorMode: "dark"` when the designer said "respect OS preference".
4431
+ // This validator catches the `auto` literal at write-time so the otter
4432
+ // self-corrects before the artifact lands on disk.
4433
+ //
4434
+ // Valid values: 'light' | 'dark' | 'system'
4435
+ // Omitting the key entirely is also valid (app shell honors prefers-color-scheme).
4436
+ theme: (artifact2) => {
4437
+ const dcm = artifact2["defaultColorMode"];
4438
+ if (dcm === void 0 || dcm === null) {
4439
+ return { success: true };
4440
+ }
4441
+ const VALID_COLOR_MODES = ["light", "dark", "system"];
4442
+ if (!VALID_COLOR_MODES.includes(dcm)) {
4443
+ return {
4444
+ success: false,
4445
+ error: {
4446
+ message: `theme-tokens.json: \`defaultColorMode: "${String(dcm)}"\` is not a valid value. Valid values are \`light\`, \`dark\`, or \`system\`. Use \`system\` for OS-following behavior (respects \`prefers-color-scheme\`). Alternatively, OMIT the \`defaultColorMode\` key entirely when the designer has specified in design-language.json operationalNotes that OS preference should be respected (trigger phrases: "respect OS preference", "no forced default").`
4447
+ }
4448
+ };
4449
+ }
4450
+ return { success: true };
4451
+ },
4140
4452
  workflow: (artifact2) => {
4141
4453
  const workflowData = artifact2["workflow"];
4142
4454
  if (!workflowData) {
@@ -4186,33 +4498,60 @@ function _validateArtifactInner(input) {
4186
4498
  return { success: true };
4187
4499
  },
4188
4500
  // swp-4071 — opt-in cross-reference validator for the services phase.
4189
- // If workflow-config exists and declares serviceHooks, every hook ref must resolve
4190
- // to a flow or state-machine in this services-config artifact.
4501
+ // Collects serviceHooks from ALL workflow-config-*.json artifacts (swp-x8sm fan-out)
4502
+ // plus the legacy single-file workflow-config.json (backward compat).
4503
+ // If any workflow declared serviceHooks, every ref must resolve to a flow or
4504
+ // state-machine in this services-config artifact.
4191
4505
  // Skips gracefully when wizard-otter didn't run (Pro-only install) or declared no hooks.
4192
4506
  services: (artifact2) => {
4193
- const workflowArtifactPath = (0, import_path7.join)(cwd, ".stackwright", "artifacts", "workflow-config.json");
4194
- if (!(0, import_fs7.existsSync)(workflowArtifactPath)) {
4195
- return { success: true };
4507
+ const artifactsDir = (0, import_path8.join)(cwd, ".stackwright", "artifacts");
4508
+ const workflowArtifactFiles = [];
4509
+ if ((0, import_fs8.existsSync)(artifactsDir)) {
4510
+ try {
4511
+ const entries = (0, import_fs8.readdirSync)(artifactsDir);
4512
+ for (const entry of entries) {
4513
+ if (entry.startsWith("workflow-config-") && entry.endsWith(".json")) {
4514
+ workflowArtifactFiles.push((0, import_path8.join)(artifactsDir, entry));
4515
+ }
4516
+ }
4517
+ } catch {
4518
+ }
4196
4519
  }
4197
- let workflowArtifact;
4198
- try {
4199
- workflowArtifact = JSON.parse((0, import_fs7.readFileSync)(workflowArtifactPath, "utf-8"));
4200
- } catch {
4520
+ const legacyPath = (0, import_path8.join)(artifactsDir, "workflow-config.json");
4521
+ if ((0, import_fs8.existsSync)(legacyPath)) {
4522
+ workflowArtifactFiles.push(legacyPath);
4523
+ }
4524
+ if (workflowArtifactFiles.length === 0) {
4201
4525
  return { success: true };
4202
4526
  }
4203
- const declaredHooks = workflowArtifact.serviceHooks ?? [];
4204
- if (declaredHooks.length === 0) {
4527
+ const declaredHooks = [];
4528
+ for (const filePath of workflowArtifactFiles) {
4529
+ try {
4530
+ const parsed = JSON.parse((0, import_fs8.readFileSync)(filePath, "utf-8"));
4531
+ if (Array.isArray(parsed.serviceHooks)) {
4532
+ declaredHooks.push(...parsed.serviceHooks);
4533
+ }
4534
+ } catch {
4535
+ }
4536
+ }
4537
+ const seenRefs = /* @__PURE__ */ new Set();
4538
+ const uniqueHooks = declaredHooks.filter((h) => {
4539
+ if (seenRefs.has(h.ref)) return false;
4540
+ seenRefs.add(h.ref);
4541
+ return true;
4542
+ });
4543
+ if (uniqueHooks.length === 0) {
4205
4544
  return { success: true };
4206
4545
  }
4207
4546
  const flows = artifact2["flows"] ?? [];
4208
4547
  const workflows = artifact2["workflows"] ?? [];
4209
4548
  const flowNames = /* @__PURE__ */ new Set([...flows.map((f) => f.name), ...workflows.map((w) => w.name)]);
4210
- const unresolved = declaredHooks.filter((h) => !flowNames.has(h.ref));
4549
+ const unresolved = uniqueHooks.filter((h) => !flowNames.has(h.ref));
4211
4550
  if (unresolved.length > 0) {
4212
4551
  return {
4213
4552
  success: false,
4214
4553
  error: {
4215
- message: `Workflow declared ${declaredHooks.length} service hook(s) but ${unresolved.length} unresolved by services-config: ` + unresolved.map((h) => `${h.ref} (${h.kind ?? "unknown"}: ${h.purpose ?? "no purpose"})`).join("; ") + ". Either add matching flows to services-config OR remove the hooks from the workflow YAML."
4554
+ message: `Workflow(s) declared ${uniqueHooks.length} service hook(s) but ${unresolved.length} unresolved by services-config: ` + unresolved.map((h) => `${h.ref} (${h.kind ?? "unknown"}: ${h.purpose ?? "no purpose"})`).join("; ") + ". Either add matching flows to services-config OR remove the hooks from the workflow YAML."
4216
4555
  }
4217
4556
  };
4218
4557
  }
@@ -4278,11 +4617,55 @@ function _validateArtifactInner(input) {
4278
4617
  return { text: JSON.stringify(result), isError: false };
4279
4618
  }
4280
4619
  }
4620
+ if (phase === "api") {
4621
+ const apiAuthResult = validateApiIntegration(artifact, input.integrationName);
4622
+ if (!apiAuthResult.valid) {
4623
+ const result = {
4624
+ valid: false,
4625
+ phase,
4626
+ violation: "schema-mismatch",
4627
+ retryPrompt: apiAuthResult.retryPrompt
4628
+ };
4629
+ return { text: JSON.stringify(result), isError: false };
4630
+ }
4631
+ }
4632
+ if (phase === "workflow" && input.workflowName !== void 0) {
4633
+ if (!isValidSlug(input.workflowName)) {
4634
+ const result = {
4635
+ valid: false,
4636
+ phase,
4637
+ violation: "schema-mismatch",
4638
+ retryPrompt: `workflowName "${input.workflowName}" is not a valid slug. Use lowercase kebab-case (a-z, 0-9, hyphens only, no spaces or path separators). Example: "patient-evacuation", "ambulance-crew-dispatch".`
4639
+ };
4640
+ return { text: JSON.stringify(result), isError: false };
4641
+ }
4642
+ }
4643
+ let authOrphanWarning;
4644
+ if (phase === "auth") {
4645
+ const authCfg = artifact["authConfig"];
4646
+ const routes = authCfg?.["protectedRoutes"];
4647
+ if (Array.isArray(routes) && routes.length > 0) {
4648
+ const artifactsDir = (0, import_path8.join)(cwd, ".stackwright", "artifacts");
4649
+ const knownSlugs = extractManifestSlugs(artifactsDir);
4650
+ const orphanPatterns = [];
4651
+ for (const route of routes) {
4652
+ const pattern = typeof route === "string" ? route : route.pattern ?? "";
4653
+ if (!pattern) continue;
4654
+ const baseSlug = extractPatternBaseSlug(pattern);
4655
+ if (baseSlug && !knownSlugs.has(baseSlug)) {
4656
+ orphanPatterns.push(pattern);
4657
+ }
4658
+ }
4659
+ if (orphanPatterns.length > 0) {
4660
+ authOrphanWarning = `The following protectedRoute patterns don't match any known page slug: ${orphanPatterns.map((p) => `"${p}"`).join(", ")}. If these are intentional cross-cutting rules (e.g., /admin/:path*, /audit/:path*), keep them; otherwise remove.`;
4661
+ }
4662
+ }
4663
+ }
4281
4664
  try {
4282
- const artifactsDir = (0, import_path7.join)(cwd, ".stackwright", "artifacts");
4283
- (0, import_fs7.mkdirSync)(artifactsDir, { recursive: true });
4284
- const artifactFile = phase === "api" && input.integrationName ? `api-config-${input.integrationName}.json` : PHASE_ARTIFACT[phase];
4285
- const artifactPath = (0, import_path7.join)(artifactsDir, artifactFile);
4665
+ const artifactsDir = (0, import_path8.join)(cwd, ".stackwright", "artifacts");
4666
+ (0, import_fs8.mkdirSync)(artifactsDir, { recursive: true });
4667
+ const artifactFile = phase === "api" && input.integrationName ? `api-config-${input.integrationName}.json` : phase === "workflow" && input.workflowName ? `workflow-config-${input.workflowName}.json` : PHASE_ARTIFACT[phase];
4668
+ const artifactPath = (0, import_path8.join)(artifactsDir, artifactFile);
4286
4669
  const serialized = JSON.stringify(artifact, null, 2) + "\n";
4287
4670
  const artifactBytes = Buffer.from(serialized, "utf-8");
4288
4671
  safeWriteSync(artifactPath, serialized);
@@ -4305,7 +4688,8 @@ function _validateArtifactInner(input) {
4305
4688
  valid: true,
4306
4689
  phase,
4307
4690
  artifactPath,
4308
- summary: `Wrote ${artifactFile} (keys: ${topKeys}${Object.keys(artifact).length > 5 ? ", ..." : ""})${signed ? " [signed]" : ""}`
4691
+ summary: `Wrote ${artifactFile} (keys: ${topKeys}${Object.keys(artifact).length > 5 ? ", ..." : ""})${signed ? " [signed]" : ""}`,
4692
+ ...authOrphanWarning ? { retryPrompt: authOrphanWarning } : {}
4309
4693
  };
4310
4694
  return { text: JSON.stringify(result), isError: false };
4311
4695
  } catch (err) {
@@ -4501,10 +4885,10 @@ function registerPipelineTools(server2) {
4501
4885
  isError: true
4502
4886
  };
4503
4887
  }
4504
- const questionsDir = (0, import_path7.join)(process.cwd(), ".stackwright", "questions");
4505
- (0, import_fs7.mkdirSync)(questionsDir, { recursive: true });
4506
- const outPath = (0, import_path7.join)(questionsDir, `${phase}.json`);
4507
- (0, import_fs7.writeFileSync)(
4888
+ const questionsDir = (0, import_path8.join)(process.cwd(), ".stackwright", "questions");
4889
+ (0, import_fs8.mkdirSync)(questionsDir, { recursive: true });
4890
+ const outPath = (0, import_path8.join)(questionsDir, `${phase}.json`);
4891
+ (0, import_fs8.writeFileSync)(
4508
4892
  outPath,
4509
4893
  JSON.stringify({ phase, questions, writtenAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)
4510
4894
  );
@@ -4543,15 +4927,19 @@ function registerPipelineTools(server2) {
4543
4927
  ),
4544
4928
  integrationName: import_zod13.z.string().optional().describe(
4545
4929
  'When phase="api": write to api-config-<integrationName>.json instead of api-config.json. Enables concurrent per-spec fan-out without collision (swp-530y).'
4930
+ ),
4931
+ workflowName: import_zod13.z.string().optional().describe(
4932
+ `When phase="workflow": write to workflow-config-<workflowName>.json instead of workflow-config.json. Enables concurrent per-workflow fan-out without collision (swp-x8sm). Must be a kebab-case slug matching the workflow's id field.`
4546
4933
  )
4547
4934
  },
4548
- async ({ phase, responseText, artifact, integrationName }) => {
4935
+ async ({ phase, responseText, artifact, integrationName, workflowName }) => {
4549
4936
  if (artifact) {
4550
4937
  return res(
4551
4938
  handleValidateArtifact({
4552
4939
  phase,
4553
4940
  artifact,
4554
- ...integrationName ? { integrationName } : {}
4941
+ ...integrationName ? { integrationName } : {},
4942
+ ...workflowName ? { workflowName } : {}
4555
4943
  })
4556
4944
  );
4557
4945
  }
@@ -4559,7 +4947,8 @@ function registerPipelineTools(server2) {
4559
4947
  handleValidateArtifact({
4560
4948
  phase,
4561
4949
  responseText: responseText ?? "",
4562
- ...integrationName ? { integrationName } : {}
4950
+ ...integrationName ? { integrationName } : {},
4951
+ ...workflowName ? { workflowName } : {}
4563
4952
  })
4564
4953
  );
4565
4954
  }
@@ -4591,8 +4980,8 @@ function registerPipelineTools(server2) {
4591
4980
 
4592
4981
  // src/tools/safe-write.ts
4593
4982
  var import_zod14 = require("zod");
4594
- var import_fs8 = require("fs");
4595
- var import_path8 = require("path");
4983
+ var import_fs9 = require("fs");
4984
+ var import_path9 = require("path");
4596
4985
  var import_telemetry2 = require("@stackwright-pro/telemetry");
4597
4986
  var OTTER_WRITE_ALLOWLISTS = {
4598
4987
  "stackwright-pro-designer-otter": [
@@ -4604,6 +4993,11 @@ var OTTER_WRITE_ALLOWLISTS = {
4604
4993
  prefix: "stackwright.theme.",
4605
4994
  suffix: ".yml",
4606
4995
  description: "Theme config YAML (merged at build time)"
4996
+ },
4997
+ {
4998
+ prefix: "stackwright.yml",
4999
+ suffix: "",
5000
+ description: "Stackwright config (themeName + customTheme writeback)"
4607
5001
  }
4608
5002
  ],
4609
5003
  "stackwright-pro-auth-otter": [
@@ -4658,6 +5052,17 @@ var OTTER_WRITE_ALLOWLISTS = {
4658
5052
  prefix: ".stackwright/artifacts/",
4659
5053
  suffix: ".json",
4660
5054
  description: "Scaffold manifest artifact"
5055
+ },
5056
+ // swp-89w5: scaffold-otter initialises mock backend wiring via stackwright_pro_emit_env_local
5057
+ {
5058
+ prefix: ".env.local",
5059
+ suffix: "",
5060
+ description: "Dotenv runtime config (mock backend wiring: NEXT_PUBLIC_MOCK_BACKEND + per-integration mock keys)"
5061
+ },
5062
+ {
5063
+ prefix: ".env.local.example",
5064
+ suffix: "",
5065
+ description: "Dotenv template (schema for real production values)"
4661
5066
  }
4662
5067
  ],
4663
5068
  "stackwright-pro-api-otter": [
@@ -4758,7 +5163,7 @@ function getMaxBytesForPath(filePath) {
4758
5163
  }
4759
5164
  var PAGE_REGISTRY_FILE = ".stackwright/page-registry.json";
4760
5165
  function extractPageSlug(filePath) {
4761
- const match = (0, import_path8.normalize)(filePath).match(/^pages\/(.+?)\/content\.ya?ml$/);
5166
+ const match = (0, import_path9.normalize)(filePath).match(/^pages\/(.+?)\/content\.ya?ml$/);
4762
5167
  return match?.[1] ?? null;
4763
5168
  }
4764
5169
  function extractContentTypes(yamlContent) {
@@ -4774,27 +5179,27 @@ function extractContentTypes(yamlContent) {
4774
5179
  return types.length > 0 ? types : ["unknown"];
4775
5180
  }
4776
5181
  function readPageRegistry(cwd) {
4777
- const regPath = (0, import_path8.join)(cwd, PAGE_REGISTRY_FILE);
4778
- if (!(0, import_fs8.existsSync)(regPath)) return {};
5182
+ const regPath = (0, import_path9.join)(cwd, PAGE_REGISTRY_FILE);
5183
+ if (!(0, import_fs9.existsSync)(regPath)) return {};
4779
5184
  try {
4780
- const stat = (0, import_fs8.lstatSync)(regPath);
5185
+ const stat = (0, import_fs9.lstatSync)(regPath);
4781
5186
  if (stat.isSymbolicLink()) return {};
4782
- return JSON.parse((0, import_fs8.readFileSync)(regPath, "utf-8"));
5187
+ return JSON.parse((0, import_fs9.readFileSync)(regPath, "utf-8"));
4783
5188
  } catch {
4784
5189
  return {};
4785
5190
  }
4786
5191
  }
4787
5192
  function writePageRegistry(cwd, registry) {
4788
- const dir = (0, import_path8.join)(cwd, ".stackwright");
4789
- (0, import_fs8.mkdirSync)(dir, { recursive: true });
4790
- const regPath = (0, import_path8.join)(cwd, PAGE_REGISTRY_FILE);
4791
- if ((0, import_fs8.existsSync)(regPath)) {
4792
- const stat = (0, import_fs8.lstatSync)(regPath);
5193
+ const dir = (0, import_path9.join)(cwd, ".stackwright");
5194
+ (0, import_fs9.mkdirSync)(dir, { recursive: true });
5195
+ const regPath = (0, import_path9.join)(cwd, PAGE_REGISTRY_FILE);
5196
+ if ((0, import_fs9.existsSync)(regPath)) {
5197
+ const stat = (0, import_fs9.lstatSync)(regPath);
4793
5198
  if (stat.isSymbolicLink()) {
4794
5199
  throw new Error("Refusing to write page registry through symlink");
4795
5200
  }
4796
5201
  }
4797
- (0, import_fs8.writeFileSync)(regPath, JSON.stringify(registry, null, 2) + "\n", { encoding: "utf-8" });
5202
+ (0, import_fs9.writeFileSync)(regPath, JSON.stringify(registry, null, 2) + "\n", { encoding: "utf-8" });
4798
5203
  }
4799
5204
  function checkPageOwnership(cwd, slug, callerOtter) {
4800
5205
  const registry = readPageRegistry(cwd);
@@ -4813,11 +5218,11 @@ function stripNextjsBracketSegments(path5) {
4813
5218
  return path5.replace(NEXTJS_BRACKET_SEGMENT_RE, "");
4814
5219
  }
4815
5220
  function checkPathAllowed(callerOtter, filePath) {
4816
- const normalized = (0, import_path8.normalize)(filePath);
5221
+ const normalized = (0, import_path9.normalize)(filePath);
4817
5222
  if (stripNextjsBracketSegments(normalized).includes("..")) {
4818
5223
  return { allowed: false, error: 'Path traversal detected: ".." segments are not allowed' };
4819
5224
  }
4820
- if ((0, import_path8.isAbsolute)(normalized)) {
5225
+ if ((0, import_path9.isAbsolute)(normalized)) {
4821
5226
  return {
4822
5227
  allowed: false,
4823
5228
  error: "Absolute paths are not allowed \u2014 use paths relative to project root"
@@ -4959,11 +5364,11 @@ function _safeWriteInner(input) {
4959
5364
  };
4960
5365
  return { text: JSON.stringify(result), isError: true };
4961
5366
  }
4962
- const normalized = (0, import_path8.normalize)(filePath);
4963
- const fullPath = (0, import_path8.join)(cwd, normalized);
4964
- if ((0, import_fs8.existsSync)(fullPath)) {
5367
+ const normalized = (0, import_path9.normalize)(filePath);
5368
+ const fullPath = (0, import_path9.join)(cwd, normalized);
5369
+ if ((0, import_fs9.existsSync)(fullPath)) {
4965
5370
  try {
4966
- const stat = (0, import_fs8.lstatSync)(fullPath);
5371
+ const stat = (0, import_fs9.lstatSync)(fullPath);
4967
5372
  if (stat.isSymbolicLink()) {
4968
5373
  const result = {
4969
5374
  success: false,
@@ -5004,9 +5409,9 @@ function _safeWriteInner(input) {
5004
5409
  }
5005
5410
  try {
5006
5411
  if (createDirectories) {
5007
- (0, import_fs8.mkdirSync)((0, import_path8.dirname)(fullPath), { recursive: true });
5412
+ (0, import_fs9.mkdirSync)((0, import_path9.dirname)(fullPath), { recursive: true });
5008
5413
  }
5009
- (0, import_fs8.writeFileSync)(fullPath, content, { encoding: "utf-8" });
5414
+ (0, import_fs9.writeFileSync)(fullPath, content, { encoding: "utf-8" });
5010
5415
  if (pageSlug) {
5011
5416
  try {
5012
5417
  const registry = readPageRegistry(cwd);
@@ -5091,8 +5496,8 @@ function registerSafeWriteTools(server2) {
5091
5496
 
5092
5497
  // src/tools/auth.ts
5093
5498
  var import_zod15 = require("zod");
5094
- var import_fs9 = require("fs");
5095
- var import_path9 = require("path");
5499
+ var import_fs10 = require("fs");
5500
+ var import_path10 = require("path");
5096
5501
  function buildHierarchy(roles) {
5097
5502
  const h = {};
5098
5503
  for (let i = 0; i < roles.length - 1; i++) {
@@ -5119,9 +5524,9 @@ ${indent} requiredRole: ${r.requiredRole}`).join("\n");
5119
5524
  }
5120
5525
  function detectNextMajorVersion(cwd) {
5121
5526
  try {
5122
- const pkgPath = (0, import_path9.join)(cwd, "package.json");
5123
- if (!(0, import_fs9.existsSync)(pkgPath)) return null;
5124
- const pkg = JSON.parse((0, import_fs9.readFileSync)(pkgPath, "utf8"));
5527
+ const pkgPath = (0, import_path10.join)(cwd, "package.json");
5528
+ if (!(0, import_fs10.existsSync)(pkgPath)) return null;
5529
+ const pkg = JSON.parse((0, import_fs10.readFileSync)(pkgPath, "utf8"));
5125
5530
  const nextVersion = pkg.dependencies?.next ?? pkg.devDependencies?.next;
5126
5531
  if (!nextVersion) return null;
5127
5532
  const match = nextVersion.match(/(\d+)/);
@@ -5785,7 +6190,7 @@ async function configureAuthHandler(params, cwd) {
5785
6190
  auditRetentionDays,
5786
6191
  normalizedRoutes
5787
6192
  );
5788
- (0, import_fs9.writeFileSync)((0, import_path9.join)(cwd, conventionFile), authFileContent, "utf8");
6193
+ (0, import_fs10.writeFileSync)((0, import_path10.join)(cwd, conventionFile), authFileContent, "utf8");
5789
6194
  filesWritten.push(conventionFile);
5790
6195
  } catch (err) {
5791
6196
  const msg = err instanceof Error ? err.message : String(err);
@@ -5805,12 +6210,12 @@ async function configureAuthHandler(params, cwd) {
5805
6210
  if (!devOnly) {
5806
6211
  try {
5807
6212
  const envBlock = generateEnvBlock(effectiveMethod, params);
5808
- const envPath = (0, import_path9.join)(cwd, ".env.example");
5809
- if ((0, import_fs9.existsSync)(envPath)) {
5810
- const existing = (0, import_fs9.readFileSync)(envPath, "utf8");
5811
- (0, import_fs9.writeFileSync)(envPath, existing.trimEnd() + "\n\n" + envBlock, "utf8");
6213
+ const envPath = (0, import_path10.join)(cwd, ".env.example");
6214
+ if ((0, import_fs10.existsSync)(envPath)) {
6215
+ const existing = (0, import_fs10.readFileSync)(envPath, "utf8");
6216
+ (0, import_fs10.writeFileSync)(envPath, existing.trimEnd() + "\n\n" + envBlock, "utf8");
5812
6217
  } else {
5813
- (0, import_fs9.writeFileSync)(envPath, envBlock, "utf8");
6218
+ (0, import_fs10.writeFileSync)(envPath, envBlock, "utf8");
5814
6219
  }
5815
6220
  filesWritten.push(".env.example");
5816
6221
  } catch (err) {
@@ -5828,10 +6233,10 @@ async function configureAuthHandler(params, cwd) {
5828
6233
  }
5829
6234
  if (devOnly) {
5830
6235
  try {
5831
- const mockAuthDir = (0, import_path9.join)(cwd, "lib");
5832
- (0, import_fs9.mkdirSync)(mockAuthDir, { recursive: true });
6236
+ const mockAuthDir = (0, import_path10.join)(cwd, "lib");
6237
+ (0, import_fs10.mkdirSync)(mockAuthDir, { recursive: true });
5833
6238
  const mockAuthContent = generateMockAuthContent(roles, mockUsers);
5834
- (0, import_fs9.writeFileSync)((0, import_path9.join)(mockAuthDir, "mock-auth.ts"), mockAuthContent, "utf8");
6239
+ (0, import_fs10.writeFileSync)((0, import_path10.join)(mockAuthDir, "mock-auth.ts"), mockAuthContent, "utf8");
5835
6240
  filesWritten.push("lib/mock-auth.ts");
5836
6241
  } catch (err) {
5837
6242
  const msg = err instanceof Error ? err.message : String(err);
@@ -5849,11 +6254,11 @@ async function configureAuthHandler(params, cwd) {
5849
6254
  };
5850
6255
  }
5851
6256
  try {
5852
- const pkgPath = (0, import_path9.join)(cwd, "package.json");
5853
- if ((0, import_fs9.existsSync)(pkgPath)) {
5854
- const existingPkg = (0, import_fs9.readFileSync)(pkgPath, "utf8");
6257
+ const pkgPath = (0, import_path10.join)(cwd, "package.json");
6258
+ if ((0, import_fs10.existsSync)(pkgPath)) {
6259
+ const existingPkg = (0, import_fs10.readFileSync)(pkgPath, "utf8");
5855
6260
  const updatedPkg = updatePackageJsonScripts(existingPkg, roles, mockUsers);
5856
- (0, import_fs9.writeFileSync)(pkgPath, updatedPkg, "utf8");
6261
+ (0, import_fs10.writeFileSync)(pkgPath, updatedPkg, "utf8");
5857
6262
  filesWritten.push("package.json");
5858
6263
  }
5859
6264
  } catch (err) {
@@ -5894,7 +6299,7 @@ async function configureAuthHandler(params, cwd) {
5894
6299
  normalizedRoutes,
5895
6300
  useProxy
5896
6301
  );
5897
- (0, import_fs9.writeFileSync)((0, import_path9.join)(cwd, "stackwright.auth.yml"), authYaml, "utf8");
6302
+ (0, import_fs10.writeFileSync)((0, import_path10.join)(cwd, "stackwright.auth.yml"), authYaml, "utf8");
5898
6303
  filesWritten.push("stackwright.auth.yml");
5899
6304
  } catch (err) {
5900
6305
  const msg = err instanceof Error ? err.message : String(err);
@@ -6007,8 +6412,8 @@ function registerAuthTools(server2) {
6007
6412
 
6008
6413
  // src/integrity.ts
6009
6414
  var import_crypto4 = require("crypto");
6010
- var import_fs10 = require("fs");
6011
- var import_path10 = require("path");
6415
+ var import_fs11 = require("fs");
6416
+ var import_path11 = require("path");
6012
6417
  var _checksums = /* @__PURE__ */ new Map([
6013
6418
  [
6014
6419
  "stackwright-pro-api-otter.json",
@@ -6016,11 +6421,11 @@ var _checksums = /* @__PURE__ */ new Map([
6016
6421
  ],
6017
6422
  [
6018
6423
  "stackwright-pro-auth-otter.json",
6019
- "1a21d9f23e250f23291124307e5a2a32af5a716566319a3290372dae3d02e637"
6424
+ "69fa03c5532ef617c86fe7af41e8174c168d282baf602b1b269a7e933ea86b7c"
6020
6425
  ],
6021
6426
  [
6022
6427
  "stackwright-pro-dashboard-otter.json",
6023
- "4a9ac39e929866ae3c929aa5bcbba3d9483a1c251c35f26f267f3e7433506f0c"
6428
+ "f55081bf4d6545e5435128919f6f9233956ba37671fc9f85300f240ad2497ab6"
6024
6429
  ],
6025
6430
  [
6026
6431
  "stackwright-pro-data-otter.json",
@@ -6036,19 +6441,19 @@ var _checksums = /* @__PURE__ */ new Map([
6036
6441
  ],
6037
6442
  [
6038
6443
  "stackwright-pro-foreman-otter.json",
6039
- "9d68b7b20af7a8a1668e2a693b4a2b05cf37acce1e9f6298a0966c97a3417e9f"
6444
+ "88c4101154d44a7c5470ded13562402a39323ee570ed3a90a73b8d85d7fd2817"
6040
6445
  ],
6041
6446
  [
6042
6447
  "stackwright-pro-form-wizard-otter.json",
6043
- "7f6f94192f26face57dc8b2e22e0a49d23ceeeb356ca4ebde05492f1b25e3c47"
6448
+ "cb350297a0068ead2424e703e8c775d91c0b87249ea6904f43b903baf8a84b79"
6044
6449
  ],
6045
6450
  [
6046
6451
  "stackwright-pro-geo-otter.json",
6047
- "fc3d18e02a6147d95d3dd9093ce74c0e8fffaecc2f9ecdbd19c163a80129d264"
6452
+ "3032fbd27de36c0cdc0e19e46a32d771208d8e86714482f5a368d46342c8317a"
6048
6453
  ],
6049
6454
  [
6050
6455
  "stackwright-pro-page-otter.json",
6051
- "879e8ff4aa645bac2c63dc962cad1a549876983c257aec756e95293c3b22ce7e"
6456
+ "cc0db24b00f0130f8b4ec8d385c73ea1bcf5d57ba45aca6b986bcbd5da328930"
6052
6457
  ],
6053
6458
  [
6054
6459
  "stackwright-pro-polish-otter.json",
@@ -6060,15 +6465,15 @@ var _checksums = /* @__PURE__ */ new Map([
6060
6465
  ],
6061
6466
  [
6062
6467
  "stackwright-pro-scaffold-otter.json",
6063
- "92930c732547c90a19e89ee0e25b5f037b7366f770941f0a01e148ff240a1b6d"
6468
+ "90272bab51bd8ed9e25c0fb6481cdd4252386850f64192edfe1e2eff44166e2d"
6064
6469
  ],
6065
6470
  [
6066
6471
  "stackwright-pro-theme-otter.json",
6067
- "01d16d0597d475db60362e3b8020035e140817d197c36f4307a48b95f4b28b60"
6472
+ "afb47f8381907145c0e098bdcaae4dd9f5c4ff825a8e88d00a218d2f6858820b"
6068
6473
  ],
6069
6474
  [
6070
6475
  "stackwright-services-otter.json",
6071
- "2a50ceb3fad166251d0ad8709a34a32118645713e0e34628165d16dced1d5c93"
6476
+ "4351348806aeb98404a2bacb044209ca411be5e35e2f2c3d03eb4d28a07fde78"
6072
6477
  ]
6073
6478
  ]);
6074
6479
  Object.freeze(_checksums);
@@ -6091,14 +6496,14 @@ function safeEqual(a, b) {
6091
6496
  return (0, import_crypto4.timingSafeEqual)(Buffer.from(a, "utf8"), Buffer.from(b, "utf8"));
6092
6497
  }
6093
6498
  function verifyOtterFile(filePath) {
6094
- const filename = (0, import_path10.basename)(filePath);
6499
+ const filename = (0, import_path11.basename)(filePath);
6095
6500
  const expected = CANONICAL_CHECKSUMS.get(filename);
6096
6501
  if (expected === void 0) {
6097
6502
  return { verified: false, filename, error: `Unknown otter file: not in canonical set` };
6098
6503
  }
6099
6504
  let stat;
6100
6505
  try {
6101
- stat = (0, import_fs10.lstatSync)(filePath);
6506
+ stat = (0, import_fs11.lstatSync)(filePath);
6102
6507
  } catch (err) {
6103
6508
  const msg = err instanceof Error ? err.message : String(err);
6104
6509
  return { verified: false, filename, error: `Cannot stat file: ${msg}` };
@@ -6116,7 +6521,7 @@ function verifyOtterFile(filePath) {
6116
6521
  }
6117
6522
  let raw;
6118
6523
  try {
6119
- raw = (0, import_fs10.readFileSync)(filePath);
6524
+ raw = (0, import_fs11.readFileSync)(filePath);
6120
6525
  } catch (err) {
6121
6526
  const msg = err instanceof Error ? err.message : String(err);
6122
6527
  return { verified: false, filename, error: `Cannot read file: ${msg}` };
@@ -6166,7 +6571,7 @@ function verifyAllOtters(otterDir) {
6166
6571
  const unknown = [];
6167
6572
  let entries;
6168
6573
  try {
6169
- entries = (0, import_fs10.readdirSync)(otterDir);
6574
+ entries = (0, import_fs11.readdirSync)(otterDir);
6170
6575
  } catch (err) {
6171
6576
  const msg = err instanceof Error ? err.message : String(err);
6172
6577
  return {
@@ -6177,9 +6582,9 @@ function verifyAllOtters(otterDir) {
6177
6582
  }
6178
6583
  const otterFiles = entries.filter((f) => f.endsWith("-otter.json"));
6179
6584
  for (const filename of otterFiles) {
6180
- const filePath = (0, import_path10.join)(otterDir, filename);
6585
+ const filePath = (0, import_path11.join)(otterDir, filename);
6181
6586
  try {
6182
- if ((0, import_fs10.lstatSync)(filePath).isSymbolicLink()) {
6587
+ if ((0, import_fs11.lstatSync)(filePath).isSymbolicLink()) {
6183
6588
  failed.push({ filename, error: "Skipped: symlink" });
6184
6589
  continue;
6185
6590
  }
@@ -6205,9 +6610,9 @@ var DEFAULT_SEARCH_PATHS = ["node_modules/@stackwright-pro/otters/src/", "packag
6205
6610
  function resolveOtterDir() {
6206
6611
  const cwd = process.cwd();
6207
6612
  for (const relative2 of DEFAULT_SEARCH_PATHS) {
6208
- const candidate = (0, import_path10.join)(cwd, relative2);
6613
+ const candidate = (0, import_path11.join)(cwd, relative2);
6209
6614
  try {
6210
- (0, import_fs10.lstatSync)(candidate);
6615
+ (0, import_fs11.lstatSync)(candidate);
6211
6616
  return candidate;
6212
6617
  } catch {
6213
6618
  }
@@ -6286,13 +6691,13 @@ function registerIntegrityTools(server2) {
6286
6691
 
6287
6692
  // src/tools/domain.ts
6288
6693
  var import_zod16 = require("zod");
6289
- var import_fs11 = require("fs");
6290
- var import_path11 = require("path");
6694
+ var import_fs12 = require("fs");
6695
+ var import_path12 = require("path");
6291
6696
  function handleListCollections(input) {
6292
6697
  const cwd = input._cwd ?? process.cwd();
6293
6698
  const sources = [
6294
6699
  {
6295
- path: (0, import_path11.join)(cwd, ".stackwright", "artifacts", "data-config.json"),
6700
+ path: (0, import_path12.join)(cwd, ".stackwright", "artifacts", "data-config.json"),
6296
6701
  source: "data-config.json",
6297
6702
  parse: (raw) => {
6298
6703
  const parsed = JSON.parse(raw);
@@ -6303,15 +6708,15 @@ function handleListCollections(input) {
6303
6708
  }
6304
6709
  },
6305
6710
  {
6306
- path: (0, import_path11.join)(cwd, "stackwright.yml"),
6711
+ path: (0, import_path12.join)(cwd, "stackwright.yml"),
6307
6712
  source: "stackwright.yml",
6308
6713
  parse: extractCollectionsFromYaml
6309
6714
  }
6310
6715
  ];
6311
6716
  for (const { path: path5, source, parse } of sources) {
6312
- if (!(0, import_fs11.existsSync)(path5)) continue;
6717
+ if (!(0, import_fs12.existsSync)(path5)) continue;
6313
6718
  try {
6314
- const collections = parse((0, import_fs11.readFileSync)(path5, "utf8"));
6719
+ const collections = parse((0, import_fs12.readFileSync)(path5, "utf8"));
6315
6720
  return {
6316
6721
  text: JSON.stringify({ collections, source, collectionCount: collections.length }),
6317
6722
  isError: false
@@ -6462,8 +6867,8 @@ function handleValidateWorkflow(input) {
6462
6867
  if (input.workflow && Object.keys(input.workflow).length > 0) {
6463
6868
  raw = input.workflow;
6464
6869
  } else {
6465
- const artifactPath = (0, import_path11.join)(cwd, ".stackwright", "artifacts", "workflow-config.json");
6466
- if (!(0, import_fs11.existsSync)(artifactPath)) {
6870
+ const artifactPath = (0, import_path12.join)(cwd, ".stackwright", "artifacts", "workflow-config.json");
6871
+ if (!(0, import_fs12.existsSync)(artifactPath)) {
6467
6872
  return fail([
6468
6873
  {
6469
6874
  code: "NO_WORKFLOW",
@@ -6472,7 +6877,7 @@ function handleValidateWorkflow(input) {
6472
6877
  ]);
6473
6878
  }
6474
6879
  try {
6475
- raw = JSON.parse((0, import_fs11.readFileSync)(artifactPath, "utf8"));
6880
+ raw = JSON.parse((0, import_fs12.readFileSync)(artifactPath, "utf8"));
6476
6881
  } catch (err) {
6477
6882
  return fail([{ code: "INVALID_JSON", message: `Failed to parse workflow artifact: ${err}` }]);
6478
6883
  }
@@ -6909,8 +7314,8 @@ function registerProPageTools(server2) {
6909
7314
  }
6910
7315
 
6911
7316
  // src/tools/scaffold-cleanup.ts
6912
- var import_fs12 = require("fs");
6913
- var import_path12 = require("path");
7317
+ var import_fs13 = require("fs");
7318
+ var import_path13 = require("path");
6914
7319
  var SCAFFOLD_FILES_TO_DELETE = [
6915
7320
  "content/posts/getting-started.yaml",
6916
7321
  "content/posts/hello-world.yaml"
@@ -6935,12 +7340,12 @@ var FALLBACK_COLORS = {
6935
7340
  mutedForeground: "#737373"
6936
7341
  };
6937
7342
  function readThemeColors(cwd) {
6938
- const tokenPath = (0, import_path12.join)(cwd, ".stackwright", "artifacts", "theme-tokens.json");
6939
- if (!(0, import_fs12.existsSync)(tokenPath)) return { ...FALLBACK_COLORS };
6940
- const stat = (0, import_fs12.lstatSync)(tokenPath);
7343
+ const tokenPath = (0, import_path13.join)(cwd, ".stackwright", "artifacts", "theme-tokens.json");
7344
+ if (!(0, import_fs13.existsSync)(tokenPath)) return { ...FALLBACK_COLORS };
7345
+ const stat = (0, import_fs13.lstatSync)(tokenPath);
6941
7346
  if (stat.isSymbolicLink()) return { ...FALLBACK_COLORS };
6942
7347
  try {
6943
- const raw = JSON.parse((0, import_fs12.readFileSync)(tokenPath, "utf-8"));
7348
+ const raw = JSON.parse((0, import_fs13.readFileSync)(tokenPath, "utf-8"));
6944
7349
  const css = raw?.cssVariables ?? {};
6945
7350
  const toHsl = (hslValue) => {
6946
7351
  if (!hslValue || typeof hslValue !== "string") return null;
@@ -7000,15 +7405,15 @@ function generateNotFoundPage(colors) {
7000
7405
  `;
7001
7406
  }
7002
7407
  function isSafePath(fullPath) {
7003
- if (!(0, import_fs12.existsSync)(fullPath)) return true;
7004
- const stat = (0, import_fs12.lstatSync)(fullPath);
7408
+ if (!(0, import_fs13.existsSync)(fullPath)) return true;
7409
+ const stat = (0, import_fs13.lstatSync)(fullPath);
7005
7410
  return !stat.isSymbolicLink();
7006
7411
  }
7007
7412
  function isDirEmpty(dirPath) {
7008
- if (!(0, import_fs12.existsSync)(dirPath)) return false;
7009
- const stat = (0, import_fs12.lstatSync)(dirPath);
7413
+ if (!(0, import_fs13.existsSync)(dirPath)) return false;
7414
+ const stat = (0, import_fs13.lstatSync)(dirPath);
7010
7415
  if (!stat.isDirectory()) return false;
7011
- return (0, import_fs12.readdirSync)(dirPath).length === 0;
7416
+ return (0, import_fs13.readdirSync)(dirPath).length === 0;
7012
7417
  }
7013
7418
  function handleCleanupScaffold(_cwd) {
7014
7419
  const cwd = _cwd ?? process.cwd();
@@ -7021,8 +7426,8 @@ function handleCleanupScaffold(_cwd) {
7021
7426
  cleanupWarnings: []
7022
7427
  };
7023
7428
  for (const relPath of SCAFFOLD_FILES_TO_DELETE) {
7024
- const fullPath = (0, import_path12.join)(cwd, relPath);
7025
- if (!(0, import_fs12.existsSync)(fullPath)) {
7429
+ const fullPath = (0, import_path13.join)(cwd, relPath);
7430
+ if (!(0, import_fs13.existsSync)(fullPath)) {
7026
7431
  result.skipped.push(relPath);
7027
7432
  continue;
7028
7433
  }
@@ -7031,7 +7436,7 @@ function handleCleanupScaffold(_cwd) {
7031
7436
  continue;
7032
7437
  }
7033
7438
  try {
7034
- (0, import_fs12.unlinkSync)(fullPath);
7439
+ (0, import_fs13.unlinkSync)(fullPath);
7035
7440
  result.deleted.push(relPath);
7036
7441
  } catch (err) {
7037
7442
  result.errors.push(`Failed to delete ${relPath}: ${String(err)}`);
@@ -7039,19 +7444,19 @@ function handleCleanupScaffold(_cwd) {
7039
7444
  }
7040
7445
  {
7041
7446
  const relPath = "pages/getting-started/content.yml";
7042
- const fullPath = (0, import_path12.join)(cwd, relPath);
7043
- if (!(0, import_fs12.existsSync)(fullPath)) {
7447
+ const fullPath = (0, import_path13.join)(cwd, relPath);
7448
+ if (!(0, import_fs13.existsSync)(fullPath)) {
7044
7449
  result.skipped.push(relPath);
7045
7450
  } else if (!isSafePath(fullPath)) {
7046
7451
  result.errors.push(`Refusing to delete symlink: ${relPath}`);
7047
7452
  } else {
7048
- const content = (0, import_fs12.readFileSync)(fullPath, "utf-8");
7453
+ const content = (0, import_fs13.readFileSync)(fullPath, "utf-8");
7049
7454
  const isScaffoldDefault = GETTING_STARTED_SCAFFOLD_FINGERPRINTS.some(
7050
7455
  (marker) => content.includes(marker)
7051
7456
  );
7052
7457
  if (isScaffoldDefault) {
7053
7458
  try {
7054
- (0, import_fs12.unlinkSync)(fullPath);
7459
+ (0, import_fs13.unlinkSync)(fullPath);
7055
7460
  result.deleted.push(relPath);
7056
7461
  } catch (err) {
7057
7462
  result.errors.push(`Failed to delete ${relPath}: ${String(err)}`);
@@ -7065,21 +7470,21 @@ function handleCleanupScaffold(_cwd) {
7065
7470
  }
7066
7471
  {
7067
7472
  const relPath = `content/posts/${POSTS_COLLECTION_FILE}`;
7068
- const fullPath = (0, import_path12.join)(cwd, relPath);
7069
- const postsDir = (0, import_path12.join)(cwd, "content/posts");
7070
- if (!(0, import_fs12.existsSync)(fullPath)) {
7473
+ const fullPath = (0, import_path13.join)(cwd, relPath);
7474
+ const postsDir = (0, import_path13.join)(cwd, "content/posts");
7475
+ if (!(0, import_fs13.existsSync)(fullPath)) {
7071
7476
  result.skipped.push(relPath);
7072
7477
  } else if (!isSafePath(fullPath)) {
7073
7478
  result.errors.push(`Refusing to delete symlink: ${relPath}`);
7074
- } else if (!(0, import_fs12.existsSync)(postsDir)) {
7479
+ } else if (!(0, import_fs13.existsSync)(postsDir)) {
7075
7480
  result.skipped.push(relPath);
7076
7481
  } else {
7077
- const userPostFiles = (0, import_fs12.readdirSync)(postsDir).filter(
7482
+ const userPostFiles = (0, import_fs13.readdirSync)(postsDir).filter(
7078
7483
  (f) => f !== POSTS_COLLECTION_FILE && POST_FILE_EXTENSIONS.some((ext) => f.endsWith(ext))
7079
7484
  );
7080
7485
  if (userPostFiles.length === 0) {
7081
7486
  try {
7082
- (0, import_fs12.unlinkSync)(fullPath);
7487
+ (0, import_fs13.unlinkSync)(fullPath);
7083
7488
  result.deleted.push(relPath);
7084
7489
  } catch (err) {
7085
7490
  result.errors.push(`Failed to delete ${relPath}: ${String(err)}`);
@@ -7092,15 +7497,15 @@ function handleCleanupScaffold(_cwd) {
7092
7497
  }
7093
7498
  }
7094
7499
  for (const relDir of SCAFFOLD_DIRS_TO_PRUNE) {
7095
- const fullDir = (0, import_path12.join)(cwd, relDir);
7096
- if (!(0, import_fs12.existsSync)(fullDir)) continue;
7500
+ const fullDir = (0, import_path13.join)(cwd, relDir);
7501
+ if (!(0, import_fs13.existsSync)(fullDir)) continue;
7097
7502
  if (!isSafePath(fullDir)) {
7098
7503
  result.errors.push(`Refusing to prune symlink directory: ${relDir}`);
7099
7504
  continue;
7100
7505
  }
7101
7506
  if (isDirEmpty(fullDir)) {
7102
7507
  try {
7103
- (0, import_fs12.rmdirSync)(fullDir);
7508
+ (0, import_fs13.rmdirSync)(fullDir);
7104
7509
  result.prunedDirs.push(relDir);
7105
7510
  } catch (err) {
7106
7511
  result.errors.push(`Failed to prune directory ${relDir}: ${String(err)}`);
@@ -7108,8 +7513,8 @@ function handleCleanupScaffold(_cwd) {
7108
7513
  }
7109
7514
  }
7110
7515
  {
7111
- const fullPath = (0, import_path12.join)(cwd, NOT_FOUND_PATH);
7112
- if (!(0, import_fs12.existsSync)(fullPath)) {
7516
+ const fullPath = (0, import_path13.join)(cwd, NOT_FOUND_PATH);
7517
+ if (!(0, import_fs13.existsSync)(fullPath)) {
7113
7518
  result.skipped.push(NOT_FOUND_PATH);
7114
7519
  } else if (!isSafePath(fullPath)) {
7115
7520
  result.errors.push(`Refusing to rewrite symlink: ${NOT_FOUND_PATH}`);
@@ -7117,9 +7522,9 @@ function handleCleanupScaffold(_cwd) {
7117
7522
  try {
7118
7523
  const colors = readThemeColors(cwd);
7119
7524
  const content = generateNotFoundPage(colors);
7120
- const dir = (0, import_path12.dirname)(fullPath);
7121
- (0, import_fs12.mkdirSync)(dir, { recursive: true });
7122
- (0, import_fs12.writeFileSync)(fullPath, content, "utf-8");
7525
+ const dir = (0, import_path13.dirname)(fullPath);
7526
+ (0, import_fs13.mkdirSync)(dir, { recursive: true });
7527
+ (0, import_fs13.writeFileSync)(fullPath, content, "utf-8");
7123
7528
  result.rewritten.push(NOT_FOUND_PATH);
7124
7529
  } catch (err) {
7125
7530
  result.errors.push(`Failed to rewrite ${NOT_FOUND_PATH}: ${String(err)}`);
@@ -7388,23 +7793,23 @@ function registerContrastTools(server2) {
7388
7793
 
7389
7794
  // src/tools/compile.ts
7390
7795
  var import_zod20 = require("zod");
7391
- var import_fs13 = __toESM(require("fs"));
7392
- var import_path13 = __toESM(require("path"));
7796
+ var import_fs14 = __toESM(require("fs"));
7797
+ var import_path14 = __toESM(require("path"));
7393
7798
  var import_server = require("@stackwright-pro/pulse/server");
7394
7799
  var import_server2 = require("@stackwright-pro/auth-nextjs/server");
7395
7800
  var import_server3 = require("@stackwright-pro/openapi/server");
7396
7801
  function buildContext(projectRoot) {
7397
7802
  return {
7398
7803
  projectRoot,
7399
- contentOutDir: import_path13.default.join(projectRoot, "public", "stackwright-content"),
7400
- imagesDir: import_path13.default.join(projectRoot, "public", "images"),
7804
+ contentOutDir: import_path14.default.join(projectRoot, "public", "stackwright-content"),
7805
+ imagesDir: import_path14.default.join(projectRoot, "public", "images"),
7401
7806
  siteConfig: loadSiteConfig(projectRoot)
7402
7807
  };
7403
7808
  }
7404
7809
  function loadSiteConfig(projectRoot) {
7405
7810
  try {
7406
- const sitePath = import_path13.default.join(projectRoot, "public", "stackwright-content", "_site.json");
7407
- return JSON.parse(import_fs13.default.readFileSync(sitePath, "utf8"));
7811
+ const sitePath = import_path14.default.join(projectRoot, "public", "stackwright-content", "_site.json");
7812
+ return JSON.parse(import_fs14.default.readFileSync(sitePath, "utf8"));
7408
7813
  } catch {
7409
7814
  return {};
7410
7815
  }
@@ -7415,8 +7820,8 @@ function formatDuration(startMs) {
7415
7820
  }
7416
7821
  async function tryOssCompile(fn, ctx, extra) {
7417
7822
  try {
7418
- const bsPath = import_path13.default.join(ctx.projectRoot, "node_modules", "@stackwright", "build-scripts");
7419
- if (!import_fs13.default.existsSync(bsPath)) {
7823
+ const bsPath = import_path14.default.join(ctx.projectRoot, "node_modules", "@stackwright", "build-scripts");
7824
+ if (!import_fs14.default.existsSync(bsPath)) {
7420
7825
  return {
7421
7826
  ok: false,
7422
7827
  error: `@stackwright/build-scripts not found at ${bsPath}. Is it installed in your project?`
@@ -7630,14 +8035,14 @@ ${results.join("\n")}`;
7630
8035
 
7631
8036
  // src/tools/strip-legacy-integrations.ts
7632
8037
  var import_zod21 = require("zod");
7633
- var import_fs14 = require("fs");
7634
- var import_path14 = require("path");
8038
+ var import_fs15 = require("fs");
8039
+ var import_path15 = require("path");
7635
8040
  function handleStripLegacyIntegrations(input) {
7636
8041
  const { projectRoot } = input;
7637
- const ymlPath = (0, import_path14.join)(projectRoot, "stackwright.yml");
7638
- const yamlPath = (0, import_path14.join)(projectRoot, "stackwright.yaml");
7639
- if (!(0, import_fs14.existsSync)(ymlPath)) {
7640
- if ((0, import_fs14.existsSync)(yamlPath)) {
8042
+ const ymlPath = (0, import_path15.join)(projectRoot, "stackwright.yml");
8043
+ const yamlPath = (0, import_path15.join)(projectRoot, "stackwright.yaml");
8044
+ if (!(0, import_fs15.existsSync)(ymlPath)) {
8045
+ if ((0, import_fs15.existsSync)(yamlPath)) {
7641
8046
  return {
7642
8047
  changed: false,
7643
8048
  removedEntries: 0,
@@ -7650,7 +8055,7 @@ function handleStripLegacyIntegrations(input) {
7650
8055
  message: "stackwright.yml not found \u2014 no-op."
7651
8056
  };
7652
8057
  }
7653
- const raw = (0, import_fs14.readFileSync)(ymlPath, "utf-8");
8058
+ const raw = (0, import_fs15.readFileSync)(ymlPath, "utf-8");
7654
8059
  const isCRLF = raw.includes("\r\n");
7655
8060
  const content = isCRLF ? raw.replace(/\r\n/g, "\n") : raw;
7656
8061
  const lines = content.split("\n");
@@ -7680,7 +8085,7 @@ function handleStripLegacyIntegrations(input) {
7680
8085
  outputContent += "\n";
7681
8086
  }
7682
8087
  const outputRaw = isCRLF ? outputContent.replace(/\n/g, "\r\n") : outputContent;
7683
- (0, import_fs14.writeFileSync)(ymlPath, outputRaw, "utf-8");
8088
+ (0, import_fs15.writeFileSync)(ymlPath, outputRaw, "utf-8");
7684
8089
  return {
7685
8090
  changed: true,
7686
8091
  removedEntries,
@@ -7705,15 +8110,15 @@ function registerStripLegacyIntegrationsTool(server2) {
7705
8110
 
7706
8111
  // src/tools/emitters.ts
7707
8112
  var import_zod22 = require("zod");
7708
- var import_fs15 = require("fs");
7709
- var import_path15 = require("path");
8113
+ var import_fs16 = require("fs");
8114
+ var import_path16 = require("path");
7710
8115
  var import_js_yaml4 = __toESM(require("js-yaml"));
7711
8116
  var import_emitters = require("@stackwright-pro/emitters");
7712
8117
  function readIntegrations(cwd) {
7713
- const filePath = (0, import_path15.join)(cwd, "stackwright.integrations.yml");
7714
- if (!(0, import_fs15.existsSync)(filePath)) return [];
8118
+ const filePath = (0, import_path16.join)(cwd, "stackwright.integrations.yml");
8119
+ if (!(0, import_fs16.existsSync)(filePath)) return [];
7715
8120
  try {
7716
- const raw = import_js_yaml4.default.load((0, import_fs15.readFileSync)(filePath, "utf-8"));
8121
+ const raw = import_js_yaml4.default.load((0, import_fs16.readFileSync)(filePath, "utf-8"));
7717
8122
  if (!raw?.integrations || !Array.isArray(raw.integrations)) return [];
7718
8123
  return raw.integrations.filter((i) => typeof i.name === "string").map((i) => ({
7719
8124
  name: i.name,
@@ -7724,15 +8129,15 @@ function readIntegrations(cwd) {
7724
8129
  }
7725
8130
  }
7726
8131
  function readServiceTokenRefs(cwd) {
7727
- const servicesDir = (0, import_path15.join)(cwd, "services");
7728
- if (!(0, import_fs15.existsSync)(servicesDir)) return [];
8132
+ const servicesDir = (0, import_path16.join)(cwd, "services");
8133
+ if (!(0, import_fs16.existsSync)(servicesDir)) return [];
7729
8134
  try {
7730
- const files = (0, import_fs15.readdirSync)(servicesDir).filter((f) => f.endsWith(".yml") || f.endsWith(".yaml"));
8135
+ const files = (0, import_fs16.readdirSync)(servicesDir).filter((f) => f.endsWith(".yml") || f.endsWith(".yaml"));
7731
8136
  const refs = [];
7732
8137
  for (const file of files) {
7733
8138
  try {
7734
8139
  const raw = import_js_yaml4.default.load(
7735
- (0, import_fs15.readFileSync)((0, import_path15.join)(servicesDir, file), "utf-8")
8140
+ (0, import_fs16.readFileSync)((0, import_path16.join)(servicesDir, file), "utf-8")
7736
8141
  );
7737
8142
  if (raw?.tokenEnv && typeof raw.tokenEnv === "string") {
7738
8143
  refs.push({
@@ -7882,12 +8287,12 @@ This is the third concrete instance of the swp-zf9y deterministic emitter patter
7882
8287
 
7883
8288
  // src/tools/list-specs.ts
7884
8289
  var import_zod23 = require("zod");
7885
- var import_fs16 = require("fs");
7886
- var import_path16 = require("path");
8290
+ var import_fs17 = require("fs");
8291
+ var import_path17 = require("path");
7887
8292
  var STRIP_SUFFIXES = ["-openapi", "-asyncapi", "-api", "-spec"];
7888
8293
  var ALLOWED_EXTS = /* @__PURE__ */ new Set([".yaml", ".yml", ".json"]);
7889
8294
  function deriveIntegrationName(fileName) {
7890
- let name = (0, import_path16.basename)(fileName, (0, import_path16.extname)(fileName));
8295
+ let name = (0, import_path17.basename)(fileName, (0, import_path17.extname)(fileName));
7891
8296
  for (const suffix of STRIP_SUFFIXES) {
7892
8297
  if (name.endsWith(suffix)) {
7893
8298
  name = name.slice(0, -suffix.length);
@@ -7905,29 +8310,29 @@ function detectFormat(snippet) {
7905
8310
  }
7906
8311
  function handleListSpecs(input) {
7907
8312
  const { projectRoot } = input;
7908
- const specsDir = (0, import_path16.join)(projectRoot, "specs");
8313
+ const specsDir = (0, import_path17.join)(projectRoot, "specs");
7909
8314
  const empty = { projectRoot, specs: [], totalCount: 0 };
7910
- if (!(0, import_fs16.existsSync)(specsDir)) return empty;
8315
+ if (!(0, import_fs17.existsSync)(specsDir)) return empty;
7911
8316
  let entries;
7912
8317
  try {
7913
- entries = (0, import_fs16.readdirSync)(specsDir);
8318
+ entries = (0, import_fs17.readdirSync)(specsDir);
7914
8319
  } catch {
7915
8320
  return empty;
7916
8321
  }
7917
8322
  const specs = [];
7918
8323
  for (const entry of entries) {
7919
- const fullPath = (0, import_path16.join)(specsDir, entry);
8324
+ const fullPath = (0, import_path17.join)(specsDir, entry);
7920
8325
  let stat;
7921
8326
  try {
7922
- stat = (0, import_fs16.statSync)(fullPath);
8327
+ stat = (0, import_fs17.statSync)(fullPath);
7923
8328
  } catch {
7924
8329
  continue;
7925
8330
  }
7926
8331
  if (!stat.isFile()) continue;
7927
- if (!ALLOWED_EXTS.has((0, import_path16.extname)(entry).toLowerCase())) continue;
8332
+ if (!ALLOWED_EXTS.has((0, import_path17.extname)(entry).toLowerCase())) continue;
7928
8333
  let snippet = "";
7929
8334
  try {
7930
- snippet = (0, import_fs16.readFileSync)(fullPath, "utf-8").slice(0, 2048);
8335
+ snippet = (0, import_fs17.readFileSync)(fullPath, "utf-8").slice(0, 2048);
7931
8336
  } catch {
7932
8337
  }
7933
8338
  specs.push({
@@ -7959,14 +8364,14 @@ function registerListSpecsTool(server2) {
7959
8364
 
7960
8365
  // src/tools/consolidate-integrations.ts
7961
8366
  var import_zod24 = require("zod");
7962
- var import_fs17 = require("fs");
8367
+ var import_fs18 = require("fs");
7963
8368
  var import_proper_lockfile2 = require("proper-lockfile");
7964
- var import_path17 = require("path");
8369
+ var import_path18 = require("path");
7965
8370
  var import_js_yaml5 = __toESM(require("js-yaml"));
7966
8371
  var BASE_MOCK_PORT = 4011;
7967
8372
  var STRIP_SUFFIXES2 = ["-openapi", "-asyncapi", "-api", "-spec"];
7968
8373
  function deriveNameFromFilename(filename) {
7969
- const base = (0, import_path17.basename)(filename, ".json").replace(/^api-config-/, "");
8374
+ const base = (0, import_path18.basename)(filename, ".json").replace(/^api-config-/, "");
7970
8375
  for (const suffix of STRIP_SUFFIXES2) {
7971
8376
  if (base.endsWith(suffix)) return base.slice(0, -suffix.length);
7972
8377
  }
@@ -7974,13 +8379,13 @@ function deriveNameFromFilename(filename) {
7974
8379
  }
7975
8380
  function handleConsolidateIntegrations(input) {
7976
8381
  const { projectRoot } = input;
7977
- const artifactsDir = (0, import_path17.join)(projectRoot, ".stackwright", "artifacts");
7978
- if (!(0, import_fs17.existsSync)(artifactsDir)) {
8382
+ const artifactsDir = (0, import_path18.join)(projectRoot, ".stackwright", "artifacts");
8383
+ if (!(0, import_fs18.existsSync)(artifactsDir)) {
7979
8384
  return { written: false, integrationCount: 0, reason: "no per-spec artifacts found" };
7980
8385
  }
7981
8386
  let entries;
7982
8387
  try {
7983
- entries = (0, import_fs17.readdirSync)(artifactsDir).filter(
8388
+ entries = (0, import_fs18.readdirSync)(artifactsDir).filter(
7984
8389
  (f) => f.startsWith("api-config-") && f.endsWith(".json") && f !== "api-config.json"
7985
8390
  );
7986
8391
  } catch {
@@ -7991,14 +8396,22 @@ function handleConsolidateIntegrations(input) {
7991
8396
  }
7992
8397
  entries.sort();
7993
8398
  const integrationsByName = /* @__PURE__ */ new Map();
8399
+ const skippedIntegrationsList = [];
7994
8400
  entries.forEach((filename, index) => {
7995
8401
  let artifact = {};
7996
8402
  try {
7997
- const raw = (0, import_fs17.readFileSync)((0, import_path17.join)(artifactsDir, filename), "utf-8");
8403
+ const raw = (0, import_fs18.readFileSync)((0, import_path18.join)(artifactsDir, filename), "utf-8");
7998
8404
  artifact = JSON.parse(raw);
7999
8405
  } catch {
8000
8406
  }
8001
8407
  const name = artifact.integrationName ?? deriveNameFromFilename(filename);
8408
+ if (artifact.skipped && artifact.skipped.length > 0) {
8409
+ skippedIntegrationsList.push({
8410
+ name,
8411
+ reason: artifact.skipped[0]?.reason ?? "explicitly skipped by api-otter"
8412
+ });
8413
+ return;
8414
+ }
8002
8415
  const spec = artifact.integrationSpec ?? artifact.specPath ?? `./specs/${name}.yaml`;
8003
8416
  const mockUrl = artifact.mockUrl ?? `http://localhost:${BASE_MOCK_PORT + index}`;
8004
8417
  const baseUrl = artifact.baseUrl ?? "";
@@ -8019,21 +8432,21 @@ function handleConsolidateIntegrations(input) {
8019
8432
  integrationsByName.set(name, entry);
8020
8433
  });
8021
8434
  const integrations = Array.from(integrationsByName.values());
8022
- const dedupedCount = entries.length - integrations.length;
8023
- const integrationsYmlPath = (0, import_path17.join)(projectRoot, "stackwright.integrations.yml");
8435
+ const dedupedCount = entries.length - skippedIntegrationsList.length - integrations.length;
8436
+ const integrationsYmlPath = (0, import_path18.join)(projectRoot, "stackwright.integrations.yml");
8024
8437
  let existing = {};
8025
- if ((0, import_fs17.existsSync)(integrationsYmlPath)) {
8438
+ if ((0, import_fs18.existsSync)(integrationsYmlPath)) {
8026
8439
  try {
8027
- const raw = (0, import_fs17.readFileSync)(integrationsYmlPath, "utf-8");
8440
+ const raw = (0, import_fs18.readFileSync)(integrationsYmlPath, "utf-8");
8028
8441
  existing = import_js_yaml5.default.load(raw) ?? {};
8029
8442
  } catch {
8030
8443
  existing = {};
8031
8444
  }
8032
8445
  }
8033
8446
  const merged = { ...existing, integrations };
8034
- (0, import_fs17.mkdirSync)((0, import_path17.join)(projectRoot), { recursive: true });
8035
- if (!(0, import_fs17.existsSync)(integrationsYmlPath)) {
8036
- (0, import_fs17.writeFileSync)(integrationsYmlPath, "", "utf-8");
8447
+ (0, import_fs18.mkdirSync)((0, import_path18.join)(projectRoot), { recursive: true });
8448
+ if (!(0, import_fs18.existsSync)(integrationsYmlPath)) {
8449
+ (0, import_fs18.writeFileSync)(integrationsYmlPath, "", "utf-8");
8037
8450
  }
8038
8451
  const release = (0, import_proper_lockfile2.lockSync)(integrationsYmlPath, {
8039
8452
  realpath: false,
@@ -8043,7 +8456,7 @@ function handleConsolidateIntegrations(input) {
8043
8456
  const header = `# stackwright.integrations.yml \u2014 Auto-generated by stackwright_pro_consolidate_integrations
8044
8457
  `;
8045
8458
  const body = import_js_yaml5.default.dump(merged, { lineWidth: 120, noRefs: true });
8046
- (0, import_fs17.writeFileSync)(integrationsYmlPath, header + body, "utf-8");
8459
+ (0, import_fs18.writeFileSync)(integrationsYmlPath, header + body, "utf-8");
8047
8460
  } finally {
8048
8461
  release();
8049
8462
  }
@@ -8051,7 +8464,11 @@ function handleConsolidateIntegrations(input) {
8051
8464
  written: true,
8052
8465
  integrationCount: integrations.length,
8053
8466
  path: "stackwright.integrations.yml",
8054
- ...dedupedCount > 0 ? { dedupedCount } : {}
8467
+ ...dedupedCount > 0 ? { dedupedCount } : {},
8468
+ ...skippedIntegrationsList.length > 0 ? {
8469
+ skippedCount: skippedIntegrationsList.length,
8470
+ skippedIntegrations: skippedIntegrationsList
8471
+ } : {}
8055
8472
  };
8056
8473
  }
8057
8474
  function registerConsolidateIntegrationsTool(server2) {
@@ -8125,7 +8542,7 @@ var package_default = {
8125
8542
  "test:coverage": "vitest run --coverage"
8126
8543
  },
8127
8544
  name: "@stackwright-pro/mcp",
8128
- version: "0.2.0-alpha.107",
8545
+ version: "0.2.0-alpha.109",
8129
8546
  description: "MCP tools for Stackwright Pro - Data Explorer, Security, ISR, and Dashboard generation",
8130
8547
  license: "SEE LICENSE IN LICENSE",
8131
8548
  main: "./dist/server.js",