speclock 3.5.4 → 4.1.0

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/src/mcp/server.js CHANGED
@@ -220,10 +220,15 @@ server.tool(
220
220
  .describe("Who created this lock"),
221
221
  },
222
222
  async ({ text, tags, source }) => {
223
- const { lockId } = addLock(PROJECT_ROOT, text, tags, source);
223
+ const { lockId, rewritten, rewriteReason } = addLock(PROJECT_ROOT, text, tags, source);
224
+
225
+ // Read the stored lock to get the normalized text
226
+ const brain = readBrain(PROJECT_ROOT);
227
+ const storedLock = brain?.specLock?.items?.find(l => l.id === lockId);
228
+ const storedText = storedLock?.text || text;
224
229
 
225
230
  // Auto-guard related files
226
- const guardResult = autoGuardRelatedFiles(PROJECT_ROOT, text);
231
+ const guardResult = autoGuardRelatedFiles(PROJECT_ROOT, storedText);
227
232
  const guardMsg = guardResult.guarded.length > 0
228
233
  ? `\nAuto-guarded ${guardResult.guarded.length} file(s): ${guardResult.guarded.join(", ")}`
229
234
  : "";
@@ -231,9 +236,14 @@ server.tool(
231
236
  // Sync active locks to package.json
232
237
  syncLocksToPackageJson(PROJECT_ROOT);
233
238
 
239
+ // Report rewrite if it happened
240
+ const rewriteMsg = rewritten
241
+ ? `\n\nSmart Lock Authoring: Rewritten for accuracy.\n Original: "${text}"\n Stored as: "${storedText}"\n Reason: ${rewriteReason}`
242
+ : "";
243
+
234
244
  return {
235
245
  content: [
236
- { type: "text", text: `Lock added (${lockId}): "${text}"${guardMsg}` },
246
+ { type: "text", text: `Lock added (${lockId}): "${storedText}"${guardMsg}${rewriteMsg}` },
237
247
  ],
238
248
  };
239
249
  }
@@ -460,10 +470,10 @@ server.tool(
460
470
  // CONTINUITY PROTECTION TOOLS
461
471
  // ========================================
462
472
 
463
- // Tool 12: speclock_check_conflict (v2.5: uses enforcer hard mode returns isError)
473
+ // Tool 12: speclock_check_conflict (v4.1: hybrid heuristic + Gemini LLM)
464
474
  server.tool(
465
475
  "speclock_check_conflict",
466
- "Check if a proposed action conflicts with any active SpecLock. Use before making significant changes. In hard enforcement mode, conflicts above the threshold will BLOCK the action (isError: true).",
476
+ "Check if a proposed action conflicts with any active SpecLock. Uses fast heuristic + Gemini LLM for universal domain coverage. In hard enforcement mode, conflicts above the threshold will BLOCK the action (isError: true).",
467
477
  {
468
478
  proposedAction: z
469
479
  .string()
@@ -471,18 +481,18 @@ server.tool(
471
481
  .describe("Description of the action you plan to take"),
472
482
  },
473
483
  async ({ proposedAction }) => {
474
- // Try LLM-enhanced check first, fall back to heuristic enforcer
475
- let result;
476
- try {
477
- const { llmCheckConflict } = await import("../core/llm-checker.js");
478
- const llmResult = await llmCheckConflict(PROJECT_ROOT, proposedAction);
479
- if (llmResult) {
480
- result = llmResult;
484
+ // Hybrid check: heuristic first, LLM for grey-zone (1-70%)
485
+ let result = await checkConflictAsync(PROJECT_ROOT, proposedAction);
486
+
487
+ // If async hybrid returned no conflict, also check enforcer for hard mode
488
+ if (!result.hasConflict) {
489
+ const enforced = enforceConflictCheck(PROJECT_ROOT, proposedAction);
490
+ if (enforced.blocked) {
491
+ return {
492
+ content: [{ type: "text", text: enforced.analysis }],
493
+ isError: true,
494
+ };
481
495
  }
482
- } catch (_) {}
483
-
484
- if (!result) {
485
- result = enforceConflictCheck(PROJECT_ROOT, proposedAction);
486
496
  }
487
497
 
488
498
  // In hard mode with blocking conflict, return isError: true