@vibecheck-ai/mcp 24.5.9 → 24.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/index.js +1069 -936
- package/package.json +15 -23
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
|
|
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 =
|
|
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 =
|
|
5960
|
+
sourceURL = join10(urlGenerate(parsed), sourceURL);
|
|
5961
5961
|
}
|
|
5962
5962
|
return normalize4(sourceURL);
|
|
5963
5963
|
}
|
|
@@ -219544,7 +219544,7 @@ var require_bindings = __commonJS({
|
|
|
219544
219544
|
var fs7 = __require("fs");
|
|
219545
219545
|
var path10 = __require("path");
|
|
219546
219546
|
var fileURLToPath2 = require_file_uri_to_path();
|
|
219547
|
-
var
|
|
219547
|
+
var join10 = path10.join;
|
|
219548
219548
|
var dirname8 = path10.dirname;
|
|
219549
219549
|
var exists2 = fs7.accessSync && function(path11) {
|
|
219550
219550
|
try {
|
|
@@ -219604,7 +219604,7 @@ var require_bindings = __commonJS({
|
|
|
219604
219604
|
var requireFunc = typeof __webpack_require__ === "function" ? __non_webpack_require__ : __require;
|
|
219605
219605
|
var tries = [], i2 = 0, l = opts.try.length, n, b, err2;
|
|
219606
219606
|
for (; i2 < l; i2++) {
|
|
219607
|
-
n =
|
|
219607
|
+
n = join10.apply(
|
|
219608
219608
|
null,
|
|
219609
219609
|
opts.try[i2].map(function(p) {
|
|
219610
219610
|
return opts[p] || p;
|
|
@@ -219665,7 +219665,7 @@ var require_bindings = __commonJS({
|
|
|
219665
219665
|
if (dir === ".") {
|
|
219666
219666
|
dir = process.cwd();
|
|
219667
219667
|
}
|
|
219668
|
-
if (exists2(
|
|
219668
|
+
if (exists2(join10(dir, "package.json")) || exists2(join10(dir, "node_modules"))) {
|
|
219669
219669
|
return dir;
|
|
219670
219670
|
}
|
|
219671
219671
|
if (prev === dir) {
|
|
@@ -219674,7 +219674,7 @@ var require_bindings = __commonJS({
|
|
|
219674
219674
|
);
|
|
219675
219675
|
}
|
|
219676
219676
|
prev = dir;
|
|
219677
|
-
dir =
|
|
219677
|
+
dir = join10(dir, "..");
|
|
219678
219678
|
}
|
|
219679
219679
|
};
|
|
219680
219680
|
}
|
|
@@ -221655,7 +221655,7 @@ ${JSON.stringify(t2, null, 2)}`);
|
|
|
221655
221655
|
|
|
221656
221656
|
// ../context-engine/dist/chunk-3ZE34DAJ.js
|
|
221657
221657
|
import { readFile as readFile2 } from "fs/promises";
|
|
221658
|
-
import { join as join3, dirname as
|
|
221658
|
+
import { join as join3, dirname as dirname5 } from "path";
|
|
221659
221659
|
import { fileURLToPath } from "url";
|
|
221660
221660
|
async function initParser() {
|
|
221661
221661
|
if (ParserClass) return ParserClass;
|
|
@@ -221795,7 +221795,7 @@ var init_chunk_3ZE34DAJ = __esm({
|
|
|
221795
221795
|
SymbolKind2["Export"] = "export";
|
|
221796
221796
|
return SymbolKind2;
|
|
221797
221797
|
})(SymbolKind || {});
|
|
221798
|
-
GRAMMAR_DIR = join3(
|
|
221798
|
+
GRAMMAR_DIR = join3(dirname5(fileURLToPath(import.meta.url)), "../../../../node_modules/tree-sitter-wasms/out");
|
|
221799
221799
|
EXT_TO_GRAMMAR = {
|
|
221800
221800
|
".ts": "typescript",
|
|
221801
221801
|
".tsx": "tsx",
|
|
@@ -278750,10 +278750,7 @@ import { execFile } from "child_process";
|
|
|
278750
278750
|
import { promisify } from "util";
|
|
278751
278751
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
278752
278752
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
278753
|
-
import {
|
|
278754
|
-
ListToolsRequestSchema,
|
|
278755
|
-
CallToolRequestSchema
|
|
278756
|
-
} from "@modelcontextprotocol/sdk/types.js";
|
|
278753
|
+
import { ListToolsRequestSchema, CallToolRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
278757
278754
|
import { runOnFiles } from "@vibecheck-ai/cli/runner";
|
|
278758
278755
|
import {
|
|
278759
278756
|
computeTrustScore,
|
|
@@ -289088,6 +289085,845 @@ rules:
|
|
|
289088
289085
|
match: "src/repositories/**"
|
|
289089
289086
|
`;
|
|
289090
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
|
+
|
|
289091
289927
|
// ../context-engine/dist/chunk-HMKLYBWJ.js
|
|
289092
289928
|
var import_better_sqlite3 = __toESM(require_lib(), 1);
|
|
289093
289929
|
var import_fast_glob2 = __toESM(require_out4(), 1);
|
|
@@ -289097,7 +289933,7 @@ import { mkdirSync } from "fs";
|
|
|
289097
289933
|
import { join as join4 } from "path";
|
|
289098
289934
|
import { readFile as readFile22 } from "fs/promises";
|
|
289099
289935
|
import { accessSync } from "fs";
|
|
289100
|
-
import { extname as extname2, resolve as resolve2, dirname as
|
|
289936
|
+
import { extname as extname2, resolve as resolve2, dirname as dirname6 } from "path";
|
|
289101
289937
|
import { createHash as createHash22 } from "crypto";
|
|
289102
289938
|
var SCHEMA_VERSION = 1;
|
|
289103
289939
|
var PersistentIndex = class {
|
|
@@ -289995,7 +290831,7 @@ var DefaultFileParser = class {
|
|
|
289995
290831
|
}
|
|
289996
290832
|
resolveImportPath(sourcePath, fromFile) {
|
|
289997
290833
|
if (!sourcePath.startsWith(".")) return sourcePath;
|
|
289998
|
-
const dir =
|
|
290834
|
+
const dir = dirname6(fromFile);
|
|
289999
290835
|
const resolved = resolve2(dir, sourcePath);
|
|
290000
290836
|
const extensions = [".ts", ".tsx", ".js", ".jsx", "/index.ts", "/index.tsx", "/index.js"];
|
|
290001
290837
|
for (const ext2 of extensions) {
|
|
@@ -290105,845 +290941,6 @@ function flattenCodeSymbols(symbols) {
|
|
|
290105
290941
|
return flat;
|
|
290106
290942
|
}
|
|
290107
290943
|
|
|
290108
|
-
// ../context-engine/dist/chunk-INBCP46U.js
|
|
290109
|
-
import * as path5 from "path";
|
|
290110
|
-
var ContextSynthesizer = class {
|
|
290111
|
-
rootPath;
|
|
290112
|
-
data;
|
|
290113
|
-
dna;
|
|
290114
|
-
graph;
|
|
290115
|
-
ruleResult;
|
|
290116
|
-
constructor(rootPath, data, dna, graph, ruleResult) {
|
|
290117
|
-
this.rootPath = rootPath;
|
|
290118
|
-
this.data = data;
|
|
290119
|
-
this.dna = dna;
|
|
290120
|
-
this.graph = graph;
|
|
290121
|
-
this.ruleResult = ruleResult || null;
|
|
290122
|
-
}
|
|
290123
|
-
/**
|
|
290124
|
-
* Synthesize the full context — the complete brain dump for AI agents.
|
|
290125
|
-
*/
|
|
290126
|
-
synthesize() {
|
|
290127
|
-
const projectIdentity = this.buildProjectIdentity();
|
|
290128
|
-
const archRules = this.buildArchRuleSummary();
|
|
290129
|
-
const activeViolations = this.ruleResult?.violations || [];
|
|
290130
|
-
const codebaseDNA = this.buildDNASummary();
|
|
290131
|
-
const fileContexts = this.buildFileContexts();
|
|
290132
|
-
const taskPlaybooks = this.buildTaskPlaybooks();
|
|
290133
|
-
const verificationSteps = this.buildVerificationSteps();
|
|
290134
|
-
const riskBriefing = this.buildRiskBriefing();
|
|
290135
|
-
return {
|
|
290136
|
-
version: "2.0.0",
|
|
290137
|
-
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
290138
|
-
projectIdentity,
|
|
290139
|
-
architecturalRules: archRules,
|
|
290140
|
-
activeViolations,
|
|
290141
|
-
codebaseDNA,
|
|
290142
|
-
fileContexts,
|
|
290143
|
-
taskPlaybooks,
|
|
290144
|
-
verificationSteps,
|
|
290145
|
-
riskBriefing
|
|
290146
|
-
};
|
|
290147
|
-
}
|
|
290148
|
-
/**
|
|
290149
|
-
* Generate context for a specific file — what an AI agent needs to know
|
|
290150
|
-
* before editing this file.
|
|
290151
|
-
*/
|
|
290152
|
-
synthesizeForFile(filePath) {
|
|
290153
|
-
const rel = this.toRelative(filePath);
|
|
290154
|
-
const file = this.data.files.find((f) => f.relativePath === rel || f.path === filePath);
|
|
290155
|
-
if (!file) return null;
|
|
290156
|
-
return this.buildSingleFileContext(file);
|
|
290157
|
-
}
|
|
290158
|
-
/**
|
|
290159
|
-
* Generate a compact markdown context document for IDE rules.
|
|
290160
|
-
* This replaces the old static rule generation with intelligence-driven context.
|
|
290161
|
-
*/
|
|
290162
|
-
generateContextDocument() {
|
|
290163
|
-
const ctx = this.synthesize();
|
|
290164
|
-
const lines = [];
|
|
290165
|
-
lines.push(`# ${ctx.projectIdentity.name} \u2014 AI Context`);
|
|
290166
|
-
lines.push(`<!-- Generated by @repo/context-engine at ${ctx.generatedAt} -->`);
|
|
290167
|
-
lines.push("");
|
|
290168
|
-
lines.push("## Project Identity");
|
|
290169
|
-
lines.push(`- **Stack**: ${ctx.projectIdentity.stack}`);
|
|
290170
|
-
lines.push(`- **Architecture**: ${ctx.projectIdentity.architecture}`);
|
|
290171
|
-
if (ctx.projectIdentity.keyPatterns.length > 0) {
|
|
290172
|
-
lines.push(`- **Key Patterns**: ${ctx.projectIdentity.keyPatterns.join(", ")}`);
|
|
290173
|
-
}
|
|
290174
|
-
lines.push("");
|
|
290175
|
-
if (ctx.codebaseDNA.conventions.length > 0) {
|
|
290176
|
-
lines.push("## Conventions (Auto-Discovered)");
|
|
290177
|
-
for (const conv of ctx.codebaseDNA.conventions) {
|
|
290178
|
-
lines.push(`- ${conv}`);
|
|
290179
|
-
}
|
|
290180
|
-
lines.push("");
|
|
290181
|
-
}
|
|
290182
|
-
if (ctx.architecturalRules.length > 0) {
|
|
290183
|
-
lines.push("## Architecture Rules");
|
|
290184
|
-
for (const rule of ctx.architecturalRules) {
|
|
290185
|
-
const icon = rule.severity === "error" ? "MUST" : rule.severity === "warning" ? "SHOULD" : "MAY";
|
|
290186
|
-
lines.push(`- **[${icon}]** ${rule.name}: ${rule.description}`);
|
|
290187
|
-
if (rule.violationCount > 0) {
|
|
290188
|
-
lines.push(` - ${rule.violationCount} active violations`);
|
|
290189
|
-
}
|
|
290190
|
-
}
|
|
290191
|
-
lines.push("");
|
|
290192
|
-
}
|
|
290193
|
-
if (ctx.codebaseDNA.boundaries.length > 0) {
|
|
290194
|
-
lines.push("## Module Boundaries");
|
|
290195
|
-
for (const boundary of ctx.codebaseDNA.boundaries) {
|
|
290196
|
-
lines.push(`- ${boundary}`);
|
|
290197
|
-
}
|
|
290198
|
-
lines.push("");
|
|
290199
|
-
}
|
|
290200
|
-
if (ctx.codebaseDNA.hotFiles.length > 0) {
|
|
290201
|
-
lines.push("## High-Impact Files (Edit With Care)");
|
|
290202
|
-
for (const file of ctx.codebaseDNA.hotFiles.slice(0, 10)) {
|
|
290203
|
-
lines.push(`- \`${file}\``);
|
|
290204
|
-
}
|
|
290205
|
-
lines.push("");
|
|
290206
|
-
}
|
|
290207
|
-
if (ctx.riskBriefing.securityConcerns.length > 0 || ctx.riskBriefing.testGaps.length > 0) {
|
|
290208
|
-
lines.push("## Risk Areas");
|
|
290209
|
-
for (const concern of ctx.riskBriefing.securityConcerns) {
|
|
290210
|
-
lines.push(`- ${concern}`);
|
|
290211
|
-
}
|
|
290212
|
-
for (const gap of ctx.riskBriefing.testGaps.slice(0, 5)) {
|
|
290213
|
-
lines.push(`- ${gap}`);
|
|
290214
|
-
}
|
|
290215
|
-
lines.push("");
|
|
290216
|
-
}
|
|
290217
|
-
if (ctx.projectIdentity.noGoZones.length > 0) {
|
|
290218
|
-
lines.push("## No-Go Zones");
|
|
290219
|
-
for (const zone of ctx.projectIdentity.noGoZones) {
|
|
290220
|
-
lines.push(`- ${zone}`);
|
|
290221
|
-
}
|
|
290222
|
-
lines.push("");
|
|
290223
|
-
}
|
|
290224
|
-
if (ctx.taskPlaybooks.length > 0) {
|
|
290225
|
-
lines.push("## Task Playbooks");
|
|
290226
|
-
for (const playbook of ctx.taskPlaybooks) {
|
|
290227
|
-
lines.push(`### ${playbook.taskType}`);
|
|
290228
|
-
for (const step of playbook.steps) {
|
|
290229
|
-
lines.push(`1. ${step}`);
|
|
290230
|
-
}
|
|
290231
|
-
if (playbook.mustVerify.length > 0) {
|
|
290232
|
-
lines.push(`**Verify**: ${playbook.mustVerify.join(", ")}`);
|
|
290233
|
-
}
|
|
290234
|
-
lines.push("");
|
|
290235
|
-
}
|
|
290236
|
-
}
|
|
290237
|
-
if (ctx.verificationSteps.length > 0) {
|
|
290238
|
-
lines.push("## Verification Protocol");
|
|
290239
|
-
for (const step of ctx.verificationSteps) {
|
|
290240
|
-
lines.push(`### On ${step.trigger}`);
|
|
290241
|
-
for (const check of step.checks) {
|
|
290242
|
-
lines.push(`- ${check}`);
|
|
290243
|
-
}
|
|
290244
|
-
if (step.commands.length > 0) {
|
|
290245
|
-
lines.push(`**Run**: \`${step.commands.join(" && ")}\``);
|
|
290246
|
-
}
|
|
290247
|
-
lines.push("");
|
|
290248
|
-
}
|
|
290249
|
-
}
|
|
290250
|
-
lines.push("## Codebase Health");
|
|
290251
|
-
lines.push(`- **Overall**: ${this.dna.healthScore.overall}/100`);
|
|
290252
|
-
const dims = this.dna.healthScore.dimensions;
|
|
290253
|
-
lines.push(`- Architecture: ${dims.architecture} | Tests: ${dims.testCoverage} | Conventions: ${dims.conventions} | Dependencies: ${dims.dependencies}`);
|
|
290254
|
-
lines.push("");
|
|
290255
|
-
lines.push("---");
|
|
290256
|
-
lines.push("<!-- context-engine:v2 -->");
|
|
290257
|
-
return lines.join("\n");
|
|
290258
|
-
}
|
|
290259
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
290260
|
-
// IDENTITY
|
|
290261
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
290262
|
-
buildProjectIdentity() {
|
|
290263
|
-
const fp = this.dna.fingerprint;
|
|
290264
|
-
const stack = [fp.framework, fp.language, fp.orm, fp.validator, fp.authLib, fp.router].filter(Boolean).join(" | ");
|
|
290265
|
-
const keyPatterns = this.dna.patterns.map((p) => p.name);
|
|
290266
|
-
const criticalPaths = this.dna.hotspots.slice(0, 5).map((h) => h.file);
|
|
290267
|
-
const noGoZones = [];
|
|
290268
|
-
if (this.ruleResult) {
|
|
290269
|
-
const errorRules = this.ruleResult.violations.filter((v) => v.severity === "error");
|
|
290270
|
-
const uniqueMessages = [...new Set(errorRules.map((v) => v.message))];
|
|
290271
|
-
noGoZones.push(...uniqueMessages.slice(0, 5));
|
|
290272
|
-
}
|
|
290273
|
-
for (const cycle of this.graph.cycles) {
|
|
290274
|
-
noGoZones.push(`Circular dependency: ${cycle.nodes.slice(0, 3).join(" \u2192 ")}...`);
|
|
290275
|
-
}
|
|
290276
|
-
const architecture = this.dna.conventions.filter((c) => c.area === "structure").map((c) => c.description).join("; ") || fp.framework;
|
|
290277
|
-
return {
|
|
290278
|
-
name: fp.name,
|
|
290279
|
-
stack,
|
|
290280
|
-
architecture,
|
|
290281
|
-
keyPatterns,
|
|
290282
|
-
criticalPaths,
|
|
290283
|
-
noGoZones: noGoZones.slice(0, 10)
|
|
290284
|
-
};
|
|
290285
|
-
}
|
|
290286
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
290287
|
-
// RULES SUMMARY
|
|
290288
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
290289
|
-
buildArchRuleSummary() {
|
|
290290
|
-
if (!this.ruleResult) return [];
|
|
290291
|
-
const breakdown = this.ruleResult.ruleBreakdown;
|
|
290292
|
-
return Object.entries(breakdown).map(([ruleId, count]) => {
|
|
290293
|
-
const violation = this.ruleResult.violations.find((v) => v.ruleId === ruleId);
|
|
290294
|
-
return {
|
|
290295
|
-
id: ruleId,
|
|
290296
|
-
name: violation?.ruleName || ruleId,
|
|
290297
|
-
type: "import_forbidden",
|
|
290298
|
-
severity: violation?.severity || "warning",
|
|
290299
|
-
scope: violation?.sourceSymbol.filePath || "",
|
|
290300
|
-
description: violation?.message || "",
|
|
290301
|
-
violationCount: count
|
|
290302
|
-
};
|
|
290303
|
-
});
|
|
290304
|
-
}
|
|
290305
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
290306
|
-
// DNA SUMMARY
|
|
290307
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
290308
|
-
buildDNASummary() {
|
|
290309
|
-
return {
|
|
290310
|
-
conventions: this.dna.conventions.filter((c) => c.confidence > 0.5).map((c) => c.description),
|
|
290311
|
-
patterns: this.dna.patterns.map((p) => `${p.name}: ${p.description}`),
|
|
290312
|
-
boundaries: this.dna.boundaries.filter((b) => b.importCount > 3).map((b) => `${b.from} \u2192 ${b.to} (${b.importCount} imports${b.isCircular ? ", CIRCULAR" : ""})`),
|
|
290313
|
-
hotFiles: this.dna.hotspots.slice(0, 10).map((h) => h.file),
|
|
290314
|
-
riskAreas: this.dna.riskMap.filter((r) => r.riskLevel === "critical" || r.riskLevel === "high").map((r) => `${r.file}: ${r.factors[0]}`)
|
|
290315
|
-
};
|
|
290316
|
-
}
|
|
290317
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
290318
|
-
// FILE CONTEXTS
|
|
290319
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
290320
|
-
buildFileContexts() {
|
|
290321
|
-
const contexts = /* @__PURE__ */ new Map();
|
|
290322
|
-
for (const file of this.data.files) {
|
|
290323
|
-
contexts.set(file.relativePath, this.buildSingleFileContext(file));
|
|
290324
|
-
}
|
|
290325
|
-
return contexts;
|
|
290326
|
-
}
|
|
290327
|
-
buildSingleFileContext(file) {
|
|
290328
|
-
const rel = file.relativePath;
|
|
290329
|
-
const role = this.classifyRole(file);
|
|
290330
|
-
const graphNode = this.graph.nodes.find((n) => n.relativePath === rel);
|
|
290331
|
-
const layer = graphNode?.layer;
|
|
290332
|
-
const dependsOn = this.graph.edges.filter((e) => e.from === rel).map((e) => e.to);
|
|
290333
|
-
const dependedOnBy = this.graph.edges.filter((e) => e.to === rel).map((e) => e.from);
|
|
290334
|
-
const applicableRules = [];
|
|
290335
|
-
if (this.ruleResult) {
|
|
290336
|
-
for (const v of this.ruleResult.violations) {
|
|
290337
|
-
if (v.sourceSymbol.filePath.includes(rel) || v.targetSymbol && v.targetSymbol.filePath.includes(rel)) {
|
|
290338
|
-
if (!applicableRules.includes(v.ruleId)) applicableRules.push(v.ruleId);
|
|
290339
|
-
}
|
|
290340
|
-
}
|
|
290341
|
-
}
|
|
290342
|
-
const conventions = this.dna.conventions.filter((c) => this.conventionAppliesToFile(c.area, file)).map((c) => c.description);
|
|
290343
|
-
const patterns = this.dna.patterns.filter((p) => p.fileMatches.some((m) => m === rel)).map((p) => p.name);
|
|
290344
|
-
const riskEntry = this.dna.riskMap.find((r) => r.file === rel);
|
|
290345
|
-
const riskLevel = riskEntry?.riskLevel || "low";
|
|
290346
|
-
const relatedFiles = this.findRelatedFiles(file, role).slice(0, 8);
|
|
290347
|
-
const editGuidance = this.generateEditGuidance(file, role, layer, dependedOnBy, conventions);
|
|
290348
|
-
return {
|
|
290349
|
-
filePath: file.path,
|
|
290350
|
-
role,
|
|
290351
|
-
layer,
|
|
290352
|
-
dependsOn,
|
|
290353
|
-
dependedOnBy,
|
|
290354
|
-
applicableRules,
|
|
290355
|
-
conventions,
|
|
290356
|
-
patterns,
|
|
290357
|
-
riskLevel,
|
|
290358
|
-
relatedFiles,
|
|
290359
|
-
editGuidance
|
|
290360
|
-
};
|
|
290361
|
-
}
|
|
290362
|
-
classifyRole(file) {
|
|
290363
|
-
const rel = file.relativePath.toLowerCase();
|
|
290364
|
-
if (rel.includes(".test.") || rel.includes(".spec.") || rel.includes("__tests__")) return "test";
|
|
290365
|
-
if (rel.includes("fixture") || rel.includes("mock")) return "fixture";
|
|
290366
|
-
if (rel.includes("migration")) return "migration";
|
|
290367
|
-
if (rel.match(/\.(css|scss|less|styl)$/)) return "style";
|
|
290368
|
-
if (rel.includes(".config.") || rel.includes("config/") || rel === "tsconfig.json") return "config";
|
|
290369
|
-
if (rel.includes("middleware")) return "middleware";
|
|
290370
|
-
if (rel.includes("/api/") || rel.includes("route")) return "route-handler";
|
|
290371
|
-
if (rel.includes("service") || rel.includes("Service")) return "service";
|
|
290372
|
-
if (rel.includes("repositor") || rel.includes("Repositor")) return "repository";
|
|
290373
|
-
if (rel.endsWith(".tsx") && !rel.includes("page.")) return "component";
|
|
290374
|
-
if (rel.includes("/types") || rel.endsWith(".d.ts")) return "type";
|
|
290375
|
-
if (rel.includes("util") || rel.includes("helper") || rel.includes("lib/")) return "util";
|
|
290376
|
-
if (rel.includes("script") || rel.includes("bin/")) return "script";
|
|
290377
|
-
if (rel.match(/^(src\/)?index\.|^(src\/)?main\.|^(src\/)?app\./)) return "entry";
|
|
290378
|
-
return "unknown";
|
|
290379
|
-
}
|
|
290380
|
-
conventionAppliesToFile(area, file) {
|
|
290381
|
-
switch (area) {
|
|
290382
|
-
case "naming":
|
|
290383
|
-
return true;
|
|
290384
|
-
case "imports":
|
|
290385
|
-
return file.relativePath.endsWith(".ts") || file.relativePath.endsWith(".tsx");
|
|
290386
|
-
case "exports":
|
|
290387
|
-
return file.exports.length > 0;
|
|
290388
|
-
case "testing":
|
|
290389
|
-
return file.relativePath.includes(".test.") || file.relativePath.includes(".spec.");
|
|
290390
|
-
case "error-handling":
|
|
290391
|
-
return !file.relativePath.includes(".test.");
|
|
290392
|
-
case "types":
|
|
290393
|
-
return file.relativePath.endsWith(".ts") || file.relativePath.endsWith(".tsx");
|
|
290394
|
-
default:
|
|
290395
|
-
return true;
|
|
290396
|
-
}
|
|
290397
|
-
}
|
|
290398
|
-
findRelatedFiles(file, role) {
|
|
290399
|
-
const related = [];
|
|
290400
|
-
const dir = path5.dirname(file.relativePath);
|
|
290401
|
-
for (const other of this.data.files) {
|
|
290402
|
-
if (other.path === file.path) continue;
|
|
290403
|
-
if (path5.dirname(other.relativePath) === dir) {
|
|
290404
|
-
related.push(other.relativePath);
|
|
290405
|
-
}
|
|
290406
|
-
}
|
|
290407
|
-
if (related.length < 5) {
|
|
290408
|
-
for (const other of this.data.files) {
|
|
290409
|
-
if (other.path === file.path) continue;
|
|
290410
|
-
if (related.includes(other.relativePath)) continue;
|
|
290411
|
-
if (this.classifyRole(other) === role) {
|
|
290412
|
-
related.push(other.relativePath);
|
|
290413
|
-
if (related.length >= 8) break;
|
|
290414
|
-
}
|
|
290415
|
-
}
|
|
290416
|
-
}
|
|
290417
|
-
return related;
|
|
290418
|
-
}
|
|
290419
|
-
generateEditGuidance(file, role, layer, dependedOnBy, conventions) {
|
|
290420
|
-
const guidance = [];
|
|
290421
|
-
if (dependedOnBy.length > 10) {
|
|
290422
|
-
guidance.push(`HIGH IMPACT: ${dependedOnBy.length} files depend on this. Changes have wide blast radius.`);
|
|
290423
|
-
}
|
|
290424
|
-
switch (role) {
|
|
290425
|
-
case "route-handler":
|
|
290426
|
-
guidance.push("Validate all inputs with schemas before processing.");
|
|
290427
|
-
guidance.push("Return consistent response shapes ({ success, data } or { success, error }).");
|
|
290428
|
-
guidance.push("Ensure authentication middleware is applied to protected endpoints.");
|
|
290429
|
-
break;
|
|
290430
|
-
case "service":
|
|
290431
|
-
guidance.push("Keep business logic here, not in controllers/routes.");
|
|
290432
|
-
guidance.push("Use dependency injection for testability.");
|
|
290433
|
-
if (layer) guidance.push(`This is in the ${layer} layer \u2014 only import from lower layers.`);
|
|
290434
|
-
break;
|
|
290435
|
-
case "repository":
|
|
290436
|
-
guidance.push("Only data access logic belongs here \u2014 no business rules.");
|
|
290437
|
-
guidance.push("Return domain objects, not raw database rows.");
|
|
290438
|
-
break;
|
|
290439
|
-
case "component":
|
|
290440
|
-
guidance.push("Keep components focused and composable.");
|
|
290441
|
-
guidance.push("Extract complex logic to custom hooks.");
|
|
290442
|
-
break;
|
|
290443
|
-
case "middleware":
|
|
290444
|
-
guidance.push("Middleware must call next() or return a response \u2014 never leave the request hanging.");
|
|
290445
|
-
guidance.push("Keep middleware focused on a single concern.");
|
|
290446
|
-
break;
|
|
290447
|
-
case "test":
|
|
290448
|
-
guidance.push("Follow Arrange-Act-Assert pattern.");
|
|
290449
|
-
guidance.push("Test edge cases and error conditions, not just happy path.");
|
|
290450
|
-
break;
|
|
290451
|
-
}
|
|
290452
|
-
for (const conv of conventions.slice(0, 3)) {
|
|
290453
|
-
guidance.push(`Convention: ${conv}`);
|
|
290454
|
-
}
|
|
290455
|
-
return guidance;
|
|
290456
|
-
}
|
|
290457
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
290458
|
-
// TASK PLAYBOOKS
|
|
290459
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
290460
|
-
buildTaskPlaybooks() {
|
|
290461
|
-
const fp = this.dna.fingerprint;
|
|
290462
|
-
const playbooks = [];
|
|
290463
|
-
playbooks.push({
|
|
290464
|
-
taskType: "Bug Fix",
|
|
290465
|
-
steps: [
|
|
290466
|
-
"Reproduce the bug and understand the expected vs actual behavior",
|
|
290467
|
-
"Identify the root cause file(s) using the dependency graph",
|
|
290468
|
-
"Write a failing test that reproduces the bug",
|
|
290469
|
-
"Apply the minimal fix at the root cause",
|
|
290470
|
-
"Verify the fix passes the test and does not break existing tests",
|
|
290471
|
-
"Check that the fix does not violate any architecture rules"
|
|
290472
|
-
],
|
|
290473
|
-
mustRead: this.dna.hotspots.slice(0, 3).map((h) => h.file),
|
|
290474
|
-
mustUpdate: ["The buggy file", "Related test file"],
|
|
290475
|
-
mustVerify: ["All existing tests pass", "New regression test passes", "No new arch rule violations"],
|
|
290476
|
-
stopConditions: ["Never modify tests to make them pass \u2014 fix the code", "Do not change public API signatures without discussion"]
|
|
290477
|
-
});
|
|
290478
|
-
if (fp.router) {
|
|
290479
|
-
playbooks.push({
|
|
290480
|
-
taskType: "Add API Endpoint",
|
|
290481
|
-
steps: [
|
|
290482
|
-
"Check existing routes in truthpack to avoid duplicates",
|
|
290483
|
-
"Create the route handler following existing patterns",
|
|
290484
|
-
"Add input validation using the project validator",
|
|
290485
|
-
"Add authentication middleware if the route is protected",
|
|
290486
|
-
"Write tests for success, validation failure, and auth failure",
|
|
290487
|
-
"Update the truthpack (run vibecheck scan)"
|
|
290488
|
-
],
|
|
290489
|
-
mustRead: ["truthpack/routes.json", ...this.dna.patterns.filter((p) => p.category === "api").map((p) => p.exemplar)],
|
|
290490
|
-
mustUpdate: ["Route file", "Test file", "Truthpack"],
|
|
290491
|
-
mustVerify: ["Route responds correctly", "Input validation works", "Auth is enforced", "Test passes"],
|
|
290492
|
-
stopConditions: ["Do not create duplicate routes", "Do not hardcode mock data in handlers"]
|
|
290493
|
-
});
|
|
290494
|
-
}
|
|
290495
|
-
if (fp.framework.includes("Next") || fp.framework.includes("React")) {
|
|
290496
|
-
playbooks.push({
|
|
290497
|
-
taskType: "Add UI Component",
|
|
290498
|
-
steps: [
|
|
290499
|
-
"Check if a similar component already exists",
|
|
290500
|
-
"Create the component following existing naming and structure patterns",
|
|
290501
|
-
"Add TypeScript props interface",
|
|
290502
|
-
"Add unit test for the component",
|
|
290503
|
-
"If using state, determine if it should be a client component"
|
|
290504
|
-
],
|
|
290505
|
-
mustRead: this.dna.patterns.filter((p) => p.category === "ui" || p.category === "state").map((p) => p.exemplar),
|
|
290506
|
-
mustUpdate: ["Component file", "Test file", "Parent component that uses it"],
|
|
290507
|
-
mustVerify: ["Component renders correctly", "Props are typed", "Test passes"],
|
|
290508
|
-
stopConditions: ['Do not use "any" type for props', 'Do not add useState in server components without "use client"']
|
|
290509
|
-
});
|
|
290510
|
-
}
|
|
290511
|
-
playbooks.push({
|
|
290512
|
-
taskType: "Refactor",
|
|
290513
|
-
steps: [
|
|
290514
|
-
"Identify all callers/dependents of the code being refactored",
|
|
290515
|
-
"Ensure comprehensive tests exist before refactoring",
|
|
290516
|
-
"Apply changes incrementally, testing after each step",
|
|
290517
|
-
"Update all dependents to use the new API",
|
|
290518
|
-
"Remove old code only after all dependents are migrated",
|
|
290519
|
-
"Verify no architecture rules are violated"
|
|
290520
|
-
],
|
|
290521
|
-
mustRead: ["Dependency graph for affected files"],
|
|
290522
|
-
mustUpdate: ["Refactored file", "All dependent files", "Tests"],
|
|
290523
|
-
mustVerify: ["All tests pass", "No new violations", "No regressions"],
|
|
290524
|
-
stopConditions: ["Never break existing public APIs without migration path", "Do not refactor and add features in the same change"]
|
|
290525
|
-
});
|
|
290526
|
-
return playbooks;
|
|
290527
|
-
}
|
|
290528
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
290529
|
-
// VERIFICATION STEPS
|
|
290530
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
290531
|
-
buildVerificationSteps() {
|
|
290532
|
-
const fp = this.dna.fingerprint;
|
|
290533
|
-
const steps = [];
|
|
290534
|
-
if (fp.language === "TypeScript") {
|
|
290535
|
-
steps.push({
|
|
290536
|
-
trigger: "Any TypeScript file change",
|
|
290537
|
-
checks: ["TypeScript compilation succeeds", "No new type errors introduced"],
|
|
290538
|
-
commands: [fp.packageManager === "pnpm" ? "pnpm run check-types" : "npm run check-types"],
|
|
290539
|
-
artifacts: []
|
|
290540
|
-
});
|
|
290541
|
-
}
|
|
290542
|
-
if (fp.testRunner) {
|
|
290543
|
-
steps.push({
|
|
290544
|
-
trigger: "Any source file change",
|
|
290545
|
-
checks: ["Related tests pass", "No test regressions"],
|
|
290546
|
-
commands: [`${fp.packageManager} run test`],
|
|
290547
|
-
artifacts: ["test-results.json"]
|
|
290548
|
-
});
|
|
290549
|
-
}
|
|
290550
|
-
if (fp.router) {
|
|
290551
|
-
steps.push({
|
|
290552
|
-
trigger: "Route handler added or modified",
|
|
290553
|
-
checks: ["Route responds with correct status", "Auth middleware is applied", "Input validation works"],
|
|
290554
|
-
commands: ["vibecheck scan"],
|
|
290555
|
-
artifacts: ["truthpack/routes.json"]
|
|
290556
|
-
});
|
|
290557
|
-
}
|
|
290558
|
-
steps.push({
|
|
290559
|
-
trigger: "Any source file change",
|
|
290560
|
-
checks: ["No new architecture rule violations", "No new circular dependencies"],
|
|
290561
|
-
commands: ["vibecheck arch-rules"],
|
|
290562
|
-
artifacts: []
|
|
290563
|
-
});
|
|
290564
|
-
return steps;
|
|
290565
|
-
}
|
|
290566
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
290567
|
-
// RISK BRIEFING
|
|
290568
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
290569
|
-
buildRiskBriefing() {
|
|
290570
|
-
const criticalFiles = this.dna.hotspots.filter((h) => h.score > 30).slice(0, 10).map((h) => h.file);
|
|
290571
|
-
const recentViolations = this.ruleResult?.violations.filter((v) => v.severity === "error").slice(0, 10) || [];
|
|
290572
|
-
const securityConcerns = [];
|
|
290573
|
-
for (const risk of this.dna.riskMap) {
|
|
290574
|
-
if (risk.riskLevel === "critical") {
|
|
290575
|
-
securityConcerns.push(`${risk.file}: ${risk.factors.join(", ")}`);
|
|
290576
|
-
}
|
|
290577
|
-
}
|
|
290578
|
-
const testGaps = [];
|
|
290579
|
-
const sourceFiles = this.data.files.filter(
|
|
290580
|
-
(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")
|
|
290581
|
-
);
|
|
290582
|
-
const testFiles = this.data.files.filter(
|
|
290583
|
-
(f) => f.relativePath.includes(".test.") || f.relativePath.includes(".spec.")
|
|
290584
|
-
);
|
|
290585
|
-
const testedBases = new Set(testFiles.map(
|
|
290586
|
-
(f) => path5.basename(f.relativePath).replace(/\.(test|spec)\.(ts|tsx|js|jsx)$/, "")
|
|
290587
|
-
));
|
|
290588
|
-
for (const file of sourceFiles) {
|
|
290589
|
-
const baseName = path5.basename(file.relativePath).replace(/\.(ts|tsx|js|jsx)$/, "");
|
|
290590
|
-
if (!testedBases.has(baseName) && file.exports.length > 0) {
|
|
290591
|
-
testGaps.push(`${file.relativePath} has exports but no test file`);
|
|
290592
|
-
}
|
|
290593
|
-
}
|
|
290594
|
-
const driftWarnings = [];
|
|
290595
|
-
for (const cycle of this.graph.cycles) {
|
|
290596
|
-
driftWarnings.push(`Circular dependency: ${cycle.nodes.slice(0, 3).join(" \u2192 ")}${cycle.nodes.length > 3 ? "..." : ""}`);
|
|
290597
|
-
}
|
|
290598
|
-
return {
|
|
290599
|
-
criticalFiles,
|
|
290600
|
-
recentViolations,
|
|
290601
|
-
securityConcerns: securityConcerns.slice(0, 5),
|
|
290602
|
-
testGaps: testGaps.slice(0, 10),
|
|
290603
|
-
driftWarnings: driftWarnings.slice(0, 5)
|
|
290604
|
-
};
|
|
290605
|
-
}
|
|
290606
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
290607
|
-
// HELPERS
|
|
290608
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
290609
|
-
toRelative(filePath) {
|
|
290610
|
-
if (filePath.startsWith(this.rootPath)) {
|
|
290611
|
-
return filePath.slice(this.rootPath.length + 1).replace(/\\/g, "/");
|
|
290612
|
-
}
|
|
290613
|
-
return filePath.replace(/\\/g, "/");
|
|
290614
|
-
}
|
|
290615
|
-
};
|
|
290616
|
-
var ContextExplainer = class {
|
|
290617
|
-
/**
|
|
290618
|
-
* Generate a rich explanation for a file.
|
|
290619
|
-
*/
|
|
290620
|
-
explain(filePath, ctx) {
|
|
290621
|
-
const fc = ctx.fileContext;
|
|
290622
|
-
const paragraphs = [];
|
|
290623
|
-
const quickFacts = [];
|
|
290624
|
-
const warnings = [];
|
|
290625
|
-
const relatedFiles = [];
|
|
290626
|
-
const overlays = [];
|
|
290627
|
-
const role = fc?.role ?? "unknown";
|
|
290628
|
-
const roleSummary = this.buildRoleSummary(filePath, role, ctx);
|
|
290629
|
-
if (fc) {
|
|
290630
|
-
const depCount = fc.dependedOnBy.length;
|
|
290631
|
-
const depsOnCount = fc.dependsOn.length;
|
|
290632
|
-
if (depCount > 0 || depsOnCount > 0) {
|
|
290633
|
-
const parts2 = [];
|
|
290634
|
-
if (depCount > 0) {
|
|
290635
|
-
const critical = depCount > 10 ? " \u2014 changes here have wide blast radius" : "";
|
|
290636
|
-
parts2.push(`${depCount} file${depCount > 1 ? "s" : ""} depend on this${critical}`);
|
|
290637
|
-
}
|
|
290638
|
-
if (depsOnCount > 0) {
|
|
290639
|
-
parts2.push(`it imports from ${depsOnCount} file${depsOnCount > 1 ? "s" : ""}`);
|
|
290640
|
-
}
|
|
290641
|
-
paragraphs.push({
|
|
290642
|
-
heading: "Dependencies",
|
|
290643
|
-
text: parts2.join(". ") + ".",
|
|
290644
|
-
importance: depCount > 10 ? "critical" : depCount > 5 ? "high" : "medium"
|
|
290645
|
-
});
|
|
290646
|
-
}
|
|
290647
|
-
quickFacts.push({ label: "Role", value: role, icon: "layer" });
|
|
290648
|
-
if (fc.layer) quickFacts.push({ label: "Layer", value: fc.layer, icon: "layer" });
|
|
290649
|
-
quickFacts.push({ label: "Dependents", value: String(depCount), icon: "dependency" });
|
|
290650
|
-
quickFacts.push({ label: "Dependencies", value: String(depsOnCount), icon: "dependency" });
|
|
290651
|
-
if (fc.riskLevel === "critical" || fc.riskLevel === "high") {
|
|
290652
|
-
quickFacts.push({ label: "Risk", value: fc.riskLevel.toUpperCase(), icon: "warning" });
|
|
290653
|
-
warnings.push({
|
|
290654
|
-
message: `This file is classified as ${fc.riskLevel} risk`,
|
|
290655
|
-
severity: fc.riskLevel === "critical" ? "error" : "warning",
|
|
290656
|
-
action: "Add comprehensive tests and review carefully before merging changes"
|
|
290657
|
-
});
|
|
290658
|
-
}
|
|
290659
|
-
for (const dep of fc.dependedOnBy.slice(0, 5)) {
|
|
290660
|
-
relatedFiles.push({
|
|
290661
|
-
filePath: dep,
|
|
290662
|
-
reason: `Imports from this file`,
|
|
290663
|
-
relationship: "depended-by",
|
|
290664
|
-
confidence: 0.9
|
|
290665
|
-
});
|
|
290666
|
-
}
|
|
290667
|
-
overlays.push({
|
|
290668
|
-
type: "code-lens",
|
|
290669
|
-
line: 1,
|
|
290670
|
-
text: `${depCount} dependent${depCount !== 1 ? "s" : ""} \xB7 ${depsOnCount} import${depsOnCount !== 1 ? "s" : ""} \xB7 ${role}`,
|
|
290671
|
-
tooltip: `This ${role} file has ${depCount} files that depend on it and imports from ${depsOnCount} files`
|
|
290672
|
-
});
|
|
290673
|
-
}
|
|
290674
|
-
if (fc && fc.conventions.length > 0) {
|
|
290675
|
-
paragraphs.push({
|
|
290676
|
-
heading: "Conventions",
|
|
290677
|
-
text: `Follow these discovered conventions: ${fc.conventions.slice(0, 3).join("; ")}.`,
|
|
290678
|
-
importance: "medium"
|
|
290679
|
-
});
|
|
290680
|
-
}
|
|
290681
|
-
if (ctx.rules) {
|
|
290682
|
-
const fileViolations = ctx.rules.violations.filter(
|
|
290683
|
-
(v) => v.sourceSymbol.filePath.includes(shortName(filePath)) || v.targetSymbol && v.targetSymbol.filePath.includes(shortName(filePath))
|
|
290684
|
-
);
|
|
290685
|
-
if (fileViolations.length > 0) {
|
|
290686
|
-
const errors = fileViolations.filter((v) => v.severity === "error");
|
|
290687
|
-
const warns = fileViolations.filter((v) => v.severity === "warning");
|
|
290688
|
-
paragraphs.push({
|
|
290689
|
-
heading: "Architecture Violations",
|
|
290690
|
-
text: `${errors.length} error${errors.length !== 1 ? "s" : ""} and ${warns.length} warning${warns.length !== 1 ? "s" : ""} from architecture rules.`,
|
|
290691
|
-
importance: errors.length > 0 ? "critical" : "high"
|
|
290692
|
-
});
|
|
290693
|
-
for (const v of fileViolations.slice(0, 5)) {
|
|
290694
|
-
warnings.push({
|
|
290695
|
-
message: v.message,
|
|
290696
|
-
severity: v.severity === "error" ? "error" : "warning",
|
|
290697
|
-
action: v.suggestedFix ?? "Review and fix the violation",
|
|
290698
|
-
ruleId: v.ruleId
|
|
290699
|
-
});
|
|
290700
|
-
overlays.push({
|
|
290701
|
-
type: "diagnostic",
|
|
290702
|
-
line: v.sourceSymbol.line,
|
|
290703
|
-
text: v.message,
|
|
290704
|
-
severity: v.severity === "error" ? "error" : "warning",
|
|
290705
|
-
tooltip: v.suggestedFix
|
|
290706
|
-
});
|
|
290707
|
-
}
|
|
290708
|
-
}
|
|
290709
|
-
}
|
|
290710
|
-
if (ctx.callGraph) {
|
|
290711
|
-
const fileNodes = ctx.callGraph.nodes.filter((n) => n.filePath.includes(shortName(filePath)));
|
|
290712
|
-
const hotFunctions = fileNodes.filter((n) => n.callerCount > 5);
|
|
290713
|
-
if (hotFunctions.length > 0) {
|
|
290714
|
-
paragraphs.push({
|
|
290715
|
-
heading: "Hot Functions",
|
|
290716
|
-
text: hotFunctions.map(
|
|
290717
|
-
(f) => `\`${f.name}\` is called by ${f.callerCount} function${f.callerCount !== 1 ? "s" : ""}${f.calleeCount > 0 ? ` and calls ${f.calleeCount}` : ""}`
|
|
290718
|
-
).join(". ") + ".",
|
|
290719
|
-
importance: "high"
|
|
290720
|
-
});
|
|
290721
|
-
for (const fn of hotFunctions) {
|
|
290722
|
-
overlays.push({
|
|
290723
|
-
type: "code-lens",
|
|
290724
|
-
line: 0,
|
|
290725
|
-
// Would need symbol line mapping
|
|
290726
|
-
text: `${fn.callerCount} callers \xB7 ${fn.calleeCount} callees`,
|
|
290727
|
-
tooltip: `Function ${fn.name} has ${fn.callerCount} callers and ${fn.calleeCount} callees`
|
|
290728
|
-
});
|
|
290729
|
-
}
|
|
290730
|
-
}
|
|
290731
|
-
const deadInFile = ctx.callGraph.stats.deadFunctions.filter(
|
|
290732
|
-
(d) => d.filePath.includes(shortName(filePath))
|
|
290733
|
-
);
|
|
290734
|
-
if (deadInFile.length > 0) {
|
|
290735
|
-
for (const dead of deadInFile) {
|
|
290736
|
-
warnings.push({
|
|
290737
|
-
message: `\`${dead.name}\` appears to be dead code (exported but never called)`,
|
|
290738
|
-
severity: "info",
|
|
290739
|
-
action: "Verify this function is not called via dynamic dispatch or external consumers, then consider removing it"
|
|
290740
|
-
});
|
|
290741
|
-
}
|
|
290742
|
-
}
|
|
290743
|
-
}
|
|
290744
|
-
if (ctx.temporal) {
|
|
290745
|
-
const hotspot = ctx.temporal.changeHotspots.find((h) => filePath.includes(h.file) || h.file.includes(shortName(filePath)));
|
|
290746
|
-
if (hotspot) {
|
|
290747
|
-
const daysSince = Math.round((Date.now() - new Date(hotspot.lastChanged).getTime()) / 864e5);
|
|
290748
|
-
paragraphs.push({
|
|
290749
|
-
heading: "Recent Activity",
|
|
290750
|
-
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.`,
|
|
290751
|
-
importance: hotspot.commits > 10 ? "high" : "medium"
|
|
290752
|
-
});
|
|
290753
|
-
quickFacts.push({ label: "Recent commits", value: String(hotspot.commits), icon: "git" });
|
|
290754
|
-
quickFacts.push({ label: "Last changed", value: `${daysSince}d ago`, icon: "git" });
|
|
290755
|
-
quickFacts.push({ label: "Authors", value: String(hotspot.authors), icon: "git" });
|
|
290756
|
-
}
|
|
290757
|
-
const churn = ctx.temporal.churnFiles.find((c) => filePath.includes(c.file) || c.file.includes(shortName(filePath)));
|
|
290758
|
-
if (churn && churn.severity !== "low") {
|
|
290759
|
-
warnings.push({
|
|
290760
|
-
message: churn.reason,
|
|
290761
|
-
severity: churn.severity === "high" ? "warning" : "info",
|
|
290762
|
-
action: "Consider whether this file needs refactoring to reduce change frequency"
|
|
290763
|
-
});
|
|
290764
|
-
}
|
|
290765
|
-
const expertise = ctx.temporal.authorExpertise.find(
|
|
290766
|
-
(e) => filePath.startsWith(e.area) || filePath.includes(e.area)
|
|
290767
|
-
);
|
|
290768
|
-
if (expertise && expertise.busFactor === 1) {
|
|
290769
|
-
warnings.push({
|
|
290770
|
-
message: `Bus factor of 1 \u2014 ${expertise.primaryAuthor} has made ${expertise.authors[0]?.percentage}% of changes to this area`,
|
|
290771
|
-
severity: "info",
|
|
290772
|
-
action: "Consider knowledge sharing or pair programming for this area"
|
|
290773
|
-
});
|
|
290774
|
-
}
|
|
290775
|
-
}
|
|
290776
|
-
if (ctx.learned) {
|
|
290777
|
-
const coEdits = ctx.learned.coEdits.filter((p) => p.files[0].includes(shortName(filePath)) || p.files[1].includes(shortName(filePath))).slice(0, 3);
|
|
290778
|
-
if (coEdits.length > 0) {
|
|
290779
|
-
for (const pair of coEdits) {
|
|
290780
|
-
const other = pair.files[0].includes(shortName(filePath)) ? pair.files[1] : pair.files[0];
|
|
290781
|
-
relatedFiles.push({
|
|
290782
|
-
filePath: other,
|
|
290783
|
-
reason: `Often edited together (${pair.count} times)`,
|
|
290784
|
-
relationship: "co-edited",
|
|
290785
|
-
confidence: pair.weight
|
|
290786
|
-
});
|
|
290787
|
-
}
|
|
290788
|
-
}
|
|
290789
|
-
}
|
|
290790
|
-
if (fc && fc.editGuidance.length > 0) {
|
|
290791
|
-
paragraphs.push({
|
|
290792
|
-
heading: "Edit Guidance",
|
|
290793
|
-
text: fc.editGuidance.join(" "),
|
|
290794
|
-
importance: "medium"
|
|
290795
|
-
});
|
|
290796
|
-
}
|
|
290797
|
-
return {
|
|
290798
|
-
filePath,
|
|
290799
|
-
roleSummary,
|
|
290800
|
-
paragraphs,
|
|
290801
|
-
quickFacts,
|
|
290802
|
-
warnings,
|
|
290803
|
-
relatedFiles,
|
|
290804
|
-
overlays
|
|
290805
|
-
};
|
|
290806
|
-
}
|
|
290807
|
-
/**
|
|
290808
|
-
* Generate a compact markdown explanation for AI agent consumption.
|
|
290809
|
-
*/
|
|
290810
|
-
explainForAgent(filePath, ctx) {
|
|
290811
|
-
const explanation = this.explain(filePath, ctx);
|
|
290812
|
-
const lines = [];
|
|
290813
|
-
lines.push(`## ${shortName(filePath)}: ${explanation.roleSummary}`);
|
|
290814
|
-
lines.push("");
|
|
290815
|
-
if (explanation.quickFacts.length > 0) {
|
|
290816
|
-
lines.push(explanation.quickFacts.map((f) => `**${f.label}**: ${f.value}`).join(" \xB7 "));
|
|
290817
|
-
lines.push("");
|
|
290818
|
-
}
|
|
290819
|
-
for (const p of explanation.paragraphs.filter((p2) => p2.importance === "critical" || p2.importance === "high")) {
|
|
290820
|
-
lines.push(`### ${p.heading}`);
|
|
290821
|
-
lines.push(p.text);
|
|
290822
|
-
lines.push("");
|
|
290823
|
-
}
|
|
290824
|
-
if (explanation.warnings.length > 0) {
|
|
290825
|
-
lines.push("### Warnings");
|
|
290826
|
-
for (const w of explanation.warnings) {
|
|
290827
|
-
const icon = w.severity === "error" ? "MUST FIX" : w.severity === "warning" ? "SHOULD FIX" : "NOTE";
|
|
290828
|
-
lines.push(`- **[${icon}]** ${w.message} \u2014 ${w.action}`);
|
|
290829
|
-
}
|
|
290830
|
-
lines.push("");
|
|
290831
|
-
}
|
|
290832
|
-
if (explanation.relatedFiles.length > 0) {
|
|
290833
|
-
lines.push("### Related Files");
|
|
290834
|
-
for (const rf of explanation.relatedFiles.slice(0, 5)) {
|
|
290835
|
-
lines.push(`- \`${rf.filePath}\` \u2014 ${rf.reason}`);
|
|
290836
|
-
}
|
|
290837
|
-
lines.push("");
|
|
290838
|
-
}
|
|
290839
|
-
return lines.join("\n");
|
|
290840
|
-
}
|
|
290841
|
-
/**
|
|
290842
|
-
* Generate IDE overlay data for the VS Code extension to consume.
|
|
290843
|
-
*/
|
|
290844
|
-
getIDEOverlays(filePath, ctx) {
|
|
290845
|
-
const explanation = this.explain(filePath, ctx);
|
|
290846
|
-
return explanation.overlays;
|
|
290847
|
-
}
|
|
290848
|
-
/**
|
|
290849
|
-
* Generate a health report explanation.
|
|
290850
|
-
*/
|
|
290851
|
-
explainHealth(health, dna) {
|
|
290852
|
-
const lines = [];
|
|
290853
|
-
const dims = health.dimensions;
|
|
290854
|
-
lines.push(`## Codebase Health: ${health.overall}/100`);
|
|
290855
|
-
lines.push("");
|
|
290856
|
-
const entries = [
|
|
290857
|
-
{ name: "Architecture", score: dims.architecture, explain: this.explainArchScore(dims.architecture, dna) },
|
|
290858
|
-
{ name: "Test Coverage", score: dims.testCoverage, explain: this.explainTestScore(dims.testCoverage) },
|
|
290859
|
-
{ name: "Conventions", score: dims.conventions, explain: this.explainConventionScore(dims.conventions, dna) },
|
|
290860
|
-
{ name: "Dependencies", score: dims.dependencies, explain: this.explainDependencyScore(dims.dependencies, dna) },
|
|
290861
|
-
{ name: "Security", score: dims.security, explain: dims.security >= 80 ? "No critical security concerns detected" : "Critical risk areas identified" },
|
|
290862
|
-
{ name: "Complexity", score: dims.complexity, explain: dims.complexity >= 80 ? "Complexity is well-managed" : "High-complexity hotspots detected" }
|
|
290863
|
-
];
|
|
290864
|
-
for (const entry of entries) {
|
|
290865
|
-
const bar = this.renderBar(entry.score);
|
|
290866
|
-
lines.push(`${bar} **${entry.name}**: ${entry.score}/100 \u2014 ${entry.explain}`);
|
|
290867
|
-
}
|
|
290868
|
-
return lines.join("\n");
|
|
290869
|
-
}
|
|
290870
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
290871
|
-
// PRIVATE
|
|
290872
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
290873
|
-
buildRoleSummary(filePath, role, ctx) {
|
|
290874
|
-
const parts2 = [];
|
|
290875
|
-
switch (role) {
|
|
290876
|
-
case "service":
|
|
290877
|
-
parts2.push("Business logic service");
|
|
290878
|
-
break;
|
|
290879
|
-
case "route-handler":
|
|
290880
|
-
parts2.push("API route handler");
|
|
290881
|
-
break;
|
|
290882
|
-
case "component":
|
|
290883
|
-
parts2.push("UI component");
|
|
290884
|
-
break;
|
|
290885
|
-
case "repository":
|
|
290886
|
-
parts2.push("Data access layer");
|
|
290887
|
-
break;
|
|
290888
|
-
case "middleware":
|
|
290889
|
-
parts2.push("Request middleware");
|
|
290890
|
-
break;
|
|
290891
|
-
case "test":
|
|
290892
|
-
parts2.push("Test file");
|
|
290893
|
-
break;
|
|
290894
|
-
case "config":
|
|
290895
|
-
parts2.push("Configuration");
|
|
290896
|
-
break;
|
|
290897
|
-
case "type":
|
|
290898
|
-
parts2.push("Type definitions");
|
|
290899
|
-
break;
|
|
290900
|
-
case "util":
|
|
290901
|
-
parts2.push("Utility module");
|
|
290902
|
-
break;
|
|
290903
|
-
case "entry":
|
|
290904
|
-
parts2.push("Entry point");
|
|
290905
|
-
break;
|
|
290906
|
-
default:
|
|
290907
|
-
parts2.push("Source file");
|
|
290908
|
-
}
|
|
290909
|
-
if (ctx.fileContext) {
|
|
290910
|
-
if (ctx.fileContext.layer) parts2.push(`in ${ctx.fileContext.layer} layer`);
|
|
290911
|
-
if (ctx.fileContext.dependedOnBy.length > 10) parts2.push("(high-impact)");
|
|
290912
|
-
}
|
|
290913
|
-
return parts2.join(" ");
|
|
290914
|
-
}
|
|
290915
|
-
explainArchScore(score, dna) {
|
|
290916
|
-
if (score >= 80) return `Strong architecture with ${dna.patterns.length} recognized patterns`;
|
|
290917
|
-
if (score >= 50) return `Moderate architecture \u2014 ${dna.patterns.length} patterns detected, room to strengthen boundaries`;
|
|
290918
|
-
return "Architecture needs attention \u2014 few recognized patterns or boundaries";
|
|
290919
|
-
}
|
|
290920
|
-
explainTestScore(score) {
|
|
290921
|
-
if (score >= 80) return "Good test coverage across source files";
|
|
290922
|
-
if (score >= 50) return "Moderate coverage \u2014 some source files lack tests";
|
|
290923
|
-
return "Low test coverage \u2014 many exported modules have no test files";
|
|
290924
|
-
}
|
|
290925
|
-
explainConventionScore(score, dna) {
|
|
290926
|
-
const strong = dna.conventions.filter((c) => c.confidence > 0.6).length;
|
|
290927
|
-
if (score >= 80) return `${strong} strong conventions enforced consistently`;
|
|
290928
|
-
if (score >= 50) return `${strong} conventions detected but inconsistently applied`;
|
|
290929
|
-
return "Few consistent conventions \u2014 codebase style varies across files";
|
|
290930
|
-
}
|
|
290931
|
-
explainDependencyScore(score, dna) {
|
|
290932
|
-
const circular = dna.boundaries.filter((b) => b.isCircular).length;
|
|
290933
|
-
if (score >= 80) return "Clean dependency graph with no circular dependencies";
|
|
290934
|
-
if (circular > 0) return `${circular} circular dependency${circular > 1 ? "ies" : "y"} detected \u2014 these increase coupling and make code harder to reason about`;
|
|
290935
|
-
return "Dependency health needs improvement";
|
|
290936
|
-
}
|
|
290937
|
-
renderBar(score) {
|
|
290938
|
-
const filled = Math.round(score / 10);
|
|
290939
|
-
return "\u2588".repeat(filled) + "\u2591".repeat(10 - filled);
|
|
290940
|
-
}
|
|
290941
|
-
};
|
|
290942
|
-
function shortName(filePath) {
|
|
290943
|
-
const parts2 = filePath.split("/");
|
|
290944
|
-
return parts2[parts2.length - 1] ?? filePath;
|
|
290945
|
-
}
|
|
290946
|
-
|
|
290947
290944
|
// ../context-engine/dist/index.js
|
|
290948
290945
|
import * as fs5 from "fs";
|
|
290949
290946
|
import * as path6 from "path";
|
|
@@ -293677,13 +293674,7 @@ var InputValidator = class {
|
|
|
293677
293674
|
errors.push("Null byte detected in path");
|
|
293678
293675
|
logger.warn("Null byte injection attempt blocked", { input });
|
|
293679
293676
|
}
|
|
293680
|
-
const suspiciousPatterns = [
|
|
293681
|
-
/\.\.\//,
|
|
293682
|
-
/\.\.\\/,
|
|
293683
|
-
/%2e%2e/i,
|
|
293684
|
-
/%252e/i,
|
|
293685
|
-
/\.\./
|
|
293686
|
-
];
|
|
293677
|
+
const suspiciousPatterns = [/\.\.\//, /\.\.\\/, /%2e%2e/i, /%252e/i, /\.\./];
|
|
293687
293678
|
for (const pattern of suspiciousPatterns) {
|
|
293688
293679
|
if (pattern.test(input)) {
|
|
293689
293680
|
errors.push(`Suspicious pattern detected: ${pattern}`);
|
|
@@ -293732,12 +293723,7 @@ var InputValidator = class {
|
|
|
293732
293723
|
break;
|
|
293733
293724
|
}
|
|
293734
293725
|
}
|
|
293735
|
-
const scriptPatterns = [
|
|
293736
|
-
/<script/i,
|
|
293737
|
-
/javascript:/i,
|
|
293738
|
-
/on\w+\s*=/i,
|
|
293739
|
-
/<iframe/i
|
|
293740
|
-
];
|
|
293726
|
+
const scriptPatterns = [/<script/i, /javascript:/i, /on\w+\s*=/i, /<iframe/i];
|
|
293741
293727
|
for (const pattern of scriptPatterns) {
|
|
293742
293728
|
if (pattern.test(input)) {
|
|
293743
293729
|
logger.warn("Potential script injection attempt blocked", { input });
|
|
@@ -293885,10 +293871,7 @@ var InputValidator = class {
|
|
|
293885
293871
|
}
|
|
293886
293872
|
}
|
|
293887
293873
|
if (args2.outcome !== void 0) {
|
|
293888
|
-
const outcomeResult = this.validateEnum(args2.outcome, [
|
|
293889
|
-
"helpful",
|
|
293890
|
-
"not_helpful"
|
|
293891
|
-
]);
|
|
293874
|
+
const outcomeResult = this.validateEnum(args2.outcome, ["helpful", "not_helpful"]);
|
|
293892
293875
|
if (!outcomeResult.valid) {
|
|
293893
293876
|
errors.push(...outcomeResult.errors.map((e) => `outcome: ${e}`));
|
|
293894
293877
|
}
|
|
@@ -293910,7 +293893,9 @@ var InputValidator = class {
|
|
|
293910
293893
|
}
|
|
293911
293894
|
}
|
|
293912
293895
|
if (args2.enginePreset !== void 0) {
|
|
293913
|
-
const presetResult = this.validateEnum(args2.enginePreset, [
|
|
293896
|
+
const presetResult = this.validateEnum(args2.enginePreset, [
|
|
293897
|
+
...VIBECHECK_SCAN_ENGINE_PRESETS
|
|
293898
|
+
]);
|
|
293914
293899
|
if (!presetResult.valid) {
|
|
293915
293900
|
errors.push(...presetResult.errors.map((e) => `enginePreset: ${e}`));
|
|
293916
293901
|
}
|
|
@@ -293928,6 +293913,21 @@ var InputValidator = class {
|
|
|
293928
293913
|
}
|
|
293929
293914
|
}
|
|
293930
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
|
+
}
|
|
293931
293931
|
}
|
|
293932
293932
|
if (errors.length > 0) {
|
|
293933
293933
|
logger.warn("Tool argument validation failed", {
|
|
@@ -294001,7 +294001,10 @@ var MCP_TOOLS = [
|
|
|
294001
294001
|
path: { type: "string", description: "Workspace root. Defaults to cwd." },
|
|
294002
294002
|
query: { type: "string", description: "Natural language query." },
|
|
294003
294003
|
limit: { type: "number", description: "Max results. Default 10." },
|
|
294004
|
-
useSemantic: {
|
|
294004
|
+
useSemantic: {
|
|
294005
|
+
type: "boolean",
|
|
294006
|
+
description: "Use semantic search (embeddings) when available. Slower but finds conceptually related code."
|
|
294007
|
+
}
|
|
294005
294008
|
},
|
|
294006
294009
|
required: ["query"]
|
|
294007
294010
|
}
|
|
@@ -294024,7 +294027,11 @@ var MCP_TOOLS = [
|
|
|
294024
294027
|
properties: {
|
|
294025
294028
|
path: { type: "string", description: "Workspace root. Defaults to cwd." },
|
|
294026
294029
|
file: { type: "string", description: "File path that was (or was not) helpful." },
|
|
294027
|
-
outcome: {
|
|
294030
|
+
outcome: {
|
|
294031
|
+
type: "string",
|
|
294032
|
+
enum: ["helpful", "not_helpful"],
|
|
294033
|
+
description: "User feedback."
|
|
294034
|
+
}
|
|
294028
294035
|
},
|
|
294029
294036
|
required: ["file", "outcome"]
|
|
294030
294037
|
}
|
|
@@ -294098,7 +294105,11 @@ var MCP_TOOLS = [
|
|
|
294098
294105
|
type: "object",
|
|
294099
294106
|
properties: {
|
|
294100
294107
|
path: { type: "string", description: "Destination path." },
|
|
294101
|
-
type: {
|
|
294108
|
+
type: {
|
|
294109
|
+
type: "string",
|
|
294110
|
+
enum: ["component", "api", "hook", "test"],
|
|
294111
|
+
description: "Type of code to forge."
|
|
294112
|
+
},
|
|
294102
294113
|
name: { type: "string", description: "Name of the component/api/hook." }
|
|
294103
294114
|
},
|
|
294104
294115
|
required: ["type", "name"]
|
|
@@ -294110,7 +294121,10 @@ var MCP_TOOLS = [
|
|
|
294110
294121
|
inputSchema: {
|
|
294111
294122
|
type: "object",
|
|
294112
294123
|
properties: {
|
|
294113
|
-
path: {
|
|
294124
|
+
path: {
|
|
294125
|
+
type: "string",
|
|
294126
|
+
description: "Workspace or project root path. Defaults to current directory."
|
|
294127
|
+
},
|
|
294114
294128
|
targetUrl: { type: "string", description: "Base URL to test." }
|
|
294115
294129
|
}
|
|
294116
294130
|
}
|
|
@@ -294121,7 +294135,10 @@ var MCP_TOOLS = [
|
|
|
294121
294135
|
inputSchema: {
|
|
294122
294136
|
type: "object",
|
|
294123
294137
|
properties: {
|
|
294124
|
-
path: {
|
|
294138
|
+
path: {
|
|
294139
|
+
type: "string",
|
|
294140
|
+
description: "Workspace or project root path. Defaults to current directory."
|
|
294141
|
+
}
|
|
294125
294142
|
}
|
|
294126
294143
|
}
|
|
294127
294144
|
},
|
|
@@ -294131,8 +294148,15 @@ var MCP_TOOLS = [
|
|
|
294131
294148
|
inputSchema: {
|
|
294132
294149
|
type: "object",
|
|
294133
294150
|
properties: {
|
|
294134
|
-
path: {
|
|
294135
|
-
|
|
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
|
+
}
|
|
294136
294160
|
},
|
|
294137
294161
|
required: ["mode"]
|
|
294138
294162
|
}
|
|
@@ -294149,13 +294173,30 @@ var MCP_TOOLS = [
|
|
|
294149
294173
|
required: ["action"]
|
|
294150
294174
|
}
|
|
294151
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
|
+
},
|
|
294152
294190
|
{
|
|
294153
294191
|
name: "vibecheck_docguard",
|
|
294154
294192
|
description: "Documentation quality analysis (orphaned, stale, or duplicate docs).",
|
|
294155
294193
|
inputSchema: {
|
|
294156
294194
|
type: "object",
|
|
294157
294195
|
properties: {
|
|
294158
|
-
path: {
|
|
294196
|
+
path: {
|
|
294197
|
+
type: "string",
|
|
294198
|
+
description: "Workspace or project root path. Defaults to current directory."
|
|
294199
|
+
}
|
|
294159
294200
|
}
|
|
294160
294201
|
}
|
|
294161
294202
|
},
|
|
@@ -294165,7 +294206,10 @@ var MCP_TOOLS = [
|
|
|
294165
294206
|
inputSchema: {
|
|
294166
294207
|
type: "object",
|
|
294167
294208
|
properties: {
|
|
294168
|
-
path: {
|
|
294209
|
+
path: {
|
|
294210
|
+
type: "string",
|
|
294211
|
+
description: "Workspace or project root path. Defaults to current directory."
|
|
294212
|
+
}
|
|
294169
294213
|
}
|
|
294170
294214
|
}
|
|
294171
294215
|
},
|
|
@@ -294175,7 +294219,10 @@ var MCP_TOOLS = [
|
|
|
294175
294219
|
inputSchema: {
|
|
294176
294220
|
type: "object",
|
|
294177
294221
|
properties: {
|
|
294178
|
-
path: {
|
|
294222
|
+
path: {
|
|
294223
|
+
type: "string",
|
|
294224
|
+
description: "Workspace or project root path. Defaults to current directory."
|
|
294225
|
+
}
|
|
294179
294226
|
}
|
|
294180
294227
|
}
|
|
294181
294228
|
},
|
|
@@ -294185,7 +294232,10 @@ var MCP_TOOLS = [
|
|
|
294185
294232
|
inputSchema: {
|
|
294186
294233
|
type: "object",
|
|
294187
294234
|
properties: {
|
|
294188
|
-
path: {
|
|
294235
|
+
path: {
|
|
294236
|
+
type: "string",
|
|
294237
|
+
description: "Workspace or project root path. Defaults to current directory."
|
|
294238
|
+
},
|
|
294189
294239
|
out: { type: "string", description: "Output path for the truthpack." }
|
|
294190
294240
|
}
|
|
294191
294241
|
}
|
|
@@ -294196,7 +294246,10 @@ var MCP_TOOLS = [
|
|
|
294196
294246
|
inputSchema: {
|
|
294197
294247
|
type: "object",
|
|
294198
294248
|
properties: {
|
|
294199
|
-
path: {
|
|
294249
|
+
path: {
|
|
294250
|
+
type: "string",
|
|
294251
|
+
description: "Workspace or project root path. Defaults to current directory."
|
|
294252
|
+
},
|
|
294200
294253
|
branch: { type: "string", description: "Branch to compare against." }
|
|
294201
294254
|
}
|
|
294202
294255
|
}
|
|
@@ -294207,7 +294260,10 @@ var MCP_TOOLS = [
|
|
|
294207
294260
|
inputSchema: {
|
|
294208
294261
|
type: "object",
|
|
294209
294262
|
properties: {
|
|
294210
|
-
path: {
|
|
294263
|
+
path: {
|
|
294264
|
+
type: "string",
|
|
294265
|
+
description: "Workspace or project root path. Defaults to current directory."
|
|
294266
|
+
}
|
|
294211
294267
|
}
|
|
294212
294268
|
}
|
|
294213
294269
|
},
|
|
@@ -294218,7 +294274,10 @@ var MCP_TOOLS = [
|
|
|
294218
294274
|
type: "object",
|
|
294219
294275
|
properties: {
|
|
294220
294276
|
intent: { type: "string", description: "Description of the feature to build." },
|
|
294221
|
-
path: {
|
|
294277
|
+
path: {
|
|
294278
|
+
type: "string",
|
|
294279
|
+
description: "Workspace or project root path. Defaults to current directory."
|
|
294280
|
+
}
|
|
294222
294281
|
},
|
|
294223
294282
|
required: ["intent"]
|
|
294224
294283
|
}
|
|
@@ -294229,7 +294288,10 @@ var MCP_TOOLS = [
|
|
|
294229
294288
|
inputSchema: {
|
|
294230
294289
|
type: "object",
|
|
294231
294290
|
properties: {
|
|
294232
|
-
path: {
|
|
294291
|
+
path: {
|
|
294292
|
+
type: "string",
|
|
294293
|
+
description: "Workspace or project root path. Defaults to current directory."
|
|
294294
|
+
}
|
|
294233
294295
|
}
|
|
294234
294296
|
}
|
|
294235
294297
|
},
|
|
@@ -294263,9 +294325,19 @@ var MCP_TOOLS = [
|
|
|
294263
294325
|
type: "object",
|
|
294264
294326
|
properties: {
|
|
294265
294327
|
path: { type: "string", description: "Project path. Defaults to current directory." },
|
|
294266
|
-
severity: {
|
|
294267
|
-
|
|
294268
|
-
|
|
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
|
+
}
|
|
294269
294341
|
}
|
|
294270
294342
|
}
|
|
294271
294343
|
},
|
|
@@ -294319,7 +294391,10 @@ var MCP_TOOLS = [
|
|
|
294319
294391
|
inputSchema: {
|
|
294320
294392
|
type: "object",
|
|
294321
294393
|
properties: {
|
|
294322
|
-
path: {
|
|
294394
|
+
path: {
|
|
294395
|
+
type: "string",
|
|
294396
|
+
description: "Workspace root path. Defaults to current directory."
|
|
294397
|
+
}
|
|
294323
294398
|
}
|
|
294324
294399
|
}
|
|
294325
294400
|
}
|
|
@@ -294470,7 +294545,7 @@ function createScanIdempotencyKey(prefix) {
|
|
|
294470
294545
|
// src/mcp-scan-meter-client.ts
|
|
294471
294546
|
var MCP_SCAN_METER_CLIENT = {
|
|
294472
294547
|
type: "mcp",
|
|
294473
|
-
version: "24.
|
|
294548
|
+
version: "24.6.3"
|
|
294474
294549
|
};
|
|
294475
294550
|
|
|
294476
294551
|
// src/server.ts
|
|
@@ -294626,10 +294701,7 @@ async function runGuard(targetPath) {
|
|
|
294626
294701
|
var MCP_TOOL_TIMEOUT_MS = 18e4;
|
|
294627
294702
|
function withTimeout(promise, ms, label) {
|
|
294628
294703
|
return new Promise((resolve6, reject) => {
|
|
294629
|
-
const timer = setTimeout(
|
|
294630
|
-
() => reject(new Error(`${label} timed out after ${ms / 1e3}s`)),
|
|
294631
|
-
ms
|
|
294632
|
-
);
|
|
294704
|
+
const timer = setTimeout(() => reject(new Error(`${label} timed out after ${ms / 1e3}s`)), ms);
|
|
294633
294705
|
promise.then(
|
|
294634
294706
|
(value) => {
|
|
294635
294707
|
clearTimeout(timer);
|
|
@@ -294758,10 +294830,8 @@ async function handleCallToolRequest(request, runtimeOverrides = {}) {
|
|
|
294758
294830
|
}
|
|
294759
294831
|
const validation = InputValidator.validateToolArgs(name2, args2);
|
|
294760
294832
|
if (!validation.valid) {
|
|
294761
|
-
return buildErrorResponse(
|
|
294762
|
-
|
|
294763
|
-
${validation.errors.join("\n")}`
|
|
294764
|
-
);
|
|
294833
|
+
return buildErrorResponse(`Invalid arguments for ${name2}:
|
|
294834
|
+
${validation.errors.join("\n")}`);
|
|
294765
294835
|
}
|
|
294766
294836
|
const BILLABLE_ENGINE_TOOLS = /* @__PURE__ */ new Set(["vibecheck_scan", "vibecheck_score"]);
|
|
294767
294837
|
if (BILLABLE_ENGINE_TOOLS.has(name2)) {
|
|
@@ -294804,9 +294874,14 @@ ${validation.errors.join("\n")}`
|
|
|
294804
294874
|
const recordMcpBillableUsage = async (toolKey) => {
|
|
294805
294875
|
const tok = process.env.VIBECHECK_TOKEN?.trim();
|
|
294806
294876
|
if (!tok || shouldSkipServerScanMetering()) return { ok: true };
|
|
294807
|
-
const r = await postServerScanRecord(
|
|
294808
|
-
|
|
294809
|
-
|
|
294877
|
+
const r = await postServerScanRecord(
|
|
294878
|
+
tok,
|
|
294879
|
+
MCP_SCAN_METER_CLIENT,
|
|
294880
|
+
createScanIdempotencyKey(toolKey),
|
|
294881
|
+
{
|
|
294882
|
+
scanType: toolKey.replace(/^mcp-/, "")
|
|
294883
|
+
}
|
|
294884
|
+
);
|
|
294810
294885
|
if (!r.ok) {
|
|
294811
294886
|
return { ok: false, message: r.error };
|
|
294812
294887
|
}
|
|
@@ -294828,9 +294903,18 @@ ${validation.errors.join("\n")}`
|
|
|
294828
294903
|
await engine.initialize(data);
|
|
294829
294904
|
return engine.getProactiveContext({ focusedFile });
|
|
294830
294905
|
};
|
|
294831
|
-
const context = await withTimeout(
|
|
294906
|
+
const context = await withTimeout(
|
|
294907
|
+
run2(),
|
|
294908
|
+
MCP_TOOL_TIMEOUT_MS,
|
|
294909
|
+
"vibecheck_context_proactive"
|
|
294910
|
+
);
|
|
294832
294911
|
return {
|
|
294833
|
-
content: [
|
|
294912
|
+
content: [
|
|
294913
|
+
{
|
|
294914
|
+
type: "text",
|
|
294915
|
+
text: typeof context === "string" ? context : JSON.stringify(context, null, 2)
|
|
294916
|
+
}
|
|
294917
|
+
]
|
|
294834
294918
|
};
|
|
294835
294919
|
}
|
|
294836
294920
|
case "vibecheck_context_intent": {
|
|
@@ -294877,12 +294961,16 @@ ${validation.errors.join("\n")}`
|
|
|
294877
294961
|
const file = args2?.file;
|
|
294878
294962
|
const outcome = args2?.outcome;
|
|
294879
294963
|
if (!file || !outcome) {
|
|
294880
|
-
return buildErrorResponse(
|
|
294964
|
+
return buildErrorResponse(
|
|
294965
|
+
'vibecheck_context_feedback requires "file" and "outcome" (helpful|not_helpful)'
|
|
294966
|
+
);
|
|
294881
294967
|
}
|
|
294882
294968
|
const feedbackFile = normalizeRelativeWorkspaceFilePath(file, "file");
|
|
294883
294969
|
await runtime.recordFeedback(targetPath, feedbackFile, outcome);
|
|
294884
294970
|
return {
|
|
294885
|
-
content: [
|
|
294971
|
+
content: [
|
|
294972
|
+
{ type: "text", text: JSON.stringify({ ok: true, file: feedbackFile, outcome }) }
|
|
294973
|
+
]
|
|
294886
294974
|
};
|
|
294887
294975
|
}
|
|
294888
294976
|
case "vibecheck_roast": {
|
|
@@ -295002,6 +295090,31 @@ ${validation.errors.join("\n")}`
|
|
|
295002
295090
|
]
|
|
295003
295091
|
};
|
|
295004
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
|
+
}
|
|
295005
295118
|
// ── Platform Unification Tool Handlers ──────────────────────────────
|
|
295006
295119
|
case "vibecheck_trust_score": {
|
|
295007
295120
|
const result = await withTimeout(
|
|
@@ -295040,10 +295153,7 @@ ${validation.errors.join("\n")}`
|
|
|
295040
295153
|
const fileFilter = args2.file;
|
|
295041
295154
|
filtered = filtered.filter((f) => f.file?.includes(fileFilter));
|
|
295042
295155
|
}
|
|
295043
|
-
const lines = [
|
|
295044
|
-
`## Findings (${filtered.length} of ${gatedReport.summary.total})`,
|
|
295045
|
-
""
|
|
295046
|
-
];
|
|
295156
|
+
const lines = [`## Findings (${filtered.length} of ${gatedReport.summary.total})`, ""];
|
|
295047
295157
|
for (const f of filtered.slice(0, 50)) {
|
|
295048
295158
|
const loc = f.file ? `${f.file}${f.line ? `:${f.line}` : ""}` : "unknown";
|
|
295049
295159
|
lines.push(`- **[${f.severity.toUpperCase()}]** ${f.message}`);
|
|
@@ -295095,16 +295205,22 @@ ${validation.errors.join("\n")}`
|
|
|
295095
295205
|
const token = process.env.VIBECHECK_TOKEN?.trim();
|
|
295096
295206
|
if (!token) {
|
|
295097
295207
|
return {
|
|
295098
|
-
content: [
|
|
295099
|
-
|
|
295100
|
-
|
|
295101
|
-
|
|
295102
|
-
|
|
295103
|
-
|
|
295104
|
-
|
|
295105
|
-
|
|
295106
|
-
|
|
295107
|
-
|
|
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
|
+
]
|
|
295108
295224
|
};
|
|
295109
295225
|
}
|
|
295110
295226
|
try {
|
|
@@ -295112,7 +295228,7 @@ ${validation.errors.join("\n")}`
|
|
|
295112
295228
|
const resp = await fetch(`${apiBase}/api/v1/findings/${findingId}`, {
|
|
295113
295229
|
method: "PATCH",
|
|
295114
295230
|
headers: {
|
|
295115
|
-
|
|
295231
|
+
Authorization: `Bearer ${token}`,
|
|
295116
295232
|
"Content-Type": "application/json"
|
|
295117
295233
|
},
|
|
295118
295234
|
body: JSON.stringify({ resolved: true })
|
|
@@ -295121,31 +295237,37 @@ ${validation.errors.join("\n")}`
|
|
|
295121
295237
|
return buildErrorResponse(`Failed to dismiss finding: HTTP ${resp.status}`);
|
|
295122
295238
|
}
|
|
295123
295239
|
return {
|
|
295124
|
-
content: [
|
|
295125
|
-
|
|
295126
|
-
|
|
295240
|
+
content: [
|
|
295241
|
+
{
|
|
295242
|
+
type: "text",
|
|
295243
|
+
text: `Finding \`${findingId}\` dismissed. Reason: ${reason}
|
|
295127
295244
|
This change is synced across all surfaces.`
|
|
295128
|
-
|
|
295245
|
+
}
|
|
295246
|
+
]
|
|
295129
295247
|
};
|
|
295130
295248
|
} catch (err2) {
|
|
295131
|
-
return buildErrorResponse(
|
|
295249
|
+
return buildErrorResponse(
|
|
295250
|
+
`Failed to dismiss finding: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
295251
|
+
);
|
|
295132
295252
|
}
|
|
295133
295253
|
}
|
|
295134
295254
|
case "vibecheck_history": {
|
|
295135
295255
|
const token = process.env.VIBECHECK_TOKEN?.trim();
|
|
295136
295256
|
if (!token) {
|
|
295137
295257
|
return {
|
|
295138
|
-
content: [
|
|
295139
|
-
|
|
295140
|
-
|
|
295141
|
-
|
|
295258
|
+
content: [
|
|
295259
|
+
{
|
|
295260
|
+
type: "text",
|
|
295261
|
+
text: "Scan history requires authentication. Set VIBECHECK_TOKEN or run `vibecheck auth login`."
|
|
295262
|
+
}
|
|
295263
|
+
]
|
|
295142
295264
|
};
|
|
295143
295265
|
}
|
|
295144
295266
|
try {
|
|
295145
295267
|
const apiBase = process.env.VIBECHECK_API_URL || "https://api.vibecheckai.dev";
|
|
295146
295268
|
const limit = args2.limit ?? 10;
|
|
295147
295269
|
const resp = await fetch(`${apiBase}/api/v1/scans/recent?limit=${limit}`, {
|
|
295148
|
-
headers: {
|
|
295270
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
295149
295271
|
});
|
|
295150
295272
|
if (!resp.ok) {
|
|
295151
295273
|
return buildErrorResponse(`Failed to fetch scan history: HTTP ${resp.status}`);
|
|
@@ -295153,7 +295275,14 @@ This change is synced across all surfaces.`
|
|
|
295153
295275
|
const body2 = await resp.json();
|
|
295154
295276
|
const scans = body2.data ?? [];
|
|
295155
295277
|
if (scans.length === 0) {
|
|
295156
|
-
return {
|
|
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
|
+
};
|
|
295157
295286
|
}
|
|
295158
295287
|
const lines = [`## Scan History (${scans.length} most recent)`, ""];
|
|
295159
295288
|
for (const s of scans) {
|
|
@@ -295164,7 +295293,9 @@ This change is synced across all surfaces.`
|
|
|
295164
295293
|
}
|
|
295165
295294
|
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
295166
295295
|
} catch (err2) {
|
|
295167
|
-
return buildErrorResponse(
|
|
295296
|
+
return buildErrorResponse(
|
|
295297
|
+
`Failed to fetch history: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
295298
|
+
);
|
|
295168
295299
|
}
|
|
295169
295300
|
}
|
|
295170
295301
|
case "vibecheck_engines": {
|
|
@@ -295247,7 +295378,9 @@ This change is synced across all surfaces.`
|
|
|
295247
295378
|
};
|
|
295248
295379
|
} catch (error) {
|
|
295249
295380
|
const err2 = error;
|
|
295250
|
-
return buildErrorResponse(
|
|
295381
|
+
return buildErrorResponse(
|
|
295382
|
+
`CLI Execution Error: ${err2.message ?? err2.stdout ?? err2.stderr ?? String(error)}`
|
|
295383
|
+
);
|
|
295251
295384
|
}
|
|
295252
295385
|
}
|
|
295253
295386
|
default:
|
|
@@ -295263,7 +295396,7 @@ function createMcpServer(runtimeOverrides = {}) {
|
|
|
295263
295396
|
const server2 = new Server(
|
|
295264
295397
|
{
|
|
295265
295398
|
name: "vibecheck-mcp",
|
|
295266
|
-
version: "24.
|
|
295399
|
+
version: "24.6.3"
|
|
295267
295400
|
},
|
|
295268
295401
|
{
|
|
295269
295402
|
capabilities: {
|