@vibecheck-ai/mcp 24.5.8 → 24.6.2

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/index.js CHANGED
@@ -5752,7 +5752,7 @@ var require_util = __commonJS({
5752
5752
  return path10;
5753
5753
  }
5754
5754
  exports2.normalize = normalize4;
5755
- function join9(aRoot, aPath) {
5755
+ function join10(aRoot, aPath) {
5756
5756
  if (aRoot === "") {
5757
5757
  aRoot = ".";
5758
5758
  }
@@ -5784,7 +5784,7 @@ var require_util = __commonJS({
5784
5784
  }
5785
5785
  return joined;
5786
5786
  }
5787
- exports2.join = join9;
5787
+ exports2.join = join10;
5788
5788
  exports2.isAbsolute = function(aPath) {
5789
5789
  return aPath.charAt(0) === "/" || urlRegexp.test(aPath);
5790
5790
  };
@@ -5957,7 +5957,7 @@ var require_util = __commonJS({
5957
5957
  parsed.path = parsed.path.substring(0, index2 + 1);
5958
5958
  }
5959
5959
  }
5960
- sourceURL = join9(urlGenerate(parsed), sourceURL);
5960
+ sourceURL = join10(urlGenerate(parsed), sourceURL);
5961
5961
  }
5962
5962
  return normalize4(sourceURL);
5963
5963
  }
@@ -219066,27 +219066,29 @@ var require_has_flag = __commonJS({
219066
219066
  }
219067
219067
  });
219068
219068
 
