@stackwright-pro/mcp 0.2.0-alpha.100 → 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/integrity.js +4 -4
- package/dist/integrity.js.map +1 -1
- package/dist/integrity.mjs +4 -4
- 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 +240 -89
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +239 -88
- package/dist/server.mjs.map +1 -1
- package/package.json +5 -4
package/dist/server.mjs
CHANGED
|
@@ -556,7 +556,7 @@ function registerDashboardTools(server2) {
|
|
|
556
556
|
yamlLines.push(` header: Status`);
|
|
557
557
|
yamlLines.push(` type: badge`);
|
|
558
558
|
}
|
|
559
|
-
const
|
|
559
|
+
const yaml2 = yamlLines.join("\n");
|
|
560
560
|
return {
|
|
561
561
|
content: [
|
|
562
562
|
{
|
|
@@ -566,7 +566,7 @@ function registerDashboardTools(server2) {
|
|
|
566
566
|
Layout: ${layout}
|
|
567
567
|
|
|
568
568
|
\`\`\`yaml
|
|
569
|
-
${
|
|
569
|
+
${yaml2}
|
|
570
570
|
\`\`\`
|
|
571
571
|
|
|
572
572
|
\u{1F4A1} Add this to pages/dashboard/content.yml and validate with stackwright_validate_pages.`
|
|
@@ -647,7 +647,7 @@ ${yaml}
|
|
|
647
647
|
yamlLines.push(" action: navigate");
|
|
648
648
|
yamlLines.push(` href: "/${entity}"`);
|
|
649
649
|
yamlLines.push(" style: secondary");
|
|
650
|
-
const
|
|
650
|
+
const yaml2 = yamlLines.join("\n");
|
|
651
651
|
return {
|
|
652
652
|
content: [
|
|
653
653
|
{
|
|
@@ -655,7 +655,7 @@ ${yaml}
|
|
|
655
655
|
text: `\u{1F4C4} Generated Detail Page for: ${entity}
|
|
656
656
|
|
|
657
657
|
\`\`\`yaml
|
|
658
|
-
${
|
|
658
|
+
${yaml2}
|
|
659
659
|
\`\`\`
|
|
660
660
|
|
|
661
661
|
\u{1F4A1} Add this to pages/${entity}/[${slugField}]/content.yml and validate.`
|
|
@@ -2455,7 +2455,7 @@ function enrichErrors(issues, synonyms) {
|
|
|
2455
2455
|
});
|
|
2456
2456
|
}
|
|
2457
2457
|
function handleValidateYamlFragment(input) {
|
|
2458
|
-
const { schemaName, yaml } = input;
|
|
2458
|
+
const { schemaName, yaml: yaml2 } = input;
|
|
2459
2459
|
if (!SUPPORTED_SCHEMA_NAMES.includes(schemaName)) {
|
|
2460
2460
|
return {
|
|
2461
2461
|
valid: false,
|
|
@@ -2465,7 +2465,7 @@ function handleValidateYamlFragment(input) {
|
|
|
2465
2465
|
const name = schemaName;
|
|
2466
2466
|
let parsed;
|
|
2467
2467
|
try {
|
|
2468
|
-
parsed = yamlLoad(
|
|
2468
|
+
parsed = yamlLoad(yaml2);
|
|
2469
2469
|
} catch (err) {
|
|
2470
2470
|
return {
|
|
2471
2471
|
valid: false,
|
|
@@ -2491,8 +2491,8 @@ function registerValidateYamlFragmentTool(server2) {
|
|
|
2491
2491
|
),
|
|
2492
2492
|
yaml: z11.string().describe("YAML string to validate (can also be JSON \u2014 js-yaml parses both)")
|
|
2493
2493
|
},
|
|
2494
|
-
async ({ schemaName, yaml }) => {
|
|
2495
|
-
const result = handleValidateYamlFragment({ schemaName, yaml });
|
|
2494
|
+
async ({ schemaName, yaml: yaml2 }) => {
|
|
2495
|
+
const result = handleValidateYamlFragment({ schemaName, yaml: yaml2 });
|
|
2496
2496
|
return {
|
|
2497
2497
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
2498
2498
|
};
|
|
@@ -2967,7 +2967,8 @@ function defaultPhaseStatus() {
|
|
|
2967
2967
|
answered: false,
|
|
2968
2968
|
executed: false,
|
|
2969
2969
|
artifactWritten: false,
|
|
2970
|
-
retryCount: 0
|
|
2970
|
+
retryCount: 0,
|
|
2971
|
+
inFlight: false
|
|
2971
2972
|
};
|
|
2972
2973
|
}
|
|
2973
2974
|
function createDefaultState() {
|
|
@@ -3084,7 +3085,13 @@ function handleSetPipelineState(input) {
|
|
|
3084
3085
|
isError: true
|
|
3085
3086
|
};
|
|
3086
3087
|
}
|
|
3087
|
-
const VALID_FIELDS = [
|
|
3088
|
+
const VALID_FIELDS = [
|
|
3089
|
+
"questionsCollected",
|
|
3090
|
+
"answered",
|
|
3091
|
+
"executed",
|
|
3092
|
+
"artifactWritten",
|
|
3093
|
+
"inFlight"
|
|
3094
|
+
];
|
|
3088
3095
|
if (input.field && !VALID_FIELDS.includes(input.field)) {
|
|
3089
3096
|
return {
|
|
3090
3097
|
text: JSON.stringify({
|
|
@@ -4121,24 +4128,94 @@ function handleValidateArtifact(input) {
|
|
|
4121
4128
|
}
|
|
4122
4129
|
return result;
|
|
4123
4130
|
}
|
|
4131
|
+
function handleEmitEvent(input) {
|
|
4132
|
+
const emitFn = input._emit ?? emit;
|
|
4133
|
+
try {
|
|
4134
|
+
const caller = input.otter ?? "foreman";
|
|
4135
|
+
if (caller !== "foreman") {
|
|
4136
|
+
return {
|
|
4137
|
+
text: JSON.stringify({
|
|
4138
|
+
emitted: false,
|
|
4139
|
+
reason: `AUTHORIZATION: only the foreman otter may emit lifecycle events. Specialist otters should signal completion via their response text and the artifact write \u2014 not by emitting phase events. Caller was "${caller}". This is informational, not a failure.`
|
|
4140
|
+
}),
|
|
4141
|
+
isError: false
|
|
4142
|
+
};
|
|
4143
|
+
}
|
|
4144
|
+
let emitted = false;
|
|
4145
|
+
if (input.type === "phase_start") {
|
|
4146
|
+
if (!input.phase) {
|
|
4147
|
+
return {
|
|
4148
|
+
text: JSON.stringify({ emitted: false, reason: "phase required for phase_start" }),
|
|
4149
|
+
isError: false
|
|
4150
|
+
};
|
|
4151
|
+
}
|
|
4152
|
+
emitted = emitFn({ type: "phase_start", phase: input.phase, otter: caller });
|
|
4153
|
+
} else if (input.type === "phase_ready") {
|
|
4154
|
+
if (!input.phase) {
|
|
4155
|
+
return {
|
|
4156
|
+
text: JSON.stringify({ emitted: false, reason: "phase required for phase_ready" }),
|
|
4157
|
+
isError: false
|
|
4158
|
+
};
|
|
4159
|
+
}
|
|
4160
|
+
emitted = emitFn({ type: "phase_ready", phase: input.phase, otter: caller });
|
|
4161
|
+
} else if (input.type === "phase_complete") {
|
|
4162
|
+
if (!input.phase) {
|
|
4163
|
+
return {
|
|
4164
|
+
text: JSON.stringify({ emitted: false, reason: "phase required for phase_complete" }),
|
|
4165
|
+
isError: false
|
|
4166
|
+
};
|
|
4167
|
+
}
|
|
4168
|
+
emitted = emitFn({
|
|
4169
|
+
type: "phase_complete",
|
|
4170
|
+
phase: input.phase,
|
|
4171
|
+
otter: caller,
|
|
4172
|
+
...input.durationSec != null ? { durationSec: input.durationSec } : {}
|
|
4173
|
+
});
|
|
4174
|
+
} else if (input.type === "agent_invoke_start") {
|
|
4175
|
+
emitted = emitFn({
|
|
4176
|
+
type: "agent_invoke_start",
|
|
4177
|
+
targetOtter: input.targetOtter ?? "",
|
|
4178
|
+
phase: input.phase,
|
|
4179
|
+
otter: caller
|
|
4180
|
+
});
|
|
4181
|
+
} else if (input.type === "agent_invoke_complete") {
|
|
4182
|
+
emitted = emitFn({
|
|
4183
|
+
type: "agent_invoke_complete",
|
|
4184
|
+
targetOtter: input.targetOtter ?? "",
|
|
4185
|
+
success: input.success ?? true,
|
|
4186
|
+
phase: input.phase,
|
|
4187
|
+
otter: caller,
|
|
4188
|
+
...input.durationSec != null ? { durationSec: input.durationSec } : {}
|
|
4189
|
+
});
|
|
4190
|
+
}
|
|
4191
|
+
return { text: JSON.stringify({ emitted }), isError: false };
|
|
4192
|
+
} catch (err) {
|
|
4193
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
4194
|
+
return {
|
|
4195
|
+
text: JSON.stringify({ emitted: false, reason: `telemetry error (non-fatal): ${reason}` }),
|
|
4196
|
+
isError: false
|
|
4197
|
+
};
|
|
4198
|
+
}
|
|
4199
|
+
}
|
|
4124
4200
|
function registerPipelineTools(server2) {
|
|
4125
|
-
const
|
|
4201
|
+
const DESC2 = "Writes state to .stackwright/ \u2014 the filesystem is the state machine.";
|
|
4126
4202
|
const res = (r) => ({
|
|
4127
4203
|
content: [{ type: "text", text: r.text }],
|
|
4128
4204
|
isError: r.isError
|
|
4129
4205
|
});
|
|
4130
4206
|
server2.tool(
|
|
4131
4207
|
"stackwright_pro_get_pipeline_state",
|
|
4132
|
-
`Read pipeline state from .stackwright/pipeline-state.json. ${
|
|
4208
|
+
`Read pipeline state from .stackwright/pipeline-state.json. ${DESC2}`,
|
|
4133
4209
|
{},
|
|
4134
4210
|
async () => res(handleGetPipelineState())
|
|
4135
4211
|
);
|
|
4136
4212
|
server2.tool(
|
|
4137
4213
|
"stackwright_pro_set_pipeline_state",
|
|
4138
|
-
`Atomic read\u2192modify\u2192write pipeline state. ${
|
|
4214
|
+
`Atomic read\u2192modify\u2192write pipeline state. ${DESC2}`,
|
|
4139
4215
|
{
|
|
4140
4216
|
phase: z13.string().optional().describe('Phase to update, e.g. "designer"'),
|
|
4141
|
-
field: z13.enum(["questionsCollected", "answered", "executed", "artifactWritten"]).optional().describe("Boolean field to set"),
|
|
4217
|
+
field: z13.enum(["questionsCollected", "answered", "executed", "artifactWritten", "inFlight"]).optional().describe("Boolean field to set"),
|
|
4218
|
+
// inFlight: set true BEFORE specialist invoke (dataflow mode, swp-ioc7); false after.
|
|
4142
4219
|
value: boolCoerce(z13.boolean().optional()).describe(
|
|
4143
4220
|
'Value for the field \u2014 must be a JSON boolean (true/false), NOT the string "true"/"false"'
|
|
4144
4221
|
),
|
|
@@ -4150,7 +4227,13 @@ function registerPipelineTools(server2) {
|
|
|
4150
4227
|
z13.array(
|
|
4151
4228
|
z13.object({
|
|
4152
4229
|
phase: z13.string(),
|
|
4153
|
-
field: z13.enum([
|
|
4230
|
+
field: z13.enum([
|
|
4231
|
+
"questionsCollected",
|
|
4232
|
+
"answered",
|
|
4233
|
+
"executed",
|
|
4234
|
+
"artifactWritten",
|
|
4235
|
+
"inFlight"
|
|
4236
|
+
]),
|
|
4154
4237
|
value: z13.boolean()
|
|
4155
4238
|
})
|
|
4156
4239
|
).optional()
|
|
@@ -4169,7 +4252,7 @@ function registerPipelineTools(server2) {
|
|
|
4169
4252
|
);
|
|
4170
4253
|
server2.tool(
|
|
4171
4254
|
"stackwright_pro_check_execution_ready",
|
|
4172
|
-
`Check all phases have answer files in .stackwright/answers/. If phase is provided, check only that phase. ${
|
|
4255
|
+
`Check all phases have answer files in .stackwright/answers/. If phase is provided, check only that phase. ${DESC2}`,
|
|
4173
4256
|
{
|
|
4174
4257
|
phase: z13.string().optional().describe("If provided, check only this phase's readiness. Omit to check all phases.")
|
|
4175
4258
|
},
|
|
@@ -4177,19 +4260,19 @@ function registerPipelineTools(server2) {
|
|
|
4177
4260
|
);
|
|
4178
4261
|
server2.tool(
|
|
4179
4262
|
"stackwright_pro_get_ready_phases",
|
|
4180
|
-
`Return phases whose dependencies are all satisfied (executed=true). Use to determine which phases can run next \u2014 enables wave-based execution. ${
|
|
4263
|
+
`Return phases whose dependencies are all satisfied (executed=true). Use to determine which phases can run next \u2014 enables wave-based execution. ${DESC2}`,
|
|
4181
4264
|
{},
|
|
4182
4265
|
async () => res(handleGetReadyPhases())
|
|
4183
4266
|
);
|
|
4184
4267
|
server2.tool(
|
|
4185
4268
|
"stackwright_pro_list_artifacts",
|
|
4186
|
-
`List phase artifacts in .stackwright/artifacts/ with completedCount/totalCount. ${
|
|
4269
|
+
`List phase artifacts in .stackwright/artifacts/ with completedCount/totalCount. ${DESC2}`,
|
|
4187
4270
|
{},
|
|
4188
4271
|
async () => res(handleListArtifacts())
|
|
4189
4272
|
);
|
|
4190
4273
|
server2.tool(
|
|
4191
4274
|
"stackwright_pro_write_phase_questions",
|
|
4192
|
-
`Parse otter question-collection response \u2192 .stackwright/questions/{phase}.json. Specialists may also call this directly with a parsed questions array. ${
|
|
4275
|
+
`Parse otter question-collection response \u2192 .stackwright/questions/{phase}.json. Specialists may also call this directly with a parsed questions array. ${DESC2}`,
|
|
4193
4276
|
{
|
|
4194
4277
|
phase: z13.string().optional().describe('Phase name, e.g. "designer" (required for direct write)'),
|
|
4195
4278
|
responseText: z13.string().optional().describe("Raw LLM response from QUESTION_COLLECTION_MODE"),
|
|
@@ -4234,13 +4317,13 @@ function registerPipelineTools(server2) {
|
|
|
4234
4317
|
);
|
|
4235
4318
|
server2.tool(
|
|
4236
4319
|
"stackwright_pro_build_specialist_prompt",
|
|
4237
|
-
`Assemble execution prompt from answers + upstream artifacts. Foreman passes verbatim. ${
|
|
4320
|
+
`Assemble execution prompt from answers + upstream artifacts. Foreman passes verbatim. ${DESC2}`,
|
|
4238
4321
|
{ phase: z13.string().describe('Phase to build prompt for, e.g. "pages"') },
|
|
4239
4322
|
async ({ phase }) => res(handleBuildSpecialistPrompt({ phase }))
|
|
4240
4323
|
);
|
|
4241
4324
|
server2.tool(
|
|
4242
4325
|
"stackwright_pro_validate_artifact",
|
|
4243
|
-
`Validate and write artifact to .stackwright/artifacts/. Returns retryPrompt on failure. ${
|
|
4326
|
+
`Validate and write artifact to .stackwright/artifacts/. Returns retryPrompt on failure. ${DESC2}`,
|
|
4244
4327
|
{
|
|
4245
4328
|
phase: z13.string().describe('Phase that produced this artifact, e.g. "designer"'),
|
|
4246
4329
|
responseText: z13.string().optional().describe("Raw response text from the specialist otter (Foreman-mediated path, legacy)"),
|
|
@@ -4272,60 +4355,26 @@ function registerPipelineTools(server2) {
|
|
|
4272
4355
|
);
|
|
4273
4356
|
server2.tool(
|
|
4274
4357
|
"stackwright_pro_emit_event",
|
|
4275
|
-
`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. ${
|
|
4358
|
+
`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}`,
|
|
4276
4359
|
{
|
|
4277
|
-
type: z13.enum([
|
|
4360
|
+
type: z13.enum([
|
|
4361
|
+
"phase_start",
|
|
4362
|
+
"phase_complete",
|
|
4363
|
+
"phase_ready",
|
|
4364
|
+
"agent_invoke_start",
|
|
4365
|
+
"agent_invoke_complete"
|
|
4366
|
+
]).describe("Event type to emit"),
|
|
4367
|
+
// phase_ready: emitted in dataflow mode (swp-ioc7) when a phase enters the ready set
|
|
4368
|
+
// (deps satisfied, not inFlight, not yet complete) — BEFORE invoking the specialist.
|
|
4278
4369
|
phase: z13.string().optional().describe("Current phase name"),
|
|
4279
4370
|
targetOtter: z13.string().optional().describe("For agent_invoke_* events: which otter is being invoked"),
|
|
4280
|
-
success: z13.boolean().optional().describe(
|
|
4371
|
+
success: boolCoerce(z13.boolean().optional()).describe(
|
|
4372
|
+
'For agent_invoke_complete: did the invocation succeed? (boolean; string "true"/"false" also accepted for caller tolerance)'
|
|
4373
|
+
),
|
|
4281
4374
|
otter: z13.string().optional().describe('Which otter is emitting (defaults to "foreman")'),
|
|
4282
4375
|
durationSec: z13.number().optional().describe("Duration in seconds (for *_complete events)")
|
|
4283
4376
|
},
|
|
4284
|
-
async ({ type, phase, targetOtter, success, otter, durationSec }) => {
|
|
4285
|
-
let emitted = false;
|
|
4286
|
-
try {
|
|
4287
|
-
if (type === "phase_start") {
|
|
4288
|
-
if (!phase) {
|
|
4289
|
-
return res({
|
|
4290
|
-
text: JSON.stringify({ emitted: false, reason: "phase required for phase_start" }),
|
|
4291
|
-
isError: false
|
|
4292
|
-
});
|
|
4293
|
-
}
|
|
4294
|
-
emitted = emit({ type: "phase_start", phase, otter: otter ?? "foreman" });
|
|
4295
|
-
} else if (type === "phase_complete") {
|
|
4296
|
-
if (!phase) {
|
|
4297
|
-
return res({
|
|
4298
|
-
text: JSON.stringify({ emitted: false, reason: "phase required for phase_complete" }),
|
|
4299
|
-
isError: false
|
|
4300
|
-
});
|
|
4301
|
-
}
|
|
4302
|
-
emitted = emit({
|
|
4303
|
-
type: "phase_complete",
|
|
4304
|
-
phase,
|
|
4305
|
-
otter: otter ?? "foreman",
|
|
4306
|
-
...durationSec != null ? { durationSec } : {}
|
|
4307
|
-
});
|
|
4308
|
-
} else if (type === "agent_invoke_start") {
|
|
4309
|
-
emitted = emit({
|
|
4310
|
-
type: "agent_invoke_start",
|
|
4311
|
-
targetOtter: targetOtter ?? "",
|
|
4312
|
-
phase,
|
|
4313
|
-
otter: otter ?? "foreman"
|
|
4314
|
-
});
|
|
4315
|
-
} else if (type === "agent_invoke_complete") {
|
|
4316
|
-
emitted = emit({
|
|
4317
|
-
type: "agent_invoke_complete",
|
|
4318
|
-
targetOtter: targetOtter ?? "",
|
|
4319
|
-
success: success ?? true,
|
|
4320
|
-
phase,
|
|
4321
|
-
otter: otter ?? "foreman",
|
|
4322
|
-
...durationSec != null ? { durationSec } : {}
|
|
4323
|
-
});
|
|
4324
|
-
}
|
|
4325
|
-
} catch {
|
|
4326
|
-
}
|
|
4327
|
-
return res({ text: JSON.stringify({ emitted }), isError: false });
|
|
4328
|
-
}
|
|
4377
|
+
async ({ type, phase, targetOtter, success, otter, durationSec }) => res(handleEmitEvent({ type, phase, targetOtter, success, otter, durationSec }))
|
|
4329
4378
|
);
|
|
4330
4379
|
}
|
|
4331
4380
|
|
|
@@ -4427,7 +4476,17 @@ var OTTER_WRITE_ALLOWLISTS = {
|
|
|
4427
4476
|
{ prefix: "pages/", suffix: "/content.yml", description: "Landing page content" },
|
|
4428
4477
|
{ prefix: "pages/", suffix: "/content.yaml", description: "Landing page content" },
|
|
4429
4478
|
{ prefix: ".stackwright/artifacts/", suffix: ".json", description: "Polish artifact" },
|
|
4430
|
-
{ prefix: "README.md", suffix: "", description: "Project README rewrite" }
|
|
4479
|
+
{ prefix: "README.md", suffix: "", description: "Project README rewrite" },
|
|
4480
|
+
{
|
|
4481
|
+
prefix: ".env.local",
|
|
4482
|
+
suffix: "",
|
|
4483
|
+
description: ".env.local runtime config \u2014 emitter output from stackwright_pro_emit_env_local (swp-8qdd)"
|
|
4484
|
+
},
|
|
4485
|
+
{
|
|
4486
|
+
prefix: ".env.local.example",
|
|
4487
|
+
suffix: "",
|
|
4488
|
+
description: ".env.local.example template \u2014 emitter output from stackwright_pro_emit_env_local (swp-8qdd)"
|
|
4489
|
+
}
|
|
4431
4490
|
],
|
|
4432
4491
|
// QA otter writes ONLY to the qa/ directory and the qa-findings artifact.
|
|
4433
4492
|
// It never writes .tsx, .ts, .yml, or .json outside these paths.
|
|
@@ -4795,10 +4854,10 @@ function handleSafeWrite(input) {
|
|
|
4795
4854
|
return result;
|
|
4796
4855
|
}
|
|
4797
4856
|
function registerSafeWriteTools(server2) {
|
|
4798
|
-
const
|
|
4857
|
+
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.";
|
|
4799
4858
|
server2.tool(
|
|
4800
4859
|
"stackwright_pro_safe_write",
|
|
4801
|
-
|
|
4860
|
+
DESC2,
|
|
4802
4861
|
{
|
|
4803
4862
|
callerOtter: z14.string().describe('The otter agent name requesting the write, e.g. "stackwright-pro-page-otter"'),
|
|
4804
4863
|
filePath: z14.string().describe('Relative path from project root, e.g. "pages/dashboard/content.yml"'),
|
|
@@ -5766,7 +5825,7 @@ var _checksums = /* @__PURE__ */ new Map([
|
|
|
5766
5825
|
],
|
|
5767
5826
|
[
|
|
5768
5827
|
"stackwright-pro-foreman-otter.json",
|
|
5769
|
-
"
|
|
5828
|
+
"c23436769a9db06fa6f9d312b4fdd8b2cef6646461020eeaf227303350073593"
|
|
5770
5829
|
],
|
|
5771
5830
|
[
|
|
5772
5831
|
"stackwright-pro-form-wizard-otter.json",
|
|
@@ -5782,7 +5841,7 @@ var _checksums = /* @__PURE__ */ new Map([
|
|
|
5782
5841
|
],
|
|
5783
5842
|
[
|
|
5784
5843
|
"stackwright-pro-polish-otter.json",
|
|
5785
|
-
"
|
|
5844
|
+
"fd8f8963266c6179eebf8eac4f656e8274aa3a721e5296abdbde5b00ad8a2297"
|
|
5786
5845
|
],
|
|
5787
5846
|
[
|
|
5788
5847
|
"stackwright-pro-qa-otter.json",
|
|
@@ -5790,7 +5849,7 @@ var _checksums = /* @__PURE__ */ new Map([
|
|
|
5790
5849
|
],
|
|
5791
5850
|
[
|
|
5792
5851
|
"stackwright-pro-scaffold-otter.json",
|
|
5793
|
-
"
|
|
5852
|
+
"c2ae10733d8bd66bd965b29a77193b0abf0ea6765cd02886df1413bf598e0588"
|
|
5794
5853
|
],
|
|
5795
5854
|
[
|
|
5796
5855
|
"stackwright-pro-theme-otter.json",
|
|
@@ -5798,7 +5857,7 @@ var _checksums = /* @__PURE__ */ new Map([
|
|
|
5798
5857
|
],
|
|
5799
5858
|
[
|
|
5800
5859
|
"stackwright-services-otter.json",
|
|
5801
|
-
"
|
|
5860
|
+
"2a50ceb3fad166251d0ad8709a34a32118645713e0e34628165d16dced1d5c93"
|
|
5802
5861
|
]
|
|
5803
5862
|
]);
|
|
5804
5863
|
Object.freeze(_checksums);
|
|
@@ -6871,10 +6930,10 @@ function handleCleanupScaffold(_cwd) {
|
|
|
6871
6930
|
};
|
|
6872
6931
|
}
|
|
6873
6932
|
function registerScaffoldCleanupTools(server2) {
|
|
6874
|
-
const
|
|
6933
|
+
const DESC2 = "Deterministic scaffold remnant cleanup \u2014 removes stale OSS scaffold files after raft run.";
|
|
6875
6934
|
server2.tool(
|
|
6876
6935
|
"stackwright_pro_cleanup_scaffold",
|
|
6877
|
-
`Remove/rewrite stale scaffold files (blog posts, hardcoded not-found page) after the raft pipeline completes. ${
|
|
6936
|
+
`Remove/rewrite stale scaffold files (blog posts, hardcoded not-found page) after the raft pipeline completes. ${DESC2}`,
|
|
6878
6937
|
{},
|
|
6879
6938
|
async () => {
|
|
6880
6939
|
const r = handleCleanupScaffold();
|
|
@@ -7442,10 +7501,100 @@ function registerStripLegacyIntegrationsTool(server2) {
|
|
|
7442
7501
|
);
|
|
7443
7502
|
}
|
|
7444
7503
|
|
|
7445
|
-
// src/tools/
|
|
7504
|
+
// src/tools/emitters.ts
|
|
7446
7505
|
import { z as z22 } from "zod";
|
|
7447
|
-
import {
|
|
7448
|
-
import { join as join13
|
|
7506
|
+
import { readFileSync as readFileSync12, existsSync as existsSync12, readdirSync as readdirSync5 } from "fs";
|
|
7507
|
+
import { join as join13 } from "path";
|
|
7508
|
+
import yaml from "js-yaml";
|
|
7509
|
+
import { emitEnvLocal } from "@stackwright-pro/emitters";
|
|
7510
|
+
function readIntegrations(cwd) {
|
|
7511
|
+
const filePath = join13(cwd, "stackwright.integrations.yml");
|
|
7512
|
+
if (!existsSync12(filePath)) return [];
|
|
7513
|
+
try {
|
|
7514
|
+
const raw = yaml.load(readFileSync12(filePath, "utf-8"));
|
|
7515
|
+
if (!raw?.integrations || !Array.isArray(raw.integrations)) return [];
|
|
7516
|
+
return raw.integrations.filter((i) => typeof i.name === "string").map((i) => ({
|
|
7517
|
+
name: i.name,
|
|
7518
|
+
auth: i.auth ? { type: i.auth.type, header: i.auth.header, envVar: i.auth.envVar } : void 0
|
|
7519
|
+
}));
|
|
7520
|
+
} catch {
|
|
7521
|
+
return [];
|
|
7522
|
+
}
|
|
7523
|
+
}
|
|
7524
|
+
function readServiceTokenRefs(cwd) {
|
|
7525
|
+
const servicesDir = join13(cwd, "services");
|
|
7526
|
+
if (!existsSync12(servicesDir)) return [];
|
|
7527
|
+
try {
|
|
7528
|
+
const files = readdirSync5(servicesDir).filter((f) => f.endsWith(".yml") || f.endsWith(".yaml"));
|
|
7529
|
+
const refs = [];
|
|
7530
|
+
for (const file of files) {
|
|
7531
|
+
try {
|
|
7532
|
+
const raw = yaml.load(
|
|
7533
|
+
readFileSync12(join13(servicesDir, file), "utf-8")
|
|
7534
|
+
);
|
|
7535
|
+
if (raw?.tokenEnv && typeof raw.tokenEnv === "string") {
|
|
7536
|
+
refs.push({
|
|
7537
|
+
name: raw.name ?? file.replace(/\.ya?ml$/, ""),
|
|
7538
|
+
tokenEnv: raw.tokenEnv
|
|
7539
|
+
});
|
|
7540
|
+
}
|
|
7541
|
+
} catch {
|
|
7542
|
+
}
|
|
7543
|
+
}
|
|
7544
|
+
return refs;
|
|
7545
|
+
} catch {
|
|
7546
|
+
return [];
|
|
7547
|
+
}
|
|
7548
|
+
}
|
|
7549
|
+
var DESC = "Returns { envLocal, envLocalExample } as JSON. The otter writes both files via stackwright_pro_safe_write.";
|
|
7550
|
+
function registerEmitterTools(server2) {
|
|
7551
|
+
server2.tool(
|
|
7552
|
+
"stackwright_pro_emit_env_local",
|
|
7553
|
+
`Emit .env.local + .env.local.example contents from stackwright.integrations.yml and services/*.yml. Deterministic emitter \u2014 same project config produces byte-identical output.
|
|
7554
|
+
|
|
7555
|
+
Call this tool, then write both files with stackwright_pro_safe_write:
|
|
7556
|
+
1. stackwright_pro_emit_env_local({ cwd }) \u2192 { envLocal, envLocalExample }
|
|
7557
|
+
2. stackwright_pro_safe_write({ filePath: '.env.local', content: envLocal, ... })
|
|
7558
|
+
3. stackwright_pro_safe_write({ filePath: '.env.local.example', content: envLocalExample, ... })
|
|
7559
|
+
|
|
7560
|
+
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}`,
|
|
7561
|
+
{
|
|
7562
|
+
cwd: z22.string().describe(
|
|
7563
|
+
"Absolute path to the project root containing stackwright.integrations.yml and optionally services/*.yml"
|
|
7564
|
+
)
|
|
7565
|
+
},
|
|
7566
|
+
async ({ cwd }) => {
|
|
7567
|
+
const integrations = readIntegrations(cwd);
|
|
7568
|
+
const services = readServiceTokenRefs(cwd);
|
|
7569
|
+
const output = emitEnvLocal({ integrations, services });
|
|
7570
|
+
return {
|
|
7571
|
+
content: [
|
|
7572
|
+
{
|
|
7573
|
+
type: "text",
|
|
7574
|
+
text: JSON.stringify(
|
|
7575
|
+
{
|
|
7576
|
+
envLocal: output.envLocal,
|
|
7577
|
+
envLocalExample: output.envLocalExample,
|
|
7578
|
+
meta: {
|
|
7579
|
+
integrationsRead: integrations.length,
|
|
7580
|
+
serviceTokenRefsRead: services.length,
|
|
7581
|
+
envVarsEmitted: (output.envLocal.match(/^NEXT_PUBLIC_[A-Z0-9_]+=.+$/m) ? output.envLocal.match(/^NEXT_PUBLIC_[A-Z0-9_]+=.+$/gm) ?? [] : []).length
|
|
7582
|
+
}
|
|
7583
|
+
},
|
|
7584
|
+
null,
|
|
7585
|
+
2
|
|
7586
|
+
)
|
|
7587
|
+
}
|
|
7588
|
+
]
|
|
7589
|
+
};
|
|
7590
|
+
}
|
|
7591
|
+
);
|
|
7592
|
+
}
|
|
7593
|
+
|
|
7594
|
+
// src/tools/list-specs.ts
|
|
7595
|
+
import { z as z23 } from "zod";
|
|
7596
|
+
import { readdirSync as readdirSync6, readFileSync as readFileSync13, statSync, existsSync as existsSync13 } from "fs";
|
|
7597
|
+
import { join as join14, extname, basename as basename2 } from "path";
|
|
7449
7598
|
var STRIP_SUFFIXES = ["-openapi", "-asyncapi", "-api", "-spec"];
|
|
7450
7599
|
var ALLOWED_EXTS = /* @__PURE__ */ new Set([".yaml", ".yml", ".json"]);
|
|
7451
7600
|
function deriveIntegrationName(fileName) {
|
|
@@ -7467,18 +7616,18 @@ function detectFormat(snippet) {
|
|
|
7467
7616
|
}
|
|
7468
7617
|
function handleListSpecs(input) {
|
|
7469
7618
|
const { projectRoot } = input;
|
|
7470
|
-
const specsDir =
|
|
7619
|
+
const specsDir = join14(projectRoot, "specs");
|
|
7471
7620
|
const empty = { projectRoot, specs: [], totalCount: 0 };
|
|
7472
|
-
if (!
|
|
7621
|
+
if (!existsSync13(specsDir)) return empty;
|
|
7473
7622
|
let entries;
|
|
7474
7623
|
try {
|
|
7475
|
-
entries =
|
|
7624
|
+
entries = readdirSync6(specsDir);
|
|
7476
7625
|
} catch {
|
|
7477
7626
|
return empty;
|
|
7478
7627
|
}
|
|
7479
7628
|
const specs = [];
|
|
7480
7629
|
for (const entry of entries) {
|
|
7481
|
-
const fullPath =
|
|
7630
|
+
const fullPath = join14(specsDir, entry);
|
|
7482
7631
|
let stat;
|
|
7483
7632
|
try {
|
|
7484
7633
|
stat = statSync(fullPath);
|
|
@@ -7489,7 +7638,7 @@ function handleListSpecs(input) {
|
|
|
7489
7638
|
if (!ALLOWED_EXTS.has(extname(entry).toLowerCase())) continue;
|
|
7490
7639
|
let snippet = "";
|
|
7491
7640
|
try {
|
|
7492
|
-
snippet =
|
|
7641
|
+
snippet = readFileSync13(fullPath, "utf-8").slice(0, 2048);
|
|
7493
7642
|
} catch {
|
|
7494
7643
|
}
|
|
7495
7644
|
specs.push({
|
|
@@ -7508,7 +7657,7 @@ function registerListSpecsTool(server2) {
|
|
|
7508
7657
|
"stackwright_pro_list_specs",
|
|
7509
7658
|
"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.",
|
|
7510
7659
|
{
|
|
7511
|
-
projectRoot:
|
|
7660
|
+
projectRoot: z23.string().describe("Absolute path to the project root directory")
|
|
7512
7661
|
},
|
|
7513
7662
|
async ({ projectRoot }) => {
|
|
7514
7663
|
const result = handleListSpecs({ projectRoot });
|
|
@@ -7525,6 +7674,7 @@ var package_default = {
|
|
|
7525
7674
|
"@modelcontextprotocol/sdk": "^1.10.0",
|
|
7526
7675
|
"@stackwright/cli": "^0.9.0",
|
|
7527
7676
|
"@stackwright-pro/auth-nextjs": "workspace:*",
|
|
7677
|
+
"@stackwright-pro/emitters": "workspace:*",
|
|
7528
7678
|
"@stackwright-pro/cli-data-explorer": "workspace:*",
|
|
7529
7679
|
"@stackwright-pro/openapi": "workspace:*",
|
|
7530
7680
|
"@stackwright-pro/pulse": "workspace:*",
|
|
@@ -7552,7 +7702,7 @@ var package_default = {
|
|
|
7552
7702
|
"test:coverage": "vitest run --coverage"
|
|
7553
7703
|
},
|
|
7554
7704
|
name: "@stackwright-pro/mcp",
|
|
7555
|
-
version: "0.2.0-alpha.
|
|
7705
|
+
version: "0.2.0-alpha.102",
|
|
7556
7706
|
description: "MCP tools for Stackwright Pro - Data Explorer, Security, ISR, and Dashboard generation",
|
|
7557
7707
|
license: "SEE LICENSE IN LICENSE",
|
|
7558
7708
|
main: "./dist/server.js",
|
|
@@ -7632,6 +7782,7 @@ registerScaffoldCleanupTools(server);
|
|
|
7632
7782
|
registerContrastTools(server);
|
|
7633
7783
|
registerCompileTools(server);
|
|
7634
7784
|
registerStripLegacyIntegrationsTool(server);
|
|
7785
|
+
registerEmitterTools(server);
|
|
7635
7786
|
registerListSpecsTool(server);
|
|
7636
7787
|
registerContentTypeTools(server);
|
|
7637
7788
|
registerPageTools(server);
|