typegraph-mcp 0.9.32 → 0.9.34

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 CHANGED
@@ -142,7 +142,7 @@ npx typegraph-mcp check
142
142
  | Symptom | Fix |
143
143
  |---|---|
144
144
  | Server won't start | `cd plugins/typegraph-mcp && npm install --include=optional` |
145
- | "TypeScript not found" | Add `typescript` to devDependencies |
145
+ | "TypeScript not found" | Run `pnpm install` or `npm install`; if TypeScript is not declared, add it to devDependencies first |
146
146
  | Tools return empty results | Check `TYPEGRAPH_TSCONFIG` points to the right tsconfig |
147
147
  | Build errors from plugins/ | Add `"plugins/**"` to tsconfig.json `exclude` array |
148
148
  | `@esbuild/*` or `@rollup/*` package missing | Reinstall with Node 22: `npm install --include=optional` |
package/check.ts CHANGED
@@ -162,6 +162,45 @@ function hasTrustedCodexProject(projectRoot: string): boolean | null {
162
162
  return matchesTrustedProject();
163
163
  }
164
164
 
165
+ function readProjectPackageJson(projectRoot: string): Record<string, unknown> | null {
166
+ const packageJsonPath = path.resolve(projectRoot, "package.json");
167
+ if (!fs.existsSync(packageJsonPath)) return null;
168
+
169
+ try {
170
+ return JSON.parse(fs.readFileSync(packageJsonPath, "utf-8")) as Record<string, unknown>;
171
+ } catch {
172
+ return null;
173
+ }
174
+ }
175
+
176
+ function getProjectInstallCommand(projectRoot: string, packageJson: Record<string, unknown> | null): string {
177
+ const packageManager = typeof packageJson?.["packageManager"] === "string"
178
+ ? packageJson["packageManager"]
179
+ : "";
180
+
181
+ if (packageManager.startsWith("pnpm@") || fs.existsSync(path.join(projectRoot, "pnpm-lock.yaml"))) {
182
+ return "pnpm install";
183
+ }
184
+ if (packageManager.startsWith("yarn@") || fs.existsSync(path.join(projectRoot, "yarn.lock"))) {
185
+ return "yarn install";
186
+ }
187
+ return "npm install";
188
+ }
189
+
190
+ function hasDeclaredDependency(packageJson: Record<string, unknown> | null, packageName: string): boolean {
191
+ const depKeys = [
192
+ "dependencies",
193
+ "devDependencies",
194
+ "peerDependencies",
195
+ "optionalDependencies",
196
+ ] as const;
197
+
198
+ return depKeys.some((key) => {
199
+ const deps = packageJson?.[key];
200
+ return typeof deps === "object" && deps !== null && packageName in deps;
201
+ });
202
+ }
203
+
165
204
  // ─── Main ────────────────────────────────────────────────────────────────────
166
205
 
