icopilot 2.2.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 (203) hide show
  1. package/CHANGELOG.md +250 -0
  2. package/LICENSE +21 -0
  3. package/README.md +214 -0
  4. package/bin/icopilot.js +6 -0
  5. package/dist/acp/router.js +123 -0
  6. package/dist/acp/schema.js +53 -0
  7. package/dist/agents/aggregator.js +187 -0
  8. package/dist/agents/custom-agents.js +97 -0
  9. package/dist/agents/goal-driven.js +411 -0
  10. package/dist/agents/multi-repo.js +350 -0
  11. package/dist/agents/parallel-runner.js +181 -0
  12. package/dist/agents/router.js +144 -0
  13. package/dist/agents/self-heal.js +481 -0
  14. package/dist/agents/tdd-agent.js +278 -0
  15. package/dist/api/github-models.js +158 -0
  16. package/dist/bridge/ide-bridge.js +479 -0
  17. package/dist/cloud/routine-executor.js +34 -0
  18. package/dist/cloud/routine-scheduler.js +67 -0
  19. package/dist/cloud/routine-storage.js +297 -0
  20. package/dist/commands/acp-cmd.js +143 -0
  21. package/dist/commands/actions-cmd.js +624 -0
  22. package/dist/commands/agent-cmd.js +144 -0
  23. package/dist/commands/alias-cmd.js +132 -0
  24. package/dist/commands/bookmark-cmd.js +77 -0
  25. package/dist/commands/changelog-cmd.js +99 -0
  26. package/dist/commands/changes-cmd.js +120 -0
  27. package/dist/commands/clipboard-cmd.js +217 -0
  28. package/dist/commands/cloud-routine-cmd.js +265 -0
  29. package/dist/commands/codegen-cmd.js +544 -0
  30. package/dist/commands/compare-cmd.js +116 -0
  31. package/dist/commands/context-cmd.js +247 -0
  32. package/dist/commands/context-viz-cmd.js +43 -0
  33. package/dist/commands/conventions-cmd.js +116 -0
  34. package/dist/commands/cost-cmd.js +51 -0
  35. package/dist/commands/deps-cmd.js +294 -0
  36. package/dist/commands/diagram-cmd.js +658 -0
  37. package/dist/commands/diff-review-cmd.js +92 -0
  38. package/dist/commands/doc-cmd.js +412 -0
  39. package/dist/commands/doctor-cmd.js +152 -0
  40. package/dist/commands/editor-cmd.js +49 -0
  41. package/dist/commands/env-cmd.js +86 -0
  42. package/dist/commands/explain-cmd.js +78 -0
  43. package/dist/commands/explain-shell-cmd.js +22 -0
  44. package/dist/commands/explore-cmd.js +231 -0
  45. package/dist/commands/feedback-cmd.js +98 -0
  46. package/dist/commands/fix-cmd.js +17 -0
  47. package/dist/commands/generate-cmd.js +38 -0
  48. package/dist/commands/git-extra.js +197 -0
  49. package/dist/commands/git-log-cmd.js +98 -0
  50. package/dist/commands/git-undo-cmd.js +137 -0
  51. package/dist/commands/git.js +155 -0
  52. package/dist/commands/history-cmd.js +122 -0
  53. package/dist/commands/index-cmd.js +65 -0
  54. package/dist/commands/init-cmd.js +73 -0
  55. package/dist/commands/lint-cmd.js +133 -0
  56. package/dist/commands/memory-cmd.js +98 -0
  57. package/dist/commands/metrics-cmd.js +97 -0
  58. package/dist/commands/mode-prefix.js +30 -0
  59. package/dist/commands/multi-cmd.js +44 -0
  60. package/dist/commands/notify-cmd.js +204 -0
  61. package/dist/commands/profile-cmd.js +101 -0
  62. package/dist/commands/prompts.js +17 -0
  63. package/dist/commands/rag-cmd.js +60 -0
  64. package/dist/commands/readme-cmd.js +564 -0
  65. package/dist/commands/reasoning-cmd.js +34 -0
  66. package/dist/commands/refactor-cmd.js +96 -0
  67. package/dist/commands/release-cmd.js +450 -0
  68. package/dist/commands/repo-cmd.js +195 -0
  69. package/dist/commands/route-cmd.js +21 -0
  70. package/dist/commands/schedule-cmd.js +109 -0
  71. package/dist/commands/search-cmd.js +47 -0
  72. package/dist/commands/security-cmd.js +156 -0
  73. package/dist/commands/settings-cmd.js +238 -0
  74. package/dist/commands/skill-cmd.js +338 -0
  75. package/dist/commands/slash.js +2721 -0
  76. package/dist/commands/snippets-cmd.js +83 -0
  77. package/dist/commands/space-cmd.js +92 -0
  78. package/dist/commands/stash-cmd.js +156 -0
  79. package/dist/commands/stats-cmd.js +36 -0
  80. package/dist/commands/style-cmd.js +85 -0
  81. package/dist/commands/suggest-cmd.js +40 -0
  82. package/dist/commands/summary-cmd.js +138 -0
  83. package/dist/commands/task-cmd.js +58 -0
  84. package/dist/commands/team-memory-cmd.js +97 -0
  85. package/dist/commands/template-cmd.js +475 -0
  86. package/dist/commands/test-cmd.js +146 -0
  87. package/dist/commands/todo-cmd.js +172 -0
  88. package/dist/commands/tokens-cmd.js +277 -0
  89. package/dist/commands/trigger-cmd.js +147 -0
  90. package/dist/commands/undo-cmd.js +18 -0
  91. package/dist/commands/voice-cmd.js +89 -0
  92. package/dist/commands/watch-cmd.js +110 -0
  93. package/dist/commands/web-cmd.js +183 -0
  94. package/dist/commands/worktree-cmd.js +119 -0
  95. package/dist/config-profile.js +66 -0
  96. package/dist/config.js +288 -0
  97. package/dist/context/compactor.js +53 -0
  98. package/dist/context/dep-context.js +329 -0
  99. package/dist/context/file-refs.js +54 -0
  100. package/dist/context/git-context.js +229 -0
  101. package/dist/context/image-input.js +66 -0
  102. package/dist/context/memory.js +55 -0
  103. package/dist/context/persistent-memory.js +104 -0
  104. package/dist/context/pinned.js +96 -0
  105. package/dist/context/priority.js +150 -0
  106. package/dist/context/read-only.js +48 -0
  107. package/dist/context/smart-files.js +286 -0
  108. package/dist/context/team-memory.js +156 -0
  109. package/dist/extensions/loader.js +149 -0
  110. package/dist/extensions/marketplace.js +49 -0
  111. package/dist/extensions/slack-provider.js +181 -0
  112. package/dist/extensions/team.js +56 -0
  113. package/dist/extensions/teams-provider.js +222 -0
  114. package/dist/extensions/voice.js +18 -0
  115. package/dist/hooks/lifecycle.js +215 -0
  116. package/dist/hooks/precommit.js +463 -0
  117. package/dist/index/embeddings.js +23 -0
  118. package/dist/index/indexer.js +86 -0
  119. package/dist/index/retrieve.js +20 -0
  120. package/dist/index/store.js +95 -0
  121. package/dist/index.js +286 -0
  122. package/dist/intelligence/dead-code.js +457 -0
  123. package/dist/intelligence/error-watch.js +263 -0
  124. package/dist/intelligence/navigation.js +141 -0
  125. package/dist/intelligence/stack-trace.js +210 -0
  126. package/dist/intelligence/symbol-index.js +410 -0
  127. package/dist/knowledge/auto-memory.js +412 -0
  128. package/dist/knowledge/conventions.js +475 -0
  129. package/dist/knowledge/corrections.js +213 -0
  130. package/dist/knowledge/rag.js +450 -0
  131. package/dist/knowledge/style-learner.js +324 -0
  132. package/dist/logger.js +35 -0
  133. package/dist/mcp/client.js +144 -0
  134. package/dist/mcp/config.js +24 -0
  135. package/dist/mcp/index.js +89 -0
  136. package/dist/modes/auto-compact.js +20 -0
  137. package/dist/modes/autopilot.js +157 -0
  138. package/dist/modes/background.js +82 -0
  139. package/dist/modes/interactive.js +187 -0
  140. package/dist/modes/oneshot.js +36 -0
  141. package/dist/modes/tui.js +265 -0
  142. package/dist/modes/turn.js +342 -0
  143. package/dist/notifications/manager.js +107 -0
  144. package/dist/plugins/marketplace.js +244 -0
  145. package/dist/providers/custom-provider.js +298 -0
  146. package/dist/providers/local-model.js +121 -0
  147. package/dist/routing/profiles.js +44 -0
  148. package/dist/routing/router.js +18 -0
  149. package/dist/sandbox/container.js +151 -0
  150. package/dist/security/audit.js +237 -0
  151. package/dist/security/content-filter.js +449 -0
  152. package/dist/security/proxy.js +301 -0
  153. package/dist/security/retention.js +281 -0
  154. package/dist/security/roles.js +252 -0
  155. package/dist/server/api-server.js +679 -0
  156. package/dist/session/bookmarks.js +72 -0
  157. package/dist/session/cloud-session.js +291 -0
  158. package/dist/session/handoff.js +405 -0
  159. package/dist/session/manager.js +35 -0
  160. package/dist/session/session.js +296 -0
  161. package/dist/session/share.js +313 -0
  162. package/dist/session/undo-journal.js +91 -0
  163. package/dist/snippets/store.js +60 -0
  164. package/dist/spaces/space-config.js +156 -0
  165. package/dist/spaces/space.js +220 -0
  166. package/dist/stats/store.js +101 -0
  167. package/dist/tools/apply-patch.js +134 -0
  168. package/dist/tools/auto-check.js +218 -0
  169. package/dist/tools/diff-edit.js +150 -0
  170. package/dist/tools/diff-prompt.js +36 -0
  171. package/dist/tools/edit-file.js +66 -0
  172. package/dist/tools/file-ops.js +205 -0
  173. package/dist/tools/glob.js +17 -0
  174. package/dist/tools/grep.js +56 -0
  175. package/dist/tools/image.js +194 -0
  176. package/dist/tools/list-directory.js +228 -0
  177. package/dist/tools/memory.js +17 -0
  178. package/dist/tools/multi-edit.js +299 -0
  179. package/dist/tools/policy.js +95 -0
  180. package/dist/tools/registry.js +484 -0
  181. package/dist/tools/retry.js +74 -0
  182. package/dist/tools/run-in-terminal.js +162 -0
  183. package/dist/tools/safety.js +64 -0
  184. package/dist/tools/sandbox.js +15 -0
  185. package/dist/tools/search-symbols.js +212 -0
  186. package/dist/tools/shell.js +118 -0
  187. package/dist/tools/web.js +167 -0
  188. package/dist/ui/prompt.js +37 -0
  189. package/dist/ui/render.js +96 -0
  190. package/dist/ui/screen.js +13 -0
  191. package/dist/ui/theme.js +56 -0
  192. package/dist/util/browser.js +34 -0
  193. package/dist/util/completion.js +350 -0
  194. package/dist/util/cost.js +28 -0
  195. package/dist/util/keybindings.js +113 -0
  196. package/dist/util/lazy.js +26 -0
  197. package/dist/util/perf.js +25 -0
  198. package/dist/util/token-worker.js +11 -0
  199. package/dist/util/tokens.js +50 -0
  200. package/dist/workflows/builtins.js +128 -0
  201. package/dist/workflows/engine.js +496 -0
  202. package/dist/workflows/file-trigger.js +197 -0
  203. package/package.json +79 -0
