memorydetective 1.0.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/CHANGELOG.md +48 -1
  2. package/LICENSE +201 -21
  3. package/NOTICE +19 -0
  4. package/README.md +22 -23
  5. package/dist/cli.js +49 -5
  6. package/dist/cli.js.map +1 -1
  7. package/dist/index.js +52 -0
  8. package/dist/index.js.map +1 -1
  9. package/dist/parsers/shortenClassName.d.ts +45 -0
  10. package/dist/parsers/shortenClassName.js +155 -0
  11. package/dist/parsers/shortenClassName.js.map +1 -0
  12. package/dist/runtime/sourcekit/client.d.ts +37 -0
  13. package/dist/runtime/sourcekit/client.js +132 -0
  14. package/dist/runtime/sourcekit/client.js.map +1 -0
  15. package/dist/runtime/sourcekit/pool.d.ts +25 -0
  16. package/dist/runtime/sourcekit/pool.js +101 -0
  17. package/dist/runtime/sourcekit/pool.js.map +1 -0
  18. package/dist/runtime/sourcekit/protocol.d.ts +37 -0
  19. package/dist/runtime/sourcekit/protocol.js +161 -0
  20. package/dist/runtime/sourcekit/protocol.js.map +1 -0
  21. package/dist/tools/analyzeMemgraph.d.ts +11 -2
  22. package/dist/tools/analyzeMemgraph.js +37 -9
  23. package/dist/tools/analyzeMemgraph.js.map +1 -1
  24. package/dist/tools/findCycles.d.ts +6 -2
  25. package/dist/tools/findCycles.js +11 -6
  26. package/dist/tools/findCycles.js.map +1 -1
  27. package/dist/tools/reachableFromCycle.d.ts +61 -0
  28. package/dist/tools/reachableFromCycle.js +116 -0
  29. package/dist/tools/reachableFromCycle.js.map +1 -0
  30. package/dist/tools/renderCycleGraph.d.ts +1 -1
  31. package/dist/tools/swift/_helpers.d.ts +29 -0
  32. package/dist/tools/swift/_helpers.js +64 -0
  33. package/dist/tools/swift/_helpers.js.map +1 -0
  34. package/dist/tools/swift/findSymbolReferences.d.ts +30 -0
  35. package/dist/tools/swift/findSymbolReferences.js +56 -0
  36. package/dist/tools/swift/findSymbolReferences.js.map +1 -0
  37. package/dist/tools/swift/getHoverInfo.d.ts +27 -0
  38. package/dist/tools/swift/getHoverInfo.js +40 -0
  39. package/dist/tools/swift/getHoverInfo.js.map +1 -0
  40. package/dist/tools/swift/getSymbolDefinition.d.ts +46 -0
  41. package/dist/tools/swift/getSymbolDefinition.js +68 -0
  42. package/dist/tools/swift/getSymbolDefinition.js.map +1 -0
  43. package/dist/tools/swift/getSymbolsOverview.d.ts +22 -0
  44. package/dist/tools/swift/getSymbolsOverview.js +33 -0
  45. package/dist/tools/swift/getSymbolsOverview.js.map +1 -0
  46. package/dist/tools/swift/index.d.ts +14 -0
  47. package/dist/tools/swift/index.js +15 -0
  48. package/dist/tools/swift/index.js.map +1 -0
  49. package/dist/tools/swift/searchPattern.d.ts +38 -0
  50. package/dist/tools/swift/searchPattern.js +71 -0
  51. package/dist/tools/swift/searchPattern.js.map +1 -0
  52. package/package.json +5 -2
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Shared utilities for the Swift source-bridging tools.
3
+ *
4
+ * Position-finding via local regex pre-scan is the load-bearing trick: we
5
+ * locate the symbol's declaration in a candidate file with a quick regex
6
+ * before sending its position to SourceKit-LSP. This avoids LSP's
7
+ * comparatively slow workspace symbol search.
8
+ */
9
+ export interface SymbolPosition {
10
+ line: number;
11
+ character: number;
12
+ matchedText: string;
13
+ }
14
+ /**
15
+ * Find the first declaration of `symbolName` in the file.
16
+ *
17
+ * Looks for the symbol after one of Swift's declaration keywords
18
+ * (`class`, `struct`, `enum`, `protocol`, `func`, `var`, `let`,
19
+ * `actor`, `extension`). Falls back to the first standalone-word
20
+ * occurrence so we still surface a position when the name is referenced
21
+ * but not declared in the file (e.g. an extension method on a type
22
+ * declared elsewhere).
23
+ *
24
+ * Returns 0-based positions matching LSP's convention.
25
+ */
26
+ export declare function findSymbolDeclaration(filePath: string, symbolName: string): SymbolPosition | null;
27
+ export declare function escapeRegex(s: string): string;
28
+ /** Extract a few lines of context around `line` for snippet display. */
29
+ export declare function snippetAt(filePath: string, line: number, padding?: number): string;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Shared utilities for the Swift source-bridging tools.
3
+ *
4
+ * Position-finding via local regex pre-scan is the load-bearing trick: we
5
+ * locate the symbol's declaration in a candidate file with a quick regex
6
+ * before sending its position to SourceKit-LSP. This avoids LSP's
7
+ * comparatively slow workspace symbol search.
8
+ */
9
+ import { readFileSync } from "node:fs";
10
+ /**
11
+ * Find the first declaration of `symbolName` in the file.
12
+ *
13
+ * Looks for the symbol after one of Swift's declaration keywords
14
+ * (`class`, `struct`, `enum`, `protocol`, `func`, `var`, `let`,
15
+ * `actor`, `extension`). Falls back to the first standalone-word
16
+ * occurrence so we still surface a position when the name is referenced
17
+ * but not declared in the file (e.g. an extension method on a type
18
+ * declared elsewhere).
19
+ *
20
+ * Returns 0-based positions matching LSP's convention.
21
+ */
22
+ export function findSymbolDeclaration(filePath, symbolName) {
23
+ const text = readFileSync(filePath, "utf8");
24
+ const lines = text.split(/\r?\n/);
25
+ const declRe = new RegExp(`\\b(?:class|struct|enum|protocol|func|var|let|actor|extension)\\s+${escapeRegex(symbolName)}\\b`);
26
+ for (let i = 0; i < lines.length; i++) {
27
+ const m = lines[i].match(declRe);
28
+ if (m) {
29
+ const ch = lines[i].indexOf(symbolName, m.index ?? 0);
30
+ if (ch >= 0) {
31
+ return { line: i, character: ch, matchedText: lines[i] };
32
+ }
33
+ }
34
+ }
35
+ const wordRe = new RegExp(`\\b${escapeRegex(symbolName)}\\b`);
36
+ for (let i = 0; i < lines.length; i++) {
37
+ const m = lines[i].match(wordRe);
38
+ if (m) {
39
+ return {
40
+ line: i,
41
+ character: m.index ?? 0,
42
+ matchedText: lines[i],
43
+ };
44
+ }
45
+ }
46
+ return null;
47
+ }
48
+ export function escapeRegex(s) {
49
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
50
+ }
51
+ /** Extract a few lines of context around `line` for snippet display. */
52
+ export function snippetAt(filePath, line, padding = 1) {
53
+ try {
54
+ const text = readFileSync(filePath, "utf8");
55
+ const lines = text.split(/\r?\n/);
56
+ const start = Math.max(0, line - padding);
57
+ const end = Math.min(lines.length - 1, line + padding);
58
+ return lines.slice(start, end + 1).join("\n");
59
+ }
60
+ catch {
61
+ return "";
62
+ }
63
+ }
64
+ //# sourceMappingURL=_helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_helpers.js","sourceRoot":"","sources":["../../../src/tools/swift/_helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAQvC;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,qBAAqB,CACnC,QAAgB,EAChB,UAAkB;IAElB,MAAM,IAAI,GAAG,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAElC,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB,qEAAqE,WAAW,CAAC,UAAU,CAAC,KAAK,CAClG,CAAC;IACF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACjC,IAAI,CAAC,EAAE,CAAC;YACN,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC;YACtD,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;gBACZ,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,WAAW,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3D,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,MAAM,WAAW,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IAC9D,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACjC,IAAI,CAAC,EAAE,CAAC;YACN,OAAO;gBACL,IAAI,EAAE,CAAC;gBACP,SAAS,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC;gBACvB,WAAW,EAAE,KAAK,CAAC,CAAC,CAAC;aACtB,CAAC;QACJ,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,CAAS;IACnC,OAAO,CAAC,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AAClD,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,SAAS,CAAC,QAAgB,EAAE,IAAY,EAAE,OAAO,GAAG,CAAC;IACnE,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAClC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,GAAG,OAAO,CAAC,CAAC;QAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,IAAI,GAAG,OAAO,CAAC,CAAC;QACvD,OAAO,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
@@ -0,0 +1,30 @@
1
+ import { z } from "zod";
2
+ import { type SourceLocation } from "../../runtime/sourcekit/protocol.js";
3
+ export declare const swiftFindSymbolReferencesSchema: z.ZodObject<{
4
+ symbolName: z.ZodString;
5
+ filePath: z.ZodString;
6
+ projectRoot: z.ZodOptional<z.ZodString>;
7
+ includeDeclaration: z.ZodDefault<z.ZodBoolean>;
8
+ }, "strip", z.ZodTypeAny, {
9
+ symbolName: string;
10
+ filePath: string;
11
+ includeDeclaration: boolean;
12
+ projectRoot?: string | undefined;
13
+ }, {
14
+ symbolName: string;
15
+ filePath: string;
16
+ projectRoot?: string | undefined;
17
+ includeDeclaration?: boolean | undefined;
18
+ }>;
19
+ export type SwiftFindSymbolReferencesInput = z.infer<typeof swiftFindSymbolReferencesSchema>;
20
+ export interface SwiftFindSymbolReferencesResult {
21
+ ok: boolean;
22
+ symbolName: string;
23
+ totalReferences: number;
24
+ references: Array<SourceLocation & {
25
+ snippet?: string;
26
+ }>;
27
+ /** True when the IndexStoreDB was missing — references are likely incomplete. Build the index with `swift build -Xswiftc -index-store-path -Xswiftc <project>/.build/index/store`. */
28
+ needsIndex?: boolean;
29
+ }
30
+ export declare function swiftFindSymbolReferences(input: SwiftFindSymbolReferencesInput): Promise<SwiftFindSymbolReferencesResult>;
@@ -0,0 +1,56 @@
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 { lspReferences, } from "../../runtime/sourcekit/protocol.js";
6
+ import { findSymbolDeclaration, snippetAt } from "./_helpers.js";
7
+ export const swiftFindSymbolReferencesSchema = z.object({
8
+ symbolName: z
9
+ .string()
10
+ .min(1)
11
+ .describe("Name of the Swift symbol to find references for."),
12
+ filePath: z
13
+ .string()
14
+ .min(1)
15
+ .describe("Path to a Swift file where the symbol is declared. The LSP query needs a position; we locate it in this file via a regex pre-scan."),
16
+ projectRoot: z
17
+ .string()
18
+ .optional()
19
+ .describe("Override the project root. Default discovers the nearest Package.swift / .xcodeproj / .xcworkspace."),
20
+ includeDeclaration: z
21
+ .boolean()
22
+ .default(true)
23
+ .describe("Include the declaration site itself in the result set."),
24
+ });
25
+ export async function swiftFindSymbolReferences(input) {
26
+ const file = resolvePath(input.filePath);
27
+ if (!existsSync(file)) {
28
+ throw new Error(`File not found: ${file}`);
29
+ }
30
+ const root = input.projectRoot
31
+ ? resolvePath(input.projectRoot)
32
+ : projectRootFor(file);
33
+ const pos = findSymbolDeclaration(file, input.symbolName);
34
+ if (!pos) {
35
+ return {
36
+ ok: true,
37
+ symbolName: input.symbolName,
38
+ totalReferences: 0,
39
+ references: [],
40
+ };
41
+ }
42
+ const client = await acquireClient(root);
43
+ const refs = await lspReferences(client, file, pos.line, pos.character, input.includeDeclaration ?? true);
44
+ const refsWithSnippets = refs.map((r) => ({
45
+ ...r,
46
+ snippet: snippetAt(r.filePath, r.line),
47
+ }));
48
+ return {
49
+ ok: true,
50
+ symbolName: input.symbolName,
51
+ totalReferences: refs.length,
52
+ references: refsWithSnippets,
53
+ needsIndex: refs.length === 0 ? true : undefined,
54
+ };
55
+ }
56
+ //# sourceMappingURL=findSymbolReferences.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findSymbolReferences.js","sourceRoot":"","sources":["../../../src/tools/swift/findSymbolReferences.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,aAAa,GAEd,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAE,qBAAqB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAEjE,MAAM,CAAC,MAAM,+BAA+B,GAAG,CAAC,CAAC,MAAM,CAAC;IACtD,UAAU,EAAE,CAAC;SACV,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CAAC,kDAAkD,CAAC;IAC/D,QAAQ,EAAE,CAAC;SACR,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CACP,oIAAoI,CACrI;IACH,WAAW,EAAE,CAAC;SACX,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CACP,qGAAqG,CACtG;IACH,kBAAkB,EAAE,CAAC;SAClB,OAAO,EAAE;SACT,OAAO,CAAC,IAAI,CAAC;SACb,QAAQ,CAAC,wDAAwD,CAAC;CACtE,CAAC,CAAC;AAeH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,KAAqC;IAErC,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,GAAG,GAAG,qBAAqB,CAAC,IAAI,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IAC1D,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO;YACL,EAAE,EAAE,IAAI;YACR,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,eAAe,EAAE,CAAC;YAClB,UAAU,EAAE,EAAE;SACf,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,IAAI,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,MAAM,aAAa,CAC9B,MAAM,EACN,IAAI,EACJ,GAAG,CAAC,IAAI,EACR,GAAG,CAAC,SAAS,EACb,KAAK,CAAC,kBAAkB,IAAI,IAAI,CACjC,CAAC;IAEF,MAAM,gBAAgB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACxC,GAAG,CAAC;QACJ,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC;KACvC,CAAC,CAAC,CAAC;IAEJ,OAAO;QACL,EAAE,EAAE,IAAI;QACR,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,eAAe,EAAE,IAAI,CAAC,MAAM;QAC5B,UAAU,EAAE,gBAAgB;QAC5B,UAAU,EAAE,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;KACjD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,27 @@
1
+ import { z } from "zod";
2
+ export declare const swiftGetHoverInfoSchema: z.ZodObject<{
3
+ filePath: z.ZodString;
4
+ line: z.ZodNumber;
5
+ character: z.ZodNumber;
6
+ projectRoot: z.ZodOptional<z.ZodString>;
7
+ }, "strip", z.ZodTypeAny, {
8
+ line: number;
9
+ character: number;
10
+ filePath: string;
11
+ projectRoot?: string | undefined;
12
+ }, {
13
+ line: number;
14
+ character: number;
15
+ filePath: string;
16
+ projectRoot?: string | undefined;
17
+ }>;
18
+ export type SwiftGetHoverInfoInput = z.infer<typeof swiftGetHoverInfoSchema>;
19
+ export interface SwiftGetHoverInfoResult {
20
+ ok: boolean;
21
+ filePath: string;
22
+ /** Markdown / plaintext hover content from SourceKit-LSP. */
23
+ contents: string;
24
+ /** Best-effort extracted declaration fragment (e.g. "class DetailViewModel : ObservableObject"). */
25
+ typeName?: string;
26
+ }
27
+ export declare function swiftGetHoverInfo(input: SwiftGetHoverInfoInput): Promise<SwiftGetHoverInfoResult>;
@@ -0,0 +1,40 @@
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 { lspHover } from "../../runtime/sourcekit/protocol.js";
6
+ export const swiftGetHoverInfoSchema = z.object({
7
+ filePath: z.string().min(1).describe("Absolute path to a Swift source file."),
8
+ line: z
9
+ .number()
10
+ .int()
11
+ .nonnegative()
12
+ .describe("Zero-based line number (LSP convention)."),
13
+ character: z
14
+ .number()
15
+ .int()
16
+ .nonnegative()
17
+ .describe("Zero-based UTF-16 character offset within the line."),
18
+ projectRoot: z.string().optional(),
19
+ });
20
+ export async function swiftGetHoverInfo(input) {
21
+ const file = resolvePath(input.filePath);
22
+ if (!existsSync(file)) {
23
+ throw new Error(`File not found: ${file}`);
24
+ }
25
+ const root = input.projectRoot
26
+ ? resolvePath(input.projectRoot)
27
+ : projectRootFor(file);
28
+ const client = await acquireClient(root);
29
+ const result = await lspHover(client, file, input.line, input.character);
30
+ const contents = result?.contents ?? "";
31
+ const typeName = extractTypeName(contents);
32
+ return { ok: true, filePath: file, contents, typeName };
33
+ }
34
+ function extractTypeName(hover) {
35
+ // Hover output usually leads with a code fence containing the
36
+ // declaration line (e.g. "let foo: Bar" or "class Baz : Quux").
37
+ const m = hover.match(/\b(class|struct|enum|protocol|actor|func|var|let)\s+\S+/);
38
+ return m ? m[0] : undefined;
39
+ }
40
+ //# sourceMappingURL=getHoverInfo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getHoverInfo.js","sourceRoot":"","sources":["../../../src/tools/swift/getHoverInfo.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,EAAE,QAAQ,EAAE,MAAM,qCAAqC,CAAC;AAE/D,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,uCAAuC,CAAC;IAC7E,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,GAAG,EAAE;SACL,WAAW,EAAE;SACb,QAAQ,CAAC,0CAA0C,CAAC;IACvD,SAAS,EAAE,CAAC;SACT,MAAM,EAAE;SACR,GAAG,EAAE;SACL,WAAW,EAAE;SACb,QAAQ,CAAC,qDAAqD,CAAC;IAClE,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACnC,CAAC,CAAC;AAaH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,KAA6B;IAE7B,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;IACzB,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,IAAI,CAAC,CAAC;IACzC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IACzE,MAAM,QAAQ,GAAG,MAAM,EAAE,QAAQ,IAAI,EAAE,CAAC;IACxC,MAAM,QAAQ,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;IAC3C,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;AAC1D,CAAC;AAED,SAAS,eAAe,CAAC,KAAa;IACpC,8DAA8D;IAC9D,gEAAgE;IAChE,MAAM,CAAC,GAAG,KAAK,CAAC,KAAK,CACnB,yDAAyD,CAC1D,CAAC;IACF,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC9B,CAAC"}
@@ -0,0 +1,46 @@
1
+ import { z } from "zod";
2
+ import { type SourceLocation } from "../../runtime/sourcekit/protocol.js";
3
+ export declare const swiftGetSymbolDefinitionSchema: z.ZodObject<{
4
+ symbolName: z.ZodString;
5
+ hint: z.ZodOptional<z.ZodObject<{
6
+ filePath: z.ZodOptional<z.ZodString>;
7
+ module: z.ZodOptional<z.ZodString>;
8
+ }, "strip", z.ZodTypeAny, {
9
+ module?: string | undefined;
10
+ filePath?: string | undefined;
11
+ }, {
12
+ module?: string | undefined;
13
+ filePath?: string | undefined;
14
+ }>>;
15
+ projectRoot: z.ZodOptional<z.ZodString>;
16
+ candidatePaths: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
17
+ }, "strip", z.ZodTypeAny, {
18
+ symbolName: string;
19
+ hint?: {
20
+ module?: string | undefined;
21
+ filePath?: string | undefined;
22
+ } | undefined;
23
+ projectRoot?: string | undefined;
24
+ candidatePaths?: string[] | undefined;
25
+ }, {
26
+ symbolName: string;
27
+ hint?: {
28
+ module?: string | undefined;
29
+ filePath?: string | undefined;
30
+ } | undefined;
31
+ projectRoot?: string | undefined;
32
+ candidatePaths?: string[] | undefined;
33
+ }>;
34
+ export type SwiftGetSymbolDefinitionInput = z.infer<typeof swiftGetSymbolDefinitionSchema>;
35
+ export interface SwiftGetSymbolDefinitionResult {
36
+ ok: boolean;
37
+ symbolName: string;
38
+ /** Definition locations returned by SourceKit-LSP (or pre-scan when LSP returns nothing). */
39
+ definitions: SourceLocation[];
40
+ /** When set, indicates we located the symbol via filename pre-scan rather than a true LSP query. */
41
+ preScanHit?: {
42
+ filePath: string;
43
+ matchedText: string;
44
+ };
45
+ }
46
+ export declare function swiftGetSymbolDefinition(input: SwiftGetSymbolDefinitionInput): Promise<SwiftGetSymbolDefinitionResult>;
@@ -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":"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,aAAa,GAEd,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAEtD,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,CAAC,MAAM,CAAC;IACrD,UAAU,EAAE,CAAC;SACV,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CACP,sFAAsF,CACvF;IACH,IAAI,EAAE,CAAC;SACJ,MAAM,CAAC;QACN,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC/B,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;KAC9B,CAAC;SACD,QAAQ,EAAE;SACV,QAAQ,CACP,6HAA6H,CAC9H;IACH,WAAW,EAAE,CAAC;SACX,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CACP,kHAAkH,CACnH;IACH,cAAc,EAAE,CAAC;SACd,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;SACjB,QAAQ,EAAE;SACV,QAAQ,CACP,6MAA6M,CAC9M;CACJ,CAAC,CAAC;AAeH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,KAAoC;IAEpC,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW;QAC5B,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,WAAW,CAAC;QAChC,CAAC,CAAC,cAAc,CACZ,KAAK,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI,EAAE,QAAQ,IAAI,OAAO,CAAC,GAAG,EAAE,CACnE,CAAC;IACN,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,2BAA2B,IAAI,EAAE,CAAC,CAAC;IACrD,CAAC;IAED,MAAM,cAAc,GAAG;QACrB,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACtD,GAAG,CAAC,KAAK,CAAC,cAAc,IAAI,EAAE,CAAC;KAChC,CAAC;IAEF,KAAK,MAAM,EAAE,IAAI,cAAc,EAAE,CAAC;QAChC,MAAM,GAAG,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;QAC5B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QAC/B,MAAM,GAAG,GAAG,qBAAqB,CAAC,GAAG,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;QACzD,IAAI,GAAG,EAAE,CAAC;YACR,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,IAAI,CAAC,CAAC;YACzC,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC;YACvE,OAAO;gBACL,EAAE,EAAE,IAAI;gBACR,UAAU,EAAE,KAAK,CAAC,UAAU;gBAC5B,WAAW,EACT,IAAI,CAAC,MAAM,GAAG,CAAC;oBACb,CAAC,CAAC,IAAI;oBACN,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE,CAAC;gBACnE,UAAU,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,WAAW,EAAE,GAAG,CAAC,WAAW,CAAC,IAAI,EAAE,EAAE;aACnE,CAAC;QACJ,CAAC;IACH,CAAC;IAED,4EAA4E;IAC5E,yEAAyE;IACzE,sEAAsE;IACtE,qBAAqB;IACrB,OAAO;QACL,EAAE,EAAE,IAAI;QACR,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,WAAW,EAAE,EAAE;QACf,UAAU,EAAE,SAAS;KACtB,CAAC;AACJ,CAAC"}
@@ -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.0.1",
3
+ "version": "1.2.0",
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": {
@@ -11,6 +11,7 @@
11
11
  "dist",
12
12
  "README.md",
13
13
  "LICENSE",
14
+ "NOTICE",
14
15
  "CHANGELOG.md",
15
16
  "USAGE.md"
16
17
  ],
@@ -35,7 +36,7 @@
35
36
  "performance"
36
37
  ],
37
38
  "author": "Carlos Henrique",
38
- "license": "MIT",
39
+ "license": "Apache-2.0",
39
40
  "homepage": "https://github.com/carloshpdoc/memorydetective#readme",
40
41
  "repository": {
41
42
  "type": "git",
@@ -50,6 +51,8 @@
50
51
  "dependencies": {
51
52
  "@modelcontextprotocol/sdk": "^1.0.0",
52
53
  "fast-xml-parser": "^5.7.2",
54
+ "vscode-jsonrpc": "^8.2.1",
55
+ "vscode-languageserver-protocol": "^3.17.5",
53
56
  "zod": "^3.23.0"
54
57
  },
55
58
  "devDependencies": {