@mgamil/mapx 0.2.4

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 (203) hide show
  1. package/LICENSE +194 -0
  2. package/README.md +488 -0
  3. package/VERSION +1 -0
  4. package/dist/agents/generator.d.ts +74 -0
  5. package/dist/agents/generator.js +375 -0
  6. package/dist/agents/templates.d.ts +29 -0
  7. package/dist/agents/templates.js +459 -0
  8. package/dist/cli.d.ts +16 -0
  9. package/dist/cli.js +1835 -0
  10. package/dist/core/cluster-engine.d.ts +32 -0
  11. package/dist/core/cluster-engine.js +314 -0
  12. package/dist/core/config.d.ts +29 -0
  13. package/dist/core/config.js +178 -0
  14. package/dist/core/context-builder.d.ts +61 -0
  15. package/dist/core/context-builder.js +252 -0
  16. package/dist/core/flow-tracer.d.ts +63 -0
  17. package/dist/core/flow-tracer.js +366 -0
  18. package/dist/core/git-tracker.d.ts +20 -0
  19. package/dist/core/git-tracker.js +159 -0
  20. package/dist/core/graph.d.ts +42 -0
  21. package/dist/core/graph.js +186 -0
  22. package/dist/core/metrics.d.ts +24 -0
  23. package/dist/core/metrics.js +87 -0
  24. package/dist/core/scanner.d.ts +53 -0
  25. package/dist/core/scanner.js +949 -0
  26. package/dist/core/store-bun.d.ts +13 -0
  27. package/dist/core/store-bun.js +34 -0
  28. package/dist/core/store-interface.d.ts +15 -0
  29. package/dist/core/store-interface.js +7 -0
  30. package/dist/core/store-node.d.ts +13 -0
  31. package/dist/core/store-node.js +35 -0
  32. package/dist/core/store.d.ts +132 -0
  33. package/dist/core/store.js +614 -0
  34. package/dist/core/workspace-manager.d.ts +9 -0
  35. package/dist/core/workspace-manager.js +64 -0
  36. package/dist/exporters/dot-exporter.d.ts +16 -0
  37. package/dist/exporters/dot-exporter.js +179 -0
  38. package/dist/exporters/graph-exporter.d.ts +14 -0
  39. package/dist/exporters/graph-exporter.js +85 -0
  40. package/dist/exporters/index.d.ts +9 -0
  41. package/dist/exporters/index.js +12 -0
  42. package/dist/exporters/llm-exporter.d.ts +18 -0
  43. package/dist/exporters/llm-exporter.js +224 -0
  44. package/dist/exporters/svg-exporter.d.ts +19 -0
  45. package/dist/exporters/svg-exporter.js +319 -0
  46. package/dist/exporters/toon-exporter.d.ts +16 -0
  47. package/dist/exporters/toon-exporter.js +246 -0
  48. package/dist/frameworks/detectors/aspnet.d.ts +11 -0
  49. package/dist/frameworks/detectors/aspnet.js +52 -0
  50. package/dist/frameworks/detectors/django.d.ts +14 -0
  51. package/dist/frameworks/detectors/django.js +135 -0
  52. package/dist/frameworks/detectors/drupal.d.ts +13 -0
  53. package/dist/frameworks/detectors/drupal.js +94 -0
  54. package/dist/frameworks/detectors/express.d.ts +12 -0
  55. package/dist/frameworks/detectors/express.js +234 -0
  56. package/dist/frameworks/detectors/fastapi.d.ts +12 -0
  57. package/dist/frameworks/detectors/fastapi.js +203 -0
  58. package/dist/frameworks/detectors/flask.d.ts +12 -0
  59. package/dist/frameworks/detectors/flask.js +244 -0
  60. package/dist/frameworks/detectors/go.d.ts +11 -0
  61. package/dist/frameworks/detectors/go.js +75 -0
  62. package/dist/frameworks/detectors/laravel.d.ts +11 -0
  63. package/dist/frameworks/detectors/laravel.js +462 -0
  64. package/dist/frameworks/detectors/nestjs.d.ts +12 -0
  65. package/dist/frameworks/detectors/nestjs.js +155 -0
  66. package/dist/frameworks/detectors/nextjs.d.ts +11 -0
  67. package/dist/frameworks/detectors/nextjs.js +118 -0
  68. package/dist/frameworks/detectors/rails.d.ts +12 -0
  69. package/dist/frameworks/detectors/rails.js +76 -0
  70. package/dist/frameworks/detectors/react-router.d.ts +11 -0
  71. package/dist/frameworks/detectors/react-router.js +115 -0
  72. package/dist/frameworks/detectors/rust.d.ts +11 -0
  73. package/dist/frameworks/detectors/rust.js +59 -0
  74. package/dist/frameworks/detectors/spring.d.ts +11 -0
  75. package/dist/frameworks/detectors/spring.js +56 -0
  76. package/dist/frameworks/detectors/sveltekit.d.ts +11 -0
  77. package/dist/frameworks/detectors/sveltekit.js +154 -0
  78. package/dist/frameworks/detectors/symfony.d.ts +13 -0
  79. package/dist/frameworks/detectors/symfony.js +175 -0
  80. package/dist/frameworks/detectors/tanstack-router.d.ts +12 -0
  81. package/dist/frameworks/detectors/tanstack-router.js +80 -0
  82. package/dist/frameworks/detectors/vapor.d.ts +11 -0
  83. package/dist/frameworks/detectors/vapor.js +52 -0
  84. package/dist/frameworks/detectors/vue-router.d.ts +12 -0
  85. package/dist/frameworks/detectors/vue-router.js +237 -0
  86. package/dist/frameworks/detectors/wordpress.d.ts +13 -0
  87. package/dist/frameworks/detectors/wordpress.js +141 -0
  88. package/dist/frameworks/detectors/yii.d.ts +11 -0
  89. package/dist/frameworks/detectors/yii.js +131 -0
  90. package/dist/frameworks/framework-registry.d.ts +13 -0
  91. package/dist/frameworks/framework-registry.js +77 -0
  92. package/dist/frameworks/route-registry.d.ts +26 -0
  93. package/dist/frameworks/route-registry.js +102 -0
  94. package/dist/index.d.ts +19 -0
  95. package/dist/index.js +30 -0
  96. package/dist/languages/index.d.ts +2 -0
  97. package/dist/languages/index.js +7 -0
  98. package/dist/languages/installer.d.ts +13 -0
  99. package/dist/languages/installer.js +103 -0
  100. package/dist/languages/registry.d.ts +19 -0
  101. package/dist/languages/registry.js +427 -0
  102. package/dist/main.d.ts +2 -0
  103. package/dist/main.js +20 -0
  104. package/dist/mcp.d.ts +11 -0
  105. package/dist/mcp.js +1699 -0
  106. package/dist/parsers/common-methods.d.ts +3 -0
  107. package/dist/parsers/common-methods.js +33 -0
  108. package/dist/parsers/fallback-parser.d.ts +10 -0
  109. package/dist/parsers/fallback-parser.js +18 -0
  110. package/dist/parsers/generic-wasm-parser.d.ts +23 -0
  111. package/dist/parsers/generic-wasm-parser.js +168 -0
  112. package/dist/parsers/ignored-symbols.d.ts +26 -0
  113. package/dist/parsers/ignored-symbols.js +77 -0
  114. package/dist/parsers/index.d.ts +9 -0
  115. package/dist/parsers/index.js +13 -0
  116. package/dist/parsers/languages/javascript.d.ts +11 -0
  117. package/dist/parsers/languages/javascript.js +28 -0
  118. package/dist/parsers/languages/php.d.ts +15 -0
  119. package/dist/parsers/languages/php.js +648 -0
  120. package/dist/parsers/languages/typescript.d.ts +10 -0
  121. package/dist/parsers/languages/typescript.js +9 -0
  122. package/dist/parsers/languages/vue.d.ts +13 -0
  123. package/dist/parsers/languages/vue.js +63 -0
  124. package/dist/parsers/parse-worker.d.ts +2 -0
  125. package/dist/parsers/parse-worker.js +185 -0
  126. package/dist/parsers/parser-interface.d.ts +9 -0
  127. package/dist/parsers/parser-interface.js +0 -0
  128. package/dist/parsers/parser-registry.d.ts +8 -0
  129. package/dist/parsers/parser-registry.js +52 -0
  130. package/dist/parsers/wasm-parser.d.ts +16 -0
  131. package/dist/parsers/wasm-parser.js +110 -0
  132. package/dist/types.d.ts +172 -0
  133. package/dist/types.js +0 -0
  134. package/dist/ui/index.html +270 -0
  135. package/dist/ui/main.js +581 -0
  136. package/dist/ui/main.js.map +7 -0
  137. package/dist/ui/styles.css +573 -0
  138. package/dist/ui-events.d.ts +36 -0
  139. package/dist/ui-events.js +61 -0
  140. package/dist/ui-server.d.ts +12 -0
  141. package/dist/ui-server.js +504 -0
  142. package/package.json +179 -0
  143. package/queries/bash/references.scm +22 -0
  144. package/queries/bash/symbols.scm +15 -0
  145. package/queries/c/references.scm +14 -0
  146. package/queries/c/symbols.scm +30 -0
  147. package/queries/c-sharp/references.scm +26 -0
  148. package/queries/c-sharp/symbols.scm +57 -0
  149. package/queries/cpp/references.scm +21 -0
  150. package/queries/cpp/symbols.scm +44 -0
  151. package/queries/dart/references.scm +33 -0
  152. package/queries/dart/symbols.scm +38 -0
  153. package/queries/elixir/references.scm +45 -0
  154. package/queries/elixir/symbols.scm +41 -0
  155. package/queries/go/references.scm +22 -0
  156. package/queries/go/symbols.scm +53 -0
  157. package/queries/java/references.scm +32 -0
  158. package/queries/java/symbols.scm +41 -0
  159. package/queries/javascript/references.scm +14 -0
  160. package/queries/javascript/symbols.scm +23 -0
  161. package/queries/kotlin/references.scm +31 -0
  162. package/queries/kotlin/symbols.scm +24 -0
  163. package/queries/lua/references.scm +19 -0
  164. package/queries/lua/symbols.scm +29 -0
  165. package/queries/pascal/references.scm +29 -0
  166. package/queries/pascal/symbols.scm +45 -0
  167. package/queries/php/references.scm +109 -0
  168. package/queries/php/symbols.scm +33 -0
  169. package/queries/python/references.scm +50 -0
  170. package/queries/python/symbols.scm +21 -0
  171. package/queries/ruby/references.scm +48 -0
  172. package/queries/ruby/symbols.scm +24 -0
  173. package/queries/rust/references.scm +31 -0
  174. package/queries/rust/symbols.scm +35 -0
  175. package/queries/scala/references.scm +30 -0
  176. package/queries/scala/symbols.scm +35 -0
  177. package/queries/svelte/references.scm +20 -0
  178. package/queries/svelte/symbols.scm +30 -0
  179. package/queries/swift/references.scm +22 -0
  180. package/queries/swift/symbols.scm +37 -0
  181. package/queries/typescript/references.scm +25 -0
  182. package/queries/typescript/symbols.scm +35 -0
  183. package/queries/vue/references.scm +20 -0
  184. package/queries/vue/symbols.scm +28 -0
  185. package/queries/zig/references.scm +20 -0
  186. package/queries/zig/symbols.scm +22 -0
  187. package/wasm/tree-sitter-c.wasm +0 -0
  188. package/wasm/tree-sitter-c_sharp.wasm +0 -0
  189. package/wasm/tree-sitter-cpp.wasm +0 -0
  190. package/wasm/tree-sitter-dart.wasm +0 -0
  191. package/wasm/tree-sitter-go.wasm +0 -0
  192. package/wasm/tree-sitter-java.wasm +0 -0
  193. package/wasm/tree-sitter-javascript.wasm +0 -0
  194. package/wasm/tree-sitter-kotlin.wasm +0 -0
  195. package/wasm/tree-sitter-php.wasm +0 -0
  196. package/wasm/tree-sitter-python.wasm +0 -0
  197. package/wasm/tree-sitter-ruby.wasm +0 -0
  198. package/wasm/tree-sitter-rust.wasm +0 -0
  199. package/wasm/tree-sitter-scala.wasm +0 -0
  200. package/wasm/tree-sitter-swift.wasm +0 -0
  201. package/wasm/tree-sitter-tsx.wasm +0 -0
  202. package/wasm/tree-sitter-typescript.wasm +0 -0
  203. package/wasm/tree-sitter-vue.wasm +0 -0
