memorydetective 1.1.0 → 1.2.1
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/CHANGELOG.md +41 -1
- package/README.md +49 -20
- package/USAGE.md +39 -23
- package/dist/cli.js +1 -1
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -1
- package/dist/runtime/sourcekit/client.d.ts +37 -0
- package/dist/runtime/sourcekit/client.js +132 -0
- package/dist/runtime/sourcekit/client.js.map +1 -0
- package/dist/runtime/sourcekit/pool.d.ts +25 -0
- package/dist/runtime/sourcekit/pool.js +101 -0
- package/dist/runtime/sourcekit/pool.js.map +1 -0
- package/dist/runtime/sourcekit/protocol.d.ts +37 -0
- package/dist/runtime/sourcekit/protocol.js +161 -0
- package/dist/runtime/sourcekit/protocol.js.map +1 -0
- package/dist/tools/swift/_helpers.d.ts +29 -0
- package/dist/tools/swift/_helpers.js +64 -0
- package/dist/tools/swift/_helpers.js.map +1 -0
- package/dist/tools/swift/findSymbolReferences.d.ts +30 -0
- package/dist/tools/swift/findSymbolReferences.js +56 -0
- package/dist/tools/swift/findSymbolReferences.js.map +1 -0
- package/dist/tools/swift/getHoverInfo.d.ts +27 -0
- package/dist/tools/swift/getHoverInfo.js +40 -0
- package/dist/tools/swift/getHoverInfo.js.map +1 -0
- package/dist/tools/swift/getSymbolDefinition.d.ts +46 -0
- package/dist/tools/swift/getSymbolDefinition.js +68 -0
- package/dist/tools/swift/getSymbolDefinition.js.map +1 -0
- package/dist/tools/swift/getSymbolsOverview.d.ts +22 -0
- package/dist/tools/swift/getSymbolsOverview.js +33 -0
- package/dist/tools/swift/getSymbolsOverview.js.map +1 -0
- package/dist/tools/swift/index.d.ts +14 -0
- package/dist/tools/swift/index.js +15 -0
- package/dist/tools/swift/index.js.map +1 -0
- package/dist/tools/swift/searchPattern.d.ts +38 -0
- package/dist/tools/swift/searchPattern.js +71 -0
- package/dist/tools/swift/searchPattern.js.map +1 -0
- package/package.json +3 -1
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { resolve as resolvePath } from "node:path";
|
|
4
|
+
import { acquireClient, projectRootFor } from "../../runtime/sourcekit/pool.js";
|
|
5
|
+
import { lspDefinition, } from "../../runtime/sourcekit/protocol.js";
|
|
6
|
+
import { findSymbolDeclaration } from "./_helpers.js";
|
|
7
|
+
export const swiftGetSymbolDefinitionSchema = z.object({
|
|
8
|
+
symbolName: z
|
|
9
|
+
.string()
|
|
10
|
+
.min(1)
|
|
11
|
+
.describe("Name of the Swift symbol to locate (class, struct, enum, protocol, func, var, etc.)."),
|
|
12
|
+
hint: z
|
|
13
|
+
.object({
|
|
14
|
+
filePath: z.string().optional(),
|
|
15
|
+
module: z.string().optional(),
|
|
16
|
+
})
|
|
17
|
+
.optional()
|
|
18
|
+
.describe("Optional hint to speed up the search. `filePath` skips the project scan; `module` is reserved for future multi-module work."),
|
|
19
|
+
projectRoot: z
|
|
20
|
+
.string()
|
|
21
|
+
.optional()
|
|
22
|
+
.describe("Override the project root. Default discovers the nearest Package.swift / .xcodeproj / .xcworkspace from the cwd."),
|
|
23
|
+
candidatePaths: z
|
|
24
|
+
.array(z.string())
|
|
25
|
+
.optional()
|
|
26
|
+
.describe("If provided, search these files for the symbol declaration before asking SourceKit-LSP. Speeds up location when the agent already has a guess (e.g. from `findSymbolReferences` or `swift_search_pattern`)."),
|
|
27
|
+
});
|
|
28
|
+
export async function swiftGetSymbolDefinition(input) {
|
|
29
|
+
const root = input.projectRoot
|
|
30
|
+
? resolvePath(input.projectRoot)
|
|
31
|
+
: projectRootFor(input.candidatePaths?.[0] ?? input.hint?.filePath ?? process.cwd());
|
|
32
|
+
if (!existsSync(root)) {
|
|
33
|
+
throw new Error(`Project root not found: ${root}`);
|
|
34
|
+
}
|
|
35
|
+
const fileCandidates = [
|
|
36
|
+
...(input.hint?.filePath ? [input.hint.filePath] : []),
|
|
37
|
+
...(input.candidatePaths ?? []),
|
|
38
|
+
];
|
|
39
|
+
for (const fp of fileCandidates) {
|
|
40
|
+
const abs = resolvePath(fp);
|
|
41
|
+
if (!existsSync(abs))
|
|
42
|
+
continue;
|
|
43
|
+
const pos = findSymbolDeclaration(abs, input.symbolName);
|
|
44
|
+
if (pos) {
|
|
45
|
+
const client = await acquireClient(root);
|
|
46
|
+
const defs = await lspDefinition(client, abs, pos.line, pos.character);
|
|
47
|
+
return {
|
|
48
|
+
ok: true,
|
|
49
|
+
symbolName: input.symbolName,
|
|
50
|
+
definitions: defs.length > 0
|
|
51
|
+
? defs
|
|
52
|
+
: [{ filePath: abs, line: pos.line, character: pos.character }],
|
|
53
|
+
preScanHit: { filePath: abs, matchedText: pos.matchedText.trim() },
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// No candidate found locally. SourceKit-LSP doesn't expose a workspace-wide
|
|
58
|
+
// "find me a symbol named X" endpoint over plain LSP, so we punt and let
|
|
59
|
+
// the agent feed back candidate paths from a prior `searchPattern` or
|
|
60
|
+
// `findCycles` call.
|
|
61
|
+
return {
|
|
62
|
+
ok: true,
|
|
63
|
+
symbolName: input.symbolName,
|
|
64
|
+
definitions: [],
|
|
65
|
+
preScanHit: undefined,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=getSymbolDefinition.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getSymbolDefinition.js","sourceRoot":"","sources":["../../../src/tools/swift/getSymbolDefinition.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { type ResolvedSymbol } from "../../runtime/sourcekit/protocol.js";
|
|
3
|
+
export declare const swiftGetSymbolsOverviewSchema: z.ZodObject<{
|
|
4
|
+
filePath: z.ZodString;
|
|
5
|
+
projectRoot: z.ZodOptional<z.ZodString>;
|
|
6
|
+
topLevelOnly: z.ZodDefault<z.ZodBoolean>;
|
|
7
|
+
}, "strip", z.ZodTypeAny, {
|
|
8
|
+
filePath: string;
|
|
9
|
+
topLevelOnly: boolean;
|
|
10
|
+
projectRoot?: string | undefined;
|
|
11
|
+
}, {
|
|
12
|
+
filePath: string;
|
|
13
|
+
projectRoot?: string | undefined;
|
|
14
|
+
topLevelOnly?: boolean | undefined;
|
|
15
|
+
}>;
|
|
16
|
+
export type SwiftGetSymbolsOverviewInput = z.infer<typeof swiftGetSymbolsOverviewSchema>;
|
|
17
|
+
export interface SwiftGetSymbolsOverviewResult {
|
|
18
|
+
ok: boolean;
|
|
19
|
+
filePath: string;
|
|
20
|
+
symbols: ResolvedSymbol[];
|
|
21
|
+
}
|
|
22
|
+
export declare function swiftGetSymbolsOverview(input: SwiftGetSymbolsOverviewInput): Promise<SwiftGetSymbolsOverviewResult>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { resolve as resolvePath } from "node:path";
|
|
4
|
+
import { acquireClient, projectRootFor } from "../../runtime/sourcekit/pool.js";
|
|
5
|
+
import { lspDocumentSymbol, } from "../../runtime/sourcekit/protocol.js";
|
|
6
|
+
export const swiftGetSymbolsOverviewSchema = z.object({
|
|
7
|
+
filePath: z.string().min(1).describe("Absolute path to a Swift source file."),
|
|
8
|
+
projectRoot: z.string().optional(),
|
|
9
|
+
topLevelOnly: z
|
|
10
|
+
.boolean()
|
|
11
|
+
.default(true)
|
|
12
|
+
.describe("Return only top-level symbols (classes, structs, enums, protocols, free functions). When false, returns nested children too. Default true keeps responses small."),
|
|
13
|
+
});
|
|
14
|
+
export async function swiftGetSymbolsOverview(input) {
|
|
15
|
+
const file = resolvePath(input.filePath);
|
|
16
|
+
if (!existsSync(file)) {
|
|
17
|
+
throw new Error(`File not found: ${file}`);
|
|
18
|
+
}
|
|
19
|
+
const root = input.projectRoot
|
|
20
|
+
? resolvePath(input.projectRoot)
|
|
21
|
+
: projectRootFor(file);
|
|
22
|
+
const client = await acquireClient(root);
|
|
23
|
+
const symbols = await lspDocumentSymbol(client, file);
|
|
24
|
+
if (input.topLevelOnly ?? true) {
|
|
25
|
+
return {
|
|
26
|
+
ok: true,
|
|
27
|
+
filePath: file,
|
|
28
|
+
symbols: symbols.map((s) => ({ ...s, children: undefined })),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
return { ok: true, filePath: file, symbols };
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=getSymbolsOverview.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getSymbolsOverview.js","sourceRoot":"","sources":["../../../src/tools/swift/getSymbolsOverview.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAChF,OAAO,EACL,iBAAiB,GAElB,MAAM,qCAAqC,CAAC;AAE7C,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,CAAC,MAAM,CAAC;IACpD,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,uCAAuC,CAAC;IAC7E,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAClC,YAAY,EAAE,CAAC;SACZ,OAAO,EAAE;SACT,OAAO,CAAC,IAAI,CAAC;SACb,QAAQ,CACP,kKAAkK,CACnK;CACJ,CAAC,CAAC;AAYH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,KAAmC;IAEnC,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACzC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,mBAAmB,IAAI,EAAE,CAAC,CAAC;IAC7C,CAAC;IACD,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW;QAC5B,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,WAAW,CAAC;QAChC,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;IAEzB,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,IAAI,CAAC,CAAC;IACzC,MAAM,OAAO,GAAG,MAAM,iBAAiB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAEtD,IAAI,KAAK,CAAC,YAAY,IAAI,IAAI,EAAE,CAAC;QAC/B,OAAO;YACL,EAAE,EAAE,IAAI;YACR,QAAQ,EAAE,IAAI;YACd,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;SAC7D,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AAC/C,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Swift source-bridging tools. Pair with the memory-graph tools so the
|
|
3
|
+
* LLM agent can go from "found this leak in the cycle" to "find it in the
|
|
4
|
+
* codebase" without leaving chat.
|
|
5
|
+
*
|
|
6
|
+
* One tool per file, all backed by a shared SourceKit-LSP client pool
|
|
7
|
+
* (`src/runtime/sourcekit/`). This file is purely a re-export aggregator
|
|
8
|
+
* for callers that want a single import point.
|
|
9
|
+
*/
|
|
10
|
+
export { swiftGetSymbolDefinition, swiftGetSymbolDefinitionSchema, type SwiftGetSymbolDefinitionInput, type SwiftGetSymbolDefinitionResult, } from "./getSymbolDefinition.js";
|
|
11
|
+
export { swiftFindSymbolReferences, swiftFindSymbolReferencesSchema, type SwiftFindSymbolReferencesInput, type SwiftFindSymbolReferencesResult, } from "./findSymbolReferences.js";
|
|
12
|
+
export { swiftGetSymbolsOverview, swiftGetSymbolsOverviewSchema, type SwiftGetSymbolsOverviewInput, type SwiftGetSymbolsOverviewResult, } from "./getSymbolsOverview.js";
|
|
13
|
+
export { swiftGetHoverInfo, swiftGetHoverInfoSchema, type SwiftGetHoverInfoInput, type SwiftGetHoverInfoResult, } from "./getHoverInfo.js";
|
|
14
|
+
export { swiftSearchPattern, swiftSearchPatternSchema, type SwiftSearchPatternInput, type SwiftSearchPatternResult, type SwiftSearchPatternMatch, } from "./searchPattern.js";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Swift source-bridging tools. Pair with the memory-graph tools so the
|
|
3
|
+
* LLM agent can go from "found this leak in the cycle" to "find it in the
|
|
4
|
+
* codebase" without leaving chat.
|
|
5
|
+
*
|
|
6
|
+
* One tool per file, all backed by a shared SourceKit-LSP client pool
|
|
7
|
+
* (`src/runtime/sourcekit/`). This file is purely a re-export aggregator
|
|
8
|
+
* for callers that want a single import point.
|
|
9
|
+
*/
|
|
10
|
+
export { swiftGetSymbolDefinition, swiftGetSymbolDefinitionSchema, } from "./getSymbolDefinition.js";
|
|
11
|
+
export { swiftFindSymbolReferences, swiftFindSymbolReferencesSchema, } from "./findSymbolReferences.js";
|
|
12
|
+
export { swiftGetSymbolsOverview, swiftGetSymbolsOverviewSchema, } from "./getSymbolsOverview.js";
|
|
13
|
+
export { swiftGetHoverInfo, swiftGetHoverInfoSchema, } from "./getHoverInfo.js";
|
|
14
|
+
export { swiftSearchPattern, swiftSearchPatternSchema, } from "./searchPattern.js";
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tools/swift/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,wBAAwB,EACxB,8BAA8B,GAG/B,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,yBAAyB,EACzB,+BAA+B,GAGhC,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,uBAAuB,EACvB,6BAA6B,GAG9B,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,iBAAiB,EACjB,uBAAuB,GAGxB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,kBAAkB,EAClB,wBAAwB,GAIzB,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Pure regex search over a Swift file. No SourceKit-LSP involvement, no
|
|
4
|
+
* IndexStoreDB. Useful for catching what LSP misses: closure capture lists
|
|
5
|
+
* (`[weak self]`, `[unowned self]`), `Task { ... self ... }` blocks, custom
|
|
6
|
+
* patterns the agent comes up with from a leak chain.
|
|
7
|
+
*/
|
|
8
|
+
export declare const swiftSearchPatternSchema: z.ZodObject<{
|
|
9
|
+
filePath: z.ZodString;
|
|
10
|
+
pattern: z.ZodString;
|
|
11
|
+
flags: z.ZodOptional<z.ZodString>;
|
|
12
|
+
maxMatches: z.ZodDefault<z.ZodNumber>;
|
|
13
|
+
}, "strip", z.ZodTypeAny, {
|
|
14
|
+
filePath: string;
|
|
15
|
+
pattern: string;
|
|
16
|
+
maxMatches: number;
|
|
17
|
+
flags?: string | undefined;
|
|
18
|
+
}, {
|
|
19
|
+
filePath: string;
|
|
20
|
+
pattern: string;
|
|
21
|
+
flags?: string | undefined;
|
|
22
|
+
maxMatches?: number | undefined;
|
|
23
|
+
}>;
|
|
24
|
+
export type SwiftSearchPatternInput = z.infer<typeof swiftSearchPatternSchema>;
|
|
25
|
+
export interface SwiftSearchPatternMatch {
|
|
26
|
+
line: number;
|
|
27
|
+
character: number;
|
|
28
|
+
text: string;
|
|
29
|
+
/** Trimmed source line for context. */
|
|
30
|
+
snippet?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface SwiftSearchPatternResult {
|
|
33
|
+
ok: boolean;
|
|
34
|
+
filePath: string;
|
|
35
|
+
matches: SwiftSearchPatternMatch[];
|
|
36
|
+
truncated: boolean;
|
|
37
|
+
}
|
|
38
|
+
export declare function swiftSearchPattern(input: SwiftSearchPatternInput): Promise<SwiftSearchPatternResult>;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { resolve as resolvePath } from "node:path";
|
|
4
|
+
/**
|
|
5
|
+
* Pure regex search over a Swift file. No SourceKit-LSP involvement, no
|
|
6
|
+
* IndexStoreDB. Useful for catching what LSP misses: closure capture lists
|
|
7
|
+
* (`[weak self]`, `[unowned self]`), `Task { ... self ... }` blocks, custom
|
|
8
|
+
* patterns the agent comes up with from a leak chain.
|
|
9
|
+
*/
|
|
10
|
+
export const swiftSearchPatternSchema = z.object({
|
|
11
|
+
filePath: z
|
|
12
|
+
.string()
|
|
13
|
+
.min(1)
|
|
14
|
+
.describe("Absolute path to a Swift source file."),
|
|
15
|
+
pattern: z
|
|
16
|
+
.string()
|
|
17
|
+
.min(1)
|
|
18
|
+
.describe("Regex pattern (JavaScript flavour). The `g` flag is implied — every match is returned."),
|
|
19
|
+
flags: z
|
|
20
|
+
.string()
|
|
21
|
+
.optional()
|
|
22
|
+
.describe('Additional RegExp flags ("i", "m", "s", "im", etc.).'),
|
|
23
|
+
maxMatches: z
|
|
24
|
+
.number()
|
|
25
|
+
.int()
|
|
26
|
+
.positive()
|
|
27
|
+
.max(500)
|
|
28
|
+
.default(50)
|
|
29
|
+
.describe("Cap on matches returned (default 50)."),
|
|
30
|
+
});
|
|
31
|
+
export async function swiftSearchPattern(input) {
|
|
32
|
+
const file = resolvePath(input.filePath);
|
|
33
|
+
if (!existsSync(file)) {
|
|
34
|
+
throw new Error(`File not found: ${file}`);
|
|
35
|
+
}
|
|
36
|
+
const flags = `g${input.flags ?? ""}`.replace(/g+/g, "g");
|
|
37
|
+
let re;
|
|
38
|
+
try {
|
|
39
|
+
re = new RegExp(input.pattern, flags);
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
throw new Error(`Invalid regex: ${err instanceof Error ? err.message : String(err)}`);
|
|
43
|
+
}
|
|
44
|
+
const text = readFileSync(file, "utf8");
|
|
45
|
+
const lines = text.split(/\r?\n/);
|
|
46
|
+
const matches = [];
|
|
47
|
+
let truncated = false;
|
|
48
|
+
const max = input.maxMatches ?? 50;
|
|
49
|
+
for (let i = 0; i < lines.length; i++) {
|
|
50
|
+
re.lastIndex = 0;
|
|
51
|
+
let m;
|
|
52
|
+
while ((m = re.exec(lines[i])) !== null) {
|
|
53
|
+
if (matches.length >= max) {
|
|
54
|
+
truncated = true;
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
matches.push({
|
|
58
|
+
line: i,
|
|
59
|
+
character: m.index,
|
|
60
|
+
text: m[0],
|
|
61
|
+
snippet: lines[i].trim(),
|
|
62
|
+
});
|
|
63
|
+
if (m[0].length === 0)
|
|
64
|
+
re.lastIndex += 1;
|
|
65
|
+
}
|
|
66
|
+
if (truncated)
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
return { ok: true, filePath: file, matches, truncated };
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=searchPattern.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"searchPattern.js","sourceRoot":"","sources":["../../../src/tools/swift/searchPattern.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memorydetective",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "MCP server for iOS leak hunting and performance investigation. Reads .memgraph + .trace files, captures new ones via xctrace and leaks(1), classifies retain cycles.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -51,6 +51,8 @@
|
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
53
53
|
"fast-xml-parser": "^5.7.2",
|
|
54
|
+
"vscode-jsonrpc": "^8.2.1",
|
|
55
|
+
"vscode-languageserver-protocol": "^3.17.5",
|
|
54
56
|
"zod": "^3.23.0"
|
|
55
57
|
},
|
|
56
58
|
"devDependencies": {
|