@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.js CHANGED
@@ -2024,6 +2024,7 @@ function registerOrchestrationTools(server2) {
2024
2024
  // src/tools/pipeline.ts
2025
2025
  var import_zod13 = require("zod");
2026
2026
  var import_fs6 = require("fs");
2027
+ var import_proper_lockfile = require("proper-lockfile");
2027
2028
  var import_path6 = require("path");
2028
2029
  var import_crypto3 = require("crypto");
2029
2030
 
@@ -2905,6 +2906,7 @@ function loadPipelineGraph() {
2905
2906
  }
2906
2907
 
2907
2908
  // src/tools/pipeline.ts
2909
+ var import_telemetry = require("@stackwright-pro/telemetry");
2908
2910
  var PHASE_ORDER = [
2909
2911
  "designer",
2910
2912
  "theme",
@@ -3015,6 +3017,27 @@ function writeState(cwd, state) {
3015
3017
  state.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3016
3018
  safeWriteSync(statePath(cwd), JSON.stringify(state, null, 2) + "\n");
3017
3019
  }
3020
+ function updateStateAtomic(cwd, mutator) {
3021
+ const dir = (0, import_path6.join)(cwd, ".stackwright");
3022
+ (0, import_fs6.mkdirSync)(dir, { recursive: true });
3023
+ const path4 = statePath(cwd);
3024
+ if (!(0, import_fs6.existsSync)(path4)) {
3025
+ safeWriteSync(path4, JSON.stringify(createDefaultState(), null, 2) + "\n");
3026
+ }
3027
+ const release = (0, import_proper_lockfile.lockSync)(path4, {
3028
+ realpath: false,
3029
+ stale: 1e4
3030
+ // Treat locks older than 10s as stale (guards against crash mid-lock)
3031
+ });
3032
+ try {
3033
+ const state = readState(cwd);
3034
+ mutator(state);
3035
+ writeState(cwd, state);
3036
+ return state;
3037
+ } finally {
3038
+ release();
3039
+ }
3040
+ }
3018
3041
  function extractJsonFromResponse(text) {
3019
3042
  let cleaned = text;
3020
3043
  cleaned = cleaned.replace(/```(?:json)?\s*/gi, "");
@@ -3090,35 +3113,57 @@ function handleSetPipelineState(input) {
3090
3113
  }
3091
3114
  }
3092
3115
  try {
3093
- const state = readState(cwd);
3094
- if (input.status) {
3095
- state.status = input.status;
3096
- }
3097
- if (input.phase) {
3098
- const phase = input.phase;
3099
- if (!state.phases[phase]) {
3100
- state.phases[phase] = defaultPhaseStatus();
3116
+ const state = updateStateAtomic(cwd, (state2) => {
3117
+ if (input.status) {
3118
+ state2.status = input.status;
3119
+ }
3120
+ if (input.phase) {
3121
+ const phase = input.phase;
3122
+ if (!state2.phases[phase]) {
3123
+ state2.phases[phase] = defaultPhaseStatus();
3124
+ }
3125
+ const phaseState = state2.phases[phase];
3126
+ if (input.field && input.value !== void 0) {
3127
+ phaseState[input.field] = input.value;
3128
+ }
3129
+ if (input.incrementRetry) {
3130
+ phaseState.retryCount += 1;
3131
+ }
3132
+ state2.currentPhase = phase;
3101
3133
  }
3102
- const phaseState = state.phases[phase];
3103
- if (input.field && input.value !== void 0) {
3104
- phaseState[input.field] = input.value;
3134
+ if (input.updates && input.updates.length > 0) {
3135
+ for (const update of input.updates) {
3136
+ if (!state2.phases[update.phase]) {
3137
+ state2.phases[update.phase] = defaultPhaseStatus();
3138
+ }
3139
+ const batchPhaseState = state2.phases[update.phase];
3140
+ batchPhaseState[update.field] = update.value;
3141
+ state2.currentPhase = update.phase;
3142
+ }
3105
3143
  }
3106
- if (input.incrementRetry) {
3107
- phaseState.retryCount += 1;
3144
+ });
3145
+ try {
3146
+ const completedPhases = Object.entries(state.phases).filter(([, ps]) => ps.artifactWritten).map(([p]) => p);
3147
+ (0, import_telemetry.emit)(
3148
+ {
3149
+ type: "pipeline_state_change",
3150
+ state: state.status,
3151
+ phases: { ready: [], completed: completedPhases, blocked: [] }
3152
+ },
3153
+ { cwd }
3154
+ );
3155
+ if (input.field === "artifactWritten" && input.value === true && input.phase) {
3156
+ (0, import_telemetry.emit)({ type: "phase_complete", phase: input.phase }, { cwd });
3108
3157
  }
3109
- state.currentPhase = phase;
3110
- }
3111
- if (input.updates && input.updates.length > 0) {
3112
- for (const update of input.updates) {
3113
- if (!state.phases[update.phase]) {
3114
- state.phases[update.phase] = defaultPhaseStatus();
3158
+ if (input.updates) {
3159
+ for (const update of input.updates) {
3160
+ if (update.field === "artifactWritten" && update.value === true) {
3161
+ (0, import_telemetry.emit)({ type: "phase_complete", phase: update.phase }, { cwd });
3162
+ }
3115
3163
  }
3116
- const batchPhaseState = state.phases[update.phase];
3117
- batchPhaseState[update.field] = update.value;
3118
- state.currentPhase = update.phase;
3119
3164
  }
3165
+ } catch {
3120
3166
  }
3121
- writeState(cwd, state);
3122
3167
  return { text: JSON.stringify(state), isError: false };
3123
3168
  } catch (err) {
3124
3169
  return { text: JSON.stringify({ error: true, message: String(err) }), isError: true };
@@ -3311,11 +3356,11 @@ function handleWritePhaseQuestions(input) {
3311
3356
  requiredPackages
3312
3357
  };
3313
3358
  safeWriteSync(filePath, JSON.stringify(payload, null, 2) + "\n");
3314
- const state = readState(cwd);
3315
- if (!state.phases[phase]) state.phases[phase] = defaultPhaseStatus();
3316
- const ps = state.phases[phase];
3317
- ps.questionsCollected = true;
3318
- writeState(cwd, state);
3359
+ updateStateAtomic(cwd, (state) => {
3360
+ if (!state.phases[phase]) state.phases[phase] = defaultPhaseStatus();
3361
+ const ps = state.phases[phase];
3362
+ ps.questionsCollected = true;
3363
+ });
3319
3364
  return {
3320
3365
  text: JSON.stringify({
3321
3366
  success: true,
@@ -3824,7 +3869,7 @@ var PHASE_ARTIFACT_SCHEMA = {
3824
3869
  2
3825
3870
  )
3826
3871
  };
3827
- function handleValidateArtifact(input) {
3872
+ function _validateArtifactInner(input) {
3828
3873
  const cwd = input._cwd ?? process.cwd();
3829
3874
  const { phase, responseText, artifact: directArtifact } = input;
3830
3875
  if (!isValidPhase2(phase)) {
@@ -4028,11 +4073,11 @@ function handleValidateArtifact(input) {
4028
4073
  signed = true;
4029
4074
  } catch {
4030
4075
  }
4031
- const state = readState(cwd);
4032
- if (!state.phases[phase]) state.phases[phase] = defaultPhaseStatus();
4033
- const ps = state.phases[phase];
4034
- ps.artifactWritten = true;
4035
- writeState(cwd, state);
4076
+ updateStateAtomic(cwd, (state) => {
4077
+ if (!state.phases[phase]) state.phases[phase] = defaultPhaseStatus();
4078
+ const ps = state.phases[phase];
4079
+ ps.artifactWritten = true;
4080
+ });
4036
4081
  const topKeys = Object.keys(artifact).slice(0, 5).join(", ");
4037
4082
  const result = {
4038
4083
  valid: true,
@@ -4046,6 +4091,32 @@ function handleValidateArtifact(input) {
4046
4091
  return { text: JSON.stringify({ error: true, phase, message }), isError: true };
4047
4092
  }
4048
4093
  }
4094
+ function handleValidateArtifact(input) {
4095
+ const cwd = input._cwd ?? process.cwd();
4096
+ const { phase } = input;
4097
+ (0, import_telemetry.emit)(
4098
+ { type: "tool_call", toolName: "stackwright_pro_validate_artifact", args: { phase }, phase },
4099
+ { cwd }
4100
+ );
4101
+ const result = _validateArtifactInner(input);
4102
+ try {
4103
+ const parsed = JSON.parse(result.text);
4104
+ if (parsed.valid === true && parsed.artifactPath) {
4105
+ (0, import_telemetry.emit)({ type: "file_write", path: parsed.artifactPath, phase }, { cwd });
4106
+ }
4107
+ (0, import_telemetry.emit)(
4108
+ {
4109
+ type: "tool_complete",
4110
+ toolName: "stackwright_pro_validate_artifact",
4111
+ success: parsed.valid === true,
4112
+ phase
4113
+ },
4114
+ { cwd }
4115
+ );
4116
+ } catch {
4117
+ }
4118
+ return result;
4119
+ }
4049
4120
  function registerPipelineTools(server2) {
4050
4121
  const DESC = "Writes state to .stackwright/ \u2014 the filesystem is the state machine.";
4051
4122
  const res = (r) => ({
@@ -4182,12 +4253,70 @@ function registerPipelineTools(server2) {
4182
4253
  return res(handleValidateArtifact({ phase, responseText: responseText ?? "" }));
4183
4254
  }
4184
4255
  );
4256
+ server2.tool(
4257
+ "stackwright_pro_emit_event",
4258
+ `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}`,
4259
+ {
4260
+ type: import_zod13.z.enum(["phase_start", "phase_complete", "agent_invoke_start", "agent_invoke_complete"]).describe("Event type to emit"),
4261
+ phase: import_zod13.z.string().optional().describe("Current phase name"),
4262
+ targetOtter: import_zod13.z.string().optional().describe("For agent_invoke_* events: which otter is being invoked"),
4263
+ success: import_zod13.z.boolean().optional().describe("For agent_invoke_complete: did the invocation succeed?"),
4264
+ otter: import_zod13.z.string().optional().describe('Which otter is emitting (defaults to "foreman")'),
4265
+ durationSec: import_zod13.z.number().optional().describe("Duration in seconds (for *_complete events)")
4266
+ },
4267
+ async ({ type, phase, targetOtter, success, otter, durationSec }) => {
4268
+ let emitted = false;
4269
+ try {
4270
+ if (type === "phase_start") {
4271
+ if (!phase) {
4272
+ return res({
4273
+ text: JSON.stringify({ emitted: false, reason: "phase required for phase_start" }),
4274
+ isError: false
4275
+ });
4276
+ }
4277
+ emitted = (0, import_telemetry.emit)({ type: "phase_start", phase, otter: otter ?? "foreman" });
4278
+ } else if (type === "phase_complete") {
4279
+ if (!phase) {
4280
+ return res({
4281
+ text: JSON.stringify({ emitted: false, reason: "phase required for phase_complete" }),
4282
+ isError: false
4283
+ });
4284
+ }
4285
+ emitted = (0, import_telemetry.emit)({
4286
+ type: "phase_complete",
4287
+ phase,
4288
+ otter: otter ?? "foreman",
4289
+ ...durationSec != null ? { durationSec } : {}
4290
+ });
4291
+ } else if (type === "agent_invoke_start") {
4292
+ emitted = (0, import_telemetry.emit)({
4293
+ type: "agent_invoke_start",
4294
+ targetOtter: targetOtter ?? "",
4295
+ phase,
4296
+ otter: otter ?? "foreman"
4297
+ });
4298
+ } else if (type === "agent_invoke_complete") {
4299
+ emitted = (0, import_telemetry.emit)({
4300
+ type: "agent_invoke_complete",
4301
+ targetOtter: targetOtter ?? "",
4302
+ success: success ?? true,
4303
+ phase,
4304
+ otter: otter ?? "foreman",
4305
+ ...durationSec != null ? { durationSec } : {}
4306
+ });
4307
+ }
4308
+ } catch {
4309
+ }
4310
+ return res({ text: JSON.stringify({ emitted }), isError: false });
4311
+ }
4312
+ );
4185
4313
  }
4186
4314
 
4187
4315
  // src/tools/safe-write.ts
4188
4316
  var import_zod14 = require("zod");
4189
4317
  var import_fs7 = require("fs");
4190
4318
  var import_path7 = require("path");
4319
+ var import_telemetry2 = require("@stackwright-pro/telemetry");
4191
4320
  var OTTER_WRITE_ALLOWLISTS = {
4192
4321
  "stackwright-pro-designer-otter": [
4193
4322
  { prefix: ".stackwright/artifacts/", suffix: ".json", description: "Design language artifact" }
@@ -4515,7 +4644,7 @@ function validateContent(filePath, content) {
4515
4644
  if (filePath === ".env" || filePath.startsWith(".env")) return validateEnvContent(content);
4516
4645
  return null;
4517
4646
  }
4518
- function handleSafeWrite(input) {
4647
+ function _safeWriteInner(input) {
4519
4648
  const cwd = input._cwd ?? process.cwd();
4520
4649
  const { callerOtter, filePath, content, createDirectories = true } = input;
4521
4650
  const check = checkPathAllowed(callerOtter, filePath);
@@ -4623,6 +4752,31 @@ function handleSafeWrite(input) {
4623
4752
  return { text: JSON.stringify(result), isError: true };
4624
4753
  }
4625
4754
  }
4755
+ function handleSafeWrite(input) {
4756
+ const cwd = input._cwd ?? process.cwd();
4757
+ const { filePath, callerOtter } = input;
4758
+ (0, import_telemetry2.emit)(
4759
+ { type: "tool_call", toolName: "stackwright_pro_safe_write", args: { callerOtter, filePath } },
4760
+ { cwd }
4761
+ );
4762
+ const result = _safeWriteInner(input);
4763
+ try {
4764
+ const parsed = JSON.parse(result.text);
4765
+ if (parsed.success === true && parsed.path) {
4766
+ (0, import_telemetry2.emit)({ type: "file_write", path: parsed.path, bytes: parsed.bytesWritten }, { cwd });
4767
+ }
4768
+ (0, import_telemetry2.emit)(
4769
+ {
4770
+ type: "tool_complete",
4771
+ toolName: "stackwright_pro_safe_write",
4772
+ success: parsed.success === true
4773
+ },
4774
+ { cwd }
4775
+ );
4776
+ } catch {
4777
+ }
4778
+ return result;
4779
+ }
4626
4780
  function registerSafeWriteTools(server2) {
4627
4781
  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.";
4628
4782
  server2.tool(
@@ -5564,63 +5718,63 @@ var import_path9 = require("path");
5564
5718
  var _checksums = /* @__PURE__ */ new Map([
5565
5719
  [
5566
5720
  "stackwright-pro-api-otter.json",
5567
- "822b35d7a330ed8ac0b42a63b0f70a41885aa9b5ea23cc5b8b998b549da4c05c"
5721
+ "b3ad28c4404af9fb3c2cfce258c707dadd37bc59ac236e1f46d6163cd8d5dc9b"
5568
5722
  ],
5569
5723
  [
5570
5724
  "stackwright-pro-auth-otter.json",
5571
- "d6cd5732667018d99456be77d5955e454da7a0999a0330b5f03a483aa1b9b33f"
5725
+ "1a21d9f23e250f23291124307e5a2a32af5a716566319a3290372dae3d02e637"
5572
5726
  ],
5573
5727
  [
5574
5728
  "stackwright-pro-dashboard-otter.json",
5575
- "19268b1ab423bfbb628ebfbc9f6767d5c0bfedd03963c6d445a2724a8bb78f9c"
5729
+ "d9277616db5680ce3d598908fa3a1b0d0f626e33948785a4c64703b85e463b31"
5576
5730
  ],
5577
5731
  [
5578
5732
  "stackwright-pro-data-otter.json",
5579
- "bb66eaab31c6872536dca4d3ff145724a46356946dd0665cc4f0346714d3bacb"
5733
+ "536cba95f32667e81ae4b6335378a095f91095598defff0de00af231ebdbc488"
5580
5734
  ],
5581
5735
  [
5582
5736
  "stackwright-pro-designer-otter.json",
5583
- "b54121c6a2ab5b1ad68c1262c58b2e04e6315feacd6bb8de8e78eb36f2fa6be6"
5737
+ "69a6c09fbb54f971c48eae7fd52faa63cf79be2923e435aca9ff802e8d541d0f"
5584
5738
  ],
5585
5739
  [
5586
5740
  "stackwright-pro-domain-expert-otter.json",
5587
- "1f21b8ff3450bdae29a4d31b31462ba22583995a448af3c11ab0a5071f46bd72"
5741
+ "14d77cade020f44d1b5a2b406e2599501f3466ee90ca4248500a441d419bc59c"
5588
5742
  ],
5589
5743
  [
5590
5744
  "stackwright-pro-foreman-otter.json",
5591
- "abb1cc5e40a8c5ff98057273f43a9e90e2791a15951090cd4d98407c0c3618e3"
5745
+ "9cbe27b8193ebec2ea916aee82d8bdc6c811cc64735923f36b93545a8f1f9551"
5592
5746
  ],
5593
5747
  [
5594
5748
  "stackwright-pro-form-wizard-otter.json",
5595
- "975ad68e8fb6fe5a10373be278946fa4d9d7ec2238a0b2bd9e4a412e5b1fdd6d"
5749
+ "7f6f94192f26face57dc8b2e22e0a49d23ceeeb356ca4ebde05492f1b25e3c47"
5596
5750
  ],
5597
5751
  [
5598
5752
  "stackwright-pro-geo-otter.json",
5599
- "eef152e5b58947c8eb1ffec2c37acffc39f84b61be4c6cb827310f052221935b"
5753
+ "15f2327b69f75a9c772c2acad5a5d6d3daefb7b55a079aca3205ecd4f33ae349"
5600
5754
  ],
5601
5755
  [
5602
5756
  "stackwright-pro-page-otter.json",
5603
- "ddfa497a4d4980080fa2918aec4d6dfd78d5c28ec6426b4b52f4f26fccaddabb"
5757
+ "c467c5d03b69d87fb1efa9482674a25e2f63782189db74b65309928d47456176"
5604
5758
  ],
5605
5759
  [
5606
5760
  "stackwright-pro-polish-otter.json",
5607
- "5e6532c1fe0737ed83b1f46dd55642e1076adb6ef23d4de636523eb3d88d3087"
5761
+ "1cfea28e121e30181b0a7c8bda0700ffc892a6f919a0ec64996a4cf3f7735b51"
5608
5762
  ],
5609
5763
  [
5610
5764
  "stackwright-pro-qa-otter.json",
5611
- "ecb1e76170723fce43f515044e304d9da32253dadecbf29bf08c90bb8f375f77"
5765
+ "822075893bb4f8496f3dca7b0e68eda460a6fbde87480c13c194582eb7744318"
5612
5766
  ],
5613
5767
  [
5614
5768
  "stackwright-pro-scaffold-otter.json",
5615
- "96ac7754b54c15732206cd4d8043b558d0c465757a0bc70fae6b498d6f02f22a"
5769
+ "341d74ff2be24893ed174e869dc40c090f82e25e11d412d63519944512c37f15"
5616
5770
  ],
5617
5771
  [
5618
5772
  "stackwright-pro-theme-otter.json",
5619
- "fb62e56642f7f94c50ce173f3e1ce978b3f20ab1567e87403b901d6dd991a7db"
5773
+ "b0eccc1945e30b80a0f9a3209c5428e8e32cd09063bd9b2ef7ecb891343774f2"
5620
5774
  ],
5621
5775
  [
5622
5776
  "stackwright-services-otter.json",
5623
- "0a5dac670482871c718099767b30bf23d8ec57e942bd40a00ce295926d82c6bd"
5777
+ "8997bdd9a6fd3e6e02563578a1649480bc21acf919534ae41f73e70ade7cf500"
5624
5778
  ]
5625
5779
  ]);
5626
5780
  Object.freeze(_checksums);
@@ -7059,14 +7213,17 @@ var package_default = {
7059
7213
  "@stackwright-pro/cli-data-explorer": "workspace:*",
7060
7214
  "@stackwright-pro/openapi": "workspace:*",
7061
7215
  "@stackwright-pro/pulse": "workspace:*",
7216
+ "@stackwright-pro/telemetry": "workspace:*",
7062
7217
  "@stackwright-pro/types": "workspace:*",
7063
7218
  "@stackwright/mcp": "0.6.0-alpha.1",
7064
7219
  "@types/js-yaml": "^4.0.9",
7065
7220
  "js-yaml": "^4.2.0",
7221
+ "proper-lockfile": "^4.1.2",
7066
7222
  zod: "^4.4.3"
7067
7223
  },
7068
7224
  devDependencies: {
7069
7225
  "@types/node": "catalog:",
7226
+ "@types/proper-lockfile": "^4.1.4",
7070
7227
  tsup: "catalog:",
7071
7228
  typescript: "catalog:",
7072
7229
  vitest: "catalog:"
@@ -7080,7 +7237,7 @@ var package_default = {
7080
7237
  "test:coverage": "vitest run --coverage"
7081
7238
  },
7082
7239
  name: "@stackwright-pro/mcp",
7083
- version: "0.2.0-alpha.97",
7240
+ version: "0.2.0-alpha.99",
7084
7241
  description: "MCP tools for Stackwright Pro - Data Explorer, Security, ISR, and Dashboard generation",
7085
7242
  license: "SEE LICENSE IN LICENSE",
7086
7243
  main: "./dist/server.js",