219069
- // ../../node_modules/.pnpm/supports-color@7.2.0/node_modules/supports-color/index.js
219069
+ // ../../node_modules/.pnpm/supports-color@8.1.1/node_modules/supports-color/index.js
219070
219070
  var require_supports_color = __commonJS({
219071
- "../../node_modules/.pnpm/supports-color@7.2.0/node_modules/supports-color/index.js"(exports2, module2) {
219071
+ "../../node_modules/.pnpm/supports-color@8.1.1/node_modules/supports-color/index.js"(exports2, module2) {
219072
219072
  "use strict";
219073
219073
  var os2 = __require("os");
219074
219074
  var tty = __require("tty");
219075
219075
  var hasFlag = require_has_flag();
219076
219076
  var { env: env3 } = process;
219077
- var forceColor;
219077
+ var flagForceColor;
219078
219078
  if (hasFlag("no-color") || hasFlag("no-colors") || hasFlag("color=false") || hasFlag("color=never")) {
219079
- forceColor = 0;
219079
+ flagForceColor = 0;
219080
219080
  } else if (hasFlag("color") || hasFlag("colors") || hasFlag("color=true") || hasFlag("color=always")) {
219081
- forceColor = 1;
219081
+ flagForceColor = 1;
219082
219082
  }
219083
- if ("FORCE_COLOR" in env3) {
219084
- if (env3.FORCE_COLOR === "true") {
219085
- forceColor = 1;
219086
- } else if (env3.FORCE_COLOR === "false") {
219087
- forceColor = 0;
219088
- } else {
219089
- forceColor = env3.FORCE_COLOR.length === 0 ? 1 : Math.min(parseInt(env3.FORCE_COLOR, 10), 3);
219083
+ function envForceColor() {
219084
+ if ("FORCE_COLOR" in env3) {
219085
+ if (env3.FORCE_COLOR === "true") {
219086
+ return 1;
219087
+ }
219088
+ if (env3.FORCE_COLOR === "false") {
219089
+ return 0;
219090
+ }
219091
+ return env3.FORCE_COLOR.length === 0 ? 1 : Math.min(Number.parseInt(env3.FORCE_COLOR, 10), 3);
219090
219092
  }
219091
219093
  }
219092
219094
  function translateLevel(level) {
@@ -219100,15 +219102,22 @@ var require_supports_color = __commonJS({
219100
219102
  has16m: level >= 3
219101
219103
  };
219102
219104
  }
219103
- function supportsColor(haveStream, streamIsTTY) {
219105
+ function supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
219106
+ const noFlagForceColor = envForceColor();
219107
+ if (noFlagForceColor !== void 0) {
219108
+ flagForceColor = noFlagForceColor;
219109
+ }
219110
+ const forceColor = sniffFlags ? flagForceColor : noFlagForceColor;
219104
219111
  if (forceColor === 0) {
219105
219112
  return 0;
219106
219113
  }
219107
- if (hasFlag("color=16m") || hasFlag("color=full") || hasFlag("color=truecolor")) {
219108
- return 3;
219109
- }
219110
- if (hasFlag("color=256")) {
219111
- return 2;
219114
+ if (sniffFlags) {
219115
+ if (hasFlag("color=16m") || hasFlag("color=full") || hasFlag("color=truecolor")) {
219116
+ return 3;
219117
+ }
219118
+ if (hasFlag("color=256")) {
219119
+ return 2;
219120
+ }
219112
219121
  }
219113
219122
  if (haveStream && !streamIsTTY && forceColor === void 0) {
219114
219123
  return 0;
@@ -219125,7 +219134,7 @@ var require_supports_color = __commonJS({
219125
219134
  return 1;
219126
219135
  }
219127
219136
  if ("CI" in env3) {
219128
- if (["TRAVIS", "CIRCLECI", "APPVEYOR", "GITLAB_CI", "GITHUB_ACTIONS", "BUILDKITE"].some((sign) => sign in env3) || env3.CI_NAME === "codeship") {
219137
+ if (["TRAVIS", "CIRCLECI", "APPVEYOR", "GITLAB_CI", "GITHUB_ACTIONS", "BUILDKITE", "DRONE"].some((sign) => sign in env3) || env3.CI_NAME === "codeship") {
219129
219138
  return 1;
219130
219139
  }
219131
219140
  return min2;
@@ -219137,7 +219146,7 @@ var require_supports_color = __commonJS({
219137
219146
  return 3;
219138
219147
  }
219139
219148
  if ("TERM_PROGRAM" in env3) {
219140
- const version3 = parseInt((env3.TERM_PROGRAM_VERSION || "").split(".")[0], 10);
219149
+ const version3 = Number.parseInt((env3.TERM_PROGRAM_VERSION || "").split(".")[0], 10);
219141
219150
  switch (env3.TERM_PROGRAM) {
219142
219151
  case "iTerm.app":
219143
219152
  return version3 >= 3 ? 3 : 2;
@@ -219156,14 +219165,17 @@ var require_supports_color = __commonJS({
219156
219165
  }
219157
219166
  return min2;
219158
219167
  }
219159
- function getSupportLevel(stream) {
219160
- const level = supportsColor(stream, stream && stream.isTTY);
219168
+ function getSupportLevel(stream, options = {}) {
219169
+ const level = supportsColor(stream, {
219170
+ streamIsTTY: stream && stream.isTTY,
219171
+ ...options
219172
+ });
219161
219173
  return translateLevel(level);
219162
219174
  }
219163
219175
  module2.exports = {
219164
219176
  supportsColor: getSupportLevel,
219165
- stdout: translateLevel(supportsColor(true, tty.isatty(1))),
219166
- stderr: translateLevel(supportsColor(true, tty.isatty(2)))
219177
+ stdout: getSupportLevel({ isTTY: tty.isatty(1) }),
219178
+ stderr: getSupportLevel({ isTTY: tty.isatty(2) })
219167
219179
  };
219168
219180
  }
219169
219181
  });
@@ -219532,7 +219544,7 @@ var require_bindings = __commonJS({
219532
219544
  var fs7 = __require("fs");
219533
219545
  var path10 = __require("path");
219534
219546
  var fileURLToPath2 = require_file_uri_to_path();
219535
- var join9 = path10.join;
219547
+ var join10 = path10.join;
219536
219548
  var dirname8 = path10.dirname;
219537
219549
  var exists2 = fs7.accessSync && function(path11) {
219538
219550
  try {
@@ -219592,7 +219604,7 @@ var require_bindings = __commonJS({
219592
219604
  var requireFunc = typeof __webpack_require__ === "function" ? __non_webpack_require__ : __require;
219593
219605
  var tries = [], i2 = 0, l = opts.try.length, n, b, err2;
219594
219606
  for (; i2 < l; i2++) {
219595
- n = join9.apply(
219607
+ n = join10.apply(
219596
219608
  null,
219597
219609
  opts.try[i2].map(function(p) {
219598
219610
  return opts[p] || p;
@@ -219653,7 +219665,7 @@ var require_bindings = __commonJS({
219653
219665
  if (dir === ".") {
219654
219666
  dir = process.cwd();
219655
219667
  }
219656
- if (exists2(join9(dir, "package.json")) || exists2(join9(dir, "node_modules"))) {
219668
+ if (exists2(join10(dir, "package.json")) || exists2(join10(dir, "node_modules"))) {
219657
219669
  return dir;
219658
219670
  }
219659
219671
  if (prev === dir) {
@@ -219662,7 +219674,7 @@ var require_bindings = __commonJS({
219662
219674
  );
219663
219675
  }
219664
219676
  prev = dir;
219665
- dir = join9(dir, "..");
219677
+ dir = join10(dir, "..");
219666
219678
  }
219667
219679
  };
219668
219680
  }
@@ -219676,7 +219688,7 @@ var require_wrappers = __commonJS({
219676
219688
  exports2.prepare = function prepare(sql) {
219677
219689
  return this[cppdb].prepare(sql, this, false);
219678
219690
  };
219679
- exports2.exec = function exec2(sql) {
219691
+ exports2.exec = function exec(sql) {
219680
219692
  this[cppdb].exec(sql);
219681
219693
  return this;
219682
219694
  };
@@ -221643,7 +221655,7 @@ ${JSON.stringify(t2, null, 2)}`);
221643
221655
 
221644
221656
  // ../context-engine/dist/chunk-3ZE34DAJ.js
221645
221657
  import { readFile as readFile2 } from "fs/promises";
221646
- import { join as join3, dirname as dirname4 } from "path";
221658
+ import { join as join3, dirname as dirname5 } from "path";
221647
221659
  import { fileURLToPath } from "url";
221648
221660
  async function initParser() {
221649
221661
  if (ParserClass) return ParserClass;
@@ -221783,7 +221795,7 @@ var init_chunk_3ZE34DAJ = __esm({
221783
221795
  SymbolKind2["Export"] = "export";
221784
221796
  return SymbolKind2;
221785
221797
  })(SymbolKind || {});
221786
- GRAMMAR_DIR = join3(dirname4(fileURLToPath(import.meta.url)), "../../../../node_modules/tree-sitter-wasms/out");
221798
+ GRAMMAR_DIR = join3(dirname5(fileURLToPath(import.meta.url)), "../../../../node_modules/tree-sitter-wasms/out");
221787
221799
  EXT_TO_GRAMMAR = {
221788
221800
  ".ts": "typescript",
221789
221801
  ".tsx": "tsx",
@@ -278734,14 +278746,11 @@ import { pathToFileURL } from "url";
278734
278746
  // src/server.ts
278735
278747
  import * as path9 from "path";
278736
278748
  import * as fs6 from "fs";
278737
- import { exec } from "child_process";
278749
+ import { execFile } from "child_process";
278738
278750
  import { promisify } from "util";
278739
278751
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
278740
278752
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
278741
- import {
278742
- ListToolsRequestSchema,
278743
- CallToolRequestSchema
278744
- } from "@modelcontextprotocol/sdk/types.js";
278753
+ import { ListToolsRequestSchema, CallToolRequestSchema } from "@modelcontextprotocol/sdk/types.js";
278745
278754
  import { runOnFiles } from "@vibecheck-ai/cli/runner";
278746
278755
  import {
278747
278756
  computeTrustScore,
@@ -289076,6 +289085,845 @@ rules:
289076
289085
  match: "src/repositories/**"
289077
289086
  `;
289078
289087
 
289088
+ // ../context-engine/dist/chunk-INBCP46U.js
289089
+ import * as path5 from "path";
289090
+ var ContextSynthesizer = class {
289091
+ rootPath;
289092
+ data;
289093
+ dna;
289094
+ graph;
289095
+ ruleResult;
289096
+ constructor(rootPath, data, dna, graph, ruleResult) {
289097
+ this.rootPath = rootPath;
289098
+ this.data = data;
289099
+ this.dna = dna;
289100
+ this.graph = graph;
289101
+ this.ruleResult = ruleResult || null;
289102
+ }
289103
+ /**
289104
+ * Synthesize the full context — the complete brain dump for AI agents.
289105
+ */
289106
+ synthesize() {
289107
+ const projectIdentity = this.buildProjectIdentity();
289108
+ const archRules = this.buildArchRuleSummary();
289109
+ const activeViolations = this.ruleResult?.violations || [];
289110
+ const codebaseDNA = this.buildDNASummary();
289111
+ const fileContexts = this.buildFileContexts();
289112
+ const taskPlaybooks = this.buildTaskPlaybooks();
289113
+ const verificationSteps = this.buildVerificationSteps();
289114
+ const riskBriefing = this.buildRiskBriefing();
289115
+ return {
289116
+ version: "2.0.0",
289117
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
289118
+ projectIdentity,
289119
+ architecturalRules: archRules,
289120
+ activeViolations,
289121
+ codebaseDNA,
289122
+ fileContexts,
289123
+ taskPlaybooks,
289124
+ verificationSteps,
289125
+ riskBriefing
289126
+ };
289127
+ }
289128
+ /**
289129
+ * Generate context for a specific file — what an AI agent needs to know
289130
+ * before editing this file.
289131
+ */
289132
+ synthesizeForFile(filePath) {
289133
+ const rel = this.toRelative(filePath);
289134
+ const file = this.data.files.find((f) => f.relativePath === rel || f.path === filePath);
289135
+ if (!file) return null;
289136
+ return this.buildSingleFileContext(file);
289137
+ }
289138
+ /**
289139
+ * Generate a compact markdown context document for IDE rules.
289140
+ * This replaces the old static rule generation with intelligence-driven context.
289141
+ */
289142
+ generateContextDocument() {
289143
+ const ctx = this.synthesize();
289144
+ const lines = [];
289145
+ lines.push(`# ${ctx.projectIdentity.name} \u2014 AI Context`);
289146
+ lines.push(`<!-- Generated by @repo/context-engine at ${ctx.generatedAt} -->`);
289147
+ lines.push("");
289148
+ lines.push("## Project Identity");
289149
+ lines.push(`- **Stack**: ${ctx.projectIdentity.stack}`);
289150
+ lines.push(`- **Architecture**: ${ctx.projectIdentity.architecture}`);
289151
+ if (ctx.projectIdentity.keyPatterns.length > 0) {
289152
+ lines.push(`- **Key Patterns**: ${ctx.projectIdentity.keyPatterns.join(", ")}`);
289153
+ }
289154
+ lines.push("");
289155
+ if (ctx.codebaseDNA.conventions.length > 0) {
289156
+ lines.push("## Conventions (Auto-Discovered)");
289157
+ for (const conv of ctx.codebaseDNA.conventions) {
289158
+ lines.push(`- ${conv}`);
289159
+ }
289160
+ lines.push("");
289161
+ }
289162
+ if (ctx.architecturalRules.length > 0) {
289163
+ lines.push("## Architecture Rules");
289164
+ for (const rule of ctx.architecturalRules) {
289165
+ const icon = rule.severity === "error" ? "MUST" : rule.severity === "warning" ? "SHOULD" : "MAY";
289166
+ lines.push(`- **[${icon}]** ${rule.name}: ${rule.description}`);
289167
+ if (rule.violationCount > 0) {
289168
+ lines.push(` - ${rule.violationCount} active violations`);
289169
+ }
289170
+ }
289171
+ lines.push("");
289172
+ }
289173
+ if (ctx.codebaseDNA.boundaries.length > 0) {
289174
+ lines.push("## Module Boundaries");
289175
+ for (const boundary of ctx.codebaseDNA.boundaries) {
289176
+ lines.push(`- ${boundary}`);
289177
+ }
289178
+ lines.push("");
289179
+ }
289180
+ if (ctx.codebaseDNA.hotFiles.length > 0) {
289181
+ lines.push("## High-Impact Files (Edit With Care)");
289182
+ for (const file of ctx.codebaseDNA.hotFiles.slice(0, 10)) {
289183
+ lines.push(`- \`${file}\``);
289184
+ }
289185
+ lines.push("");
289186
+ }
289187
+ if (ctx.riskBriefing.securityConcerns.length > 0 || ctx.riskBriefing.testGaps.length > 0) {
289188
+ lines.push("## Risk Areas");
289189
+ for (const concern of ctx.riskBriefing.securityConcerns) {
289190
+ lines.push(`- ${concern}`);
289191
+ }
289192
+ for (const gap of ctx.riskBriefing.testGaps.slice(0, 5)) {
289193
+ lines.push(`- ${gap}`);
289194
+ }
289195
+ lines.push("");
289196
+ }
289197
+ if (ctx.projectIdentity.noGoZones.length > 0) {
289198
+ lines.push("## No-Go Zones");
289199
+ for (const zone of ctx.projectIdentity.noGoZones) {
289200
+ lines.push(`- ${zone}`);
289201
+ }
289202
+ lines.push("");
289203
+ }
289204
+ if (ctx.taskPlaybooks.length > 0) {
289205
+ lines.push("## Task Playbooks");
289206
+ for (const playbook of ctx.taskPlaybooks) {
289207
+ lines.push(`### ${playbook.taskType}`);
289208
+ for (const step of playbook.steps) {
289209
+ lines.push(`1. ${step}`);
289210
+ }
289211
+ if (playbook.mustVerify.length > 0) {
289212
+ lines.push(`**Verify**: ${playbook.mustVerify.join(", ")}`);
289213
+ }
289214
+ lines.push("");
289215
+ }
289216
+ }
289217
+ if (ctx.verificationSteps.length > 0) {
289218
+ lines.push("## Verification Protocol");
289219
+ for (const step of ctx.verificationSteps) {
289220
+ lines.push(`### On ${step.trigger}`);
289221
+ for (const check of step.checks) {
289222
+ lines.push(`- ${check}`);
289223
+ }
289224
+ if (step.commands.length > 0) {
289225
+ lines.push(`**Run**: \`${step.commands.join(" && ")}\``);
289226
+ }
289227
+ lines.push("");
289228
+ }
289229
+ }
289230
+ lines.push("## Codebase Health");
289231
+ lines.push(`- **Overall**: ${this.dna.healthScore.overall}/100`);
289232
+ const dims = this.dna.healthScore.dimensions;
289233
+ lines.push(`- Architecture: ${dims.architecture} | Tests: ${dims.testCoverage} | Conventions: ${dims.conventions} | Dependencies: ${dims.dependencies}`);
289234
+ lines.push("");
289235
+ lines.push("---");
289236
+ lines.push("<!-- context-engine:v2 -->");
289237
+ return lines.join("\n");
289238
+ }
289239
+ // ═══════════════════════════════════════════════════════════════════════════
289240
+ // IDENTITY
289241
+ // ═══════════════════════════════════════════════════════════════════════════
289242
+ buildProjectIdentity() {
289243
+ const fp = this.dna.fingerprint;
289244
+ const stack = [fp.framework, fp.language, fp.orm, fp.validator, fp.authLib, fp.router].filter(Boolean).join(" | ");
289245
+ const keyPatterns = this.dna.patterns.map((p) => p.name);
289246
+ const criticalPaths = this.dna.hotspots.slice(0, 5).map((h) => h.file);
289247
+ const noGoZones = [];
289248
+ if (this.ruleResult) {
289249
+ const errorRules = this.ruleResult.violations.filter((v) => v.severity === "error");
289250
+ const uniqueMessages = [...new Set(errorRules.map((v) => v.message))];
289251
+ noGoZones.push(...uniqueMessages.slice(0, 5));
289252
+ }
289253
+ for (const cycle of this.graph.cycles) {
289254
+ noGoZones.push(`Circular dependency: ${cycle.nodes.slice(0, 3).join(" \u2192 ")}...`);
289255
+ }
289256
+ const architecture = this.dna.conventions.filter((c) => c.area === "structure").map((c) => c.description).join("; ") || fp.framework;
289257
+ return {
289258
+ name: fp.name,
289259
+ stack,
289260
+ architecture,
289261
+ keyPatterns,
289262
+ criticalPaths,
289263
+ noGoZones: noGoZones.slice(0, 10)
289264
+ };
289265
+ }
289266
+ // ═══════════════════════════════════════════════════════════════════════════
289267
+ // RULES SUMMARY
289268
+ // ═══════════════════════════════════════════════════════════════════════════
289269
+ buildArchRuleSummary() {
289270
+ if (!this.ruleResult) return [];
289271
+ const breakdown = this.ruleResult.ruleBreakdown;
289272
+ return Object.entries(breakdown).map(([ruleId, count]) => {
289273
+ const violation = this.ruleResult.violations.find((v) => v.ruleId === ruleId);
289274
+ return {
289275
+ id: ruleId,
289276
+ name: violation?.ruleName || ruleId,
289277
+ type: "import_forbidden",
289278
+ severity: violation?.severity || "warning",
289279
+ scope: violation?.sourceSymbol.filePath || "",
289280
+ description: violation?.message || "",
289281
+ violationCount: count
289282
+ };
289283
+ });
289284
+ }
289285
+ // ═══════════════════════════════════════════════════════════════════════════
289286
+ // DNA SUMMARY
289287
+ // ═══════════════════════════════════════════════════════════════════════════
289288
+ buildDNASummary() {
289289
+ return {
289290
+ conventions: this.dna.conventions.filter((c) => c.confidence > 0.5).map((c) => c.description),
289291
+ patterns: this.dna.patterns.map((p) => `${p.name}: ${p.description}`),
289292
+ boundaries: this.dna.boundaries.filter((b) => b.importCount > 3).map((b) => `${b.from} \u2192 ${b.to} (${b.importCount} imports${b.isCircular ? ", CIRCULAR" : ""})`),
289293
+ hotFiles: this.dna.hotspots.slice(0, 10).map((h) => h.file),
289294
+ riskAreas: this.dna.riskMap.filter((r) => r.riskLevel === "critical" || r.riskLevel === "high").map((r) => `${r.file}: ${r.factors[0]}`)
289295
+ };
289296
+ }
289297
+ // ═══════════════════════════════════════════════════════════════════════════
289298
+ // FILE CONTEXTS
289299
+ // ═══════════════════════════════════════════════════════════════════════════
289300
+ buildFileContexts() {
289301
+ const contexts = /* @__PURE__ */ new Map();
289302
+ for (const file of this.data.files) {
289303
+ contexts.set(file.relativePath, this.buildSingleFileContext(file));
289304
+ }
289305
+ return contexts;
289306
+ }
289307
+ buildSingleFileContext(file) {
289308
+ const rel = file.relativePath;
289309
+ const role = this.classifyRole(file);
289310
+ const graphNode = this.graph.nodes.find((n) => n.relativePath === rel);
289311
+ const layer = graphNode?.layer;
289312
+ const dependsOn = this.graph.edges.filter((e) => e.from === rel).map((e) => e.to);
289313
+ const dependedOnBy = this.graph.edges.filter((e) => e.to === rel).map((e) => e.from);
289314
+ const applicableRules = [];
289315
+ if (this.ruleResult) {
289316
+ for (const v of this.ruleResult.violations) {
289317
+ if (v.sourceSymbol.filePath.includes(rel) || v.targetSymbol && v.targetSymbol.filePath.includes(rel)) {
289318
+ if (!applicableRules.includes(v.ruleId)) applicableRules.push(v.ruleId);
289319
+ }
289320
+ }
289321
+ }
289322
+ const conventions = this.dna.conventions.filter((c) => this.conventionAppliesToFile(c.area, file)).map((c) => c.description);
289323
+ const patterns = this.dna.patterns.filter((p) => p.fileMatches.some((m) => m === rel)).map((p) => p.name);
289324
+ const riskEntry = this.dna.riskMap.find((r) => r.file === rel);
289325
+ const riskLevel = riskEntry?.riskLevel || "low";
289326
+ const relatedFiles = this.findRelatedFiles(file, role).slice(0, 8);
289327
+ const editGuidance = this.generateEditGuidance(file, role, layer, dependedOnBy, conventions);
289328
+ return {
289329
+ filePath: file.path,
289330
+ role,
289331
+ layer,
289332
+ dependsOn,
289333
+ dependedOnBy,
289334
+ applicableRules,
289335
+ conventions,
289336
+ patterns,
289337
+ riskLevel,
289338
+ relatedFiles,
289339
+ editGuidance
289340
+ };
289341
+ }
289342
+ classifyRole(file) {
289343
+ const rel = file.relativePath.toLowerCase();
289344
+ if (rel.includes(".test.") || rel.includes(".spec.") || rel.includes("__tests__")) return "test";
289345
+ if (rel.includes("fixture") || rel.includes("mock")) return "fixture";
289346
+ if (rel.includes("migration")) return "migration";
289347
+ if (rel.match(/\.(css|scss|less|styl)$/)) return "style";
289348
+ if (rel.includes(".config.") || rel.includes("config/") || rel === "tsconfig.json") return "config";
289349
+ if (rel.includes("middleware")) return "middleware";
289350
+ if (rel.includes("/api/") || rel.includes("route")) return "route-handler";
289351
+ if (rel.includes("service") || rel.includes("Service")) return "service";
289352
+ if (rel.includes("repositor") || rel.includes("Repositor")) return "repository";
289353
+ if (rel.endsWith(".tsx") && !rel.includes("page.")) return "component";
289354
+ if (rel.includes("/types") || rel.endsWith(".d.ts")) return "type";
289355
+ if (rel.includes("util") || rel.includes("helper") || rel.includes("lib/")) return "util";
289356
+ if (rel.includes("script") || rel.includes("bin/")) return "script";
289357
+ if (rel.match(/^(src\/)?index\.|^(src\/)?main\.|^(src\/)?app\./)) return "entry";
289358
+ return "unknown";
289359
+ }
289360
+ conventionAppliesToFile(area, file) {
289361
+ switch (area) {
289362
+ case "naming":
289363
+ return true;
289364
+ case "imports":
289365
+ return file.relativePath.endsWith(".ts") || file.relativePath.endsWith(".tsx");
289366
+ case "exports":
289367
+ return file.exports.length > 0;
289368
+ case "testing":
289369
+ return file.relativePath.includes(".test.") || file.relativePath.includes(".spec.");
289370
+ case "error-handling":
289371
+ return !file.relativePath.includes(".test.");
289372
+ case "types":
289373
+ return file.relativePath.endsWith(".ts") || file.relativePath.endsWith(".tsx");
289374
+ default:
289375
+ return true;
289376
+ }
289377
+ }
289378
+ findRelatedFiles(file, role) {
289379
+ const related = [];
289380
+ const dir = path5.dirname(file.relativePath);
289381
+ for (const other of this.data.files) {
289382
+ if (other.path === file.path) continue;
289383
+ if (path5.dirname(other.relativePath) === dir) {
289384
+ related.push(other.relativePath);
289385
+ }
289386
+ }
289387
+ if (related.length < 5) {
289388
+ for (const other of this.data.files) {
289389
+ if (other.path === file.path) continue;
289390
+ if (related.includes(other.relativePath)) continue;
289391
+ if (this.classifyRole(other) === role) {
289392
+ related.push(other.relativePath);
289393
+ if (related.length >= 8) break;
289394
+ }
289395
+ }
289396
+ }
289397
+ return related;
289398
+ }
289399
+ generateEditGuidance(file, role, layer, dependedOnBy, conventions) {
289400
+ const guidance = [];
289401
+ if (dependedOnBy.length > 10) {
289402
+ guidance.push(`HIGH IMPACT: ${dependedOnBy.length} files depend on this. Changes have wide blast radius.`);
289403
+ }
289404
+ switch (role) {
289405
+ case "route-handler":
289406
+ guidance.push("Validate all inputs with schemas before processing.");
289407
+ guidance.push("Return consistent response shapes ({ success, data } or { success, error }).");
289408
+ guidance.push("Ensure authentication middleware is applied to protected endpoints.");
289409
+ break;
289410
+ case "service":
289411
+ guidance.push("Keep business logic here, not in controllers/routes.");
289412
+ guidance.push("Use dependency injection for testability.");
289413
+ if (layer) guidance.push(`This is in the ${layer} layer \u2014 only import from lower layers.`);
289414
+ break;
289415
+ case "repository":
289416
+ guidance.push("Only data access logic belongs here \u2014 no business rules.");
289417
+ guidance.push("Return domain objects, not raw database rows.");
289418
+ break;
289419
+ case "component":
289420
+ guidance.push("Keep components focused and composable.");
289421
+ guidance.push("Extract complex logic to custom hooks.");
289422
+ break;
289423
+ case "middleware":
289424
+ guidance.push("Middleware must call next() or return a response \u2014 never leave the request hanging.");
289425
+ guidance.push("Keep middleware focused on a single concern.");
289426
+ break;
289427
+ case "test":
289428
+ guidance.push("Follow Arrange-Act-Assert pattern.");
289429
+ guidance.push("Test edge cases and error conditions, not just happy path.");
289430
+ break;
289431
+ }
289432
+ for (const conv of conventions.slice(0, 3)) {
289433
+ guidance.push(`Convention: ${conv}`);
289434
+ }
289435
+ return guidance;
289436
+ }
289437
+ // ═══════════════════════════════════════════════════════════════════════════
289438
+ // TASK PLAYBOOKS
289439
+ // ═══════════════════════════════════════════════════════════════════════════
289440
+ buildTaskPlaybooks() {
289441
+ const fp = this.dna.fingerprint;
289442
+ const playbooks = [];
289443
+ playbooks.push({
289444
+ taskType: "Bug Fix",
289445
+ steps: [
289446
+ "Reproduce the bug and understand the expected vs actual behavior",
289447
+ "Identify the root cause file(s) using the dependency graph",
289448
+ "Write a failing test that reproduces the bug",
289449
+ "Apply the minimal fix at the root cause",
289450
+ "Verify the fix passes the test and does not break existing tests",
289451
+ "Check that the fix does not violate any architecture rules"
289452
+ ],
289453
+ mustRead: this.dna.hotspots.slice(0, 3).map((h) => h.file),
289454
+ mustUpdate: ["The buggy file", "Related test file"],
289455
+ mustVerify: ["All existing tests pass", "New regression test passes", "No new arch rule violations"],
289456
+ stopConditions: ["Never modify tests to make them pass \u2014 fix the code", "Do not change public API signatures without discussion"]
289457
+ });
289458
+ if (fp.router) {
289459
+ playbooks.push({
289460
+ taskType: "Add API Endpoint",
289461
+ steps: [
289462
+ "Check existing routes in truthpack to avoid duplicates",
289463
+ "Create the route handler following existing patterns",
289464
+ "Add input validation using the project validator",
289465
+ "Add authentication middleware if the route is protected",
289466
+ "Write tests for success, validation failure, and auth failure",
289467
+ "Update the truthpack (run vibecheck scan)"
289468
+ ],
289469
+ mustRead: ["truthpack/routes.json", ...this.dna.patterns.filter((p) => p.category === "api").map((p) => p.exemplar)],
289470
+ mustUpdate: ["Route file", "Test file", "Truthpack"],
289471
+ mustVerify: ["Route responds correctly", "Input validation works", "Auth is enforced", "Test passes"],
289472
+ stopConditions: ["Do not create duplicate routes", "Do not hardcode mock data in handlers"]
289473
+ });
289474
+ }
289475
+ if (fp.framework.includes("Next") || fp.framework.includes("React")) {
289476
+ playbooks.push({
289477
+ taskType: "Add UI Component",
289478
+ steps: [
289479
+ "Check if a similar component already exists",
289480
+ "Create the component following existing naming and structure patterns",
289481
+ "Add TypeScript props interface",
289482
+ "Add unit test for the component",
289483
+ "If using state, determine if it should be a client component"
289484
+ ],
289485
+ mustRead: this.dna.patterns.filter((p) => p.category === "ui" || p.category === "state").map((p) => p.exemplar),
289486
+ mustUpdate: ["Component file", "Test file", "Parent component that uses it"],
289487
+ mustVerify: ["Component renders correctly", "Props are typed", "Test passes"],
289488
+ stopConditions: ['Do not use "any" type for props', 'Do not add useState in server components without "use client"']
289489
+ });
289490
+ }
289491
+ playbooks.push({
289492
+ taskType: "Refactor",
289493
+ steps: [
289494
+ "Identify all callers/dependents of the code being refactored",
289495
+ "Ensure comprehensive tests exist before refactoring",
289496
+ "Apply changes incrementally, testing after each step",
289497
+ "Update all dependents to use the new API",
289498
+ "Remove old code only after all dependents are migrated",
289499
+ "Verify no architecture rules are violated"
289500
+ ],
289501
+ mustRead: ["Dependency graph for affected files"],
289502
+ mustUpdate: ["Refactored file", "All dependent files", "Tests"],
289503
+ mustVerify: ["All tests pass", "No new violations", "No regressions"],
289504
+ stopConditions: ["Never break existing public APIs without migration path", "Do not refactor and add features in the same change"]
289505
+ });
289506
+ return playbooks;
289507
+ }
289508
+ // ═══════════════════════════════════════════════════════════════════════════
289509
+ // VERIFICATION STEPS
289510
+ // ═══════════════════════════════════════════════════════════════════════════
289511
+ buildVerificationSteps() {
289512
+ const fp = this.dna.fingerprint;
289513
+ const steps = [];
289514
+ if (fp.language === "TypeScript") {
289515
+ steps.push({
289516
+ trigger: "Any TypeScript file change",
289517
+ checks: ["TypeScript compilation succeeds", "No new type errors introduced"],
289518
+ commands: [fp.packageManager === "pnpm" ? "pnpm run check-types" : "npm run check-types"],
289519
+ artifacts: []
289520
+ });
289521
+ }
289522
+ if (fp.testRunner) {
289523
+ steps.push({
289524
+ trigger: "Any source file change",
289525
+ checks: ["Related tests pass", "No test regressions"],
289526
+ commands: [`${fp.packageManager} run test`],
289527
+ artifacts: ["test-results.json"]
289528
+ });
289529
+ }
289530
+ if (fp.router) {
289531
+ steps.push({
289532
+ trigger: "Route handler added or modified",
289533
+ checks: ["Route responds with correct status", "Auth middleware is applied", "Input validation works"],
289534
+ commands: ["vibecheck scan"],
289535
+ artifacts: ["truthpack/routes.json"]
289536
+ });
289537
+ }
289538
+ steps.push({
289539
+ trigger: "Any source file change",
289540
+ checks: ["No new architecture rule violations", "No new circular dependencies"],
289541
+ commands: ["vibecheck arch-rules"],
289542
+ artifacts: []
289543
+ });
289544
+ return steps;
289545
+ }
289546
+ // ═══════════════════════════════════════════════════════════════════════════
289547
+ // RISK BRIEFING
289548
+ // ═══════════════════════════════════════════════════════════════════════════
289549
+ buildRiskBriefing() {
289550
+ const criticalFiles = this.dna.hotspots.filter((h) => h.score > 30).slice(0, 10).map((h) => h.file);
289551
+ const recentViolations = this.ruleResult?.violations.filter((v) => v.severity === "error").slice(0, 10) || [];
289552
+ const securityConcerns = [];
289553
+ for (const risk of this.dna.riskMap) {
289554
+ if (risk.riskLevel === "critical") {
289555
+ securityConcerns.push(`${risk.file}: ${risk.factors.join(", ")}`);
289556
+ }
289557
+ }
289558
+ const testGaps = [];
289559
+ const sourceFiles = this.data.files.filter(
289560
+ (f) => !f.relativePath.includes(".test.") && !f.relativePath.includes(".spec.") && (f.relativePath.endsWith(".ts") || f.relativePath.endsWith(".tsx")) && !f.relativePath.includes("config") && !f.relativePath.includes(".d.ts")
289561
+ );
289562
+ const testFiles = this.data.files.filter(
289563
+ (f) => f.relativePath.includes(".test.") || f.relativePath.includes(".spec.")
289564
+ );
289565
+ const testedBases = new Set(testFiles.map(
289566
+ (f) => path5.basename(f.relativePath).replace(/\.(test|spec)\.(ts|tsx|js|jsx)$/, "")
289567
+ ));
289568
+ for (const file of sourceFiles) {
289569
+ const baseName = path5.basename(file.relativePath).replace(/\.(ts|tsx|js|jsx)$/, "");
289570
+ if (!testedBases.has(baseName) && file.exports.length > 0) {
289571
+ testGaps.push(`${file.relativePath} has exports but no test file`);
289572
+ }
289573
+ }
289574
+ const driftWarnings = [];
289575
+ for (const cycle of this.graph.cycles) {
289576
+ driftWarnings.push(`Circular dependency: ${cycle.nodes.slice(0, 3).join(" \u2192 ")}${cycle.nodes.length > 3 ? "..." : ""}`);
289577
+ }
289578
+ return {
289579
+ criticalFiles,
289580
+ recentViolations,
289581
+ securityConcerns: securityConcerns.slice(0, 5),
289582
+ testGaps: testGaps.slice(0, 10),
289583
+ driftWarnings: driftWarnings.slice(0, 5)
289584
+ };
289585
+ }
289586
+ // ═══════════════════════════════════════════════════════════════════════════
289587
+ // HELPERS
289588
+ // ═══════════════════════════════════════════════════════════════════════════
289589
+ toRelative(filePath) {
289590
+ if (filePath.startsWith(this.rootPath)) {
289591
+ return filePath.slice(this.rootPath.length + 1).replace(/\\/g, "/");
289592
+ }
289593
+ return filePath.replace(/\\/g, "/");
289594
+ }
289595
+ };
289596
+ var ContextExplainer = class {
289597
+ /**
289598
+ * Generate a rich explanation for a file.
289599
+ */
289600
+ explain(filePath, ctx) {
289601
+ const fc = ctx.fileContext;
289602
+ const paragraphs = [];
289603
+ const quickFacts = [];
289604
+ const warnings = [];
289605
+ const relatedFiles = [];
289606
+ const overlays = [];
289607
+ const role = fc?.role ?? "unknown";
289608
+ const roleSummary = this.buildRoleSummary(filePath, role, ctx);
289609
+ if (fc) {
289610
+ const depCount = fc.dependedOnBy.length;
289611
+ const depsOnCount = fc.dependsOn.length;
289612
+ if (depCount > 0 || depsOnCount > 0) {
289613
+ const parts2 = [];
289614
+ if (depCount > 0) {
289615
+ const critical = depCount > 10 ? " \u2014 changes here have wide blast radius" : "";
289616
+ parts2.push(`${depCount} file${depCount > 1 ? "s" : ""} depend on this${critical}`);
289617
+ }
289618
+ if (depsOnCount > 0) {
289619
+ parts2.push(`it imports from ${depsOnCount} file${depsOnCount > 1 ? "s" : ""}`);
289620
+ }
289621
+ paragraphs.push({
289622
+ heading: "Dependencies",
289623
+ text: parts2.join(". ") + ".",
289624
+ importance: depCount > 10 ? "critical" : depCount > 5 ? "high" : "medium"
289625
+ });
289626
+ }
289627
+ quickFacts.push({ label: "Role", value: role, icon: "layer" });
289628
+ if (fc.layer) quickFacts.push({ label: "Layer", value: fc.layer, icon: "layer" });
289629
+ quickFacts.push({ label: "Dependents", value: String(depCount), icon: "dependency" });
289630
+ quickFacts.push({ label: "Dependencies", value: String(depsOnCount), icon: "dependency" });
289631
+ if (fc.riskLevel === "critical" || fc.riskLevel === "high") {
289632
+ quickFacts.push({ label: "Risk", value: fc.riskLevel.toUpperCase(), icon: "warning" });
289633
+ warnings.push({
289634
+ message: `This file is classified as ${fc.riskLevel} risk`,
289635
+ severity: fc.riskLevel === "critical" ? "error" : "warning",
289636
+ action: "Add comprehensive tests and review carefully before merging changes"
289637
+ });
289638
+ }
289639
+ for (const dep of fc.dependedOnBy.slice(0, 5)) {
289640
+ relatedFiles.push({
289641
+ filePath: dep,
289642
+ reason: `Imports from this file`,
289643
+ relationship: "depended-by",
289644
+ confidence: 0.9
289645
+ });
289646
+ }
289647
+ overlays.push({
289648
+ type: "code-lens",
289649
+ line: 1,
289650
+ text: `${depCount} dependent${depCount !== 1 ? "s" : ""} \xB7 ${depsOnCount} import${depsOnCount !== 1 ? "s" : ""} \xB7 ${role}`,
289651
+ tooltip: `This ${role} file has ${depCount} files that depend on it and imports from ${depsOnCount} files`
289652
+ });
289653
+ }
289654
+ if (fc && fc.conventions.length > 0) {
289655
+ paragraphs.push({
289656
+ heading: "Conventions",
289657
+ text: `Follow these discovered conventions: ${fc.conventions.slice(0, 3).join("; ")}.`,
289658
+ importance: "medium"
289659
+ });
289660
+ }
289661
+ if (ctx.rules) {
289662
+ const fileViolations = ctx.rules.violations.filter(
289663
+ (v) => v.sourceSymbol.filePath.includes(shortName(filePath)) || v.targetSymbol && v.targetSymbol.filePath.includes(shortName(filePath))
289664
+ );
289665
+ if (fileViolations.length > 0) {
289666
+ const errors = fileViolations.filter((v) => v.severity === "error");
289667
+ const warns = fileViolations.filter((v) => v.severity === "warning");
289668
+ paragraphs.push({
289669
+ heading: "Architecture Violations",
289670
+ text: `${errors.length} error${errors.length !== 1 ? "s" : ""} and ${warns.length} warning${warns.length !== 1 ? "s" : ""} from architecture rules.`,
289671
+ importance: errors.length > 0 ? "critical" : "high"
289672
+ });
289673
+ for (const v of fileViolations.slice(0, 5)) {
289674
+ warnings.push({
289675
+ message: v.message,
289676
+ severity: v.severity === "error" ? "error" : "warning",
289677
+ action: v.suggestedFix ?? "Review and fix the violation",
289678
+ ruleId: v.ruleId
289679
+ });
289680
+ overlays.push({
289681
+ type: "diagnostic",
289682
+ line: v.sourceSymbol.line,
289683
+ text: v.message,
289684
+ severity: v.severity === "error" ? "error" : "warning",
289685
+ tooltip: v.suggestedFix
289686
+ });
289687
+ }
289688
+ }
289689
+ }
289690
+ if (ctx.callGraph) {
289691
+ const fileNodes = ctx.callGraph.nodes.filter((n) => n.filePath.includes(shortName(filePath)));
289692
+ const hotFunctions = fileNodes.filter((n) => n.callerCount > 5);
289693
+ if (hotFunctions.length > 0) {
289694
+ paragraphs.push({
289695
+ heading: "Hot Functions",
289696
+ text: hotFunctions.map(
289697
+ (f) => `\`${f.name}\` is called by ${f.callerCount} function${f.callerCount !== 1 ? "s" : ""}${f.calleeCount > 0 ? ` and calls ${f.calleeCount}` : ""}`
289698
+ ).join(". ") + ".",
289699
+ importance: "high"
289700
+ });
289701
+ for (const fn of hotFunctions) {
289702
+ overlays.push({
289703
+ type: "code-lens",
289704
+ line: 0,
289705
+ // Would need symbol line mapping
289706
+ text: `${fn.callerCount} callers \xB7 ${fn.calleeCount} callees`,
289707
+ tooltip: `Function ${fn.name} has ${fn.callerCount} callers and ${fn.calleeCount} callees`
289708
+ });
289709
+ }
289710
+ }
289711
+ const deadInFile = ctx.callGraph.stats.deadFunctions.filter(
289712
+ (d) => d.filePath.includes(shortName(filePath))
289713
+ );
289714
+ if (deadInFile.length > 0) {
289715
+ for (const dead of deadInFile) {
289716
+ warnings.push({
289717
+ message: `\`${dead.name}\` appears to be dead code (exported but never called)`,
289718
+ severity: "info",
289719
+ action: "Verify this function is not called via dynamic dispatch or external consumers, then consider removing it"
289720
+ });
289721
+ }
289722
+ }
289723
+ }
289724
+ if (ctx.temporal) {
289725
+ const hotspot = ctx.temporal.changeHotspots.find((h) => filePath.includes(h.file) || h.file.includes(shortName(filePath)));
289726
+ if (hotspot) {
289727
+ const daysSince = Math.round((Date.now() - new Date(hotspot.lastChanged).getTime()) / 864e5);
289728
+ paragraphs.push({
289729
+ heading: "Recent Activity",
289730
+ text: `Changed ${hotspot.commits} times in the last ${ctx.temporal.stats.analysisWindowDays} days by ${hotspot.authors} author${hotspot.authors !== 1 ? "s" : ""}. Last modified ${daysSince} day${daysSince !== 1 ? "s" : ""} ago.`,
289731
+ importance: hotspot.commits > 10 ? "high" : "medium"
289732
+ });
289733
+ quickFacts.push({ label: "Recent commits", value: String(hotspot.commits), icon: "git" });
289734
+ quickFacts.push({ label: "Last changed", value: `${daysSince}d ago`, icon: "git" });
289735
+ quickFacts.push({ label: "Authors", value: String(hotspot.authors), icon: "git" });
289736
+ }
289737
+ const churn = ctx.temporal.churnFiles.find((c) => filePath.includes(c.file) || c.file.includes(shortName(filePath)));
289738
+ if (churn && churn.severity !== "low") {
289739
+ warnings.push({
289740
+ message: churn.reason,
289741
+ severity: churn.severity === "high" ? "warning" : "info",
289742
+ action: "Consider whether this file needs refactoring to reduce change frequency"
289743
+ });
289744
+ }
289745
+ const expertise = ctx.temporal.authorExpertise.find(
289746
+ (e) => filePath.startsWith(e.area) || filePath.includes(e.area)
289747
+ );
289748
+ if (expertise && expertise.busFactor === 1) {
289749
+ warnings.push({
289750
+ message: `Bus factor of 1 \u2014 ${expertise.primaryAuthor} has made ${expertise.authors[0]?.percentage}% of changes to this area`,
289751
+ severity: "info",
289752
+ action: "Consider knowledge sharing or pair programming for this area"
289753
+ });
289754
+ }
289755
+ }
289756
+ if (ctx.learned) {
289757
+ const coEdits = ctx.learned.coEdits.filter((p) => p.files[0].includes(shortName(filePath)) || p.files[1].includes(shortName(filePath))).slice(0, 3);
289758
+ if (coEdits.length > 0) {
289759
+ for (const pair of coEdits) {
289760
+ const other = pair.files[0].includes(shortName(filePath)) ? pair.files[1] : pair.files[0];
289761
+ relatedFiles.push({
289762
+ filePath: other,
289763
+ reason: `Often edited together (${pair.count} times)`,
289764
+ relationship: "co-edited",
289765
+ confidence: pair.weight
289766
+ });
289767
+ }
289768
+ }
289769
+ }
289770
+ if (fc && fc.editGuidance.length > 0) {
289771
+ paragraphs.push({
289772
+ heading: "Edit Guidance",
289773
+ text: fc.editGuidance.join(" "),
289774
+ importance: "medium"
289775
+ });
289776
+ }
289777
+ return {
289778
+ filePath,
289779
+ roleSummary,
289780
+ paragraphs,
289781
+ quickFacts,
289782
+ warnings,
289783
+ relatedFiles,
289784
+ overlays
289785
+ };
289786
+ }
289787
+ /**
289788
+ * Generate a compact markdown explanation for AI agent consumption.
289789
+ */
289790
+ explainForAgent(filePath, ctx) {
289791
+ const explanation = this.explain(filePath, ctx);
289792
+ const lines = [];
289793
+ lines.push(`## ${shortName(filePath)}: ${explanation.roleSummary}`);
289794
+ lines.push("");
289795
+ if (explanation.quickFacts.length > 0) {
289796
+ lines.push(explanation.quickFacts.map((f) => `**${f.label}**: ${f.value}`).join(" \xB7 "));
289797
+ lines.push("");
289798
+ }
289799
+ for (const p of explanation.paragraphs.filter((p2) => p2.importance === "critical" || p2.importance === "high")) {
289800
+ lines.push(`### ${p.heading}`);
289801
+ lines.push(p.text);
289802
+ lines.push("");
289803
+ }
289804
+ if (explanation.warnings.length > 0) {
289805
+ lines.push("### Warnings");
289806
+ for (const w of explanation.warnings) {
289807
+ const icon = w.severity === "error" ? "MUST FIX" : w.severity === "warning" ? "SHOULD FIX" : "NOTE";
289808
+ lines.push(`- **[${icon}]** ${w.message} \u2014 ${w.action}`);
289809
+ }
289810
+ lines.push("");
289811
+ }
289812
+ if (explanation.relatedFiles.length > 0) {
289813
+ lines.push("### Related Files");
289814
+ for (const rf of explanation.relatedFiles.slice(0, 5)) {
289815
+ lines.push(`- \`${rf.filePath}\` \u2014 ${rf.reason}`);
289816
+ }
289817
+ lines.push("");
289818
+ }
289819
+ return lines.join("\n");
289820
+ }
289821
+ /**
289822
+ * Generate IDE overlay data for the VS Code extension to consume.
289823
+ */
289824
+ getIDEOverlays(filePath, ctx) {
289825
+ const explanation = this.explain(filePath, ctx);
289826
+ return explanation.overlays;
289827
+ }
289828
+ /**
289829
+ * Generate a health report explanation.
289830
+ */
289831
+ explainHealth(health, dna) {
289832
+ const lines = [];
289833
+ const dims = health.dimensions;
289834
+ lines.push(`## Codebase Health: ${health.overall}/100`);
289835
+ lines.push("");
289836
+ const entries = [
289837
+ { name: "Architecture", score: dims.architecture, explain: this.explainArchScore(dims.architecture, dna) },
289838
+ { name: "Test Coverage", score: dims.testCoverage, explain: this.explainTestScore(dims.testCoverage) },
289839
+ { name: "Conventions", score: dims.conventions, explain: this.explainConventionScore(dims.conventions, dna) },
289840
+ { name: "Dependencies", score: dims.dependencies, explain: this.explainDependencyScore(dims.dependencies, dna) },
289841
+ { name: "Security", score: dims.security, explain: dims.security >= 80 ? "No critical security concerns detected" : "Critical risk areas identified" },
289842
+ { name: "Complexity", score: dims.complexity, explain: dims.complexity >= 80 ? "Complexity is well-managed" : "High-complexity hotspots detected" }
289843
+ ];
289844
+ for (const entry of entries) {
289845
+ const bar = this.renderBar(entry.score);
289846
+ lines.push(`${bar} **${entry.name}**: ${entry.score}/100 \u2014 ${entry.explain}`);
289847
+ }
289848
+ return lines.join("\n");
289849
+ }
289850
+ // ═══════════════════════════════════════════════════════════════════════════
289851
+ // PRIVATE
289852
+ // ═══════════════════════════════════════════════════════════════════════════
289853
+ buildRoleSummary(filePath, role, ctx) {
289854
+ const parts2 = [];
289855
+ switch (role) {
289856
+ case "service":
289857
+ parts2.push("Business logic service");
289858
+ break;
289859
+ case "route-handler":
289860
+ parts2.push("API route handler");
289861
+ break;
289862
+ case "component":
289863
+ parts2.push("UI component");
289864
+ break;
289865
+ case "repository":
289866
+ parts2.push("Data access layer");
289867
+ break;
289868
+ case "middleware":
289869
+ parts2.push("Request middleware");
289870
+ break;
289871
+ case "test":
289872
+ parts2.push("Test file");
289873
+ break;
289874
+ case "config":
289875
+ parts2.push("Configuration");
289876
+ break;
289877
+ case "type":
289878
+ parts2.push("Type definitions");
289879
+ break;
289880
+ case "util":
289881
+ parts2.push("Utility module");
289882
+ break;
289883
+ case "entry":
289884
+ parts2.push("Entry point");
289885
+ break;
289886
+ default:
289887
+ parts2.push("Source file");
289888
+ }
289889
+ if (ctx.fileContext) {
289890
+ if (ctx.fileContext.layer) parts2.push(`in ${ctx.fileContext.layer} layer`);
289891
+ if (ctx.fileContext.dependedOnBy.length > 10) parts2.push("(high-impact)");
289892
+ }
289893
+ return parts2.join(" ");
289894
+ }
289895
+ explainArchScore(score, dna) {
289896
+ if (score >= 80) return `Strong architecture with ${dna.patterns.length} recognized patterns`;
289897
+ if (score >= 50) return `Moderate architecture \u2014 ${dna.patterns.length} patterns detected, room to strengthen boundaries`;
289898
+ return "Architecture needs attention \u2014 few recognized patterns or boundaries";
289899
+ }
289900
+ explainTestScore(score) {
289901
+ if (score >= 80) return "Good test coverage across source files";
289902
+ if (score >= 50) return "Moderate coverage \u2014 some source files lack tests";
289903
+ return "Low test coverage \u2014 many exported modules have no test files";
289904
+ }
289905
+ explainConventionScore(score, dna) {
289906
+ const strong = dna.conventions.filter((c) => c.confidence > 0.6).length;
289907
+ if (score >= 80) return `${strong} strong conventions enforced consistently`;
289908
+ if (score >= 50) return `${strong} conventions detected but inconsistently applied`;
289909
+ return "Few consistent conventions \u2014 codebase style varies across files";
289910
+ }
289911
+ explainDependencyScore(score, dna) {
289912
+ const circular = dna.boundaries.filter((b) => b.isCircular).length;
289913
+ if (score >= 80) return "Clean dependency graph with no circular dependencies";
289914
+ if (circular > 0) return `${circular} circular dependency${circular > 1 ? "ies" : "y"} detected \u2014 these increase coupling and make code harder to reason about`;
289915
+ return "Dependency health needs improvement";
289916
+ }
289917
+ renderBar(score) {
289918
+ const filled = Math.round(score / 10);
289919
+ return "\u2588".repeat(filled) + "\u2591".repeat(10 - filled);
289920
+ }
289921
+ };
289922
+ function shortName(filePath) {
289923
+ const parts2 = filePath.split("/");
289924
+ return parts2[parts2.length - 1] ?? filePath;
289925
+ }
289926
+
289079
289927
  // ../context-engine/dist/chunk-HMKLYBWJ.js
289080
289928
  var import_better_sqlite3 = __toESM(require_lib(), 1);
289081
289929
  var import_fast_glob2 = __toESM(require_out4(), 1);
@@ -289085,7 +289933,7 @@ import { mkdirSync } from "fs";
289085
289933
  import { join as join4 } from "path";
289086
289934
  import { readFile as readFile22 } from "fs/promises";
289087
289935
  import { accessSync } from "fs";
289088
- import { extname as extname2, resolve as resolve2, dirname as dirname5 } from "path";
289936
+ import { extname as extname2, resolve as resolve2, dirname as dirname6 } from "path";
289089
289937
  import { createHash as createHash22 } from "crypto";
289090
289938
  var SCHEMA_VERSION = 1;
289091
289939
  var PersistentIndex = class {
@@ -289983,7 +290831,7 @@ var DefaultFileParser = class {
289983
290831
  }
289984
290832
  resolveImportPath(sourcePath, fromFile) {
289985
290833
  if (!sourcePath.startsWith(".")) return sourcePath;
289986
- const dir = dirname5(fromFile);
290834
+ const dir = dirname6(fromFile);
289987
290835
  const resolved = resolve2(dir, sourcePath);
289988
290836
  const extensions = [".ts", ".tsx", ".js", ".jsx", "/index.ts", "/index.tsx", "/index.js"];
289989
290837
  for (const ext2 of extensions) {
@@ -290093,845 +290941,6 @@ function flattenCodeSymbols(symbols) {
290093
290941
  return flat;
290094
290942
  }
290095
290943
 
290096
- // ../context-engine/dist/chunk-INBCP46U.js
290097
- import * as path5 from "path";
290098
- var ContextSynthesizer = class {
290099
- rootPath;
290100
- data;
290101
- dna;
290102
- graph;
290103
- ruleResult;
290104
- constructor(rootPath, data, dna, graph, ruleResult) {
290105
- this.rootPath = rootPath;
290106
- this.data = data;
290107
- this.dna = dna;
290108
- this.graph = graph;
290109
- this.ruleResult = ruleResult || null;
290110
- }
290111
- /**
290112
- * Synthesize the full context — the complete brain dump for AI agents.
290113
- */
290114
- synthesize() {
290115
- const projectIdentity = this.buildProjectIdentity();
290116
- const archRules = this.buildArchRuleSummary();
290117
- const activeViolations = this.ruleResult?.violations || [];
290118
- const codebaseDNA = this.buildDNASummary();
290119
- const fileContexts = this.buildFileContexts();
290120
- const taskPlaybooks = this.buildTaskPlaybooks();
290121
- const verificationSteps = this.buildVerificationSteps();
290122
- const riskBriefing = this.buildRiskBriefing();
290123
- return {
290124
- version: "2.0.0",
290125
- generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
290126
- projectIdentity,
290127
- architecturalRules: archRules,
290128
- activeViolations,
290129
- codebaseDNA,
290130
- fileContexts,
290131
- taskPlaybooks,
290132
- verificationSteps,
290133
- riskBriefing
290134
- };
290135
- }
290136
- /**
290137
- * Generate context for a specific file — what an AI agent needs to know
290138
- * before editing this file.
290139
- */
290140
- synthesizeForFile(filePath) {
290141
- const rel = this.toRelative(filePath);
290142
- const file = this.data.files.find((f) => f.relativePath === rel || f.path === filePath);
290143
- if (!file) return null;
290144
- return this.buildSingleFileContext(file);
290145
- }
290146
- /**
290147
- * Generate a compact markdown context document for IDE rules.
290148
- * This replaces the old static rule generation with intelligence-driven context.
290149
- */
290150
- generateContextDocument() {
290151
- const ctx = this.synthesize();
290152
- const lines = [];
290153
- lines.push(`# ${ctx.projectIdentity.name} \u2014 AI Context`);
290154
- lines.push(`<!-- Generated by @repo/context-engine at ${ctx.generatedAt} -->`);
290155
- lines.push("");
290156
- lines.push("## Project Identity");
290157
- lines.push(`- **Stack**: ${ctx.projectIdentity.stack}`);
290158
- lines.push(`- **Architecture**: ${ctx.projectIdentity.architecture}`);
290159
- if (ctx.projectIdentity.keyPatterns.length > 0) {
290160
- lines.push(`- **Key Patterns**: ${ctx.projectIdentity.keyPatterns.join(", ")}`);
290161
- }
290162
- lines.push("");
290163
- if (ctx.codebaseDNA.conventions.length > 0) {
290164
- lines.push("## Conventions (Auto-Discovered)");
290165
- for (const conv of ctx.codebaseDNA.conventions) {
290166
- lines.push(`- ${conv}`);
290167
- }
290168
- lines.push("");
290169
- }
290170
- if (ctx.architecturalRules.length > 0) {
290171
- lines.push("## Architecture Rules");
290172
- for (const rule of ctx.architecturalRules) {
290173
- const icon = rule.severity === "error" ? "MUST" : rule.severity === "warning" ? "SHOULD" : "MAY";
290174
- lines.push(`- **[${icon}]** ${rule.name}: ${rule.description}`);
290175
- if (rule.violationCount > 0) {
290176
- lines.push(` - ${rule.violationCount} active violations`);
290177
- }
290178
- }
290179
- lines.push("");
290180
- }
290181
- if (ctx.codebaseDNA.boundaries.length > 0) {
290182
- lines.push("## Module Boundaries");
290183
- for (const boundary of ctx.codebaseDNA.boundaries) {
290184
- lines.push(`- ${boundary}`);
290185
- }
290186
- lines.push("");
290187
- }
290188
- if (ctx.codebaseDNA.hotFiles.length > 0) {
290189
- lines.push("## High-Impact Files (Edit With Care)");
290190
- for (const file of ctx.codebaseDNA.hotFiles.slice(0, 10)) {
290191
- lines.push(`- \`${file}\``);
290192
- }
290193
- lines.push("");
290194
- }
290195
- if (ctx.riskBriefing.securityConcerns.length > 0 || ctx.riskBriefing.testGaps.length > 0) {
290196
- lines.push("## Risk Areas");
290197
- for (const concern of ctx.riskBriefing.securityConcerns) {
290198
- lines.push(`- ${concern}`);
290199
- }
290200
- for (const gap of ctx.riskBriefing.testGaps.slice(0, 5)) {
290201
- lines.push(`- ${gap}`);
290202
- }
290203
- lines.push("");
290204
- }
290205
- if (ctx.projectIdentity.noGoZones.length > 0) {
290206
- lines.push("## No-Go Zones");
290207
- for (const zone of ctx.projectIdentity.noGoZones) {
290208
- lines.push(`- ${zone}`);
290209
- }
290210
- lines.push("");
290211
- }
290212
- if (ctx.taskPlaybooks.length > 0) {
290213
- lines.push("## Task Playbooks");
290214
- for (const playbook of ctx.taskPlaybooks) {
290215
- lines.push(`### ${playbook.taskType}`);
290216
- for (const step of playbook.steps) {
290217
- lines.push(`1. ${step}`);
290218
- }
290219
- if (playbook.mustVerify.length > 0) {
290220
- lines.push(`**Verify**: ${playbook.mustVerify.join(", ")}`);
290221
- }
290222
- lines.push("");
290223
- }
290224
- }
290225
- if (ctx.verificationSteps.length > 0) {
290226
- lines.push("## Verification Protocol");
290227
- for (const step of ctx.verificationSteps) {
290228
- lines.push(`### On ${step.trigger}`);
290229
- for (const check of step.checks) {
290230
- lines.push(`- ${check}`);
290231
- }
290232
- if (step.commands.length > 0) {
290233
- lines.push(`**Run**: \`${step.commands.join(" && ")}\``);
290234
- }
290235
- lines.push("");
290236
- }
290237
- }
290238
- lines.push("## Codebase Health");
290239
- lines.push(`- **Overall**: ${this.dna.healthScore.overall}/100`);
290240
- const dims = this.dna.healthScore.dimensions;
290241
- lines.push(`- Architecture: ${dims.architecture} | Tests: ${dims.testCoverage} | Conventions: ${dims.conventions} | Dependencies: ${dims.dependencies}`);
290242
- lines.push("");
290243
- lines.push("---");
290244
- lines.push("<!-- context-engine:v2 -->");
290245
- return lines.join("\n");
290246
- }
290247
- // ═══════════════════════════════════════════════════════════════════════════
290248
- // IDENTITY
290249
- // ═══════════════════════════════════════════════════════════════════════════
290250
- buildProjectIdentity() {
290251
- const fp = this.dna.fingerprint;
290252
- const stack = [fp.framework, fp.language, fp.orm, fp.validator, fp.authLib, fp.router].filter(Boolean).join(" | ");
290253
- const keyPatterns = this.dna.patterns.map((p) => p.name);
290254
- const criticalPaths = this.dna.hotspots.slice(0, 5).map((h) => h.file);
290255
- const noGoZones = [];
290256
- if (this.ruleResult) {
290257
- const errorRules = this.ruleResult.violations.filter((v) => v.severity === "error");
290258
- const uniqueMessages = [...new Set(errorRules.map((v) => v.message))];
290259
- noGoZones.push(...uniqueMessages.slice(0, 5));
290260
- }
290261
- for (const cycle of this.graph.cycles) {
290262
- noGoZones.push(`Circular dependency: ${cycle.nodes.slice(0, 3).join(" \u2192 ")}...`);
290263
- }
290264
- const architecture = this.dna.conventions.filter((c) => c.area === "structure").map((c) => c.description).join("; ") || fp.framework;
290265
- return {
290266
- name: fp.name,
290267
- stack,
290268
- architecture,
290269
- keyPatterns,
290270
- criticalPaths,
290271
- noGoZones: noGoZones.slice(0, 10)
290272
- };
290273
- }
290274
- // ═══════════════════════════════════════════════════════════════════════════
290275
- // RULES SUMMARY
290276
- // ═══════════════════════════════════════════════════════════════════════════
290277
- buildArchRuleSummary() {
290278
- if (!this.ruleResult) return [];
290279
- const breakdown = this.ruleResult.ruleBreakdown;
290280
- return Object.entries(breakdown).map(([ruleId, count]) => {
290281
- const violation = this.ruleResult.violations.find((v) => v.ruleId === ruleId);
290282
- return {
290283
- id: ruleId,
290284
- name: violation?.ruleName || ruleId,
290285
- type: "import_forbidden",
290286
- severity: violation?.severity || "warning",
290287
- scope: violation?.sourceSymbol.filePath || "",
290288
- description: violation?.message || "",
290289
- violationCount: count
290290
- };
290291
- });
290292
- }
290293
- // ═══════════════════════════════════════════════════════════════════════════
290294
- // DNA SUMMARY
290295
- // ═══════════════════════════════════════════════════════════════════════════
290296
- buildDNASummary() {
290297
- return {
290298
- conventions: this.dna.conventions.filter((c) => c.confidence > 0.5).map((c) => c.description),
290299
- patterns: this.dna.patterns.map((p) => `${p.name}: ${p.description}`),
290300
- boundaries: this.dna.boundaries.filter((b) => b.importCount > 3).map((b) => `${b.from} \u2192 ${b.to} (${b.importCount} imports${b.isCircular ? ", CIRCULAR" : ""})`),
290301
- hotFiles: this.dna.hotspots.slice(0, 10).map((h) => h.file),
290302
- riskAreas: this.dna.riskMap.filter((r) => r.riskLevel === "critical" || r.riskLevel === "high").map((r) => `${r.file}: ${r.factors[0]}`)
290303
- };
290304
- }
290305
- // ═══════════════════════════════════════════════════════════════════════════
290306
- // FILE CONTEXTS
290307
- // ═══════════════════════════════════════════════════════════════════════════
290308
- buildFileContexts() {
290309
- const contexts = /* @__PURE__ */ new Map();
290310
- for (const file of this.data.files) {
290311
- contexts.set(file.relativePath, this.buildSingleFileContext(file));
290312
- }
290313
- return contexts;
290314
- }
290315
- buildSingleFileContext(file) {
290316
- const rel = file.relativePath;
290317
- const role = this.classifyRole(file);
290318
- const graphNode = this.graph.nodes.find((n) => n.relativePath === rel);
290319
- const layer = graphNode?.layer;
290320
- const dependsOn = this.graph.edges.filter((e) => e.from === rel).map((e) => e.to);
290321
- const dependedOnBy = this.graph.edges.filter((e) => e.to === rel).map((e) => e.from);
290322
- const applicableRules = [];
290323
- if (this.ruleResult) {
290324
- for (const v of this.ruleResult.violations) {
290325
- if (v.sourceSymbol.filePath.includes(rel) || v.targetSymbol && v.targetSymbol.filePath.includes(rel)) {
290326
- if (!applicableRules.includes(v.ruleId)) applicableRules.push(v.ruleId);
290327
- }
290328
- }
290329
- }
290330
- const conventions = this.dna.conventions.filter((c) => this.conventionAppliesToFile(c.area, file)).map((c) => c.description);
290331
- const patterns = this.dna.patterns.filter((p) => p.fileMatches.some((m) => m === rel)).map((p) => p.name);
290332
- const riskEntry = this.dna.riskMap.find((r) => r.file === rel);
290333
- const riskLevel = riskEntry?.riskLevel || "low";
290334
- const relatedFiles = this.findRelatedFiles(file, role).slice(0, 8);
290335
- const editGuidance = this.generateEditGuidance(file, role, layer, dependedOnBy, conventions);
290336
- return {
290337
- filePath: file.path,
290338
- role,
290339
- layer,
290340
- dependsOn,
290341
- dependedOnBy,
290342
- applicableRules,
290343
- conventions,
290344
- patterns,
290345
- riskLevel,
290346
- relatedFiles,
290347
- editGuidance
290348
- };
290349
- }
290350
- classifyRole(file) {
290351
- const rel = file.relativePath.toLowerCase();
290352
- if (rel.includes(".test.") || rel.includes(".spec.") || rel.includes("__tests__")) return "test";
290353
- if (rel.includes("fixture") || rel.includes("mock")) return "fixture";
290354
- if (rel.includes("migration")) return "migration";
290355
- if (rel.match(/\.(css|scss|less|styl)$/)) return "style";
290356
- if (rel.includes(".config.") || rel.includes("config/") || rel === "tsconfig.json") return "config";
290357
- if (rel.includes("middleware")) return "middleware";
290358
- if (rel.includes("/api/") || rel.includes("route")) return "route-handler";
290359
- if (rel.includes("service") || rel.includes("Service")) return "service";
290360
- if (rel.includes("repositor") || rel.includes("Repositor")) return "repository";
290361
- if (rel.endsWith(".tsx") && !rel.includes("page.")) return "component";
290362
- if (rel.includes("/types") || rel.endsWith(".d.ts")) return "type";
290363
- if (rel.includes("util") || rel.includes("helper") || rel.includes("lib/")) return "util";
290364
- if (rel.includes("script") || rel.includes("bin/")) return "script";
290365
- if (rel.match(/^(src\/)?index\.|^(src\/)?main\.|^(src\/)?app\./)) return "entry";
290366
- return "unknown";
290367
- }
290368
- conventionAppliesToFile(area, file) {
290369
- switch (area) {
290370
- case "naming":
290371
- return true;
290372
- case "imports":
290373
- return file.relativePath.endsWith(".ts") || file.relativePath.endsWith(".tsx");
290374
- case "exports":
290375
- return file.exports.length > 0;
290376
- case "testing":
290377
- return file.relativePath.includes(".test.") || file.relativePath.includes(".spec.");
290378
- case "error-handling":
290379
- return !file.relativePath.includes(".test.");
290380
- case "types":
290381
- return file.relativePath.endsWith(".ts") || file.relativePath.endsWith(".tsx");
290382
- default:
290383
- return true;
290384
- }
290385
- }
290386
- findRelatedFiles(file, role) {
290387
- const related = [];
290388
- const dir = path5.dirname(file.relativePath);
290389
- for (const other of this.data.files) {
290390
- if (other.path === file.path) continue;
290391
- if (path5.dirname(other.relativePath) === dir) {
290392
- related.push(other.relativePath);
290393
- }
290394
- }
290395
- if (related.length < 5) {
290396
- for (const other of this.data.files) {
290397
- if (other.path === file.path) continue;
290398
- if (related.includes(other.relativePath)) continue;
290399
- if (this.classifyRole(other) === role) {
290400
- related.push(other.relativePath);
290401
- if (related.length >= 8) break;
290402
- }
290403
- }
290404
- }
290405
- return related;
290406
- }
290407
- generateEditGuidance(file, role, layer, dependedOnBy, conventions) {
290408
- const guidance = [];
290409
- if (dependedOnBy.length > 10) {
290410
- guidance.push(`HIGH IMPACT: ${dependedOnBy.length} files depend on this. Changes have wide blast radius.`);
290411
- }
290412
- switch (role) {
290413
- case "route-handler":
290414
- guidance.push("Validate all inputs with schemas before processing.");
290415
- guidance.push("Return consistent response shapes ({ success, data } or { success, error }).");
290416
- guidance.push("Ensure authentication middleware is applied to protected endpoints.");
290417
- break;
290418
- case "service":
290419
- guidance.push("Keep business logic here, not in controllers/routes.");
290420
- guidance.push("Use dependency injection for testability.");
290421
- if (layer) guidance.push(`This is in the ${layer} layer \u2014 only import from lower layers.`);
290422
- break;
290423
- case "repository":
290424
- guidance.push("Only data access logic belongs here \u2014 no business rules.");
290425
- guidance.push("Return domain objects, not raw database rows.");
290426
- break;
290427
- case "component":
290428
- guidance.push("Keep components focused and composable.");
290429
- guidance.push("Extract complex logic to custom hooks.");
290430
- break;
290431
- case "middleware":
290432
- guidance.push("Middleware must call next() or return a response \u2014 never leave the request hanging.");
290433
- guidance.push("Keep middleware focused on a single concern.");
290434
- break;
290435
- case "test":
290436
- guidance.push("Follow Arrange-Act-Assert pattern.");
290437
- guidance.push("Test edge cases and error conditions, not just happy path.");
290438
- break;
290439
- }
290440
- for (const conv of conventions.slice(0, 3)) {
290441
- guidance.push(`Convention: ${conv}`);
290442
- }
290443
- return guidance;
290444
- }
290445
- // ═══════════════════════════════════════════════════════════════════════════
290446
- // TASK PLAYBOOKS
290447
- // ═══════════════════════════════════════════════════════════════════════════
290448
- buildTaskPlaybooks() {
290449
- const fp = this.dna.fingerprint;
290450
- const playbooks = [];
290451
- playbooks.push({
290452
- taskType: "Bug Fix",
290453
- steps: [
290454
- "Reproduce the bug and understand the expected vs actual behavior",
290455
- "Identify the root cause file(s) using the dependency graph",
290456
- "Write a failing test that reproduces the bug",
290457
- "Apply the minimal fix at the root cause",
290458
- "Verify the fix passes the test and does not break existing tests",
290459
- "Check that the fix does not violate any architecture rules"
290460
- ],
290461
- mustRead: this.dna.hotspots.slice(0, 3).map((h) => h.file),
290462
- mustUpdate: ["The buggy file", "Related test file"],
290463
- mustVerify: ["All existing tests pass", "New regression test passes", "No new arch rule violations"],
290464
- stopConditions: ["Never modify tests to make them pass \u2014 fix the code", "Do not change public API signatures without discussion"]
290465
- });
290466
- if (fp.router) {
290467
- playbooks.push({
290468
- taskType: "Add API Endpoint",
290469
- steps: [
290470
- "Check existing routes in truthpack to avoid duplicates",
290471
- "Create the route handler following existing patterns",
290472
- "Add input validation using the project validator",
290473
- "Add authentication middleware if the route is protected",
290474
- "Write tests for success, validation failure, and auth failure",
290475
- "Update the truthpack (run vibecheck scan)"
290476
- ],
290477
- mustRead: ["truthpack/routes.json", ...this.dna.patterns.filter((p) => p.category === "api").map((p) => p.exemplar)],
290478
- mustUpdate: ["Route file", "Test file", "Truthpack"],
290479
- mustVerify: ["Route responds correctly", "Input validation works", "Auth is enforced", "Test passes"],
290480
- stopConditions: ["Do not create duplicate routes", "Do not hardcode mock data in handlers"]
290481
- });
290482
- }
290483
- if (fp.framework.includes("Next") || fp.framework.includes("React")) {
290484
- playbooks.push({
290485
- taskType: "Add UI Component",
290486
- steps: [
290487
- "Check if a similar component already exists",
290488
- "Create the component following existing naming and structure patterns",
290489
- "Add TypeScript props interface",
290490
- "Add unit test for the component",
290491
- "If using state, determine if it should be a client component"
290492
- ],
290493
- mustRead: this.dna.patterns.filter((p) => p.category === "ui" || p.category === "state").map((p) => p.exemplar),
290494
- mustUpdate: ["Component file", "Test file", "Parent component that uses it"],
290495
- mustVerify: ["Component renders correctly", "Props are typed", "Test passes"],
290496
- stopConditions: ['Do not use "any" type for props', 'Do not add useState in server components without "use client"']
290497
- });
290498
- }
290499
- playbooks.push({
290500
- taskType: "Refactor",
290501
- steps: [
290502
- "Identify all callers/dependents of the code being refactored",
290503
- "Ensure comprehensive tests exist before refactoring",
290504
- "Apply changes incrementally, testing after each step",
290505
- "Update all dependents to use the new API",
290506
- "Remove old code only after all dependents are migrated",
290507
- "Verify no architecture rules are violated"
290508
- ],
290509
- mustRead: ["Dependency graph for affected files"],
290510
- mustUpdate: ["Refactored file", "All dependent files", "Tests"],
290511
- mustVerify: ["All tests pass", "No new violations", "No regressions"],
290512
- stopConditions: ["Never break existing public APIs without migration path", "Do not refactor and add features in the same change"]
290513
- });
290514
- return playbooks;
290515
- }
290516
- // ═══════════════════════════════════════════════════════════════════════════
290517
- // VERIFICATION STEPS
290518
- // ═══════════════════════════════════════════════════════════════════════════
290519
- buildVerificationSteps() {
290520
- const fp = this.dna.fingerprint;
290521
- const steps = [];
290522
- if (fp.language === "TypeScript") {
290523
- steps.push({
290524
- trigger: "Any TypeScript file change",
290525
- checks: ["TypeScript compilation succeeds", "No new type errors introduced"],
290526
- commands: [fp.packageManager === "pnpm" ? "pnpm run check-types" : "npm run check-types"],
290527
- artifacts: []
290528
- });
290529
- }
290530
- if (fp.testRunner) {
290531
- steps.push({
290532
- trigger: "Any source file change",
290533
- checks: ["Related tests pass", "No test regressions"],
290534
- commands: [`${fp.packageManager} run test`],
290535
- artifacts: ["test-results.json"]
290536
- });
290537
- }
290538
- if (fp.router) {
290539
- steps.push({
290540
- trigger: "Route handler added or modified",
290541
- checks: ["Route responds with correct status", "Auth middleware is applied", "Input validation works"],
290542
- commands: ["vibecheck scan"],
290543
- artifacts: ["truthpack/routes.json"]
290544
- });
290545
- }
290546
- steps.push({
290547
- trigger: "Any source file change",
290548
- checks: ["No new architecture rule violations", "No new circular dependencies"],
290549
- commands: ["vibecheck arch-rules"],
290550
- artifacts: []
290551
- });
290552
- return steps;
290553
- }
290554
- // ═══════════════════════════════════════════════════════════════════════════
290555
- // RISK BRIEFING
290556
- // ═══════════════════════════════════════════════════════════════════════════
290557
- buildRiskBriefing() {
290558
- const criticalFiles = this.dna.hotspots.filter((h) => h.score > 30).slice(0, 10).map((h) => h.file);
290559
- const recentViolations = this.ruleResult?.violations.filter((v) => v.severity === "error").slice(0, 10) || [];
290560
- const securityConcerns = [];
290561
- for (const risk of this.dna.riskMap) {
290562
- if (risk.riskLevel === "critical") {
290563
- securityConcerns.push(`${risk.file}: ${risk.factors.join(", ")}`);
290564
- }
290565
- }
290566
- const testGaps = [];
290567
- const sourceFiles = this.data.files.filter(
290568
- (f) => !f.relativePath.includes(".test.") && !f.relativePath.includes(".spec.") && (f.relativePath.endsWith(".ts") || f.relativePath.endsWith(".tsx")) && !f.relativePath.includes("config") && !f.relativePath.includes(".d.ts")
290569
- );
290570
- const testFiles = this.data.files.filter(
290571
- (f) => f.relativePath.includes(".test.") || f.relativePath.includes(".spec.")
290572
- );
290573
- const testedBases = new Set(testFiles.map(
290574
- (f) => path5.basename(f.relativePath).replace(/\.(test|spec)\.(ts|tsx|js|jsx)$/, "")
290575
- ));
290576
- for (const file of sourceFiles) {
290577
- const baseName = path5.basename(file.relativePath).replace(/\.(ts|tsx|js|jsx)$/, "");
290578
- if (!testedBases.has(baseName) && file.exports.length > 0) {
290579
- testGaps.push(`${file.relativePath} has exports but no test file`);
290580
- }
290581
- }
290582
- const driftWarnings = [];
290583
- for (const cycle of this.graph.cycles) {
290584
- driftWarnings.push(`Circular dependency: ${cycle.nodes.slice(0, 3).join(" \u2192 ")}${cycle.nodes.length > 3 ? "..." : ""}`);
290585
- }
290586
- return {
290587
- criticalFiles,
290588
- recentViolations,
290589
- securityConcerns: securityConcerns.slice(0, 5),
290590
- testGaps: testGaps.slice(0, 10),
290591
- driftWarnings: driftWarnings.slice(0, 5)
290592
- };
290593
- }
290594
- // ═══════════════════════════════════════════════════════════════════════════
290595
- // HELPERS
290596
- // ═══════════════════════════════════════════════════════════════════════════
290597
- toRelative(filePath) {
290598
- if (filePath.startsWith(this.rootPath)) {
290599
- return filePath.slice(this.rootPath.length + 1).replace(/\\/g, "/");
290600
- }
290601
- return filePath.replace(/\\/g, "/");
290602
- }
290603
- };
290604
- var ContextExplainer = class {
290605
- /**
290606
- * Generate a rich explanation for a file.
290607
- */
290608
- explain(filePath, ctx) {
290609
- const fc = ctx.fileContext;
290610
- const paragraphs = [];
290611
- const quickFacts = [];
290612
- const warnings = [];
290613
- const relatedFiles = [];
290614
- const overlays = [];
290615
- const role = fc?.role ?? "unknown";
290616
- const roleSummary = this.buildRoleSummary(filePath, role, ctx);
290617
- if (fc) {
290618
- const depCount = fc.dependedOnBy.length;
290619
- const depsOnCount = fc.dependsOn.length;
290620
- if (depCount > 0 || depsOnCount > 0) {
290621
- const parts2 = [];
290622
- if (depCount > 0) {
290623
- const critical = depCount > 10 ? " \u2014 changes here have wide blast radius" : "";
290624
- parts2.push(`${depCount} file${depCount > 1 ? "s" : ""} depend on this${critical}`);
290625
- }
290626
- if (depsOnCount > 0) {
290627
- parts2.push(`it imports from ${depsOnCount} file${depsOnCount > 1 ? "s" : ""}`);
290628
- }
290629
- paragraphs.push({
290630
- heading: "Dependencies",
290631
- text: parts2.join(". ") + ".",
290632
- importance: depCount > 10 ? "critical" : depCount > 5 ? "high" : "medium"
290633
- });
290634
- }
290635
- quickFacts.push({ label: "Role", value: role, icon: "layer" });
290636
- if (fc.layer) quickFacts.push({ label: "Layer", value: fc.layer, icon: "layer" });
290637
- quickFacts.push({ label: "Dependents", value: String(depCount), icon: "dependency" });
290638
- quickFacts.push({ label: "Dependencies", value: String(depsOnCount), icon: "dependency" });
290639
- if (fc.riskLevel === "critical" || fc.riskLevel === "high") {
290640
- quickFacts.push({ label: "Risk", value: fc.riskLevel.toUpperCase(), icon: "warning" });
290641
- warnings.push({
290642
- message: `This file is classified as ${fc.riskLevel} risk`,
290643
- severity: fc.riskLevel === "critical" ? "error" : "warning",
290644
- action: "Add comprehensive tests and review carefully before merging changes"
290645
- });
290646
- }
290647
- for (const dep of fc.dependedOnBy.slice(0, 5)) {
290648
- relatedFiles.push({
290649
- filePath: dep,
290650
- reason: `Imports from this file`,
290651
- relationship: "depended-by",
290652
- confidence: 0.9
290653
- });
290654
- }
290655
- overlays.push({
290656
- type: "code-lens",
290657
- line: 1,
290658
- text: `${depCount} dependent${depCount !== 1 ? "s" : ""} \xB7 ${depsOnCount} import${depsOnCount !== 1 ? "s" : ""} \xB7 ${role}`,
290659
- tooltip: `This ${role} file has ${depCount} files that depend on it and imports from ${depsOnCount} files`
290660
- });
290661
- }
290662
- if (fc && fc.conventions.length > 0) {
290663
- paragraphs.push({
290664
- heading: "Conventions",
290665
- text: `Follow these discovered conventions: ${fc.conventions.slice(0, 3).join("; ")}.`,
290666
- importance: "medium"
290667
- });
290668
- }
290669
- if (ctx.rules) {
290670
- const fileViolations = ctx.rules.violations.filter(
290671
- (v) => v.sourceSymbol.filePath.includes(shortName(filePath)) || v.targetSymbol && v.targetSymbol.filePath.includes(shortName(filePath))
290672
- );
290673
- if (fileViolations.length > 0) {
290674
- const errors = fileViolations.filter((v) => v.severity === "error");
290675
- const warns = fileViolations.filter((v) => v.severity === "warning");
290676
- paragraphs.push({
290677
- heading: "Architecture Violations",
290678
- text: `${errors.length} error${errors.length !== 1 ? "s" : ""} and ${warns.length} warning${warns.length !== 1 ? "s" : ""} from architecture rules.`,
290679
- importance: errors.length > 0 ? "critical" : "high"
290680
- });
290681
- for (const v of fileViolations.slice(0, 5)) {
290682
- warnings.push({
290683
- message: v.message,
290684
- severity: v.severity === "error" ? "error" : "warning",
290685
- action: v.suggestedFix ?? "Review and fix the violation",
290686
- ruleId: v.ruleId
290687
- });
290688
- overlays.push({
290689
- type: "diagnostic",
290690
- line: v.sourceSymbol.line,
290691
- text: v.message,
290692
- severity: v.severity === "error" ? "error" : "warning",
290693
- tooltip: v.suggestedFix
290694
- });
290695
- }
290696
- }
290697
- }
290698
- if (ctx.callGraph) {
290699
- const fileNodes = ctx.callGraph.nodes.filter((n) => n.filePath.includes(shortName(filePath)));
290700
- const hotFunctions = fileNodes.filter((n) => n.callerCount > 5);
290701
- if (hotFunctions.length > 0) {
290702
- paragraphs.push({
290703
- heading: "Hot Functions",
290704
- text: hotFunctions.map(
290705
- (f) => `\`${f.name}\` is called by ${f.callerCount} function${f.callerCount !== 1 ? "s" : ""}${f.calleeCount > 0 ? ` and calls ${f.calleeCount}` : ""}`
290706
- ).join(". ") + ".",
290707
- importance: "high"
290708
- });
290709
- for (const fn of hotFunctions) {
290710
- overlays.push({
290711
- type: "code-lens",
290712
- line: 0,
290713
- // Would need symbol line mapping
290714
- text: `${fn.callerCount} callers \xB7 ${fn.calleeCount} callees`,
290715
- tooltip: `Function ${fn.name} has ${fn.callerCount} callers and ${fn.calleeCount} callees`
290716
- });
290717
- }
290718
- }
290719
- const deadInFile = ctx.callGraph.stats.deadFunctions.filter(
290720
- (d) => d.filePath.includes(shortName(filePath))
290721
- );
290722
- if (deadInFile.length > 0) {
290723
- for (const dead of deadInFile) {
290724
- warnings.push({
290725
- message: `\`${dead.name}\` appears to be dead code (exported but never called)`,
290726
- severity: "info",
290727
- action: "Verify this function is not called via dynamic dispatch or external consumers, then consider removing it"
290728
- });
290729
- }
290730
- }
290731
- }
290732
- if (ctx.temporal) {
290733
- const hotspot = ctx.temporal.changeHotspots.find((h) => filePath.includes(h.file) || h.file.includes(shortName(filePath)));
290734
- if (hotspot) {
290735
- const daysSince = Math.round((Date.now() - new Date(hotspot.lastChanged).getTime()) / 864e5);
290736
- paragraphs.push({
290737
- heading: "Recent Activity",
290738
- text: `Changed ${hotspot.commits} times in the last ${ctx.temporal.stats.analysisWindowDays} days by ${hotspot.authors} author${hotspot.authors !== 1 ? "s" : ""}. Last modified ${daysSince} day${daysSince !== 1 ? "s" : ""} ago.`,
290739
- importance: hotspot.commits > 10 ? "high" : "medium"
290740
- });
290741
- quickFacts.push({ label: "Recent commits", value: String(hotspot.commits), icon: "git" });
290742
- quickFacts.push({ label: "Last changed", value: `${daysSince}d ago`, icon: "git" });
290743
- quickFacts.push({ label: "Authors", value: String(hotspot.authors), icon: "git" });
290744
- }
290745
- const churn = ctx.temporal.churnFiles.find((c) => filePath.includes(c.file) || c.file.includes(shortName(filePath)));
290746
- if (churn && churn.severity !== "low") {
290747
- warnings.push({
290748
- message: churn.reason,
290749
- severity: churn.severity === "high" ? "warning" : "info",
290750
- action: "Consider whether this file needs refactoring to reduce change frequency"
290751
- });
290752
- }
290753
- const expertise = ctx.temporal.authorExpertise.find(
290754
- (e) => filePath.startsWith(e.area) || filePath.includes(e.area)
290755
- );
290756
- if (expertise && expertise.busFactor === 1) {
290757
- warnings.push({
290758
- message: `Bus factor of 1 \u2014 ${expertise.primaryAuthor} has made ${expertise.authors[0]?.percentage}% of changes to this area`,
290759
- severity: "info",
290760
- action: "Consider knowledge sharing or pair programming for this area"
290761
- });
290762
- }
290763
- }
290764
- if (ctx.learned) {
290765
- const coEdits = ctx.learned.coEdits.filter((p) => p.files[0].includes(shortName(filePath)) || p.files[1].includes(shortName(filePath))).slice(0, 3);
290766
- if (coEdits.length > 0) {
290767
- for (const pair of coEdits) {
290768
- const other = pair.files[0].includes(shortName(filePath)) ? pair.files[1] : pair.files[0];
290769
- relatedFiles.push({
290770
- filePath: other,
290771
- reason: `Often edited together (${pair.count} times)`,
290772
- relationship: "co-edited",
290773
- confidence: pair.weight
290774
- });
290775
- }
290776
- }
290777
- }
290778
- if (fc && fc.editGuidance.length > 0) {
290779
- paragraphs.push({
290780
- heading: "Edit Guidance",
290781
- text: fc.editGuidance.join(" "),
290782
- importance: "medium"
290783
- });
290784
- }
290785
- return {
290786
- filePath,
290787
- roleSummary,
290788
- paragraphs,
290789
- quickFacts,
290790
- warnings,
290791
- relatedFiles,
290792
- overlays
290793
- };
290794
- }
290795
- /**
290796
- * Generate a compact markdown explanation for AI agent consumption.
290797
- */
290798
- explainForAgent(filePath, ctx) {
290799
- const explanation = this.explain(filePath, ctx);
290800
- const lines = [];
290801
- lines.push(`## ${shortName(filePath)}: ${explanation.roleSummary}`);
290802
- lines.push("");
290803
- if (explanation.quickFacts.length > 0) {
290804
- lines.push(explanation.quickFacts.map((f) => `**${f.label}**: ${f.value}`).join(" \xB7 "));
290805
- lines.push("");
290806
- }
290807
- for (const p of explanation.paragraphs.filter((p2) => p2.importance === "critical" || p2.importance === "high")) {
290808
- lines.push(`### ${p.heading}`);
290809
- lines.push(p.text);
290810
- lines.push("");
290811
- }
290812
- if (explanation.warnings.length > 0) {
290813
- lines.push("### Warnings");
290814
- for (const w of explanation.warnings) {
290815
- const icon = w.severity === "error" ? "MUST FIX" : w.severity === "warning" ? "SHOULD FIX" : "NOTE";
290816
- lines.push(`- **[${icon}]** ${w.message} \u2014 ${w.action}`);
290817
- }
290818
- lines.push("");
290819
- }
290820
- if (explanation.relatedFiles.length > 0) {
290821
- lines.push("### Related Files");
290822
- for (const rf of explanation.relatedFiles.slice(0, 5)) {
290823
- lines.push(`- \`${rf.filePath}\` \u2014 ${rf.reason}`);
290824
- }
290825
- lines.push("");
290826
- }
290827
- return lines.join("\n");
290828
- }
290829
- /**
290830
- * Generate IDE overlay data for the VS Code extension to consume.
290831
- */
290832
- getIDEOverlays(filePath, ctx) {
290833
- const explanation = this.explain(filePath, ctx);
290834
- return explanation.overlays;
290835
- }
290836
- /**
290837
- * Generate a health report explanation.
290838
- */
290839
- explainHealth(health, dna) {
290840
- const lines = [];
290841
- const dims = health.dimensions;
290842
- lines.push(`## Codebase Health: ${health.overall}/100`);
290843
- lines.push("");
290844
- const entries = [
290845
- { name: "Architecture", score: dims.architecture, explain: this.explainArchScore(dims.architecture, dna) },
290846
- { name: "Test Coverage", score: dims.testCoverage, explain: this.explainTestScore(dims.testCoverage) },
290847
- { name: "Conventions", score: dims.conventions, explain: this.explainConventionScore(dims.conventions, dna) },
290848
- { name: "Dependencies", score: dims.dependencies, explain: this.explainDependencyScore(dims.dependencies, dna) },
290849
- { name: "Security", score: dims.security, explain: dims.security >= 80 ? "No critical security concerns detected" : "Critical risk areas identified" },
290850
- { name: "Complexity", score: dims.complexity, explain: dims.complexity >= 80 ? "Complexity is well-managed" : "High-complexity hotspots detected" }
290851
- ];
290852
- for (const entry of entries) {
290853
- const bar = this.renderBar(entry.score);
290854
- lines.push(`${bar} **${entry.name}**: ${entry.score}/100 \u2014 ${entry.explain}`);
290855
- }
290856
- return lines.join("\n");
290857
- }
290858
- // ═══════════════════════════════════════════════════════════════════════════
290859
- // PRIVATE
290860
- // ═══════════════════════════════════════════════════════════════════════════
290861
- buildRoleSummary(filePath, role, ctx) {
290862
- const parts2 = [];
290863
- switch (role) {
290864
- case "service":
290865
- parts2.push("Business logic service");
290866
- break;
290867
- case "route-handler":
290868
- parts2.push("API route handler");
290869
- break;
290870
- case "component":
290871
- parts2.push("UI component");
290872
- break;
290873
- case "repository":
290874
- parts2.push("Data access layer");
290875
- break;
290876
- case "middleware":
290877
- parts2.push("Request middleware");
290878
- break;
290879
- case "test":
290880
- parts2.push("Test file");
290881
- break;
290882
- case "config":
290883
- parts2.push("Configuration");
290884
- break;
290885
- case "type":
290886
- parts2.push("Type definitions");
290887
- break;
290888
- case "util":
290889
- parts2.push("Utility module");
290890
- break;
290891
- case "entry":
290892
- parts2.push("Entry point");
290893
- break;
290894
- default:
290895
- parts2.push("Source file");
290896
- }
290897
- if (ctx.fileContext) {
290898
- if (ctx.fileContext.layer) parts2.push(`in ${ctx.fileContext.layer} layer`);
290899
- if (ctx.fileContext.dependedOnBy.length > 10) parts2.push("(high-impact)");
290900
- }
290901
- return parts2.join(" ");
290902
- }
290903
- explainArchScore(score, dna) {
290904
- if (score >= 80) return `Strong architecture with ${dna.patterns.length} recognized patterns`;
290905
- if (score >= 50) return `Moderate architecture \u2014 ${dna.patterns.length} patterns detected, room to strengthen boundaries`;
290906
- return "Architecture needs attention \u2014 few recognized patterns or boundaries";
290907
- }
290908
- explainTestScore(score) {
290909
- if (score >= 80) return "Good test coverage across source files";
290910
- if (score >= 50) return "Moderate coverage \u2014 some source files lack tests";
290911
- return "Low test coverage \u2014 many exported modules have no test files";
290912
- }
290913
- explainConventionScore(score, dna) {
290914
- const strong = dna.conventions.filter((c) => c.confidence > 0.6).length;
290915
- if (score >= 80) return `${strong} strong conventions enforced consistently`;
290916
- if (score >= 50) return `${strong} conventions detected but inconsistently applied`;
290917
- return "Few consistent conventions \u2014 codebase style varies across files";
290918
- }
290919
- explainDependencyScore(score, dna) {
290920
- const circular = dna.boundaries.filter((b) => b.isCircular).length;
290921
- if (score >= 80) return "Clean dependency graph with no circular dependencies";
290922
- if (circular > 0) return `${circular} circular dependency${circular > 1 ? "ies" : "y"} detected \u2014 these increase coupling and make code harder to reason about`;
290923
- return "Dependency health needs improvement";
290924
- }
290925
- renderBar(score) {
290926
- const filled = Math.round(score / 10);
290927
- return "\u2588".repeat(filled) + "\u2591".repeat(10 - filled);
290928
- }
290929
- };
290930
- function shortName(filePath) {
290931
- const parts2 = filePath.split("/");
290932
- return parts2[parts2.length - 1] ?? filePath;
290933
- }
290934
-
290935
290944
  // ../context-engine/dist/index.js
290936
290945
  import * as fs5 from "fs";
290937
290946
  import * as path6 from "path";
@@ -291813,7 +291822,7 @@ var ContextEngine = class {
291813
291822
  // src/server.ts
291814
291823
  init_dist();
291815
291824
 
291816
- // ../subscriptions/dist/chunk-EQ52N5FM.js
291825
+ // ../subscriptions/dist/chunk-IRYOMNQT.js
291817
291826
  var PLAN_IDS = ["free", "pro", "team", "enterprise"];
291818
291827
  var PLAN_RANK = Object.fromEntries(
291819
291828
  PLAN_IDS.map((id, index2) => [id, index2])
@@ -291898,9 +291907,9 @@ var PLAN_DEFINITIONS = {
291898
291907
  },
291899
291908
  pro: {
291900
291909
  displayName: "Pro",
291901
- tagline: "$9.99/mo or $99.99/yr (save 17%).",
291902
- monthlyPriceUsd: 9.99,
291903
- priceLabel: "$9.99/mo",
291910
+ tagline: "$19.00/mo or $189.99/yr (save 17%).",
291911
+ monthlyPriceUsd: 19,
291912
+ priceLabel: "$19.00/mo",
291904
291913
  billingInterval: "month",
291905
291914
  badgeToken: "tier-pro",
291906
291915
  highlights: [
@@ -293661,20 +293670,11 @@ var InputValidator = class {
293661
293670
  errors.push("Path traversal detected");
293662
293671
  logger.warn("Path traversal attempt blocked", { input, normalized });
293663
293672
  }
293664
- if (path7.isAbsolute(input)) {
293665
- errors.push("Absolute paths not allowed");
293666
- }
293667
293673
  if (input.includes("\0")) {
293668
293674
  errors.push("Null byte detected in path");
293669
293675
  logger.warn("Null byte injection attempt blocked", { input });
293670
293676
  }
293671
- const suspiciousPatterns = [
293672
- /\.\.\//,
293673
- /\.\.\\/,
293674
- /%2e%2e/i,
293675
- /%252e/i,
293676
- /\.\./
293677
- ];
293677
+ const suspiciousPatterns = [/\.\.\//, /\.\.\\/, /%2e%2e/i, /%252e/i, /\.\./];
293678
293678
  for (const pattern of suspiciousPatterns) {
293679
293679
  if (pattern.test(input)) {
293680
293680
  errors.push(`Suspicious pattern detected: ${pattern}`);
@@ -293723,12 +293723,7 @@ var InputValidator = class {
293723
293723
  break;
293724
293724
  }
293725
293725
  }
293726
- const scriptPatterns = [
293727
- /<script/i,
293728
- /javascript:/i,
293729
- /on\w+\s*=/i,
293730
- /<iframe/i
293731
- ];
293726
+ const scriptPatterns = [/<script/i, /javascript:/i, /on\w+\s*=/i, /<iframe/i];
293732
293727
  for (const pattern of scriptPatterns) {
293733
293728
  if (pattern.test(input)) {
293734
293729
  logger.warn("Potential script injection attempt blocked", { input });
@@ -293876,10 +293871,7 @@ var InputValidator = class {
293876
293871
  }
293877
293872
  }
293878
293873
  if (args2.outcome !== void 0) {
293879
- const outcomeResult = this.validateEnum(args2.outcome, [
293880
- "helpful",
293881
- "not_helpful"
293882
- ]);
293874
+ const outcomeResult = this.validateEnum(args2.outcome, ["helpful", "not_helpful"]);
293883
293875
  if (!outcomeResult.valid) {
293884
293876
  errors.push(...outcomeResult.errors.map((e) => `outcome: ${e}`));
293885
293877
  }
@@ -293901,7 +293893,9 @@ var InputValidator = class {
293901
293893
  }
293902
293894
  }
293903
293895
  if (args2.enginePreset !== void 0) {
293904
- const presetResult = this.validateEnum(args2.enginePreset, [...VIBECHECK_SCAN_ENGINE_PRESETS]);
293896
+ const presetResult = this.validateEnum(args2.enginePreset, [
293897
+ ...VIBECHECK_SCAN_ENGINE_PRESETS
293898
+ ]);
293905
293899
  if (!presetResult.valid) {
293906
293900
  errors.push(...presetResult.errors.map((e) => `enginePreset: ${e}`));
293907
293901
  }
@@ -293919,6 +293913,21 @@ var InputValidator = class {
293919
293913
  }
293920
293914
  }
293921
293915
  break;
293916
+ case "vibecheck_ghost": {
293917
+ if (args2.file !== void 0) {
293918
+ const fileResult = this.validatePath(args2.file);
293919
+ if (!fileResult.valid) {
293920
+ errors.push(...fileResult.errors.map((e) => `file: ${e}`));
293921
+ }
293922
+ }
293923
+ if (args2.path !== void 0) {
293924
+ const pathResult = this.validatePath(args2.path);
293925
+ if (!pathResult.valid) {
293926
+ errors.push(...pathResult.errors.map((e) => `path: ${e}`));
293927
+ }
293928
+ }
293929
+ break;
293930
+ }
293922
293931
  }
293923
293932
  if (errors.length > 0) {
293924
293933
  logger.warn("Tool argument validation failed", {
@@ -293992,7 +294001,10 @@ var MCP_TOOLS = [
293992
294001
  path: { type: "string", description: "Workspace root. Defaults to cwd." },
293993
294002
  query: { type: "string", description: "Natural language query." },
293994
294003
  limit: { type: "number", description: "Max results. Default 10." },
293995
- useSemantic: { type: "boolean", description: "Use semantic search (embeddings) when available. Slower but finds conceptually related code." }
294004
+ useSemantic: {
294005
+ type: "boolean",
294006
+ description: "Use semantic search (embeddings) when available. Slower but finds conceptually related code."
294007
+ }
293996
294008
  },
293997
294009
  required: ["query"]
293998
294010
  }
@@ -294015,7 +294027,11 @@ var MCP_TOOLS = [
294015
294027
  properties: {
294016
294028
  path: { type: "string", description: "Workspace root. Defaults to cwd." },
294017
294029
  file: { type: "string", description: "File path that was (or was not) helpful." },
294018
- outcome: { type: "string", enum: ["helpful", "not_helpful"], description: "User feedback." }
294030
+ outcome: {
294031
+ type: "string",
294032
+ enum: ["helpful", "not_helpful"],
294033
+ description: "User feedback."
294034
+ }
294019
294035
  },
294020
294036
  required: ["file", "outcome"]
294021
294037
  }
@@ -294089,7 +294105,11 @@ var MCP_TOOLS = [
294089
294105
  type: "object",
294090
294106
  properties: {
294091
294107
  path: { type: "string", description: "Destination path." },
294092
- type: { type: "string", enum: ["component", "api", "hook", "test"], description: "Type of code to forge." },
294108
+ type: {
294109
+ type: "string",
294110
+ enum: ["component", "api", "hook", "test"],
294111
+ description: "Type of code to forge."
294112
+ },
294093
294113
  name: { type: "string", description: "Name of the component/api/hook." }
294094
294114
  },
294095
294115
  required: ["type", "name"]
@@ -294101,7 +294121,10 @@ var MCP_TOOLS = [
294101
294121
  inputSchema: {
294102
294122
  type: "object",
294103
294123
  properties: {
294104
- path: { type: "string", description: "Workspace or project root path. Defaults to current directory." },
294124
+ path: {
294125
+ type: "string",
294126
+ description: "Workspace or project root path. Defaults to current directory."
294127
+ },
294105
294128
  targetUrl: { type: "string", description: "Base URL to test." }
294106
294129
  }
294107
294130
  }
@@ -294112,7 +294135,10 @@ var MCP_TOOLS = [
294112
294135
  inputSchema: {
294113
294136
  type: "object",
294114
294137
  properties: {
294115
- path: { type: "string", description: "Workspace or project root path. Defaults to current directory." }
294138
+ path: {
294139
+ type: "string",
294140
+ description: "Workspace or project root path. Defaults to current directory."
294141
+ }
294116
294142
  }
294117
294143
  }
294118
294144
  },
@@ -294122,8 +294148,15 @@ var MCP_TOOLS = [
294122
294148
  inputSchema: {
294123
294149
  type: "object",
294124
294150
  properties: {
294125
- path: { type: "string", description: "Workspace or project root path. Defaults to current directory." },
294126
- mode: { type: "string", enum: ["enforce", "observe", "off"], description: "Firewall mode to set." }
294151
+ path: {
294152
+ type: "string",
294153
+ description: "Workspace or project root path. Defaults to current directory."
294154
+ },
294155
+ mode: {
294156
+ type: "string",
294157
+ enum: ["enforce", "observe", "off"],
294158
+ description: "Firewall mode to set."
294159
+ }
294127
294160
  },
294128
294161
  required: ["mode"]
294129
294162
  }
@@ -294140,13 +294173,30 @@ var MCP_TOOLS = [
294140
294173
  required: ["action"]
294141
294174
  }
294142
294175
  },
294176
+ {
294177
+ name: "vibecheck_ghost",
294178
+ description: "Ghost Mode \u2014 symbolic execution trace showing how AI-generated code will break at runtime without running it. Returns a line-by-line trace with verdicts.",
294179
+ inputSchema: {
294180
+ type: "object",
294181
+ properties: {
294182
+ file: {
294183
+ type: "string",
294184
+ description: "File path to trace (relative to workspace or absolute)."
294185
+ }
294186
+ },
294187
+ required: ["file"]
294188
+ }
294189
+ },
294143
294190
  {
294144
294191
  name: "vibecheck_docguard",
294145
294192
  description: "Documentation quality analysis (orphaned, stale, or duplicate docs).",
294146
294193
  inputSchema: {
294147
294194
  type: "object",
294148
294195
  properties: {
294149
- path: { type: "string", description: "Workspace or project root path. Defaults to current directory." }
294196
+ path: {
294197
+ type: "string",
294198
+ description: "Workspace or project root path. Defaults to current directory."
294199
+ }
294150
294200
  }
294151
294201
  }
294152
294202
  },
@@ -294156,7 +294206,10 @@ var MCP_TOOLS = [
294156
294206
  inputSchema: {
294157
294207
  type: "object",
294158
294208
  properties: {
294159
- path: { type: "string", description: "Workspace or project root path. Defaults to current directory." }
294209
+ path: {
294210
+ type: "string",
294211
+ description: "Workspace or project root path. Defaults to current directory."
294212
+ }
294160
294213
  }
294161
294214
  }
294162
294215
  },
@@ -294166,7 +294219,10 @@ var MCP_TOOLS = [
294166
294219
  inputSchema: {
294167
294220
  type: "object",
294168
294221
  properties: {
294169
- path: { type: "string", description: "Workspace or project root path. Defaults to current directory." }
294222
+ path: {
294223
+ type: "string",
294224
+ description: "Workspace or project root path. Defaults to current directory."
294225
+ }
294170
294226
  }
294171
294227
  }
294172
294228
  },
@@ -294176,7 +294232,10 @@ var MCP_TOOLS = [
294176
294232
  inputSchema: {
294177
294233
  type: "object",
294178
294234
  properties: {
294179
- path: { type: "string", description: "Workspace or project root path. Defaults to current directory." },
294235
+ path: {
294236
+ type: "string",
294237
+ description: "Workspace or project root path. Defaults to current directory."
294238
+ },
294180
294239
  out: { type: "string", description: "Output path for the truthpack." }
294181
294240
  }
294182
294241
  }
@@ -294187,7 +294246,10 @@ var MCP_TOOLS = [
294187
294246
  inputSchema: {
294188
294247
  type: "object",
294189
294248
  properties: {
294190
- path: { type: "string", description: "Workspace or project root path. Defaults to current directory." },
294249
+ path: {
294250
+ type: "string",
294251
+ description: "Workspace or project root path. Defaults to current directory."
294252
+ },
294191
294253
  branch: { type: "string", description: "Branch to compare against." }
294192
294254
  }
294193
294255
  }
@@ -294198,7 +294260,10 @@ var MCP_TOOLS = [
294198
294260
  inputSchema: {
294199
294261
  type: "object",
294200
294262
  properties: {
294201
- path: { type: "string", description: "Workspace or project root path. Defaults to current directory." }
294263
+ path: {
294264
+ type: "string",
294265
+ description: "Workspace or project root path. Defaults to current directory."
294266
+ }
294202
294267
  }
294203
294268
  }
294204
294269
  },
@@ -294209,7 +294274,10 @@ var MCP_TOOLS = [
294209
294274
  type: "object",
294210
294275
  properties: {
294211
294276
  intent: { type: "string", description: "Description of the feature to build." },
294212
- path: { type: "string", description: "Workspace or project root path. Defaults to current directory." }
294277
+ path: {
294278
+ type: "string",
294279
+ description: "Workspace or project root path. Defaults to current directory."
294280
+ }
294213
294281
  },
294214
294282
  required: ["intent"]
294215
294283
  }
@@ -294220,7 +294288,10 @@ var MCP_TOOLS = [
294220
294288
  inputSchema: {
294221
294289
  type: "object",
294222
294290
  properties: {
294223
- path: { type: "string", description: "Workspace or project root path. Defaults to current directory." }
294291
+ path: {
294292
+ type: "string",
294293
+ description: "Workspace or project root path. Defaults to current directory."
294294
+ }
294224
294295
  }
294225
294296
  }
294226
294297
  },
@@ -294254,9 +294325,19 @@ var MCP_TOOLS = [
294254
294325
  type: "object",
294255
294326
  properties: {
294256
294327
  path: { type: "string", description: "Project path. Defaults to current directory." },
294257
- severity: { type: "string", enum: ["critical", "high", "medium", "low", "info"], description: "Filter by severity." },
294258
- engine: { type: "string", description: 'Filter by engine ID (e.g. "phantom_dep", "ghost_route").' },
294259
- file: { type: "string", description: "Filter findings to a specific file or directory path." }
294328
+ severity: {
294329
+ type: "string",
294330
+ enum: ["critical", "high", "medium", "low", "info"],
294331
+ description: "Filter by severity."
294332
+ },
294333
+ engine: {
294334
+ type: "string",
294335
+ description: 'Filter by engine ID (e.g. "phantom_dep", "ghost_route").'
294336
+ },
294337
+ file: {
294338
+ type: "string",
294339
+ description: "Filter findings to a specific file or directory path."
294340
+ }
294260
294341
  }
294261
294342
  }
294262
294343
  },
@@ -294310,7 +294391,10 @@ var MCP_TOOLS = [
294310
294391
  inputSchema: {
294311
294392
  type: "object",
294312
294393
  properties: {
294313
- path: { type: "string", description: "Workspace root path. Defaults to current directory." }
294394
+ path: {
294395
+ type: "string",
294396
+ description: "Workspace root path. Defaults to current directory."
294397
+ }
294314
294398
  }
294315
294399
  }
294316
294400
  }
@@ -294461,12 +294545,12 @@ function createScanIdempotencyKey(prefix) {
294461
294545
  // src/mcp-scan-meter-client.ts
294462
294546
  var MCP_SCAN_METER_CLIENT = {
294463
294547
  type: "mcp",
294464
- version: "24.5.8"
294548
+ version: "24.6.2"
294465
294549
  };
294466
294550
 
294467
294551
  // src/server.ts
294468
294552
  import { uploadScanToApi } from "@repo/shared/sync/upload-scan";
294469
- var execAsync = promisify(exec);
294553
+ var execFileAsync = promisify(execFile);
294470
294554
  async function executeScan(targetPath, engineToggles = null) {
294471
294555
  const resolved = path9.resolve(targetPath);
294472
294556
  const stat4 = fs6.statSync(resolved);
@@ -294617,10 +294701,7 @@ async function runGuard(targetPath) {
294617
294701
  var MCP_TOOL_TIMEOUT_MS = 18e4;
294618
294702
  function withTimeout(promise, ms, label) {
294619
294703
  return new Promise((resolve6, reject) => {
294620
- const timer = setTimeout(
294621
- () => reject(new Error(`${label} timed out after ${ms / 1e3}s`)),
294622
- ms
294623
- );
294704
+ const timer = setTimeout(() => reject(new Error(`${label} timed out after ${ms / 1e3}s`)), ms);
294624
294705
  promise.then(
294625
294706
  (value) => {
294626
294707
  clearTimeout(timer);
@@ -294749,10 +294830,8 @@ async function handleCallToolRequest(request, runtimeOverrides = {}) {
294749
294830
  }
294750
294831
  const validation = InputValidator.validateToolArgs(name2, args2);
294751
294832
  if (!validation.valid) {
294752
- return buildErrorResponse(
294753
- `Invalid arguments for ${name2}:
294754
- ${validation.errors.join("\n")}`
294755
- );
294833
+ return buildErrorResponse(`Invalid arguments for ${name2}:
294834
+ ${validation.errors.join("\n")}`);
294756
294835
  }
294757
294836
  const BILLABLE_ENGINE_TOOLS = /* @__PURE__ */ new Set(["vibecheck_scan", "vibecheck_score"]);
294758
294837
  if (BILLABLE_ENGINE_TOOLS.has(name2)) {
@@ -294795,9 +294874,14 @@ ${validation.errors.join("\n")}`
294795
294874
  const recordMcpBillableUsage = async (toolKey) => {
294796
294875
  const tok = process.env.VIBECHECK_TOKEN?.trim();
294797
294876
  if (!tok || shouldSkipServerScanMetering()) return { ok: true };
294798
- const r = await postServerScanRecord(tok, MCP_SCAN_METER_CLIENT, createScanIdempotencyKey(toolKey), {
294799
- scanType: toolKey.replace(/^mcp-/, "")
294800
- });
294877
+ const r = await postServerScanRecord(
294878
+ tok,
294879
+ MCP_SCAN_METER_CLIENT,
294880
+ createScanIdempotencyKey(toolKey),
294881
+ {
294882
+ scanType: toolKey.replace(/^mcp-/, "")
294883
+ }
294884
+ );
294801
294885
  if (!r.ok) {
294802
294886
  return { ok: false, message: r.error };
294803
294887
  }
@@ -294819,9 +294903,18 @@ ${validation.errors.join("\n")}`
294819
294903
  await engine.initialize(data);
294820
294904
  return engine.getProactiveContext({ focusedFile });
294821
294905
  };
294822
- const context = await withTimeout(run2(), MCP_TOOL_TIMEOUT_MS, "vibecheck_context_proactive");
294906
+ const context = await withTimeout(
294907
+ run2(),
294908
+ MCP_TOOL_TIMEOUT_MS,
294909
+ "vibecheck_context_proactive"
294910
+ );
294823
294911
  return {
294824
- content: [{ type: "text", text: typeof context === "string" ? context : JSON.stringify(context, null, 2) }]
294912
+ content: [
294913
+ {
294914
+ type: "text",
294915
+ text: typeof context === "string" ? context : JSON.stringify(context, null, 2)
294916
+ }
294917
+ ]
294825
294918
  };
294826
294919
  }
294827
294920
  case "vibecheck_context_intent": {
@@ -294868,12 +294961,16 @@ ${validation.errors.join("\n")}`
294868
294961
  const file = args2?.file;
294869
294962
  const outcome = args2?.outcome;
294870
294963
  if (!file || !outcome) {
294871
- return buildErrorResponse('vibecheck_context_feedback requires "file" and "outcome" (helpful|not_helpful)');
294964
+ return buildErrorResponse(
294965
+ 'vibecheck_context_feedback requires "file" and "outcome" (helpful|not_helpful)'
294966
+ );
294872
294967
  }
294873
294968
  const feedbackFile = normalizeRelativeWorkspaceFilePath(file, "file");
294874
294969
  await runtime.recordFeedback(targetPath, feedbackFile, outcome);
294875
294970
  return {
294876
- content: [{ type: "text", text: JSON.stringify({ ok: true, file: feedbackFile, outcome }) }]
294971
+ content: [
294972
+ { type: "text", text: JSON.stringify({ ok: true, file: feedbackFile, outcome }) }
294973
+ ]
294877
294974
  };
294878
294975
  }
294879
294976
  case "vibecheck_roast": {
@@ -294993,6 +295090,31 @@ ${validation.errors.join("\n")}`
294993
295090
  ]
294994
295091
  };
294995
295092
  }
295093
+ case "vibecheck_ghost": {
295094
+ const fileArg = args2.file;
295095
+ if (!fileArg || typeof fileArg !== "string") {
295096
+ return buildErrorResponse('vibecheck_ghost requires "file"');
295097
+ }
295098
+ let relFile;
295099
+ try {
295100
+ relFile = normalizeRelativeWorkspaceFilePath(fileArg, "file");
295101
+ } catch (e) {
295102
+ return buildErrorResponse(e instanceof Error ? e.message : "Invalid file path");
295103
+ }
295104
+ const absFile = path9.join(targetPath, relFile);
295105
+ if (!fs6.existsSync(absFile) || !fs6.statSync(absFile).isFile()) {
295106
+ return buildErrorResponse(`Not a file: ${relFile}`);
295107
+ }
295108
+ const { runGhostTrace } = await import("@vibecheck/engines");
295109
+ const trace = await withTimeout(
295110
+ runGhostTrace({ workspaceRoot: targetPath, filePath: absFile }),
295111
+ MCP_TOOL_TIMEOUT_MS,
295112
+ "vibecheck_ghost"
295113
+ );
295114
+ return {
295115
+ content: [{ type: "text", text: JSON.stringify(trace, null, 2) }]
295116
+ };
295117
+ }
294996
295118
  // ── Platform Unification Tool Handlers ──────────────────────────────
294997
295119
  case "vibecheck_trust_score": {
294998
295120
  const result = await withTimeout(
@@ -295031,10 +295153,7 @@ ${validation.errors.join("\n")}`
295031
295153
  const fileFilter = args2.file;
295032
295154
  filtered = filtered.filter((f) => f.file?.includes(fileFilter));
295033
295155
  }
295034
- const lines = [
295035
- `## Findings (${filtered.length} of ${gatedReport.summary.total})`,
295036
- ""
295037
- ];
295156
+ const lines = [`## Findings (${filtered.length} of ${gatedReport.summary.total})`, ""];
295038
295157
  for (const f of filtered.slice(0, 50)) {
295039
295158
  const loc = f.file ? `${f.file}${f.line ? `:${f.line}` : ""}` : "unknown";
295040
295159
  lines.push(`- **[${f.severity.toUpperCase()}]** ${f.message}`);
@@ -295086,16 +295205,22 @@ ${validation.errors.join("\n")}`
295086
295205
  const token = process.env.VIBECHECK_TOKEN?.trim();
295087
295206
  if (!token) {
295088
295207
  return {
295089
- content: [{
295090
- type: "text",
295091
- text: JSON.stringify({
295092
- ok: false,
295093
- message: "Authentication required to dismiss findings across surfaces. Set VIBECHECK_TOKEN or run `vibecheck auth login`.",
295094
- localOnly: true,
295095
- findingId,
295096
- reason
295097
- }, null, 2)
295098
- }]
295208
+ content: [
295209
+ {
295210
+ type: "text",
295211
+ text: JSON.stringify(
295212
+ {
295213
+ ok: false,
295214
+ message: "Authentication required to dismiss findings across surfaces. Set VIBECHECK_TOKEN or run `vibecheck auth login`.",
295215
+ localOnly: true,
295216
+ findingId,
295217
+ reason
295218
+ },
295219
+ null,
295220
+ 2
295221
+ )
295222
+ }
295223
+ ]
295099
295224
  };
295100
295225
  }
295101
295226
  try {
@@ -295103,7 +295228,7 @@ ${validation.errors.join("\n")}`
295103
295228
  const resp = await fetch(`${apiBase}/api/v1/findings/${findingId}`, {
295104
295229
  method: "PATCH",
295105
295230
  headers: {
295106
- "Authorization": `Bearer ${token}`,
295231
+ Authorization: `Bearer ${token}`,
295107
295232
  "Content-Type": "application/json"
295108
295233
  },
295109
295234
  body: JSON.stringify({ resolved: true })
@@ -295112,31 +295237,37 @@ ${validation.errors.join("\n")}`
295112
295237
  return buildErrorResponse(`Failed to dismiss finding: HTTP ${resp.status}`);
295113
295238
  }
295114
295239
  return {
295115
- content: [{
295116
- type: "text",
295117
- text: `Finding \`${findingId}\` dismissed. Reason: ${reason}
295240
+ content: [
295241
+ {
295242
+ type: "text",
295243
+ text: `Finding \`${findingId}\` dismissed. Reason: ${reason}
295118
295244
  This change is synced across all surfaces.`
295119
- }]
295245
+ }
295246
+ ]
295120
295247
  };
295121
295248
  } catch (err2) {
295122
- return buildErrorResponse(`Failed to dismiss finding: ${err2 instanceof Error ? err2.message : String(err2)}`);
295249
+ return buildErrorResponse(
295250
+ `Failed to dismiss finding: ${err2 instanceof Error ? err2.message : String(err2)}`
295251
+ );
295123
295252
  }
295124
295253
  }
295125
295254
  case "vibecheck_history": {
295126
295255
  const token = process.env.VIBECHECK_TOKEN?.trim();
295127
295256
  if (!token) {
295128
295257
  return {
295129
- content: [{
295130
- type: "text",
295131
- text: "Scan history requires authentication. Set VIBECHECK_TOKEN or run `vibecheck auth login`."
295132
- }]
295258
+ content: [
295259
+ {
295260
+ type: "text",
295261
+ text: "Scan history requires authentication. Set VIBECHECK_TOKEN or run `vibecheck auth login`."
295262
+ }
295263
+ ]
295133
295264
  };
295134
295265
  }
295135
295266
  try {
295136
295267
  const apiBase = process.env.VIBECHECK_API_URL || "https://api.vibecheckai.dev";
295137
295268
  const limit = args2.limit ?? 10;
295138
295269
  const resp = await fetch(`${apiBase}/api/v1/scans/recent?limit=${limit}`, {
295139
- headers: { "Authorization": `Bearer ${token}` }
295270
+ headers: { Authorization: `Bearer ${token}` }
295140
295271
  });
295141
295272
  if (!resp.ok) {
295142
295273
  return buildErrorResponse(`Failed to fetch scan history: HTTP ${resp.status}`);
@@ -295144,7 +295275,14 @@ This change is synced across all surfaces.`
295144
295275
  const body2 = await resp.json();
295145
295276
  const scans = body2.data ?? [];
295146
295277
  if (scans.length === 0) {
295147
- return { content: [{ type: "text", text: "No scan history found. Run `vibecheck scan .` to create your first scan." }] };
295278
+ return {
295279
+ content: [
295280
+ {
295281
+ type: "text",
295282
+ text: "No scan history found. Run `vibecheck scan .` to create your first scan."
295283
+ }
295284
+ ]
295285
+ };
295148
295286
  }
295149
295287
  const lines = [`## Scan History (${scans.length} most recent)`, ""];
295150
295288
  for (const s of scans) {
@@ -295155,7 +295293,9 @@ This change is synced across all surfaces.`
295155
295293
  }
295156
295294
  return { content: [{ type: "text", text: lines.join("\n") }] };
295157
295295
  } catch (err2) {
295158
- return buildErrorResponse(`Failed to fetch history: ${err2 instanceof Error ? err2.message : String(err2)}`);
295296
+ return buildErrorResponse(
295297
+ `Failed to fetch history: ${err2 instanceof Error ? err2.message : String(err2)}`
295298
+ );
295159
295299
  }
295160
295300
  }
295161
295301
  case "vibecheck_engines": {
@@ -295218,10 +295358,16 @@ This change is synced across all surfaces.`
295218
295358
  case "vibecheck_explain_file":
295219
295359
  case "vibecheck_review": {
295220
295360
  const cmdName = name2.replace("vibecheck_", "");
295221
- const cliArgs = Object.entries(args2).filter(([k]) => k !== "path").map(([k, v]) => `--${k}="${String(v)}"`).join(" ");
295361
+ const cliArgv = [cmdName];
295362
+ for (const [k, v] of Object.entries(args2)) {
295363
+ if (k === "path") continue;
295364
+ cliArgv.push(`--${k}`, String(v));
295365
+ }
295222
295366
  const workDir = targetPath || workspaceRoot;
295367
+ const vibecheckBin = new URL("../node_modules/.bin/vibecheck", import.meta.url).pathname;
295368
+ const binPath = fs6.existsSync(vibecheckBin) ? vibecheckBin : "vibecheck";
295223
295369
  try {
295224
- const { stdout, stderr } = await execAsync(`npx vibecheck ${cmdName} ${cliArgs}`, { cwd: workDir });
295370
+ const { stdout, stderr } = await execFileAsync(binPath, cliArgv, { cwd: workDir });
295225
295371
  return {
295226
295372
  content: [
295227
295373
  {
@@ -295231,7 +295377,10 @@ This change is synced across all surfaces.`
295231
295377
  ]
295232
295378
  };
295233
295379
  } catch (error) {
295234
- return buildErrorResponse(`CLI Execution Error: ${error.message || error.stdout || error.stderr || String(error)}`);
295380
+ const err2 = error;
295381
+ return buildErrorResponse(
295382
+ `CLI Execution Error: ${err2.message ?? err2.stdout ?? err2.stderr ?? String(error)}`
295383
+ );
295235
295384
  }
295236
295385
  }
295237
295386
  default:
@@ -295247,7 +295396,7 @@ function createMcpServer(runtimeOverrides = {}) {
295247
295396
  const server2 = new Server(
295248
295397
  {
295249
295398
  name: "vibecheck-mcp",
295250
- version: "2.0.0-alpha"
295399
+ version: "24.6.2"
295251
295400
  },
295252
295401
  {
295253
295402
  capabilities: {