kiri-mcp-server 0.10.0 → 0.12.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 (96) hide show
  1. package/README.md +52 -10
  2. package/config/kiri.yml +25 -0
  3. package/config/scoring-profiles.yml +82 -35
  4. package/dist/config/kiri.yml +25 -0
  5. package/dist/config/scoring-profiles.yml +82 -35
  6. package/dist/package.json +9 -1
  7. package/dist/src/indexer/cli.d.ts.map +1 -1
  8. package/dist/src/indexer/cli.js +712 -98
  9. package/dist/src/indexer/cli.js.map +1 -1
  10. package/dist/src/indexer/git.d.ts.map +1 -1
  11. package/dist/src/indexer/git.js +41 -3
  12. package/dist/src/indexer/git.js.map +1 -1
  13. package/dist/src/indexer/migrations/repo-merger.d.ts +33 -0
  14. package/dist/src/indexer/migrations/repo-merger.d.ts.map +1 -0
  15. package/dist/src/indexer/migrations/repo-merger.js +67 -0
  16. package/dist/src/indexer/migrations/repo-merger.js.map +1 -0
  17. package/dist/src/indexer/schema.d.ts +66 -0
  18. package/dist/src/indexer/schema.d.ts.map +1 -1
  19. package/dist/src/indexer/schema.js +337 -0
  20. package/dist/src/indexer/schema.js.map +1 -1
  21. package/dist/src/server/boost-profiles.d.ts +6 -5
  22. package/dist/src/server/boost-profiles.d.ts.map +1 -1
  23. package/dist/src/server/boost-profiles.js +138 -0
  24. package/dist/src/server/boost-profiles.js.map +1 -1
  25. package/dist/src/server/config-loader.d.ts +9 -0
  26. package/dist/src/server/config-loader.d.ts.map +1 -0
  27. package/dist/src/server/config-loader.js +121 -0
  28. package/dist/src/server/config-loader.js.map +1 -0
  29. package/dist/src/server/config.d.ts +47 -0
  30. package/dist/src/server/config.d.ts.map +1 -0
  31. package/dist/src/server/config.js +157 -0
  32. package/dist/src/server/config.js.map +1 -0
  33. package/dist/src/server/context.d.ts +29 -0
  34. package/dist/src/server/context.d.ts.map +1 -1
  35. package/dist/src/server/context.js +26 -1
  36. package/dist/src/server/context.js.map +1 -1
  37. package/dist/src/server/handlers/snippets-get.d.ts +36 -0
  38. package/dist/src/server/handlers/snippets-get.d.ts.map +1 -0
  39. package/dist/src/server/handlers/snippets-get.js +120 -0
  40. package/dist/src/server/handlers/snippets-get.js.map +1 -0
  41. package/dist/src/server/handlers.d.ts +33 -20
  42. package/dist/src/server/handlers.d.ts.map +1 -1
  43. package/dist/src/server/handlers.js +1805 -370
  44. package/dist/src/server/handlers.js.map +1 -1
  45. package/dist/src/server/indexBootstrap.d.ts.map +1 -1
  46. package/dist/src/server/indexBootstrap.js +49 -2
  47. package/dist/src/server/indexBootstrap.js.map +1 -1
  48. package/dist/src/server/main.d.ts.map +1 -1
  49. package/dist/src/server/main.js +7 -0
  50. package/dist/src/server/main.js.map +1 -1
  51. package/dist/src/server/profile-selector.d.ts +33 -0
  52. package/dist/src/server/profile-selector.d.ts.map +1 -0
  53. package/dist/src/server/profile-selector.js +291 -0
  54. package/dist/src/server/profile-selector.js.map +1 -0
  55. package/dist/src/server/rpc.d.ts.map +1 -1
  56. package/dist/src/server/rpc.js +60 -10
  57. package/dist/src/server/rpc.js.map +1 -1
  58. package/dist/src/server/runtime.d.ts.map +1 -1
  59. package/dist/src/server/runtime.js +14 -4
  60. package/dist/src/server/runtime.js.map +1 -1
  61. package/dist/src/server/scoring.d.ts +7 -1
  62. package/dist/src/server/scoring.d.ts.map +1 -1
  63. package/dist/src/server/scoring.js +121 -21
  64. package/dist/src/server/scoring.js.map +1 -1
  65. package/dist/src/server/services/index.d.ts +24 -0
  66. package/dist/src/server/services/index.d.ts.map +1 -0
  67. package/dist/src/server/services/index.js +20 -0
  68. package/dist/src/server/services/index.js.map +1 -0
  69. package/dist/src/server/services/repo-repository.d.ts +61 -0
  70. package/dist/src/server/services/repo-repository.d.ts.map +1 -0
  71. package/dist/src/server/services/repo-repository.js +93 -0
  72. package/dist/src/server/services/repo-repository.js.map +1 -0
  73. package/dist/src/server/services/repo-resolver.d.ts +28 -0
  74. package/dist/src/server/services/repo-resolver.d.ts.map +1 -0
  75. package/dist/src/server/services/repo-resolver.js +62 -0
  76. package/dist/src/server/services/repo-resolver.js.map +1 -0
  77. package/dist/src/shared/duckdb.d.ts.map +1 -1
  78. package/dist/src/shared/duckdb.js +21 -1
  79. package/dist/src/shared/duckdb.js.map +1 -1
  80. package/dist/src/shared/fs/safePath.d.ts +7 -0
  81. package/dist/src/shared/fs/safePath.d.ts.map +1 -0
  82. package/dist/src/shared/fs/safePath.js +23 -0
  83. package/dist/src/shared/fs/safePath.js.map +1 -0
  84. package/dist/src/shared/tokenizer.d.ts +1 -1
  85. package/dist/src/shared/tokenizer.d.ts.map +1 -1
  86. package/dist/src/shared/tokenizer.js +97 -15
  87. package/dist/src/shared/tokenizer.js.map +1 -1
  88. package/dist/src/shared/utils/glob.d.ts +5 -0
  89. package/dist/src/shared/utils/glob.d.ts.map +1 -0
  90. package/dist/src/shared/utils/glob.js +22 -0
  91. package/dist/src/shared/utils/glob.js.map +1 -0
  92. package/dist/src/shared/utils/retry.d.ts +8 -0
  93. package/dist/src/shared/utils/retry.d.ts.map +1 -0
  94. package/dist/src/shared/utils/retry.js +20 -0
  95. package/dist/src/shared/utils/retry.js.map +1 -0
  96. package/package.json +9 -1
