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.
- package/README.md +13 -17
- package/config/scoring-profiles.yml +78 -0
- package/config/stop-words.yml +307 -0
- package/dist/config/scoring-profiles.yml +78 -0
- package/dist/config/stop-words.yml +307 -0
- package/dist/package.json +2 -2
- package/dist/src/indexer/cli.d.ts +1 -0
- package/dist/src/indexer/cli.d.ts.map +1 -1
- package/dist/src/indexer/cli.js +22 -2
- package/dist/src/indexer/cli.js.map +1 -1
- package/dist/src/indexer/cochange.d.ts +97 -0
- package/dist/src/indexer/cochange.d.ts.map +1 -0
- package/dist/src/indexer/cochange.js +315 -0
- package/dist/src/indexer/cochange.js.map +1 -0
- package/dist/src/indexer/graph-metrics.d.ts +68 -0
- package/dist/src/indexer/graph-metrics.d.ts.map +1 -0
- package/dist/src/indexer/graph-metrics.js +239 -0
- package/dist/src/indexer/graph-metrics.js.map +1 -0
- package/dist/src/indexer/schema.d.ts +15 -0
- package/dist/src/indexer/schema.d.ts.map +1 -1
- package/dist/src/indexer/schema.js +86 -0
- package/dist/src/indexer/schema.js.map +1 -1
- package/dist/src/server/config.d.ts.map +1 -1
- package/dist/src/server/config.js +22 -16
- package/dist/src/server/config.js.map +1 -1
- package/dist/src/server/handlers/snippets-get.d.ts +10 -0
- package/dist/src/server/handlers/snippets-get.d.ts.map +1 -1
- package/dist/src/server/handlers/snippets-get.js +40 -3
- package/dist/src/server/handlers/snippets-get.js.map +1 -1
- package/dist/src/server/handlers.d.ts +1 -1
- package/dist/src/server/handlers.d.ts.map +1 -1
- package/dist/src/server/handlers.js +195 -51
- package/dist/src/server/handlers.js.map +1 -1
- package/dist/src/server/idf-provider.d.ts +110 -0
- package/dist/src/server/idf-provider.d.ts.map +1 -0
- package/dist/src/server/idf-provider.js +233 -0
- package/dist/src/server/idf-provider.js.map +1 -0
- package/dist/src/server/rpc.d.ts.map +1 -1
- package/dist/src/server/rpc.js +43 -4
- package/dist/src/server/rpc.js.map +1 -1
- package/dist/src/server/scoring.d.ts +10 -0
- package/dist/src/server/scoring.d.ts.map +1 -1
- package/dist/src/server/scoring.js +73 -0
- package/dist/src/server/scoring.js.map +1 -1
- package/dist/src/server/services/index.d.ts +2 -0
- package/dist/src/server/services/index.d.ts.map +1 -1
- package/dist/src/server/services/index.js +3 -0
- package/dist/src/server/services/index.js.map +1 -1
- package/dist/src/server/stop-words.d.ts +106 -0
- package/dist/src/server/stop-words.d.ts.map +1 -0
- package/dist/src/server/stop-words.js +312 -0
- package/dist/src/server/stop-words.js.map +1 -0
- package/dist/src/shared/adaptive-k-categories.d.ts +5 -0
- package/dist/src/shared/adaptive-k-categories.d.ts.map +1 -0
- package/dist/src/shared/adaptive-k-categories.js +17 -0
- package/dist/src/shared/adaptive-k-categories.js.map +1 -0
- package/dist/src/shared/duckdb.d.ts +8 -2
- package/dist/src/shared/duckdb.d.ts.map +1 -1
- package/dist/src/shared/duckdb.js +37 -62
- package/dist/src/shared/duckdb.js.map +1 -1
- 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
|
|
73
|
-
|
|
74
|
-
|
|
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":"
|
|
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":"AAKA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAKnD,OAAO,EACL,KAAK,gBAAgB,EAItB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAkB,aAAa,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../../src/server/handlers.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAKnD,OAAO,EACL,KAAK,gBAAgB,EAItB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAkB,aAAa,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAIhF,OAAO,EAAwB,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAI3E,OAAO,EACL,WAAW,EACX,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,aAAa,GACnB,MAAM,4BAA4B,CAAC;AAkMpC;;;;;;;;;;;GAWG;AACH,wBAAsB,sBAAsB,CAAC,EAAE,EAAE,YAAY,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA0DzF;AA2GD,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,gBAAgB,CAAC;IACjC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;CACtD;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf;AAID,MAAM,WAAW,sBAAsB;IACrC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,sBAAsB,CAAC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,gBAAgB,CAAC;IACjC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IACrD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAoTD,MAAM,WAAW,4BAA4B;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,4BAA4B,EAAE,CAAC;IAC3C,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,kBAAkB,EAAE,CAAC;CAClC;AAoMD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IACnC,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,UAAU,GAAG,SAAS,CAAC;IAClC,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,KAAK,EAAE,eAAe,EAAE,CAAC;CAC1B;AA4tED,wBAAsB,WAAW,CAC/B,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAqQ9B;AA2zCD,wBAAsB,cAAc,CAClC,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,oBAAoB,GAC3B,OAAO,CAAC,oBAAoB,CAAC,CA+E/B;AAED,wBAAsB,WAAW,CAC/B,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,iBAAiB,CAAC,CAuJ5B;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,aAAa,CACjC,EAAE,EAAE,YAAY,EAChB,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,cAAc,GACxB,OAAO,CAAC,MAAM,CAAC,CAGjB;AAED,wBAAsB,aAAa,CACjC,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,mBAAmB,CAAC,CAO9B"}
|
|
@@ -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
|
-
|
|
594
|
-
"path-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
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
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
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 && !
|
|
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 && !
|
|
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 && !
|
|
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 && !
|
|
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 ||
|
|
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 && !
|
|
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
|
-
|
|
2959
|
-
|
|
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) {
|