@getmikk/mcp-server 1.9.1 → 1.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +159 -64
- package/dist/index.cjs.map +3 -3
- package/package.json +7 -7
package/dist/index.cjs
CHANGED
|
@@ -15394,7 +15394,7 @@ ${lanes.join("\n")}
|
|
|
15394
15394
|
writeOutputIsTTY() {
|
|
15395
15395
|
return process.stdout.isTTY;
|
|
15396
15396
|
},
|
|
15397
|
-
readFile:
|
|
15397
|
+
readFile: readFile6,
|
|
15398
15398
|
writeFile: writeFile22,
|
|
15399
15399
|
watchFile: watchFile2,
|
|
15400
15400
|
watchDirectory,
|
|
@@ -15600,7 +15600,7 @@ ${lanes.join("\n")}
|
|
|
15600
15600
|
callback
|
|
15601
15601
|
);
|
|
15602
15602
|
}
|
|
15603
|
-
function
|
|
15603
|
+
function readFile6(fileName, _encoding) {
|
|
15604
15604
|
let buffer;
|
|
15605
15605
|
try {
|
|
15606
15606
|
buffer = _fs.readFileSync(fileName);
|
|
@@ -52040,7 +52040,7 @@ ${lanes.join("\n")}
|
|
|
52040
52040
|
const possibleOption = getSpellingSuggestion(unknownOption, diagnostics.optionDeclarations, getOptionName);
|
|
52041
52041
|
return possibleOption ? createDiagnosticForNodeInSourceFileOrCompilerDiagnostic(sourceFile, node, diagnostics.unknownDidYouMeanDiagnostic, unknownOptionErrorText || unknownOption, possibleOption.name) : createDiagnosticForNodeInSourceFileOrCompilerDiagnostic(sourceFile, node, diagnostics.unknownOptionDiagnostic, unknownOptionErrorText || unknownOption);
|
|
52042
52042
|
}
|
|
52043
|
-
function parseCommandLineWorker(diagnostics, commandLine,
|
|
52043
|
+
function parseCommandLineWorker(diagnostics, commandLine, readFile6) {
|
|
52044
52044
|
const options = {};
|
|
52045
52045
|
let watchOptions;
|
|
52046
52046
|
const fileNames = [];
|
|
@@ -52088,7 +52088,7 @@ ${lanes.join("\n")}
|
|
|
52088
52088
|
}
|
|
52089
52089
|
}
|
|
52090
52090
|
function parseResponseFile(fileName) {
|
|
52091
|
-
const text = tryReadFile(fileName,
|
|
52091
|
+
const text = tryReadFile(fileName, readFile6 || ((fileName2) => sys.readFile(fileName2)));
|
|
52092
52092
|
if (!isString(text)) {
|
|
52093
52093
|
errors.push(text);
|
|
52094
52094
|
return;
|
|
@@ -52190,8 +52190,8 @@ ${lanes.join("\n")}
|
|
|
52190
52190
|
unknownDidYouMeanDiagnostic: Diagnostics.Unknown_compiler_option_0_Did_you_mean_1,
|
|
52191
52191
|
optionTypeMismatchDiagnostic: Diagnostics.Compiler_option_0_expects_an_argument
|
|
52192
52192
|
};
|
|
52193
|
-
function parseCommandLine(commandLine,
|
|
52194
|
-
return parseCommandLineWorker(compilerOptionsDidYouMeanDiagnostics, commandLine,
|
|
52193
|
+
function parseCommandLine(commandLine, readFile6) {
|
|
52194
|
+
return parseCommandLineWorker(compilerOptionsDidYouMeanDiagnostics, commandLine, readFile6);
|
|
52195
52195
|
}
|
|
52196
52196
|
function getOptionFromName(optionName, allowShort) {
|
|
52197
52197
|
return getOptionDeclarationFromName(getOptionsNameMap, optionName, allowShort);
|
|
@@ -52273,8 +52273,8 @@ ${lanes.join("\n")}
|
|
|
52273
52273
|
watchOptionsToExtend
|
|
52274
52274
|
);
|
|
52275
52275
|
}
|
|
52276
|
-
function readConfigFile(fileName,
|
|
52277
|
-
const textOrDiagnostic = tryReadFile(fileName,
|
|
52276
|
+
function readConfigFile(fileName, readFile6) {
|
|
52277
|
+
const textOrDiagnostic = tryReadFile(fileName, readFile6);
|
|
52278
52278
|
return isString(textOrDiagnostic) ? parseConfigFileTextToJson(fileName, textOrDiagnostic) : { config: {}, error: textOrDiagnostic };
|
|
52279
52279
|
}
|
|
52280
52280
|
function parseConfigFileTextToJson(fileName, jsonText) {
|
|
@@ -52289,14 +52289,14 @@ ${lanes.join("\n")}
|
|
|
52289
52289
|
error: jsonSourceFile.parseDiagnostics.length ? jsonSourceFile.parseDiagnostics[0] : void 0
|
|
52290
52290
|
};
|
|
52291
52291
|
}
|
|
52292
|
-
function readJsonConfigFile(fileName,
|
|
52293
|
-
const textOrDiagnostic = tryReadFile(fileName,
|
|
52292
|
+
function readJsonConfigFile(fileName, readFile6) {
|
|
52293
|
+
const textOrDiagnostic = tryReadFile(fileName, readFile6);
|
|
52294
52294
|
return isString(textOrDiagnostic) ? parseJsonText(fileName, textOrDiagnostic) : { fileName, parseDiagnostics: [textOrDiagnostic] };
|
|
52295
52295
|
}
|
|
52296
|
-
function tryReadFile(fileName,
|
|
52296
|
+
function tryReadFile(fileName, readFile6) {
|
|
52297
52297
|
let text;
|
|
52298
52298
|
try {
|
|
52299
|
-
text =
|
|
52299
|
+
text = readFile6(fileName);
|
|
52300
52300
|
} catch (e) {
|
|
52301
52301
|
return createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, fileName, e.message);
|
|
52302
52302
|
}
|
|
@@ -141824,12 +141824,12 @@ ${lanes.join("\n")}
|
|
|
141824
141824
|
function createCompilerHost(options, setParentNodes) {
|
|
141825
141825
|
return createCompilerHostWorker(options, setParentNodes);
|
|
141826
141826
|
}
|
|
141827
|
-
function createGetSourceFile(
|
|
141827
|
+
function createGetSourceFile(readFile6, setParentNodes) {
|
|
141828
141828
|
return (fileName, languageVersionOrOptions, onError) => {
|
|
141829
141829
|
let text;
|
|
141830
141830
|
try {
|
|
141831
141831
|
mark("beforeIORead");
|
|
141832
|
-
text =
|
|
141832
|
+
text = readFile6(fileName);
|
|
141833
141833
|
mark("afterIORead");
|
|
141834
141834
|
measure("I/O Read", "beforeIORead", "afterIORead");
|
|
141835
141835
|
} catch (e) {
|
|
@@ -142733,7 +142733,7 @@ ${lanes.join("\n")}
|
|
|
142733
142733
|
getRedirectFromOutput,
|
|
142734
142734
|
forEachResolvedProjectReference: forEachResolvedProjectReference2
|
|
142735
142735
|
});
|
|
142736
|
-
const
|
|
142736
|
+
const readFile6 = host.readFile.bind(host);
|
|
142737
142737
|
(_e = tracing) == null ? void 0 : _e.push(tracing.Phase.Program, "shouldProgramCreateNewSourceFiles", { hasOldProgram: !!oldProgram });
|
|
142738
142738
|
const shouldCreateNewSourceFile = shouldProgramCreateNewSourceFiles(oldProgram, options);
|
|
142739
142739
|
(_f = tracing) == null ? void 0 : _f.pop();
|
|
@@ -142959,7 +142959,7 @@ ${lanes.join("\n")}
|
|
|
142959
142959
|
shouldTransformImportCall,
|
|
142960
142960
|
emitBuildInfo,
|
|
142961
142961
|
fileExists: fileExists2,
|
|
142962
|
-
readFile:
|
|
142962
|
+
readFile: readFile6,
|
|
142963
142963
|
directoryExists,
|
|
142964
142964
|
getSymlinkCache,
|
|
142965
142965
|
realpath: (_o = host.realpath) == null ? void 0 : _o.bind(host),
|
|
@@ -236933,7 +236933,8 @@ var ImpactAnalyzer = class {
|
|
|
236933
236933
|
analyze(changedNodeIds) {
|
|
236934
236934
|
const visited = /* @__PURE__ */ new Set();
|
|
236935
236935
|
const depthMap = /* @__PURE__ */ new Map();
|
|
236936
|
-
const
|
|
236936
|
+
const pathConfidence = /* @__PURE__ */ new Map();
|
|
236937
|
+
const queue = changedNodeIds.map((id) => ({ id, depth: 0, confidence: 1 }));
|
|
236937
236938
|
let maxDepth = 0;
|
|
236938
236939
|
const changedSet = new Set(changedNodeIds);
|
|
236939
236940
|
const changedModules = /* @__PURE__ */ new Set();
|
|
@@ -236943,16 +236944,19 @@ var ImpactAnalyzer = class {
|
|
|
236943
236944
|
changedModules.add(node.moduleId);
|
|
236944
236945
|
}
|
|
236945
236946
|
while (queue.length > 0) {
|
|
236946
|
-
const { id: current, depth } = queue.shift();
|
|
236947
|
+
const { id: current, depth, confidence: pathConf } = queue.shift();
|
|
236947
236948
|
if (visited.has(current))
|
|
236948
236949
|
continue;
|
|
236949
236950
|
visited.add(current);
|
|
236950
236951
|
depthMap.set(current, depth);
|
|
236952
|
+
pathConfidence.set(current, pathConf);
|
|
236951
236953
|
maxDepth = Math.max(maxDepth, depth);
|
|
236952
236954
|
const dependents = this.graph.inEdges.get(current) || [];
|
|
236953
236955
|
for (const edge of dependents) {
|
|
236954
236956
|
if (!visited.has(edge.source) && edge.type !== "contains") {
|
|
236955
|
-
|
|
236957
|
+
const edgeConf = edge.confidence ?? 1;
|
|
236958
|
+
const newPathConf = Math.min(pathConf, edgeConf);
|
|
236959
|
+
queue.push({ id: edge.source, depth: depth + 1, confidence: newPathConf });
|
|
236956
236960
|
}
|
|
236957
236961
|
}
|
|
236958
236962
|
}
|
|
@@ -236984,19 +236988,33 @@ var ImpactAnalyzer = class {
|
|
|
236984
236988
|
changed: changedNodeIds,
|
|
236985
236989
|
impacted,
|
|
236986
236990
|
depth: maxDepth,
|
|
236987
|
-
confidence: this.computeConfidence(impacted
|
|
236991
|
+
confidence: this.computeConfidence(impacted, pathConfidence),
|
|
236988
236992
|
classified
|
|
236989
236993
|
};
|
|
236990
236994
|
}
|
|
236991
236995
|
/**
|
|
236992
|
-
*
|
|
236993
|
-
*
|
|
236994
|
-
*
|
|
236996
|
+
* Derive confidence from the actual quality of edges traversed, not from
|
|
236997
|
+
* result size. A small result built from fuzzy/unresolved edges is LOW
|
|
236998
|
+
* confidence; a large result built from high-confidence AST edges is HIGH.
|
|
236999
|
+
*
|
|
237000
|
+
* Algorithm:
|
|
237001
|
+
* - Compute the average minimum-path-confidence across all impacted nodes.
|
|
237002
|
+
* - Penalise for deep chains (they amplify uncertainty).
|
|
237003
|
+
* - Map the combined score to HIGH / MEDIUM / LOW.
|
|
236995
237004
|
*/
|
|
236996
|
-
computeConfidence(
|
|
236997
|
-
if (
|
|
237005
|
+
computeConfidence(impacted, pathConfidence) {
|
|
237006
|
+
if (impacted.length === 0)
|
|
237007
|
+
return "high";
|
|
237008
|
+
let total = 0;
|
|
237009
|
+
for (const id of impacted) {
|
|
237010
|
+
total += pathConfidence.get(id) ?? 1;
|
|
237011
|
+
}
|
|
237012
|
+
const avgConf = total / impacted.length;
|
|
237013
|
+
const sizePenalty = impacted.length > 20 ? 0.15 : impacted.length > 10 ? 0.08 : 0;
|
|
237014
|
+
const score = avgConf - sizePenalty;
|
|
237015
|
+
if (score >= 0.75)
|
|
236998
237016
|
return "high";
|
|
236999
|
-
if (
|
|
237017
|
+
if (score >= 0.5)
|
|
237000
237018
|
return "medium";
|
|
237001
237019
|
return "low";
|
|
237002
237020
|
}
|
|
@@ -237007,7 +237025,6 @@ var ENTRY_POINT_PATTERNS = [
|
|
|
237007
237025
|
/^(main|bootstrap|start|init|setup|configure|register|mount)$/i,
|
|
237008
237026
|
/^(app|server|index|mod|program)$/i,
|
|
237009
237027
|
/Handler$/i,
|
|
237010
|
-
// Express/Koa/Hono handlers
|
|
237011
237028
|
/Middleware$/i,
|
|
237012
237029
|
/Controller$/i,
|
|
237013
237030
|
/^use[A-Z]/,
|
|
@@ -237023,14 +237040,26 @@ var TEST_PATTERNS = [
|
|
|
237023
237040
|
/\.spec\./,
|
|
237024
237041
|
/__test__/
|
|
237025
237042
|
];
|
|
237043
|
+
var DYNAMIC_USAGE_PATTERNS = [
|
|
237044
|
+
/^addEventListener$/i,
|
|
237045
|
+
/^removeEventListener$/i,
|
|
237046
|
+
/^on[A-Z]/,
|
|
237047
|
+
/(invoke|dispatch|emit|call|apply)/i,
|
|
237048
|
+
/^ngOnInit$/i,
|
|
237049
|
+
/^componentDidMount$/i,
|
|
237050
|
+
/^componentWillUnmount$/i
|
|
237051
|
+
];
|
|
237026
237052
|
var DeadCodeDetector = class {
|
|
237027
237053
|
graph;
|
|
237028
237054
|
lock;
|
|
237029
237055
|
routeHandlers;
|
|
237056
|
+
/** Files that have at least one unresolved import (empty resolvedPath) */
|
|
237057
|
+
filesWithUnresolvedImports;
|
|
237030
237058
|
constructor(graph, lock) {
|
|
237031
237059
|
this.graph = graph;
|
|
237032
237060
|
this.lock = lock;
|
|
237033
237061
|
this.routeHandlers = new Set((lock.routes ?? []).map((r) => r.handler).filter(Boolean));
|
|
237062
|
+
this.filesWithUnresolvedImports = this.buildUnresolvedImportFileSet();
|
|
237034
237063
|
}
|
|
237035
237064
|
detect() {
|
|
237036
237065
|
const dead = [];
|
|
@@ -237049,13 +237078,15 @@ var DeadCodeDetector = class {
|
|
|
237049
237078
|
continue;
|
|
237050
237079
|
if (this.isExempt(fn, id))
|
|
237051
237080
|
continue;
|
|
237081
|
+
const confidence = this.inferConfidence(fn);
|
|
237052
237082
|
const entry = {
|
|
237053
237083
|
id,
|
|
237054
237084
|
name: fn.name,
|
|
237055
237085
|
file: fn.file,
|
|
237056
237086
|
moduleId,
|
|
237057
237087
|
type: "function",
|
|
237058
|
-
reason: this.inferReason(fn,
|
|
237088
|
+
reason: this.inferReason(fn),
|
|
237089
|
+
confidence
|
|
237059
237090
|
};
|
|
237060
237091
|
dead.push(entry);
|
|
237061
237092
|
byModule[moduleId].dead++;
|
|
@@ -237069,9 +237100,7 @@ var DeadCodeDetector = class {
|
|
|
237069
237100
|
}
|
|
237070
237101
|
const inEdges = this.graph.inEdges.get(id) || [];
|
|
237071
237102
|
const hasCallers = inEdges.some((e) => e.type === "calls" || e.type === "imports");
|
|
237072
|
-
if (hasCallers)
|
|
237073
|
-
continue;
|
|
237074
|
-
if (cls.isExported)
|
|
237103
|
+
if (hasCallers || cls.isExported)
|
|
237075
237104
|
continue;
|
|
237076
237105
|
const entry = {
|
|
237077
237106
|
id,
|
|
@@ -237079,7 +237108,8 @@ var DeadCodeDetector = class {
|
|
|
237079
237108
|
file: cls.file,
|
|
237080
237109
|
moduleId,
|
|
237081
237110
|
type: "class",
|
|
237082
|
-
reason: "Class has no callers or importers and is not exported"
|
|
237111
|
+
reason: "Class has no callers or importers and is not exported",
|
|
237112
|
+
confidence: this.filesWithUnresolvedImports.has(cls.file) ? "medium" : "high"
|
|
237083
237113
|
};
|
|
237084
237114
|
dead.push(entry);
|
|
237085
237115
|
byModule[moduleId].dead++;
|
|
@@ -237094,7 +237124,7 @@ var DeadCodeDetector = class {
|
|
|
237094
237124
|
byModule
|
|
237095
237125
|
};
|
|
237096
237126
|
}
|
|
237097
|
-
// ───
|
|
237127
|
+
// ─── Private helpers ───────────────────────────────────────────
|
|
237098
237128
|
isExempt(fn, id) {
|
|
237099
237129
|
if (fn.isExported)
|
|
237100
237130
|
return true;
|
|
@@ -237106,24 +237136,66 @@ var DeadCodeDetector = class {
|
|
|
237106
237136
|
return true;
|
|
237107
237137
|
if (fn.name === "constructor" || fn.name === "__init__")
|
|
237108
237138
|
return true;
|
|
237109
|
-
if (this.isCalledByExportedInSameFile(fn
|
|
237139
|
+
if (this.isCalledByExportedInSameFile(fn))
|
|
237110
237140
|
return true;
|
|
237111
237141
|
return false;
|
|
237112
237142
|
}
|
|
237113
|
-
isCalledByExportedInSameFile(fn
|
|
237143
|
+
isCalledByExportedInSameFile(fn) {
|
|
237114
237144
|
for (const callerId of fn.calledBy) {
|
|
237115
237145
|
const caller = this.lock.functions[callerId];
|
|
237116
|
-
if (caller && caller.isExported && caller.file === fn.file)
|
|
237146
|
+
if (caller && caller.isExported && caller.file === fn.file)
|
|
237117
237147
|
return true;
|
|
237118
|
-
}
|
|
237119
237148
|
}
|
|
237120
237149
|
return false;
|
|
237121
237150
|
}
|
|
237122
|
-
|
|
237151
|
+
/**
|
|
237152
|
+
* Assign a confidence level to a dead code finding.
|
|
237153
|
+
*
|
|
237154
|
+
* Priority (first match wins):
|
|
237155
|
+
* medium — lock.calledBy has entries that didn't become graph edges:
|
|
237156
|
+
* something references this function but resolution failed.
|
|
237157
|
+
* medium — file has unresolved imports: the graph may be incomplete.
|
|
237158
|
+
* low — function name matches common dynamic-dispatch patterns.
|
|
237159
|
+
* high — none of the above: safe to remove.
|
|
237160
|
+
*/
|
|
237161
|
+
inferConfidence(fn) {
|
|
237162
|
+
if (fn.calledBy.length > 0)
|
|
237163
|
+
return "medium";
|
|
237164
|
+
if (this.filesWithUnresolvedImports.has(fn.file))
|
|
237165
|
+
return "medium";
|
|
237166
|
+
if (DYNAMIC_USAGE_PATTERNS.some((p) => p.test(fn.name)))
|
|
237167
|
+
return "low";
|
|
237168
|
+
return "high";
|
|
237169
|
+
}
|
|
237170
|
+
inferReason(fn) {
|
|
237123
237171
|
if (fn.calledBy.length === 0) {
|
|
237124
237172
|
return "No callers found anywhere in the codebase";
|
|
237125
237173
|
}
|
|
237126
|
-
return `${fn.calledBy.length}
|
|
237174
|
+
return `${fn.calledBy.length} reference(s) in lock but none resolved to active call edges`;
|
|
237175
|
+
}
|
|
237176
|
+
/**
|
|
237177
|
+
* Build the set of file paths that have at least one import whose
|
|
237178
|
+
* resolvedPath is empty. Used to downgrade confidence for all dead
|
|
237179
|
+
* findings in those files, since the graph may be incomplete.
|
|
237180
|
+
*
|
|
237181
|
+
* We derive this from the lock's file entries. Each file entry stores
|
|
237182
|
+
* its imports; any import with an empty resolvedPath (or no match in
|
|
237183
|
+
* the graph nodes) indicates an unresolved dependency.
|
|
237184
|
+
*/
|
|
237185
|
+
buildUnresolvedImportFileSet() {
|
|
237186
|
+
const result = /* @__PURE__ */ new Set();
|
|
237187
|
+
if (!this.lock.files)
|
|
237188
|
+
return result;
|
|
237189
|
+
for (const [filePath, fileInfo] of Object.entries(this.lock.files)) {
|
|
237190
|
+
const imports = fileInfo.imports ?? [];
|
|
237191
|
+
for (const imp of imports) {
|
|
237192
|
+
if (!imp.resolvedPath || imp.resolvedPath === "") {
|
|
237193
|
+
result.add(filePath);
|
|
237194
|
+
break;
|
|
237195
|
+
}
|
|
237196
|
+
}
|
|
237197
|
+
}
|
|
237198
|
+
return result;
|
|
237127
237199
|
}
|
|
237128
237200
|
};
|
|
237129
237201
|
|
|
@@ -237272,22 +237344,43 @@ var MikkLockSchema = external_exports.object({
|
|
|
237272
237344
|
})
|
|
237273
237345
|
});
|
|
237274
237346
|
|
|
237275
|
-
// ../core/dist/
|
|
237347
|
+
// ../core/dist/utils/json.js
|
|
237276
237348
|
var fs = __toESM(require("node:fs/promises"), 1);
|
|
237349
|
+
async function readJsonSafe(filePath, fileLabel = "JSON file") {
|
|
237350
|
+
let content;
|
|
237351
|
+
try {
|
|
237352
|
+
content = await fs.readFile(filePath, "utf-8");
|
|
237353
|
+
} catch (e) {
|
|
237354
|
+
if (e.code === "ENOENT") {
|
|
237355
|
+
throw e;
|
|
237356
|
+
}
|
|
237357
|
+
throw new Error(`Failed to read ${fileLabel}: ${e.message}`);
|
|
237358
|
+
}
|
|
237359
|
+
const sanitized = content.replace(/^\uFEFF/, "");
|
|
237360
|
+
try {
|
|
237361
|
+
return JSON.parse(sanitized);
|
|
237362
|
+
} catch (e) {
|
|
237363
|
+
throw new Error(`Malformed ${fileLabel}: Syntax error - ${e.message}`);
|
|
237364
|
+
}
|
|
237365
|
+
}
|
|
237366
|
+
|
|
237367
|
+
// ../core/dist/contract/contract-reader.js
|
|
237277
237368
|
var ContractReader = class {
|
|
237278
237369
|
/** Read and validate mikk.json */
|
|
237279
237370
|
async read(contractPath) {
|
|
237280
|
-
let
|
|
237371
|
+
let json;
|
|
237281
237372
|
try {
|
|
237282
|
-
|
|
237283
|
-
} catch {
|
|
237284
|
-
|
|
237373
|
+
json = await readJsonSafe(contractPath, "mikk.json");
|
|
237374
|
+
} catch (e) {
|
|
237375
|
+
if (e.code === "ENOENT") {
|
|
237376
|
+
throw new ContractNotFoundError(contractPath);
|
|
237377
|
+
}
|
|
237378
|
+
throw e;
|
|
237285
237379
|
}
|
|
237286
|
-
const json = JSON.parse(content.replace(/^\uFEFF/, ""));
|
|
237287
237380
|
const result = MikkContractSchema.safeParse(json);
|
|
237288
237381
|
if (!result.success) {
|
|
237289
237382
|
const errors = result.error.issues.map((i) => ` ${i.path.join(".")}: ${i.message}`).join("\n");
|
|
237290
|
-
throw new Error(`Invalid mikk.json:
|
|
237383
|
+
throw new Error(`Invalid mikk.json structure:
|
|
237291
237384
|
${errors}`);
|
|
237292
237385
|
}
|
|
237293
237386
|
return result.data;
|
|
@@ -237299,18 +237392,20 @@ var fs2 = __toESM(require("node:fs/promises"), 1);
|
|
|
237299
237392
|
var LockReader = class {
|
|
237300
237393
|
/** Read and validate mikk.lock.json */
|
|
237301
237394
|
async read(lockPath) {
|
|
237302
|
-
let
|
|
237395
|
+
let json;
|
|
237303
237396
|
try {
|
|
237304
|
-
|
|
237305
|
-
} catch {
|
|
237306
|
-
|
|
237397
|
+
json = await readJsonSafe(lockPath, "mikk.lock.json");
|
|
237398
|
+
} catch (e) {
|
|
237399
|
+
if (e.code === "ENOENT") {
|
|
237400
|
+
throw new LockNotFoundError();
|
|
237401
|
+
}
|
|
237402
|
+
throw e;
|
|
237307
237403
|
}
|
|
237308
|
-
const json = JSON.parse(content.replace(/^\uFEFF/, ""));
|
|
237309
237404
|
const hydrated = hydrateLock(json);
|
|
237310
237405
|
const result = MikkLockSchema.safeParse(hydrated);
|
|
237311
237406
|
if (!result.success) {
|
|
237312
237407
|
const errors = result.error.issues.map((i) => ` ${i.path.join(".")}: ${i.message}`).join("\n");
|
|
237313
|
-
throw new Error(`Invalid mikk.lock.json:
|
|
237408
|
+
throw new Error(`Invalid mikk.lock.json structure:
|
|
237314
237409
|
${errors}`);
|
|
237315
237410
|
}
|
|
237316
237411
|
return result.data;
|
|
@@ -238745,7 +238840,7 @@ function registerTools(server2, projectRoot) {
|
|
|
238745
238840
|
);
|
|
238746
238841
|
server2.tool(
|
|
238747
238842
|
"mikk_query_context",
|
|
238748
|
-
"Ask an architecture question \
|
|
238843
|
+
"Ask an architecture question \u2014 returns graph-traced context with relevant functions, files, and call chains. Use this to understand how code flows through the project.",
|
|
238749
238844
|
{
|
|
238750
238845
|
question: external_exports.string().describe("The architecture question or task description"),
|
|
238751
238846
|
maxHops: external_exports.number().optional().default(4).describe("Graph traversal depth (default: 4)"),
|
|
@@ -238953,7 +239048,7 @@ ${staleness}` : "";
|
|
|
238953
239048
|
constraintStatus: totalViolations === 0 ? "pass" : "fail",
|
|
238954
239049
|
files: fileReports,
|
|
238955
239050
|
warning: staleness,
|
|
238956
|
-
hint: totalViolations > 0 ? "\
|
|
239051
|
+
hint: totalViolations > 0 ? "\u26A0 Constraint violations detected! Review the violations before proceeding. Use mikk_get_constraints for full rule context." : "All constraints satisfied. If safe, proceed with your edits."
|
|
238957
239052
|
};
|
|
238958
239053
|
const _rawBE = _filesTok(lock, filesToEdit) * 4;
|
|
238959
239054
|
response.tokens = _track(projectRoot, _rawBE, response);
|
|
@@ -239087,7 +239182,7 @@ ${staleness}` : "";
|
|
|
239087
239182
|
content: [{
|
|
239088
239183
|
type: "text",
|
|
239089
239184
|
text: [
|
|
239090
|
-
"\
|
|
239185
|
+
"\u26A0 Semantic search requires @xenova/transformers.",
|
|
239091
239186
|
"",
|
|
239092
239187
|
"Install it in your project root:",
|
|
239093
239188
|
" npm install @xenova/transformers",
|
|
@@ -239216,7 +239311,7 @@ ${content}`
|
|
|
239216
239311
|
);
|
|
239217
239312
|
server2.tool(
|
|
239218
239313
|
"mikk_dead_code",
|
|
239219
|
-
"Detect dead code \
|
|
239314
|
+
"Detect dead code \u2014 functions with zero callers after exempting exports, entry points, route handlers, tests, and constructors. Use this before refactoring or cleanup.",
|
|
239220
239315
|
{
|
|
239221
239316
|
moduleId: external_exports.string().optional().describe("Filter results to a specific module ID")
|
|
239222
239317
|
},
|
|
@@ -239241,7 +239336,7 @@ ${content}`
|
|
|
239241
239336
|
);
|
|
239242
239337
|
server2.tool(
|
|
239243
239338
|
"mikk_manage_adr",
|
|
239244
|
-
"CRUD for Architectural Decision Records (ADRs) in mikk.json. Actions: list, get, add, update, remove. WHEN TO USE: When making architectural changes \
|
|
239339
|
+
"CRUD for Architectural Decision Records (ADRs) in mikk.json. Actions: list, get, add, update, remove. WHEN TO USE: When making architectural changes \u2014 document WHY so future AI agents understand. AFTER THIS: ADRs automatically surface in mikk_query_context responses. Required for add: id, title, reason.",
|
|
239245
239340
|
{
|
|
239246
239341
|
action: external_exports.enum(["list", "get", "add", "update", "remove"]).describe("The CRUD action to perform"),
|
|
239247
239342
|
id: external_exports.string().optional().describe("ADR id (required for get, update, remove)"),
|
|
@@ -239351,7 +239446,7 @@ ${content}`
|
|
|
239351
239446
|
);
|
|
239352
239447
|
server2.tool(
|
|
239353
239448
|
"mikk_read_file",
|
|
239354
|
-
"Read file scoped to specific functions. Returns bodies with metadata headers (params, calls, calledBy). WHEN TO USE: When you know which functions you need \
|
|
239449
|
+
"Read file scoped to specific functions. Returns bodies with metadata headers (params, calls, calledBy). WHEN TO USE: When you know which functions you need \u2014 saves tokens vs mikk_get_file. AFTER THIS: Use mikk_before_edit before making changes. TIP: This is the preferred way to read code \u2014 always specify function names when possible.",
|
|
239355
239450
|
{
|
|
239356
239451
|
file: external_exports.string().describe("File path relative to project root"),
|
|
239357
239452
|
functions: external_exports.array(external_exports.string()).optional().describe("Function names to extract. If omitted, returns the whole file.")
|
|
@@ -239391,7 +239486,7 @@ ${content}` }]
|
|
|
239391
239486
|
(f) => (f.name === fnName || f.name.endsWith(`.${fnName}`)) && (f.file === normalizedFile || f.file.endsWith("/" + normalizedFile))
|
|
239392
239487
|
);
|
|
239393
239488
|
if (!fn) {
|
|
239394
|
-
sections.push(`// \
|
|
239489
|
+
sections.push(`// \u26A0 Function "${fnName}" not found in ${file}`);
|
|
239395
239490
|
continue;
|
|
239396
239491
|
}
|
|
239397
239492
|
const header = [
|
|
@@ -239531,7 +239626,7 @@ ${staleness}` : "";
|
|
|
239531
239626
|
);
|
|
239532
239627
|
server2.tool(
|
|
239533
239628
|
"mikk_rename",
|
|
239534
|
-
"Plan a coordinated multi-file rename. Finds all call sites and import locations for a function and provides a step-by-step edit plan. WHEN TO USE: Before renaming any function \
|
|
239629
|
+
"Plan a coordinated multi-file rename. Finds all call sites and import locations for a function and provides a step-by-step edit plan. WHEN TO USE: Before renaming any function \u2014 ensures you update ALL call sites. AFTER THIS: Execute the edit plan, then run mikk analyze.",
|
|
239535
239630
|
{
|
|
239536
239631
|
functionName: external_exports.string().describe("The current function name to rename"),
|
|
239537
239632
|
newName: external_exports.string().describe("The desired new name")
|
|
@@ -239631,7 +239726,7 @@ async function loadContractAndLock(projectRoot) {
|
|
|
239631
239726
|
const syncStatus = lock.syncState?.status ?? "unknown";
|
|
239632
239727
|
let staleness = null;
|
|
239633
239728
|
if (syncStatus === "drifted" || syncStatus === "conflict") {
|
|
239634
|
-
staleness = `\
|
|
239729
|
+
staleness = `\u26A0 Lock file is ${syncStatus}. Run \`mikk analyze\` for accurate results.`;
|
|
239635
239730
|
}
|
|
239636
239731
|
if (!staleness) {
|
|
239637
239732
|
const fileEntries = Object.entries(lock.files);
|
|
@@ -239654,7 +239749,7 @@ async function loadContractAndLock(projectRoot) {
|
|
|
239654
239749
|
}
|
|
239655
239750
|
}
|
|
239656
239751
|
if (mismatched > 0) {
|
|
239657
|
-
staleness = `\
|
|
239752
|
+
staleness = `\u26A0 STALE: ${mismatched} file(s) changed since last analysis (${mismatchedFiles.slice(0, 3).join(", ")}${mismatched > 3 ? "..." : ""}). Run \`mikk analyze\`.`;
|
|
239658
239753
|
}
|
|
239659
239754
|
}
|
|
239660
239755
|
const graph = buildGraphFromLock(lock);
|
|
@@ -239726,7 +239821,7 @@ function detectCircularDeps(fns, lock) {
|
|
|
239726
239821
|
const cycleStart = cyclePath.indexOf(id);
|
|
239727
239822
|
const cycle = cyclePath.slice(cycleStart).map((cid) => lock.functions[cid]?.name ?? cid);
|
|
239728
239823
|
cycle.push(lock.functions[id]?.name ?? id);
|
|
239729
|
-
warnings.push(`\
|
|
239824
|
+
warnings.push(`\u26A0 Circular: ${cycle.join(" -> ")}`);
|
|
239730
239825
|
return true;
|
|
239731
239826
|
}
|
|
239732
239827
|
if (visited.has(id)) return false;
|
|
@@ -239861,7 +239956,7 @@ async function safeRead(filePath) {
|
|
|
239861
239956
|
}
|
|
239862
239957
|
|
|
239863
239958
|
// src/server.ts
|
|
239864
|
-
var VERSION = true ? "1.9.
|
|
239959
|
+
var VERSION = true ? "1.9.2" : "0.0.0-dev";
|
|
239865
239960
|
function createMikkMcpServer(projectRoot) {
|
|
239866
239961
|
const server2 = new McpServer({
|
|
239867
239962
|
name: "mikk",
|