@ulpi/cli 0.1.5 → 0.1.6

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 (109) hide show
  1. package/LICENSE +21 -0
  2. package/dist/{auth-PN7TMQHV-2W4ICG64.js → auth-FWM7MM4Q-VZC3U2XZ.js} +1 -1
  3. package/dist/{auth-BFFBUJUC.js → auth-HDK7ECJL.js} +2 -1
  4. package/dist/{chunk-RJIRWQJD.js → chunk-3BCW6ABU.js} +402 -142
  5. package/dist/{chunk-L3PWNHSA.js → chunk-3WB5CXH4.js} +180 -5
  6. package/dist/{chunk-K4OVPFY2.js → chunk-4UCJIAOU.js} +2 -2
  7. package/dist/chunk-4XTHZVDS.js +109 -0
  8. package/dist/chunk-4ZPOZULQ.js +6522 -0
  9. package/dist/{chunk-SIAQVRKG.js → chunk-5MI5GIXM.js} +48 -2
  10. package/dist/{chunk-KLEASXUR.js → chunk-6ZL6NXMV.js} +1 -1
  11. package/dist/{chunk-AV5RB3N2.js → chunk-76D3BYJD.js} +48 -0
  12. package/dist/{chunk-DOIKS6C5.js → chunk-AWOSRA5F.js} +1 -1
  13. package/dist/{chunk-UCMT5OKP.js → chunk-BFEKZZHM.js} +274 -57
  14. package/dist/chunk-C7CLUQI6.js +1286 -0
  15. package/dist/{chunk-ELTGWMDE.js → chunk-E3B5NROU.js} +7 -7
  16. package/dist/chunk-EJ7TW77N.js +1418 -0
  17. package/dist/{chunk-6OURRFP7.js → chunk-IV6MWETF.js} +383 -168
  18. package/dist/chunk-IZPJHSPX.js +1478 -0
  19. package/dist/chunk-JLHNLM3C.js +228 -0
  20. package/dist/{chunk-P2RESJRN.js → chunk-KYYI23AQ.js} +2 -2
  21. package/dist/chunk-S6ANCSYO.js +1271 -0
  22. package/dist/chunk-SEU7WWNQ.js +1251 -0
  23. package/dist/chunk-SNQ7NAIS.js +453 -0
  24. package/dist/{ulpi-RMMCUAGP-EWYUE7RU.js → chunk-TSLDGT5O.js} +73 -35
  25. package/dist/{chunk-EIWYSP3A.js → chunk-UXHCHOWQ.js} +83 -62
  26. package/dist/chunk-V2H5D6Y3.js +146 -0
  27. package/dist/{chunk-5SCG7UYM.js → chunk-VVEDXI7E.js} +1 -1
  28. package/dist/chunk-VXH5Y4FO.js +6761 -0
  29. package/dist/chunk-WED4LM5N.js +322 -0
  30. package/dist/{chunk-74WVVWJ4.js → chunk-YOKL7RB5.js} +184 -15
  31. package/dist/chunk-Z53CAR7G.js +298 -0
  32. package/dist/{ci-JQ56YIKC.js → ci-X3U2W4HC.js} +124 -26
  33. package/dist/cloud-2F3NLVHN.js +274 -0
  34. package/dist/{codemap-HMYBXJL2.js → codemap-XNGMAF3F.js} +37 -37
  35. package/dist/codex-MB5YTMRT.js +132 -0
  36. package/dist/{config-YYWEN7U2.js → config-OOELBYTH.js} +1 -1
  37. package/dist/dist-2BJYR5EI.js +59 -0
  38. package/dist/dist-3EIQTZHT.js +1380 -0
  39. package/dist/{dist-WAMAQVPK.js → dist-4U5L2X2C.js} +2 -2
  40. package/dist/{dist-4XTJ6HLM.js → dist-54KAMNLO.js} +16 -15
  41. package/dist/dist-6M4MZWZW.js +58 -0
  42. package/dist/dist-6X576SU2.js +27 -0
  43. package/dist/dist-7QOEYLFX.js +103 -0
  44. package/dist/dist-AYBGHEDY.js +2541 -0
  45. package/dist/dist-EK45QNEM.js +45 -0
  46. package/dist/{dist-U7ZIJMZD.js → dist-FKFEJRPX.js} +16 -15
  47. package/dist/dist-GTEJUBBT.js +66 -0
  48. package/dist/dist-HA74OKJZ.js +40 -0
  49. package/dist/{dist-XG2GG5SD.js → dist-HU5RZAON.js} +14 -2
  50. package/dist/dist-IYE3OBRB.js +374 -0
  51. package/dist/{dist-7WLLPWWB.js → dist-JLU26AB6.js} +12 -9
  52. package/dist/{dist-6G7JC2RA.js → dist-KUCI6JFE.js} +49 -9
  53. package/dist/dist-NUEMFZFL.js +33 -0
  54. package/dist/{dist-GWGTAHNM.js → dist-NUXMDXZ3.js} +31 -3
  55. package/dist/{dist-5R4RYNQO.js → dist-YCNWHSLN.js} +15 -5
  56. package/dist/{dist-6MFVWIFF.js → dist-YFFG2ZD6.js} +9 -16
  57. package/dist/dist-ZG4OKCSR.js +15 -0
  58. package/dist/doctor-SI4LLLDZ.js +345 -0
  59. package/dist/{export-import-4A5MWLIA.js → export-import-JFQH4KSJ.js} +1 -1
  60. package/dist/{history-RNUWO4JZ.js → history-5NE46ZAH.js} +7 -7
  61. package/dist/{hooks-installer-K2JXEBNN.js → hooks-installer-UN5JZLDQ.js} +2 -2
  62. package/dist/index.js +394 -618
  63. package/dist/{init-NQWFZPKO.js → init-5FK3VKRT.js} +76 -10
  64. package/dist/job-HIDMAFW2.js +376 -0
  65. package/dist/jobs.memory-PLMMSFHB-VBECCTHN.js +33 -0
  66. package/dist/kiro-VMUHDFGK.js +153 -0
  67. package/dist/{launchd-OYXUAVW6.js → launchd-6AWT54HR.js} +9 -17
  68. package/dist/mcp-PDUD7SGP.js +249 -0
  69. package/dist/mcp-installer-PQU3XOGO.js +259 -0
  70. package/dist/mcp-setup-OA7IB3H3.js +263 -0
  71. package/dist/{memory-D6ZFFCI2.js → memory-ZNAEAK3B.js} +17 -17
  72. package/dist/{ollama-3XCUZMZT-FYKHW4TZ.js → ollama-3XCUZMZT-4JMH6B7P.js} +1 -1
  73. package/dist/{openai-E7G2YAHU-IG33BFYF.js → openai-E7G2YAHU-T3HMBPH7.js} +2 -2
  74. package/dist/portal-JYWVHXDU.js +210 -0
  75. package/dist/prd-Q4J5NVAR.js +408 -0
  76. package/dist/repos-WWZXNN3P.js +271 -0
  77. package/dist/review-integration-5WHEJU2A.js +14 -0
  78. package/dist/{rules-3OFGWHP4.js → rules-Y4VSOY5Y.js} +3 -3
  79. package/dist/run-VPNXEIBY.js +687 -0
  80. package/dist/server-COL4AXKU-P7S7NNF6.js +11 -0
  81. package/dist/server-KKSETHDV-XSSLEENT.js +20 -0
  82. package/dist/{skills-GY2CTPWN.js → skills-QEYU2N27.js} +4 -2
  83. package/dist/start-JYOEL7AJ.js +303 -0
  84. package/dist/{status-SE43TIFJ.js → status-BHQYYGAL.js} +2 -2
  85. package/dist/{templates-O2XDKB5R.js → templates-CBRUJ66V.js} +6 -5
  86. package/dist/tui-DP7736EX.js +61 -0
  87. package/dist/ulpi-5EN6JCAS-LFE3WSL4.js +10 -0
  88. package/dist/{uninstall-KWGSGZTI.js → uninstall-ICUV6DDV.js} +3 -3
  89. package/dist/{update-QYZA4D23.js → update-7ZMAYRBH.js} +3 -3
  90. package/dist/{version-checker-MVB74DEX.js → version-checker-4ZFMZA7Y.js} +2 -2
  91. package/package.json +39 -31
  92. package/dist/chunk-26LLDX2T.js +0 -553
  93. package/dist/chunk-DDRLI6JU.js +0 -331
  94. package/dist/chunk-IFATANHR.js +0 -453
  95. package/dist/chunk-JWUUVXIV.js +0 -13694
  96. package/dist/chunk-LD52XG3X.js +0 -4273
  97. package/dist/chunk-MIAQVCFW.js +0 -39
  98. package/dist/chunk-YYZOFYS6.js +0 -415
  99. package/dist/dist-XD4YI27T.js +0 -26
  100. package/dist/mcp-installer-TOYDP77X.js +0 -124
  101. package/dist/projects-COUJP4ZC.js +0 -271
  102. package/dist/review-KMGP2S25.js +0 -152
  103. package/dist/server-USLHY6GH-F4JSXCWA.js +0 -18
  104. package/dist/server-X5P6WH2M-ULZF5WHZ.js +0 -11
  105. package/dist/skills/ulpi-generate-guardian/SKILL.md +0 -750
  106. package/dist/skills/ulpi-generate-guardian/references/framework-rules.md +0 -849
  107. package/dist/skills/ulpi-generate-guardian/references/language-rules.md +0 -591
  108. package/dist/ui-4SM2SUI6.js +0 -167
  109. package/dist/ui.html +0 -698
