codebase-analyzer-mcp 2.1.0 → 2.1.1

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.
@@ -6,13 +6,13 @@
6
6
  },
7
7
  "metadata": {
8
8
  "description": "Multi-layer codebase analysis tools",
9
- "version": "2.1.0"
9
+ "version": "2.1.1"
10
10
  },
11
11
  "plugins": [
12
12
  {
13
13
  "name": "codebase-analyzer",
14
14
  "description": "Multi-layer codebase analysis with Gemini AI. 4 agents, 1 command, 1 skill for architecture analysis, pattern detection, and dataflow tracing.",
15
- "version": "2.1.0",
15
+ "version": "2.1.1",
16
16
  "author": {
17
17
  "name": "Jake Correa",
18
18
  "url": "https://github.com/jaykaycodes"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codebase-analyzer",
3
- "version": "2.1.0",
3
+ "version": "2.1.1",
4
4
  "description": "Multi-layer codebase analysis with Gemini AI. 4 agents, 1 command, 1 skill for architecture analysis, pattern detection, and dataflow tracing.",
5
5
  "author": {
6
6
  "name": "Jake Correa",
package/dist/cli/index.js CHANGED
@@ -42998,11 +42998,39 @@ var init_node = __esm(() => {
42998
42998
  });
42999
42999
 
43000
43000
  // src/core/gemini.ts
43001
+ import { readFileSync } from "fs";
43002
+ import { join as join2 } from "path";
43003
+ import { homedir } from "os";
43004
+ function resolveApiKey() {
43005
+ if (process.env.GEMINI_API_KEY) {
43006
+ return process.env.GEMINI_API_KEY;
43007
+ }
43008
+ try {
43009
+ const configPath = join2(homedir(), ".config", "codebase-analyzer", "config.json");
43010
+ const config = JSON.parse(readFileSync(configPath, "utf-8"));
43011
+ if (config.geminiApiKey) {
43012
+ return config.geminiApiKey;
43013
+ }
43014
+ } catch {}
43015
+ return;
43016
+ }
43017
+ function hasGeminiKey() {
43018
+ return !!resolveApiKey();
43019
+ }
43001
43020
  function getClient() {
43002
43021
  if (!client) {
43003
- const apiKey = process.env.GEMINI_API_KEY;
43022
+ const apiKey = resolveApiKey();
43004
43023
  if (!apiKey) {
43005
- throw new Error("GEMINI_API_KEY environment variable is required. Get one at https://aistudio.google.com/apikey");
43024
+ throw new Error(`GEMINI_API_KEY not found. Set it up using one of these methods:
43025
+
43026
+ ` + `Option 1: Config file (recommended for plugin installs):
43027
+ ` + ` mkdir -p ~/.config/codebase-analyzer
43028
+ ` + ` echo '{"geminiApiKey":"your_key"}' > ~/.config/codebase-analyzer/config.json
43029
+
43030
+ ` + `Option 2: MCP server env (for ~/.mcp.json installs):
43031
+ ` + ` "env": { "GEMINI_API_KEY": "your_key" }
43032
+
43033
+ ` + "Get a free key at https://aistudio.google.com/apikey");
43006
43034
  }
43007
43035
  client = new GoogleGenAI({ apiKey });
43008
43036
  }
@@ -43104,6 +43132,21 @@ var init_gemini = __esm(() => {
43104
43132
 
43105
43133
  // src/core/layers/semantic.ts
43106
43134
  async function semanticAnalysis(surface, structural, options = {}) {
43135
+ if (!hasGeminiKey()) {
43136
+ throw new Error(`Semantic analysis (deep depth) requires GEMINI_API_KEY.
43137
+
43138
+ ` + `To set it up, add the env var to your MCP server config in ~/.mcp.json:
43139
+
43140
+ ` + ` "codebase-analyzer": {
43141
+ ` + ` "command": "npx",
43142
+ ` + ` "args": ["-y", "codebase-analyzer-mcp"],
43143
+ ` + ` "env": { "GEMINI_API_KEY": "your_key" }
43144
+ ` + ` }
43145
+
43146
+ ` + `Get a free key at https://aistudio.google.com/apikey
43147
+
43148
+ ` + "Use --depth surface or --depth standard for free analysis without an API key.");
43149
+ }
43107
43150
  const repoName = surface.repositoryMap.name;
43108
43151
  const languages = surface.repositoryMap.languages.slice(0, 5).map((l) => `${l.language} (${l.percentage}%)`).join(", ");
43109
43152
  const fileCount = surface.repositoryMap.fileCount;
@@ -43881,7 +43924,7 @@ var init_logger = __esm(() => {
43881
43924
  });
43882
43925
 
43883
43926
  // src/core/orchestrator.ts
43884
- import { join as join2 } from "path";
43927
+ import { join as join3 } from "path";
43885
43928
  import { readFile as readFile2 } from "fs/promises";
43886
43929
  async function orchestrateAnalysis(repoPath, options = {}) {
43887
43930
  const startTime = Date.now();
@@ -43962,7 +44005,7 @@ async function orchestrateAnalysis(repoPath, options = {}) {
43962
44005
  });
43963
44006
  const allFiles = [];
43964
44007
  for (const module of modulesToAnalyze) {
43965
- const moduleDir = join2(repoPath, module.path);
44008
+ const moduleDir = join3(repoPath, module.path);
43966
44009
  }
43967
44010
  structural = await analyzeModulesInParallel(repoPath, modulesToAnalyze, state);
43968
44011
  logger.stopSpinner(`Structural: ${structural.length} modules analyzed`);
@@ -44053,7 +44096,7 @@ async function analyzeModulesInParallel(repoPath, modules, state) {
44053
44096
  state.tasks.push(...batchTasks);
44054
44097
  const fileContents = new Map;
44055
44098
  await Promise.all(batch.map(async (module) => {
44056
- const modulePath = join2(repoPath, module.path);
44099
+ const modulePath = join3(repoPath, module.path);
44057
44100
  try {
44058
44101
  const { glob: glob2 } = await Promise.resolve().then(() => (init_esm6(), exports_esm));
44059
44102
  const files = await glob2("**/*", {
@@ -44063,8 +44106,8 @@ async function analyzeModulesInParallel(repoPath, modules, state) {
44063
44106
  ignore: ["**/node_modules/**", "**/.git/**"]
44064
44107
  });
44065
44108
  for (const file of files.slice(0, 50)) {
44066
- const filePath = join2(module.path, file);
44067
- const fullPath = join2(repoPath, filePath);
44109
+ const filePath = join3(module.path, file);
44110
+ const fullPath = join3(repoPath, filePath);
44068
44111
  try {
44069
44112
  const content = await readFile2(fullPath, "utf-8");
44070
44113
  if (content.length < 1e5) {
@@ -44158,7 +44201,7 @@ var init_orchestrator = __esm(() => {
44158
44201
 
44159
44202
  // src/core/repo-loader.ts
44160
44203
  import { readFile as readFile3, stat as stat4 } from "node:fs/promises";
44161
- import { join as join3 } from "node:path";
44204
+ import { join as join4 } from "node:path";
44162
44205
  async function resolveSource(source) {
44163
44206
  if (await isGitHubUrl(source)) {
44164
44207
  const repoPath = await cloneGitHubRepo(source);
@@ -44185,7 +44228,7 @@ async function cloneGitHubRepo(url) {
44185
44228
  const { execSync } = await import("node:child_process");
44186
44229
  const { mkdtemp } = await import("node:fs/promises");
44187
44230
  const { tmpdir } = await import("node:os");
44188
- const tempDir = await mkdtemp(join3(tmpdir(), "cba-"));
44231
+ const tempDir = await mkdtemp(join4(tmpdir(), "cba-"));
44189
44232
  const normalizedUrl = url.replace(/\.git$/, "").replace(/\/$/, "");
44190
44233
  try {
44191
44234
  execSync(`git clone --depth 1 ${normalizedUrl}.git ${tempDir}`, {
@@ -44208,7 +44251,7 @@ var package_default;
44208
44251
  var init_package = __esm(() => {
44209
44252
  package_default = {
44210
44253
  name: "codebase-analyzer-mcp",
44211
- version: "2.1.0",
44254
+ version: "2.1.1",
44212
44255
  description: "Multi-layer codebase analysis with Gemini AI. MCP server + Claude plugin with progressive disclosure.",
44213
44256
  type: "module",
44214
44257
  main: "dist/mcp/server.js",
@@ -58152,9 +58195,24 @@ __export(exports_patterns, {
58152
58195
  executeFindPatterns: () => executeFindPatterns,
58153
58196
  DETECTABLE_PATTERNS: () => DETECTABLE_PATTERNS
58154
58197
  });
58155
- import { join as join4 } from "path";
58198
+ import { join as join5 } from "path";
58156
58199
  import { readFile as readFile4 } from "fs/promises";
58157
58200
  async function executeFindPatterns(input) {
58201
+ if (!hasGeminiKey()) {
58202
+ throw new Error(`find_patterns requires GEMINI_API_KEY for AI-powered pattern detection.
58203
+
58204
+ ` + `To set it up, add the env var to your MCP server config in ~/.mcp.json:
58205
+
58206
+ ` + ` "codebase-analyzer": {
58207
+ ` + ` "command": "npx",
58208
+ ` + ` "args": ["-y", "codebase-analyzer-mcp"],
58209
+ ` + ` "env": { "GEMINI_API_KEY": "your_key" }
58210
+ ` + ` }
58211
+
58212
+ ` + `Get a free key at https://aistudio.google.com/apikey
58213
+
58214
+ ` + "Alternatively, use analyze_repo (free, no API key needed) or query_repo (degrades gracefully without Gemini).");
58215
+ }
58158
58216
  const { source, patternTypes } = input;
58159
58217
  const { repoPath, cleanup } = await resolveSource(source);
58160
58218
  try {
@@ -58166,7 +58224,7 @@ async function executeFindPatterns(input) {
58166
58224
  ignore: ["**/node_modules/**", "**/.git/**", "**/dist/**", "**/build/**"]
58167
58225
  });
58168
58226
  for (const file2 of keyFiles.slice(0, 50)) {
58169
- const fullPath = join4(repoPath, file2);
58227
+ const fullPath = join5(repoPath, file2);
58170
58228
  try {
58171
58229
  const content = await readFile4(fullPath, "utf-8");
58172
58230
  if (content.length < 50000) {
@@ -58264,9 +58322,24 @@ __export(exports_dataflow, {
58264
58322
  traceDataflowSchema: () => traceDataflowSchema,
58265
58323
  executeTraceDataflow: () => executeTraceDataflow
58266
58324
  });
58267
- import { join as join5 } from "path";
58325
+ import { join as join6 } from "path";
58268
58326
  import { readFile as readFile5 } from "fs/promises";
58269
58327
  async function executeTraceDataflow(input) {
58328
+ if (!hasGeminiKey()) {
58329
+ throw new Error(`trace_dataflow requires GEMINI_API_KEY for AI-powered dataflow analysis.
58330
+
58331
+ ` + `To set it up, add the env var to your MCP server config in ~/.mcp.json:
58332
+
58333
+ ` + ` "codebase-analyzer": {
58334
+ ` + ` "command": "npx",
58335
+ ` + ` "args": ["-y", "codebase-analyzer-mcp"],
58336
+ ` + ` "env": { "GEMINI_API_KEY": "your_key" }
58337
+ ` + ` }
58338
+
58339
+ ` + `Get a free key at https://aistudio.google.com/apikey
58340
+
58341
+ ` + "Alternatively, use query_repo which degrades gracefully without Gemini.");
58342
+ }
58270
58343
  const { source, from, to } = input;
58271
58344
  const { repoPath, cleanup } = await resolveSource(source);
58272
58345
  try {
@@ -58278,7 +58351,7 @@ async function executeTraceDataflow(input) {
58278
58351
  ignore: ["**/node_modules/**", "**/.git/**", "**/dist/**", "**/build/**", "**/*.test.*", "**/*.spec.*"]
58279
58352
  });
58280
58353
  for (const file2 of codeFiles.slice(0, 60)) {
58281
- const fullPath = join5(repoPath, file2);
58354
+ const fullPath = join6(repoPath, file2);
58282
58355
  try {
58283
58356
  const content = await readFile5(fullPath, "utf-8");
58284
58357
  if (content.length < 50000) {
@@ -58377,7 +58450,7 @@ __export(exports_query, {
58377
58450
  queryRepoSchema: () => queryRepoSchema,
58378
58451
  executeQueryRepo: () => executeQueryRepo
58379
58452
  });
58380
- import { basename as basename4, join as join6 } from "path";
58453
+ import { basename as basename4, join as join7 } from "path";
58381
58454
  import { readFile as readFile6 } from "fs/promises";
58382
58455
  function extractSourceName(source) {
58383
58456
  const githubMatch = source.match(/github\.com\/([^\/]+\/[^\/]+)/);
@@ -58470,7 +58543,7 @@ async function executeQueryRepo(input) {
58470
58543
  for (const filePath of filesToRead) {
58471
58544
  if (totalChars >= MAX_TOTAL_CHARS)
58472
58545
  break;
58473
- const fullPath = join6(repoPath, filePath);
58546
+ const fullPath = join7(repoPath, filePath);
58474
58547
  try {
58475
58548
  const content = await readFile6(fullPath, "utf-8");
58476
58549
  const truncated = content.length > MAX_PER_FILE ? content.slice(0, MAX_PER_FILE) + `
@@ -73713,7 +73786,7 @@ var init_expand = __esm(() => {
73713
73786
 
73714
73787
  // src/mcp/tools/read-files.ts
73715
73788
  import { readFile as readFile7, stat as stat5 } from "fs/promises";
73716
- import { join as join7, resolve, normalize as normalize2 } from "path";
73789
+ import { join as join8, resolve, normalize as normalize2 } from "path";
73717
73790
  async function executeReadFiles(input) {
73718
73791
  const { analysisId, paths, maxLines = 500 } = input;
73719
73792
  const cached2 = analysisCache.getByAnalysisId(analysisId);
@@ -73742,7 +73815,7 @@ async function executeReadFiles(input) {
73742
73815
  if (normalized.startsWith("..") || normalized.startsWith("/")) {
73743
73816
  return { path: filePath, error: "Invalid path: must be relative and within the repository" };
73744
73817
  }
73745
- const fullPath = resolve(join7(resolvedRepoPath, normalized));
73818
+ const fullPath = resolve(join8(resolvedRepoPath, normalized));
73746
73819
  if (!fullPath.startsWith(resolvedRepoPath)) {
73747
73820
  return { path: filePath, error: "Invalid path: traversal outside repository" };
73748
73821
  }
@@ -53283,7 +53283,7 @@ class StdioServerTransport {
53283
53283
  }
53284
53284
 
53285
53285
  // src/core/orchestrator.ts
53286
- import { join as join2 } from "path";
53286
+ import { join as join3 } from "path";
53287
53287
  import { readFile as readFile2 } from "fs/promises";
53288
53288
 
53289
53289
  // src/core/layers/surface.ts
@@ -69556,12 +69556,40 @@ function getApiKeyFromEnv() {
69556
69556
  }
69557
69557
 
69558
69558
  // src/core/gemini.ts
69559
+ import { readFileSync } from "fs";
69560
+ import { join as join2 } from "path";
69561
+ import { homedir } from "os";
69559
69562
  var client = null;
69563
+ function resolveApiKey() {
69564
+ if (process.env.GEMINI_API_KEY) {
69565
+ return process.env.GEMINI_API_KEY;
69566
+ }
69567
+ try {
69568
+ const configPath = join2(homedir(), ".config", "codebase-analyzer", "config.json");
69569
+ const config2 = JSON.parse(readFileSync(configPath, "utf-8"));
69570
+ if (config2.geminiApiKey) {
69571
+ return config2.geminiApiKey;
69572
+ }
69573
+ } catch {}
69574
+ return;
69575
+ }
69576
+ function hasGeminiKey() {
69577
+ return !!resolveApiKey();
69578
+ }
69560
69579
  function getClient() {
69561
69580
  if (!client) {
69562
- const apiKey = process.env.GEMINI_API_KEY;
69581
+ const apiKey = resolveApiKey();
69563
69582
  if (!apiKey) {
69564
- throw new Error("GEMINI_API_KEY environment variable is required. Get one at https://aistudio.google.com/apikey");
69583
+ throw new Error(`GEMINI_API_KEY not found. Set it up using one of these methods:
69584
+
69585
+ ` + `Option 1: Config file (recommended for plugin installs):
69586
+ ` + ` mkdir -p ~/.config/codebase-analyzer
69587
+ ` + ` echo '{"geminiApiKey":"your_key"}' > ~/.config/codebase-analyzer/config.json
69588
+
69589
+ ` + `Option 2: MCP server env (for ~/.mcp.json installs):
69590
+ ` + ` "env": { "GEMINI_API_KEY": "your_key" }
69591
+
69592
+ ` + "Get a free key at https://aistudio.google.com/apikey");
69565
69593
  }
69566
69594
  client = new GoogleGenAI({ apiKey });
69567
69595
  }
@@ -69731,6 +69759,21 @@ Focus on:
69731
69759
 
69732
69760
  Be specific and reference actual file paths when possible.`;
69733
69761
  async function semanticAnalysis(surface, structural, options = {}) {
69762
+ if (!hasGeminiKey()) {
69763
+ throw new Error(`Semantic analysis (deep depth) requires GEMINI_API_KEY.
69764
+
69765
+ ` + `To set it up, add the env var to your MCP server config in ~/.mcp.json:
69766
+
69767
+ ` + ` "codebase-analyzer": {
69768
+ ` + ` "command": "npx",
69769
+ ` + ` "args": ["-y", "codebase-analyzer-mcp"],
69770
+ ` + ` "env": { "GEMINI_API_KEY": "your_key" }
69771
+ ` + ` }
69772
+
69773
+ ` + `Get a free key at https://aistudio.google.com/apikey
69774
+
69775
+ ` + "Use --depth surface or --depth standard for free analysis without an API key.");
69776
+ }
69734
69777
  const repoName = surface.repositoryMap.name;
69735
69778
  const languages = surface.repositoryMap.languages.slice(0, 5).map((l) => `${l.language} (${l.percentage}%)`).join(", ");
69736
69779
  const fileCount = surface.repositoryMap.fileCount;
@@ -70245,7 +70288,7 @@ async function orchestrateAnalysis(repoPath, options = {}) {
70245
70288
  });
70246
70289
  const allFiles = [];
70247
70290
  for (const module of modulesToAnalyze) {
70248
- const moduleDir = join2(repoPath, module.path);
70291
+ const moduleDir = join3(repoPath, module.path);
70249
70292
  }
70250
70293
  structural = await analyzeModulesInParallel(repoPath, modulesToAnalyze, state);
70251
70294
  logger.stopSpinner(`Structural: ${structural.length} modules analyzed`);
@@ -70336,7 +70379,7 @@ async function analyzeModulesInParallel(repoPath, modules, state) {
70336
70379
  state.tasks.push(...batchTasks);
70337
70380
  const fileContents = new Map;
70338
70381
  await Promise.all(batch.map(async (module) => {
70339
- const modulePath = join2(repoPath, module.path);
70382
+ const modulePath = join3(repoPath, module.path);
70340
70383
  try {
70341
70384
  const { glob: glob2 } = await Promise.resolve().then(() => (init_esm6(), exports_esm));
70342
70385
  const files = await glob2("**/*", {
@@ -70346,8 +70389,8 @@ async function analyzeModulesInParallel(repoPath, modules, state) {
70346
70389
  ignore: ["**/node_modules/**", "**/.git/**"]
70347
70390
  });
70348
70391
  for (const file2 of files.slice(0, 50)) {
70349
- const filePath = join2(module.path, file2);
70350
- const fullPath = join2(repoPath, filePath);
70392
+ const filePath = join3(module.path, file2);
70393
+ const fullPath = join3(repoPath, filePath);
70351
70394
  try {
70352
70395
  const content = await readFile2(fullPath, "utf-8");
70353
70396
  if (content.length < 1e5) {
@@ -70504,7 +70547,7 @@ import { basename as basename4 } from "path";
70504
70547
  // src/core/repo-loader.ts
70505
70548
  init_esm6();
70506
70549
  import { readFile as readFile3, stat as stat4 } from "node:fs/promises";
70507
- import { join as join3 } from "node:path";
70550
+ import { join as join4 } from "node:path";
70508
70551
  async function resolveSource(source) {
70509
70552
  if (await isGitHubUrl(source)) {
70510
70553
  const repoPath = await cloneGitHubRepo(source);
@@ -70531,7 +70574,7 @@ async function cloneGitHubRepo(url2) {
70531
70574
  const { execSync } = await import("node:child_process");
70532
70575
  const { mkdtemp } = await import("node:fs/promises");
70533
70576
  const { tmpdir } = await import("node:os");
70534
- const tempDir = await mkdtemp(join3(tmpdir(), "cba-"));
70577
+ const tempDir = await mkdtemp(join4(tmpdir(), "cba-"));
70535
70578
  const normalizedUrl = url2.replace(/\.git$/, "").replace(/\/$/, "");
70536
70579
  try {
70537
70580
  execSync(`git clone --depth 1 ${normalizedUrl}.git ${tempDir}`, {
@@ -70614,7 +70657,7 @@ async function executeExpandSection(input) {
70614
70657
  }
70615
70658
  // src/mcp/tools/patterns.ts
70616
70659
  init_esm6();
70617
- import { join as join4 } from "path";
70660
+ import { join as join5 } from "path";
70618
70661
  import { readFile as readFile4 } from "fs/promises";
70619
70662
  var DETECTABLE_PATTERNS = [
70620
70663
  "singleton",
@@ -70641,6 +70684,21 @@ var findPatternsSchema = {
70641
70684
  patternTypes: exports_external.array(exports_external.string()).optional().describe(`Optional: specific patterns to look for. Available: ${DETECTABLE_PATTERNS.join(", ")}`)
70642
70685
  };
70643
70686
  async function executeFindPatterns(input) {
70687
+ if (!hasGeminiKey()) {
70688
+ throw new Error(`find_patterns requires GEMINI_API_KEY for AI-powered pattern detection.
70689
+
70690
+ ` + `To set it up, add the env var to your MCP server config in ~/.mcp.json:
70691
+
70692
+ ` + ` "codebase-analyzer": {
70693
+ ` + ` "command": "npx",
70694
+ ` + ` "args": ["-y", "codebase-analyzer-mcp"],
70695
+ ` + ` "env": { "GEMINI_API_KEY": "your_key" }
70696
+ ` + ` }
70697
+
70698
+ ` + `Get a free key at https://aistudio.google.com/apikey
70699
+
70700
+ ` + "Alternatively, use analyze_repo (free, no API key needed) or query_repo (degrades gracefully without Gemini).");
70701
+ }
70644
70702
  const { source, patternTypes } = input;
70645
70703
  const { repoPath, cleanup } = await resolveSource(source);
70646
70704
  try {
@@ -70652,7 +70710,7 @@ async function executeFindPatterns(input) {
70652
70710
  ignore: ["**/node_modules/**", "**/.git/**", "**/dist/**", "**/build/**"]
70653
70711
  });
70654
70712
  for (const file2 of keyFiles.slice(0, 50)) {
70655
- const fullPath = join4(repoPath, file2);
70713
+ const fullPath = join5(repoPath, file2);
70656
70714
  try {
70657
70715
  const content = await readFile4(fullPath, "utf-8");
70658
70716
  if (content.length < 50000) {
@@ -70713,7 +70771,7 @@ Respond with this exact JSON structure:
70713
70771
  }
70714
70772
  // src/mcp/tools/dataflow.ts
70715
70773
  init_esm6();
70716
- import { join as join5 } from "path";
70774
+ import { join as join6 } from "path";
70717
70775
  import { readFile as readFile5 } from "fs/promises";
70718
70776
  var traceDataflowSchema = {
70719
70777
  source: exports_external.string().describe("Local path or GitHub URL to the repository"),
@@ -70721,6 +70779,21 @@ var traceDataflowSchema = {
70721
70779
  to: exports_external.string().optional().describe("Optional: destination to trace to (if known)")
70722
70780
  };
70723
70781
  async function executeTraceDataflow(input) {
70782
+ if (!hasGeminiKey()) {
70783
+ throw new Error(`trace_dataflow requires GEMINI_API_KEY for AI-powered dataflow analysis.
70784
+
70785
+ ` + `To set it up, add the env var to your MCP server config in ~/.mcp.json:
70786
+
70787
+ ` + ` "codebase-analyzer": {
70788
+ ` + ` "command": "npx",
70789
+ ` + ` "args": ["-y", "codebase-analyzer-mcp"],
70790
+ ` + ` "env": { "GEMINI_API_KEY": "your_key" }
70791
+ ` + ` }
70792
+
70793
+ ` + `Get a free key at https://aistudio.google.com/apikey
70794
+
70795
+ ` + "Alternatively, use query_repo which degrades gracefully without Gemini.");
70796
+ }
70724
70797
  const { source, from, to } = input;
70725
70798
  const { repoPath, cleanup } = await resolveSource(source);
70726
70799
  try {
@@ -70732,7 +70805,7 @@ async function executeTraceDataflow(input) {
70732
70805
  ignore: ["**/node_modules/**", "**/.git/**", "**/dist/**", "**/build/**", "**/*.test.*", "**/*.spec.*"]
70733
70806
  });
70734
70807
  for (const file2 of codeFiles.slice(0, 60)) {
70735
- const fullPath = join5(repoPath, file2);
70808
+ const fullPath = join6(repoPath, file2);
70736
70809
  try {
70737
70810
  const content = await readFile5(fullPath, "utf-8");
70738
70811
  if (content.length < 50000) {
@@ -70813,7 +70886,7 @@ If you cannot find the entry point, explain what you looked for in the summary a
70813
70886
  }
70814
70887
  // src/mcp/tools/read-files.ts
70815
70888
  import { readFile as readFile6, stat as stat5 } from "fs/promises";
70816
- import { join as join6, resolve, normalize as normalize2 } from "path";
70889
+ import { join as join7, resolve, normalize as normalize2 } from "path";
70817
70890
  var readFilesSchema = {
70818
70891
  analysisId: exports_external.string().describe("The analysisId from a previous analyze_repo result"),
70819
70892
  paths: exports_external.array(exports_external.string()).min(1).max(20).describe("Relative file paths from the repository (max 20)"),
@@ -70847,7 +70920,7 @@ async function executeReadFiles(input) {
70847
70920
  if (normalized.startsWith("..") || normalized.startsWith("/")) {
70848
70921
  return { path: filePath, error: "Invalid path: must be relative and within the repository" };
70849
70922
  }
70850
- const fullPath = resolve(join6(resolvedRepoPath, normalized));
70923
+ const fullPath = resolve(join7(resolvedRepoPath, normalized));
70851
70924
  if (!fullPath.startsWith(resolvedRepoPath)) {
70852
70925
  return { path: filePath, error: "Invalid path: traversal outside repository" };
70853
70926
  }
@@ -70874,7 +70947,7 @@ async function executeReadFiles(input) {
70874
70947
  };
70875
70948
  }
70876
70949
  // src/mcp/tools/query.ts
70877
- import { basename as basename5, join as join7 } from "path";
70950
+ import { basename as basename5, join as join8 } from "path";
70878
70951
  import { readFile as readFile7 } from "fs/promises";
70879
70952
  var queryRepoSchema = {
70880
70953
  source: exports_external.string().describe("Local path or GitHub URL to the repository"),
@@ -70971,7 +71044,7 @@ async function executeQueryRepo(input) {
70971
71044
  for (const filePath of filesToRead) {
70972
71045
  if (totalChars >= MAX_TOTAL_CHARS)
70973
71046
  break;
70974
- const fullPath = join7(repoPath, filePath);
71047
+ const fullPath = join8(repoPath, filePath);
70975
71048
  try {
70976
71049
  const content = await readFile7(fullPath, "utf-8");
70977
71050
  const truncated = content.length > MAX_PER_FILE ? content.slice(0, MAX_PER_FILE) + `
@@ -71063,7 +71136,7 @@ function buildFallbackAnswer(question, analysisId, cached2, scored, fileContents
71063
71136
  // package.json
71064
71137
  var package_default = {
71065
71138
  name: "codebase-analyzer-mcp",
71066
- version: "2.1.0",
71139
+ version: "2.1.1",
71067
71140
  description: "Multi-layer codebase analysis with Gemini AI. MCP server + Claude plugin with progressive disclosure.",
71068
71141
  type: "module",
71069
71142
  main: "dist/mcp/server.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codebase-analyzer-mcp",
3
- "version": "2.1.0",
3
+ "version": "2.1.1",
4
4
  "description": "Multi-layer codebase analysis with Gemini AI. MCP server + Claude plugin with progressive disclosure.",
5
5
  "type": "module",
6
6
  "main": "dist/mcp/server.js",