@@ -0,0 +1,410 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import fg from 'fast-glob';
4
+ const DEFAULT_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.mts', '.cts', '.mjs', '.cjs'];
5
+ const DEFAULT_EXCLUDE = [
6
+ '**/node_modules/**',
7
+ '**/dist/**',
8
+ '**/.git/**',
9
+ '**/coverage/**',
10
+ '**/.icopilot/**',
11
+ ];
12
+ const TOP_LEVEL_PATTERNS = [
13
+ {
14
+ kind: 'function',
15
+ regex: /^\s*(export\s+)?(?:default\s+)?(?:async\s+)?function\s+([A-Za-z_$][\w$]*)\s*(?:<[^>{\r\n]+>)?\s*\([^)]*\)\s*(?::\s*[^={]+)?/gm,
16
+ },
17
+ {
18
+ kind: 'class',
19
+ regex: /^\s*(export\s+)?(?:default\s+)?class\s+([A-Za-z_$][\w$]*)\b[^{]*/gm,
20
+ },
21
+ {
22
+ kind: 'interface',
23
+ regex: /^\s*(export\s+)?interface\s+([A-Za-z_$][\w$]*)\b[^{=]*/gm,
24
+ },
25
+ {
26
+ kind: 'type',
27
+ regex: /^\s*(export\s+)?type\s+([A-Za-z_$][\w$]*)\b[^=]*=/gm,
28
+ },
29
+ {
30
+ kind: 'enum',
31
+ regex: /^\s*(export\s+)?enum\s+([A-Za-z_$][\w$]*)\b[^{]*/gm,
32
+ },
33
+ {
34
+ kind: 'variable',
35
+ regex: /^\s*(export\s+)?(?:const|let)\s+([A-Za-z_$][\w$]*)\b/gm,
36
+ },
37
+ ];
38
+ const CLASS_PATTERN = /^\s*(export\s+)?(?:default\s+)?class\s+([A-Za-z_$][\w$]*)\b[^{]*\{/gm;
39
+ const METHOD_PATTERN = /^\s*(?:(?:public|private|protected|static|async|abstract|override|get|set|readonly)\s+)*(#?[A-Za-z_$][\w$]*)\s*(?:<[^>{\r\n]+>)?\s*\([^;{}]*\)\s*(?::\s*[^={]+)?\s*\{$/;
40
+ export class SymbolIndex {
41
+ rootDir = '';
42
+ cachePath = '';
43
+ symbols = [];
44
+ async build(rootDir, options = {}) {
45
+ const resolvedRoot = path.resolve(rootDir);
46
+ const includePrivate = options.includePrivate ?? true;
47
+ const files = await fg(toPatterns(options.extensions), {
48
+ cwd: resolvedRoot,
49
+ onlyFiles: true,
50
+ dot: false,
51
+ unique: true,
52
+ ignore: [...DEFAULT_EXCLUDE, ...(options.exclude ?? [])],
53
+ });
54
+ const collected = [];
55
+ for (const file of files.sort()) {
56
+ const absolute = path.join(resolvedRoot, file);
57
+ let content = '';
58
+ try {
59
+ content = await fs.promises.readFile(absolute, 'utf8');
60
+ }
61
+ catch {
62
+ continue;
63
+ }
64
+ const relativeFile = path.normalize(file);
65
+ collected.push(...extractSymbols(content, relativeFile, includePrivate));
66
+ }
67
+ this.rootDir = resolvedRoot;
68
+ this.cachePath = path.join(resolvedRoot, '.icopilot', 'symbol-index.json');
69
+ this.symbols = normalizeSymbols(collected);
70
+ this.save(this.cachePath);
71
+ }
72
+ search(query) {
73
+ const needle = query.trim().toLowerCase();
74
+ if (!needle)
75
+ return [];
76
+ return this.symbols
77
+ .map((symbol) => ({ symbol, score: scoreSymbolName(symbol.name, needle) }))
78
+ .filter((item) => item.score > 0)
79
+ .sort((a, b) => b.score - a.score ||
80
+ a.symbol.name.localeCompare(b.symbol.name) ||
81
+ a.symbol.file.localeCompare(b.symbol.file) ||
82
+ a.symbol.line - b.symbol.line)
83
+ .map((item) => item.symbol);
84
+ }
85
+ getByFile(file) {
86
+ const normalized = normalizeFileKey(file, this.rootDir);
87
+ return this.symbols.filter((symbol) => path.normalize(symbol.file) === normalized);
88
+ }
89
+ getByKind(kind) {
90
+ return this.symbols.filter((symbol) => symbol.kind === kind);
91
+ }
92
+ getExported() {
93
+ return this.symbols.filter((symbol) => symbol.exported);
94
+ }
95
+ save(filePath) {
96
+ const absolute = path.resolve(filePath);
97
+ const payload = {
98
+ rootDir: this.rootDir,
99
+ createdAt: new Date().toISOString(),
100
+ symbols: this.symbols,
101
+ };
102
+ fs.mkdirSync(path.dirname(absolute), { recursive: true });
103
+ fs.writeFileSync(absolute, `${JSON.stringify(payload, null, 2)}\n`, 'utf8');
104
+ this.cachePath = absolute;
105
+ }
106
+ load(filePath) {
107
+ const absolute = path.resolve(filePath);
108
+ const raw = fs.readFileSync(absolute, 'utf8');
109
+ const parsed = JSON.parse(raw);
110
+ const payload = Array.isArray(parsed)
111
+ ? { rootDir: this.rootDir, symbols: parsed }
112
+ : {
113
+ rootDir: typeof parsed.rootDir === 'string' ? parsed.rootDir : this.rootDir,
114
+ symbols: Array.isArray(parsed.symbols) ? parsed.symbols : [],
115
+ };
116
+ this.rootDir = payload.rootDir;
117
+ this.cachePath = absolute;
118
+ this.symbols = normalizeSymbols(payload.symbols);
119
+ }
120
+ }
121
+ function extractSymbols(content, file, includePrivate) {
122
+ const lineStarts = buildLineStarts(content);
123
+ const depthMap = buildBraceDepthMap(content);
124
+ const symbols = [];
125
+ for (const { kind, regex } of TOP_LEVEL_PATTERNS) {
126
+ regex.lastIndex = 0;
127
+ for (const match of content.matchAll(regex)) {
128
+ const [declaration = '', exportToken = '', name = ''] = match;
129
+ if (!name)
130
+ continue;
131
+ if ((depthMap[match.index ?? 0] ?? 0) !== 0)
132
+ continue;
133
+ const exported = Boolean(exportToken?.trim());
134
+ if (!includePrivate && !exported)
135
+ continue;
136
+ symbols.push({
137
+ name,
138
+ kind,
139
+ file,
140
+ line: lineNumberAt(match.index ?? 0, lineStarts),
141
+ signature: cleanSignature(declaration),
142
+ exported,
143
+ });
144
+ }
145
+ }
146
+ for (const block of classBlocks(content)) {
147
+ for (const method of extractMethods(block.body, lineStarts, block.bodyStart, file, includePrivate)) {
148
+ symbols.push(method);
149
+ }
150
+ }
151
+ return symbols;
152
+ }
153
+ function classBlocks(content) {
154
+ const blocks = [];
155
+ for (const match of content.matchAll(CLASS_PATTERN)) {
156
+ const fullMatch = match[0] ?? '';
157
+ const openOffset = fullMatch.lastIndexOf('{');
158
+ if (openOffset < 0 || match.index == null)
159
+ continue;
160
+ const openIndex = match.index + openOffset;
161
+ const closeIndex = findMatchingBrace(content, openIndex);
162
+ if (closeIndex < 0)
163
+ continue;
164
+ blocks.push({
165
+ body: content.slice(openIndex + 1, closeIndex),
166
+ bodyStart: openIndex + 1,
167
+ });
168
+ }
169
+ return blocks;
170
+ }
171
+ function extractMethods(classBody, lineStarts, bodyStart, file, includePrivate) {
172
+ const methods = [];
173
+ let depth = 0;
174
+ let current = '';
175
+ let currentStart = 0;
176
+ let started = false;
177
+ for (let i = 0; i < classBody.length; i += 1) {
178
+ const char = classBody[i];
179
+ if (startsWith(classBody, i, '//')) {
180
+ i = skipLineComment(classBody, i + 2);
181
+ continue;
182
+ }
183
+ if (startsWith(classBody, i, '/*')) {
184
+ i = skipBlockComment(classBody, i + 2);
185
+ continue;
186
+ }
187
+ if (char === '"' || char === "'" || char === '`') {
188
+ i = skipString(classBody, i);
189
+ continue;
190
+ }
191
+ if (char === '{') {
192
+ if (depth === 0) {
193
+ const signature = `${current}{`;
194
+ const match = METHOD_PATTERN.exec(signature);
195
+ METHOD_PATTERN.lastIndex = 0;
196
+ if (match) {
197
+ const name = match[1] ?? '';
198
+ const isPrivate = name.startsWith('#') || /\b(?:private|protected)\b/.test(signature);
199
+ if (name && name !== 'constructor' && (includePrivate || !isPrivate)) {
200
+ methods.push({
201
+ name,
202
+ kind: 'method',
203
+ file,
204
+ line: lineNumberAt(bodyStart + currentStart, lineStarts),
205
+ signature: cleanSignature(signature),
206
+ exported: false,
207
+ });
208
+ }
209
+ }
210
+ }
211
+ depth += 1;
212
+ current = '';
213
+ started = false;
214
+ continue;
215
+ }
216
+ if (char === '}') {
217
+ depth = Math.max(0, depth - 1);
218
+ if (depth === 0) {
219
+ current = '';
220
+ started = false;
221
+ }
222
+ continue;
223
+ }
224
+ if (depth > 0)
225
+ continue;
226
+ if (!started && !/\s/.test(char)) {
227
+ currentStart = i;
228
+ started = true;
229
+ }
230
+ current += char;
231
+ if (char === ';') {
232
+ current = '';
233
+ started = false;
234
+ }
235
+ }
236
+ return methods;
237
+ }
238
+ function toPatterns(extensions) {
239
+ const values = extensions?.length ? extensions : DEFAULT_EXTENSIONS;
240
+ const patterns = values.map((value) => {
241
+ const trimmed = value.trim();
242
+ if (!trimmed)
243
+ return '';
244
+ if (trimmed.includes('*') || trimmed.includes('/'))
245
+ return trimmed;
246
+ const normalized = trimmed.startsWith('.') ? trimmed : `.${trimmed}`;
247
+ return `**/*${normalized}`;
248
+ });
249
+ return patterns.filter(Boolean);
250
+ }
251
+ function normalizeSymbols(symbols) {
252
+ const byKey = new Map();
253
+ for (const CodeSymbol of symbols) {
254
+ if (!CodeSymbol || typeof CodeSymbol.name !== 'string' || typeof CodeSymbol.file !== 'string')
255
+ continue;
256
+ const normalized = {
257
+ name: CodeSymbol.name,
258
+ kind: CodeSymbol.kind,
259
+ file: path.normalize(CodeSymbol.file),
260
+ line: Number.isFinite(CodeSymbol.line) ? Math.max(1, Math.trunc(CodeSymbol.line)) : 1,
261
+ signature: CodeSymbol.signature,
262
+ exported: Boolean(CodeSymbol.exported),
263
+ };
264
+ byKey.set(`${normalized.file}:${normalized.line}:${normalized.kind}:${normalized.name}`, normalized);
265
+ }
266
+ return [...byKey.values()].sort((a, b) => a.file.localeCompare(b.file) ||
267
+ a.line - b.line ||
268
+ a.name.localeCompare(b.name) ||
269
+ a.kind.localeCompare(b.kind));
270
+ }
271
+ function normalizeFileKey(file, rootDir) {
272
+ const target = rootDir && path.isAbsolute(file)
273
+ ? path.relative(rootDir, path.resolve(file))
274
+ : path.normalize(file);
275
+ return path.normalize(target);
276
+ }
277
+ function cleanSignature(signature) {
278
+ return signature
279
+ .replace(/\s+/g, ' ')
280
+ .replace(/\s+\{$/, '')
281
+ .trim();
282
+ }
283
+ function buildLineStarts(text) {
284
+ const starts = [0];
285
+ for (let i = 0; i < text.length; i += 1) {
286
+ if (text[i] === '\n')
287
+ starts.push(i + 1);
288
+ }
289
+ return starts;
290
+ }
291
+ function buildBraceDepthMap(text) {
292
+ const depthMap = new Array(text.length).fill(0);
293
+ let depth = 0;
294
+ for (let i = 0; i < text.length; i += 1) {
295
+ depthMap[i] = depth;
296
+ const char = text[i];
297
+ if (startsWith(text, i, '//')) {
298
+ i = skipLineComment(text, i + 2);
299
+ continue;
300
+ }
301
+ if (startsWith(text, i, '/*')) {
302
+ i = skipBlockComment(text, i + 2);
303
+ continue;
304
+ }
305
+ if (char === '"' || char === "'" || char === '`') {
306
+ i = skipString(text, i);
307
+ continue;
308
+ }
309
+ if (char === '{')
310
+ depth += 1;
311
+ if (char === '}')
312
+ depth = Math.max(0, depth - 1);
313
+ }
314
+ return depthMap;
315
+ }
316
+ function lineNumberAt(index, lineStarts) {
317
+ let low = 0;
318
+ let high = lineStarts.length - 1;
319
+ while (low <= high) {
320
+ const mid = Math.floor((low + high) / 2);
321
+ const start = lineStarts[mid] ?? 0;
322
+ const next = lineStarts[mid + 1] ?? Number.MAX_SAFE_INTEGER;
323
+ if (index < start)
324
+ high = mid - 1;
325
+ else if (index >= next)
326
+ low = mid + 1;
327
+ else
328
+ return mid + 1;
329
+ }
330
+ return 1;
331
+ }
332
+ function findMatchingBrace(text, openIndex) {
333
+ let depth = 0;
334
+ for (let i = openIndex; i < text.length; i += 1) {
335
+ const char = text[i];
336
+ if (startsWith(text, i, '//')) {
337
+ i = skipLineComment(text, i + 2);
338
+ continue;
339
+ }
340
+ if (startsWith(text, i, '/*')) {
341
+ i = skipBlockComment(text, i + 2);
342
+ continue;
343
+ }
344
+ if (char === '"' || char === "'" || char === '`') {
345
+ i = skipString(text, i);
346
+ continue;
347
+ }
348
+ if (char === '{')
349
+ depth += 1;
350
+ if (char === '}') {
351
+ depth -= 1;
352
+ if (depth === 0)
353
+ return i;
354
+ }
355
+ }
356
+ return -1;
357
+ }
358
+ function skipLineComment(text, start) {
359
+ let index = start;
360
+ while (index < text.length && text[index] !== '\n')
361
+ index += 1;
362
+ return index;
363
+ }
364
+ function skipBlockComment(text, start) {
365
+ let index = start;
366
+ while (index < text.length - 1) {
367
+ if (text[index] === '*' && text[index + 1] === '/')
368
+ return index + 1;
369
+ index += 1;
370
+ }
371
+ return text.length - 1;
372
+ }
373
+ function skipString(text, start) {
374
+ const quote = text[start];
375
+ let index = start + 1;
376
+ while (index < text.length) {
377
+ const char = text[index];
378
+ if (char === '\\') {
379
+ index += 2;
380
+ continue;
381
+ }
382
+ if (char === quote)
383
+ return index;
384
+ index += 1;
385
+ }
386
+ return text.length - 1;
387
+ }
388
+ function startsWith(text, index, value) {
389
+ return text.slice(index, index + value.length) === value;
390
+ }
391
+ function scoreSymbolName(name, query) {
392
+ const candidate = name.toLowerCase();
393
+ if (candidate === query)
394
+ return 1_000;
395
+ if (candidate.startsWith(query))
396
+ return 800 - (candidate.length - query.length);
397
+ const containsIndex = candidate.indexOf(query);
398
+ if (containsIndex >= 0)
399
+ return 600 - containsIndex * 2 - (candidate.length - query.length);
400
+ let cursor = 0;
401
+ let score = 400;
402
+ for (const char of query) {
403
+ const next = candidate.indexOf(char, cursor);
404
+ if (next < 0)
405
+ return 0;
406
+ score -= next - cursor;
407
+ cursor = next + 1;
408
+ }
409
+ return Math.max(1, score - (candidate.length - query.length));
410
+ }