167
206
  export async function main(configOverride?: TypegraphConfig): Promise<CheckResult> {
@@ -171,6 +210,8 @@ export async function main(configOverride?: TypegraphConfig): Promise<CheckResul
171
210
  let passed = 0;
172
211
  let failed = 0;
173
212
  let warned = 0;
213
+ const projectPackageJson = readProjectPackageJson(projectRoot);
214
+ const installCommand = getProjectInstallCommand(projectRoot, projectPackageJson);
174
215
 
175
216
  function pass(msg: string): void {
176
217
  console.log(` \u2713 ${msg}`);
@@ -228,9 +269,14 @@ export async function main(configOverride?: TypegraphConfig): Promise<CheckResul
228
269
  tsVersion = tsPkg.version;
229
270
  pass(`TypeScript found (v${tsVersion})`);
230
271
  } catch {
272
+ const hasDeclaredTs = hasDeclaredDependency(projectPackageJson, "typescript");
231
273
  fail(
232
- "TypeScript not found in project",
233
- "Add `typescript` to devDependencies and run `npm install`"
274
+ hasDeclaredTs
275
+ ? "TypeScript is declared but not installed in project"
276
+ : "TypeScript not found in project",
277
+ hasDeclaredTs
278
+ ? `Run \`${installCommand}\` to install project dependencies`
279
+ : `Add \`typescript\` to devDependencies and run \`${installCommand}\``
234
280
  );
235
281
  }
236
282
 
package/cli.ts CHANGED
@@ -40,10 +40,25 @@ interface AgentDef {
40
40
  const AGENT_SNIPPET = `
41
41
  ## TypeScript Navigation (typegraph-mcp)
42
42
 
43
- Use the \`ts_*\` MCP tools instead of grep/glob for navigating TypeScript code. They resolve through barrel files, re-exports, and project references returning precise results, not string matches.
43
+ Where suitable, use the \`ts_*\` MCP tools instead of grep/glob for navigating TypeScript code. They resolve through barrel files, re-exports, and project references and return semantic results instead of string matches.
44
44
 
45
- - **Point queries** (tsserver): \`ts_find_symbol\`, \`ts_definition\`, \`ts_references\`, \`ts_type_info\`, \`ts_navigate_to\`, \`ts_trace_chain\`, \`ts_blast_radius\`, \`ts_module_exports\`
46
- - **Graph queries** (import graph): \`ts_dependency_tree\`, \`ts_dependents\`, \`ts_import_cycles\`, \`ts_shortest_path\`, \`ts_subgraph\`, \`ts_module_boundary\`
45
+ - Point queries: \`ts_find_symbol\`, \`ts_definition\`, \`ts_references\`, \`ts_type_info\`, \`ts_navigate_to\`, \`ts_trace_chain\`, \`ts_blast_radius\`, \`ts_module_exports\`
46
+ - Graph queries: \`ts_dependency_tree\`, \`ts_dependents\`, \`ts_import_cycles\`, \`ts_shortest_path\`, \`ts_subgraph\`, \`ts_module_boundary\`
47
+
48
+ Start with the navigation tools before reading entire files. Use direct file reads only after the MCP tools identify the exact symbols or lines that matter.
49
+
50
+ Use \`rg\` or \`grep\` when semantic symbol navigation is not the right tool, especially for:
51
+
52
+ - docs, config, SQL, migrations, JSON, env vars, route strings, and other non-TypeScript assets
53
+ - broad text discovery when you do not yet know the symbol name
54
+ - exact string matching across the repo
55
+ - validating wording or finding repeated plan/document references
56
+
57
+ Practical rule:
58
+
59
+ - use \`ts_*\` first for TypeScript symbol definition, references, types, and dependency analysis
60
+ - use \`rg\`/\`grep\` for text search and non-TypeScript exploration
61
+ - combine both when a task spans TypeScript code and surrounding docs/config
47
62
  `.trimStart();
48
63
 
49
64
  const SNIPPET_MARKER = "## TypeScript Navigation (typegraph-mcp)";
@@ -808,39 +823,35 @@ async function setupAgentInstructions(projectRoot: string, selectedAgents: Agent
808
823
  return; // No agents with instruction files selected (e.g. Cursor only)
809
824
  }
810
825
 
811
- // Find existing files, resolve symlinks to deduplicate
826
+ // Ensure each selected agent file exists and has the snippet once. Resolve
827
+ // symlinks to avoid writing duplicate content through multiple aliases.
812
828
  const seenRealPaths = new Map<string, string>(); // realPath -> first agentFile name
813
- const existingFiles: { file: string; realPath: string; hasSnippet: boolean }[] = [];
814
829
  for (const agentFile of agentFiles) {
815
830
  const filePath = path.resolve(projectRoot, agentFile);
816
- if (!fs.existsSync(filePath)) continue;
817
- const realPath = fs.realpathSync(filePath);
831
+ if (!fs.existsSync(filePath)) {
832
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
833
+ fs.writeFileSync(filePath, AGENT_SNIPPET + "\n");
834
+ p.log.success(`${agentFile}: created with typegraph-mcp instructions`);
835
+ continue;
836
+ }
818
837
 
838
+ const realPath = fs.realpathSync(filePath);
819
839
  const previousFile = seenRealPaths.get(realPath);
820
840
  if (previousFile) {
821
841
  p.log.info(`${agentFile}: same file as ${previousFile} (skipped)`);
822
842
  continue;
823
843
  }
824
- seenRealPaths.set(realPath, agentFile);
825
- const content = fs.readFileSync(filePath, "utf-8");
826
- existingFiles.push({ file: agentFile, realPath, hasSnippet: content.includes(SNIPPET_MARKER) });
827
- }
828
844
 
829
- if (existingFiles.length === 0) {
830
- p.log.warn(`No agent instruction files found (${agentFiles.join(", ")})`);
831
- p.note(AGENT_SNIPPET, "Add this snippet to your agent instructions file");
832
- } else if (existingFiles.some((f) => f.hasSnippet)) {
833
- for (const f of existingFiles) {
834
- if (f.hasSnippet) {
835
- p.log.info(`${f.file}: already has typegraph-mcp instructions`);
836
- }
845
+ seenRealPaths.set(realPath, agentFile);
846
+ const content = fs.readFileSync(realPath, "utf-8");
847
+ if (content.includes(SNIPPET_MARKER)) {
848
+ p.log.info(`${agentFile}: already has typegraph-mcp instructions`);
849
+ continue;
837
850
  }
838
- } else {
839
- const target = existingFiles[0]!;
840
- const content = fs.readFileSync(target.realPath, "utf-8");
851
+
841
852
  const appendContent = (content.endsWith("\n") ? "" : "\n") + "\n" + AGENT_SNIPPET;
842
- fs.appendFileSync(target.realPath, appendContent);
843
- p.log.success(`${target.file}: appended typegraph-mcp instructions`);
853
+ fs.appendFileSync(realPath, appendContent);
854
+ p.log.success(`${agentFile}: appended typegraph-mcp instructions`);
844
855
  }
845
856
 
846
857
  // Update --plugin-dir line in CLAUDE.md if Claude Code is selected
package/dist/check.js CHANGED
@@ -472,11 +472,44 @@ function hasTrustedCodexProject(projectRoot) {
472
472
  }
473
473
  return matchesTrustedProject();
474
474
  }
475
+ function readProjectPackageJson(projectRoot) {
476
+ const packageJsonPath = path3.resolve(projectRoot, "package.json");
477
+ if (!fs2.existsSync(packageJsonPath)) return null;
478
+ try {
479
+ return JSON.parse(fs2.readFileSync(packageJsonPath, "utf-8"));
480
+ } catch {
481
+ return null;
482
+ }
483
+ }
484
+ function getProjectInstallCommand(projectRoot, packageJson) {
485
+ const packageManager = typeof packageJson?.["packageManager"] === "string" ? packageJson["packageManager"] : "";
486
+ if (packageManager.startsWith("pnpm@") || fs2.existsSync(path3.join(projectRoot, "pnpm-lock.yaml"))) {
487
+ return "pnpm install";
488
+ }
489
+ if (packageManager.startsWith("yarn@") || fs2.existsSync(path3.join(projectRoot, "yarn.lock"))) {
490
+ return "yarn install";
491
+ }
492
+ return "npm install";
493
+ }
494
+ function hasDeclaredDependency(packageJson, packageName) {
495
+ const depKeys = [
496
+ "dependencies",
497
+ "devDependencies",
498
+ "peerDependencies",
499
+ "optionalDependencies"
500
+ ];
501
+ return depKeys.some((key) => {
502
+ const deps = packageJson?.[key];
503
+ return typeof deps === "object" && deps !== null && packageName in deps;
504
+ });
505
+ }
475
506
  async function main(configOverride) {
476
507
  const { projectRoot, tsconfigPath, toolDir, toolIsEmbedded, toolRelPath } = configOverride ?? resolveConfig(import.meta.dirname);
477
508
  let passed = 0;
478
509
  let failed = 0;
479
510
  let warned = 0;
511
+ const projectPackageJson = readProjectPackageJson(projectRoot);
512
+ const installCommand = getProjectInstallCommand(projectRoot, projectPackageJson);
480
513
  function pass(msg) {
481
514
  console.log(` \u2713 ${msg}`);
482
515
  passed++;
@@ -522,9 +555,10 @@ async function main(configOverride) {
522
555
  tsVersion = tsPkg.version;
523
556
  pass(`TypeScript found (v${tsVersion})`);
524
557
  } catch {
558
+ const hasDeclaredTs = hasDeclaredDependency(projectPackageJson, "typescript");
525
559
  fail(
526
- "TypeScript not found in project",
527
- "Add `typescript` to devDependencies and run `npm install`"
560
+ hasDeclaredTs ? "TypeScript is declared but not installed in project" : "TypeScript not found in project",
561
+ hasDeclaredTs ? `Run \`${installCommand}\` to install project dependencies` : `Add \`typescript\` to devDependencies and run \`${installCommand}\``
528
562
  );
529
563
  }
530
564
  const tsconfigAbs = path3.resolve(projectRoot, tsconfigPath);
package/dist/cli.js CHANGED
@@ -479,11 +479,44 @@ function hasTrustedCodexProject(projectRoot3) {
479
479
  }
480
480
  return matchesTrustedProject();
481
481
  }
482
+ function readProjectPackageJson(projectRoot3) {
483
+ const packageJsonPath = path3.resolve(projectRoot3, "package.json");
484
+ if (!fs2.existsSync(packageJsonPath)) return null;
485
+ try {
486
+ return JSON.parse(fs2.readFileSync(packageJsonPath, "utf-8"));
487
+ } catch {
488
+ return null;
489
+ }
490
+ }
491
+ function getProjectInstallCommand(projectRoot3, packageJson) {
492
+ const packageManager = typeof packageJson?.["packageManager"] === "string" ? packageJson["packageManager"] : "";
493
+ if (packageManager.startsWith("pnpm@") || fs2.existsSync(path3.join(projectRoot3, "pnpm-lock.yaml"))) {
494
+ return "pnpm install";
495
+ }
496
+ if (packageManager.startsWith("yarn@") || fs2.existsSync(path3.join(projectRoot3, "yarn.lock"))) {
497
+ return "yarn install";
498
+ }
499
+ return "npm install";
500
+ }
501
+ function hasDeclaredDependency(packageJson, packageName) {
502
+ const depKeys = [
503
+ "dependencies",
504
+ "devDependencies",
505
+ "peerDependencies",
506
+ "optionalDependencies"
507
+ ];
508
+ return depKeys.some((key) => {
509
+ const deps = packageJson?.[key];
510
+ return typeof deps === "object" && deps !== null && packageName in deps;
511
+ });
512
+ }
482
513
  async function main(configOverride) {
483
514
  const { projectRoot: projectRoot3, tsconfigPath: tsconfigPath3, toolDir, toolIsEmbedded, toolRelPath } = configOverride ?? resolveConfig(import.meta.dirname);
484
515
  let passed = 0;
485
516
  let failed = 0;
486
517
  let warned = 0;
518
+ const projectPackageJson = readProjectPackageJson(projectRoot3);
519
+ const installCommand = getProjectInstallCommand(projectRoot3, projectPackageJson);
487
520
  function pass(msg) {
488
521
  console.log(` \u2713 ${msg}`);
489
522
  passed++;
@@ -493,9 +526,9 @@ async function main(configOverride) {
493
526
  console.log(` Fix: ${fix}`);
494
527
  failed++;
495
528
  }
496
- function warn(msg, note2) {
529
+ function warn(msg, note) {
497
530
  console.log(` ! ${msg}`);
498
- console.log(` ${note2}`);
531
+ console.log(` ${note}`);
499
532
  warned++;
500
533
  }
501
534
  function skip(msg) {
@@ -529,9 +562,10 @@ async function main(configOverride) {
529
562
  tsVersion = tsPkg.version;
530
563
  pass(`TypeScript found (v${tsVersion})`);
531
564
  } catch {
565
+ const hasDeclaredTs = hasDeclaredDependency(projectPackageJson, "typescript");
532
566
  fail(
533
- "TypeScript not found in project",
534
- "Add `typescript` to devDependencies and run `npm install`"
567
+ hasDeclaredTs ? "TypeScript is declared but not installed in project" : "TypeScript not found in project",
568
+ hasDeclaredTs ? `Run \`${installCommand}\` to install project dependencies` : `Add \`typescript\` to devDependencies and run \`${installCommand}\``
535
569
  );
536
570
  }
537
571
  const tsconfigAbs = path3.resolve(projectRoot3, tsconfigPath3);
@@ -2881,10 +2915,25 @@ import * as p from "@clack/prompts";
2881
2915
  var AGENT_SNIPPET = `
2882
2916
  ## TypeScript Navigation (typegraph-mcp)
2883
2917
 
2884
- Use the \`ts_*\` MCP tools instead of grep/glob for navigating TypeScript code. They resolve through barrel files, re-exports, and project references \u2014 returning precise results, not string matches.
2918
+ Where suitable, use the \`ts_*\` MCP tools instead of grep/glob for navigating TypeScript code. They resolve through barrel files, re-exports, and project references and return semantic results instead of string matches.
2919
+
2920
+ - Point queries: \`ts_find_symbol\`, \`ts_definition\`, \`ts_references\`, \`ts_type_info\`, \`ts_navigate_to\`, \`ts_trace_chain\`, \`ts_blast_radius\`, \`ts_module_exports\`
2921
+ - Graph queries: \`ts_dependency_tree\`, \`ts_dependents\`, \`ts_import_cycles\`, \`ts_shortest_path\`, \`ts_subgraph\`, \`ts_module_boundary\`
2922
+
2923
+ Start with the navigation tools before reading entire files. Use direct file reads only after the MCP tools identify the exact symbols or lines that matter.
2885
2924
 
2886
- - **Point queries** (tsserver): \`ts_find_symbol\`, \`ts_definition\`, \`ts_references\`, \`ts_type_info\`, \`ts_navigate_to\`, \`ts_trace_chain\`, \`ts_blast_radius\`, \`ts_module_exports\`
2887
- - **Graph queries** (import graph): \`ts_dependency_tree\`, \`ts_dependents\`, \`ts_import_cycles\`, \`ts_shortest_path\`, \`ts_subgraph\`, \`ts_module_boundary\`
2925
+ Use \`rg\` or \`grep\` when semantic symbol navigation is not the right tool, especially for:
2926
+
2927
+ - docs, config, SQL, migrations, JSON, env vars, route strings, and other non-TypeScript assets
2928
+ - broad text discovery when you do not yet know the symbol name
2929
+ - exact string matching across the repo
2930
+ - validating wording or finding repeated plan/document references
2931
+
2932
+ Practical rule:
2933
+
2934
+ - use \`ts_*\` first for TypeScript symbol definition, references, types, and dependency analysis
2935
+ - use \`rg\`/\`grep\` for text search and non-TypeScript exploration
2936
+ - combine both when a task spans TypeScript code and surrounding docs/config
2888
2937
  `.trimStart();
2889
2938
  var SNIPPET_MARKER = "## TypeScript Navigation (typegraph-mcp)";
2890
2939
  var PLUGIN_DIR_NAME = "plugins/typegraph-mcp";
@@ -3462,10 +3511,14 @@ async function setupAgentInstructions(projectRoot3, selectedAgents) {
3462
3511
  return;
3463
3512
  }
3464
3513
  const seenRealPaths = /* @__PURE__ */ new Map();
3465
- const existingFiles = [];
3466
3514
  for (const agentFile of agentFiles) {
3467
3515
  const filePath = path9.resolve(projectRoot3, agentFile);
3468
- if (!fs8.existsSync(filePath)) continue;
3516
+ if (!fs8.existsSync(filePath)) {
3517
+ fs8.mkdirSync(path9.dirname(filePath), { recursive: true });
3518
+ fs8.writeFileSync(filePath, AGENT_SNIPPET + "\n");
3519
+ p.log.success(`${agentFile}: created with typegraph-mcp instructions`);
3520
+ continue;
3521
+ }
3469
3522
  const realPath = fs8.realpathSync(filePath);
3470
3523
  const previousFile = seenRealPaths.get(realPath);
3471
3524
  if (previousFile) {
@@ -3473,24 +3526,14 @@ async function setupAgentInstructions(projectRoot3, selectedAgents) {
3473
3526
  continue;
3474
3527
  }
3475
3528
  seenRealPaths.set(realPath, agentFile);
3476
- const content = fs8.readFileSync(filePath, "utf-8");
3477
- existingFiles.push({ file: agentFile, realPath, hasSnippet: content.includes(SNIPPET_MARKER) });
3478
- }
3479
- if (existingFiles.length === 0) {
3480
- p.log.warn(`No agent instruction files found (${agentFiles.join(", ")})`);
3481
- p.note(AGENT_SNIPPET, "Add this snippet to your agent instructions file");
3482
- } else if (existingFiles.some((f) => f.hasSnippet)) {
3483
- for (const f of existingFiles) {
3484
- if (f.hasSnippet) {
3485
- p.log.info(`${f.file}: already has typegraph-mcp instructions`);
3486
- }
3529
+ const content = fs8.readFileSync(realPath, "utf-8");
3530
+ if (content.includes(SNIPPET_MARKER)) {
3531
+ p.log.info(`${agentFile}: already has typegraph-mcp instructions`);
3532
+ continue;
3487
3533
  }
3488
- } else {
3489
- const target = existingFiles[0];
3490
- const content = fs8.readFileSync(target.realPath, "utf-8");
3491
3534
  const appendContent = (content.endsWith("\n") ? "" : "\n") + "\n" + AGENT_SNIPPET;
3492
- fs8.appendFileSync(target.realPath, appendContent);
3493
- p.log.success(`${target.file}: appended typegraph-mcp instructions`);
3535
+ fs8.appendFileSync(realPath, appendContent);
3536
+ p.log.success(`${agentFile}: appended typegraph-mcp instructions`);
3494
3537
  }
3495
3538
  if (selectedAgents.includes("claude-code")) {
3496
3539
  const claudeMdPath = path9.resolve(projectRoot3, "CLAUDE.md");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "typegraph-mcp",
3
- "version": "0.9.32",
3
+ "version": "0.9.34",
4
4
  "description": "Type-aware codebase navigation for AI coding agents — 14 MCP tools powered by tsserver + oxc",
5
5
  "license": "MIT",
6
6
  "type": "module",