@compr/opscontext-mcp 2.5.2 โ†’ 2.5.3

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.
Files changed (2) hide show
  1. package/dist/cli.js +52 -3
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -226,6 +226,52 @@ function generateMcpJson() {
226
226
  // ---------------------------------------------------------------------------
227
227
  // Template for pre-commit hook (CE doc freshness + secret scanner)
228
228
  // ---------------------------------------------------------------------------
229
+ /**
230
+ * ๐Ÿ”’ LOCKED [SKIPPING-A-HOOK-MUST-NAME-WHAT-IS-UNENFORCED] โ€” 2026-08-20
231
+ * โ›” NEVER print a bare "already exists, skipping" for a hook slot. NEVER overwrite or
232
+ * append to a foreign hook either.
233
+ * WHY: `init` refuses to clobber an existing hook, which is right, but it said so with one
234
+ * grey line among a column of green ticks. The user reads "init done" and believes the
235
+ * policy gates are live; in that repo they were never installed and enforce nothing.
236
+ * This is exactly ยงE1 arriving through a different door: there, the rule existed and no
237
+ * hook called it; here, a hook exists and does not call the rule. Both produce a gate
238
+ * that is present in every document and absent at runtime, and neither produces an error.
239
+ * A real instance is on this machine: invocme-odoo-connector has a hand-written
240
+ * pre-commit, so `init` skipped it, and none of that repo's CE gates have ever run.
241
+ * Appending instead is not the fix, and is how that repo ended up with a whole secret
242
+ * scanner sitting unreachable behind an earlier `exit 0`.
243
+ * FIX: on skip, read the existing hook. If it does not invoke the CE CLI, say which gates are
244
+ * therefore unenforced and print the one line that wires them in. Silence here reads as
245
+ * success.
246
+ */
247
+ function existingHookInvokesCE(path) {
248
+ try {
249
+ const body = readFileSync(path, "utf-8");
250
+ return /\b(contextengine|opscontext)\b/.test(body);
251
+ }
252
+ catch {
253
+ // Unreadable is not proof of absence; say nothing rather than claim a verdict.
254
+ return true;
255
+ }
256
+ }
257
+ function warnSkippedHook(path, slot) {
258
+ if (existingHookInvokesCE(path))
259
+ return;
260
+ const gates = {
261
+ "pre-commit": "secret-scan, doc-coverage, rule-parity",
262
+ "commit-msg": "commit-message-required",
263
+ "post-commit": "audit trail of the push",
264
+ };
265
+ const wire = {
266
+ "pre-commit": 'contextengine hook secret-scan && contextengine hook doc-coverage && contextengine hook rule-parity',
267
+ "commit-msg": 'contextengine hook commit-message-required "$1"',
268
+ "post-commit": "contextengine end-session",
269
+ };
270
+ console.log(` โš ๏ธ that hook never calls ContextEngine, so these enforce NOTHING here:`);
271
+ console.log(` ${gates[slot]}`);
272
+ console.log(` Wire them by adding this line to ${path}:`);
273
+ console.log(` ${wire[slot]}`);
274
+ }
229
275
  function generatePreCommitHook() {
230
276
  const lines = [];
231
277
  lines.push("#!/bin/zsh");
@@ -508,7 +554,8 @@ async function runInit() {
508
554
  const postCommitDest = join(hooksDir, "post-commit");
509
555
  const commitMsgDest = join(hooksDir, "commit-msg");
510
556
  if (existsSync(preCommitDest)) {
511
- console.log(" โญ .git/hooks/pre-commit already exists โ€” skipping");
557
+ console.log(" โญ .git/hooks/pre-commit already exists โ€” skipping (never overwritten)");
558
+ warnSkippedHook(preCommitDest, "pre-commit");
512
559
  skipped++;
513
560
  }
514
561
  else {
@@ -523,7 +570,8 @@ async function runInit() {
523
570
  // [COMMIT-MSG-HOOK-MUST-BE-INSTALLED] โ€” without this, every
524
571
  // commit_message_required rule is silently unenforced.
525
572
  if (existsSync(commitMsgDest)) {
526
- console.log(" โญ .git/hooks/commit-msg already exists โ€” skipping");
573
+ console.log(" โญ .git/hooks/commit-msg already exists โ€” skipping (never overwritten)");
574
+ warnSkippedHook(commitMsgDest, "commit-msg");
527
575
  skipped++;
528
576
  }
529
577
  else {
@@ -536,7 +584,8 @@ async function runInit() {
536
584
  }
537
585
  }
538
586
  if (existsSync(postCommitDest)) {
539
- console.log(" โญ .git/hooks/post-commit already exists โ€” skipping");
587
+ console.log(" โญ .git/hooks/post-commit already exists โ€” skipping (never overwritten)");
588
+ warnSkippedHook(postCommitDest, "post-commit");
540
589
  skipped++;
541
590
  }
542
591
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@compr/opscontext-mcp",
3
- "version": "2.5.2",
3
+ "version": "2.5.3",
4
4
  "description": "OpsContext for AI Agents โ€” read-only fleet visibility (PM2/nginx/Docker/git/cron) + tamper-evident audit log + policy-as-code hooks. The ops + compliance layer Claude Code can't grow natively.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",