kiri-mcp-server 0.12.0 → 0.14.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 (50) hide show
  1. package/README.md +10 -14
  2. package/config/domain-terms.yml +37 -0
  3. package/dist/config/domain-terms.yml +37 -0
  4. package/dist/package.json +6 -1
  5. package/dist/src/server/boost-profiles.d.ts.map +1 -1
  6. package/dist/src/server/boost-profiles.js +92 -6
  7. package/dist/src/server/boost-profiles.js.map +1 -1
  8. package/dist/src/server/config.d.ts +2 -0
  9. package/dist/src/server/config.d.ts.map +1 -1
  10. package/dist/src/server/config.js +56 -0
  11. package/dist/src/server/config.js.map +1 -1
  12. package/dist/src/server/context.d.ts +4 -0
  13. package/dist/src/server/context.d.ts.map +1 -1
  14. package/dist/src/server/context.js +6 -0
  15. package/dist/src/server/context.js.map +1 -1
  16. package/dist/src/server/domain-terms.d.ts +28 -0
  17. package/dist/src/server/domain-terms.d.ts.map +1 -0
  18. package/dist/src/server/domain-terms.js +203 -0
  19. package/dist/src/server/domain-terms.js.map +1 -0
  20. package/dist/src/server/handlers.d.ts +2 -0
  21. package/dist/src/server/handlers.d.ts.map +1 -1
  22. package/dist/src/server/handlers.js +170 -14
  23. package/dist/src/server/handlers.js.map +1 -1
  24. package/dist/src/server/observability/metrics.d.ts +12 -0
  25. package/dist/src/server/observability/metrics.d.ts.map +1 -1
  26. package/dist/src/server/observability/metrics.js +11 -0
  27. package/dist/src/server/observability/metrics.js.map +1 -1
  28. package/dist/src/server/rpc.d.ts.map +1 -1
  29. package/dist/src/server/rpc.js +37 -2
  30. package/dist/src/server/rpc.js.map +1 -1
  31. package/dist/src/server/runtime.d.ts.map +1 -1
  32. package/dist/src/server/runtime.js +1 -0
  33. package/dist/src/server/runtime.js.map +1 -1
  34. package/dist/src/server/services/index.d.ts +2 -0
  35. package/dist/src/server/services/index.d.ts.map +1 -1
  36. package/dist/src/server/services/index.js +12 -0
  37. package/dist/src/server/services/index.js.map +1 -1
  38. package/dist/src/shared/adaptive-k-categories.d.ts +5 -0
  39. package/dist/src/shared/adaptive-k-categories.d.ts.map +1 -0
  40. package/dist/src/shared/adaptive-k-categories.js +17 -0
  41. package/dist/src/shared/adaptive-k-categories.js.map +1 -0
  42. package/dist/src/shared/adaptive-k.d.ts +12 -0
  43. package/dist/src/shared/adaptive-k.d.ts.map +1 -0
  44. package/dist/src/shared/adaptive-k.js +8 -0
  45. package/dist/src/shared/adaptive-k.js.map +1 -0
  46. package/dist/src/shared/config-validate-adaptive-k.d.ts +3 -0
  47. package/dist/src/shared/config-validate-adaptive-k.d.ts.map +1 -0
  48. package/dist/src/shared/config-validate-adaptive-k.js +44 -0
  49. package/dist/src/shared/config-validate-adaptive-k.js.map +1 -0
  50. package/package.json +6 -1
