@mortice/mapwright 0.8.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 (166) hide show
  1. package/CHANGELOG.md +161 -0
  2. package/LICENSE +21 -0
  3. package/README.md +262 -0
  4. package/dist/catalogue/canonicalisation.d.ts +36 -0
  5. package/dist/catalogue/canonicalisation.js +262 -0
  6. package/dist/catalogue/config.d.ts +13 -0
  7. package/dist/catalogue/config.js +77 -0
  8. package/dist/catalogue/deterministic.d.ts +3 -0
  9. package/dist/catalogue/deterministic.js +25 -0
  10. package/dist/catalogue/diagnostics.d.ts +2 -0
  11. package/dist/catalogue/diagnostics.js +34 -0
  12. package/dist/catalogue/evidence.d.ts +1 -0
  13. package/dist/catalogue/evidence.js +10 -0
  14. package/dist/catalogue/examples.d.ts +3 -0
  15. package/dist/catalogue/examples.js +20 -0
  16. package/dist/catalogue/identity.d.ts +61 -0
  17. package/dist/catalogue/identity.js +114 -0
  18. package/dist/catalogue/ignore.d.ts +4 -0
  19. package/dist/catalogue/ignore.js +29 -0
  20. package/dist/catalogue/indexer.d.ts +20 -0
  21. package/dist/catalogue/indexer.js +1393 -0
  22. package/dist/catalogue/linkage.d.ts +16 -0
  23. package/dist/catalogue/linkage.js +419 -0
  24. package/dist/catalogue/moduleResolution.d.ts +4 -0
  25. package/dist/catalogue/moduleResolution.js +67 -0
  26. package/dist/catalogue/ordering.d.ts +13 -0
  27. package/dist/catalogue/ordering.js +86 -0
  28. package/dist/catalogue/packages.d.ts +12 -0
  29. package/dist/catalogue/packages.js +475 -0
  30. package/dist/catalogue/pythonReExports.d.ts +7 -0
  31. package/dist/catalogue/pythonReExports.js +113 -0
  32. package/dist/catalogue/resolveRefs.d.ts +40 -0
  33. package/dist/catalogue/resolveRefs.js +284 -0
  34. package/dist/catalogue/scanner.d.ts +8 -0
  35. package/dist/catalogue/scanner.js +99 -0
  36. package/dist/catalogue/schema.d.ts +289 -0
  37. package/dist/catalogue/schema.js +1 -0
  38. package/dist/catalogue/store.d.ts +41 -0
  39. package/dist/catalogue/store.js +209 -0
  40. package/dist/catalogue/templates.d.ts +18 -0
  41. package/dist/catalogue/templates.js +158 -0
  42. package/dist/catalogue/version.d.ts +12 -0
  43. package/dist/catalogue/version.js +30 -0
  44. package/dist/cli/commands/adoptionPlan.d.ts +8 -0
  45. package/dist/cli/commands/adoptionPlan.js +24 -0
  46. package/dist/cli/commands/auditDocs.d.ts +8 -0
  47. package/dist/cli/commands/auditDocs.js +24 -0
  48. package/dist/cli/commands/changeImpact.d.ts +10 -0
  49. package/dist/cli/commands/changeImpact.js +36 -0
  50. package/dist/cli/commands/changes.d.ts +12 -0
  51. package/dist/cli/commands/changes.js +31 -0
  52. package/dist/cli/commands/check.d.ts +9 -0
  53. package/dist/cli/commands/check.js +4 -0
  54. package/dist/cli/commands/compatibility.d.ts +10 -0
  55. package/dist/cli/commands/compatibility.js +43 -0
  56. package/dist/cli/commands/diff.d.ts +31 -0
  57. package/dist/cli/commands/diff.js +118 -0
  58. package/dist/cli/commands/dogfood.d.ts +8 -0
  59. package/dist/cli/commands/dogfood.js +24 -0
  60. package/dist/cli/commands/drift.d.ts +8 -0
  61. package/dist/cli/commands/drift.js +13 -0
  62. package/dist/cli/commands/index.d.ts +9 -0
  63. package/dist/cli/commands/index.js +9 -0
  64. package/dist/cli/commands/indexWorkspace.d.ts +8 -0
  65. package/dist/cli/commands/indexWorkspace.js +23 -0
  66. package/dist/cli/commands/recommendConfig.d.ts +7 -0
  67. package/dist/cli/commands/recommendConfig.js +20 -0
  68. package/dist/cli/commands/schema.d.ts +1 -0
  69. package/dist/cli/commands/schema.js +37 -0
  70. package/dist/cli/commands/template.d.ts +5 -0
  71. package/dist/cli/commands/template.js +44 -0
  72. package/dist/cli/commands/validate.d.ts +9 -0
  73. package/dist/cli/commands/validate.js +85 -0
  74. package/dist/cli/index.d.ts +2 -0
  75. package/dist/cli/index.js +211 -0
  76. package/dist/comparison/artefactValidate.d.ts +19 -0
  77. package/dist/comparison/artefactValidate.js +334 -0
  78. package/dist/comparison/candidates.d.ts +12 -0
  79. package/dist/comparison/candidates.js +391 -0
  80. package/dist/comparison/compare.d.ts +15 -0
  81. package/dist/comparison/compare.js +240 -0
  82. package/dist/comparison/compatibility.d.ts +26 -0
  83. package/dist/comparison/compatibility.js +316 -0
  84. package/dist/comparison/drift.d.ts +34 -0
  85. package/dist/comparison/drift.js +71 -0
  86. package/dist/comparison/identity.d.ts +30 -0
  87. package/dist/comparison/identity.js +73 -0
  88. package/dist/comparison/impact.d.ts +14 -0
  89. package/dist/comparison/impact.js +291 -0
  90. package/dist/comparison/index.d.ts +6 -0
  91. package/dist/comparison/index.js +6 -0
  92. package/dist/comparison/integrity.d.ts +12 -0
  93. package/dist/comparison/integrity.js +199 -0
  94. package/dist/comparison/ordering.d.ts +4 -0
  95. package/dist/comparison/ordering.js +69 -0
  96. package/dist/comparison/projections.d.ts +19 -0
  97. package/dist/comparison/projections.js +185 -0
  98. package/dist/comparison/relationshipPolicy.d.ts +19 -0
  99. package/dist/comparison/relationshipPolicy.js +98 -0
  100. package/dist/comparison/schema.d.ts +178 -0
  101. package/dist/comparison/schema.js +16 -0
  102. package/dist/comparison/store.d.ts +58 -0
  103. package/dist/comparison/store.js +462 -0
  104. package/dist/comparison/validate.d.ts +23 -0
  105. package/dist/comparison/validate.js +137 -0
  106. package/dist/comparison/version.d.ts +9 -0
  107. package/dist/comparison/version.js +11 -0
  108. package/dist/extractors/docTags.d.ts +9 -0
  109. package/dist/extractors/docTags.js +106 -0
  110. package/dist/extractors/generic.d.ts +5 -0
  111. package/dist/extractors/generic.js +3 -0
  112. package/dist/extractors/javascript.d.ts +3 -0
  113. package/dist/extractors/javascript.js +4 -0
  114. package/dist/extractors/markdown.d.ts +7 -0
  115. package/dist/extractors/markdown.js +269 -0
  116. package/dist/extractors/python.d.ts +7 -0
  117. package/dist/extractors/python.js +314 -0
  118. package/dist/extractors/typescript.d.ts +7 -0
  119. package/dist/extractors/typescript.js +387 -0
  120. package/dist/index.d.ts +2 -0
  121. package/dist/index.js +2 -0
  122. package/dist/mcp/resources.d.ts +18 -0
  123. package/dist/mcp/resources.js +57 -0
  124. package/dist/mcp/server.d.ts +2 -0
  125. package/dist/mcp/server.js +95 -0
  126. package/dist/mcp/tools.d.ts +29 -0
  127. package/dist/mcp/tools.js +902 -0
  128. package/dist/queries/adoption.d.ts +62 -0
  129. package/dist/queries/adoption.js +366 -0
  130. package/dist/queries/capabilities.d.ts +45 -0
  131. package/dist/queries/capabilities.js +124 -0
  132. package/dist/queries/changes.d.ts +33 -0
  133. package/dist/queries/changes.js +53 -0
  134. package/dist/queries/dependencies.d.ts +17 -0
  135. package/dist/queries/dependencies.js +94 -0
  136. package/dist/queries/dogfood.d.ts +50 -0
  137. package/dist/queries/dogfood.js +116 -0
  138. package/dist/queries/editingContext.d.ts +84 -0
  139. package/dist/queries/editingContext.js +240 -0
  140. package/dist/queries/examples.d.ts +8 -0
  141. package/dist/queries/examples.js +16 -0
  142. package/dist/queries/files.d.ts +8 -0
  143. package/dist/queries/files.js +14 -0
  144. package/dist/queries/lists.d.ts +94 -0
  145. package/dist/queries/lists.js +184 -0
  146. package/dist/queries/overview.d.ts +2 -0
  147. package/dist/queries/overview.js +8 -0
  148. package/dist/queries/publicApis.d.ts +60 -0
  149. package/dist/queries/publicApis.js +120 -0
  150. package/dist/queries/ranking.d.ts +67 -0
  151. package/dist/queries/ranking.js +387 -0
  152. package/dist/queries/recommendConfig.d.ts +47 -0
  153. package/dist/queries/recommendConfig.js +123 -0
  154. package/dist/queries/relationships.d.ts +5 -0
  155. package/dist/queries/relationships.js +15 -0
  156. package/dist/queries/reuseCandidates.d.ts +34 -0
  157. package/dist/queries/reuseCandidates.js +181 -0
  158. package/dist/queries/symbols.d.ts +40 -0
  159. package/dist/queries/symbols.js +41 -0
  160. package/dist/security/pathPolicy.d.ts +12 -0
  161. package/dist/security/pathPolicy.js +46 -0
  162. package/dist/security/secretPatterns.d.ts +1 -0
  163. package/dist/security/secretPatterns.js +4 -0
  164. package/dist/security/sourceAccess.d.ts +12 -0
  165. package/dist/security/sourceAccess.js +35 -0
  166. package/package.json +60 -0