@@ -0,0 +1,121 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { parse } from "yaml";
4
+ import { z } from "zod";
5
+ const ENV_PREFIX = "KIRI_PATH_PENALTY_";
6
+ const penaltyCache = new Map();
7
+ const PathPenaltySchema = z.object({
8
+ prefix: z.string().trim().min(1, "prefix is required"),
9
+ multiplier: z.number().finite().nonnegative(),
10
+ });
11
+ const ConfigSchema = z.object({
12
+ path_penalties: z.array(PathPenaltySchema).optional(),
13
+ });
14
+ function normalizePrefix(raw) {
15
+ const trimmed = raw.trim();
16
+ if (trimmed.length === 0) {
17
+ throw new Error("Path penalty prefix cannot be empty");
18
+ }
19
+ const hadTrailingSlash = /[\\/]+$/.test(trimmed);
20
+ const normalized = path.posix
21
+ .normalize(trimmed.replace(/\\/g, "/"))
22
+ .replace(/^([A-Za-z]:)?\//, "") // drop drive letter or leading slash
23
+ .replace(/^\.\//, "");
24
+ if (normalized === "" || normalized === ".") {
25
+ throw new Error(`Path penalty prefix "${raw}" resolves to an empty path`);
26
+ }
27
+ if (normalized.startsWith("../") || normalized.includes("/../")) {
28
+ throw new Error(`Path penalty prefix "${raw}" must not contain ".." segments`);
29
+ }
30
+ // Re-append trailing slash only when the original explicitly had it
31
+ return hadTrailingSlash && !normalized.endsWith("/") ? `${normalized}/` : normalized;
32
+ }
33
+ function sortByPrefixLength(entries) {
34
+ return [...entries].sort((a, b) => b.prefix.length - a.prefix.length);
35
+ }
36
+ function decodeEnvKey(key) {
37
+ const encoded = key.slice(ENV_PREFIX.length);
38
+ if (!encoded) {
39
+ throw new Error("KIRI_PATH_PENALTY_* must include a prefix segment");
40
+ }
41
+ // Spec: "/" ↔ "__" encoding
42
+ const decoded = encoded.replace(/__/g, "/");
43
+ return normalizePrefix(decoded);
44
+ }
45
+ function parseEnvPathPenalties(env = process.env) {
46
+ const entries = [];
47
+ for (const [key, rawValue] of Object.entries(env)) {
48
+ if (!key.startsWith(ENV_PREFIX) || rawValue === undefined) {
49
+ continue;
50
+ }
51
+ const prefix = decodeEnvKey(key);
52
+ const multiplier = Number.parseFloat(rawValue);
53
+ if (!Number.isFinite(multiplier)) {
54
+ throw new Error(`Invalid multiplier for ${key}: ${rawValue}`);
55
+ }
56
+ if (multiplier < 0) {
57
+ throw new Error(`Multiplier for ${key} must be >= 0`);
58
+ }
59
+ entries.push({ prefix, multiplier });
60
+ }
61
+ return entries;
62
+ }
63
+ function readYamlConfig(cwd) {
64
+ const candidates = [
65
+ path.join(cwd, ".kiri/config.yaml"),
66
+ path.join(cwd, ".kiri/config.yml"),
67
+ path.join(cwd, "config/kiri.yaml"),
68
+ path.join(cwd, "config/kiri.yml"),
69
+ ];
70
+ for (const candidate of candidates) {
71
+ if (!fs.existsSync(candidate)) {
72
+ continue;
73
+ }
74
+ const raw = fs.readFileSync(candidate, "utf8");
75
+ const parsed = parse(raw);
76
+ const result = ConfigSchema.safeParse(parsed);
77
+ if (!result.success) {
78
+ const details = result.error.issues.map((issue) => issue.message).join(", ");
79
+ throw new Error(`Invalid path penalty config in ${candidate}: ${details}`);
80
+ }
81
+ const entries = result.data.path_penalties?.map((entry) => ({
82
+ prefix: normalizePrefix(entry.prefix),
83
+ multiplier: entry.multiplier,
84
+ })) ?? [];
85
+ return entries;
86
+ }
87
+ return [];
88
+ }
89
+ function mergePathPenalties(priorityOrdered) {
90
+ const merged = new Map();
91
+ for (const entries of priorityOrdered) {
92
+ for (const entry of entries) {
93
+ merged.set(entry.prefix, entry);
94
+ }
95
+ }
96
+ return sortByPrefixLength(Array.from(merged.values()));
97
+ }
98
+ /**
99
+ * Global resolver for path penalties (Issue #106)
100
+ * Precedence: profile < env < YAML (last definition wins)
101
+ */
102
+ export function loadPathPenalties(basePathMultipliers = [], cwd = process.cwd()) {
103
+ const envEntries = Object.entries(process.env)
104
+ .filter(([key]) => key.startsWith(ENV_PREFIX))
105
+ .sort(([a], [b]) => a.localeCompare(b));
106
+ const cacheKey = JSON.stringify({ cwd, base: basePathMultipliers, env: envEntries });
107
+ const cached = penaltyCache.get(cacheKey);
108
+ if (cached) {
109
+ return cached;
110
+ }
111
+ const envPenalties = parseEnvPathPenalties();
112
+ const yamlPenalties = readYamlConfig(cwd);
113
+ const merged = mergePathPenalties([basePathMultipliers, envPenalties, yamlPenalties]);
114
+ penaltyCache.set(cacheKey, merged);
115
+ return merged;
116
+ }
117
+ /** Test helper: merge logic without disk/env side effects */
118
+ export function mergePathPenaltyEntries(base, env, yaml) {
119
+ return mergePathPenalties([base, env, yaml]);
120
+ }
121
+ //# sourceMappingURL=config-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-loader.js","sourceRoot":"","sources":["../../../src/server/config-loader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ import type { PathMultiplier } from "./boost-profiles.js";
2
+ export interface ServerConfig {
3
+ features: {
4
+ suppressNonCode: boolean;
5
+ suppressFinalResults: boolean;
6
+ clampSnippets: boolean;
7
+ snippetWindow: number;
8
+ };
9
+ hints: {
10
+ enabled: boolean;
11
+ priority: {
12
+ textMultiplier: number;
13
+ pathMultiplier: number;
14
+ baseBonus: number;
15
+ };
16
+ directory: {
17
+ enabled: boolean;
18
+ limit: number;
19
+ maxFiles: number;
20
+ };
21
+ dependency: {
22
+ enabled: boolean;
23
+ outLimit: number;
24
+ inLimit: number;
25
+ };
26
+ semantic: {
27
+ enabled: boolean;
28
+ limit: number;
29
+ dirCandidateLimit: number;
30
+ threshold: number;
31
+ };
32
+ substring: {
33
+ enabled: boolean;
34
+ limit: number;
35
+ boost: number;
36
+ };
37
+ perHintLimit: number;
38
+ dbQueryLimit: number;
39
+ };
40
+ penalties: {
41
+ pathMissDelta: number;
42
+ largeFileDelta: number;
43
+ };
44
+ pathPenalties: PathMultiplier[];
45
+ }
46
+ export declare function loadServerConfig(): ServerConfig;
47
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/server/config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAG1D,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE;QACR,eAAe,EAAE,OAAO,CAAC;QACzB,oBAAoB,EAAE,OAAO,CAAC;QAC9B,aAAa,EAAE,OAAO,CAAC;QACvB,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,KAAK,EAAE;QACL,OAAO,EAAE,OAAO,CAAC;QACjB,QAAQ,EAAE;YACR,cAAc,EAAE,MAAM,CAAC;YACvB,cAAc,EAAE,MAAM,CAAC;YACvB,SAAS,EAAE,MAAM,CAAC;SACnB,CAAC;QACF,SAAS,EAAE;YACT,OAAO,EAAE,OAAO,CAAC;YACjB,KAAK,EAAE,MAAM,CAAC;YACd,QAAQ,EAAE,MAAM,CAAC;SAClB,CAAC;QACF,UAAU,EAAE;YACV,OAAO,EAAE,OAAO,CAAC;YACjB,QAAQ,EAAE,MAAM,CAAC;YACjB,OAAO,EAAE,MAAM,CAAC;SACjB,CAAC;QACF,QAAQ,EAAE;YACR,OAAO,EAAE,OAAO,CAAC;YACjB,KAAK,EAAE,MAAM,CAAC;YACd,iBAAiB,EAAE,MAAM,CAAC;YAC1B,SAAS,EAAE,MAAM,CAAC;SACnB,CAAC;QACF,SAAS,EAAE;YACT,OAAO,EAAE,OAAO,CAAC;YACjB,KAAK,EAAE,MAAM,CAAC;YACd,KAAK,EAAE,MAAM,CAAC;SACf,CAAC;QACF,YAAY,EAAE,MAAM,CAAC;QACrB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,SAAS,EAAE;QACT,aAAa,EAAE,MAAM,CAAC;QACtB,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,aAAa,EAAE,cAAc,EAAE,CAAC;CACjC;AAuFD,wBAAgB,gBAAgB,IAAI,YAAY,CAoF/C"}
@@ -0,0 +1,157 @@
1
+ import process from "node:process";
2
+ import { loadPathPenalties } from "./config-loader.js";
3
+ let cachedConfig = null;
4
+ function envFlagEnabled(value, defaultEnabled) {
5
+ if (value === undefined) {
6
+ return defaultEnabled;
7
+ }
8
+ const normalized = value.trim().toLowerCase();
9
+ return normalized !== "0" && normalized !== "false" && normalized !== "off";
10
+ }
11
+ function parseEnvNumber(value, fallback) {
12
+ if (value === undefined) {
13
+ return fallback;
14
+ }
15
+ const parsed = Number.parseInt(value, 10);
16
+ if (Number.isFinite(parsed) && parsed >= 0) {
17
+ return parsed;
18
+ }
19
+ return fallback;
20
+ }
21
+ function parseEnvFloat(value, fallback) {
22
+ if (value === undefined) {
23
+ return fallback;
24
+ }
25
+ const parsed = Number.parseFloat(value);
26
+ if (Number.isFinite(parsed)) {
27
+ return parsed;
28
+ }
29
+ return fallback;
30
+ }
31
+ function validateServerConfig(config) {
32
+ const { features, hints, penalties } = config;
33
+ if (features.snippetWindow < 8) {
34
+ throw new Error("snippetWindow must be >= 8 to keep bundle context useful");
35
+ }
36
+ if (hints.priority.textMultiplier <= 0 || hints.priority.pathMultiplier <= 0) {
37
+ throw new Error("Hint priority multipliers must be positive numbers");
38
+ }
39
+ if (hints.priority.baseBonus <= 0) {
40
+ throw new Error("Hint priority base bonus must be positive");
41
+ }
42
+ if (hints.directory.limit < 0 || hints.directory.maxFiles < 1) {
43
+ throw new Error("Directory hint limits must be non-negative and maxFiles >= 1");
44
+ }
45
+ if (hints.dependency.outLimit < 0 || hints.dependency.inLimit < 0) {
46
+ throw new Error("Dependency hint limits must be non-negative");
47
+ }
48
+ if (hints.semantic.limit < 0 || hints.semantic.dirCandidateLimit < 1) {
49
+ throw new Error("Semantic hint limits must be non-negative and candidate limit >= 1");
50
+ }
51
+ if (hints.semantic.threshold <= 0 || hints.semantic.threshold > 1) {
52
+ throw new Error("Semantic hint threshold must be within (0, 1]");
53
+ }
54
+ if (hints.perHintLimit < 0) {
55
+ throw new Error("Per-hint limit must be >= 0");
56
+ }
57
+ if (hints.enabled && hints.perHintLimit < 1) {
58
+ throw new Error("Per-hint limit must be >= 1 when hints are enabled");
59
+ }
60
+ if (hints.dbQueryLimit < 0) {
61
+ throw new Error("Hint DB query budget must be >= 0");
62
+ }
63
+ if (hints.enabled && hints.dbQueryLimit < 1) {
64
+ throw new Error("Hint DB query budget must be >= 1 when hints are enabled");
65
+ }
66
+ if (hints.substring.limit < 0) {
67
+ throw new Error("Substring hint limit must be non-negative");
68
+ }
69
+ if (penalties.pathMissDelta > 0 || penalties.largeFileDelta > 0) {
70
+ throw new Error("Penalty deltas should be <= 0 (they reduce scores)");
71
+ }
72
+ for (const entry of config.pathPenalties) {
73
+ if (!entry.prefix || typeof entry.prefix !== "string") {
74
+ throw new Error("Path penalty prefix must be a non-empty string");
75
+ }
76
+ if (!Number.isFinite(entry.multiplier) || entry.multiplier < 0) {
77
+ throw new Error(`Path penalty multiplier for ${entry.prefix} must be a non-negative finite number`);
78
+ }
79
+ }
80
+ }
81
+ export function loadServerConfig() {
82
+ if (cachedConfig) {
83
+ return cachedConfig;
84
+ }
85
+ const suppressNonCode = envFlagEnabled(process.env.KIRI_SUPPRESS_NON_CODE, true);
86
+ const suppressFinalResults = envFlagEnabled(process.env.KIRI_SUPPRESS_FINAL_RESULTS, true);
87
+ const clampSnippets = envFlagEnabled(process.env.KIRI_CLAMP_SNIPPETS, true);
88
+ const snippetWindow = Math.max(8, parseEnvNumber(process.env.KIRI_SNIPPET_WINDOW, 16));
89
+ const directoryEnabled = envFlagEnabled(process.env.KIRI_HINT_ENABLE_DIR, false);
90
+ const dependencyEnabled = envFlagEnabled(process.env.KIRI_HINT_ENABLE_DEP, false);
91
+ const semanticEnabled = envFlagEnabled(process.env.KIRI_HINT_ENABLE_SEM, false);
92
+ const substringEnabled = envFlagEnabled(process.env.KIRI_HINT_ENABLE_SUBSTRING, true);
93
+ const hintsEnabled = directoryEnabled || dependencyEnabled || semanticEnabled;
94
+ const pathPenalties = loadPathPenalties();
95
+ const config = {
96
+ features: {
97
+ suppressNonCode,
98
+ suppressFinalResults,
99
+ clampSnippets,
100
+ snippetWindow,
101
+ },
102
+ hints: {
103
+ enabled: hintsEnabled,
104
+ priority: {
105
+ textMultiplier: parseEnvFloat(process.env.KIRI_HINT_TEXT_MULTIPLIER, 6),
106
+ pathMultiplier: parseEnvFloat(process.env.KIRI_HINT_PATH_MULTIPLIER, 2),
107
+ baseBonus: parseEnvFloat(process.env.KIRI_HINT_BASE_BONUS, 5),
108
+ },
109
+ directory: {
110
+ enabled: directoryEnabled,
111
+ limit: directoryEnabled
112
+ ? Math.max(0, parseEnvNumber(process.env.KIRI_HINT_NEAR_LIMIT_DIR, 2))
113
+ : 0,
114
+ maxFiles: Math.max(1, parseEnvNumber(process.env.KIRI_HINT_NEAR_MAX_FILES, 10)),
115
+ },
116
+ dependency: {
117
+ enabled: dependencyEnabled,
118
+ outLimit: dependencyEnabled
119
+ ? Math.max(0, parseEnvNumber(process.env.KIRI_HINT_DEP_OUT_LIMIT, 1))
120
+ : 0,
121
+ inLimit: dependencyEnabled
122
+ ? Math.max(0, parseEnvNumber(process.env.KIRI_HINT_DEP_IN_LIMIT, 1))
123
+ : 0,
124
+ },
125
+ semantic: {
126
+ enabled: semanticEnabled,
127
+ limit: semanticEnabled
128
+ ? Math.max(0, parseEnvNumber(process.env.KIRI_HINT_SEM_LIMIT, 2))
129
+ : 0,
130
+ dirCandidateLimit: Math.max(1, parseEnvNumber(process.env.KIRI_HINT_SEM_DIR_CANDIDATES, 20)),
131
+ threshold: parseEnvFloat(process.env.KIRI_HINT_SEM_THRESHOLD, 0.65),
132
+ },
133
+ substring: {
134
+ enabled: substringEnabled,
135
+ limit: substringEnabled
136
+ ? Math.max(0, parseEnvNumber(process.env.KIRI_HINT_SUBSTRING_LIMIT, 3))
137
+ : 0,
138
+ boost: parseEnvFloat(process.env.KIRI_HINT_SUBSTRING_BOOST, 3),
139
+ },
140
+ perHintLimit: hintsEnabled
141
+ ? Math.max(1, parseEnvNumber(process.env.KIRI_HINT_PER_HINT_LIMIT, 6))
142
+ : 0,
143
+ dbQueryLimit: hintsEnabled
144
+ ? Math.max(1, parseEnvNumber(process.env.KIRI_HINT_DB_QUERY_LIMIT, 12))
145
+ : 0,
146
+ },
147
+ penalties: {
148
+ pathMissDelta: parseEnvFloat(process.env.KIRI_PATH_MISS_DELTA, -0.5),
149
+ largeFileDelta: parseEnvFloat(process.env.KIRI_LARGE_FILE_DELTA, -0.8),
150
+ },
151
+ pathPenalties,
152
+ };
153
+ validateServerConfig(config);
154
+ cachedConfig = config;
155
+ return config;
156
+ }
157
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/server/config.ts"],"names":[],"mappings":"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"}
@@ -1,18 +1,47 @@
1
1
  import { DuckDBClient } from "../shared/duckdb.js";
2
2
  import { WarningManager } from "./rpc.js";
3
+ import { ServerServices } from "./services/index.js";
3
4
  export interface FtsStatusCache {
4
5
  ready: boolean;
5
6
  schemaExists: boolean;
6
7
  anyDirty: boolean;
7
8
  lastChecked: number;
8
9
  }
10
+ export interface TableAvailability {
11
+ hasMetadataTables: boolean;
12
+ hasLinkTable: boolean;
13
+ hasHintLog: boolean;
14
+ hasHintDictionary: boolean;
15
+ }
9
16
  export interface ServerContext {
10
17
  db: DuckDBClient;
11
18
  repoId: number;
19
+ services: ServerServices;
12
20
  features?: {
13
21
  fts?: boolean;
14
22
  };
15
23
  ftsStatusCache?: FtsStatusCache;
24
+ tableAvailability: TableAvailability;
16
25
  warningManager: WarningManager;
17
26
  }
27
+ /**
28
+ * createServerContext
29
+ *
30
+ * ServerContext を生成するファクトリ関数。
31
+ * テストや複数のエントリポイントで共通の初期化パスを提供する。
32
+ *
33
+ * @param options - コンテキスト初期化オプション
34
+ * @returns 初期化された ServerContext
35
+ */
36
+ export declare function createServerContext(options: {
37
+ db: DuckDBClient;
38
+ repoId: number;
39
+ services: ServerServices;
40
+ features?: {
41
+ fts?: boolean;
42
+ };
43
+ ftsStatusCache?: FtsStatusCache;
44
+ tableAvailability: TableAvailability;
45
+ warningManager: WarningManager;
46
+ }): ServerContext;
18
47
  //# sourceMappingURL=context.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/server/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE1C,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,OAAO,CAAC;IACf,YAAY,EAAE,OAAO,CAAC;IACtB,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,YAAY,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE;QACT,GAAG,CAAC,EAAE,OAAO,CAAC;KACf,CAAC;IACF,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,cAAc,EAAE,cAAc,CAAC;CAChC"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/server/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,OAAO,CAAC;IACf,YAAY,EAAE,OAAO,CAAC;IACtB,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,iBAAiB,EAAE,OAAO,CAAC;IAC3B,YAAY,EAAE,OAAO,CAAC;IACtB,UAAU,EAAE,OAAO,CAAC;IACpB,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,YAAY,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,cAAc,CAAC;IACzB,QAAQ,CAAC,EAAE;QACT,GAAG,CAAC,EAAE,OAAO,CAAC;KACf,CAAC;IACF,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,cAAc,EAAE,cAAc,CAAC;CAChC;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE;IAC3C,EAAE,EAAE,YAAY,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,cAAc,CAAC;IACzB,QAAQ,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAC7B,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,cAAc,EAAE,cAAc,CAAC;CAChC,GAAG,aAAa,CAmBhB"}
@@ -1,2 +1,27 @@
1
- export {};
1
+ /**
2
+ * createServerContext
3
+ *
4
+ * ServerContext を生成するファクトリ関数。
5
+ * テストや複数のエントリポイントで共通の初期化パスを提供する。
6
+ *
7
+ * @param options - コンテキスト初期化オプション
8
+ * @returns 初期化された ServerContext
9
+ */
10
+ export function createServerContext(options) {
11
+ const context = {
12
+ db: options.db,
13
+ repoId: options.repoId,
14
+ services: options.services,
15
+ tableAvailability: options.tableAvailability,
16
+ warningManager: options.warningManager,
17
+ };
18
+ // exactOptionalPropertyTypes: true を満たすため、undefined の場合は代入しない
19
+ if (options.features !== undefined) {
20
+ context.features = options.features;
21
+ }
22
+ if (options.ftsStatusCache !== undefined) {
23
+ context.ftsStatusCache = options.ftsStatusCache;
24
+ }
25
+ return context;
26
+ }
2
27
  //# sourceMappingURL=context.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/server/context.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/server/context.ts"],"names":[],"mappings":"AA+BA;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAQnC;IACC,MAAM,OAAO,GAAkB;QAC7B,EAAE,EAAE,OAAO,CAAC,EAAE;QACd,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,iBAAiB,EAAE,OAAO,CAAC,iBAAiB;QAC5C,cAAc,EAAE,OAAO,CAAC,cAAc;KACvC,CAAC;IAEF,8DAA8D;IAC9D,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QACnC,OAAO,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IACtC,CAAC;IAED,IAAI,OAAO,CAAC,cAAc,KAAK,SAAS,EAAE,CAAC;QACzC,OAAO,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;IAClD,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,36 @@
1
+ import { ServerContext } from "../context.js";
2
+ /**
3
+ * snippets_get tool のパラメータ
4
+ */
5
+ export interface SnippetsGetParams {
6
+ path: string;
7
+ start_line?: number;
8
+ end_line?: number;
9
+ compact?: boolean;
10
+ includeLineNumbers?: boolean;
11
+ }
12
+ /**
13
+ * snippets_get tool のレスポンス
14
+ */
15
+ export interface SnippetResult {
16
+ path: string;
17
+ startLine: number;
18
+ endLine: number;
19
+ content?: string;
20
+ totalLines: number;
21
+ symbolName: string | null;
22
+ symbolKind: string | null;
23
+ }
24
+ /**
25
+ * snippets_get MCP Tool Handler
26
+ *
27
+ * 指定されたファイルパスから、シンボル境界を考慮したコードスニペットを取得する。
28
+ * start_line/end_line が指定されていない場合、最適なシンボル(関数、クラスなど)を自動選択する。
29
+ *
30
+ * @param context - サーバーコンテキスト(DB、repoId等)
31
+ * @param params - snippets_get パラメータ
32
+ * @returns スニペット結果
33
+ * @throws Error ファイルが見つからない、バイナリファイル、コンテンツが利用不可の場合
34
+ */
35
+ export declare function snippetsGet(context: ServerContext, params: SnippetsGetParams): Promise<SnippetResult>;
36
+ //# sourceMappingURL=snippets-get.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snippets-get.d.ts","sourceRoot":"","sources":["../../../../src/server/handlers/snippets-get.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAmDD;;;;;;;;;;GAUG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,aAAa,CAAC,CAmHxB"}
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Constants
3
+ */
4
+ const DEFAULT_SNIPPET_WINDOW = 150;
5
+ /**
6
+ * 行番号をプレフィックスとして追加する(動的幅調整)
7
+ *
8
+ * @param snippet - スニペットの内容
9
+ * @param startLine - 開始行番号(1-indexed)
10
+ * @returns 行番号付きスニペット
11
+ */
12
+ function prependLineNumbers(snippet, startLine) {
13
+ const lines = snippet.split(/\r?\n/);
14
+ if (lines.length === 0) {
15
+ return snippet;
16
+ }
17
+ // Calculate required width from the last line number (dynamic sizing)
18
+ const endLine = startLine + lines.length - 1;
19
+ const width = String(endLine).length;
20
+ return lines
21
+ .map((line, index) => `${String(startLine + index).padStart(width, " ")}→${line}`)
22
+ .join("\n");
23
+ }
24
+ /**
25
+ * snippets_get MCP Tool Handler
26
+ *
27
+ * 指定されたファイルパスから、シンボル境界を考慮したコードスニペットを取得する。
28
+ * start_line/end_line が指定されていない場合、最適なシンボル(関数、クラスなど)を自動選択する。
29
+ *
30
+ * @param context - サーバーコンテキスト(DB、repoId等)
31
+ * @param params - snippets_get パラメータ
32
+ * @returns スニペット結果
33
+ * @throws Error ファイルが見つからない、バイナリファイル、コンテンツが利用不可の場合
34
+ */
35
+ export async function snippetsGet(context, params) {
36
+ const { db, repoId } = context;
37
+ if (!params.path) {
38
+ throw new Error("snippets_get requires a file path. Specify a tracked text file path to continue.");
39
+ }
40
+ const rows = await db.all(`
41
+ SELECT f.path, f.lang, f.ext, f.is_binary, b.content
42
+ FROM file f
43
+ JOIN blob b ON b.hash = f.blob_hash
44
+ WHERE f.repo_id = ? AND f.path = ?
45
+ LIMIT 1
46
+ `, [repoId, params.path]);
47
+ if (rows.length === 0) {
48
+ throw new Error(`Requested snippet file ${params.path} was not indexed. Re-run the indexer or choose another path.`);
49
+ }
50
+ const row = rows[0];
51
+ if (!row) {
52
+ throw new Error(`Requested snippet file ${params.path} was not indexed. Re-run the indexer or choose another path.`);
53
+ }
54
+ if (row.is_binary) {
55
+ throw new Error("Binary snippets are not supported. Choose a text file to preview its content.");
56
+ }
57
+ if (row.content === null) {
58
+ throw new Error(`Snippet content was NULL for ${params.path}. Re-run the indexer with --full to repopulate blob content.`);
59
+ }
60
+ const lines = row.content.split(/\r?\n/);
61
+ const totalLines = lines.length;
62
+ const snippetRows = await db.all(`
63
+ SELECT s.snippet_id, s.start_line, s.end_line, s.symbol_id, sym.name AS symbol_name, sym.kind AS symbol_kind
64
+ FROM snippet s
65
+ LEFT JOIN symbol sym
66
+ ON sym.repo_id = s.repo_id
67
+ AND sym.path = s.path
68
+ AND sym.symbol_id = s.symbol_id
69
+ WHERE s.repo_id = ? AND s.path = ?
70
+ ORDER BY s.start_line
71
+ `, [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;
75
+ let snippetSelection = null;
76
+ if (useSymbolSnippets) {
77
+ snippetSelection =
78
+ snippetRows.find((snippet) => requestedStart >= snippet.start_line && requestedStart <= snippet.end_line) ?? null;
79
+ if (!snippetSelection) {
80
+ const firstSnippet = snippetRows[0];
81
+ if (firstSnippet && requestedStart < firstSnippet.start_line) {
82
+ snippetSelection = firstSnippet;
83
+ }
84
+ else {
85
+ snippetSelection = snippetRows[snippetRows.length - 1] ?? null;
86
+ }
87
+ }
88
+ }
89
+ let startLine;
90
+ let endLine;
91
+ let symbolName = null;
92
+ let symbolKind = null;
93
+ if (snippetSelection) {
94
+ startLine = snippetSelection.start_line;
95
+ endLine = snippetSelection.end_line;
96
+ symbolName = snippetSelection.symbol_name;
97
+ symbolKind = snippetSelection.symbol_kind;
98
+ }
99
+ else {
100
+ startLine = Math.max(1, Math.min(totalLines, requestedStart));
101
+ endLine = Math.max(startLine, Math.min(totalLines, requestedEnd));
102
+ }
103
+ const isCompact = params.compact === true;
104
+ const addLineNumbers = params.includeLineNumbers === true && !isCompact;
105
+ let content;
106
+ if (!isCompact) {
107
+ const snippetContent = lines.slice(startLine - 1, endLine).join("\n");
108
+ content = addLineNumbers ? prependLineNumbers(snippetContent, startLine) : snippetContent;
109
+ }
110
+ return {
111
+ path: row.path,
112
+ startLine,
113
+ endLine,
114
+ ...(content !== undefined && { content }),
115
+ totalLines,
116
+ symbolName,
117
+ symbolKind,
118
+ };
119
+ }
120
+ //# sourceMappingURL=snippets-get.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snippets-get.js","sourceRoot":"","sources":["../../../../src/server/handlers/snippets-get.ts"],"names":[],"mappings":"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"}