@@ -0,0 +1,32 @@
1
+ import { Store } from './store.js';
2
+ import './store-interface.js';
3
+ import './graph.js';
4
+ import '../types.js';
5
+
6
+ type ClusterSource = 'namespace' | 'directory' | 'community';
7
+ interface Cluster {
8
+ name: string;
9
+ label: string;
10
+ source: ClusterSource;
11
+ parentName: string | null;
12
+ depth: number;
13
+ fileCount: number;
14
+ }
15
+ interface ClusterResult {
16
+ clustersFound: number;
17
+ namespaceClusters: number;
18
+ directoryClusters: number;
19
+ communityClusters: number;
20
+ filesAssigned: number;
21
+ durationMs: number;
22
+ }
23
+ declare class ClusterEngine {
24
+ private store;
25
+ constructor(store: Store);
26
+ detect(repo: string): ClusterResult;
27
+ private detectNamespaceClusters;
28
+ private detectDirectoryClusters;
29
+ private detectCommunityClusters;
30
+ }
31
+
32
+ export { type Cluster, ClusterEngine, type ClusterResult, type ClusterSource };
@@ -0,0 +1,314 @@
1
+ class ClusterEngine {
2
+ constructor(store) {
3
+ this.store = store;
4
+ }
5
+ store;
6
+ detect(repo) {
7
+ const startTime = Date.now();
8
+ const nsRes = this.detectNamespaceClusters(repo);
9
+ const dirRes = this.detectDirectoryClusters(repo, nsRes.memberships);
10
+ const allPrimaryMemberships = /* @__PURE__ */ new Map();
11
+ for (const [f, c] of nsRes.memberships.entries()) {
12
+ allPrimaryMemberships.set(f, c);
13
+ }
14
+ for (const [f, c] of dirRes.memberships.entries()) {
15
+ allPrimaryMemberships.set(f, c);
16
+ }
17
+ const primaryClustersMap = /* @__PURE__ */ new Map();
18
+ for (const c of nsRes.clusters) {
19
+ primaryClustersMap.set(c.name, c);
20
+ }
21
+ for (const c of dirRes.clusters) {
22
+ if (!primaryClustersMap.has(c.name)) {
23
+ primaryClustersMap.set(c.name, c);
24
+ }
25
+ }
26
+ const primaryFileCounts = /* @__PURE__ */ new Map();
27
+ for (const clusterName of allPrimaryMemberships.values()) {
28
+ let current = clusterName;
29
+ while (current) {
30
+ primaryFileCounts.set(current, (primaryFileCounts.get(current) || 0) + 1);
31
+ const idx = current.lastIndexOf(".");
32
+ current = idx !== -1 ? current.substring(0, idx) : null;
33
+ }
34
+ }
35
+ const primaryClusters = Array.from(primaryClustersMap.values()).map((c) => ({
36
+ ...c,
37
+ fileCount: primaryFileCounts.get(c.name) || 0
38
+ }));
39
+ const clusterFileSets = /* @__PURE__ */ new Map();
40
+ for (const c of primaryClusters) {
41
+ clusterFileSets.set(c.name, /* @__PURE__ */ new Set());
42
+ }
43
+ for (const [filePath, clusterName] of allPrimaryMemberships.entries()) {
44
+ let current = clusterName;
45
+ while (current) {
46
+ if (clusterFileSets.has(current)) {
47
+ clusterFileSets.get(current).add(filePath);
48
+ }
49
+ const idx = current.lastIndexOf(".");
50
+ current = idx !== -1 ? current.substring(0, idx) : null;
51
+ }
52
+ }
53
+ const files = this.store.getAllFiles(repo);
54
+ const edges = this.store.getAllEdges(repo);
55
+ const commRes = this.detectCommunityClusters(repo, files, edges, clusterFileSets);
56
+ this.store.inTransaction(() => {
57
+ this.store.clearClusters(repo);
58
+ for (const c of primaryClusters) {
59
+ this.store.insertCluster({
60
+ repo,
61
+ name: c.name,
62
+ label: c.label,
63
+ source: c.source,
64
+ parentName: c.parentName,
65
+ depth: c.depth,
66
+ fileCount: c.fileCount
67
+ });
68
+ }
69
+ for (const [filePath, clusterName] of allPrimaryMemberships.entries()) {
70
+ this.store.insertClusterMembership({
71
+ filePath,
72
+ clusterName,
73
+ repo,
74
+ isPrimary: 1
75
+ });
76
+ }
77
+ for (const c of commRes.clusters) {
78
+ this.store.insertCluster({
79
+ repo,
80
+ name: c.name,
81
+ label: c.label,
82
+ source: c.source,
83
+ parentName: c.parentName,
84
+ depth: c.depth,
85
+ fileCount: c.fileCount
86
+ });
87
+ }
88
+ for (const m of commRes.memberships) {
89
+ this.store.insertClusterMembership({
90
+ filePath: m.filePath,
91
+ clusterName: m.clusterName,
92
+ repo,
93
+ isPrimary: 0
94
+ });
95
+ }
96
+ });
97
+ const namespaceCount = primaryClusters.filter((c) => c.source === "namespace").length;
98
+ const directoryCount = primaryClusters.filter((c) => c.source === "directory").length;
99
+ return {
100
+ clustersFound: primaryClusters.length + commRes.clusters.length,
101
+ namespaceClusters: namespaceCount,
102
+ directoryClusters: directoryCount,
103
+ communityClusters: commRes.clusters.length,
104
+ filesAssigned: allPrimaryMemberships.size,
105
+ durationMs: Date.now() - startTime
106
+ };
107
+ }
108
+ detectNamespaceClusters(repo) {
109
+ const files = this.store.getAllFiles(repo);
110
+ const clustersMap = /* @__PURE__ */ new Map();
111
+ const memberships = /* @__PURE__ */ new Map();
112
+ for (const f of files) {
113
+ let ns = f.namespace || null;
114
+ if (!ns && f.metadata) {
115
+ try {
116
+ const meta = JSON.parse(f.metadata);
117
+ ns = meta.namespace || null;
118
+ } catch {
119
+ }
120
+ }
121
+ if (ns) {
122
+ const normalized = ns.replace(/\\/g, ".").replace(/^\.|\.$/g, "");
123
+ if (!normalized) continue;
124
+ memberships.set(f.path, normalized);
125
+ let current = normalized;
126
+ while (current) {
127
+ if (!clustersMap.has(current)) {
128
+ const parts = current.split(".");
129
+ const parentName = parts.slice(0, -1).join(".") || null;
130
+ const depth = parts.length - 1;
131
+ clustersMap.set(current, {
132
+ name: current,
133
+ label: parts.join(" / "),
134
+ source: "namespace",
135
+ parentName,
136
+ depth,
137
+ fileCount: 0
138
+ });
139
+ }
140
+ const idx = current.lastIndexOf(".");
141
+ current = idx !== -1 ? current.substring(0, idx) : "";
142
+ }
143
+ }
144
+ }
145
+ return { clusters: Array.from(clustersMap.values()), memberships };
146
+ }
147
+ detectDirectoryClusters(repo, existingPrimaryMemberships) {
148
+ const files = this.store.getAllFiles(repo);
149
+ const dirFileCount = /* @__PURE__ */ new Map();
150
+ for (const f of files) {
151
+ const filePath = f.path;
152
+ const parts = filePath.split("/");
153
+ if (parts.length <= 1) continue;
154
+ for (let i = 1; i < parts.length; i++) {
155
+ const dirPath = parts.slice(0, i).join("/");
156
+ if (!dirFileCount.has(dirPath)) {
157
+ dirFileCount.set(dirPath, /* @__PURE__ */ new Set());
158
+ }
159
+ dirFileCount.get(dirPath).add(filePath);
160
+ }
161
+ }
162
+ const clustersMap = /* @__PURE__ */ new Map();
163
+ const memberships = /* @__PURE__ */ new Map();
164
+ for (const [dirPath, fileSet] of dirFileCount.entries()) {
165
+ if (fileSet.size >= 2) {
166
+ const clusterName = dirPath.replace(/\//g, ".");
167
+ const parts = clusterName.split(".");
168
+ const parentName = parts.slice(0, -1).join(".") || null;
169
+ const depth = parts.length;
170
+ clustersMap.set(clusterName, {
171
+ name: clusterName,
172
+ label: parts.join(" / "),
173
+ source: "directory",
174
+ parentName,
175
+ depth,
176
+ fileCount: 0
177
+ });
178
+ }
179
+ }
180
+ for (const f of files) {
181
+ const filePath = f.path;
182
+ if (existingPrimaryMemberships.has(filePath)) continue;
183
+ const parts = filePath.split("/");
184
+ if (parts.length <= 1) continue;
185
+ for (let i = parts.length - 1; i >= 1; i--) {
186
+ const dirPath = parts.slice(0, i).join("/");
187
+ const clusterName = dirPath.replace(/\//g, ".");
188
+ if (clustersMap.has(clusterName)) {
189
+ memberships.set(filePath, clusterName);
190
+ break;
191
+ }
192
+ }
193
+ }
194
+ return { clusters: Array.from(clustersMap.values()), memberships };
195
+ }
196
+ detectCommunityClusters(repo, files, edges, existingClusterFileSets) {
197
+ const filesList = files.map((f) => f.path).sort();
198
+ const weights = /* @__PURE__ */ new Map();
199
+ const addWeight = (u, v, w) => {
200
+ if (!weights.has(u)) weights.set(u, /* @__PURE__ */ new Map());
201
+ const uMap = weights.get(u);
202
+ uMap.set(v, (uMap.get(v) || 0) + w);
203
+ };
204
+ for (const e of edges) {
205
+ const src = e.source_file;
206
+ const tgt = e.target_file;
207
+ if (src === tgt) continue;
208
+ addWeight(src, tgt, 1);
209
+ addWeight(tgt, src, 1);
210
+ }
211
+ const labels = /* @__PURE__ */ new Map();
212
+ for (const node of filesList) {
213
+ labels.set(node, node);
214
+ }
215
+ const seededShuffle = (array, seed) => {
216
+ const result = [...array];
217
+ let currentSeed = seed;
218
+ const random = () => {
219
+ currentSeed = (currentSeed * 9301 + 49297) % 233280;
220
+ return currentSeed / 233280;
221
+ };
222
+ for (let i = result.length - 1; i > 0; i--) {
223
+ const j = Math.floor(random() * (i + 1));
224
+ const temp = result[i];
225
+ result[i] = result[j];
226
+ result[j] = temp;
227
+ }
228
+ return result;
229
+ };
230
+ const maxIterations = 10;
231
+ let changed = true;
232
+ for (let iter = 0; iter < maxIterations && changed; iter++) {
233
+ changed = false;
234
+ const shuffledNodes = seededShuffle(filesList, 42 + iter);
235
+ for (const node of shuffledNodes) {
236
+ const neighbors = weights.get(node);
237
+ if (!neighbors || neighbors.size === 0) continue;
238
+ const labelWeights = /* @__PURE__ */ new Map();
239
+ for (const [neighbor, weight] of neighbors.entries()) {
240
+ const nLabel = labels.get(neighbor);
241
+ labelWeights.set(nLabel, (labelWeights.get(nLabel) || 0) + weight);
242
+ }
243
+ let maxLabel = labels.get(node);
244
+ let maxW = 0;
245
+ const sortedLabels = Array.from(labelWeights.keys()).sort();
246
+ for (const label of sortedLabels) {
247
+ const w = labelWeights.get(label);
248
+ if (w > maxW) {
249
+ maxW = w;
250
+ maxLabel = label;
251
+ } else if (w === maxW) {
252
+ if (label < maxLabel) {
253
+ maxLabel = label;
254
+ }
255
+ }
256
+ }
257
+ if (labels.get(node) !== maxLabel) {
258
+ labels.set(node, maxLabel);
259
+ changed = true;
260
+ }
261
+ }
262
+ }
263
+ const communityGroups = /* @__PURE__ */ new Map();
264
+ for (const [node, label] of labels.entries()) {
265
+ if (!communityGroups.has(label)) {
266
+ communityGroups.set(label, []);
267
+ }
268
+ communityGroups.get(label).push(node);
269
+ }
270
+ const clusters = [];
271
+ const memberships = [];
272
+ let communityIndex = 1;
273
+ const sortedGroupKeys = Array.from(communityGroups.keys()).sort();
274
+ for (const key of sortedGroupKeys) {
275
+ const filePaths = communityGroups.get(key);
276
+ if (filePaths.length < 3) continue;
277
+ const commFileSet = new Set(filePaths);
278
+ let overlaps = false;
279
+ for (const fileSet of existingClusterFileSets.values()) {
280
+ if (fileSet.size === commFileSet.size) {
281
+ let allMatch = true;
282
+ for (const f of filePaths) {
283
+ if (!fileSet.has(f)) {
284
+ allMatch = false;
285
+ break;
286
+ }
287
+ }
288
+ if (allMatch) {
289
+ overlaps = true;
290
+ break;
291
+ }
292
+ }
293
+ }
294
+ if (!overlaps) {
295
+ const commName = `community_${communityIndex++}`;
296
+ clusters.push({
297
+ name: commName,
298
+ label: `Community ${commName.split("_")[1]}`,
299
+ source: "community",
300
+ parentName: null,
301
+ depth: 0,
302
+ fileCount: filePaths.length
303
+ });
304
+ for (const f of filePaths) {
305
+ memberships.push({ filePath: f, clusterName: commName });
306
+ }
307
+ }
308
+ }
309
+ return { clusters, memberships };
310
+ }
311
+ }
312
+ export {
313
+ ClusterEngine
314
+ };
@@ -0,0 +1,29 @@
1
+ import { RepoConfig, UserLanguageDefinition } from '../types.js';
2
+ import { LanguageDefinition } from '../languages/registry.js';
3
+
4
+ declare class Config {
5
+ private configPath;
6
+ private config;
7
+ private constructor();
8
+ static load(workspaceRoot: string): Promise<Config>;
9
+ static init(workspaceRoot: string, repoName?: string, isLaravel?: boolean, shouldAddLaravelExcludes?: boolean): Promise<Config>;
10
+ save(): Promise<void>;
11
+ get repos(): RepoConfig[];
12
+ get settings(): {
13
+ [key: string]: any;
14
+ maxTokenBudget: number;
15
+ excludePatterns: string[];
16
+ includePatterns: string[];
17
+ php?: {
18
+ facadeMap?: Record<string, string>;
19
+ };
20
+ };
21
+ get languages(): Record<string, UserLanguageDefinition>;
22
+ getResolvedUserLanguages(): Record<string, LanguageDefinition>;
23
+ addRepo(name: string, path: string): void;
24
+ removeRepo(nameOrPath: string): void;
25
+ getWorkspaceRoot(): string;
26
+ get repo(): RepoConfig;
27
+ }
28
+
29
+ export { Config };
@@ -0,0 +1,178 @@
1
+ import { readFile, writeFile, mkdir, readdir } from "node:fs/promises";
2
+ import { existsSync } from "node:fs";
3
+ import { resolve, join } from "node:path";
4
+ import { getBuiltinLanguages } from "../languages/registry.js";
5
+ const DEFAULT_CONFIG = {
6
+ version: "1.0.0",
7
+ repos: [],
8
+ languages: {},
9
+ settings: {
10
+ maxTokenBudget: 16384,
11
+ excludePatterns: [
12
+ "node_modules/**",
13
+ "vendor/**",
14
+ ".git/**",
15
+ "dist/**",
16
+ ".mapx/**",
17
+ "*.min.js",
18
+ "*.min.css",
19
+ "package-lock.json",
20
+ "composer.lock"
21
+ ],
22
+ includePatterns: []
23
+ }
24
+ };
25
+ class Config {
26
+ configPath;
27
+ config;
28
+ constructor(configPath, config) {
29
+ this.configPath = configPath;
30
+ this.config = config;
31
+ }
32
+ static async load(workspaceRoot) {
33
+ const configPath = join(workspaceRoot, ".mapx", "config.json");
34
+ const mapxDir = join(workspaceRoot, ".mapx");
35
+ if (!existsSync(configPath)) {
36
+ await mkdir(mapxDir, { recursive: true });
37
+ const defaultConfig = {
38
+ ...DEFAULT_CONFIG,
39
+ repos: [{
40
+ name: resolve(workspaceRoot).split("/").pop() || "default",
41
+ path: "."
42
+ }]
43
+ };
44
+ await writeFile(configPath, JSON.stringify(defaultConfig, null, 2), "utf-8");
45
+ return new Config(configPath, defaultConfig);
46
+ }
47
+ const data = await readFile(configPath, "utf-8");
48
+ const config = JSON.parse(data);
49
+ return new Config(configPath, config);
50
+ }
51
+ static async init(workspaceRoot, repoName, isLaravel = false, shouldAddLaravelExcludes = false) {
52
+ const mapxDir = join(workspaceRoot, ".mapx");
53
+ await mkdir(mapxDir, { recursive: true });
54
+ const configPath = join(mapxDir, "config.json");
55
+ let existing = null;
56
+ if (existsSync(configPath)) {
57
+ try {
58
+ existing = JSON.parse(await readFile(configPath, "utf-8"));
59
+ } catch {
60
+ existing = null;
61
+ }
62
+ }
63
+ const defaultExclude = [...DEFAULT_CONFIG.settings.excludePatterns];
64
+ if (shouldAddLaravelExcludes) {
65
+ const LARAVEL_DEFAULT_EXCLUDES = [
66
+ "database/migrations/**",
67
+ "database/seeders/**",
68
+ "database/factories/**",
69
+ "storage/**",
70
+ "bootstrap/cache/**",
71
+ "public/**",
72
+ "resources/views/**",
73
+ "**/*.blade.php",
74
+ "vendor/**"
75
+ ];
76
+ for (const pattern of LARAVEL_DEFAULT_EXCLUDES) {
77
+ if (!defaultExclude.includes(pattern)) {
78
+ defaultExclude.push(pattern);
79
+ }
80
+ }
81
+ } else {
82
+ let isPHP = false;
83
+ let isJSTS = false;
84
+ try {
85
+ const rootFiles = await readdir(workspaceRoot);
86
+ for (const file of rootFiles) {
87
+ if (file === "composer.json" || file.endsWith(".php")) {
88
+ isPHP = true;
89
+ }
90
+ if (file === "package.json" || file === "tsconfig.json" || file.endsWith(".ts") || file.endsWith(".js") || file.endsWith(".tsx") || file.endsWith(".jsx")) {
91
+ isJSTS = true;
92
+ }
93
+ }
94
+ } catch {
95
+ }
96
+ if (isPHP) {
97
+ defaultExclude.push("**/migrations/**", "**/seeds/**", "**/storage/**");
98
+ }
99
+ if (isJSTS) {
100
+ defaultExclude.push("**/dist/**", "**/__tests__/**", "**/*.test.ts", "**/*.spec.ts");
101
+ }
102
+ }
103
+ const userExclude = existing?.settings?.excludePatterns ?? [];
104
+ const userInclude = existing?.settings?.includePatterns ?? [];
105
+ const mergedExclude = [.../* @__PURE__ */ new Set([...userExclude, ...defaultExclude.filter((p) => !userExclude.includes(p))])];
106
+ const mergedInclude = [.../* @__PURE__ */ new Set([...userInclude, ...DEFAULT_CONFIG.settings.includePatterns])];
107
+ const defaultRepoName = repoName || resolve(workspaceRoot).split("/").pop() || "default";
108
+ const existingRepos = existing?.repos ?? [];
109
+ const repos = existingRepos.length > 0 ? existingRepos : [{
110
+ name: defaultRepoName,
111
+ path: ".",
112
+ ...isLaravel ? { framework: "laravel" } : {}
113
+ }];
114
+ if (repos[0] && isLaravel) {
115
+ repos[0].framework = "laravel";
116
+ }
117
+ const config = {
118
+ version: existing?.version ?? DEFAULT_CONFIG.version,
119
+ repos,
120
+ languages: existing?.languages ?? {},
121
+ settings: {
122
+ maxTokenBudget: existing?.settings?.maxTokenBudget ?? DEFAULT_CONFIG.settings.maxTokenBudget,
123
+ excludePatterns: mergedExclude,
124
+ includePatterns: mergedInclude
125
+ }
126
+ };
127
+ await writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
128
+ return new Config(configPath, config);
129
+ }
130
+ async save() {
131
+ await writeFile(this.configPath, JSON.stringify(this.config, null, 2), "utf-8");
132
+ }
133
+ get repos() {
134
+ return this.config.repos;
135
+ }
136
+ get settings() {
137
+ return this.config.settings;
138
+ }
139
+ get languages() {
140
+ return this.config.languages;
141
+ }
142
+ getResolvedUserLanguages() {
143
+ const result = {};
144
+ const builtins = getBuiltinLanguages();
145
+ for (const [name, userDef] of Object.entries(this.config.languages)) {
146
+ if (builtins[name]) continue;
147
+ result[name] = {
148
+ name,
149
+ extensions: userDef.extensions,
150
+ grammarWasm: userDef.grammarWasm,
151
+ queries: {
152
+ symbols: userDef.queries.symbols || "",
153
+ references: userDef.queries.references || ""
154
+ },
155
+ nodeMappings: userDef.nodeMappings,
156
+ tier: "user"
157
+ };
158
+ }
159
+ return result;
160
+ }
161
+ addRepo(name, path) {
162
+ if (!this.config.repos.find((r) => r.path === path)) {
163
+ this.config.repos.push({ name, path });
164
+ }
165
+ }
166
+ removeRepo(nameOrPath) {
167
+ this.config.repos = this.config.repos.filter((r) => r.name !== nameOrPath && r.path !== nameOrPath);
168
+ }
169
+ getWorkspaceRoot() {
170
+ return resolve(this.configPath, "..", "..");
171
+ }
172
+ get repo() {
173
+ return this.config.repos[0];
174
+ }
175
+ }
176
+ export {
177
+ Config
178
+ };
@@ -0,0 +1,61 @@
1
+ import { MapxGraph } from './graph.js';
2
+ import { Store } from './store.js';
3
+ import '../types.js';
4
+ import './store-interface.js';
5
+
6
+ interface ContextOptions {
7
+ task: string;
8
+ seeds?: string[];
9
+ tokens?: number;
10
+ depth?: number;
11
+ repo?: string;
12
+ }
13
+ interface ContextResult {
14
+ includedFiles: Array<{
15
+ path: string;
16
+ language: string;
17
+ lineCount: number;
18
+ sizeBytes: number;
19
+ symbols: Array<{
20
+ name: string;
21
+ kind: string;
22
+ scope: string | null;
23
+ startLine: number;
24
+ endLine: number;
25
+ }>;
26
+ }>;
27
+ excludedFiles: string[];
28
+ edges: Array<{
29
+ sourceFile: string;
30
+ targetFile: string;
31
+ sourceSymbol: string | null;
32
+ targetSymbol: string | null;
33
+ edgeType: string;
34
+ }>;
35
+ estimatedTokens: number;
36
+ matchedSymbols?: Array<{
37
+ name: string;
38
+ kind: string;
39
+ filePath: string;
40
+ }>;
41
+ files?: Array<{
42
+ path: string;
43
+ language: string;
44
+ lineCount: number;
45
+ sizeBytes: number;
46
+ }>;
47
+ symbols?: Array<{
48
+ name: string;
49
+ kind: string;
50
+ filePath: string;
51
+ }>;
52
+ }
53
+ declare class ContextBuilder {
54
+ private store;
55
+ private graph;
56
+ constructor(store: Store, graph: MapxGraph);
57
+ static extractKeywords(text: string): string[];
58
+ buildContext(options: ContextOptions): Promise<ContextResult>;
59
+ }
60
+
61
+ export { ContextBuilder, type ContextOptions, type ContextResult };