kiri-mcp-server 0.13.0 → 0.16.0

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 (61) hide show
  1. package/README.md +13 -17
  2. package/config/scoring-profiles.yml +78 -0
  3. package/config/stop-words.yml +307 -0
  4. package/dist/config/scoring-profiles.yml +78 -0
  5. package/dist/config/stop-words.yml +307 -0
  6. package/dist/package.json +2 -2
  7. package/dist/src/indexer/cli.d.ts +1 -0
  8. package/dist/src/indexer/cli.d.ts.map +1 -1
  9. package/dist/src/indexer/cli.js +22 -2
  10. package/dist/src/indexer/cli.js.map +1 -1
  11. package/dist/src/indexer/cochange.d.ts +97 -0
  12. package/dist/src/indexer/cochange.d.ts.map +1 -0
  13. package/dist/src/indexer/cochange.js +315 -0
  14. package/dist/src/indexer/cochange.js.map +1 -0
  15. package/dist/src/indexer/graph-metrics.d.ts +68 -0
  16. package/dist/src/indexer/graph-metrics.d.ts.map +1 -0
  17. package/dist/src/indexer/graph-metrics.js +239 -0
  18. package/dist/src/indexer/graph-metrics.js.map +1 -0
  19. package/dist/src/indexer/schema.d.ts +15 -0
  20. package/dist/src/indexer/schema.d.ts.map +1 -1
  21. package/dist/src/indexer/schema.js +86 -0
  22. package/dist/src/indexer/schema.js.map +1 -1
  23. package/dist/src/server/config.d.ts.map +1 -1
  24. package/dist/src/server/config.js +22 -16
  25. package/dist/src/server/config.js.map +1 -1
  26. package/dist/src/server/handlers/snippets-get.d.ts +10 -0
  27. package/dist/src/server/handlers/snippets-get.d.ts.map +1 -1
  28. package/dist/src/server/handlers/snippets-get.js +40 -3
  29. package/dist/src/server/handlers/snippets-get.js.map +1 -1
  30. package/dist/src/server/handlers.d.ts +1 -1
  31. package/dist/src/server/handlers.d.ts.map +1 -1
  32. package/dist/src/server/handlers.js +195 -51
  33. package/dist/src/server/handlers.js.map +1 -1
  34. package/dist/src/server/idf-provider.d.ts +110 -0
  35. package/dist/src/server/idf-provider.d.ts.map +1 -0
  36. package/dist/src/server/idf-provider.js +233 -0
  37. package/dist/src/server/idf-provider.js.map +1 -0
  38. package/dist/src/server/rpc.d.ts.map +1 -1
  39. package/dist/src/server/rpc.js +43 -4
  40. package/dist/src/server/rpc.js.map +1 -1
  41. package/dist/src/server/scoring.d.ts +10 -0
  42. package/dist/src/server/scoring.d.ts.map +1 -1
  43. package/dist/src/server/scoring.js +73 -0
  44. package/dist/src/server/scoring.js.map +1 -1
  45. package/dist/src/server/services/index.d.ts +2 -0
  46. package/dist/src/server/services/index.d.ts.map +1 -1
  47. package/dist/src/server/services/index.js +3 -0
  48. package/dist/src/server/services/index.js.map +1 -1
  49. package/dist/src/server/stop-words.d.ts +106 -0
  50. package/dist/src/server/stop-words.d.ts.map +1 -0
  51. package/dist/src/server/stop-words.js +312 -0
  52. package/dist/src/server/stop-words.js.map +1 -0
  53. package/dist/src/shared/adaptive-k-categories.d.ts +5 -0
  54. package/dist/src/shared/adaptive-k-categories.d.ts.map +1 -0
  55. package/dist/src/shared/adaptive-k-categories.js +17 -0
  56. package/dist/src/shared/adaptive-k-categories.js.map +1 -0
  57. package/dist/src/shared/duckdb.d.ts +8 -2
  58. package/dist/src/shared/duckdb.d.ts.map +1 -1
  59. package/dist/src/shared/duckdb.js +37 -62
  60. package/dist/src/shared/duckdb.js.map +1 -1
  61. package/package.json +2 -2
