@iderouter/index-mcp 0.2.0-beta.3 → 0.2.0-beta.5

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +112 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iderouter/index-mcp",
3
- "version": "0.2.0-beta.3",
3
+ "version": "0.2.0-beta.5",
4
4
  "description": "Hybrid local code index MCP powered by IDERouter embeddings.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/index.js CHANGED
@@ -366,12 +366,13 @@ function contextProbeCacheKey(codebasePath) {
366
366
  return `context:${codebasePath}`;
367
367
  }
368
368
 
369
- function summarizeContextText(text, maxLines = 8) {
369
+ function summarizeContextText(text, maxLines = 4) {
370
370
  return String(text || "")
371
371
  .split("\n")
372
372
  .map((line) => line.trim())
373
373
  .filter(Boolean)
374
374
  .filter((line) => !/^#/.test(line))
375
+ .map((line) => line.replace(/\*\*/g, "").replace(/`/g, ""))
375
376
  .slice(0, maxLines)
376
377
  .join(" ");
377
378
  }
@@ -6978,11 +6979,118 @@ function summarizeRepoTechStack(results) {
6978
6979
  return stack;
6979
6980
  }
6980
6981
 
6982
+ function summarizeRepoTechStackFromContext(contextProbe, fallbackStack) {
6983
+ const summary = String(contextProbe?.summary || "").toLowerCase();
6984
+ const merged = new Set(Array.isArray(fallbackStack) ? fallbackStack : []);
6985
+ if (summary.includes("go ")) merged.add("Go backend");
6986
+ if (summary.includes("react") || summary.includes("typescript")) merged.add("TypeScript/React frontend");
6987
+ if (summary.includes("tailwind")) merged.add("Tailwind CSS");
6988
+ if (summary.includes("gin")) merged.add("Gin web framework");
6989
+ if (summary.includes("gorm")) merged.add("GORM");
6990
+ if (summary.includes("redis")) merged.add("Redis");
6991
+ if (summary.includes("sqlite") || summary.includes("mysql") || summary.includes("postgresql")) {
6992
+ merged.add("Multi-database support");
6993
+ }
6994
+ return [...merged];
6995
+ }
6996
+
6997
+ function repoSummarySkeletonPaths(index) {
6998
+ const availableFiles = new Set(
6999
+ Array.isArray(index?.files)
7000
+ ? index.files.map((file) => String(file?.relativePath || ""))
7001
+ : Array.isArray(index?.chunks)
7002
+ ? index.chunks.map((chunk) => String(chunk?.relativePath || ""))
7003
+ : [],
7004
+ );
7005
+ const preferred = [
7006
+ "main.go",
7007
+ "router/main.go",
7008
+ "router/api-router.go",
7009
+ "router/web-router.go",
7010
+ "controller/relay.go",
7011
+ "controller/iderouter.go",
7012
+ "service/iderouter_routing.go",
7013
+ "service/channel_select.go",
7014
+ "service/iderouter_agent.go",
7015
+ "model/main.go",
7016
+ "model/channel.go",
7017
+ "relay/compatible_handler.go",
7018
+ "relay/common/relay_info.go",
7019
+ "setting/iderouter_setting/agent.go",
7020
+ "web/default/src/main.tsx",
7021
+ ];
7022
+ return preferred.filter((relativePath) => availableFiles.has(relativePath));
7023
+ }
7024
+
7025
+ function summaryChunkScore(chunk) {
7026
+ const content = String(chunk?.content || "");
7027
+ let score = 0;
7028
+ if (chunk?.granularity !== "coarse") score += 3;
7029
+ if (/\bfunc\s+[A-Za-z0-9_]+/.test(content)) score += 5;
7030
+ if (/\btype\s+[A-Za-z0-9_]+/.test(content)) score += 3;
7031
+ if (/\bpackage\s+[A-Za-z0-9_]+/.test(content)) score += 1;
7032
+ if (/createRoot|ReactDOM|Gin|router|Handle|RunExpr|RelayInfo|Select/.test(content)) score += 2;
7033
+ return score;
7034
+ }
7035
+
7036
+ function selectSummaryChunkForFile(index, relativePath) {
7037
+ const chunks = Array.isArray(index?.chunks)
7038
+ ? index.chunks.filter((chunk) => String(chunk.relativePath || "") === relativePath)
7039
+ : [];
7040
+ if (chunks.length === 0) return null;
7041
+ return chunks
7042
+ .slice()
7043
+ .sort((left, right) => summaryChunkScore(right) - summaryChunkScore(left) || Number(left.startLine || 0) - Number(right.startLine || 0))[0];
7044
+ }
7045
+
7046
+ function summarizeEntrypoints(index, results, limit = 5) {
7047
+ const preferredRoleOrder = ["controller", "router", "service", "relay", "model", "pkg", "frontend", "core"];
7048
+ const byPath = new Map((results || []).map((result) => [String(result.relativePath || ""), result]));
7049
+ const skeleton = [];
7050
+ for (const relativePath of repoSummarySkeletonPaths(index)) {
7051
+ const item = byPath.get(relativePath) || selectSummaryChunkForFile(index, relativePath);
7052
+ if (item) skeleton.push(item);
7053
+ if (skeleton.length >= limit) return skeleton.slice(0, limit);
7054
+ }
7055
+ const bestByRole = new Map();
7056
+ for (const result of results || []) {
7057
+ const role = pathRole(result.relativePath);
7058
+ if (!preferredRoleOrder.includes(role)) continue;
7059
+ const existing = bestByRole.get(role);
7060
+ if (!existing || Number(result.score || 0) > Number(existing.score || 0)) {
7061
+ bestByRole.set(role, result);
7062
+ }
7063
+ }
7064
+ const ordered = [];
7065
+ const seenFile = new Set(skeleton.map((item) => item.relativePath));
7066
+ for (const item of skeleton) {
7067
+ ordered.push(item);
7068
+ }
7069
+ for (const role of preferredRoleOrder) {
7070
+ const item = bestByRole.get(role);
7071
+ if (item && seenFile.has(item.relativePath)) continue;
7072
+ if (item) ordered.push(item);
7073
+ if (ordered.length >= limit) break;
7074
+ }
7075
+ if (ordered.length >= limit) return ordered.slice(0, limit);
7076
+ const seen = new Set(ordered.map((item) => `${item.relativePath}:${item.startLine}:${item.endLine}`));
7077
+ for (const result of results || []) {
7078
+ const key = `${result.relativePath}:${result.startLine}:${result.endLine}`;
7079
+ if (seen.has(key)) continue;
7080
+ if (["docs", "tests", "mcp", "other"].includes(pathRole(result.relativePath))) continue;
7081
+ ordered.push(result);
7082
+ seen.add(key);
7083
+ if (ordered.length >= limit) break;
7084
+ }
7085
+ return ordered;
7086
+ }
7087
+
6981
7088
  function repoSummaryLines(codebasePath, state, contextProbe) {
6982
7089
  const results = Array.isArray(state?.results) ? state.results : [];
6983
7090
  const topResults = results.slice(0, 8);
6984
7091
  const domains = summarizeRepoDomains(topResults);
6985
- const stack = summarizeRepoTechStack(topResults);
7092
+ const stack = summarizeRepoTechStackFromContext(contextProbe, summarizeRepoTechStack(topResults));
7093
+ const entrypoints = summarizeEntrypoints(state?.index, topResults, 5);
6986
7094
  const lines = [
6987
7095
  `Fast repository summary for ${codebasePath}:`,
6988
7096
  "",
@@ -7001,10 +7109,10 @@ function repoSummaryLines(codebasePath, state, contextProbe) {
7001
7109
  lines.push(`- ${path.basename(item.filePath)}: ${item.excerpt || "context file detected"}`);
7002
7110
  }
7003
7111
  }
7004
- if (topResults.length > 0) {
7112
+ if (entrypoints.length > 0) {
7005
7113
  lines.push("");
7006
7114
  lines.push("Key entrypoints:");
7007
- for (const result of topResults.slice(0, 5)) {
7115
+ for (const result of entrypoints) {
7008
7116
  lines.push(`- ${result.relativePath}:${result.startLine}-${result.endLine}`);
7009
7117
  }
7010
7118
  }