@stackwright-pro/mcp 0.2.0-alpha.101 → 0.2.0-alpha.103
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/integrity.js +5 -5
- package/dist/integrity.js.map +1 -1
- package/dist/integrity.mjs +5 -5
- package/dist/integrity.mjs.map +1 -1
- package/dist/pipeline-constants.js.map +1 -1
- package/dist/pipeline-constants.mjs.map +1 -1
- package/dist/server.js +249 -30
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +248 -29
- package/dist/server.mjs.map +1 -1
- package/package.json +5 -4
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
|
|
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
|
-
${
|
|
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
|
|
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
|
-
${
|
|
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)(
|
|
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 = [
|
|
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 {
|
|
@@ -4195,7 +4210,8 @@ function registerPipelineTools(server2) {
|
|
|
4195
4210
|
`Atomic read\u2192modify\u2192write pipeline state. ${DESC}`,
|
|
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([
|
|
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()
|
|
@@ -4331,7 +4353,15 @@ function registerPipelineTools(server2) {
|
|
|
4331
4353
|
"stackwright_pro_emit_event",
|
|
4332
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. ${DESC}`,
|
|
4333
4355
|
{
|
|
4334
|
-
type: import_zod13.z.enum([
|
|
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.
|
|
@@ -5757,7 +5797,7 @@ var import_path9 = require("path");
|
|
|
5757
5797
|
var _checksums = /* @__PURE__ */ new Map([
|
|
5758
5798
|
[
|
|
5759
5799
|
"stackwright-pro-api-otter.json",
|
|
5760
|
-
"
|
|
5800
|
+
"a5ff25531c330289af89e433e696ba1ffef8f7ea09985a559cf915592c9734ad"
|
|
5761
5801
|
],
|
|
5762
5802
|
[
|
|
5763
5803
|
"stackwright-pro-auth-otter.json",
|
|
@@ -5765,7 +5805,7 @@ var _checksums = /* @__PURE__ */ new Map([
|
|
|
5765
5805
|
],
|
|
5766
5806
|
[
|
|
5767
5807
|
"stackwright-pro-dashboard-otter.json",
|
|
5768
|
-
"
|
|
5808
|
+
"f8d594b14e05efea30cf253052d7c7df3e8c4174d739be4cd33f9c96cecdc451"
|
|
5769
5809
|
],
|
|
5770
5810
|
[
|
|
5771
5811
|
"stackwright-pro-data-otter.json",
|
|
@@ -5781,7 +5821,7 @@ var _checksums = /* @__PURE__ */ new Map([
|
|
|
5781
5821
|
],
|
|
5782
5822
|
[
|
|
5783
5823
|
"stackwright-pro-foreman-otter.json",
|
|
5784
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
5848
|
+
"92930c732547c90a19e89ee0e25b5f037b7366f770941f0a01e148ff240a1b6d"
|
|
5809
5849
|
],
|
|
5810
5850
|
[
|
|
5811
5851
|
"stackwright-pro-theme-otter.json",
|
|
@@ -7448,14 +7488,191 @@ function registerStripLegacyIntegrationsTool(server2) {
|
|
|
7448
7488
|
);
|
|
7449
7489
|
}
|
|
7450
7490
|
|
|
7451
|
-
// src/tools/
|
|
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 ENV_LOCAL_DESC = "Returns { envLocal, envLocalExample } as JSON. The otter writes both files via stackwright_pro_safe_write.";
|
|
7537
|
+
var PROVIDERS_DESC = "Returns { providersFile } as JSON. The otter writes the file via stackwright_pro_safe_write.";
|
|
7538
|
+
var PORTS_DESC = "Returns { ports } as JSON \u2014 map of integration name \u2192 port number.";
|
|
7539
|
+
function registerEmitterTools(server2) {
|
|
7540
|
+
server2.tool(
|
|
7541
|
+
"stackwright_pro_emit_env_local",
|
|
7542
|
+
`Emit .env.local + .env.local.example contents from stackwright.integrations.yml and services/*.yml. Deterministic emitter \u2014 same project config produces byte-identical output.
|
|
7543
|
+
|
|
7544
|
+
Call this tool, then write both files with stackwright_pro_safe_write:
|
|
7545
|
+
1. stackwright_pro_emit_env_local({ cwd }) \u2192 { envLocal, envLocalExample }
|
|
7546
|
+
2. stackwright_pro_safe_write({ filePath: '.env.local', content: envLocal, ... })
|
|
7547
|
+
3. stackwright_pro_safe_write({ filePath: '.env.local.example', content: envLocalExample, ... })
|
|
7548
|
+
|
|
7549
|
+
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. ${ENV_LOCAL_DESC}`,
|
|
7550
|
+
{
|
|
7551
|
+
cwd: import_zod22.z.string().describe(
|
|
7552
|
+
"Absolute path to the project root containing stackwright.integrations.yml and optionally services/*.yml"
|
|
7553
|
+
)
|
|
7554
|
+
},
|
|
7555
|
+
async ({ cwd }) => {
|
|
7556
|
+
const integrations = readIntegrations(cwd);
|
|
7557
|
+
const services = readServiceTokenRefs(cwd);
|
|
7558
|
+
const output = (0, import_emitters.emitEnvLocal)({ integrations, services });
|
|
7559
|
+
return {
|
|
7560
|
+
content: [
|
|
7561
|
+
{
|
|
7562
|
+
type: "text",
|
|
7563
|
+
text: JSON.stringify(
|
|
7564
|
+
{
|
|
7565
|
+
envLocal: output.envLocal,
|
|
7566
|
+
envLocalExample: output.envLocalExample,
|
|
7567
|
+
meta: {
|
|
7568
|
+
integrationsRead: integrations.length,
|
|
7569
|
+
serviceTokenRefsRead: services.length,
|
|
7570
|
+
envVarsEmitted: (output.envLocal.match(/^NEXT_PUBLIC_[A-Z0-9_]+=.+$/m) ? output.envLocal.match(/^NEXT_PUBLIC_[A-Z0-9_]+=.+$/gm) ?? [] : []).length
|
|
7571
|
+
}
|
|
7572
|
+
},
|
|
7573
|
+
null,
|
|
7574
|
+
2
|
|
7575
|
+
)
|
|
7576
|
+
}
|
|
7577
|
+
]
|
|
7578
|
+
};
|
|
7579
|
+
}
|
|
7580
|
+
);
|
|
7581
|
+
server2.tool(
|
|
7582
|
+
"stackwright_pro_emit_providers_file",
|
|
7583
|
+
`Emit app/_components/providers.tsx from stackwright.integrations.yml. Deterministic emitter \u2014 uses LITERAL env access per integration so Next.js can inline NEXT_PUBLIC_* vars at build time. Also registers the map provider (default: Cesium for Pro).
|
|
7584
|
+
|
|
7585
|
+
\u26A0\uFE0F CRITICAL: DO NOT compose providers.tsx by hand. Computed env access (process.env[varName]) returns undefined in browser bundles for NEXT_PUBLIC_* vars \u2014 this silently drops all auth headers and causes 401s on every integration.
|
|
7586
|
+
|
|
7587
|
+
Workflow:
|
|
7588
|
+
1. stackwright_pro_emit_providers_file({ cwd, mapProvider: 'cesium' }) \u2192 { providersFile }
|
|
7589
|
+
2. stackwright_pro_safe_write({ filePath: 'app/_components/providers.tsx', content: providersFile, ... })
|
|
7590
|
+
|
|
7591
|
+
mapProvider options:
|
|
7592
|
+
- 'cesium' (default for Pro): 3D globe + terrain, @stackwright-pro/cesium + cesium peer dep
|
|
7593
|
+
- 'maplibre': free-tier 2D maps, no API key \u2014 OSS-style 2D dashboards only (opt-out)
|
|
7594
|
+
- 'none': skip map registration (ONLY if no pages use map content_items)
|
|
7595
|
+
|
|
7596
|
+
This is the second concrete instance of the swp-zf9y deterministic emitter pattern. Refs: swp-57ku, swp-2m89, swp-zf9y, drawer 957. ${PROVIDERS_DESC}`,
|
|
7597
|
+
{
|
|
7598
|
+
cwd: import_zod22.z.string().describe("Absolute path to the project root containing stackwright.integrations.yml"),
|
|
7599
|
+
mapProvider: import_zod22.z.enum(["maplibre", "cesium", "none"]).optional().describe(
|
|
7600
|
+
"Map provider to register (default: cesium for Pro \u2014 3D globe + terrain). Use maplibre for OSS-style 2D dashboards (explicit opt-out). Use none if pages have no map content_items."
|
|
7601
|
+
)
|
|
7602
|
+
},
|
|
7603
|
+
async ({ cwd, mapProvider }) => {
|
|
7604
|
+
const integrations = readIntegrations(cwd);
|
|
7605
|
+
const providersFile = (0, import_emitters.emitProvidersFile)({ integrations, mapProvider });
|
|
7606
|
+
return {
|
|
7607
|
+
content: [
|
|
7608
|
+
{
|
|
7609
|
+
type: "text",
|
|
7610
|
+
text: JSON.stringify(
|
|
7611
|
+
{
|
|
7612
|
+
providersFile,
|
|
7613
|
+
meta: {
|
|
7614
|
+
integrationsRead: integrations.length,
|
|
7615
|
+
integrationsWithAuth: integrations.filter((i) => i.auth?.envVar).length,
|
|
7616
|
+
mapProvider: mapProvider ?? "cesium"
|
|
7617
|
+
}
|
|
7618
|
+
},
|
|
7619
|
+
null,
|
|
7620
|
+
2
|
|
7621
|
+
)
|
|
7622
|
+
}
|
|
7623
|
+
]
|
|
7624
|
+
};
|
|
7625
|
+
}
|
|
7626
|
+
);
|
|
7627
|
+
server2.tool(
|
|
7628
|
+
"stackwright_pro_emit_mock_ports",
|
|
7629
|
+
`Emit mock backend port assignments from stackwright.integrations.yml. Deterministic emitter \u2014 assigns consecutive ports starting at 4011 to each integration in declaration order. Skipped integrations (skipMockPort=true) are omitted and do not consume a port number.
|
|
7630
|
+
|
|
7631
|
+
\u26A0\uFE0F DO NOT pick mockUrl port numbers yourself. Port collisions occur when different models pick different defaults (Opus always picked 4010 for all integrations in run-4).
|
|
7632
|
+
|
|
7633
|
+
Workflow:
|
|
7634
|
+
1. stackwright_pro_emit_mock_ports({ cwd }) \u2192 { ports }
|
|
7635
|
+
2. For each integration, set mockUrl to http://localhost:<ports[name]>
|
|
7636
|
+
3. Write stackwright.integrations.yml via stackwright_pro_safe_write
|
|
7637
|
+
|
|
7638
|
+
This is the third concrete instance of the swp-zf9y deterministic emitter pattern. Refs: swp-phkh, swp-zf9y. ${PORTS_DESC}`,
|
|
7639
|
+
{
|
|
7640
|
+
cwd: import_zod22.z.string().describe("Absolute path to the project root containing stackwright.integrations.yml")
|
|
7641
|
+
},
|
|
7642
|
+
async ({ cwd }) => {
|
|
7643
|
+
const integrations = readIntegrations(cwd);
|
|
7644
|
+
const { ports } = (0, import_emitters.emitMockPorts)({ integrations });
|
|
7645
|
+
return {
|
|
7646
|
+
content: [
|
|
7647
|
+
{
|
|
7648
|
+
type: "text",
|
|
7649
|
+
text: JSON.stringify(
|
|
7650
|
+
{
|
|
7651
|
+
ports,
|
|
7652
|
+
meta: {
|
|
7653
|
+
integrationsRead: integrations.length,
|
|
7654
|
+
portsAssigned: Object.keys(ports).length,
|
|
7655
|
+
portRange: Object.keys(ports).length > 0 ? `${Math.min(...Object.values(ports))}\u2013${Math.max(...Object.values(ports))}` : "none"
|
|
7656
|
+
}
|
|
7657
|
+
},
|
|
7658
|
+
null,
|
|
7659
|
+
2
|
|
7660
|
+
)
|
|
7661
|
+
}
|
|
7662
|
+
]
|
|
7663
|
+
};
|
|
7664
|
+
}
|
|
7665
|
+
);
|
|
7666
|
+
}
|
|
7667
|
+
|
|
7668
|
+
// src/tools/list-specs.ts
|
|
7669
|
+
var import_zod23 = require("zod");
|
|
7670
|
+
var import_fs15 = require("fs");
|
|
7671
|
+
var import_path15 = require("path");
|
|
7455
7672
|
var STRIP_SUFFIXES = ["-openapi", "-asyncapi", "-api", "-spec"];
|
|
7456
7673
|
var ALLOWED_EXTS = /* @__PURE__ */ new Set([".yaml", ".yml", ".json"]);
|
|
7457
7674
|
function deriveIntegrationName(fileName) {
|
|
7458
|
-
let name = (0,
|
|
7675
|
+
let name = (0, import_path15.basename)(fileName, (0, import_path15.extname)(fileName));
|
|
7459
7676
|
for (const suffix of STRIP_SUFFIXES) {
|
|
7460
7677
|
if (name.endsWith(suffix)) {
|
|
7461
7678
|
name = name.slice(0, -suffix.length);
|
|
@@ -7473,29 +7690,29 @@ function detectFormat(snippet) {
|
|
|
7473
7690
|
}
|
|
7474
7691
|
function handleListSpecs(input) {
|
|
7475
7692
|
const { projectRoot } = input;
|
|
7476
|
-
const specsDir = (0,
|
|
7693
|
+
const specsDir = (0, import_path15.join)(projectRoot, "specs");
|
|
7477
7694
|
const empty = { projectRoot, specs: [], totalCount: 0 };
|
|
7478
|
-
if (!(0,
|
|
7695
|
+
if (!(0, import_fs15.existsSync)(specsDir)) return empty;
|
|
7479
7696
|
let entries;
|
|
7480
7697
|
try {
|
|
7481
|
-
entries = (0,
|
|
7698
|
+
entries = (0, import_fs15.readdirSync)(specsDir);
|
|
7482
7699
|
} catch {
|
|
7483
7700
|
return empty;
|
|
7484
7701
|
}
|
|
7485
7702
|
const specs = [];
|
|
7486
7703
|
for (const entry of entries) {
|
|
7487
|
-
const fullPath = (0,
|
|
7704
|
+
const fullPath = (0, import_path15.join)(specsDir, entry);
|
|
7488
7705
|
let stat;
|
|
7489
7706
|
try {
|
|
7490
|
-
stat = (0,
|
|
7707
|
+
stat = (0, import_fs15.statSync)(fullPath);
|
|
7491
7708
|
} catch {
|
|
7492
7709
|
continue;
|
|
7493
7710
|
}
|
|
7494
7711
|
if (!stat.isFile()) continue;
|
|
7495
|
-
if (!ALLOWED_EXTS.has((0,
|
|
7712
|
+
if (!ALLOWED_EXTS.has((0, import_path15.extname)(entry).toLowerCase())) continue;
|
|
7496
7713
|
let snippet = "";
|
|
7497
7714
|
try {
|
|
7498
|
-
snippet = (0,
|
|
7715
|
+
snippet = (0, import_fs15.readFileSync)(fullPath, "utf-8").slice(0, 2048);
|
|
7499
7716
|
} catch {
|
|
7500
7717
|
}
|
|
7501
7718
|
specs.push({
|
|
@@ -7514,7 +7731,7 @@ function registerListSpecsTool(server2) {
|
|
|
7514
7731
|
"stackwright_pro_list_specs",
|
|
7515
7732
|
"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
7733
|
{
|
|
7517
|
-
projectRoot:
|
|
7734
|
+
projectRoot: import_zod23.z.string().describe("Absolute path to the project root directory")
|
|
7518
7735
|
},
|
|
7519
7736
|
async ({ projectRoot }) => {
|
|
7520
7737
|
const result = handleListSpecs({ projectRoot });
|
|
@@ -7531,6 +7748,7 @@ var package_default = {
|
|
|
7531
7748
|
"@modelcontextprotocol/sdk": "^1.10.0",
|
|
7532
7749
|
"@stackwright/cli": "^0.9.0",
|
|
7533
7750
|
"@stackwright-pro/auth-nextjs": "workspace:*",
|
|
7751
|
+
"@stackwright-pro/emitters": "workspace:*",
|
|
7534
7752
|
"@stackwright-pro/cli-data-explorer": "workspace:*",
|
|
7535
7753
|
"@stackwright-pro/openapi": "workspace:*",
|
|
7536
7754
|
"@stackwright-pro/pulse": "workspace:*",
|
|
@@ -7558,7 +7776,7 @@ var package_default = {
|
|
|
7558
7776
|
"test:coverage": "vitest run --coverage"
|
|
7559
7777
|
},
|
|
7560
7778
|
name: "@stackwright-pro/mcp",
|
|
7561
|
-
version: "0.2.0-alpha.
|
|
7779
|
+
version: "0.2.0-alpha.103",
|
|
7562
7780
|
description: "MCP tools for Stackwright Pro - Data Explorer, Security, ISR, and Dashboard generation",
|
|
7563
7781
|
license: "SEE LICENSE IN LICENSE",
|
|
7564
7782
|
main: "./dist/server.js",
|
|
@@ -7625,6 +7843,7 @@ registerScaffoldCleanupTools(server);
|
|
|
7625
7843
|
registerContrastTools(server);
|
|
7626
7844
|
registerCompileTools(server);
|
|
7627
7845
|
registerStripLegacyIntegrationsTool(server);
|
|
7846
|
+
registerEmitterTools(server);
|
|
7628
7847
|
registerListSpecsTool(server);
|
|
7629
7848
|
(0, import_register.registerContentTypeTools)(server);
|
|
7630
7849
|
(0, import_register.registerPageTools)(server);
|