typegraph-mcp 0.9.34 → 0.9.36
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.
- package/README.md +9 -3
- package/check.ts +13 -3
- package/cli.ts +211 -35
- package/dist/check.js +8 -3
- package/dist/cli.js +148 -29
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -128,9 +128,13 @@ typegraph-mcp <command> [options]
|
|
|
128
128
|
bench Run benchmarks (token, latency, accuracy)
|
|
129
129
|
start Start the MCP server (stdin/stdout)
|
|
130
130
|
|
|
131
|
-
--yes
|
|
131
|
+
--yes Skip prompts
|
|
132
|
+
--clean-global-codex Also remove a stale global Codex MCP entry for this project
|
|
133
|
+
--help Show help
|
|
132
134
|
```
|
|
133
135
|
|
|
136
|
+
`remove` always cleans up project-local config. If it detects a legacy global `~/.codex/config.toml` entry that points at the current project, it will ask before removing it in interactive mode. In non-interactive mode, pass `--clean-global-codex` to allow that global cleanup.
|
|
137
|
+
|
|
134
138
|
## Troubleshooting
|
|
135
139
|
|
|
136
140
|
Run the health check first — it catches most issues:
|
|
@@ -156,11 +160,13 @@ Add this to your project's `.codex/config.toml`:
|
|
|
156
160
|
|
|
157
161
|
```toml
|
|
158
162
|
[mcp_servers.typegraph]
|
|
159
|
-
command = "
|
|
160
|
-
args = ["
|
|
163
|
+
command = "/absolute/path/to/your-project/plugins/typegraph-mcp/node_modules/.bin/tsx"
|
|
164
|
+
args = ["/absolute/path/to/your-project/plugins/typegraph-mcp/server.ts"]
|
|
161
165
|
env = { TYPEGRAPH_PROJECT_ROOT = "/absolute/path/to/your-project", TYPEGRAPH_TSCONFIG = "/absolute/path/to/your-project/tsconfig.json" }
|
|
162
166
|
```
|
|
163
167
|
|
|
168
|
+
Using the plugin-local `tsx` binary avoids relying on `npx tsx` being resolvable when Codex launches the MCP server.
|
|
169
|
+
|
|
164
170
|
Codex only loads project `.codex/config.toml` files for trusted projects. If needed, add this to `~/.codex/config.toml`:
|
|
165
171
|
|
|
166
172
|
```toml
|
package/check.ts
CHANGED
|
@@ -112,11 +112,19 @@ function hasCodexTypegraphRegistration(content: string): boolean {
|
|
|
112
112
|
return /\[mcp_servers\.typegraph\]/.test(content);
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
function hasCodexTsxLauncher(content: string): boolean {
|
|
116
|
+
return (
|
|
117
|
+
/command\s*=\s*"[^"]*tsx(?:\.cmd)?"/.test(content) ||
|
|
118
|
+
/args\s*=\s*\[[\s\S]*"tsx"/.test(content)
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
115
122
|
function hasCompleteCodexTypegraphRegistration(content: string): boolean {
|
|
116
123
|
return (
|
|
117
124
|
hasCodexTypegraphRegistration(content) &&
|
|
118
125
|
/command\s*=\s*"[^"]+"/.test(content) &&
|
|
119
|
-
/args\s*=\s*\[[\s\S]
|
|
126
|
+
/args\s*=\s*\[[\s\S]*\]/.test(content) &&
|
|
127
|
+
hasCodexTsxLauncher(content) &&
|
|
120
128
|
/TYPEGRAPH_PROJECT_ROOT\s*=/.test(content) &&
|
|
121
129
|
/TYPEGRAPH_TSCONFIG\s*=/.test(content)
|
|
122
130
|
);
|
|
@@ -308,7 +316,8 @@ export async function main(configOverride?: TypegraphConfig): Promise<CheckResul
|
|
|
308
316
|
const codexConfigPath = path.resolve(projectRoot, ".codex/config.toml");
|
|
309
317
|
const hasSection = hasCodexTypegraphRegistration(projectCodexConfig);
|
|
310
318
|
const hasCommand = /command\s*=\s*"[^"]+"/.test(projectCodexConfig);
|
|
311
|
-
const hasArgs = /args\s*=\s*\[[\s\S]
|
|
319
|
+
const hasArgs = /args\s*=\s*\[[\s\S]*\]/.test(projectCodexConfig);
|
|
320
|
+
const hasTsxLauncher = hasCodexTsxLauncher(projectCodexConfig);
|
|
312
321
|
const hasEnvRoot = /TYPEGRAPH_PROJECT_ROOT\s*=/.test(projectCodexConfig);
|
|
313
322
|
const hasEnvTsconfig = /TYPEGRAPH_TSCONFIG\s*=/.test(projectCodexConfig);
|
|
314
323
|
if (hasProjectCodexRegistration) {
|
|
@@ -324,7 +333,8 @@ export async function main(configOverride?: TypegraphConfig): Promise<CheckResul
|
|
|
324
333
|
const issues: string[] = [];
|
|
325
334
|
if (!hasSection) issues.push("[mcp_servers.typegraph] section is missing");
|
|
326
335
|
if (!hasCommand) issues.push("command is missing");
|
|
327
|
-
if (!hasArgs) issues.push("args
|
|
336
|
+
if (!hasArgs) issues.push("args are missing");
|
|
337
|
+
if (!hasTsxLauncher) issues.push("command should point to tsx or args should include 'tsx'");
|
|
328
338
|
if (!hasEnvRoot) issues.push("TYPEGRAPH_PROJECT_ROOT is missing");
|
|
329
339
|
if (!hasEnvTsconfig) issues.push("TYPEGRAPH_TSCONFIG is missing");
|
|
330
340
|
fail(
|
package/cli.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
* Options:
|
|
12
12
|
* --yes Skip confirmation prompts (accept all defaults)
|
|
13
|
+
* --clean-global-codex Also remove a stale global Codex MCP entry for this project
|
|
13
14
|
* --help Show help
|
|
14
15
|
*/
|
|
15
16
|
|
|
@@ -35,6 +36,17 @@ interface AgentDef {
|
|
|
35
36
|
detect: (projectRoot: string) => boolean;
|
|
36
37
|
}
|
|
37
38
|
|
|
39
|
+
interface LegacyGlobalCodexCleanup {
|
|
40
|
+
globalConfigPath: string;
|
|
41
|
+
nextContent: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface RemovePluginOptions {
|
|
45
|
+
removeGlobalCodex: boolean;
|
|
46
|
+
legacyGlobalCodexCleanup: LegacyGlobalCodexCleanup | null;
|
|
47
|
+
warnAboutGlobalCodex: boolean;
|
|
48
|
+
}
|
|
49
|
+
|
|
38
50
|
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
39
51
|
|
|
40
52
|
const AGENT_SNIPPET = `
|
|
@@ -163,8 +175,9 @@ Commands:
|
|
|
163
175
|
start Start the MCP server (stdin/stdout)
|
|
164
176
|
|
|
165
177
|
Options:
|
|
166
|
-
--yes
|
|
167
|
-
--
|
|
178
|
+
--yes Skip confirmation prompts (accept all defaults)
|
|
179
|
+
--clean-global-codex Also remove a stale global Codex MCP entry for this project
|
|
180
|
+
--help Show this help
|
|
168
181
|
`.trim();
|
|
169
182
|
|
|
170
183
|
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
@@ -207,12 +220,78 @@ function getAbsoluteMcpServerEntry(projectRoot: string): {
|
|
|
207
220
|
};
|
|
208
221
|
}
|
|
209
222
|
|
|
223
|
+
function getCodexMcpServerEntry(projectRoot: string): {
|
|
224
|
+
command: string;
|
|
225
|
+
args: string[];
|
|
226
|
+
env: Record<string, string>;
|
|
227
|
+
} {
|
|
228
|
+
return {
|
|
229
|
+
command: path.resolve(projectRoot, PLUGIN_DIR_NAME, "node_modules/.bin/tsx"),
|
|
230
|
+
args: [path.resolve(projectRoot, PLUGIN_DIR_NAME, "server.ts")],
|
|
231
|
+
env: {
|
|
232
|
+
TYPEGRAPH_PROJECT_ROOT: projectRoot,
|
|
233
|
+
TYPEGRAPH_TSCONFIG: path.resolve(projectRoot, "tsconfig.json"),
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
210
238
|
function getCodexConfigPath(projectRoot: string): string {
|
|
211
239
|
return path.resolve(projectRoot, ".codex/config.toml");
|
|
212
240
|
}
|
|
213
241
|
|
|
214
|
-
function
|
|
215
|
-
return
|
|
242
|
+
function isTomlSectionGroup(sectionName: string | null, prefix: string): boolean {
|
|
243
|
+
return sectionName === prefix || sectionName?.startsWith(`${prefix}.`) === true;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function splitTomlBlocks(content: string): Array<{ sectionName: string | null; raw: string }> {
|
|
247
|
+
const lines = content.split(/\r?\n/);
|
|
248
|
+
const blocks: Array<{ sectionName: string | null; raw: string }> = [];
|
|
249
|
+
let sectionName: string | null = null;
|
|
250
|
+
let currentLines: string[] = [];
|
|
251
|
+
|
|
252
|
+
for (const line of lines) {
|
|
253
|
+
const match = line.match(/^\[([^\]]+)\]\s*$/);
|
|
254
|
+
if (match) {
|
|
255
|
+
if (currentLines.length > 0 || sectionName !== null) {
|
|
256
|
+
blocks.push({ sectionName, raw: currentLines.join("\n") });
|
|
257
|
+
}
|
|
258
|
+
sectionName = match[1]!;
|
|
259
|
+
currentLines = [line];
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
currentLines.push(line);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (currentLines.length > 0 || sectionName !== null) {
|
|
267
|
+
blocks.push({ sectionName, raw: currentLines.join("\n") });
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return blocks;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function removeTomlSectionGroup(
|
|
274
|
+
content: string,
|
|
275
|
+
prefix: string
|
|
276
|
+
): { content: string; removed: boolean; removedContent: string } {
|
|
277
|
+
const blocks = splitTomlBlocks(content);
|
|
278
|
+
const removedBlocks = blocks.filter((block) => isTomlSectionGroup(block.sectionName, prefix));
|
|
279
|
+
if (removedBlocks.length === 0) {
|
|
280
|
+
return { content, removed: false, removedContent: "" };
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const keptBlocks = blocks.filter((block) => !isTomlSectionGroup(block.sectionName, prefix));
|
|
284
|
+
const nextContent = keptBlocks
|
|
285
|
+
.map((block) => block.raw)
|
|
286
|
+
.join("\n")
|
|
287
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
288
|
+
.trimEnd();
|
|
289
|
+
|
|
290
|
+
return {
|
|
291
|
+
content: nextContent ? `${nextContent}\n` : "",
|
|
292
|
+
removed: true,
|
|
293
|
+
removedContent: removedBlocks.map((block) => block.raw).join("\n").trim(),
|
|
294
|
+
};
|
|
216
295
|
}
|
|
217
296
|
|
|
218
297
|
function upsertCodexMcpSection(content: string, block: string): { content: string; changed: boolean } {
|
|
@@ -236,12 +315,13 @@ function upsertCodexMcpSection(content: string, block: string): { content: strin
|
|
|
236
315
|
}
|
|
237
316
|
|
|
238
317
|
function makeCodexMcpBlock(projectRoot: string): string {
|
|
239
|
-
const absoluteEntry =
|
|
318
|
+
const absoluteEntry = getCodexMcpServerEntry(projectRoot);
|
|
319
|
+
const args = absoluteEntry.args.map((arg) => `"${arg}"`).join(", ");
|
|
240
320
|
return [
|
|
241
321
|
"",
|
|
242
322
|
"[mcp_servers.typegraph]",
|
|
243
323
|
`command = "${absoluteEntry.command}"`,
|
|
244
|
-
`args = [
|
|
324
|
+
`args = [${args}]`,
|
|
245
325
|
`env = { TYPEGRAPH_PROJECT_ROOT = "${absoluteEntry.env.TYPEGRAPH_PROJECT_ROOT}", TYPEGRAPH_TSCONFIG = "${absoluteEntry.env.TYPEGRAPH_TSCONFIG}" }`,
|
|
246
326
|
"",
|
|
247
327
|
].join("\n");
|
|
@@ -288,6 +368,92 @@ function isCodexProjectTrusted(projectRoot: string): boolean {
|
|
|
288
368
|
return matchesTrustedProject();
|
|
289
369
|
}
|
|
290
370
|
|
|
371
|
+
function pathEqualsOrContains(candidatePath: string, targetPath: string): boolean {
|
|
372
|
+
const resolvedCandidate = path.resolve(candidatePath);
|
|
373
|
+
const resolvedTarget = path.resolve(targetPath);
|
|
374
|
+
if (resolvedCandidate === resolvedTarget || resolvedCandidate.startsWith(`${resolvedTarget}${path.sep}`)) {
|
|
375
|
+
return true;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
try {
|
|
379
|
+
const realCandidate = fs.realpathSync(candidatePath);
|
|
380
|
+
const realTarget = fs.realpathSync(targetPath);
|
|
381
|
+
return realCandidate === realTarget || realCandidate.startsWith(`${realTarget}${path.sep}`);
|
|
382
|
+
} catch {
|
|
383
|
+
return false;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function findLegacyGlobalCodexCleanup(projectRoot: string): LegacyGlobalCodexCleanup | null {
|
|
388
|
+
const home = process.env.HOME;
|
|
389
|
+
if (!home) return null;
|
|
390
|
+
|
|
391
|
+
const globalConfigPath = path.join(home, ".codex/config.toml");
|
|
392
|
+
if (!fs.existsSync(globalConfigPath)) return null;
|
|
393
|
+
|
|
394
|
+
const content = fs.readFileSync(globalConfigPath, "utf-8");
|
|
395
|
+
const { content: nextContent, removed, removedContent } = removeTomlSectionGroup(content, "mcp_servers.typegraph");
|
|
396
|
+
if (!removed) return null;
|
|
397
|
+
|
|
398
|
+
const pluginRoot = path.resolve(projectRoot, PLUGIN_DIR_NAME);
|
|
399
|
+
const quotedPaths = Array.from(removedContent.matchAll(/"([^"\n]+)"/g), (match) => match[1]!);
|
|
400
|
+
const looksProjectSpecific = quotedPaths.some((quotedPath) =>
|
|
401
|
+
pathEqualsOrContains(quotedPath, projectRoot) ||
|
|
402
|
+
pathEqualsOrContains(quotedPath, pluginRoot)
|
|
403
|
+
);
|
|
404
|
+
|
|
405
|
+
if (!looksProjectSpecific) {
|
|
406
|
+
return null;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return { globalConfigPath, nextContent };
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function removeLegacyGlobalCodexMcp(cleanup: LegacyGlobalCodexCleanup): void {
|
|
413
|
+
if (cleanup.nextContent === "") {
|
|
414
|
+
fs.unlinkSync(cleanup.globalConfigPath);
|
|
415
|
+
} else {
|
|
416
|
+
fs.writeFileSync(cleanup.globalConfigPath, cleanup.nextContent);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
p.log.info("~/.codex/config.toml: removed stale global typegraph MCP server entry for this project");
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
async function resolveRemovePluginOptions(
|
|
423
|
+
projectRoot: string,
|
|
424
|
+
yes: boolean,
|
|
425
|
+
cleanGlobalCodex: boolean
|
|
426
|
+
): Promise<RemovePluginOptions> {
|
|
427
|
+
const legacyGlobalCodexCleanup = findLegacyGlobalCodexCleanup(projectRoot);
|
|
428
|
+
let removeGlobalCodex = cleanGlobalCodex;
|
|
429
|
+
|
|
430
|
+
if (legacyGlobalCodexCleanup && !cleanGlobalCodex && !yes) {
|
|
431
|
+
const shouldRemoveGlobal = await p.confirm({
|
|
432
|
+
message: "Also remove the stale global Codex MCP entry for this project from ~/.codex/config.toml?",
|
|
433
|
+
initialValue: false,
|
|
434
|
+
});
|
|
435
|
+
if (p.isCancel(shouldRemoveGlobal)) {
|
|
436
|
+
p.cancel("Removal cancelled.");
|
|
437
|
+
process.exit(0);
|
|
438
|
+
}
|
|
439
|
+
removeGlobalCodex = shouldRemoveGlobal;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
return {
|
|
443
|
+
removeGlobalCodex,
|
|
444
|
+
legacyGlobalCodexCleanup,
|
|
445
|
+
warnAboutGlobalCodex: legacyGlobalCodexCleanup !== null && !removeGlobalCodex,
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function warnAboutStaleGlobalCodex(): void {
|
|
450
|
+
p.log.warn(
|
|
451
|
+
"Left a stale global Codex MCP entry for this project in ~/.codex/config.toml. " +
|
|
452
|
+
"Codex may show MCP startup warnings or errors until you remove it. " +
|
|
453
|
+
"Re-run `typegraph-mcp remove --clean-global-codex` or remove the `typegraph` block manually."
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
|
|
291
457
|
/** Register the typegraph MCP server in agent-specific config files */
|
|
292
458
|
function registerMcpServers(projectRoot: string, selectedAgents: AgentId[]): void {
|
|
293
459
|
if (selectedAgents.includes("cursor")) {
|
|
@@ -401,26 +567,19 @@ function registerCodexMcp(projectRoot: string): void {
|
|
|
401
567
|
function deregisterCodexMcp(projectRoot: string): void {
|
|
402
568
|
const configPath = ".codex/config.toml";
|
|
403
569
|
const fullPath = getCodexConfigPath(projectRoot);
|
|
404
|
-
if (
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
content = content.replace(/\n{3,}/g, "\n\n").trimEnd() + "\n";
|
|
417
|
-
|
|
418
|
-
if (content.trim() === "") {
|
|
419
|
-
fs.unlinkSync(fullPath);
|
|
420
|
-
} else {
|
|
421
|
-
fs.writeFileSync(fullPath, content);
|
|
570
|
+
if (fs.existsSync(fullPath)) {
|
|
571
|
+
const content = fs.readFileSync(fullPath, "utf-8");
|
|
572
|
+
const { content: nextContent, removed } = removeTomlSectionGroup(content, "mcp_servers.typegraph");
|
|
573
|
+
|
|
574
|
+
if (removed) {
|
|
575
|
+
if (nextContent === "") {
|
|
576
|
+
fs.unlinkSync(fullPath);
|
|
577
|
+
} else {
|
|
578
|
+
fs.writeFileSync(fullPath, nextContent);
|
|
579
|
+
}
|
|
580
|
+
p.log.info(`${configPath}: removed typegraph MCP server`);
|
|
581
|
+
}
|
|
422
582
|
}
|
|
423
|
-
p.log.info(`${configPath}: removed typegraph MCP server`);
|
|
424
583
|
}
|
|
425
584
|
|
|
426
585
|
// ─── TSConfig Exclude ─────────────────────────────────────────────────────────
|
|
@@ -606,7 +765,11 @@ async function setup(yes: boolean): Promise<void> {
|
|
|
606
765
|
}
|
|
607
766
|
|
|
608
767
|
if (action === "remove") {
|
|
609
|
-
await
|
|
768
|
+
const removeOptions = await resolveRemovePluginOptions(projectRoot, false, false);
|
|
769
|
+
await removePlugin(projectRoot, targetDir, removeOptions);
|
|
770
|
+
if (removeOptions.warnAboutGlobalCodex) {
|
|
771
|
+
warnAboutStaleGlobalCodex();
|
|
772
|
+
}
|
|
610
773
|
return;
|
|
611
774
|
}
|
|
612
775
|
|
|
@@ -748,16 +911,26 @@ async function setup(yes: boolean): Promise<void> {
|
|
|
748
911
|
|
|
749
912
|
// ─── Remove Command ──────────────────────────────────────────────────────────
|
|
750
913
|
|
|
751
|
-
async function removePlugin(
|
|
914
|
+
async function removePlugin(
|
|
915
|
+
projectRoot: string,
|
|
916
|
+
pluginDir: string,
|
|
917
|
+
options: RemovePluginOptions
|
|
918
|
+
): Promise<void> {
|
|
752
919
|
const s = p.spinner();
|
|
753
920
|
s.start("Removing typegraph-mcp...");
|
|
754
921
|
|
|
755
|
-
// 1.
|
|
922
|
+
// 1. Deregister MCP server from agent config files while project paths still exist
|
|
923
|
+
deregisterMcpServers(projectRoot);
|
|
924
|
+
if (options.removeGlobalCodex && options.legacyGlobalCodexCleanup) {
|
|
925
|
+
removeLegacyGlobalCodexMcp(options.legacyGlobalCodexCleanup);
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
// 2. Remove plugin directory
|
|
756
929
|
if (fs.existsSync(pluginDir)) {
|
|
757
930
|
fs.rmSync(pluginDir, { recursive: true });
|
|
758
931
|
}
|
|
759
932
|
|
|
760
|
-
//
|
|
933
|
+
// 3. Remove .agents/skills/ entries (only typegraph-mcp skills, not the whole dir)
|
|
761
934
|
const agentsSkillsDir = path.resolve(projectRoot, ".agents/skills");
|
|
762
935
|
for (const skill of SKILL_NAMES) {
|
|
763
936
|
const skillDir = path.join(agentsSkillsDir, skill);
|
|
@@ -774,7 +947,7 @@ async function removePlugin(projectRoot: string, pluginDir: string): Promise<voi
|
|
|
774
947
|
}
|
|
775
948
|
}
|
|
776
949
|
|
|
777
|
-
//
|
|
950
|
+
// 4. Remove agent instruction snippet from all known agent files
|
|
778
951
|
const allAgentFiles = AGENT_IDS
|
|
779
952
|
.map((id) => AGENTS[id].agentFile)
|
|
780
953
|
.filter((f): f is string => f !== null);
|
|
@@ -797,7 +970,7 @@ async function removePlugin(projectRoot: string, pluginDir: string): Promise<voi
|
|
|
797
970
|
}
|
|
798
971
|
}
|
|
799
972
|
|
|
800
|
-
//
|
|
973
|
+
// 5. Remove --plugin-dir ./plugins/typegraph-mcp from CLAUDE.md
|
|
801
974
|
const claudeMdPath = path.resolve(projectRoot, "CLAUDE.md");
|
|
802
975
|
if (fs.existsSync(claudeMdPath)) {
|
|
803
976
|
let content = fs.readFileSync(claudeMdPath, "utf-8");
|
|
@@ -807,9 +980,6 @@ async function removePlugin(projectRoot: string, pluginDir: string): Promise<voi
|
|
|
807
980
|
|
|
808
981
|
s.stop("Removed typegraph-mcp");
|
|
809
982
|
|
|
810
|
-
// 5. Deregister MCP server from agent config files
|
|
811
|
-
deregisterMcpServers(projectRoot);
|
|
812
|
-
|
|
813
983
|
p.outro("typegraph-mcp has been uninstalled from this project.");
|
|
814
984
|
}
|
|
815
985
|
|
|
@@ -913,6 +1083,7 @@ async function runVerification(pluginDir: string, selectedAgents: AgentId[]): Pr
|
|
|
913
1083
|
async function remove(yes: boolean): Promise<void> {
|
|
914
1084
|
const projectRoot = process.cwd();
|
|
915
1085
|
const pluginDir = path.resolve(projectRoot, PLUGIN_DIR_NAME);
|
|
1086
|
+
const cleanGlobalCodex = args.includes("--clean-global-codex");
|
|
916
1087
|
|
|
917
1088
|
process.stdout.write("\x1Bc");
|
|
918
1089
|
p.intro("TypeGraph MCP Remove");
|
|
@@ -930,7 +1101,12 @@ async function remove(yes: boolean): Promise<void> {
|
|
|
930
1101
|
}
|
|
931
1102
|
}
|
|
932
1103
|
|
|
933
|
-
await
|
|
1104
|
+
const removeOptions = await resolveRemovePluginOptions(projectRoot, yes, cleanGlobalCodex);
|
|
1105
|
+
await removePlugin(projectRoot, pluginDir, removeOptions);
|
|
1106
|
+
|
|
1107
|
+
if (removeOptions.warnAboutGlobalCodex) {
|
|
1108
|
+
warnAboutStaleGlobalCodex();
|
|
1109
|
+
}
|
|
934
1110
|
}
|
|
935
1111
|
|
|
936
1112
|
// ─── Check Command ───────────────────────────────────────────────────────────
|
package/dist/check.js
CHANGED
|
@@ -440,8 +440,11 @@ function readProjectCodexConfig(projectRoot) {
|
|
|
440
440
|
function hasCodexTypegraphRegistration(content) {
|
|
441
441
|
return /\[mcp_servers\.typegraph\]/.test(content);
|
|
442
442
|
}
|
|
443
|
+
function hasCodexTsxLauncher(content) {
|
|
444
|
+
return /command\s*=\s*"[^"]*tsx(?:\.cmd)?"/.test(content) || /args\s*=\s*\[[\s\S]*"tsx"/.test(content);
|
|
445
|
+
}
|
|
443
446
|
function hasCompleteCodexTypegraphRegistration(content) {
|
|
444
|
-
return hasCodexTypegraphRegistration(content) && /command\s*=\s*"[^"]+"/.test(content) && /args\s*=\s*\[[\s\S]
|
|
447
|
+
return hasCodexTypegraphRegistration(content) && /command\s*=\s*"[^"]+"/.test(content) && /args\s*=\s*\[[\s\S]*\]/.test(content) && hasCodexTsxLauncher(content) && /TYPEGRAPH_PROJECT_ROOT\s*=/.test(content) && /TYPEGRAPH_TSCONFIG\s*=/.test(content);
|
|
445
448
|
}
|
|
446
449
|
function hasTrustedCodexProject(projectRoot) {
|
|
447
450
|
const home = process.env.HOME;
|
|
@@ -584,7 +587,8 @@ async function main(configOverride) {
|
|
|
584
587
|
const codexConfigPath = path3.resolve(projectRoot, ".codex/config.toml");
|
|
585
588
|
const hasSection = hasCodexTypegraphRegistration(projectCodexConfig);
|
|
586
589
|
const hasCommand = /command\s*=\s*"[^"]+"/.test(projectCodexConfig);
|
|
587
|
-
const hasArgs = /args\s*=\s*\[[\s\S]
|
|
590
|
+
const hasArgs = /args\s*=\s*\[[\s\S]*\]/.test(projectCodexConfig);
|
|
591
|
+
const hasTsxLauncher = hasCodexTsxLauncher(projectCodexConfig);
|
|
588
592
|
const hasEnvRoot = /TYPEGRAPH_PROJECT_ROOT\s*=/.test(projectCodexConfig);
|
|
589
593
|
const hasEnvTsconfig = /TYPEGRAPH_TSCONFIG\s*=/.test(projectCodexConfig);
|
|
590
594
|
if (hasProjectCodexRegistration) {
|
|
@@ -600,7 +604,8 @@ async function main(configOverride) {
|
|
|
600
604
|
const issues = [];
|
|
601
605
|
if (!hasSection) issues.push("[mcp_servers.typegraph] section is missing");
|
|
602
606
|
if (!hasCommand) issues.push("command is missing");
|
|
603
|
-
if (!hasArgs) issues.push("args
|
|
607
|
+
if (!hasArgs) issues.push("args are missing");
|
|
608
|
+
if (!hasTsxLauncher) issues.push("command should point to tsx or args should include 'tsx'");
|
|
604
609
|
if (!hasEnvRoot) issues.push("TYPEGRAPH_PROJECT_ROOT is missing");
|
|
605
610
|
if (!hasEnvTsconfig) issues.push("TYPEGRAPH_TSCONFIG is missing");
|
|
606
611
|
fail(
|
package/dist/cli.js
CHANGED
|
@@ -447,8 +447,11 @@ function readProjectCodexConfig(projectRoot3) {
|
|
|
447
447
|
function hasCodexTypegraphRegistration(content) {
|
|
448
448
|
return /\[mcp_servers\.typegraph\]/.test(content);
|
|
449
449
|
}
|
|
450
|
+
function hasCodexTsxLauncher(content) {
|
|
451
|
+
return /command\s*=\s*"[^"]*tsx(?:\.cmd)?"/.test(content) || /args\s*=\s*\[[\s\S]*"tsx"/.test(content);
|
|
452
|
+
}
|
|
450
453
|
function hasCompleteCodexTypegraphRegistration(content) {
|
|
451
|
-
return hasCodexTypegraphRegistration(content) && /command\s*=\s*"[^"]+"/.test(content) && /args\s*=\s*\[[\s\S]
|
|
454
|
+
return hasCodexTypegraphRegistration(content) && /command\s*=\s*"[^"]+"/.test(content) && /args\s*=\s*\[[\s\S]*\]/.test(content) && hasCodexTsxLauncher(content) && /TYPEGRAPH_PROJECT_ROOT\s*=/.test(content) && /TYPEGRAPH_TSCONFIG\s*=/.test(content);
|
|
452
455
|
}
|
|
453
456
|
function hasTrustedCodexProject(projectRoot3) {
|
|
454
457
|
const home = process.env.HOME;
|
|
@@ -591,7 +594,8 @@ async function main(configOverride) {
|
|
|
591
594
|
const codexConfigPath = path3.resolve(projectRoot3, ".codex/config.toml");
|
|
592
595
|
const hasSection = hasCodexTypegraphRegistration(projectCodexConfig);
|
|
593
596
|
const hasCommand = /command\s*=\s*"[^"]+"/.test(projectCodexConfig);
|
|
594
|
-
const hasArgs = /args\s*=\s*\[[\s\S]
|
|
597
|
+
const hasArgs = /args\s*=\s*\[[\s\S]*\]/.test(projectCodexConfig);
|
|
598
|
+
const hasTsxLauncher = hasCodexTsxLauncher(projectCodexConfig);
|
|
595
599
|
const hasEnvRoot = /TYPEGRAPH_PROJECT_ROOT\s*=/.test(projectCodexConfig);
|
|
596
600
|
const hasEnvTsconfig = /TYPEGRAPH_TSCONFIG\s*=/.test(projectCodexConfig);
|
|
597
601
|
if (hasProjectCodexRegistration) {
|
|
@@ -607,7 +611,8 @@ async function main(configOverride) {
|
|
|
607
611
|
const issues = [];
|
|
608
612
|
if (!hasSection) issues.push("[mcp_servers.typegraph] section is missing");
|
|
609
613
|
if (!hasCommand) issues.push("command is missing");
|
|
610
|
-
if (!hasArgs) issues.push("args
|
|
614
|
+
if (!hasArgs) issues.push("args are missing");
|
|
615
|
+
if (!hasTsxLauncher) issues.push("command should point to tsx or args should include 'tsx'");
|
|
611
616
|
if (!hasEnvRoot) issues.push("TYPEGRAPH_PROJECT_ROOT is missing");
|
|
612
617
|
if (!hasEnvTsconfig) issues.push("TYPEGRAPH_TSCONFIG is missing");
|
|
613
618
|
fail(
|
|
@@ -3024,8 +3029,9 @@ Commands:
|
|
|
3024
3029
|
start Start the MCP server (stdin/stdout)
|
|
3025
3030
|
|
|
3026
3031
|
Options:
|
|
3027
|
-
--yes
|
|
3028
|
-
--
|
|
3032
|
+
--yes Skip confirmation prompts (accept all defaults)
|
|
3033
|
+
--clean-global-codex Also remove a stale global Codex MCP entry for this project
|
|
3034
|
+
--help Show this help
|
|
3029
3035
|
`.trim();
|
|
3030
3036
|
function copyFile(src, dest) {
|
|
3031
3037
|
const destDir = path9.dirname(dest);
|
|
@@ -3045,10 +3051,10 @@ var MCP_SERVER_ENTRY = {
|
|
|
3045
3051
|
TYPEGRAPH_TSCONFIG: "./tsconfig.json"
|
|
3046
3052
|
}
|
|
3047
3053
|
};
|
|
3048
|
-
function
|
|
3054
|
+
function getCodexMcpServerEntry(projectRoot3) {
|
|
3049
3055
|
return {
|
|
3050
|
-
command: "
|
|
3051
|
-
args: [
|
|
3056
|
+
command: path9.resolve(projectRoot3, PLUGIN_DIR_NAME, "node_modules/.bin/tsx"),
|
|
3057
|
+
args: [path9.resolve(projectRoot3, PLUGIN_DIR_NAME, "server.ts")],
|
|
3052
3058
|
env: {
|
|
3053
3059
|
TYPEGRAPH_PROJECT_ROOT: projectRoot3,
|
|
3054
3060
|
TYPEGRAPH_TSCONFIG: path9.resolve(projectRoot3, "tsconfig.json")
|
|
@@ -3058,8 +3064,45 @@ function getAbsoluteMcpServerEntry(projectRoot3) {
|
|
|
3058
3064
|
function getCodexConfigPath(projectRoot3) {
|
|
3059
3065
|
return path9.resolve(projectRoot3, ".codex/config.toml");
|
|
3060
3066
|
}
|
|
3061
|
-
function
|
|
3062
|
-
return
|
|
3067
|
+
function isTomlSectionGroup(sectionName, prefix) {
|
|
3068
|
+
return sectionName === prefix || sectionName?.startsWith(`${prefix}.`) === true;
|
|
3069
|
+
}
|
|
3070
|
+
function splitTomlBlocks(content) {
|
|
3071
|
+
const lines = content.split(/\r?\n/);
|
|
3072
|
+
const blocks = [];
|
|
3073
|
+
let sectionName = null;
|
|
3074
|
+
let currentLines = [];
|
|
3075
|
+
for (const line of lines) {
|
|
3076
|
+
const match = line.match(/^\[([^\]]+)\]\s*$/);
|
|
3077
|
+
if (match) {
|
|
3078
|
+
if (currentLines.length > 0 || sectionName !== null) {
|
|
3079
|
+
blocks.push({ sectionName, raw: currentLines.join("\n") });
|
|
3080
|
+
}
|
|
3081
|
+
sectionName = match[1];
|
|
3082
|
+
currentLines = [line];
|
|
3083
|
+
continue;
|
|
3084
|
+
}
|
|
3085
|
+
currentLines.push(line);
|
|
3086
|
+
}
|
|
3087
|
+
if (currentLines.length > 0 || sectionName !== null) {
|
|
3088
|
+
blocks.push({ sectionName, raw: currentLines.join("\n") });
|
|
3089
|
+
}
|
|
3090
|
+
return blocks;
|
|
3091
|
+
}
|
|
3092
|
+
function removeTomlSectionGroup(content, prefix) {
|
|
3093
|
+
const blocks = splitTomlBlocks(content);
|
|
3094
|
+
const removedBlocks = blocks.filter((block) => isTomlSectionGroup(block.sectionName, prefix));
|
|
3095
|
+
if (removedBlocks.length === 0) {
|
|
3096
|
+
return { content, removed: false, removedContent: "" };
|
|
3097
|
+
}
|
|
3098
|
+
const keptBlocks = blocks.filter((block) => !isTomlSectionGroup(block.sectionName, prefix));
|
|
3099
|
+
const nextContent = keptBlocks.map((block) => block.raw).join("\n").replace(/\n{3,}/g, "\n\n").trimEnd();
|
|
3100
|
+
return {
|
|
3101
|
+
content: nextContent ? `${nextContent}
|
|
3102
|
+
` : "",
|
|
3103
|
+
removed: true,
|
|
3104
|
+
removedContent: removedBlocks.map((block) => block.raw).join("\n").trim()
|
|
3105
|
+
};
|
|
3063
3106
|
}
|
|
3064
3107
|
function upsertCodexMcpSection(content, block) {
|
|
3065
3108
|
const sectionRe = /\n?\[mcp_servers\.typegraph\]\n[\s\S]*?(?=\n\[|$)/;
|
|
@@ -3078,12 +3121,13 @@ ${normalizedBlock}
|
|
|
3078
3121
|
return { content: nextContent, changed: true };
|
|
3079
3122
|
}
|
|
3080
3123
|
function makeCodexMcpBlock(projectRoot3) {
|
|
3081
|
-
const absoluteEntry =
|
|
3124
|
+
const absoluteEntry = getCodexMcpServerEntry(projectRoot3);
|
|
3125
|
+
const args2 = absoluteEntry.args.map((arg) => `"${arg}"`).join(", ");
|
|
3082
3126
|
return [
|
|
3083
3127
|
"",
|
|
3084
3128
|
"[mcp_servers.typegraph]",
|
|
3085
3129
|
`command = "${absoluteEntry.command}"`,
|
|
3086
|
-
`args = [
|
|
3130
|
+
`args = [${args2}]`,
|
|
3087
3131
|
`env = { TYPEGRAPH_PROJECT_ROOT = "${absoluteEntry.env.TYPEGRAPH_PROJECT_ROOT}", TYPEGRAPH_TSCONFIG = "${absoluteEntry.env.TYPEGRAPH_TSCONFIG}" }`,
|
|
3088
3132
|
""
|
|
3089
3133
|
].join("\n");
|
|
@@ -3118,6 +3162,71 @@ function isCodexProjectTrusted(projectRoot3) {
|
|
|
3118
3162
|
}
|
|
3119
3163
|
return matchesTrustedProject();
|
|
3120
3164
|
}
|
|
3165
|
+
function pathEqualsOrContains(candidatePath, targetPath) {
|
|
3166
|
+
const resolvedCandidate = path9.resolve(candidatePath);
|
|
3167
|
+
const resolvedTarget = path9.resolve(targetPath);
|
|
3168
|
+
if (resolvedCandidate === resolvedTarget || resolvedCandidate.startsWith(`${resolvedTarget}${path9.sep}`)) {
|
|
3169
|
+
return true;
|
|
3170
|
+
}
|
|
3171
|
+
try {
|
|
3172
|
+
const realCandidate = fs8.realpathSync(candidatePath);
|
|
3173
|
+
const realTarget = fs8.realpathSync(targetPath);
|
|
3174
|
+
return realCandidate === realTarget || realCandidate.startsWith(`${realTarget}${path9.sep}`);
|
|
3175
|
+
} catch {
|
|
3176
|
+
return false;
|
|
3177
|
+
}
|
|
3178
|
+
}
|
|
3179
|
+
function findLegacyGlobalCodexCleanup(projectRoot3) {
|
|
3180
|
+
const home = process.env.HOME;
|
|
3181
|
+
if (!home) return null;
|
|
3182
|
+
const globalConfigPath = path9.join(home, ".codex/config.toml");
|
|
3183
|
+
if (!fs8.existsSync(globalConfigPath)) return null;
|
|
3184
|
+
const content = fs8.readFileSync(globalConfigPath, "utf-8");
|
|
3185
|
+
const { content: nextContent, removed, removedContent } = removeTomlSectionGroup(content, "mcp_servers.typegraph");
|
|
3186
|
+
if (!removed) return null;
|
|
3187
|
+
const pluginRoot = path9.resolve(projectRoot3, PLUGIN_DIR_NAME);
|
|
3188
|
+
const quotedPaths = Array.from(removedContent.matchAll(/"([^"\n]+)"/g), (match) => match[1]);
|
|
3189
|
+
const looksProjectSpecific = quotedPaths.some(
|
|
3190
|
+
(quotedPath) => pathEqualsOrContains(quotedPath, projectRoot3) || pathEqualsOrContains(quotedPath, pluginRoot)
|
|
3191
|
+
);
|
|
3192
|
+
if (!looksProjectSpecific) {
|
|
3193
|
+
return null;
|
|
3194
|
+
}
|
|
3195
|
+
return { globalConfigPath, nextContent };
|
|
3196
|
+
}
|
|
3197
|
+
function removeLegacyGlobalCodexMcp(cleanup) {
|
|
3198
|
+
if (cleanup.nextContent === "") {
|
|
3199
|
+
fs8.unlinkSync(cleanup.globalConfigPath);
|
|
3200
|
+
} else {
|
|
3201
|
+
fs8.writeFileSync(cleanup.globalConfigPath, cleanup.nextContent);
|
|
3202
|
+
}
|
|
3203
|
+
p.log.info("~/.codex/config.toml: removed stale global typegraph MCP server entry for this project");
|
|
3204
|
+
}
|
|
3205
|
+
async function resolveRemovePluginOptions(projectRoot3, yes2, cleanGlobalCodex) {
|
|
3206
|
+
const legacyGlobalCodexCleanup = findLegacyGlobalCodexCleanup(projectRoot3);
|
|
3207
|
+
let removeGlobalCodex = cleanGlobalCodex;
|
|
3208
|
+
if (legacyGlobalCodexCleanup && !cleanGlobalCodex && !yes2) {
|
|
3209
|
+
const shouldRemoveGlobal = await p.confirm({
|
|
3210
|
+
message: "Also remove the stale global Codex MCP entry for this project from ~/.codex/config.toml?",
|
|
3211
|
+
initialValue: false
|
|
3212
|
+
});
|
|
3213
|
+
if (p.isCancel(shouldRemoveGlobal)) {
|
|
3214
|
+
p.cancel("Removal cancelled.");
|
|
3215
|
+
process.exit(0);
|
|
3216
|
+
}
|
|
3217
|
+
removeGlobalCodex = shouldRemoveGlobal;
|
|
3218
|
+
}
|
|
3219
|
+
return {
|
|
3220
|
+
removeGlobalCodex,
|
|
3221
|
+
legacyGlobalCodexCleanup,
|
|
3222
|
+
warnAboutGlobalCodex: legacyGlobalCodexCleanup !== null && !removeGlobalCodex
|
|
3223
|
+
};
|
|
3224
|
+
}
|
|
3225
|
+
function warnAboutStaleGlobalCodex() {
|
|
3226
|
+
p.log.warn(
|
|
3227
|
+
"Left a stale global Codex MCP entry for this project in ~/.codex/config.toml. Codex may show MCP startup warnings or errors until you remove it. Re-run `typegraph-mcp remove --clean-global-codex` or remove the `typegraph` block manually."
|
|
3228
|
+
);
|
|
3229
|
+
}
|
|
3121
3230
|
function registerMcpServers(projectRoot3, selectedAgents) {
|
|
3122
3231
|
if (selectedAgents.includes("cursor")) {
|
|
3123
3232
|
registerJsonMcp(projectRoot3, ".cursor/mcp.json", "mcpServers");
|
|
@@ -3205,20 +3314,18 @@ function registerCodexMcp(projectRoot3) {
|
|
|
3205
3314
|
function deregisterCodexMcp(projectRoot3) {
|
|
3206
3315
|
const configPath = ".codex/config.toml";
|
|
3207
3316
|
const fullPath = getCodexConfigPath(projectRoot3);
|
|
3208
|
-
if (
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
fs8.writeFileSync(fullPath, content);
|
|
3317
|
+
if (fs8.existsSync(fullPath)) {
|
|
3318
|
+
const content = fs8.readFileSync(fullPath, "utf-8");
|
|
3319
|
+
const { content: nextContent, removed } = removeTomlSectionGroup(content, "mcp_servers.typegraph");
|
|
3320
|
+
if (removed) {
|
|
3321
|
+
if (nextContent === "") {
|
|
3322
|
+
fs8.unlinkSync(fullPath);
|
|
3323
|
+
} else {
|
|
3324
|
+
fs8.writeFileSync(fullPath, nextContent);
|
|
3325
|
+
}
|
|
3326
|
+
p.log.info(`${configPath}: removed typegraph MCP server`);
|
|
3327
|
+
}
|
|
3220
3328
|
}
|
|
3221
|
-
p.log.info(`${configPath}: removed typegraph MCP server`);
|
|
3222
3329
|
}
|
|
3223
3330
|
function ensureTsconfigExclude(projectRoot3) {
|
|
3224
3331
|
const tsconfigPath3 = path9.resolve(projectRoot3, "tsconfig.json");
|
|
@@ -3357,7 +3464,11 @@ async function setup(yes2) {
|
|
|
3357
3464
|
process.exit(0);
|
|
3358
3465
|
}
|
|
3359
3466
|
if (action === "remove") {
|
|
3360
|
-
await
|
|
3467
|
+
const removeOptions = await resolveRemovePluginOptions(projectRoot3, false, false);
|
|
3468
|
+
await removePlugin(projectRoot3, targetDir, removeOptions);
|
|
3469
|
+
if (removeOptions.warnAboutGlobalCodex) {
|
|
3470
|
+
warnAboutStaleGlobalCodex();
|
|
3471
|
+
}
|
|
3361
3472
|
return;
|
|
3362
3473
|
}
|
|
3363
3474
|
if (action === "exit") {
|
|
@@ -3460,9 +3571,13 @@ async function setup(yes2) {
|
|
|
3460
3571
|
ensureEslintIgnore(projectRoot3);
|
|
3461
3572
|
await runVerification(targetDir, selectedAgents);
|
|
3462
3573
|
}
|
|
3463
|
-
async function removePlugin(projectRoot3, pluginDir) {
|
|
3574
|
+
async function removePlugin(projectRoot3, pluginDir, options) {
|
|
3464
3575
|
const s = p.spinner();
|
|
3465
3576
|
s.start("Removing typegraph-mcp...");
|
|
3577
|
+
deregisterMcpServers(projectRoot3);
|
|
3578
|
+
if (options.removeGlobalCodex && options.legacyGlobalCodexCleanup) {
|
|
3579
|
+
removeLegacyGlobalCodexMcp(options.legacyGlobalCodexCleanup);
|
|
3580
|
+
}
|
|
3466
3581
|
if (fs8.existsSync(pluginDir)) {
|
|
3467
3582
|
fs8.rmSync(pluginDir, { recursive: true });
|
|
3468
3583
|
}
|
|
@@ -3502,7 +3617,6 @@ async function removePlugin(projectRoot3, pluginDir) {
|
|
|
3502
3617
|
fs8.writeFileSync(claudeMdPath, content);
|
|
3503
3618
|
}
|
|
3504
3619
|
s.stop("Removed typegraph-mcp");
|
|
3505
|
-
deregisterMcpServers(projectRoot3);
|
|
3506
3620
|
p.outro("typegraph-mcp has been uninstalled from this project.");
|
|
3507
3621
|
}
|
|
3508
3622
|
async function setupAgentInstructions(projectRoot3, selectedAgents) {
|
|
@@ -3582,6 +3696,7 @@ async function runVerification(pluginDir, selectedAgents) {
|
|
|
3582
3696
|
async function remove(yes2) {
|
|
3583
3697
|
const projectRoot3 = process.cwd();
|
|
3584
3698
|
const pluginDir = path9.resolve(projectRoot3, PLUGIN_DIR_NAME);
|
|
3699
|
+
const cleanGlobalCodex = args.includes("--clean-global-codex");
|
|
3585
3700
|
process.stdout.write("\x1Bc");
|
|
3586
3701
|
p.intro("TypeGraph MCP Remove");
|
|
3587
3702
|
if (!fs8.existsSync(pluginDir)) {
|
|
@@ -3595,7 +3710,11 @@ async function remove(yes2) {
|
|
|
3595
3710
|
process.exit(0);
|
|
3596
3711
|
}
|
|
3597
3712
|
}
|
|
3598
|
-
await
|
|
3713
|
+
const removeOptions = await resolveRemovePluginOptions(projectRoot3, yes2, cleanGlobalCodex);
|
|
3714
|
+
await removePlugin(projectRoot3, pluginDir, removeOptions);
|
|
3715
|
+
if (removeOptions.warnAboutGlobalCodex) {
|
|
3716
|
+
warnAboutStaleGlobalCodex();
|
|
3717
|
+
}
|
|
3599
3718
|
}
|
|
3600
3719
|
function resolvePluginDir() {
|
|
3601
3720
|
const installed = path9.resolve(process.cwd(), PLUGIN_DIR_NAME);
|