@@ -1,10 +1,10 @@
1
- import "./chunk-5SCG7UYM.js";
1
+ import "./chunk-VVEDXI7E.js";
2
2
  import {
3
3
  LAUNCHD_LABEL,
4
4
  LOGS_DIR,
5
5
  getApiPort,
6
6
  getBinaryPath
7
- } from "./chunk-DDRLI6JU.js";
7
+ } from "./chunk-C7CLUQI6.js";
8
8
  import "./chunk-4VNS5WPM.js";
9
9
 
10
10
  // src/launchd.ts
@@ -30,23 +30,15 @@ function needsLegacyMigration() {
30
30
  function generatePlist(options = {}) {
31
31
  const port = options.port ?? getApiPort();
32
32
  const binary = getBinaryPath();
33
- const firstSpace = binary.indexOf(" ");
34
- const parts = firstSpace === -1 ? [binary] : [binary.slice(0, firstSpace), binary.slice(firstSpace + 1)];
35
- for (let i = 0; i < parts.length; i++) {
36
- const p = parts[i].replace(/^"|"$/g, "");
37
- if (p && !p.startsWith("/")) {
38
- try {
39
- const resolved = execFileSync("which", [p], { encoding: "utf-8", timeout: 3e3 }).trim();
40
- if (resolved) parts[i] = resolved;
41
- else parts[i] = p;
42
- } catch {
43
- parts[i] = p;
44
- }
45
- } else {
46
- parts[i] = p;
33
+ let resolvedBinary = binary;
34
+ if (!binary.startsWith("/")) {
35
+ try {
36
+ const resolved = execFileSync("which", [binary], { encoding: "utf-8", timeout: 3e3 }).trim();
37
+ if (resolved) resolvedBinary = resolved;
38
+ } catch {
47
39
  }
48
40
  }
49
- const programArgs = parts.map((p) => ` <string>${escapeXml(p)}</string>`).join("\n");
41
+ const programArgs = ` <string>${escapeXml(resolvedBinary)}</string>`;
50
42
  return `<?xml version="1.0" encoding="UTF-8"?>
51
43
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
52
44
  <plist version="1.0">
@@ -0,0 +1,249 @@
1
+ import "./chunk-4VNS5WPM.js";
2
+
3
+ // src/commands/mcp.ts
4
+ import chalk from "chalk";
5
+ async function runMcp(args, projectDir) {
6
+ const sub = args[0];
7
+ switch (sub) {
8
+ case "gateway":
9
+ return runGateway(projectDir);
10
+ case "list":
11
+ return runList();
12
+ case "add":
13
+ return runAdd(args.slice(1));
14
+ case "remove":
15
+ return runRemove(args.slice(1));
16
+ case "enable":
17
+ return runEnable(args.slice(1), projectDir);
18
+ case "disable":
19
+ return runDisable(args.slice(1), projectDir);
20
+ case "status":
21
+ return runStatus(projectDir);
22
+ case "catalog":
23
+ return runCatalogList(args.slice(1));
24
+ case "cloud-connect":
25
+ return runCloudConnect(projectDir);
26
+ default:
27
+ printUsage();
28
+ }
29
+ }
30
+ function printUsage() {
31
+ console.log(`
32
+ Usage: ulpi mcp <subcommand>
33
+
34
+ Gateway:
35
+ gateway Start the MCP gateway (stdio, blocks until closed)
36
+
37
+ Library:
38
+ list List global MCP library entries
39
+ add <name> Add MCP to library (--from-catalog to use bundled definition)
40
+ remove <name> Remove MCP from library
41
+
42
+ Per-Repo:
43
+ enable <name> Enable MCP for current repo
44
+ disable <name> Disable MCP for current repo
45
+ status Show enabled MCPs for current repo
46
+
47
+ Catalog:
48
+ catalog [--search=<q>] List available catalog entries
49
+
50
+ Cloud:
51
+ cloud-connect Configure .mcp.json to connect to Cloud MCP Gateway
52
+ `.trim());
53
+ }
54
+ async function runGateway(projectDir) {
55
+ const { startGateway } = await import("./dist-IYE3OBRB.js");
56
+ await startGateway({ projectDir });
57
+ }
58
+ async function runList() {
59
+ const { listLibrary } = await import("./dist-EK45QNEM.js");
60
+ const entries = listLibrary();
61
+ if (entries.length === 0) {
62
+ console.log(chalk.dim("No MCPs in library. Run 'ulpi mcp add <name> --from-catalog' to add one."));
63
+ return;
64
+ }
65
+ console.log(chalk.bold(`MCP Library (${entries.length} entries)
66
+ `));
67
+ for (const entry of entries) {
68
+ const name = chalk.cyan(entry.name);
69
+ const display = entry.display_name ? ` (${entry.display_name})` : "";
70
+ const cat = chalk.dim(`[${entry.category}]`);
71
+ const desc = entry.description ? chalk.dim(` \u2014 ${entry.description}`) : "";
72
+ console.log(` ${name}${display} ${cat}${desc}`);
73
+ console.log(` ${chalk.dim(`${entry.command} ${entry.args.join(" ")}`)}`);
74
+ }
75
+ }
76
+ async function runAdd(args) {
77
+ const name = args.find((a) => !a.startsWith("-"));
78
+ const fromCatalog = args.includes("--from-catalog");
79
+ if (!name) {
80
+ console.error(chalk.red("Usage: ulpi mcp add <name> [--from-catalog]"));
81
+ return;
82
+ }
83
+ if (!/^[a-z0-9][a-z0-9_-]*$/.test(name) || name.length > 128) {
84
+ console.error(chalk.red("Name must be lowercase alphanumeric with hyphens/underscores, max 128 chars."));
85
+ return;
86
+ }
87
+ const { addToLibrary, getLibraryEntry } = await import("./dist-EK45QNEM.js");
88
+ if (getLibraryEntry(name)) {
89
+ console.log(chalk.yellow(`MCP "${name}" already exists in library. Use 'ulpi mcp remove ${name}' first.`));
90
+ return;
91
+ }
92
+ const projectDir = process.cwd();
93
+ if (fromCatalog) {
94
+ const { getCatalogEntry, enableMcp } = await import("./dist-EK45QNEM.js");
95
+ const entry = getCatalogEntry(name);
96
+ if (!entry) {
97
+ console.error(chalk.red(`"${name}" not found in catalog. Run 'ulpi mcp catalog' to see available entries.`));
98
+ return;
99
+ }
100
+ addToLibrary(name, {
101
+ name: entry.name,
102
+ display_name: entry.display_name,
103
+ description: entry.description,
104
+ command: entry.command,
105
+ args: entry.args,
106
+ transport: entry.transport,
107
+ category: entry.category,
108
+ config_schema: entry.config_schema,
109
+ tags: entry.tags,
110
+ source: "catalog"
111
+ });
112
+ enableMcp(projectDir, name);
113
+ console.log(chalk.green(`Added "${name}" to library and enabled for this repo.`));
114
+ } else {
115
+ const commandIdx = args.indexOf("--command");
116
+ const command = commandIdx !== -1 ? args[commandIdx + 1] : void 0;
117
+ if (!command) {
118
+ console.error(chalk.red("For manual add, provide --command: ulpi mcp add <name> --command npx --args '-y,@scope/pkg'"));
119
+ console.error(chalk.dim("Or use --from-catalog to add from the bundled catalog."));
120
+ return;
121
+ }
122
+ const argsIdx = args.indexOf("--args");
123
+ const mcpArgs = argsIdx !== -1 ? args[argsIdx + 1]?.split(",") ?? [] : [];
124
+ const { enableMcp: enableManual } = await import("./dist-EK45QNEM.js");
125
+ addToLibrary(name, {
126
+ name,
127
+ command,
128
+ args: mcpArgs,
129
+ transport: "stdio",
130
+ category: "custom",
131
+ tags: [],
132
+ source: "manual"
133
+ });
134
+ enableManual(projectDir, name);
135
+ console.log(chalk.green(`Added "${name}" to library and enabled for this repo.`));
136
+ }
137
+ }
138
+ async function runRemove(args) {
139
+ const name = args[0];
140
+ if (!name) {
141
+ console.error(chalk.red("Usage: ulpi mcp remove <name>"));
142
+ return;
143
+ }
144
+ const { removeFromLibrary, disableMcp } = await import("./dist-EK45QNEM.js");
145
+ const removed = removeFromLibrary(name);
146
+ if (removed) {
147
+ disableMcp(process.cwd(), name);
148
+ console.log(chalk.green(`Removed "${name}" from library and disabled for this repo.`));
149
+ } else {
150
+ console.log(chalk.yellow(`"${name}" not found in library.`));
151
+ }
152
+ }
153
+ async function runEnable(args, projectDir) {
154
+ const name = args[0];
155
+ if (!name) {
156
+ console.error(chalk.red("Usage: ulpi mcp enable <name>"));
157
+ return;
158
+ }
159
+ const { getLibraryEntry, enableMcp } = await import("./dist-EK45QNEM.js");
160
+ if (!getLibraryEntry(name)) {
161
+ console.error(chalk.red(`"${name}" not in library. Run 'ulpi mcp add ${name} --from-catalog' first.`));
162
+ return;
163
+ }
164
+ const config = {};
165
+ for (let i = 1; i < args.length; i++) {
166
+ if (args[i]?.includes("=")) {
167
+ const [key, ...rest] = args[i].split("=");
168
+ if (key) config[key] = rest.join("=");
169
+ }
170
+ }
171
+ enableMcp(projectDir, name, Object.keys(config).length > 0 ? config : void 0);
172
+ console.log(chalk.green(`Enabled "${name}" for this repo.`));
173
+ if (Object.keys(config).length > 0) {
174
+ console.log(chalk.dim(` Config: ${Object.entries(config).map(([k, v]) => `${k}=${v}`).join(", ")}`));
175
+ }
176
+ }
177
+ async function runDisable(args, projectDir) {
178
+ const name = args[0];
179
+ if (!name) {
180
+ console.error(chalk.red("Usage: ulpi mcp disable <name>"));
181
+ return;
182
+ }
183
+ const { disableMcp } = await import("./dist-EK45QNEM.js");
184
+ const disabled = disableMcp(projectDir, name);
185
+ if (disabled) {
186
+ console.log(chalk.green(`Disabled "${name}" for this repo.`));
187
+ } else {
188
+ console.log(chalk.yellow(`"${name}" not enabled for this repo.`));
189
+ }
190
+ }
191
+ async function runStatus(projectDir) {
192
+ const { listActivations, getLibraryEntry } = await import("./dist-EK45QNEM.js");
193
+ const activations = listActivations(projectDir);
194
+ if (activations.length === 0) {
195
+ console.log(chalk.dim("No MCPs configured for this repo. Run 'ulpi mcp enable <name>' to add one."));
196
+ return;
197
+ }
198
+ console.log(chalk.bold(`MCPs for this repo (${activations.length})
199
+ `));
200
+ for (const { name, activation } of activations) {
201
+ const status = activation.enabled ? chalk.green("ON") : chalk.red("OFF");
202
+ const def = getLibraryEntry(name);
203
+ const display = def?.display_name ? ` (${def.display_name})` : "";
204
+ const missing = !def ? chalk.yellow(" [not in library]") : "";
205
+ console.log(` ${status} ${chalk.cyan(name)}${display}${missing}`);
206
+ const configEntries = Object.entries(activation.config);
207
+ if (configEntries.length > 0) {
208
+ for (const [key, value] of configEntries) {
209
+ const displayValue = typeof value === "string" && value.startsWith("env:") ? chalk.dim(value) : chalk.dim(String(value));
210
+ console.log(` ${chalk.dim(key)} = ${displayValue}`);
211
+ }
212
+ }
213
+ }
214
+ }
215
+ async function runCatalogList(args) {
216
+ const searchFlag = args.find((a) => a.startsWith("--search="));
217
+ const query = searchFlag ? searchFlag.slice("--search=".length) : void 0;
218
+ const { loadCatalog, searchCatalog } = await import("./dist-EK45QNEM.js");
219
+ const entries = query ? searchCatalog(query) : loadCatalog();
220
+ if (entries.length === 0) {
221
+ console.log(chalk.dim(query ? `No catalog entries matching "${query}".` : "Catalog is empty."));
222
+ return;
223
+ }
224
+ console.log(chalk.bold(`MCP Catalog (${entries.length} entries)${query ? ` \u2014 search: "${query}"` : ""}
225
+ `));
226
+ const byCategory = /* @__PURE__ */ new Map();
227
+ for (const entry of entries) {
228
+ const cat = entry.category ?? "custom";
229
+ if (!byCategory.has(cat)) byCategory.set(cat, []);
230
+ byCategory.get(cat).push(entry);
231
+ }
232
+ for (const [category, items] of byCategory) {
233
+ console.log(chalk.bold(` ${category.charAt(0).toUpperCase() + category.slice(1)}`));
234
+ for (const item of items) {
235
+ const name = chalk.cyan(item.name);
236
+ const desc = item.description ? chalk.dim(` \u2014 ${item.description}`) : "";
237
+ console.log(` ${name}${desc}`);
238
+ }
239
+ console.log("");
240
+ }
241
+ console.log(chalk.dim(`Add to library: ulpi mcp add <name> --from-catalog`));
242
+ }
243
+ async function runCloudConnect(projectDir) {
244
+ const { runCloud } = await import("./cloud-2F3NLVHN.js");
245
+ await runCloud(["connect"], projectDir);
246
+ }
247
+ export {
248
+ runMcp
249
+ };
@@ -0,0 +1,259 @@
1
+ import {
2
+ getBinaryPath
3
+ } from "./chunk-C7CLUQI6.js";
4
+ import "./chunk-4VNS5WPM.js";
5
+
6
+ // src/mcp-installer.ts
7
+ import * as fs from "fs";
8
+ import * as path from "path";
9
+ import { execFileSync } from "child_process";
10
+ function mcpJsonPath(projectDir) {
11
+ return path.join(projectDir, ".mcp.json");
12
+ }
13
+ function readMcpJson(projectDir) {
14
+ const filePath = mcpJsonPath(projectDir);
15
+ if (fs.existsSync(filePath)) {
16
+ try {
17
+ const data = JSON.parse(fs.readFileSync(filePath, "utf-8"));
18
+ return { mcpServers: data.mcpServers ?? {} };
19
+ } catch {
20
+ }
21
+ }
22
+ return { mcpServers: {} };
23
+ }
24
+ function writeMcpJson(projectDir, mcpServers) {
25
+ const filePath = mcpJsonPath(projectDir);
26
+ fs.writeFileSync(filePath, JSON.stringify({ mcpServers }, null, 2) + "\n", "utf-8");
27
+ }
28
+ function buildMcpEntry(subcommandArgs, description) {
29
+ return { command: getBinaryPath(), args: subcommandArgs, description };
30
+ }
31
+ function installMcpServer(projectDir) {
32
+ const { mcpServers } = readMcpJson(projectDir);
33
+ if (mcpServers["codemap"]) {
34
+ return { installed: false, message: "CodeMap MCP server already registered" };
35
+ }
36
+ mcpServers["codemap"] = buildMcpEntry(["codemap", "serve"], "Semantic code search (hybrid vector + BM25), symbol lookup, file summaries, dependency graph analysis (PageRank, cycles, coupling). Use search_code for concept-based queries, search_symbols to find functions/types by name.");
37
+ writeMcpJson(projectDir, mcpServers);
38
+ return { installed: true, message: "CodeMap MCP server registered in .mcp.json" };
39
+ }
40
+ function uninstallMcpServer(projectDir) {
41
+ const filePath = mcpJsonPath(projectDir);
42
+ if (!fs.existsSync(filePath)) return;
43
+ try {
44
+ const { mcpServers } = readMcpJson(projectDir);
45
+ if (mcpServers["codemap"]) {
46
+ delete mcpServers["codemap"];
47
+ writeMcpJson(projectDir, mcpServers);
48
+ }
49
+ } catch {
50
+ }
51
+ }
52
+ function isMcpServerInstalled(projectDir) {
53
+ const { mcpServers } = readMcpJson(projectDir);
54
+ return !!mcpServers["codemap"];
55
+ }
56
+ function installMemoryMcpServer(projectDir) {
57
+ const { mcpServers } = readMcpJson(projectDir);
58
+ if (mcpServers["memory"]) {
59
+ return { installed: false, message: "Memory MCP server already registered" };
60
+ }
61
+ mcpServers["memory"] = buildMcpEntry(["memory", "serve"], "Persistent agent memory across sessions. Search past decisions, patterns, and learnings. Save new memories, browse timeline, get session context, and manage retention.");
62
+ writeMcpJson(projectDir, mcpServers);
63
+ return { installed: true, message: "Memory MCP server registered in .mcp.json" };
64
+ }
65
+ function uninstallMemoryMcpServer(projectDir) {
66
+ const filePath = mcpJsonPath(projectDir);
67
+ if (!fs.existsSync(filePath)) return;
68
+ try {
69
+ const { mcpServers } = readMcpJson(projectDir);
70
+ if (mcpServers["memory"]) {
71
+ delete mcpServers["memory"];
72
+ writeMcpJson(projectDir, mcpServers);
73
+ }
74
+ } catch {
75
+ }
76
+ }
77
+ function isMemoryMcpServerInstalled(projectDir) {
78
+ const { mcpServers } = readMcpJson(projectDir);
79
+ return !!mcpServers["memory"];
80
+ }
81
+ function installGatewayMcpServer(projectDir) {
82
+ const { mcpServers } = readMcpJson(projectDir);
83
+ if (mcpServers["tools"]) {
84
+ return { installed: false, message: "MCP Gateway server already registered" };
85
+ }
86
+ mcpServers["tools"] = buildMcpEntry(["mcp", "gateway"], "MCP gateway proxy \u2014 aggregates tools from third-party MCP servers (databases, APIs, services) enabled for this project. Tool names are prefixed with the server name (e.g. postgres__query, stripe__list_customers).");
87
+ writeMcpJson(projectDir, mcpServers);
88
+ return { installed: true, message: "MCP Gateway server registered in .mcp.json" };
89
+ }
90
+ function isCommandAvailable(command) {
91
+ try {
92
+ execFileSync("which", [command], { encoding: "utf-8", timeout: 5e3 });
93
+ return true;
94
+ } catch {
95
+ return false;
96
+ }
97
+ }
98
+ function getProjectMcpEntries() {
99
+ const binary = getBinaryPath();
100
+ return {
101
+ codemap: { command: binary, args: ["codemap", "serve"] },
102
+ memory: { command: binary, args: ["memory", "serve"] },
103
+ gateway: { command: binary, args: ["mcp", "gateway"] }
104
+ };
105
+ }
106
+ function readJsonConfig(filePath) {
107
+ if (fs.existsSync(filePath)) {
108
+ try {
109
+ return JSON.parse(fs.readFileSync(filePath, "utf-8"));
110
+ } catch {
111
+ }
112
+ }
113
+ return {};
114
+ }
115
+ function writeJsonConfig(filePath, config) {
116
+ const dir = path.dirname(filePath);
117
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
118
+ fs.writeFileSync(filePath, JSON.stringify(config, null, 2) + "\n", "utf-8");
119
+ }
120
+ function writeCodexProjectMcp(projectDir) {
121
+ const filePath = path.join(projectDir, ".codex", "config.toml");
122
+ const entries = getProjectMcpEntries();
123
+ const added = [];
124
+ let content = "";
125
+ if (fs.existsSync(filePath)) {
126
+ content = fs.readFileSync(filePath, "utf-8");
127
+ }
128
+ const servers = {
129
+ "ulpi-codemap": entries.codemap,
130
+ "ulpi-memory": entries.memory,
131
+ "tools": entries.gateway
132
+ };
133
+ for (const [name, entry] of Object.entries(servers)) {
134
+ const sectionHeader = `[mcp_servers.${name}]`;
135
+ if (content.includes(sectionHeader)) continue;
136
+ const argsToml = entry.args.map((a) => `"${a}"`).join(", ");
137
+ content += `
138
+ ${sectionHeader}
139
+ command = "${entry.command}"
140
+ args = [${argsToml}]
141
+ `;
142
+ added.push(name);
143
+ }
144
+ if (added.length === 0) return null;
145
+ const dir = path.dirname(filePath);
146
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
147
+ fs.writeFileSync(filePath, content, "utf-8");
148
+ return { cli: "Codex", file: ".codex/config.toml", servers: added };
149
+ }
150
+ function writeKiroProjectMcp(projectDir) {
151
+ const filePath = path.join(projectDir, ".kiro", "settings", "mcp.json");
152
+ const entries = getProjectMcpEntries();
153
+ const added = [];
154
+ const config = readJsonConfig(filePath);
155
+ const mcpServers = config.mcpServers ?? {};
156
+ const servers = {
157
+ "ulpi-codemap": entries.codemap,
158
+ "ulpi-memory": entries.memory,
159
+ "tools": entries.gateway
160
+ };
161
+ for (const [name, entry] of Object.entries(servers)) {
162
+ if (mcpServers[name]) continue;
163
+ mcpServers[name] = { command: entry.command, args: entry.args, transportType: "stdio" };
164
+ added.push(name);
165
+ }
166
+ if (added.length === 0) return null;
167
+ config.mcpServers = mcpServers;
168
+ writeJsonConfig(filePath, config);
169
+ return { cli: "Kiro", file: ".kiro/settings/mcp.json", servers: added };
170
+ }
171
+ function writeGeminiProjectMcp(projectDir) {
172
+ const filePath = path.join(projectDir, ".gemini", "settings.json");
173
+ const entries = getProjectMcpEntries();
174
+ const added = [];
175
+ const config = readJsonConfig(filePath);
176
+ const mcpServers = config.mcpServers ?? {};
177
+ const servers = {
178
+ "ulpi-codemap": entries.codemap,
179
+ "ulpi-memory": entries.memory,
180
+ "tools": entries.gateway
181
+ };
182
+ for (const [name, entry] of Object.entries(servers)) {
183
+ if (mcpServers[name]) continue;
184
+ mcpServers[name] = { command: entry.command, args: entry.args };
185
+ added.push(name);
186
+ }
187
+ if (added.length === 0) return null;
188
+ config.mcpServers = mcpServers;
189
+ writeJsonConfig(filePath, config);
190
+ return { cli: "Gemini", file: ".gemini/settings.json", servers: added };
191
+ }
192
+ function writeOpenCodeProjectMcp(projectDir) {
193
+ const filePath = path.join(projectDir, "opencode.json");
194
+ const entries = getProjectMcpEntries();
195
+ const added = [];
196
+ const config = readJsonConfig(filePath);
197
+ const mcp = config.mcp ?? {};
198
+ const servers = {
199
+ "ulpi-codemap": entries.codemap,
200
+ "ulpi-memory": entries.memory,
201
+ "tools": entries.gateway
202
+ };
203
+ for (const [name, entry] of Object.entries(servers)) {
204
+ if (mcp[name]) continue;
205
+ mcp[name] = { command: [entry.command, ...entry.args], type: "local" };
206
+ added.push(name);
207
+ }
208
+ if (added.length === 0) return null;
209
+ config.mcp = mcp;
210
+ writeJsonConfig(filePath, config);
211
+ return { cli: "OpenCode", file: "opencode.json", servers: added };
212
+ }
213
+ function writeDroidProjectMcp(projectDir) {
214
+ const filePath = path.join(projectDir, ".factory", "mcp.json");
215
+ const entries = getProjectMcpEntries();
216
+ const added = [];
217
+ const config = readJsonConfig(filePath);
218
+ const mcpServers = config.mcpServers ?? {};
219
+ const servers = {
220
+ "ulpi-codemap": entries.codemap,
221
+ "ulpi-memory": entries.memory,
222
+ "tools": entries.gateway
223
+ };
224
+ for (const [name, entry] of Object.entries(servers)) {
225
+ if (mcpServers[name]) continue;
226
+ mcpServers[name] = { command: entry.command, args: entry.args, type: "stdio" };
227
+ added.push(name);
228
+ }
229
+ if (added.length === 0) return null;
230
+ config.mcpServers = mcpServers;
231
+ writeJsonConfig(filePath, config);
232
+ return { cli: "Droid", file: ".factory/mcp.json", servers: added };
233
+ }
234
+ function installMcpForAllClis(projectDir) {
235
+ const results = [];
236
+ const clis = [
237
+ { binary: "codex", writer: writeCodexProjectMcp },
238
+ { binary: "kiro", writer: writeKiroProjectMcp },
239
+ { binary: "gemini", writer: writeGeminiProjectMcp },
240
+ { binary: "opencode", writer: writeOpenCodeProjectMcp },
241
+ { binary: "droid", writer: writeDroidProjectMcp }
242
+ ];
243
+ for (const { binary, writer } of clis) {
244
+ if (!isCommandAvailable(binary)) continue;
245
+ const result = writer(projectDir);
246
+ if (result) results.push(result);
247
+ }
248
+ return results;
249
+ }
250
+ export {
251
+ installGatewayMcpServer,
252
+ installMcpForAllClis,
253
+ installMcpServer,
254
+ installMemoryMcpServer,
255
+ isMcpServerInstalled,
256
+ isMemoryMcpServerInstalled,
257
+ uninstallMcpServer,
258
+ uninstallMemoryMcpServer
259
+ };