@@ -2,6 +2,7 @@
2
2
  * Constants
3
3
  */
4
4
  const DEFAULT_SNIPPET_WINDOW = 150;
5
+ const MAX_FULL_LINES = 500; // view: "full" 時の安全上限
5
6
  /**
6
7
  * 行番号をプレフィックスとして追加する(動的幅調整)
7
8
  *
@@ -69,9 +70,42 @@ export async function snippetsGet(context, params) {
69
70
  WHERE s.repo_id = ? AND s.path = ?
70
71
  ORDER BY s.start_line
71
72
  `, [repoId, params.path]);
72
- const requestedStart = params.start_line ?? 1;
73
- const requestedEnd = params.end_line ?? Math.min(totalLines, requestedStart + DEFAULT_SNIPPET_WINDOW - 1);
74
- const useSymbolSnippets = snippetRows.length > 0 && params.end_line === undefined;
73
+ const view = params.view ?? "auto";
74
+ // view パラメータに基づいて取得戦略を決定
75
+ let useSymbolSnippets;
76
+ let requestedStart;
77
+ let requestedEnd;
78
+ switch (view) {
79
+ case "symbol":
80
+ // 強制的にシンボル境界を使用
81
+ useSymbolSnippets = snippetRows.length > 0;
82
+ requestedStart = params.start_line ?? 1;
83
+ requestedEnd =
84
+ params.end_line ?? Math.min(totalLines, requestedStart + DEFAULT_SNIPPET_WINDOW - 1);
85
+ break;
86
+ case "lines":
87
+ // 行ベース取得(シンボル境界を無視)
88
+ useSymbolSnippets = false;
89
+ requestedStart = params.start_line ?? 1;
90
+ requestedEnd =
91
+ params.end_line ?? Math.min(totalLines, requestedStart + DEFAULT_SNIPPET_WINDOW - 1);
92
+ break;
93
+ case "full":
94
+ // ファイル全体を返す(安全上限付き、start_line/end_line は無視)
95
+ useSymbolSnippets = false;
96
+ requestedStart = 1; // 常に1行目から開始
97
+ requestedEnd = Math.min(totalLines, MAX_FULL_LINES);
98
+ break;
99
+ case "auto":
100
+ default: {
101
+ // 現行動作: シンボルがあり end_line 未指定の場合はシンボル境界を使用
102
+ useSymbolSnippets = snippetRows.length > 0 && params.end_line === undefined;
103
+ requestedStart = params.start_line ?? 1;
104
+ requestedEnd =
105
+ params.end_line ?? Math.min(totalLines, requestedStart + DEFAULT_SNIPPET_WINDOW - 1);
106
+ break;
107
+ }
108
+ }
75
109
  let snippetSelection = null;
76
110
  if (useSymbolSnippets) {
77
111
  snippetSelection =
@@ -107,6 +141,8 @@ export async function snippetsGet(context, params) {
107
141
  const snippetContent = lines.slice(startLine - 1, endLine).join("\n");
108
142
  content = addLineNumbers ? prependLineNumbers(snippetContent, startLine) : snippetContent;
109
143
  }
144
+ // view: "full" でファイルが MAX_FULL_LINES を超えた場合に truncated フラグを設定
145
+ const truncated = view === "full" && totalLines > MAX_FULL_LINES;
110
146
  return {
111
147
  path: row.path,
112
148
  startLine,
@@ -115,6 +151,7 @@ export async function snippetsGet(context, params) {
115
151
  totalLines,
116
152
  symbolName,
117
153
  symbolKind,
154
+ ...(truncated && { truncated }),
118
155
  };
119
156
  }
120
157
  //# sourceMappingURL=snippets-get.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"snippets-get.js","sourceRoot":"","sources":["../../../../src/server/handlers/snippets-get.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"snippets-get.js","sourceRoot":"","sources":["../../../../src/server/handlers/snippets-get.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@ import { DuckDBClient } from "../shared/duckdb.js";
2
2
  import { type BoostProfileName } from "./boost-profiles.js";
3
3
  import { ServerContext, TableAvailability } from "./context.js";
4
4
  import { ServerServices } from "./services/index.js";
5
- export { snippetsGet, type SnippetsGetParams, type SnippetResult, } from "./handlers/snippets-get.js";
5
+ export { snippetsGet, type SnippetsGetParams, type SnippetsGetView, type SnippetResult, } from "./handlers/snippets-get.js";
6
6
  /**
7
7
  * checkTableAvailability
8
8
  *
@@ -1 +1 @@
1
- {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../../src/server/handlers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../../src/server/handlers.ts"],"names":[],"mappings":"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"}
@@ -8,8 +8,10 @@ import { expandAbbreviations } from "./abbreviations.js";
8
8
  import { getBoostProfile, } from "./boost-profiles.js";
9
9
  import { loadPathPenalties, mergePathPenaltyEntries } from "./config-loader.js";
10
10
  import { loadServerConfig } from "./config.js";
11
+ import { createIdfProvider } from "./idf-provider.js";
11
12
  import { coerceProfileName, loadScoringProfile } from "./scoring.js";
12
13
  import { createServerServices } from "./services/index.js";
14
+ import { loadStopWords } from "./stop-words.js";
13
15
  // Re-export extracted handlers for backward compatibility
14
16
  export { snippetsGet, } from "./handlers/snippets-get.js";
15
17
  // Configuration file patterns (v0.8.0+: consolidated to avoid duplication)
@@ -533,6 +535,8 @@ const CLAMP_SNIPPETS_ENABLED = serverConfig.features.clampSnippets;
533
535
  const FALLBACK_SNIPPET_WINDOW = serverConfig.features.snippetWindow;
534
536
  const MAX_RERANK_LIMIT = 50;
535
537
  const MAX_ARTIFACT_HINTS = 8;
538
+ /** Minimum confidence floor for co-change scoring to prevent zero-boost from low Jaccard scores */
539
+ const MIN_COCHANGE_CONFIDENCE_FLOOR = 0.2;
536
540
  const DOMAIN_PATH_HINT_LIMIT = MAX_ARTIFACT_HINTS;
