@stackwright-pro/mcp 0.2.0-alpha.101 → 0.2.0-alpha.102

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
@@ -580,7 +580,7 @@ function registerDashboardTools(server2) {
580
580
  yamlLines.push(` header: Status`);
581
581
  yamlLines.push(` type: badge`);
582
582
  }
583
- const yaml = yamlLines.join("\n");
583
+ const yaml2 = yamlLines.join("\n");
584
584
  return {
585
585
  content: [
586
586
  {
@@ -590,7 +590,7 @@ function registerDashboardTools(server2) {
590
590
  Layout: ${layout}
591
591
 
592
592
  \`\`\`yaml
593
- ${yaml}
593
+ ${yaml2}
594
594
  \`\`\`
595
595
 
596
596
  \u{1F4A1} Add this to pages/dashboard/content.yml and validate with stackwright_validate_pages.`
@@ -671,7 +671,7 @@ ${yaml}
671
671
  yamlLines.push(" action: navigate");
672
672
  yamlLines.push(` href: "/${entity}"`);
673
673
  yamlLines.push(" style: secondary");
674
- const yaml = yamlLines.join("\n");
674
+ const yaml2 = yamlLines.join("\n");
675
675
  return {
676
676
  content: [
677
677
  {
@@ -679,7 +679,7 @@ ${yaml}
679
679
  text: `\u{1F4C4} Generated Detail Page for: ${entity}
680
680
 
681
681
  \`\`\`yaml
682
- ${yaml}
682
+ ${yaml2}
683
683
  \`\`\`
684
684
 
685
685
  \u{1F4A1} Add this to pages/${entity}/[${slugField}]/content.yml and validate.`
@@ -2451,7 +2451,7 @@ function enrichErrors(issues, synonyms) {
2451
2451
  });
2452
2452
  }
2453
2453
  function handleValidateYamlFragment(input) {
2454
- const { schemaName, yaml } = input;
2454
+ const { schemaName, yaml: yaml2 } = input;
2455
2455
  if (!SUPPORTED_SCHEMA_NAMES.includes(schemaName)) {
2456
2456
  return {
2457
2457
  valid: false,
@@ -2461,7 +2461,7 @@ function handleValidateYamlFragment(input) {
2461
2461
  const name = schemaName;
2462
2462
  let parsed;
2463
2463
  try {
2464
- parsed = (0, import_js_yaml.load)(yaml);
2464
+ parsed = (0, import_js_yaml.load)(yaml2);
2465
2465
  } catch (err) {
2466
2466
  return {
2467
2467
  valid: false,
@@ -2487,8 +2487,8 @@ function registerValidateYamlFragmentTool(server2) {
2487
2487
  ),
2488
2488
  yaml: import_zod11.z.string().describe("YAML string to validate (can also be JSON \u2014 js-yaml parses both)")
2489
2489
  },
2490
- async ({ schemaName, yaml }) => {
2491
- const result = handleValidateYamlFragment({ schemaName, yaml });
2490
+ async ({ schemaName, yaml: yaml2 }) => {
2491
+ const result = handleValidateYamlFragment({ schemaName, yaml: yaml2 });
2492
2492
  return {
2493
2493
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
2494
2494
  };
@@ -2963,7 +2963,8 @@ function defaultPhaseStatus() {
2963
2963
  answered: false,
2964
2964
  executed: false,
2965
2965
  artifactWritten: false,
2966
- retryCount: 0
2966
+ retryCount: 0,
2967
+ inFlight: false
2967
2968
  };
2968
2969
  }
2969
2970
  function createDefaultState() {
@@ -3080,7 +3081,13 @@ function handleSetPipelineState(input) {
3080
3081
  isError: true
3081
3082
  };
3082
3083
  }
3083
- const VALID_FIELDS = ["questionsCollected", "answered", "executed", "artifactWritten"];
3084
+ const VALID_FIELDS = [
3085
+ "questionsCollected",
3086
+ "answered",
3087
+ "executed",
3088
+ "artifactWritten",
3089
+ "inFlight"
3090
+ ];
3084
3091
  if (input.field && !VALID_FIELDS.includes(input.field)) {
3085
3092
  return {
3086
3093
  text: JSON.stringify({
@@ -4139,6 +4146,14 @@ function handleEmitEvent(input) {
4139
4146
  };
4140
4147
  }
4141
4148
  emitted = emitFn({ type: "phase_start", phase: input.phase, otter: caller });
4149
+ } else if (input.type === "phase_ready") {
4150
+ if (!input.phase) {
4151
+ return {
4152
+ text: JSON.stringify({ emitted: false, reason: "phase required for phase_ready" }),
4153
+ isError: false
4154
+ };
4155
+ }
4156
+ emitted = emitFn({ type: "phase_ready", phase: input.phase, otter: caller });
4142
4157
  } else if (input.type === "phase_complete") {
4143
4158
  if (!input.phase) {
4144
4159
  return {
@@ -4179,23 +4194,24 @@ function handleEmitEvent(input) {
4179
4194
  }
4180
4195
  }
4181
4196
  function registerPipelineTools(server2) {
4182
- const DESC = "Writes state to .stackwright/ \u2014 the filesystem is the state machine.";
4197
+ const DESC2 = "Writes state to .stackwright/ \u2014 the filesystem is the state machine.";
4183
4198
  const res = (r) => ({
4184
4199
  content: [{ type: "text", text: r.text }],
4185
4200
  isError: r.isError
4186
4201
  });
4187
4202
  server2.tool(
4188
4203
  "stackwright_pro_get_pipeline_state",
4189
- `Read pipeline state from .stackwright/pipeline-state.json. ${DESC}`,
4204
+ `Read pipeline state from .stackwright/pipeline-state.json. ${DESC2}`,
4190
4205
  {},
4191
4206
  async () => res(handleGetPipelineState())
4192
4207
  );
4193
4208
  server2.tool(
4194
4209
  "stackwright_pro_set_pipeline_state",
4195
- `Atomic read\u2192modify\u2192write pipeline state. ${DESC}`,
4210
+ `Atomic read\u2192modify\u2192write pipeline state. ${DESC2}`,
4196
4211
  {
4197
4212
  phase: import_zod13.z.string().optional().describe('Phase to update, e.g. "designer"'),
4198
- field: import_zod13.z.enum(["questionsCollected", "answered", "executed", "artifactWritten"]).optional().describe("Boolean field to set"),
4213
+ field: import_zod13.z.enum(["questionsCollected", "answered", "executed", "artifactWritten", "inFlight"]).optional().describe("Boolean field to set"),
4214
+ // inFlight: set true BEFORE specialist invoke (dataflow mode, swp-ioc7); false after.
4199
4215
  value: boolCoerce(import_zod13.z.boolean().optional()).describe(
4200
4216
  'Value for the field \u2014 must be a JSON boolean (true/false), NOT the string "true"/"false"'
4201
4217
  ),
@@ -4207,7 +4223,13 @@ function registerPipelineTools(server2) {
4207
4223
  import_zod13.z.array(
4208
4224
  import_zod13.z.object({
4209
4225
  phase: import_zod13.z.string(),
4210
- field: import_zod13.z.enum(["questionsCollected", "answered", "executed", "artifactWritten"]),
4226
+ field: import_zod13.z.enum([
4227
+ "questionsCollected",
4228
+ "answered",
4229
+ "executed",
4230
+ "artifactWritten",
4231
+ "inFlight"
4232
+ ]),
4211
4233
  value: import_zod13.z.boolean()
4212
4234
  })
4213
4235
  ).optional()
@@ -4226,7 +4248,7 @@ function registerPipelineTools(server2) {
4226
4248
  );
4227
4249
  server2.tool(
4228
4250
  "stackwright_pro_check_execution_ready",
4229
- `Check all phases have answer files in .stackwright/answers/. If phase is provided, check only that phase. ${DESC}`,
4251
+ `Check all phases have answer files in .stackwright/answers/. If phase is provided, check only that phase. ${DESC2}`,
4230
4252
  {
4231
4253
  phase: import_zod13.z.string().optional().describe("If provided, check only this phase's readiness. Omit to check all phases.")
4232
4254
  },
@@ -4234,19 +4256,19 @@ function registerPipelineTools(server2) {
4234
4256
  );
4235
4257
  server2.tool(
4236
4258
  "stackwright_pro_get_ready_phases",
4237
- `Return phases whose dependencies are all satisfied (executed=true). Use to determine which phases can run next \u2014 enables wave-based execution. ${DESC}`,
4259
+ `Return phases whose dependencies are all satisfied (executed=true). Use to determine which phases can run next \u2014 enables wave-based execution. ${DESC2}`,
4238
4260
  {},
4239
4261
  async () => res(handleGetReadyPhases())
4240
4262
  );
4241
4263
  server2.tool(
4242
4264
  "stackwright_pro_list_artifacts",
4243
- `List phase artifacts in .stackwright/artifacts/ with completedCount/totalCount. ${DESC}`,
4265
+ `List phase artifacts in .stackwright/artifacts/ with completedCount/totalCount. ${DESC2}`,
4244
4266
  {},
4245
4267
  async () => res(handleListArtifacts())
4246
4268
  );
4247
4269
  server2.tool(
4248
4270
  "stackwright_pro_write_phase_questions",
4249
- `Parse otter question-collection response \u2192 .stackwright/questions/{phase}.json. Specialists may also call this directly with a parsed questions array. ${DESC}`,
4271
+ `Parse otter question-collection response \u2192 .stackwright/questions/{phase}.json. Specialists may also call this directly with a parsed questions array. ${DESC2}`,
4250
4272
  {
4251
4273
  phase: import_zod13.z.string().optional().describe('Phase name, e.g. "designer" (required for direct write)'),
4252
4274
  responseText: import_zod13.z.string().optional().describe("Raw LLM response from QUESTION_COLLECTION_MODE"),
@@ -4291,13 +4313,13 @@ function registerPipelineTools(server2) {
4291
4313
  );
4292
4314
  server2.tool(
4293
4315
  "stackwright_pro_build_specialist_prompt",
4294
- `Assemble execution prompt from answers + upstream artifacts. Foreman passes verbatim. ${DESC}`,
4316
+ `Assemble execution prompt from answers + upstream artifacts. Foreman passes verbatim. ${DESC2}`,
4295
4317
  { phase: import_zod13.z.string().describe('Phase to build prompt for, e.g. "pages"') },
4296
4318
  async ({ phase }) => res(handleBuildSpecialistPrompt({ phase }))
4297
4319
  );
4298
4320
  server2.tool(
4299
4321
  "stackwright_pro_validate_artifact",
4300
- `Validate and write artifact to .stackwright/artifacts/. Returns retryPrompt on failure. ${DESC}`,
4322
+ `Validate and write artifact to .stackwright/artifacts/. Returns retryPrompt on failure. ${DESC2}`,
4301
4323
  {
4302
4324
  phase: import_zod13.z.string().describe('Phase that produced this artifact, e.g. "designer"'),
4303
4325
  responseText: import_zod13.z.string().optional().describe("Raw response text from the specialist otter (Foreman-mediated path, legacy)"),
@@ -4329,9 +4351,17 @@ function registerPipelineTools(server2) {
4329
4351
  );
4330
4352
  server2.tool(
4331
4353
  "stackwright_pro_emit_event",
4332
- `FOREMAN ONLY \u2014 specialist otters that call this will receive an authorization rejection (no-op, not an error). Emit a telemetry event to .stackwright/pipeline-events.ndjson. Foreman calls this at phase boundaries and agent-invoke boundaries. Best-effort: failures are silent and never block. ${DESC}`,
4354
+ `FOREMAN ONLY \u2014 specialist otters that call this will receive an authorization rejection (no-op, not an error). Emit a telemetry event to .stackwright/pipeline-events.ndjson. Foreman calls this at phase boundaries and agent-invoke boundaries. Best-effort: failures are silent and never block. ${DESC2}`,
4333
4355
  {
4334
- type: import_zod13.z.enum(["phase_start", "phase_complete", "agent_invoke_start", "agent_invoke_complete"]).describe("Event type to emit"),
4356
+ type: import_zod13.z.enum([
4357
+ "phase_start",
4358
+ "phase_complete",
4359
+ "phase_ready",
4360
+ "agent_invoke_start",
4361
+ "agent_invoke_complete"
4362
+ ]).describe("Event type to emit"),
4363
+ // phase_ready: emitted in dataflow mode (swp-ioc7) when a phase enters the ready set
4364
+ // (deps satisfied, not inFlight, not yet complete) — BEFORE invoking the specialist.
4335
4365
  phase: import_zod13.z.string().optional().describe("Current phase name"),
4336
4366
  targetOtter: import_zod13.z.string().optional().describe("For agent_invoke_* events: which otter is being invoked"),
4337
4367
  success: boolCoerce(import_zod13.z.boolean().optional()).describe(
@@ -4442,7 +4472,17 @@ var OTTER_WRITE_ALLOWLISTS = {
4442
4472
  { prefix: "pages/", suffix: "/content.yml", description: "Landing page content" },
4443
4473
  { prefix: "pages/", suffix: "/content.yaml", description: "Landing page content" },
4444
4474
  { prefix: ".stackwright/artifacts/", suffix: ".json", description: "Polish artifact" },
4445
- { prefix: "README.md", suffix: "", description: "Project README rewrite" }
4475
+ { prefix: "README.md", suffix: "", description: "Project README rewrite" },
4476
+ {
4477
+ prefix: ".env.local",
4478
+ suffix: "",
4479
+ description: ".env.local runtime config \u2014 emitter output from stackwright_pro_emit_env_local (swp-8qdd)"
4480
+ },
4481
+ {
4482
+ prefix: ".env.local.example",
4483
+ suffix: "",
4484
+ description: ".env.local.example template \u2014 emitter output from stackwright_pro_emit_env_local (swp-8qdd)"
4485
+ }
4446
4486
  ],
4447
4487
  // QA otter writes ONLY to the qa/ directory and the qa-findings artifact.
4448
4488
  // It never writes .tsx, .ts, .yml, or .json outside these paths.
@@ -4810,10 +4850,10 @@ function handleSafeWrite(input) {
4810
4850
  return result;
4811
4851
  }
4812
4852
  function registerSafeWriteTools(server2) {
4813
- const DESC = "Controlled file-write chokepoint. Every write from specialist otters goes through this tool with per-otter path allowlists. The LLM cannot write to arbitrary filesystem paths.";
4853
+ const DESC2 = "Controlled file-write chokepoint. Every write from specialist otters goes through this tool with per-otter path allowlists. The LLM cannot write to arbitrary filesystem paths.";
4814
4854
  server2.tool(
4815
4855
  "stackwright_pro_safe_write",
4816
- DESC,
4856
+ DESC2,
4817
4857
  {
4818
4858
  callerOtter: import_zod14.z.string().describe('The otter agent name requesting the write, e.g. "stackwright-pro-page-otter"'),
4819
4859
  filePath: import_zod14.z.string().describe('Relative path from project root, e.g. "pages/dashboard/content.yml"'),
@@ -5781,7 +5821,7 @@ var _checksums = /* @__PURE__ */ new Map([
5781
5821
  ],
5782
5822
  [
5783
5823
  "stackwright-pro-foreman-otter.json",
5784
- "9191e176241c15f3d77180e1b843cc3a517b708737b5bd3cda7db62f2d6675d1"
5824
+ "c23436769a9db06fa6f9d312b4fdd8b2cef6646461020eeaf227303350073593"
5785
5825
  ],
5786
5826
  [
5787
5827
  "stackwright-pro-form-wizard-otter.json",
@@ -5797,7 +5837,7 @@ var _checksums = /* @__PURE__ */ new Map([
5797
5837
  ],
5798
5838
  [
5799
5839
  "stackwright-pro-polish-otter.json",
5800
- "9d0c53651b60507607efc0d9a0f9d166bf5ae8571f1645682e0b151d42c276ab"
5840
+ "fd8f8963266c6179eebf8eac4f656e8274aa3a721e5296abdbde5b00ad8a2297"
5801
5841
  ],
5802
5842
  [
5803
5843
  "stackwright-pro-qa-otter.json",
@@ -5805,7 +5845,7 @@ var _checksums = /* @__PURE__ */ new Map([
5805
5845
  ],
5806
5846
  [
5807
5847
  "stackwright-pro-scaffold-otter.json",
5808
- "5756ea77178334684b4df1d9d3395bca62cedb4991ab07dc4cf61aa7e540ea14"
5848
+ "c2ae10733d8bd66bd965b29a77193b0abf0ea6765cd02886df1413bf598e0588"
5809
5849
  ],
5810
5850
  [
5811
5851
  "stackwright-pro-theme-otter.json",
@@ -6877,10 +6917,10 @@ function handleCleanupScaffold(_cwd) {
6877
6917
  };
6878
6918
  }
6879
6919
  function registerScaffoldCleanupTools(server2) {
6880
- const DESC = "Deterministic scaffold remnant cleanup \u2014 removes stale OSS scaffold files after raft run.";
6920
+ const DESC2 = "Deterministic scaffold remnant cleanup \u2014 removes stale OSS scaffold files after raft run.";
6881
6921
  server2.tool(
6882
6922
  "stackwright_pro_cleanup_scaffold",
6883
- `Remove/rewrite stale scaffold files (blog posts, hardcoded not-found page) after the raft pipeline completes. ${DESC}`,
6923
+ `Remove/rewrite stale scaffold files (blog posts, hardcoded not-found page) after the raft pipeline completes. ${DESC2}`,
6884
6924
  {},
6885
6925
  async () => {
6886
6926
  const r = handleCleanupScaffold();
@@ -7448,14 +7488,104 @@ function registerStripLegacyIntegrationsTool(server2) {
7448
7488
  );
7449
7489
  }
7450
7490
 
7451
- // src/tools/list-specs.ts
7491
+ // src/tools/emitters.ts
7452
7492
  var import_zod22 = require("zod");
7453
7493
  var import_fs14 = require("fs");
7454
7494
  var import_path14 = require("path");
7495
+ var import_js_yaml3 = __toESM(require("js-yaml"));
7496
+ var import_emitters = require("@stackwright-pro/emitters");
7497
+ function readIntegrations(cwd) {
7498
+ const filePath = (0, import_path14.join)(cwd, "stackwright.integrations.yml");
7499
+ if (!(0, import_fs14.existsSync)(filePath)) return [];
7500
+ try {
7501
+ const raw = import_js_yaml3.default.load((0, import_fs14.readFileSync)(filePath, "utf-8"));
7502
+ if (!raw?.integrations || !Array.isArray(raw.integrations)) return [];
7503
+ return raw.integrations.filter((i) => typeof i.name === "string").map((i) => ({
7504
+ name: i.name,
7505
+ auth: i.auth ? { type: i.auth.type, header: i.auth.header, envVar: i.auth.envVar } : void 0
7506
+ }));
7507
+ } catch {
7508
+ return [];
7509
+ }
7510
+ }
7511
+ function readServiceTokenRefs(cwd) {
7512
+ const servicesDir = (0, import_path14.join)(cwd, "services");
7513
+ if (!(0, import_fs14.existsSync)(servicesDir)) return [];
7514
+ try {
7515
+ const files = (0, import_fs14.readdirSync)(servicesDir).filter((f) => f.endsWith(".yml") || f.endsWith(".yaml"));
7516
+ const refs = [];
7517
+ for (const file of files) {
7518
+ try {
7519
+ const raw = import_js_yaml3.default.load(
7520
+ (0, import_fs14.readFileSync)((0, import_path14.join)(servicesDir, file), "utf-8")
7521
+ );
7522
+ if (raw?.tokenEnv && typeof raw.tokenEnv === "string") {
7523
+ refs.push({
7524
+ name: raw.name ?? file.replace(/\.ya?ml$/, ""),
7525
+ tokenEnv: raw.tokenEnv
7526
+ });
7527
+ }
7528
+ } catch {
7529
+ }
7530
+ }
7531
+ return refs;
7532
+ } catch {
7533
+ return [];
7534
+ }
7535
+ }
7536
+ var DESC = "Returns { envLocal, envLocalExample } as JSON. The otter writes both files via stackwright_pro_safe_write.";
7537
+ function registerEmitterTools(server2) {
7538
+ server2.tool(
7539
+ "stackwright_pro_emit_env_local",
7540
+ `Emit .env.local + .env.local.example contents from stackwright.integrations.yml and services/*.yml. Deterministic emitter \u2014 same project config produces byte-identical output.
7541
+
7542
+ Call this tool, then write both files with stackwright_pro_safe_write:
7543
+ 1. stackwright_pro_emit_env_local({ cwd }) \u2192 { envLocal, envLocalExample }
7544
+ 2. stackwright_pro_safe_write({ filePath: '.env.local', content: envLocal, ... })
7545
+ 3. stackwright_pro_safe_write({ filePath: '.env.local.example', content: envLocalExample, ... })
7546
+
7547
+ Do NOT compose the contents by hand. This emitter is the source of truth. If an env var is missing, update integrations.yml or the emitter \u2014 not the file. This is the first concrete instance of the swp-zf9y deterministic emitter pattern. ${DESC}`,
7548
+ {
7549
+ cwd: import_zod22.z.string().describe(
7550
+ "Absolute path to the project root containing stackwright.integrations.yml and optionally services/*.yml"
7551
+ )
7552
+ },
7553
+ async ({ cwd }) => {
7554
+ const integrations = readIntegrations(cwd);
7555
+ const services = readServiceTokenRefs(cwd);
7556
+ const output = (0, import_emitters.emitEnvLocal)({ integrations, services });
7557
+ return {
7558
+ content: [
7559
+ {
7560
+ type: "text",
7561
+ text: JSON.stringify(
7562
+ {
7563
+ envLocal: output.envLocal,
7564
+ envLocalExample: output.envLocalExample,
7565
+ meta: {
7566
+ integrationsRead: integrations.length,
7567
+ serviceTokenRefsRead: services.length,
7568
+ envVarsEmitted: (output.envLocal.match(/^NEXT_PUBLIC_[A-Z0-9_]+=.+$/m) ? output.envLocal.match(/^NEXT_PUBLIC_[A-Z0-9_]+=.+$/gm) ?? [] : []).length
7569
+ }
7570
+ },
7571
+ null,
7572
+ 2
7573
+ )
7574
+ }
7575
+ ]
7576
+ };
7577
+ }
7578
+ );
7579
+ }
7580
+
7581
+ // src/tools/list-specs.ts
7582
+ var import_zod23 = require("zod");
7583
+ var import_fs15 = require("fs");
7584
+ var import_path15 = require("path");
7455
7585
  var STRIP_SUFFIXES = ["-openapi", "-asyncapi", "-api", "-spec"];
7456
7586
  var ALLOWED_EXTS = /* @__PURE__ */ new Set([".yaml", ".yml", ".json"]);
7457
7587
  function deriveIntegrationName(fileName) {
7458
- let name = (0, import_path14.basename)(fileName, (0, import_path14.extname)(fileName));
7588
+ let name = (0, import_path15.basename)(fileName, (0, import_path15.extname)(fileName));
7459
7589
  for (const suffix of STRIP_SUFFIXES) {
7460
7590
  if (name.endsWith(suffix)) {
7461
7591
  name = name.slice(0, -suffix.length);
@@ -7473,29 +7603,29 @@ function detectFormat(snippet) {
7473
7603
  }
7474
7604
  function handleListSpecs(input) {
7475
7605
  const { projectRoot } = input;
7476
- const specsDir = (0, import_path14.join)(projectRoot, "specs");
7606
+ const specsDir = (0, import_path15.join)(projectRoot, "specs");
7477
7607
  const empty = { projectRoot, specs: [], totalCount: 0 };
7478
- if (!(0, import_fs14.existsSync)(specsDir)) return empty;
7608
+ if (!(0, import_fs15.existsSync)(specsDir)) return empty;
7479
7609
  let entries;
7480
7610
  try {
7481
- entries = (0, import_fs14.readdirSync)(specsDir);
7611
+ entries = (0, import_fs15.readdirSync)(specsDir);
7482
7612
  } catch {
7483
7613
  return empty;
7484
7614
  }
7485
7615
  const specs = [];
7486
7616
  for (const entry of entries) {
7487
- const fullPath = (0, import_path14.join)(specsDir, entry);
7617
+ const fullPath = (0, import_path15.join)(specsDir, entry);
7488
7618
  let stat;
7489
7619
  try {
7490
- stat = (0, import_fs14.statSync)(fullPath);
7620
+ stat = (0, import_fs15.statSync)(fullPath);
7491
7621
  } catch {
7492
7622
  continue;
7493
7623
  }
7494
7624
  if (!stat.isFile()) continue;
7495
- if (!ALLOWED_EXTS.has((0, import_path14.extname)(entry).toLowerCase())) continue;
7625
+ if (!ALLOWED_EXTS.has((0, import_path15.extname)(entry).toLowerCase())) continue;
7496
7626
  let snippet = "";
7497
7627
  try {
7498
- snippet = (0, import_fs14.readFileSync)(fullPath, "utf-8").slice(0, 2048);
7628
+ snippet = (0, import_fs15.readFileSync)(fullPath, "utf-8").slice(0, 2048);
7499
7629
  } catch {
7500
7630
  }
7501
7631
  specs.push({
@@ -7514,7 +7644,7 @@ function registerListSpecsTool(server2) {
7514
7644
  "stackwright_pro_list_specs",
7515
7645
  "Enumerate OpenAPI/AsyncAPI specs in the project's specs/ directory. Returns one entry per spec with integration name (derived from filename), detected format, and size. Used by the foreman to fan out the api phase with one api-otter invocation per spec.",
7516
7646
  {
7517
- projectRoot: import_zod22.z.string().describe("Absolute path to the project root directory")
7647
+ projectRoot: import_zod23.z.string().describe("Absolute path to the project root directory")
7518
7648
  },
7519
7649
  async ({ projectRoot }) => {
7520
7650
  const result = handleListSpecs({ projectRoot });
@@ -7531,6 +7661,7 @@ var package_default = {
7531
7661
  "@modelcontextprotocol/sdk": "^1.10.0",
7532
7662
  "@stackwright/cli": "^0.9.0",
7533
7663
  "@stackwright-pro/auth-nextjs": "workspace:*",
7664
+ "@stackwright-pro/emitters": "workspace:*",
7534
7665
  "@stackwright-pro/cli-data-explorer": "workspace:*",
7535
7666
  "@stackwright-pro/openapi": "workspace:*",
7536
7667
  "@stackwright-pro/pulse": "workspace:*",
@@ -7558,7 +7689,7 @@ var package_default = {
7558
7689
  "test:coverage": "vitest run --coverage"
7559
7690
  },
7560
7691
  name: "@stackwright-pro/mcp",
7561
- version: "0.2.0-alpha.101",
7692
+ version: "0.2.0-alpha.102",
7562
7693
  description: "MCP tools for Stackwright Pro - Data Explorer, Security, ISR, and Dashboard generation",
7563
7694
  license: "SEE LICENSE IN LICENSE",
7564
7695
  main: "./dist/server.js",
@@ -7625,6 +7756,7 @@ registerScaffoldCleanupTools(server);
7625
7756
  registerContrastTools(server);
7626
7757
  registerCompileTools(server);
7627
7758
  registerStripLegacyIntegrationsTool(server);
7759
+ registerEmitterTools(server);
7628
7760
  registerListSpecsTool(server);
7629
7761
  (0, import_register.registerContentTypeTools)(server);
7630
7762
  (0, import_register.registerPageTools)(server);