@@ -0,0 +1,387 @@
1
+ import ts from "typescript";
2
+ import path from "node:path";
3
+ import { readFileSync } from "node:fs";
4
+ import { parseDocTags } from "./docTags.js";
5
+ import { createExternalPackageId, resolveTypeScriptModuleTarget } from "../catalogue/moduleResolution.js";
6
+ import { truncateEvidenceText } from "../catalogue/evidence.js";
7
+ import { implementationFingerprint, relationshipIdentityId, signatureFingerprint, symbolIdentityId } from "../catalogue/identity.js";
8
+ export function extractTypeScriptSymbols(workspaceRoot, file, packageRecord) {
9
+ const absolute = path.resolve(workspaceRoot, file.path);
10
+ const sourceText = readFileSync(absolute, "utf8");
11
+ const sourceFile = ts.createSourceFile(file.path, sourceText, ts.ScriptTarget.ES2022, true);
12
+ const symbols = [];
13
+ const relationships = [];
14
+ const diagnostics = [];
15
+ const symbolIdsByName = new Map();
16
+ const packageId = packageRecord?.id ?? "package:unknown";
17
+ const modulePath = file.path;
18
+ const symbolProvenance = file.isGenerated ? "generated_declaration" : "authored_source";
19
+ const importStatements = [];
20
+ const exportStatements = [];
21
+ const pushSymbol = (node, kind, name, exported, parentId, containerPath = "") => {
22
+ const jsDoc = ts.getJSDocCommentsAndTags(node)
23
+ .map((n) => n.getText(sourceFile))
24
+ .join("\n");
25
+ const parsed = jsDoc ? parseDocTags(jsDoc, file.path) : { summary: "", tags: [], diagnostics: [] };
26
+ diagnostics.push(...parsed.diagnostics);
27
+ const start = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
28
+ const end = sourceFile.getLineAndCharacterOfPosition(node.getEnd());
29
+ const nodeText = node.getText(sourceFile);
30
+ const signature = nodeText.split(/\r?\n/)[0].trim().slice(0, 300);
31
+ const symbolId = symbolIdentityId({
32
+ language: file.language,
33
+ kind,
34
+ packageId,
35
+ modulePath,
36
+ qualifiedContainerPath: containerPath,
37
+ declaredName: name
38
+ });
39
+ const symbol = {
40
+ id: symbolId,
41
+ name,
42
+ qualifiedName: `${file.path}::${name}`,
43
+ kind,
44
+ language: file.language,
45
+ filePath: file.path,
46
+ packageId,
47
+ modulePath,
48
+ qualifiedContainerPath: containerPath,
49
+ range: {
50
+ startLine: start.line + 1,
51
+ startColumn: start.character + 1,
52
+ endLine: end.line + 1,
53
+ endColumn: end.character + 1
54
+ },
55
+ signature,
56
+ signatureFingerprint: signatureFingerprint([kind, signature]),
57
+ implementationFingerprint: implementationFingerprint([kind, nodeText]),
58
+ canonicalSymbolId: symbolId,
59
+ duplicateOf: null,
60
+ symbolProvenance,
61
+ visibility: exported ? "published" : "private",
62
+ exported,
63
+ doc: {
64
+ present: Boolean(jsDoc),
65
+ text: parsed.summary,
66
+ tags: parsed.tags
67
+ },
68
+ capabilities: parsed.tags.filter((t) => t.tag === "capability").map((t) => t.value),
69
+ parents: parentId ? [parentId] : [],
70
+ children: [],
71
+ confidence: 0.9,
72
+ provenance: {
73
+ source: "typescript-ast",
74
+ extractors: ["typescript"]
75
+ }
76
+ };
77
+ symbols.push(symbol);
78
+ const currentIds = symbolIdsByName.get(name) ?? [];
79
+ currentIds.push(symbolId);
80
+ symbolIdsByName.set(name, currentIds.sort());
81
+ file.symbols.push(symbolId);
82
+ relationships.push({
83
+ id: relationshipIdentityId({
84
+ type: "defines",
85
+ fromKind: "file",
86
+ fromId: file.path,
87
+ toKind: "symbol",
88
+ toId: symbolId,
89
+ discriminant: null
90
+ }),
91
+ type: "defines",
92
+ from: { kind: "file", id: file.path },
93
+ to: { kind: "symbol", id: symbolId },
94
+ confidence: 1,
95
+ provenance: { source: "typescript-ast" }
96
+ });
97
+ if (parentId) {
98
+ const parent = symbols.find((item) => item.id === parentId);
99
+ parent?.children.push(symbolId);
100
+ relationships.push({
101
+ id: relationshipIdentityId({
102
+ type: "contains",
103
+ fromKind: "symbol",
104
+ fromId: parentId,
105
+ toKind: "symbol",
106
+ toId: symbolId,
107
+ discriminant: null
108
+ }),
109
+ type: "contains",
110
+ from: { kind: "symbol", id: parentId },
111
+ to: { kind: "symbol", id: symbolId },
112
+ confidence: 0.95,
113
+ provenance: { source: "typescript-ast" }
114
+ });
115
+ }
116
+ if (exported) {
117
+ relationships.push({
118
+ id: relationshipIdentityId({
119
+ type: "exports",
120
+ fromKind: "file",
121
+ fromId: file.path,
122
+ toKind: "symbol",
123
+ toId: symbolId,
124
+ discriminant: null
125
+ }),
126
+ type: "exports",
127
+ from: { kind: "file", id: file.path },
128
+ to: { kind: "symbol", id: symbolId },
129
+ evidence: {
130
+ filePath: file.path,
131
+ startLine: start.line + 1,
132
+ endLine: end.line + 1,
133
+ text: truncateEvidenceText(symbol.signature)
134
+ },
135
+ confidence: 0.95,
136
+ provenance: { source: "typescript-ast" }
137
+ });
138
+ }
139
+ return symbolId;
140
+ };
141
+ const childContainerPath = (parent) => {
142
+ return parent.qualifiedContainerPath ? `${parent.qualifiedContainerPath}.${parent.name}` : parent.name;
143
+ };
144
+ const visit = (node, parentId, containerPath = "") => {
145
+ if (ts.isFunctionDeclaration(node) && node.name) {
146
+ pushSymbol(node, "function", node.name.text, hasExport(node), parentId, containerPath);
147
+ }
148
+ else if (ts.isClassDeclaration(node) && node.name) {
149
+ const classId = pushSymbol(node, "class", node.name.text, hasExport(node), parentId, containerPath);
150
+ const classSymbol = symbols.find((item) => item.id === classId);
151
+ const memberContainerPath = classSymbol ? childContainerPath(classSymbol) : node.name.text;
152
+ for (const member of node.members) {
153
+ if ((ts.isMethodDeclaration(member) || ts.isPropertyDeclaration(member)) && member.name) {
154
+ const memberName = member.name.getText(sourceFile);
155
+ pushSymbol(member, ts.isMethodDeclaration(member) ? "method" : "variable", memberName, false, classId, memberContainerPath);
156
+ }
157
+ }
158
+ }
159
+ else if (ts.isInterfaceDeclaration(node)) {
160
+ pushSymbol(node, "interface", node.name.text, hasExport(node), parentId, containerPath);
161
+ }
162
+ else if (ts.isTypeAliasDeclaration(node)) {
163
+ pushSymbol(node, "type", node.name.text, hasExport(node), parentId, containerPath);
164
+ }
165
+ else if (ts.isEnumDeclaration(node)) {
166
+ pushSymbol(node, "enum", node.name.text, hasExport(node), parentId, containerPath);
167
+ }
168
+ else if (ts.isVariableStatement(node)) {
169
+ const exported = hasExport(node);
170
+ for (const decl of node.declarationList.declarations) {
171
+ if (ts.isIdentifier(decl.name)) {
172
+ pushSymbol(decl, "constant", decl.name.text, exported, parentId, containerPath);
173
+ }
174
+ }
175
+ }
176
+ else if (ts.isImportDeclaration(node)) {
177
+ const moduleSpecifier = ts.isStringLiteral(node.moduleSpecifier) ? node.moduleSpecifier.text : undefined;
178
+ if (moduleSpecifier) {
179
+ const start = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
180
+ const end = sourceFile.getLineAndCharacterOfPosition(node.getEnd());
181
+ const importKind = node.importClause?.isTypeOnly
182
+ ? "type"
183
+ : node.importClause?.namedBindings && ts.isNamespaceImport(node.importClause.namedBindings)
184
+ ? "namespace"
185
+ : node.importClause
186
+ ? "static"
187
+ : "side-effect";
188
+ importStatements.push({
189
+ moduleSpecifier,
190
+ startLine: start.line + 1,
191
+ endLine: end.line + 1,
192
+ text: node.getText(sourceFile),
193
+ importKind
194
+ });
195
+ }
196
+ }
197
+ else if (ts.isExportDeclaration(node)) {
198
+ const moduleSpecifier = node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier) ? node.moduleSpecifier.text : undefined;
199
+ const start = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
200
+ const end = sourceFile.getLineAndCharacterOfPosition(node.getEnd());
201
+ const exportedNames = node.exportClause && ts.isNamedExports(node.exportClause)
202
+ ? node.exportClause.elements.map((element) => (element.propertyName ?? element.name).text)
203
+ : node.exportClause && ts.isNamespaceExport(node.exportClause)
204
+ ? [node.exportClause.name.text]
205
+ : [];
206
+ exportStatements.push({
207
+ moduleSpecifier,
208
+ startLine: start.line + 1,
209
+ endLine: end.line + 1,
210
+ text: node.getText(sourceFile),
211
+ exportedNames
212
+ });
213
+ }
214
+ else if (ts.isExportAssignment(node)) {
215
+ const start = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
216
+ const end = sourceFile.getLineAndCharacterOfPosition(node.getEnd());
217
+ const targetId = ts.isIdentifier(node.expression) ? symbolIdsByName.get(node.expression.text)?.[0] : undefined;
218
+ const to = targetId ? { kind: "symbol", id: targetId } : { kind: "file", id: file.path };
219
+ relationships.push({
220
+ id: relationshipIdentityId({
221
+ type: "exports",
222
+ fromKind: "file",
223
+ fromId: file.path,
224
+ toKind: to.kind,
225
+ toId: to.id,
226
+ discriminant: "default"
227
+ }),
228
+ type: "exports",
229
+ from: { kind: "file", id: file.path },
230
+ to,
231
+ localDiscriminant: "default",
232
+ evidence: {
233
+ filePath: file.path,
234
+ startLine: start.line + 1,
235
+ endLine: end.line + 1,
236
+ text: truncateEvidenceText(node.getText(sourceFile))
237
+ },
238
+ confidence: 0.8,
239
+ provenance: { source: "typescript-ast" }
240
+ });
241
+ }
242
+ ts.forEachChild(node, (child) => visit(child, parentId, containerPath));
243
+ };
244
+ visit(sourceFile);
245
+ for (const statement of importStatements) {
246
+ const localTarget = resolveTypeScriptModuleTarget(workspaceRoot, file.path, statement.moduleSpecifier);
247
+ if (statement.moduleSpecifier.startsWith(".")) {
248
+ if (localTarget) {
249
+ relationships.push({
250
+ id: relationshipIdentityId({
251
+ type: "imports",
252
+ fromKind: "file",
253
+ fromId: file.path,
254
+ toKind: "file",
255
+ toId: localTarget,
256
+ discriminant: statement.importKind
257
+ }),
258
+ type: "imports",
259
+ from: { kind: "file", id: file.path },
260
+ to: { kind: "file", id: localTarget },
261
+ localDiscriminant: statement.importKind,
262
+ evidence: { filePath: file.path, startLine: statement.startLine, endLine: statement.endLine, text: truncateEvidenceText(statement.text) },
263
+ confidence: 0.95,
264
+ provenance: { source: "typescript-ast" }
265
+ });
266
+ }
267
+ else {
268
+ diagnostics.push({
269
+ id: `diag:${file.path}:${statement.startLine}:unresolved_import:${statement.moduleSpecifier}`,
270
+ severity: "warning",
271
+ category: "resolution",
272
+ code: "unresolved_import",
273
+ message: `Unresolved local import ${statement.moduleSpecifier}.`,
274
+ filePath: file.path,
275
+ range: { startLine: statement.startLine, endLine: statement.endLine },
276
+ target: { kind: "file", id: file.path },
277
+ provenance: { source: "typescript-ast" }
278
+ });
279
+ }
280
+ continue;
281
+ }
282
+ const externalId = createExternalPackageId(statement.moduleSpecifier);
283
+ relationships.push({
284
+ id: relationshipIdentityId({
285
+ type: "imports",
286
+ fromKind: "file",
287
+ fromId: file.path,
288
+ toKind: "externalPackage",
289
+ toId: externalId,
290
+ discriminant: statement.importKind
291
+ }),
292
+ type: "imports",
293
+ from: { kind: "file", id: file.path },
294
+ to: { kind: "externalPackage", id: externalId },
295
+ localDiscriminant: statement.importKind,
296
+ evidence: { filePath: file.path, startLine: statement.startLine, endLine: statement.endLine, text: truncateEvidenceText(statement.text) },
297
+ confidence: 0.8,
298
+ provenance: { source: "typescript-ast" }
299
+ });
300
+ }
301
+ for (const statement of exportStatements) {
302
+ if (statement.moduleSpecifier) {
303
+ const localTarget = resolveTypeScriptModuleTarget(workspaceRoot, file.path, statement.moduleSpecifier);
304
+ if (statement.moduleSpecifier.startsWith(".")) {
305
+ if (localTarget) {
306
+ relationships.push({
307
+ id: relationshipIdentityId({
308
+ type: "exports",
309
+ fromKind: "file",
310
+ fromId: file.path,
311
+ toKind: "file",
312
+ toId: localTarget,
313
+ discriminant: "re-export"
314
+ }),
315
+ type: "exports",
316
+ from: { kind: "file", id: file.path },
317
+ to: { kind: "file", id: localTarget },
318
+ localDiscriminant: "re-export",
319
+ evidence: { filePath: file.path, startLine: statement.startLine, endLine: statement.endLine, text: truncateEvidenceText(statement.text) },
320
+ confidence: 0.9,
321
+ provenance: { source: "typescript-ast" }
322
+ });
323
+ }
324
+ else {
325
+ diagnostics.push({
326
+ id: `diag:${file.path}:${statement.startLine}:unresolved_import:${statement.moduleSpecifier}`,
327
+ severity: "warning",
328
+ category: "resolution",
329
+ code: "unresolved_import",
330
+ message: `Unresolved local export source ${statement.moduleSpecifier}.`,
331
+ filePath: file.path,
332
+ range: { startLine: statement.startLine, endLine: statement.endLine },
333
+ target: { kind: "file", id: file.path },
334
+ provenance: { source: "typescript-ast" }
335
+ });
336
+ }
337
+ }
338
+ else {
339
+ const externalId = createExternalPackageId(statement.moduleSpecifier);
340
+ relationships.push({
341
+ id: relationshipIdentityId({
342
+ type: "exports",
343
+ fromKind: "file",
344
+ fromId: file.path,
345
+ toKind: "externalPackage",
346
+ toId: externalId,
347
+ discriminant: "re-export"
348
+ }),
349
+ type: "exports",
350
+ from: { kind: "file", id: file.path },
351
+ to: { kind: "externalPackage", id: externalId },
352
+ localDiscriminant: "re-export",
353
+ evidence: { filePath: file.path, startLine: statement.startLine, endLine: statement.endLine, text: truncateEvidenceText(statement.text) },
354
+ confidence: 0.75,
355
+ provenance: { source: "typescript-ast" }
356
+ });
357
+ }
358
+ continue;
359
+ }
360
+ for (const exportedName of statement.exportedNames) {
361
+ const matchingIds = symbolIdsByName.get(exportedName) ?? [];
362
+ for (const symbolId of matchingIds) {
363
+ relationships.push({
364
+ id: relationshipIdentityId({
365
+ type: "exports",
366
+ fromKind: "file",
367
+ fromId: file.path,
368
+ toKind: "symbol",
369
+ toId: symbolId,
370
+ discriminant: "named"
371
+ }),
372
+ type: "exports",
373
+ from: { kind: "file", id: file.path },
374
+ to: { kind: "symbol", id: symbolId },
375
+ localDiscriminant: "named",
376
+ evidence: { filePath: file.path, startLine: statement.startLine, endLine: statement.endLine, text: truncateEvidenceText(statement.text) },
377
+ confidence: 0.9,
378
+ provenance: { source: "typescript-ast" }
379
+ });
380
+ }
381
+ }
382
+ }
383
+ return { symbols, relationships, diagnostics };
384
+ }
385
+ function hasExport(node) {
386
+ return Boolean(ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Export);
387
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./catalogue/indexer.js";
2
+ export * from "./catalogue/schema.js";
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export * from "./catalogue/indexer.js";
2
+ export * from "./catalogue/schema.js";
@@ -0,0 +1,18 @@
1
+ import type { CatalogueSnapshot } from "../catalogue/schema.js";
2
+ import type { ComparisonSnapshot } from "../comparison/schema.js";
3
+ export declare const RESOURCE_URIS: {
4
+ readonly overview: "mapwright://workspace/overview";
5
+ readonly files: "mapwright://files";
6
+ readonly symbols: "mapwright://symbols";
7
+ readonly capabilities: "mapwright://capabilities";
8
+ readonly publicApis: "mapwright://public-apis";
9
+ readonly examples: "mapwright://examples";
10
+ readonly packages: "mapwright://packages";
11
+ readonly relationships: "mapwright://relationships";
12
+ readonly diagnostics: "mapwright://diagnostics";
13
+ readonly comparisonSummary: "mapwright://comparison/summary";
14
+ readonly comparisonChanges: "mapwright://comparison/changes";
15
+ readonly comparisonCompatibility: "mapwright://comparison/compatibility";
16
+ };
17
+ export declare function readResource(snapshot: CatalogueSnapshot, uri: string): unknown;
18
+ export declare function readComparisonResource(comparison: ComparisonSnapshot | undefined, uri: string): unknown;
@@ -0,0 +1,57 @@
1
+ import { getWorkspaceOverview } from "../queries/overview.js";
2
+ import { selectCapabilities, selectDiagnostics, selectExamples, selectFiles, selectPackages, selectPublicApis, selectRelationships, selectSymbols, toResourceEnvelope } from "../queries/lists.js";
3
+ import { getChangeSummary, getCompatibilityReport, listChanges } from "../queries/changes.js";
4
+ import { comparisonNotLoadedFailure } from "./tools.js";
5
+ export const RESOURCE_URIS = {
6
+ overview: "mapwright://workspace/overview",
7
+ files: "mapwright://files",
8
+ symbols: "mapwright://symbols",
9
+ capabilities: "mapwright://capabilities",
10
+ publicApis: "mapwright://public-apis",
11
+ examples: "mapwright://examples",
12
+ packages: "mapwright://packages",
13
+ relationships: "mapwright://relationships",
14
+ diagnostics: "mapwright://diagnostics",
15
+ comparisonSummary: "mapwright://comparison/summary",
16
+ comparisonChanges: "mapwright://comparison/changes",
17
+ comparisonCompatibility: "mapwright://comparison/compatibility"
18
+ };
19
+ export function readResource(snapshot, uri) {
20
+ switch (uri) {
21
+ case RESOURCE_URIS.overview:
22
+ return getWorkspaceOverview(snapshot);
23
+ case RESOURCE_URIS.files:
24
+ return toResourceEnvelope("files", selectFiles(snapshot));
25
+ case RESOURCE_URIS.symbols:
26
+ return toResourceEnvelope("symbols", selectSymbols(snapshot));
27
+ case RESOURCE_URIS.capabilities:
28
+ return toResourceEnvelope("capabilities", selectCapabilities(snapshot));
29
+ case RESOURCE_URIS.publicApis:
30
+ return toResourceEnvelope("publicApis", selectPublicApis(snapshot));
31
+ case RESOURCE_URIS.examples:
32
+ return toResourceEnvelope("examples", selectExamples(snapshot));
33
+ case RESOURCE_URIS.packages:
34
+ return toResourceEnvelope("packages", selectPackages(snapshot));
35
+ case RESOURCE_URIS.relationships:
36
+ return toResourceEnvelope("relationships", selectRelationships(snapshot));
37
+ case RESOURCE_URIS.diagnostics:
38
+ return toResourceEnvelope("diagnostics", selectDiagnostics(snapshot));
39
+ default:
40
+ return { error: "unknown_resource" };
41
+ }
42
+ }
43
+ export function readComparisonResource(comparison, uri) {
44
+ if (!comparison) {
45
+ return comparisonNotLoadedFailure();
46
+ }
47
+ switch (uri) {
48
+ case RESOURCE_URIS.comparisonSummary:
49
+ return getChangeSummary(comparison);
50
+ case RESOURCE_URIS.comparisonChanges:
51
+ return listChanges(comparison, { offset: 0, limit: 50 });
52
+ case RESOURCE_URIS.comparisonCompatibility:
53
+ return getCompatibilityReport(comparison, { offset: 0, limit: 50 });
54
+ default:
55
+ return { error: "unknown_resource" };
56
+ }
57
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export declare function startServer(): Promise<void>;
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env node
2
+ import { fileURLToPath } from "node:url";
3
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
+ import { catalogueNotLoadedFailure, registerTools } from "./tools.js";
6
+ import { RESOURCE_URIS, readResource, readComparisonResource } from "./resources.js";
7
+ import { MAPWRIGHT_GENERATOR_NAME, MAPWRIGHT_GENERATOR_VERSION } from "../catalogue/version.js";
8
+ export async function startServer() {
9
+ let snapshot;
10
+ let comparison;
11
+ const readLoadedResource = (uri) => {
12
+ if (!snapshot) {
13
+ return catalogueNotLoadedFailure();
14
+ }
15
+ return readResource(snapshot, uri);
16
+ };
17
+ const readLoadedComparison = (uri) => readComparisonResource(comparison, uri);
18
+ const server = new McpServer({
19
+ name: MAPWRIGHT_GENERATOR_NAME,
20
+ version: MAPWRIGHT_GENERATOR_VERSION
21
+ }, {
22
+ capabilities: {
23
+ tools: {},
24
+ resources: {}
25
+ },
26
+ instructions: "Mortice Mapwright provides an agent-readable catalogue of files, symbols, capabilities, public APIs, relationships, tests, and diagnostics."
27
+ });
28
+ registerTools(server, {
29
+ onSnapshotChange: (nextSnapshot) => {
30
+ snapshot = nextSnapshot;
31
+ },
32
+ onComparisonChange: (nextComparison) => {
33
+ comparison = nextComparison;
34
+ }
35
+ });
36
+ server.registerResource("workspace-overview", RESOURCE_URIS.overview, { mimeType: "application/json" }, async () => ({
37
+ contents: [{ uri: RESOURCE_URIS.overview, text: JSON.stringify(readLoadedResource(RESOURCE_URIS.overview)) }]
38
+ }));
39
+ server.registerResource("files", RESOURCE_URIS.files, { mimeType: "application/json" }, async () => ({
40
+ contents: [{ uri: RESOURCE_URIS.files, text: JSON.stringify(readLoadedResource(RESOURCE_URIS.files)) }]
41
+ }));
42
+ server.registerResource("symbols", RESOURCE_URIS.symbols, { mimeType: "application/json" }, async () => ({
43
+ contents: [{ uri: RESOURCE_URIS.symbols, text: JSON.stringify(readLoadedResource(RESOURCE_URIS.symbols)) }]
44
+ }));
45
+ server.registerResource("capabilities", RESOURCE_URIS.capabilities, { mimeType: "application/json" }, async () => ({
46
+ contents: [{ uri: RESOURCE_URIS.capabilities, text: JSON.stringify(readLoadedResource(RESOURCE_URIS.capabilities)) }]
47
+ }));
48
+ server.registerResource("public-apis", RESOURCE_URIS.publicApis, { mimeType: "application/json" }, async () => ({
49
+ contents: [{ uri: RESOURCE_URIS.publicApis, text: JSON.stringify(readLoadedResource(RESOURCE_URIS.publicApis)) }]
50
+ }));
51
+ server.registerResource("examples", RESOURCE_URIS.examples, { mimeType: "application/json" }, async () => ({
52
+ contents: [{ uri: RESOURCE_URIS.examples, text: JSON.stringify(readLoadedResource(RESOURCE_URIS.examples)) }]
53
+ }));
54
+ server.registerResource("packages", RESOURCE_URIS.packages, { mimeType: "application/json" }, async () => ({
55
+ contents: [{ uri: RESOURCE_URIS.packages, text: JSON.stringify(readLoadedResource(RESOURCE_URIS.packages)) }]
56
+ }));
57
+ server.registerResource("relationships", RESOURCE_URIS.relationships, { mimeType: "application/json" }, async () => ({
58
+ contents: [{ uri: RESOURCE_URIS.relationships, text: JSON.stringify(readLoadedResource(RESOURCE_URIS.relationships)) }]
59
+ }));
60
+ server.registerResource("diagnostics", RESOURCE_URIS.diagnostics, { mimeType: "application/json" }, async () => ({
61
+ contents: [{ uri: RESOURCE_URIS.diagnostics, text: JSON.stringify(readLoadedResource(RESOURCE_URIS.diagnostics)) }]
62
+ }));
63
+ server.registerResource("comparison-summary", RESOURCE_URIS.comparisonSummary, { mimeType: "application/json" }, async () => ({
64
+ contents: [
65
+ {
66
+ uri: RESOURCE_URIS.comparisonSummary,
67
+ text: JSON.stringify(readLoadedComparison(RESOURCE_URIS.comparisonSummary))
68
+ }
69
+ ]
70
+ }));
71
+ server.registerResource("comparison-changes", RESOURCE_URIS.comparisonChanges, { mimeType: "application/json" }, async () => ({
72
+ contents: [
73
+ {
74
+ uri: RESOURCE_URIS.comparisonChanges,
75
+ text: JSON.stringify(readLoadedComparison(RESOURCE_URIS.comparisonChanges))
76
+ }
77
+ ]
78
+ }));
79
+ server.registerResource("comparison-compatibility", RESOURCE_URIS.comparisonCompatibility, { mimeType: "application/json" }, async () => ({
80
+ contents: [
81
+ {
82
+ uri: RESOURCE_URIS.comparisonCompatibility,
83
+ text: JSON.stringify(readLoadedComparison(RESOURCE_URIS.comparisonCompatibility))
84
+ }
85
+ ]
86
+ }));
87
+ const transport = new StdioServerTransport();
88
+ await server.connect(transport);
89
+ }
90
+ if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
91
+ startServer().catch((error) => {
92
+ process.stderr.write(`Mapwright MCP server failed: ${String(error)}\n`);
93
+ process.exit(1);
94
+ });
95
+ }
@@ -0,0 +1,29 @@
1
+ import { z } from "zod";
2
+ import type { CatalogueSnapshot } from "../catalogue/schema.js";
3
+ import { type ComparisonSnapshot } from "../comparison/schema.js";
4
+ export declare const CATALOGUE_NOT_LOADED_CODE = "catalogue_not_loaded";
5
+ export declare const CATALOGUE_NOT_LOADED_MESSAGE = "Run load_committed_catalogue or index_workspace first.";
6
+ export declare const COMPARISON_NOT_LOADED_CODE = "comparison_not_loaded";
7
+ export declare const COMPARISON_NOT_LOADED_MESSAGE = "Run compare_catalogues or load_comparison first.";
8
+ export declare function success(data: unknown, warnings?: string[]): Record<string, unknown>;
9
+ export declare function failure(code: string, message: string): Record<string, unknown>;
10
+ export declare function catalogueNotLoadedFailure(): Record<string, unknown>;
11
+ export declare function comparisonNotLoadedFailure(): Record<string, unknown>;
12
+ export declare function catalogueNotLoadedContent(): {
13
+ content: Array<{
14
+ type: "text";
15
+ text: string;
16
+ }>;
17
+ };
18
+ export interface RegisterToolsOptions {
19
+ initialSnapshot?: CatalogueSnapshot;
20
+ initialWorkspace?: string;
21
+ onSnapshotChange?: (snapshot: CatalogueSnapshot, workspaceRoot: string) => void;
22
+ onComparisonChange?: (comparison: ComparisonSnapshot | undefined) => void;
23
+ }
24
+ export declare function registerTools(server: {
25
+ registerTool: (name: string, config: {
26
+ description?: string;
27
+ inputSchema?: Record<string, z.ZodTypeAny>;
28
+ }, cb: (args: any) => Promise<any>) => void;
29
+ }, options?: RegisterToolsOptions): void;