@@ -0,0 +1,203 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { parse } from "yaml";
5
+ import { z } from "zod";
6
+ const SAFE_PATH_PATTERN = /^[a-zA-Z0-9_.\-/]+$/;
7
+ const DEFAULT_CANDIDATE_FILES = [
8
+ "config/domain-terms.yml",
9
+ "config/domain-terms.yaml",
10
+ ".kiri/domain-terms.yml",
11
+ ".kiri/domain-terms.yaml",
12
+ ];
13
+ const TermDefinitionSchema = z
14
+ .object({
15
+ aliases: z.array(z.string().trim().min(1)).optional(),
16
+ files: z.array(z.string().trim().min(1)).optional(),
17
+ })
18
+ .strict();
19
+ function normalizeTermId(raw) {
20
+ const trimmed = raw.trim();
21
+ if (!trimmed) {
22
+ throw new Error("Domain term is empty → YAML内のkey/aliasesを確認してください");
23
+ }
24
+ const decamelized = trimmed.replace(/([a-z0-9])([A-Z])/g, "$1-$2");
25
+ const replaced = decamelized.replace(/[\s_]+/g, "-");
26
+ const collapsed = replaced.replace(/[^a-zA-Z0-9-]/g, "-").replace(/-+/g, "-");
27
+ const normalized = collapsed.replace(/^-|-$/g, "").toLowerCase();
28
+ if (normalized.length < 2) {
29
+ throw new Error(`Domain term "${raw}" is too short after normalization → 2文字以上の識別子にしてください`);
30
+ }
31
+ return normalized;
32
+ }
33
+ function normalizePath(raw) {
34
+ const trimmed = raw.trim();
35
+ if (!trimmed) {
36
+ return null;
37
+ }
38
+ const normalized = trimmed.replace(/^\.\/?/, "").replace(/\\/g, "/");
39
+ if (!SAFE_PATH_PATTERN.test(normalized)) {
40
+ return null;
41
+ }
42
+ return normalized;
43
+ }
44
+ function splitTermParts(term) {
45
+ const parts = term
46
+ .split(/[-_]/)
47
+ .flatMap((p) => p.split(/(?=[A-Z])/)) // simple camelCase split
48
+ .map((p) => p.trim().toLowerCase())
49
+ .filter((p) => p.length >= 3);
50
+ return Array.from(new Set(parts));
51
+ }
52
+ function buildEntries(input) {
53
+ const entries = [];
54
+ for (const [category, items] of Object.entries(input)) {
55
+ for (const item of items) {
56
+ for (const [canonical, definition] of Object.entries(item)) {
57
+ const normalizedCanonical = normalizeTermId(canonical);
58
+ const schemaResult = TermDefinitionSchema.safeParse(definition);
59
+ if (!schemaResult.success) {
60
+ const message = schemaResult.error.issues.map((issue) => issue.message).join(", ");
61
+ throw new Error(`Invalid domain term definition for ${canonical}: ${message} → YAMLの形式を修正してください`);
62
+ }
63
+ const aliases = schemaResult.data.aliases ?? [];
64
+ const normalizedAliases = new Set();
65
+ normalizedAliases.add(normalizedCanonical);
66
+ for (const alias of aliases) {
67
+ const main = normalizeTermId(alias);
68
+ normalizedAliases.add(main);
69
+ for (const part of splitTermParts(alias)) {
70
+ normalizedAliases.add(part);
71
+ }
72
+ }
73
+ // canonicalも分割パーツを追加
74
+ for (const part of splitTermParts(canonical)) {
75
+ normalizedAliases.add(part);
76
+ }
77
+ const files = (schemaResult.data.files ?? [])
78
+ .map((file) => normalizePath(file))
79
+ .filter((value) => Boolean(value));
80
+ entries.push({
81
+ canonical,
82
+ normalizedCanonical,
83
+ aliases,
84
+ normalizedAliases: Array.from(normalizedAliases),
85
+ files,
86
+ category,
87
+ });
88
+ }
89
+ }
90
+ }
91
+ return entries;
92
+ }
93
+ function extractCandidateTerms(text) {
94
+ const matches = text.match(/[A-Za-z0-9_-]+/g) ?? [];
95
+ const candidates = new Set();
96
+ for (const token of matches) {
97
+ try {
98
+ const normalized = normalizeTermId(token);
99
+ candidates.add(normalized);
100
+ }
101
+ catch {
102
+ continue;
103
+ }
104
+ }
105
+ return Array.from(candidates);
106
+ }
107
+ export class DomainTermsDictionary {
108
+ lookup;
109
+ constructor(entries) {
110
+ this.lookup = new Map();
111
+ for (const entry of entries) {
112
+ this.lookup.set(entry.normalizedCanonical, entry);
113
+ for (const alias of entry.normalizedAliases) {
114
+ this.lookup.set(alias, entry);
115
+ }
116
+ }
117
+ }
118
+ expandFromText(text) {
119
+ const candidates = extractCandidateTerms(text);
120
+ return this.expandCandidates(candidates);
121
+ }
122
+ expandCandidates(candidates) {
123
+ const matchedEntries = new Map();
124
+ for (const candidate of candidates) {
125
+ const entry = this.lookup.get(candidate);
126
+ if (entry) {
127
+ matchedEntries.set(entry.normalizedCanonical, entry);
128
+ }
129
+ }
130
+ if (matchedEntries.size === 0) {
131
+ return { matched: [], aliases: [], fileHints: [] };
132
+ }
133
+ const aliasKeywords = new Set();
134
+ const fileHints = [];
135
+ for (const entry of matchedEntries.values()) {
136
+ for (const alias of entry.normalizedAliases) {
137
+ aliasKeywords.add(alias);
138
+ }
139
+ if (!aliasKeywords.has(entry.normalizedCanonical)) {
140
+ aliasKeywords.add(entry.normalizedCanonical);
141
+ }
142
+ for (const file of entry.files) {
143
+ if (fileHints.some((hint) => hint.path === file)) {
144
+ continue;
145
+ }
146
+ fileHints.push({ path: file, source: entry.canonical });
147
+ }
148
+ }
149
+ return {
150
+ matched: Array.from(matchedEntries.keys()),
151
+ aliases: Array.from(aliasKeywords),
152
+ fileHints,
153
+ };
154
+ }
155
+ }
156
+ function resolveConfigPath(configPath, cwd = process.cwd()) {
157
+ const explicit = configPath ?? process.env.KIRI_DOMAIN_TERMS_CONFIG;
158
+ if (explicit) {
159
+ const resolved = path.isAbsolute(explicit) ? explicit : path.join(cwd, explicit);
160
+ if (!fs.existsSync(resolved)) {
161
+ throw new Error(`Domain terms config not found at ${resolved} → パスを確認するかファイルを配置してください`);
162
+ }
163
+ return resolved;
164
+ }
165
+ const currentDir = path.dirname(fileURLToPath(import.meta.url));
166
+ const candidates = [
167
+ ...DEFAULT_CANDIDATE_FILES.map((candidate) => path.join(cwd, candidate)),
168
+ path.join(currentDir, "../../../config/domain-terms.yml"),
169
+ path.join(currentDir, "../../../config/domain-terms.yaml"),
170
+ ];
171
+ for (const candidate of candidates) {
172
+ if (fs.existsSync(candidate)) {
173
+ return candidate;
174
+ }
175
+ }
176
+ return undefined;
177
+ }
178
+ export function loadDomainTerms(options = {}) {
179
+ const resolvedPath = resolveConfigPath(options.configPath, options.cwd);
180
+ if (!resolvedPath) {
181
+ return new DomainTermsDictionary([]);
182
+ }
183
+ const raw = fs.readFileSync(resolvedPath, "utf8");
184
+ const parsed = parse(raw);
185
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
186
+ throw new Error("Invalid domain terms config: top-level must be a mapping → YAMLを修正してください");
187
+ }
188
+ const dictionary = {};
189
+ for (const [category, value] of Object.entries(parsed)) {
190
+ if (!Array.isArray(value)) {
191
+ throw new Error(`Invalid domain terms config: category "${category}" must be an array of terms → YAMLを修正してください`);
192
+ }
193
+ dictionary[category] = value.map((entry, idx) => {
194
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
195
+ throw new Error(`Invalid domain terms config: entry #${idx + 1} in category "${category}" must be a mapping → YAMLを修正してください`);
196
+ }
197
+ return entry;
198
+ });
199
+ }
200
+ const entries = buildEntries(dictionary);
201
+ return new DomainTermsDictionary(entries);
202
+ }
203
+ //# sourceMappingURL=domain-terms.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domain-terms.js","sourceRoot":"","sources":["../../../src/server/domain-terms.ts"],"names":[],"mappings":"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"}
@@ -50,6 +50,8 @@ export interface ContextBundleParams {
50
50
  includeTokensEstimate?: boolean;
51
51
  metadata_filters?: Record<string, string | string[]>;
52
52
  requestId?: string;
53
+ path_prefix?: string;
54
+ category?: string;
53
55
  }