537
541
  const SAFE_PATH_PATTERN = /^[a-zA-Z0-9_.\-/]+$/;
538
542
  const HINT_PRIORITY_TEXT_MULTIPLIER = serverConfig.hints.priority.textMultiplier;
@@ -590,15 +594,16 @@ const WHY_TAG_PRIORITY = {
590
594
  substring: 4, // Substring hint expansion
591
595
  "path-phrase": 5, // Path contains multi-word phrase
592
596
  structural: 6, // Semantic similarity
593
- "path-segment": 7, // Path component matches
594
- "path-keyword": 8, // Path keyword match
595
- dep: 9, // Dependency relationship
596
- near: 10, // Proximity to editing file
597
- boost: 11, // File type boost
598
- recent: 12, // Recently changed
599
- symbol: 13, // Symbol match
600
- penalty: 14, // Penalty explanations (keep for transparency)
601
- keyword: 15, // Generic keyword (deprecated, kept for compatibility)
597
+ cochange: 7, // Co-change history (files that change together)
598
+ "path-segment": 8, // Path component matches
599
+ "path-keyword": 9, // Path keyword match
600
+ dep: 10, // Dependency relationship
601
+ near: 11, // Proximity to editing file
602
+ boost: 12, // File type boost
603
+ recent: 13, // Recently changed
604
+ symbol: 14, // Symbol match
605
+ penalty: 15, // Penalty explanations (keep for transparency)
606
+ keyword: 16, // Generic keyword (deprecated, kept for compatibility)
602
607
  };
603
608
  // Reserve at least one slot for important structural tags
604
609
  const RESERVED_WHY_SLOTS = {
@@ -663,39 +668,18 @@ function selectWhyTags(reasons) {
663
668
  }
664
669
  return Array.from(selected);
665
670
  }
