@stackwright-pro/mcp 0.2.0-alpha.97 → 0.2.0-alpha.99

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/server.mjs CHANGED
@@ -2000,6 +2000,7 @@ function registerOrchestrationTools(server2) {
2000
2000
  // src/tools/pipeline.ts
2001
2001
  import { z as z13 } from "zod";
2002
2002
  import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync5, mkdirSync as mkdirSync4, lstatSync as lstatSync6 } from "fs";
2003
+ import { lockSync } from "proper-lockfile";
2003
2004
  import { join as join5 } from "path";
2004
2005
  import { createHash as createHash3 } from "crypto";
2005
2006
 
@@ -2909,6 +2910,7 @@ function loadPipelineGraph() {
2909
2910
  }
2910
2911
 
2911
2912
  // src/tools/pipeline.ts
2913
+ import { emit } from "@stackwright-pro/telemetry";
2912
2914
  var PHASE_ORDER = [
2913
2915
  "designer",
2914
2916
  "theme",
@@ -3019,6 +3021,27 @@ function writeState(cwd, state) {
3019
3021
  state.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3020
3022
  safeWriteSync(statePath(cwd), JSON.stringify(state, null, 2) + "\n");
3021
3023
  }
3024
+ function updateStateAtomic(cwd, mutator) {
3025
+ const dir = join5(cwd, ".stackwright");
3026
+ mkdirSync4(dir, { recursive: true });
3027
+ const path4 = statePath(cwd);
3028
+ if (!existsSync5(path4)) {
3029
+ safeWriteSync(path4, JSON.stringify(createDefaultState(), null, 2) + "\n");
3030
+ }
3031
+ const release = lockSync(path4, {
3032
+ realpath: false,
3033
+ stale: 1e4
3034
+ // Treat locks older than 10s as stale (guards against crash mid-lock)
3035
+ });
3036
+ try {
3037
+ const state = readState(cwd);
3038
+ mutator(state);
3039
+ writeState(cwd, state);
3040
+ return state;
3041
+ } finally {
3042
+ release();
3043
+ }
3044
+ }
3022
3045
  function extractJsonFromResponse(text) {
3023
3046
  let cleaned = text;
3024
3047
  cleaned = cleaned.replace(/```(?:json)?\s*/gi, "");
@@ -3094,35 +3117,57 @@ function handleSetPipelineState(input) {
3094
3117
  }
3095
3118
  }
3096
3119
  try {
3097
- const state = readState(cwd);
3098
- if (input.status) {
3099
- state.status = input.status;
3100
- }
3101
- if (input.phase) {
3102
- const phase = input.phase;
3103
- if (!state.phases[phase]) {
3104
- state.phases[phase] = defaultPhaseStatus();
3120
+ const state = updateStateAtomic(cwd, (state2) => {
3121
+ if (input.status) {
3122
+ state2.status = input.status;
3123
+ }
3124
+ if (input.phase) {
3125
+ const phase = input.phase;
3126
+ if (!state2.phases[phase]) {
3127
+ state2.phases[phase] = defaultPhaseStatus();
3128
+ }
3129
+ const phaseState = state2.phases[phase];
3130
+ if (input.field && input.value !== void 0) {
3131
+ phaseState[input.field] = input.value;
3132
+ }
3133
+ if (input.incrementRetry) {
3134
+ phaseState.retryCount += 1;
3135
+ }
3136
+ state2.currentPhase = phase;
3105
3137
  }
3106
- const phaseState = state.phases[phase];
3107
- if (input.field && input.value !== void 0) {
3108
- phaseState[input.field] = input.value;
3138
+ if (input.updates && input.updates.length > 0) {
3139
+ for (const update of input.updates) {
3140
+ if (!state2.phases[update.phase]) {
3141
+ state2.phases[update.phase] = defaultPhaseStatus();
3142
+ }
3143
+ const batchPhaseState = state2.phases[update.phase];
3144
+ batchPhaseState[update.field] = update.value;
3145
+ state2.currentPhase = update.phase;
3146
+ }
3109
3147
  }
3110
- if (input.incrementRetry) {
3111
- phaseState.retryCount += 1;
3148
+ });
3149
+ try {
3150
+ const completedPhases = Object.entries(state.phases).filter(([, ps]) => ps.artifactWritten).map(([p]) => p);
3151
+ emit(
3152
+ {
3153
+ type: "pipeline_state_change",
3154
+ state: state.status,
3155
+ phases: { ready: [], completed: completedPhases, blocked: [] }
3156
+ },
3157
+ { cwd }
3158
+ );
3159
+ if (input.field === "artifactWritten" && input.value === true && input.phase) {
3160
+ emit({ type: "phase_complete", phase: input.phase }, { cwd });
3112
3161
  }
3113
- state.currentPhase = phase;
3114
- }
3115
- if (input.updates && input.updates.length > 0) {
3116
- for (const update of input.updates) {
3117
- if (!state.phases[update.phase]) {
3118
- state.phases[update.phase] = defaultPhaseStatus();
3162
+ if (input.updates) {
3163
+ for (const update of input.updates) {
3164
+ if (update.field === "artifactWritten" && update.value === true) {
3165
+ emit({ type: "phase_complete", phase: update.phase }, { cwd });
3166
+ }
3119
3167
  }
3120
- const batchPhaseState = state.phases[update.phase];
3121
- batchPhaseState[update.field] = update.value;
3122
- state.currentPhase = update.phase;
3123
3168
  }
3169
+ } catch {
3124
3170
  }
3125
- writeState(cwd, state);
3126
3171
  return { text: JSON.stringify(state), isError: false };
3127
3172
  } catch (err) {
3128
3173
  return { text: JSON.stringify({ error: true, message: String(err) }), isError: true };
@@ -3315,11 +3360,11 @@ function handleWritePhaseQuestions(input) {
3315
3360
  requiredPackages
3316
3361
  };
3317
3362
  safeWriteSync(filePath, JSON.stringify(payload, null, 2) + "\n");
3318
- const state = readState(cwd);
3319
- if (!state.phases[phase]) state.phases[phase] = defaultPhaseStatus();
3320
- const ps = state.phases[phase];
3321
- ps.questionsCollected = true;
3322
- writeState(cwd, state);
3363
+ updateStateAtomic(cwd, (state) => {
3364
+ if (!state.phases[phase]) state.phases[phase] = defaultPhaseStatus();
3365
+ const ps = state.phases[phase];
3366
+ ps.questionsCollected = true;
3367
+ });
3323
3368
  return {
3324
3369
  text: JSON.stringify({
3325
3370
  success: true,
@@ -3828,7 +3873,7 @@ var PHASE_ARTIFACT_SCHEMA = {
3828
3873
  2
3829
3874
  )
3830
3875
  };
3831
- function handleValidateArtifact(input) {
3876
+ function _validateArtifactInner(input) {
3832
3877
  const cwd = input._cwd ?? process.cwd();
3833
3878
  const { phase, responseText, artifact: directArtifact } = input;
3834
3879
  if (!isValidPhase2(phase)) {
@@ -4032,11 +4077,11 @@ function handleValidateArtifact(input) {
4032
4077
  signed = true;
4033
4078
  } catch {
4034
4079
  }
4035
- const state = readState(cwd);
4036
- if (!state.phases[phase]) state.phases[phase] = defaultPhaseStatus();
4037
- const ps = state.phases[phase];
4038
- ps.artifactWritten = true;
4039
- writeState(cwd, state);
4080
+ updateStateAtomic(cwd, (state) => {
4081
+ if (!state.phases[phase]) state.phases[phase] = defaultPhaseStatus();
4082
+ const ps = state.phases[phase];
4083
+ ps.artifactWritten = true;
4084
+ });
4040
4085
  const topKeys = Object.keys(artifact).slice(0, 5).join(", ");
4041
4086
  const result = {
4042
4087
  valid: true,
@@ -4050,6 +4095,32 @@ function handleValidateArtifact(input) {
4050
4095
  return { text: JSON.stringify({ error: true, phase, message }), isError: true };
4051
4096
  }
4052
4097
  }
4098
+ function handleValidateArtifact(input) {
4099
+ const cwd = input._cwd ?? process.cwd();
4100
+ const { phase } = input;
4101
+ emit(
4102
+ { type: "tool_call", toolName: "stackwright_pro_validate_artifact", args: { phase }, phase },
4103
+ { cwd }
4104
+ );
4105
+ const result = _validateArtifactInner(input);
4106
+ try {
4107
+ const parsed = JSON.parse(result.text);
4108
+ if (parsed.valid === true && parsed.artifactPath) {
4109
+ emit({ type: "file_write", path: parsed.artifactPath, phase }, { cwd });
4110
+ }
4111
+ emit(
4112
+ {
4113
+ type: "tool_complete",
4114
+ toolName: "stackwright_pro_validate_artifact",
4115
+ success: parsed.valid === true,
4116
+ phase
4117
+ },
4118
+ { cwd }
4119
+ );
4120
+ } catch {
4121
+ }
4122
+ return result;
4123
+ }
4053
4124
  function registerPipelineTools(server2) {
4054
4125
  const DESC = "Writes state to .stackwright/ \u2014 the filesystem is the state machine.";
4055
4126
  const res = (r) => ({
@@ -4186,12 +4257,70 @@ function registerPipelineTools(server2) {
4186
4257
  return res(handleValidateArtifact({ phase, responseText: responseText ?? "" }));
4187
4258
  }
4188
4259
  );
4260
+ server2.tool(
4261
+ "stackwright_pro_emit_event",
4262
+ `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}`,
4263
+ {
4264
+ type: z13.enum(["phase_start", "phase_complete", "agent_invoke_start", "agent_invoke_complete"]).describe("Event type to emit"),
4265
+ phase: z13.string().optional().describe("Current phase name"),
4266
+ targetOtter: z13.string().optional().describe("For agent_invoke_* events: which otter is being invoked"),
4267
+ success: z13.boolean().optional().describe("For agent_invoke_complete: did the invocation succeed?"),
4268
+ otter: z13.string().optional().describe('Which otter is emitting (defaults to "foreman")'),
4269
+ durationSec: z13.number().optional().describe("Duration in seconds (for *_complete events)")
4270
+ },
4271
+ async ({ type, phase, targetOtter, success, otter, durationSec }) => {
4272
+ let emitted = false;
4273
+ try {
4274
+ if (type === "phase_start") {
4275
+ if (!phase) {
4276
+ return res({
4277
+ text: JSON.stringify({ emitted: false, reason: "phase required for phase_start" }),
4278
+ isError: false
4279
+ });
4280
+ }
4281
+ emitted = emit({ type: "phase_start", phase, otter: otter ?? "foreman" });
4282
+ } else if (type === "phase_complete") {
4283
+ if (!phase) {
4284
+ return res({
4285
+ text: JSON.stringify({ emitted: false, reason: "phase required for phase_complete" }),
4286
+ isError: false
4287
+ });
4288
+ }
4289
+ emitted = emit({
4290
+ type: "phase_complete",
4291
+ phase,
4292
+ otter: otter ?? "foreman",
4293
+ ...durationSec != null ? { durationSec } : {}
4294
+ });
4295
+ } else if (type === "agent_invoke_start") {
4296
+ emitted = emit({
4297
+ type: "agent_invoke_start",
4298
+ targetOtter: targetOtter ?? "",
4299
+ phase,
4300
+ otter: otter ?? "foreman"
4301
+ });
4302
+ } else if (type === "agent_invoke_complete") {
4303
+ emitted = emit({
4304
+ type: "agent_invoke_complete",
4305
+ targetOtter: targetOtter ?? "",
4306
+ success: success ?? true,
4307
+ phase,
4308
+ otter: otter ?? "foreman",
4309
+ ...durationSec != null ? { durationSec } : {}
4310
+ });
4311
+ }
4312
+ } catch {
4313
+ }
4314
+ return res({ text: JSON.stringify({ emitted }), isError: false });
4315
+ }
4316
+ );
4189
4317
  }
4190
4318
 
4191
4319
  // src/tools/safe-write.ts
4192
4320
  import { z as z14 } from "zod";
4193
4321
  import { writeFileSync as writeFileSync5, readFileSync as readFileSync6, existsSync as existsSync6, mkdirSync as mkdirSync5, lstatSync as lstatSync7 } from "fs";
4194
4322
  import { normalize, isAbsolute, dirname, join as join6 } from "path";
4323
+ import { emit as emit2 } from "@stackwright-pro/telemetry";
4195
4324
  var OTTER_WRITE_ALLOWLISTS = {
4196
4325
  "stackwright-pro-designer-otter": [
4197
4326
  { prefix: ".stackwright/artifacts/", suffix: ".json", description: "Design language artifact" }
@@ -4519,7 +4648,7 @@ function validateContent(filePath, content) {
4519
4648
  if (filePath === ".env" || filePath.startsWith(".env")) return validateEnvContent(content);
4520
4649
  return null;
4521
4650
  }
4522
- function handleSafeWrite(input) {
4651
+ function _safeWriteInner(input) {
4523
4652
  const cwd = input._cwd ?? process.cwd();
4524
4653
  const { callerOtter, filePath, content, createDirectories = true } = input;
4525
4654
  const check = checkPathAllowed(callerOtter, filePath);
@@ -4627,6 +4756,31 @@ function handleSafeWrite(input) {
4627
4756
  return { text: JSON.stringify(result), isError: true };
4628
4757
  }
4629
4758
  }
4759
+ function handleSafeWrite(input) {
4760
+ const cwd = input._cwd ?? process.cwd();
4761
+ const { filePath, callerOtter } = input;
4762
+ emit2(
4763
+ { type: "tool_call", toolName: "stackwright_pro_safe_write", args: { callerOtter, filePath } },
4764
+ { cwd }
4765
+ );
4766
+ const result = _safeWriteInner(input);
4767
+ try {
4768
+ const parsed = JSON.parse(result.text);
4769
+ if (parsed.success === true && parsed.path) {
4770
+ emit2({ type: "file_write", path: parsed.path, bytes: parsed.bytesWritten }, { cwd });
4771
+ }
4772
+ emit2(
4773
+ {
4774
+ type: "tool_complete",
4775
+ toolName: "stackwright_pro_safe_write",
4776
+ success: parsed.success === true
4777
+ },
4778
+ { cwd }
4779
+ );
4780
+ } catch {
4781
+ }
4782
+ return result;
4783
+ }
4630
4784
  function registerSafeWriteTools(server2) {
4631
4785
  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.";
4632
4786
  server2.tool(
@@ -5568,63 +5722,63 @@ import { join as join8, basename } from "path";
5568
5722
  var _checksums = /* @__PURE__ */ new Map([
5569
5723
  [
5570
5724
  "stackwright-pro-api-otter.json",
5571
- "822b35d7a330ed8ac0b42a63b0f70a41885aa9b5ea23cc5b8b998b549da4c05c"
5725
+ "b3ad28c4404af9fb3c2cfce258c707dadd37bc59ac236e1f46d6163cd8d5dc9b"
5572
5726
  ],
5573
5727
  [
5574
5728
  "stackwright-pro-auth-otter.json",
5575
- "d6cd5732667018d99456be77d5955e454da7a0999a0330b5f03a483aa1b9b33f"
5729
+ "1a21d9f23e250f23291124307e5a2a32af5a716566319a3290372dae3d02e637"
5576
5730
  ],
5577
5731
  [
5578
5732
  "stackwright-pro-dashboard-otter.json",
5579
- "19268b1ab423bfbb628ebfbc9f6767d5c0bfedd03963c6d445a2724a8bb78f9c"
5733
+ "d9277616db5680ce3d598908fa3a1b0d0f626e33948785a4c64703b85e463b31"
5580
5734
  ],
5581
5735
  [
5582
5736
  "stackwright-pro-data-otter.json",
5583
- "bb66eaab31c6872536dca4d3ff145724a46356946dd0665cc4f0346714d3bacb"
5737
+ "536cba95f32667e81ae4b6335378a095f91095598defff0de00af231ebdbc488"
5584
5738
  ],
5585
5739
  [
5586
5740
  "stackwright-pro-designer-otter.json",
5587
- "b54121c6a2ab5b1ad68c1262c58b2e04e6315feacd6bb8de8e78eb36f2fa6be6"
5741
+ "69a6c09fbb54f971c48eae7fd52faa63cf79be2923e435aca9ff802e8d541d0f"
5588
5742
  ],
5589
5743
  [
5590
5744
  "stackwright-pro-domain-expert-otter.json",
5591
- "1f21b8ff3450bdae29a4d31b31462ba22583995a448af3c11ab0a5071f46bd72"
5745
+ "14d77cade020f44d1b5a2b406e2599501f3466ee90ca4248500a441d419bc59c"
5592
5746
  ],
5593
5747
  [
5594
5748
  "stackwright-pro-foreman-otter.json",
5595
- "abb1cc5e40a8c5ff98057273f43a9e90e2791a15951090cd4d98407c0c3618e3"
5749
+ "9cbe27b8193ebec2ea916aee82d8bdc6c811cc64735923f36b93545a8f1f9551"
5596
5750
  ],
5597
5751
  [
5598
5752
  "stackwright-pro-form-wizard-otter.json",
5599
- "975ad68e8fb6fe5a10373be278946fa4d9d7ec2238a0b2bd9e4a412e5b1fdd6d"
5753
+ "7f6f94192f26face57dc8b2e22e0a49d23ceeeb356ca4ebde05492f1b25e3c47"
5600
5754
  ],
5601
5755
  [
5602
5756
  "stackwright-pro-geo-otter.json",
5603
- "eef152e5b58947c8eb1ffec2c37acffc39f84b61be4c6cb827310f052221935b"
5757
+ "15f2327b69f75a9c772c2acad5a5d6d3daefb7b55a079aca3205ecd4f33ae349"
5604
5758
  ],
5605
5759
  [
5606
5760
  "stackwright-pro-page-otter.json",
5607
- "ddfa497a4d4980080fa2918aec4d6dfd78d5c28ec6426b4b52f4f26fccaddabb"
5761
+ "c467c5d03b69d87fb1efa9482674a25e2f63782189db74b65309928d47456176"
5608
5762
  ],
5609
5763
  [
5610
5764
  "stackwright-pro-polish-otter.json",
5611
- "5e6532c1fe0737ed83b1f46dd55642e1076adb6ef23d4de636523eb3d88d3087"
5765
+ "1cfea28e121e30181b0a7c8bda0700ffc892a6f919a0ec64996a4cf3f7735b51"
5612
5766
  ],
5613
5767
  [
5614
5768
  "stackwright-pro-qa-otter.json",
5615
- "ecb1e76170723fce43f515044e304d9da32253dadecbf29bf08c90bb8f375f77"
5769
+ "822075893bb4f8496f3dca7b0e68eda460a6fbde87480c13c194582eb7744318"
5616
5770
  ],
5617
5771
  [
5618
5772
  "stackwright-pro-scaffold-otter.json",
5619
- "96ac7754b54c15732206cd4d8043b558d0c465757a0bc70fae6b498d6f02f22a"
5773
+ "341d74ff2be24893ed174e869dc40c090f82e25e11d412d63519944512c37f15"
5620
5774
  ],
5621
5775
  [
5622
5776
  "stackwright-pro-theme-otter.json",
5623
- "fb62e56642f7f94c50ce173f3e1ce978b3f20ab1567e87403b901d6dd991a7db"
5777
+ "b0eccc1945e30b80a0f9a3209c5428e8e32cd09063bd9b2ef7ecb891343774f2"
5624
5778
  ],
5625
5779
  [
5626
5780
  "stackwright-services-otter.json",
5627
- "0a5dac670482871c718099767b30bf23d8ec57e942bd40a00ce295926d82c6bd"
5781
+ "8997bdd9a6fd3e6e02563578a1649480bc21acf919534ae41f73e70ade7cf500"
5628
5782
  ]
5629
5783
  ]);
5630
5784
  Object.freeze(_checksums);
@@ -7072,14 +7226,17 @@ var package_default = {
7072
7226
  "@stackwright-pro/cli-data-explorer": "workspace:*",
7073
7227
  "@stackwright-pro/openapi": "workspace:*",
7074
7228
  "@stackwright-pro/pulse": "workspace:*",
7229
+ "@stackwright-pro/telemetry": "workspace:*",
7075
7230
  "@stackwright-pro/types": "workspace:*",
7076
7231
  "@stackwright/mcp": "0.6.0-alpha.1",
7077
7232
  "@types/js-yaml": "^4.0.9",
7078
7233
  "js-yaml": "^4.2.0",
7234
+ "proper-lockfile": "^4.1.2",
7079
7235
  zod: "^4.4.3"
7080
7236
  },
7081
7237
  devDependencies: {
7082
7238
  "@types/node": "catalog:",
7239
+ "@types/proper-lockfile": "^4.1.4",
7083
7240
  tsup: "catalog:",
7084
7241
  typescript: "catalog:",
7085
7242
  vitest: "catalog:"
@@ -7093,7 +7250,7 @@ var package_default = {
7093
7250
  "test:coverage": "vitest run --coverage"
7094
7251
  },
7095
7252
  name: "@stackwright-pro/mcp",
7096
- version: "0.2.0-alpha.97",
7253
+ version: "0.2.0-alpha.99",
7097
7254
  description: "MCP tools for Stackwright Pro - Data Explorer, Security, ISR, and Dashboard generation",
7098
7255
  license: "SEE LICENSE IN LICENSE",
7099
7256
  main: "./dist/server.js",