@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,74 @@
1
+ import { ProviderTemplate, McpConfigEntry } from './templates.js';
2
+
3
+ interface AgentAction {
4
+ provider: string;
5
+ filename: string;
6
+ filepath: string;
7
+ status: 'create' | 'append' | 'update_clean' | 'update_conflict' | 'up_to_date' | 'no_sentinel';
8
+ oldContent?: string;
9
+ newContent: string;
10
+ diff?: string;
11
+ }
12
+ interface McpConfigAction {
13
+ tool: string;
14
+ filename: string;
15
+ filepath: string;
16
+ status: 'create' | 'update' | 'merge' | 'up_to_date';
17
+ content: string;
18
+ }
19
+ declare class AgentGenerator {
20
+ private version;
21
+ constructor();
22
+ private readVersion;
23
+ getVersion(): string;
24
+ listProviders(): string[];
25
+ getTemplate(provider: string): ProviderTemplate | undefined;
26
+ private substitute;
27
+ plan(providers: string[], options: {
28
+ dir: string;
29
+ mcpPort?: number;
30
+ }): AgentAction[];
31
+ execute(action: AgentAction): void;
32
+ revert(options: {
33
+ dir: string;
34
+ }): void;
35
+ /**
36
+ * Detect which agent tools are present in the project directory.
37
+ * Returns the list of MCP config entries that should be generated.
38
+ */
39
+ detectAgentTools(dir: string): McpConfigEntry[];
40
+ /**
41
+ * List all available MCP config targets.
42
+ */
43
+ listMcpConfigs(): McpConfigEntry[];
44
+ /**
45
+ * Plan and generate MCP config files for detected (or specified) agent tools.
46
+ * For JSON config files that already exist, we merge the mapx entry into them
47
+ * rather than overwriting the entire file.
48
+ */
49
+ generateMcpConfigs(tools: McpConfigEntry[], options: {
50
+ dir: string;
51
+ }): McpConfigAction[];
52
+ /**
53
+ * Execute MCP config actions (write files to disk).
54
+ */
55
+ executeMcpConfig(action: McpConfigAction): void;
56
+ /**
57
+ * Merge the mapx MCP entry into an existing config object without
58
+ * destroying other entries. Handles the different config shapes:
59
+ * - opencode: { mcp: { mapx: ... } }
60
+ * - gemini-cli: { mcpServers: { mapx: ... } }
61
+ * - cursor/vscode: { mcpServers/servers: { mapx: ... } }
62
+ */
63
+ private mergeMapxIntoConfig;
64
+ /**
65
+ * Remove mapx entries from MCP config files during uninit.
66
+ * If the config file has only mapx entries, delete the file entirely.
67
+ */
68
+ revertMcpConfigs(options: {
69
+ dir: string;
70
+ }): void;
71
+ diff(oldStr: string, newStr: string): string;
72
+ }
73
+
74
+ export { type AgentAction, AgentGenerator, type McpConfigAction };
@@ -0,0 +1,375 @@
1
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, unlinkSync, readdirSync, rmdirSync } from "node:fs";
2
+ import { join, dirname, basename, resolve, extname } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { TEMPLATES, MCP_CONFIGS } from "./templates.js";
5
+ function getSentinelMarkers(filename) {
6
+ const ext = extname(filename).toLowerCase();
7
+ const base = basename(filename).toLowerCase();
8
+ if ([".yaml", ".yml", ".sh", ".bash", ".py", ".toml", ".conf"].includes(ext) || base === ".clinerules") {
9
+ return {
10
+ open: (v) => `# mapx v${v}`,
11
+ close: "# /mapx",
12
+ regex: /#\s*mapx\s+v([\d.]+)\s*\n([\s\S]*?)#\s*\/mapx/i
13
+ };
14
+ }
15
+ return {
16
+ open: (v) => `<!-- mapx v${v} -->`,
17
+ close: "<!-- /mapx -->",
18
+ regex: /<!--\s*mapx\s+v([\d.]+)\s*-->([\s\S]*?)<!--\s*\/mapx\s*-->/i
19
+ };
20
+ }
21
+ class AgentGenerator {
22
+ version;
23
+ constructor() {
24
+ this.version = this.readVersion();
25
+ }
26
+ readVersion() {
27
+ const base = dirname(fileURLToPath(import.meta.url));
28
+ for (const candidate of [
29
+ join(base, "VERSION"),
30
+ join(base, "..", "VERSION"),
31
+ join(base, "..", "..", "VERSION")
32
+ ]) {
33
+ if (existsSync(candidate)) {
34
+ return readFileSync(candidate, "utf-8").trim();
35
+ }
36
+ }
37
+ return "0.1.7";
38
+ }
39
+ getVersion() {
40
+ return this.version;
41
+ }
42
+ listProviders() {
43
+ return Object.keys(TEMPLATES);
44
+ }
45
+ getTemplate(provider) {
46
+ return TEMPLATES[provider];
47
+ }
48
+ substitute(content, dir, mcpPort = 3456) {
49
+ const projectDir = resolve(dir);
50
+ const projectName = basename(projectDir);
51
+ return content.replaceAll("{{PROJECT_NAME}}", projectName).replaceAll("{{PROJECT_DIR}}", projectDir).replaceAll("{{MAPX_VERSION}}", this.version).replaceAll("{{MCP_PORT}}", mcpPort.toString());
52
+ }
53
+ plan(providers, options) {
54
+ const actions = [];
55
+ const dir = options.dir;
56
+ const mcpPort = options.mcpPort || 3456;
57
+ for (const provider of providers) {
58
+ const template = TEMPLATES[provider];
59
+ if (!template) continue;
60
+ const filepath = join(dir, template.filename);
61
+ const rawContent = this.substitute(template.content, dir, mcpPort);
62
+ const markers = getSentinelMarkers(template.filename);
63
+ const wrappedNewContent = `${markers.open(this.version)}
64
+ ${rawContent}
65
+ ${markers.close}`;
66
+ if (!existsSync(filepath)) {
67
+ actions.push({
68
+ provider,
69
+ filename: template.filename,
70
+ filepath,
71
+ status: template.isAppend ? "append" : "create",
72
+ newContent: wrappedNewContent
73
+ });
74
+ continue;
75
+ }
76
+ const existingFileContent = readFileSync(filepath, "utf-8");
77
+ const match = existingFileContent.match(markers.regex);
78
+ if (!match) {
79
+ if (template.isAppend) {
80
+ actions.push({
81
+ provider,
82
+ filename: template.filename,
83
+ filepath,
84
+ status: "append",
85
+ oldContent: existingFileContent,
86
+ newContent: existingFileContent.endsWith("\n") ? `${existingFileContent}
87
+ ${wrappedNewContent}` : `${existingFileContent}
88
+
89
+ ${wrappedNewContent}`
90
+ });
91
+ } else {
92
+ actions.push({
93
+ provider,
94
+ filename: template.filename,
95
+ filepath,
96
+ status: "no_sentinel",
97
+ oldContent: existingFileContent,
98
+ newContent: wrappedNewContent,
99
+ diff: this.diff(existingFileContent, wrappedNewContent)
100
+ });
101
+ }
102
+ continue;
103
+ }
104
+ const fileVersion = match[1];
105
+ const fileContentInside = match[2];
106
+ const expectedContentOld = fileContentInside;
107
+ const expectedContentNew = rawContent;
108
+ if (expectedContentOld.trim() === expectedContentNew.trim() && fileVersion === this.version) {
109
+ actions.push({
110
+ provider,
111
+ filename: template.filename,
112
+ filepath,
113
+ status: "up_to_date",
114
+ oldContent: existingFileContent,
115
+ newContent: existingFileContent
116
+ });
117
+ continue;
118
+ }
119
+ const newFileContent = existingFileContent.replace(markers.regex, wrappedNewContent);
120
+ actions.push({
121
+ provider,
122
+ filename: template.filename,
123
+ filepath,
124
+ status: fileVersion === this.version ? "update_conflict" : "update_clean",
125
+ oldContent: existingFileContent,
126
+ newContent: newFileContent,
127
+ diff: this.diff(fileContentInside, rawContent)
128
+ });
129
+ }
130
+ return actions;
131
+ }
132
+ execute(action) {
133
+ if (action.status === "up_to_date") return;
134
+ const parentDir = dirname(action.filepath);
135
+ if (!existsSync(parentDir)) {
136
+ mkdirSync(parentDir, { recursive: true });
137
+ }
138
+ writeFileSync(action.filepath, action.newContent, "utf-8");
139
+ }
140
+ revert(options) {
141
+ const dir = options.dir;
142
+ for (const provider of Object.keys(TEMPLATES)) {
143
+ const template = TEMPLATES[provider];
144
+ if (!template) continue;
145
+ const filepath = join(dir, template.filename);
146
+ if (!existsSync(filepath)) continue;
147
+ try {
148
+ const content = readFileSync(filepath, "utf-8");
149
+ const markers = getSentinelMarkers(template.filename);
150
+ const match = content.match(markers.regex);
151
+ if (match) {
152
+ const cleaned = content.replace(markers.regex, "").trim();
153
+ if (cleaned.length === 0) {
154
+ unlinkSync(filepath);
155
+ console.log(` \u2713 Removed ${template.filename}`);
156
+ let parentDir = dirname(filepath);
157
+ const resolvedDir = resolve(dir);
158
+ while (parentDir !== resolvedDir && parentDir !== "/" && parentDir !== ".") {
159
+ if (existsSync(parentDir) && readdirSync(parentDir).length === 0) {
160
+ rmdirSync(parentDir);
161
+ parentDir = dirname(parentDir);
162
+ } else {
163
+ break;
164
+ }
165
+ }
166
+ } else {
167
+ writeFileSync(filepath, cleaned, "utf-8");
168
+ console.log(` \u2713 Cleaned mapx integration from ${template.filename}`);
169
+ }
170
+ }
171
+ } catch (err) {
172
+ console.error(` \u2717 Failed to revert agent file ${template.filename}: ${err.message}`);
173
+ }
174
+ }
175
+ }
176
+ /**
177
+ * Detect which agent tools are present in the project directory.
178
+ * Returns the list of MCP config entries that should be generated.
179
+ */
180
+ detectAgentTools(dir) {
181
+ return MCP_CONFIGS.filter((entry) => entry.detect(dir));
182
+ }
183
+ /**
184
+ * List all available MCP config targets.
185
+ */
186
+ listMcpConfigs() {
187
+ return MCP_CONFIGS;
188
+ }
189
+ /**
190
+ * Plan and generate MCP config files for detected (or specified) agent tools.
191
+ * For JSON config files that already exist, we merge the mapx entry into them
192
+ * rather than overwriting the entire file.
193
+ */
194
+ generateMcpConfigs(tools, options) {
195
+ const dir = resolve(options.dir);
196
+ const actions = [];
197
+ for (const entry of tools) {
198
+ const filepath = join(dir, entry.filename);
199
+ const newContent = entry.generate(dir);
200
+ if (!existsSync(filepath)) {
201
+ actions.push({
202
+ tool: entry.name,
203
+ filename: entry.filename,
204
+ filepath,
205
+ status: "create",
206
+ content: newContent
207
+ });
208
+ continue;
209
+ }
210
+ try {
211
+ const existingRaw = readFileSync(filepath, "utf-8");
212
+ const existing = JSON.parse(existingRaw);
213
+ const newObj = JSON.parse(newContent);
214
+ const merged = this.mergeMapxIntoConfig(existing, newObj, entry.name);
215
+ const mergedStr = JSON.stringify(merged, null, 2);
216
+ if (existingRaw.trim() === mergedStr.trim()) {
217
+ actions.push({
218
+ tool: entry.name,
219
+ filename: entry.filename,
220
+ filepath,
221
+ status: "up_to_date",
222
+ content: mergedStr
223
+ });
224
+ } else {
225
+ actions.push({
226
+ tool: entry.name,
227
+ filename: entry.filename,
228
+ filepath,
229
+ status: "merge",
230
+ content: mergedStr
231
+ });
232
+ }
233
+ } catch {
234
+ actions.push({
235
+ tool: entry.name,
236
+ filename: entry.filename,
237
+ filepath,
238
+ status: "update",
239
+ content: newContent
240
+ });
241
+ }
242
+ }
243
+ return actions;
244
+ }
245
+ /**
246
+ * Execute MCP config actions (write files to disk).
247
+ */
248
+ executeMcpConfig(action) {
249
+ if (action.status === "up_to_date") return;
250
+ const parentDir = dirname(action.filepath);
251
+ if (!existsSync(parentDir)) {
252
+ mkdirSync(parentDir, { recursive: true });
253
+ }
254
+ writeFileSync(action.filepath, action.content + "\n", "utf-8");
255
+ }
256
+ /**
257
+ * Merge the mapx MCP entry into an existing config object without
258
+ * destroying other entries. Handles the different config shapes:
259
+ * - opencode: { mcp: { mapx: ... } }
260
+ * - gemini-cli: { mcpServers: { mapx: ... } }
261
+ * - cursor/vscode: { mcpServers/servers: { mapx: ... } }
262
+ */
263
+ mergeMapxIntoConfig(existing, newObj, toolName) {
264
+ const merged = { ...existing };
265
+ for (const key of Object.keys(newObj)) {
266
+ if (key === "$schema") {
267
+ if (!merged.$schema) merged.$schema = newObj.$schema;
268
+ continue;
269
+ }
270
+ const val = newObj[key];
271
+ if (typeof val === "object" && val !== null && "mapx" in val) {
272
+ if (!merged[key]) merged[key] = {};
273
+ merged[key] = { ...merged[key], mapx: val.mapx };
274
+ }
275
+ }
276
+ return merged;
277
+ }
278
+ /**
279
+ * Remove mapx entries from MCP config files during uninit.
280
+ * If the config file has only mapx entries, delete the file entirely.
281
+ */
282
+ revertMcpConfigs(options) {
283
+ const dir = resolve(options.dir);
284
+ for (const entry of MCP_CONFIGS) {
285
+ const filepath = join(dir, entry.filename);
286
+ if (!existsSync(filepath)) continue;
287
+ try {
288
+ const raw = readFileSync(filepath, "utf-8");
289
+ const obj = JSON.parse(raw);
290
+ let modified = false;
291
+ for (const key of ["mcp", "mcpServers", "servers"]) {
292
+ if (obj[key] && typeof obj[key] === "object" && "mapx" in obj[key]) {
293
+ delete obj[key].mapx;
294
+ modified = true;
295
+ if (Object.keys(obj[key]).length === 0) {
296
+ delete obj[key];
297
+ }
298
+ }
299
+ }
300
+ if (!modified) continue;
301
+ const remaining = Object.keys(obj).filter((k) => k !== "$schema");
302
+ if (remaining.length === 0) {
303
+ unlinkSync(filepath);
304
+ console.log(` \u2713 Removed ${entry.filename}`);
305
+ let parentDir = dirname(filepath);
306
+ const resolvedDir = resolve(dir);
307
+ while (parentDir !== resolvedDir && parentDir !== "/" && parentDir !== ".") {
308
+ if (existsSync(parentDir) && readdirSync(parentDir).length === 0) {
309
+ rmdirSync(parentDir);
310
+ parentDir = dirname(parentDir);
311
+ } else {
312
+ break;
313
+ }
314
+ }
315
+ } else {
316
+ writeFileSync(filepath, JSON.stringify(obj, null, 2) + "\n", "utf-8");
317
+ console.log(` \u2713 Removed mapx entry from ${entry.filename}`);
318
+ }
319
+ } catch (err) {
320
+ console.error(` \u2717 Failed to revert MCP config ${entry.filename}: ${err.message}`);
321
+ }
322
+ }
323
+ }
324
+ diff(oldStr, newStr) {
325
+ const oldLines = oldStr.split("\n");
326
+ const newLines = newStr.split("\n");
327
+ const diff = [];
328
+ let i = 0, j = 0;
329
+ while (i < oldLines.length || j < newLines.length) {
330
+ if (i < oldLines.length && j < newLines.length) {
331
+ if (oldLines[i] === newLines[j]) {
332
+ diff.push(` ${oldLines[i]}`);
333
+ i++;
334
+ j++;
335
+ } else {
336
+ let found = false;
337
+ for (let k = 1; k < 5; k++) {
338
+ if (i + k < oldLines.length && oldLines[i + k] === newLines[j]) {
339
+ for (let m = 0; m < k; m++) {
340
+ diff.push(`- ${oldLines[i + m]}`);
341
+ }
342
+ i += k;
343
+ found = true;
344
+ break;
345
+ }
346
+ if (j + k < newLines.length && oldLines[i] === newLines[j + k]) {
347
+ for (let m = 0; m < k; m++) {
348
+ diff.push(`+ ${newLines[j + m]}`);
349
+ }
350
+ j += k;
351
+ found = true;
352
+ break;
353
+ }
354
+ }
355
+ if (!found) {
356
+ diff.push(`- ${oldLines[i]}`);
357
+ diff.push(`+ ${newLines[j]}`);
358
+ i++;
359
+ j++;
360
+ }
361
+ }
362
+ } else if (i < oldLines.length) {
363
+ diff.push(`- ${oldLines[i]}`);
364
+ i++;
365
+ } else if (j < newLines.length) {
366
+ diff.push(`+ ${newLines[j]}`);
367
+ j++;
368
+ }
369
+ }
370
+ return diff.join("\n");
371
+ }
372
+ }
373
+ export {
374
+ AgentGenerator
375
+ };
@@ -0,0 +1,29 @@
1
+ interface ProviderTemplate {
2
+ filename: string;
3
+ isAppend: boolean;
4
+ content: string;
5
+ /** True if this template is an MCP server config (JSON) rather than an instruction doc */
6
+ isMcpConfig?: boolean;
7
+ }
8
+ /**
9
+ * MCP config definitions for agent tools.
10
+ * Each entry generates a project-local JSON config file that auto-registers
11
+ * mapx as an MCP server so the agent can discover all 25 tools on startup.
12
+ *
13
+ * Config files use the sentinel-block pattern (<!-- mapx --> markers are not
14
+ * used — the entire file is owned by mapx and can safely be overwritten).
15
+ */
16
+ interface McpConfigEntry {
17
+ /** Agent tool name (e.g. 'opencode', 'gemini-cli') */
18
+ name: string;
19
+ /** Relative path from project root to the config file */
20
+ filename: string;
21
+ /** Function to generate the JSON config content */
22
+ generate: (projectDir: string) => string;
23
+ /** How to detect if this agent tool is active / desired */
24
+ detect: (projectDir: string) => boolean;
25
+ }
26
+ declare const MCP_CONFIGS: McpConfigEntry[];
27
+ declare const TEMPLATES: Record<string, ProviderTemplate>;
28
+
29
+ export { MCP_CONFIGS, type McpConfigEntry, type ProviderTemplate, TEMPLATES };