666
- const STOP_WORDS = new Set([
667
- "the",
668
- "and",
669
- "for",
670
- "with",
671
- "from",
672
- "this",
673
- "that",
674
- "have",
675
- "has",
676
- "will",
677
- "would",
678
- "into",
679
- "about",
680
- "there",
681
- "their",
682
- "your",
683
- "fix",
684
- "test",
685
- "tests",
686
- "issue",
687
- "error",
688
- "bug",
689
- "fail",
690
- "failing",
691
- "make",
692
- "when",
693
- "where",
694
- "should",
695
- "could",
696
- "need",
697
- "goal",
698
- ]);
671
+ /**
672
+ * ストップワードサービスの遅延初期化
673
+ * シングルトンキャッシュを使用し、config/stop-words.yml から読み込み
674
+ * @see Issue #48: Improve context_bundle stop word coverage and configurability
675
+ */
676
+ let _stopWordsService = null;
677
+ function getStopWordsService() {
678
+ if (!_stopWordsService) {
679
+ _stopWordsService = loadStopWords();
680
+ }
681
+ return _stopWordsService;
682
+ }
699
683
  function prioritizeHintCandidates(rankedCandidates, hintPaths, limit) {
700
684
  if (rankedCandidates.length === 0) {
701
685
  return [];
@@ -822,7 +806,7 @@ function extractCompoundTerms(text) {
822
806
  const matches = Array.from(text.matchAll(compoundPattern)).map((m) => m[1]);
823
807
  return matches
824
808
  .map((term) => term.toLowerCase())
825
- .filter((term) => term.length >= 3 && !STOP_WORDS.has(term));
809
+ .filter((term) => term.length >= 3 && !getStopWordsService().has(term));
826
810
  }
827
811
  /**
828
812
  * パスライクな用語を抽出
@@ -837,7 +821,7 @@ function extractPathSegments(text) {
837
821
  for (const path of matches) {
838
822
  const parts = path.toLowerCase().split("/");
839
823
  for (const part of parts) {
840
- if (part.length >= 3 && !STOP_WORDS.has(part) && !segments.includes(part)) {
824
+ if (part.length >= 3 && !getStopWordsService().has(part) && !segments.includes(part)) {
841
825
  segments.push(part);
842
826
  }
843
827
  }
@@ -849,7 +833,7 @@ function extractPathSegments(text) {
849
833
  * 共有トークン化ユーティリティを使用
850
834
  */
851
835
  function extractRegularWords(text, strategy) {
852
- const words = tokenizeText(text, strategy).filter((word) => word.length >= 3 && !STOP_WORDS.has(word));
836
+ const words = tokenizeText(text, strategy).filter((word) => word.length >= 3 && !getStopWordsService().has(word));
853
837
  return words;
854
838
  }
855
839
  /**
@@ -879,7 +863,7 @@ function extractKeywords(text) {
879
863
  // ハイフンとアンダースコアの両方で分割
880
864
  const parts = term
881
865
  .split(/[-_]/)
882
- .filter((part) => part.length >= 3 && !STOP_WORDS.has(part));
866
+ .filter((part) => part.length >= 3 && !getStopWordsService().has(part));
883
867
  result.keywords.push(...parts);
884
868
  }
885
869
  }
@@ -904,7 +888,7 @@ function addKeywordDerivedPathSegments(result) {
904
888
  }
905
889
  const additional = [];
906
890
  for (const keyword of result.keywords) {
907
- if (keyword.length < 3 || STOP_WORDS.has(keyword)) {
891
+ if (keyword.length < 3 || getStopWordsService().has(keyword)) {
908
892
  continue;
909
893
  }
910
894
  if (result.pathSegments.includes(keyword) || additional.includes(keyword)) {
@@ -1348,6 +1332,133 @@ function applyStructuralScores(candidates, queryEmbedding, structuralWeight) {
1348
1332
  candidate.reasons.add(`structural:${similarity.toFixed(2)}`);
1349
1333
  }
1350
1334
  }
1335
+ /**
1336
+ * Graph Layer: Apply graph-based scoring boosts (Phase 3.2)
1337
+ *
1338
+ * Uses precomputed metrics from graph_metrics table:
1339
+ * - inbound_count: Number of files that import this file (PageRank-like importance)
1340
+ * - importance_score: Normalized PageRank score [0, 1]
1341
+ *
1342
+ * Boosts are additive and scaled by profile weights.
1343
+ */
1344
+ async function applyGraphLayerScores(db, repoId, candidates, weights) {
1345
+ // Skip if both weights are zero (disabled)
1346
+ if (weights.graphInbound <= 0 && weights.graphImportance <= 0) {
1347
+ return;
1348
+ }
1349
+ if (candidates.length === 0) {
1350
+ return;
1351
+ }
1352
+ // Fetch graph metrics for all candidate paths
1353
+ const paths = candidates.map((c) => c.path);
1354
+ const placeholders = paths.map(() => "?").join(", ");
1355
+ const metrics = await db.all(`
1356
+ SELECT path, inbound_count, importance_score
1357
+ FROM graph_metrics
1358
+ WHERE repo_id = ? AND path IN (${placeholders})
1359
+ `, [repoId, ...paths]);
1360
+ // Build lookup map
1361
+ const metricsMap = new Map();
1362
+ for (const m of metrics) {
1363
+ metricsMap.set(m.path, {
1364
+ inbound: m.inbound_count,
1365
+ importance: m.importance_score,
1366
+ });
1367
+ }
1368
+ // Compute max inbound for normalization (log scale)
1369
+ let maxInbound = 1;
1370
+ for (const m of metrics) {
1371
+ if (m.inbound_count > maxInbound) {
1372
+ maxInbound = m.inbound_count;
1373
+ }
1374
+ }
1375
+ // Apply boosts
1376
+ for (const candidate of candidates) {
1377
+ const graphMetrics = metricsMap.get(candidate.path);
1378
+ if (!graphMetrics) {
1379
+ continue;
1380
+ }
1381
+ // Inbound dependency boost (log-scaled to dampen very high values)
1382
+ if (weights.graphInbound > 0 && graphMetrics.inbound > 0) {
1383
+ // Log-scale normalization: log(1 + count) / log(1 + max)
1384
+ const normalizedInbound = Math.log(1 + graphMetrics.inbound) / Math.log(1 + maxInbound);
1385
+ const inboundBoost = weights.graphInbound * normalizedInbound;
1386
+ candidate.score += inboundBoost;
1387
+ candidate.reasons.add(`graph:inbound:${graphMetrics.inbound}`);
1388
+ }
1389
+ // Importance score boost (already normalized to [0, 1])
1390
+ if (weights.graphImportance > 0 && graphMetrics.importance > 0) {
1391
+ const importanceBoost = weights.graphImportance * graphMetrics.importance;
1392
+ candidate.score += importanceBoost;
1393
+ candidate.reasons.add(`graph:importance:${graphMetrics.importance.toFixed(2)}`);
1394
+ }
1395
+ }
1396
+ }
1397
+ /**
1398
+ * Apply co-change scores based on git history.
1399
+ * Files that frequently change together with editing_path get boosted.
1400
+ *
1401
+ * Phase 4: Co-change graph integration.
1402
+ *
1403
+ * @param db - DuckDB client
1404
+ * @param repoId - Repository ID
1405
+ * @param candidates - Candidate files to score
1406
+ * @param weights - Scoring weights (uses cochange weight)
1407
+ * @param editingPath - Currently edited file path (optional)
1408
+ */
1409
+ async function applyCochangeScores(db, repoId, candidates, weights, editingPath) {
1410
+ // Skip if cochange weight is zero (disabled by default)
1411
+ if (weights.cochange <= 0) {
1412
+ return;
1413
+ }
1414
+ // Skip if no editing_path provided (co-change needs a reference file)
1415
+ if (!editingPath || candidates.length === 0) {
1416
+ return;
1417
+ }
1418
+ // Query co-change edges involving editing_path
1419
+ // Both directions: editing_path can be file1 or file2 (canonical ordering)
1420
+ const cochangeEdges = await db.all(`
1421
+ SELECT
1422
+ CASE WHEN file1 = ? THEN file2 ELSE file1 END as neighbor,
1423
+ cochange_count,
1424
+ confidence
1425
+ FROM cochange
1426
+ WHERE repo_id = ? AND (file1 = ? OR file2 = ?)
1427
+ `, [editingPath, repoId, editingPath, editingPath]);
1428
+ if (cochangeEdges.length === 0) {
1429
+ return;
1430
+ }
1431
+ // Build lookup map: neighbor path -> (count, confidence)
1432
+ const cochangeMap = new Map();
1433
+ for (const edge of cochangeEdges) {
1434
+ cochangeMap.set(edge.neighbor, {
1435
+ count: edge.cochange_count,
1436
+ confidence: edge.confidence ?? 0,
1437
+ });
1438
+ }
1439
+ // Compute max count for normalization
1440
+ let maxCount = 1;
1441
+ for (const edge of cochangeEdges) {
1442
+ if (edge.cochange_count > maxCount) {
1443
+ maxCount = edge.cochange_count;
1444
+ }
1445
+ }
1446
+ // Apply cochange boost to candidates
1447
+ for (const candidate of candidates) {
1448
+ const cochange = cochangeMap.get(candidate.path);
1449
+ if (!cochange || cochange.count <= 0) {
1450
+ continue;
1451
+ }
1452
+ // Normalize cochange count using log scale (similar to inbound boost)
1453
+ const normalizedCount = Math.log(1 + cochange.count) / Math.log(1 + maxCount);
1454
+ // Weight the boost by confidence (Jaccard similarity) for quality
1455
+ // Final boost = weight * normalized_count * confidence
1456
+ const confidenceFactor = Math.max(cochange.confidence, MIN_COCHANGE_CONFIDENCE_FLOOR);
1457
+ const cochangeBoost = weights.cochange * normalizedCount * confidenceFactor;
1458
+ candidate.score += cochangeBoost;
1459
+ candidate.reasons.add(`cochange:${cochange.count}:${(cochange.confidence * 100).toFixed(0)}%`);
1460
+ }
1461
+ }
1351
1462
  async function fetchEmbeddingMap(db, repoId, paths) {
1352
1463
  const map = new Map();
1353
1464
  if (paths.length === 0) {
@@ -2752,6 +2863,14 @@ async function contextBundleImpl(context, params) {
2752
2863
  // 2. そうでなければprofile/artifacts/boost_profileから自動検出
2753
2864
  const detectedCategory = determineCategory(params);
2754
2865
  const adaptiveK = getAdaptiveK(detectedCategory, serverConfig.adaptiveK);
2866
+ if (process.env.KIRI_TRACE_ADAPTIVE_K === "1") {
2867
+ console.info("[adaptive-k]", JSON.stringify({
2868
+ detectedCategory,
2869
+ selectedK: adaptiveK,
2870
+ userLimit: params.limit ?? null,
2871
+ enabled: serverConfig.adaptiveK.enabled,
2872
+ }));
2873
+ }
2755
2874
  const adaptiveLimit = normalizeBundleLimit(adaptiveK);
2756
2875
  const requestedLimit = normalizeBundleLimit(params.limit);
2757
2876
  // AdaptiveKが無効な場合はrequestLimitをそのまま使用
@@ -2821,12 +2940,26 @@ async function contextBundleImpl(context, params) {
2821
2940
  const pathSegments = artifacts.editing_path
2822
2941
  .split(/[/_.-]/)
2823
2942
  .map((segment) => segment.toLowerCase())
2824
- .filter((segment) => segment.length >= 3 && !STOP_WORDS.has(segment));
2943
+ .filter((segment) => segment.length >= 3 && !getStopWordsService().has(segment));
2825
2944
  extractedTerms.pathSegments.push(...pathSegments.slice(0, MAX_KEYWORDS));
2826
2945
  }
2827
2946
  const candidates = new Map();
2828
2947
  const stringMatchSeeds = new Set();
2829
2948
  const fileCache = new Map();
2949
+ // Phase 2: IDF重み付けプロバイダーの初期化
2950
+ // キーワードの文書頻度に基づいて重みを計算し、高頻度語を自動的に減衰
2951
+ const idfProvider = createIdfProvider(db, repoId);
2952
+ const idfWeights = new Map();
2953
+ // 抽出されたキーワードのIDF重みを事前計算(非同期バッチ処理)
2954
+ if (extractedTerms.keywords.length > 0) {
2955
+ const computedWeights = await idfProvider.computeIdfBatch(extractedTerms.keywords);
2956
+ for (const [term, weight] of computedWeights) {
2957
+ idfWeights.set(term, weight);
2958
+ }
2959
+ if (process.env.KIRI_TRACE_IDF === "1") {
2960
+ console.info("[idf-weights]", JSON.stringify(Object.fromEntries(Array.from(idfWeights.entries()).map(([k, v]) => [k, v.toFixed(3)]))));
2961
+ }
2962
+ }
2830
2963
  // ✅ Cache boost profile config to avoid redundant lookups in hot path
2831
2964
  const boostProfile = params.boost_profile ??
2832
2965
  (hasHintMetadataFilters ? "balanced" : hasStrictMetadataFilters ? "docs" : "default");
@@ -2953,10 +3086,17 @@ async function contextBundleImpl(context, params) {
2953
3086
  continue; // Should not happen, but defensive check
2954
3087
  }
2955
3088
  const candidate = ensureCandidate(candidates, row.path);
2956
- // 各マッチしたキーワードに対してスコアリング
3089
+ // 各マッチしたキーワードに対してスコアリング(Phase 2: IDF重み付け)
2957
3090
  for (const keyword of matchedKeywords) {
2958
- candidate.score += weights.textMatch;
2959
- candidate.reasons.add(`text:${keyword}`);
3091
+ // IDF重みを適用(事前計算済み、なければデフォルト1.0)
3092
+ // 減衰適用: 0.6 + 0.4 * idfWeight でファイル種別マルチプライヤとのバランスを維持
3093
+ // - 高頻度語: IDF=0 → 0.6 (40%減)
3094
+ // - 低頻度語: IDF=1 → 1.0 (減衰なし)
3095
+ const rawIdfWeight = idfWeights.get(keyword.toLowerCase()) ?? 1.0;
3096
+ const dampedIdfWeight = 0.6 + 0.4 * rawIdfWeight;
3097
+ const weightedScore = weights.textMatch * dampedIdfWeight;
3098
+ candidate.score += weightedScore;
3099
+ candidate.reasons.add(`text:${keyword}:idf=${rawIdfWeight.toFixed(2)}`);
2960
3100
  candidate.keywordHits.add(keyword);
2961
3101
  }
2962
3102
  // Apply boost profile once per file
@@ -3330,6 +3470,10 @@ async function contextBundleImpl(context, params) {
3330
3470
  }
3331
3471
  }
3332
3472
  applyStructuralScores(materializedCandidates, queryEmbedding, weights.structural);
3473
+ // Phase 3.2: Apply graph layer scoring (inbound dependencies, PageRank importance)
3474
+ await applyGraphLayerScores(db, repoId, materializedCandidates, weights);
3475
+ // Phase 4: Apply co-change scores (files that change together with editing_path)
3476
+ await applyCochangeScores(db, repoId, materializedCandidates, weights, artifacts.editing_path);
3333
3477
  // ✅ CRITICAL SAFETY: Apply multipliers AFTER all additive scoring (v0.7.0)
3334
3478
  // Only apply to positive scores to prevent negative score inversion
3335
3479
  for (const candidate of materializedCandidates) {