54
56
  export interface ContextBundleItem {
55
57
  path: string;
@@ -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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"}
@@ -1,6 +1,7 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { checkFTSSchemaExists } from "../indexer/schema.js";
4
+ import { getAdaptiveK } from "../shared/adaptive-k.js";
4
5
  import { generateEmbedding, structuralSimilarity } from "../shared/embedding.js";
5
6
  import { encode as encodeGPT, tokenizeText } from "../shared/tokenizer.js";
6
7
  import { expandAbbreviations } from "./abbreviations.js";
@@ -337,6 +338,76 @@ function bucketArtifactHints(hints) {
337
338
  }
338
339
  return buckets;
339
340
  }
341
+ function selectDomainPathHints(hints) {
342
+ const selected = [];
343
+ for (const hint of hints) {
344
+ if (!SAFE_PATH_PATTERN.test(hint.path)) {
345
+ continue;
346
+ }
347
+ if (selected.some((entry) => entry.path === hint.path)) {
348
+ continue;
349
+ }
350
+ selected.push(hint);
351
+ if (selected.length >= DOMAIN_PATH_HINT_LIMIT) {
352
+ break;
353
+ }
354
+ }
355
+ return selected;
356
+ }
357
+ /**
358
+ * AdaptiveK用カテゴリ自動検出
359
+ *
360
+ * 優先順位:
361
+ * 1. params.categoryが明示的に指定されている場合はそれを使用
362
+ * 2. profile/artifacts/boost_profileから推論
363
+ *
364
+ * @param params - context_bundleのパラメータ
365
+ * @returns 検出されたカテゴリ(undefined = デフォルトK値を使用)
366
+ */
367
+ function determineCategory(params) {
368
+ // 明示的に指定されている場合はそれを優先
369
+ if (params.category) {
370
+ return params.category;
371
+ }
372
+ const { profile, boost_profile, artifacts } = params;
373
+ // artifacts.failing_testsがある場合はデバッグ作業
374
+ if (artifacts?.failing_tests && artifacts.failing_tests.length > 0) {
375
+ return "debug";
376
+ }
377
+ // profileからの推論
378
+ if (profile === "testfail") {
379
+ return "testfail";
380
+ }
381
+ if (profile === "bugfix" || profile === "typeerror") {
382
+ return "debug";
383
+ }
384
+ if (profile === "refactor") {
385
+ return "api";
386
+ }
387
+ if (profile === "feature") {
388
+ return "feature";
389
+ }
390
+ // boost_profileからの推論
391
+ if (boost_profile === "docs") {
392
+ return "docs";
393
+ }
394
+ // artifacts.editing_pathからの推論
395
+ if (artifacts?.editing_path) {
396
+ const editingPath = artifacts.editing_path.toLowerCase();
397
+ // テストファイルを編集中 → debug
398
+ if (editingPath.includes(".test.") ||
399
+ editingPath.includes(".spec.") ||
400
+ editingPath.includes("__tests__")) {
401
+ return "debug";
402
+ }
403
+ // ドキュメントを編集中 → docs
404
+ if (editingPath.endsWith(".md") || editingPath.includes("/docs/")) {
405
+ return "docs";
406
+ }
407
+ }
408
+ // 推論できない場合はundefined(kDefaultを使用)
409
+ return undefined;
410
+ }
340
411
  function isMissingTableError(error, table) {
341
412
  if (!(error instanceof Error)) {
342
413
  return false;
@@ -462,6 +533,7 @@ const CLAMP_SNIPPETS_ENABLED = serverConfig.features.clampSnippets;
462
533
  const FALLBACK_SNIPPET_WINDOW = serverConfig.features.snippetWindow;
463
534
  const MAX_RERANK_LIMIT = 50;
464
535
  const MAX_ARTIFACT_HINTS = 8;
536
+ const DOMAIN_PATH_HINT_LIMIT = MAX_ARTIFACT_HINTS;
465
537
  const SAFE_PATH_PATTERN = /^[a-zA-Z0-9_.\-/]+$/;
466
538
  const HINT_PRIORITY_TEXT_MULTIPLIER = serverConfig.hints.priority.textMultiplier;
467
539
  const HINT_PRIORITY_PATH_MULTIPLIER = serverConfig.hints.priority.pathMultiplier;
@@ -873,6 +945,25 @@ function ensureCandidate(map, filePath) {
873
945
  }
874
946
  return candidate;
875
947
  }
948
+ function normalizePathPrefix(rawPrefix) {
949
+ // Normalize, strip leading slashes/dots, and ensure trailing slash for exact prefix match
950
+ const normalized = path.posix.normalize(rawPrefix.replace(/\\/g, "/"));
951
+ const stripped = normalized.replace(/^\.\//, "").replace(/^\/+/, "");
952
+ if (stripped === "" || stripped === ".") {
953
+ return "";
954
+ }
955
+ return stripped.endsWith("/") ? stripped : `${stripped}/`;
956
+ }
957
+ function normalizeFilePath(filePath) {
958
+ return path.posix.normalize(filePath.replace(/\\/g, "/")).replace(/^\/+/, "");
959
+ }
960
+ function pathMatchesPrefix(filePath, normalizedPrefix) {
961
+ if (!normalizedPrefix) {
962
+ return true;
963
+ }
964
+ const normalizedPath = normalizeFilePath(filePath);
965
+ return normalizedPath.startsWith(normalizedPrefix);
966
+ }
876
967
  async function expandHintCandidatesForHints(params) {
877
968
  const { hintPaths, config } = params;
878
969
  if (hintPaths.length === 0 || config.perHintLimit <= 0 || config.dbQueryBudget <= 0) {
@@ -1575,13 +1666,17 @@ async function safeLinkQuery(db, tableAvailability, sql, params) {
1575
1666
  throw error;
1576
1667
  }
1577
1668
  }
1578
- async function fetchMetadataOnlyCandidates(db, tableAvailability, repoId, filters, limit) {
1669
+ async function fetchMetadataOnlyCandidates(db, tableAvailability, repoId, filters, limit, pathPrefix) {
1579
1670
  if (!tableAvailability.hasMetadataTables || filters.length === 0 || limit <= 0) {
1580
1671
  return [];
1581
1672
  }
1582
1673
  const filterClauses = buildMetadataFilterConditions(filters);
1583
1674
  const whereClauses = ["f.repo_id = ?"];
1584
1675
  const params = [repoId];
1676
+ if (pathPrefix) {
1677
+ whereClauses.push("f.path LIKE ?");
1678
+ params.push(`${pathPrefix}%`);
1679
+ }
1585
1680
  for (const clause of filterClauses) {
1586
1681
  whereClauses.push(clause.sql);
1587
1682
  params.push(...clause.params);
@@ -1606,13 +1701,17 @@ async function fetchMetadataOnlyCandidates(db, tableAvailability, repoId, filter
1606
1701
  throw error;
1607
1702
  }
1608
1703
  }
1609
- async function fetchMetadataKeywordMatches(db, tableAvailability, repoId, keywords, filters, limit, excludePaths) {
1704
+ async function fetchMetadataKeywordMatches(db, tableAvailability, repoId, keywords, filters, limit, excludePaths, pathPrefix) {
1610
1705
  if (!tableAvailability.hasMetadataTables || keywords.length === 0 || limit <= 0) {
1611
1706
  return [];
1612
1707
  }
1613
1708
  const keywordClauses = keywords.map(() => "mk.value ILIKE ?").join(" OR ");
1614
1709
  const params = [repoId, ...keywords.map((kw) => `%${kw}%`)];
1615
1710
  const whereClauses = ["mk.repo_id = ?", `(${keywordClauses})`];
1711
+ if (pathPrefix) {
1712
+ whereClauses.push("f.path LIKE ?");
1713
+ params.push(`${pathPrefix}%`);
1714
+ }
1616
1715
  if (excludePaths.size > 0) {
1617
1716
  const placeholders = Array.from(excludePaths)
1618
1717
  .map(() => "?")
@@ -2166,6 +2265,7 @@ export async function filesSearch(context, params) {
2166
2265
  cleanedQuery = cleanedQuery.trim();
2167
2266
  hasTextQuery = cleanedQuery.length > 0;
2168
2267
  }
2268
+ const pathPrefix = params.path_prefix && params.path_prefix.length > 0 ? params.path_prefix : undefined;
2169
2269
  const metadataValueSeed = metadataFilters
2170
2270
  .flatMap((filter) => filter.values)
2171
2271
  .map((value) => value.trim())
@@ -2264,7 +2364,7 @@ export async function filesSearch(context, params) {
2264
2364
  candidateRows.push(...textRows);
2265
2365
  }
2266
2366
  if (!hasTextQuery && hasAnyMetadataFilters) {
2267
- const metadataOnlyRows = await fetchMetadataOnlyCandidates(db, context.tableAvailability, repoId, metadataFilters, limit * 2);
2367
+ const metadataOnlyRows = await fetchMetadataOnlyCandidates(db, context.tableAvailability, repoId, metadataFilters, limit * 2, pathPrefix);
2268
2368
  for (const row of metadataOnlyRows) {
2269
2369
  row.score = 1 + metadataFilters.length * 0.2;
2270
2370
  }
@@ -2274,7 +2374,7 @@ export async function filesSearch(context, params) {
2274
2374
  const metadataKeywords = splitQueryWords(cleanedQuery.toLowerCase()).map((kw) => kw.toLowerCase());
2275
2375
  if (metadataKeywords.length > 0) {
2276
2376
  const excludePaths = new Set(candidateRows.map((row) => row.path));
2277
- const metadataRows = await fetchMetadataKeywordMatches(db, context.tableAvailability, repoId, metadataKeywords, metadataFilters, limit * 2, excludePaths);
2377
+ const metadataRows = await fetchMetadataKeywordMatches(db, context.tableAvailability, repoId, metadataKeywords, metadataFilters, limit * 2, excludePaths, pathPrefix);
2278
2378
  candidateRows.push(...metadataRows);
2279
2379
  }
2280
2380
  }
@@ -2647,7 +2747,30 @@ async function contextBundleImpl(context, params) {
2647
2747
  mergedFilters: metadataFilters,
2648
2748
  }));
2649
2749
  }
2650
- const limit = normalizeBundleLimit(params.limit);
2750
+ // AdaptiveK: カテゴリに基づいてK値を動的に調整
2751
+ // 1. params.categoryが明示的に指定されていればそれを使用
2752
+ // 2. そうでなければprofile/artifacts/boost_profileから自動検出
2753
+ const detectedCategory = determineCategory(params);
2754
+ const adaptiveK = getAdaptiveK(detectedCategory, serverConfig.adaptiveK);
2755
+ if (process.env.KIRI_TRACE_ADAPTIVE_K === "1") {
2756
+ console.info("[adaptive-k]", JSON.stringify({
2757
+ detectedCategory,
2758
+ selectedK: adaptiveK,
2759
+ userLimit: params.limit ?? null,
2760
+ enabled: serverConfig.adaptiveK.enabled,
2761
+ }));
2762
+ }
2763
+ const adaptiveLimit = normalizeBundleLimit(adaptiveK);
2764
+ const requestedLimit = normalizeBundleLimit(params.limit);
2765
+ // AdaptiveKが無効な場合はrequestLimitをそのまま使用
2766
+ // AdaptiveKが有効な場合:
2767
+ // - ユーザー指定limitがなければadaptiveKを使用
2768
+ // - ユーザー指定がある場合は小さい方を採用(過剰取得防止)
2769
+ const limit = serverConfig.adaptiveK.enabled
2770
+ ? params.limit === undefined
2771
+ ? adaptiveLimit
2772
+ : Math.min(requestedLimit, adaptiveLimit)
2773
+ : requestedLimit;
2651
2774
  const artifacts = params.artifacts ?? {};
2652
2775
  const artifactHints = normalizeArtifactHints(artifacts.hints);
2653
2776
  const hintBuckets = bucketArtifactHints(artifactHints);
@@ -2655,6 +2778,9 @@ async function contextBundleImpl(context, params) {
2655
2778
  const substringHints = hintBuckets.substringHints;
2656
2779
  const includeTokensEstimate = params.includeTokensEstimate === true;
2657
2780
  const isCompact = params.compact === true;
2781
+ const pathPrefix = params.path_prefix && params.path_prefix.length > 0
2782
+ ? normalizePathPrefix(params.path_prefix)
2783
+ : undefined;
2658
2784
  // 項目2: トークンバジェット保護警告
2659
2785
  // 大量データ+非コンパクトモード+トークン推定なしの場合に警告
2660
2786
  // リクエストごとに警告(warnForRequestを使用)
@@ -2684,6 +2810,13 @@ async function contextBundleImpl(context, params) {
2684
2810
  .join(" ");
2685
2811
  keywordSources.push(filterSeed);
2686
2812
  }
2813
+ const baseSeed = keywordSources.join(" ");
2814
+ const domainExpansion = process.env.KIRI_ENABLE_DOMAIN_TERMS === "1"
2815
+ ? context.services.domainTerms.expandFromText(baseSeed)
2816
+ : { matched: [], aliases: [], fileHints: [] };
2817
+ if (domainExpansion.aliases.length > 0) {
2818
+ keywordSources.push(domainExpansion.aliases.join(" "));
2819
+ }
2687
2820
  const semanticSeed = keywordSources.join(" ");
2688
2821
  const queryEmbedding = generateEmbedding(semanticSeed)?.values ?? null;
2689
2822
  const extractedTerms = extractKeywords(semanticSeed);
@@ -2717,6 +2850,12 @@ async function contextBundleImpl(context, params) {
2717
2850
  .join(" OR ");
2718
2851
  // DEBUG: Log SQL query parameters for troubleshooting
2719
2852
  traceSearch(`Executing phrase match query with repo_id=${repoId}, phrases=${JSON.stringify(extractedTerms.phrases)}`);
2853
+ const phraseWhereClauses = ["f.repo_id = ?", "f.is_binary = FALSE", `(${phrasePlaceholders})`];
2854
+ const phraseParams = [repoId, ...extractedTerms.phrases];
2855
+ if (pathPrefix) {
2856
+ phraseWhereClauses.push("f.path LIKE ?");
2857
+ phraseParams.push(`${pathPrefix}%`);
2858
+ }
2720
2859
  const rows = await db.all(`
2721
2860
  SELECT f.path, f.lang, f.ext, f.is_binary, b.content, fe.vector_json, fe.dims AS vector_dims
2722
2861
  FROM file f
@@ -2724,12 +2863,11 @@ async function contextBundleImpl(context, params) {
2724
2863
  LEFT JOIN file_embedding fe
2725
2864
  ON fe.repo_id = f.repo_id
2726
2865
  AND fe.path = f.path
2727
- WHERE f.repo_id = ?
2728
- AND f.is_binary = FALSE
2729
- AND (${phrasePlaceholders})
2866
+ WHERE ${phraseWhereClauses.join(`
2867
+ AND `)}
2730
2868
  ORDER BY f.path
2731
2869
  LIMIT ?
2732
- `, [repoId, ...extractedTerms.phrases, MAX_MATCHES_PER_KEYWORD * extractedTerms.phrases.length]);
2870
+ `, [...phraseParams, MAX_MATCHES_PER_KEYWORD * extractedTerms.phrases.length]);
2733
2871
  // DEBUG: Log returned paths and verify they match expected repo_id
2734
2872
  if (rows.length > 0) {
2735
2873
  traceSearch(`Phrase match returned ${rows.length} rows. Sample paths: ${rows
@@ -2790,6 +2928,16 @@ async function contextBundleImpl(context, params) {
2790
2928
  const keywordPlaceholders = extractedTerms.keywords
2791
2929
  .map(() => "b.content ILIKE '%' || ? || '%'")
2792
2930
  .join(" OR ");
2931
+ const keywordWhereClauses = [
2932
+ "f.repo_id = ?",
2933
+ "f.is_binary = FALSE",
2934
+ `(${keywordPlaceholders})`,
2935
+ ];
2936
+ const keywordParams = [repoId, ...extractedTerms.keywords];
2937
+ if (pathPrefix) {
2938
+ keywordWhereClauses.push("f.path LIKE ?");
2939
+ keywordParams.push(`${pathPrefix}%`);
2940
+ }
2793
2941
  const rows = await db.all(`
2794
2942
  SELECT f.path, f.lang, f.ext, f.is_binary, b.content, fe.vector_json, fe.dims AS vector_dims
2795
2943
  FROM file f
@@ -2797,12 +2945,11 @@ async function contextBundleImpl(context, params) {
2797
2945
  LEFT JOIN file_embedding fe
2798
2946
  ON fe.repo_id = f.repo_id
2799
2947
  AND fe.path = f.path
2800
- WHERE f.repo_id = ?
2801
- AND f.is_binary = FALSE
2802
- AND (${keywordPlaceholders})
2948
+ WHERE ${keywordWhereClauses.join(`
2949
+ AND `)}
2803
2950
  ORDER BY f.path
2804
2951
  LIMIT ?
2805
- `, [repoId, ...extractedTerms.keywords, MAX_MATCHES_PER_KEYWORD * extractedTerms.keywords.length]);
2952
+ `, [...keywordParams, MAX_MATCHES_PER_KEYWORD * extractedTerms.keywords.length]);
2806
2953
  for (const row of rows) {
2807
2954
  if (row.content === null) {
2808
2955
  continue;
@@ -2943,9 +3090,15 @@ async function contextBundleImpl(context, params) {
2943
3090
  sourceHint: hintPath,
2944
3091
  origin: "artifact",
2945
3092
  }));
3093
+ const domainPathTargets = selectDomainPathHints(domainExpansion.fileHints).map((hint) => ({
3094
+ path: hint.path,
3095
+ sourceHint: hint.source,
3096
+ origin: "dictionary",
3097
+ }));
2946
3098
  const dictionaryPathTargets = await fetchDictionaryPathHints(db, context.tableAvailability, repoId, substringHints, HINT_DICTIONARY_LIMIT);
2947
3099
  const { list: resolvedPathHintTargets, meta: hintSeedMeta } = createHintSeedMeta([
2948
3100
  ...artifactPathTargets,
3101
+ ...domainPathTargets,
2949
3102
  ...dictionaryPathTargets,
2950
3103
  ]);
2951
3104
  if (resolvedPathHintTargets.length > 0) {
@@ -3040,6 +3193,9 @@ async function contextBundleImpl(context, params) {
3040
3193
  const materializeCandidates = async () => {
3041
3194
  const result = [];
3042
3195
  for (const candidate of candidates.values()) {
3196
+ if (!pathMatchesPrefix(candidate.path, pathPrefix)) {
3197
+ continue;
3198
+ }
3043
3199
  if (isSuppressedPath(candidate.path)) {
3044
3200
  continue;
3045
3201
  }
@@ -3073,7 +3229,7 @@ async function contextBundleImpl(context, params) {
3073
3229
  if (!hasAnyMetadataFilters) {
3074
3230
  return;
3075
3231
  }
3076
- const metadataRows = await fetchMetadataOnlyCandidates(db, context.tableAvailability, repoId, metadataFilters, limit * 2);
3232
+ const metadataRows = await fetchMetadataOnlyCandidates(db, context.tableAvailability, repoId, metadataFilters, limit * 2, pathPrefix);
3077
3233
  if (metadataRows.length === 0) {
3078
3234
  return;
3079
3235
  }