yandecode 0.1.0 → 0.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.
- package/dist/bin.js +14 -8
- package/dist/bin.js.map +7 -1
- package/dist/chunks/chunk-23DG7PZZ.js +58 -0
- package/dist/chunks/chunk-23DG7PZZ.js.map +7 -0
- package/dist/chunks/chunk-2S6B3DDQ.js +1769 -0
- package/dist/chunks/chunk-2S6B3DDQ.js.map +7 -0
- package/dist/chunks/chunk-6J5CZVCJ.js +120 -0
- package/dist/chunks/chunk-6J5CZVCJ.js.map +7 -0
- package/dist/chunks/chunk-7L6DD2VD.js +768 -0
- package/dist/chunks/chunk-7L6DD2VD.js.map +7 -0
- package/dist/chunks/chunk-DWD37AI2.js +39 -0
- package/dist/chunks/chunk-DWD37AI2.js.map +7 -0
- package/dist/chunks/chunk-KPDBXQ62.js +62 -0
- package/dist/chunks/chunk-KPDBXQ62.js.map +7 -0
- package/dist/chunks/chunk-KVBKQXTU.js +351 -0
- package/dist/chunks/chunk-KVBKQXTU.js.map +7 -0
- package/dist/chunks/chunk-L2DDIZPI.js +53 -0
- package/dist/chunks/chunk-L2DDIZPI.js.map +7 -0
- package/dist/chunks/chunk-OIAMKDTQ.js +27 -0
- package/dist/chunks/chunk-OIAMKDTQ.js.map +7 -0
- package/dist/chunks/chunk-PUWSEWX3.js +61 -0
- package/dist/chunks/chunk-PUWSEWX3.js.map +7 -0
- package/dist/chunks/chunk-QCQC73WX.js +122 -0
- package/dist/chunks/chunk-QCQC73WX.js.map +7 -0
- package/dist/chunks/chunk-TYLH4NIC.js +39 -0
- package/dist/chunks/chunk-TYLH4NIC.js.map +7 -0
- package/dist/chunks/chunk-VKVJGBNJ.js +72 -0
- package/dist/chunks/chunk-VKVJGBNJ.js.map +7 -0
- package/dist/chunks/chunk-Y2MIXROU.js +20072 -0
- package/dist/chunks/chunk-Y2MIXROU.js.map +7 -0
- package/dist/chunks/cli-V6PJTDLM.js +3464 -0
- package/dist/chunks/cli-V6PJTDLM.js.map +7 -0
- package/dist/chunks/doctor-LLDVEPWL.js +184 -0
- package/dist/chunks/doctor-LLDVEPWL.js.map +7 -0
- package/dist/chunks/entry-YAOEX3EB.js +187 -0
- package/dist/chunks/entry-YAOEX3EB.js.map +7 -0
- package/dist/chunks/init-4ZJFCKIJ.js +46 -0
- package/dist/chunks/init-4ZJFCKIJ.js.map +7 -0
- package/dist/chunks/mcp-serve-KWKMUGWZ.js +22575 -0
- package/dist/chunks/mcp-serve-KWKMUGWZ.js.map +7 -0
- package/dist/chunks/modules-CJB5IKUJ.js +121 -0
- package/dist/chunks/modules-CJB5IKUJ.js.map +7 -0
- package/dist/chunks/runtime-3E4NIFAL.js +208 -0
- package/dist/chunks/runtime-3E4NIFAL.js.map +7 -0
- package/dist/chunks/runtime-7CRR63B5.js +395 -0
- package/dist/chunks/runtime-7CRR63B5.js.map +7 -0
- package/dist/chunks/runtime-CKMVHAU4.js +64 -0
- package/dist/chunks/runtime-CKMVHAU4.js.map +7 -0
- package/dist/chunks/runtime-CVZMWIJO.js +318 -0
- package/dist/chunks/runtime-CVZMWIJO.js.map +7 -0
- package/dist/chunks/runtime-DBNXBOAL.js +295 -0
- package/dist/chunks/runtime-DBNXBOAL.js.map +7 -0
- package/dist/chunks/runtime-EKHO2JXB.js +197 -0
- package/dist/chunks/runtime-EKHO2JXB.js.map +7 -0
- package/dist/chunks/runtime-EY6SVZYP.js +539 -0
- package/dist/chunks/runtime-EY6SVZYP.js.map +7 -0
- package/dist/chunks/runtime-UB2O2HUN.js +212 -0
- package/dist/chunks/runtime-UB2O2HUN.js.map +7 -0
- package/dist/chunks/runtime-WAFQWW4M.js +755 -0
- package/dist/chunks/runtime-WAFQWW4M.js.map +7 -0
- package/dist/chunks/runtime-ZGGF6RVA.js +271 -0
- package/dist/chunks/runtime-ZGGF6RVA.js.map +7 -0
- package/dist/chunks/status-SPIESL4F.js +38 -0
- package/dist/chunks/status-SPIESL4F.js.map +7 -0
- package/dist/chunks/uninstall-J2OWCX4A.js +54 -0
- package/dist/chunks/uninstall-J2OWCX4A.js.map +7 -0
- package/dist/chunks/update-OX5MZYQY.js +34 -0
- package/dist/chunks/update-OX5MZYQY.js.map +7 -0
- package/dist/content/agents/yandecode-reviewer.md +22 -0
- package/dist/content/agents/yandecode-security-reviewer.md +23 -0
- package/dist/content/skills/NOTICE.md +29 -0
- package/dist/content/skills/yandecode-code-review/SKILL.md +37 -0
- package/dist/content/skills/yandecode-debugging/SKILL.md +33 -0
- package/dist/content/skills/yandecode-instructions/SKILL.md +26 -0
- package/dist/content/skills/yandecode-memory/SKILL.md +36 -0
- package/dist/content/skills/yandecode-security-review/SKILL.md +24 -0
- package/dist/content/skills/yandecode-tdd/SKILL.md +38 -0
- package/dist/content/skills/yandecode-verification/SKILL.md +29 -0
- package/dist/content/skills/yandecode-workflow/SKILL.md +50 -0
- package/package.json +25 -19
- package/dist/bin.d.ts +0 -3
- package/dist/bin.d.ts.map +0 -1
- package/dist/cli.d.ts +0 -6
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -21
- package/dist/cli.js.map +0 -1
- package/dist/commands/doctor.d.ts +0 -2
- package/dist/commands/doctor.d.ts.map +0 -1
- package/dist/commands/doctor.js +0 -19
- package/dist/commands/doctor.js.map +0 -1
- package/dist/commands/hook.d.ts +0 -2
- package/dist/commands/hook.d.ts.map +0 -1
- package/dist/commands/hook.js +0 -23
- package/dist/commands/hook.js.map +0 -1
- package/dist/commands/index-cmd.d.ts +0 -2
- package/dist/commands/index-cmd.d.ts.map +0 -1
- package/dist/commands/index-cmd.js +0 -47
- package/dist/commands/index-cmd.js.map +0 -1
- package/dist/commands/index.d.ts +0 -13
- package/dist/commands/index.d.ts.map +0 -1
- package/dist/commands/index.js +0 -15
- package/dist/commands/index.js.map +0 -1
- package/dist/commands/init.d.ts +0 -2
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/init.js +0 -22
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/mcp.d.ts +0 -2
- package/dist/commands/mcp.d.ts.map +0 -1
- package/dist/commands/mcp.js +0 -71
- package/dist/commands/mcp.js.map +0 -1
- package/dist/commands/rag.d.ts +0 -2
- package/dist/commands/rag.d.ts.map +0 -1
- package/dist/commands/rag.js +0 -27
- package/dist/commands/rag.js.map +0 -1
- package/dist/commands/start.d.ts +0 -6
- package/dist/commands/start.d.ts.map +0 -1
- package/dist/commands/start.js +0 -76
- package/dist/commands/start.js.map +0 -1
- package/dist/commands/status.d.ts +0 -2
- package/dist/commands/status.d.ts.map +0 -1
- package/dist/commands/status.js +0 -29
- package/dist/commands/status.js.map +0 -1
- package/dist/commands/statusline.d.ts +0 -2
- package/dist/commands/statusline.d.ts.map +0 -1
- package/dist/commands/statusline.js +0 -90
- package/dist/commands/statusline.js.map +0 -1
- package/dist/commands/swarm.d.ts +0 -4
- package/dist/commands/swarm.d.ts.map +0 -1
- package/dist/commands/swarm.js +0 -48
- package/dist/commands/swarm.js.map +0 -1
- package/dist/commands/uninstall.d.ts +0 -2
- package/dist/commands/uninstall.d.ts.map +0 -1
- package/dist/commands/uninstall.js +0 -16
- package/dist/commands/uninstall.js.map +0 -1
- package/dist/context.d.ts +0 -13
- package/dist/context.d.ts.map +0 -1
- package/dist/context.js +0 -31
- package/dist/context.js.map +0 -1
- package/dist/doctor/checks.d.ts +0 -18
- package/dist/doctor/checks.d.ts.map +0 -1
- package/dist/doctor/checks.js +0 -269
- package/dist/doctor/checks.js.map +0 -1
- package/dist/hooks/handlers.d.ts +0 -21
- package/dist/hooks/handlers.d.ts.map +0 -1
- package/dist/hooks/handlers.js +0 -191
- package/dist/hooks/handlers.js.map +0 -1
- package/dist/index.d.ts +0 -9
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -9
- package/dist/index.js.map +0 -1
- package/dist/integration/claude-md.d.ts +0 -6
- package/dist/integration/claude-md.d.ts.map +0 -1
- package/dist/integration/claude-md.js +0 -30
- package/dist/integration/claude-md.js.map +0 -1
- package/dist/integration/gitignore.d.ts +0 -3
- package/dist/integration/gitignore.d.ts.map +0 -1
- package/dist/integration/gitignore.js +0 -13
- package/dist/integration/gitignore.js.map +0 -1
- package/dist/integration/init.d.ts +0 -17
- package/dist/integration/init.d.ts.map +0 -1
- package/dist/integration/init.js +0 -86
- package/dist/integration/init.js.map +0 -1
- package/dist/integration/json-utils.d.ts +0 -13
- package/dist/integration/json-utils.d.ts.map +0 -1
- package/dist/integration/json-utils.js +0 -33
- package/dist/integration/json-utils.js.map +0 -1
- package/dist/integration/manifest.d.ts +0 -17
- package/dist/integration/manifest.d.ts.map +0 -1
- package/dist/integration/manifest.js +0 -26
- package/dist/integration/manifest.js.map +0 -1
- package/dist/integration/materialize.d.ts +0 -9
- package/dist/integration/materialize.d.ts.map +0 -1
- package/dist/integration/materialize.js +0 -21
- package/dist/integration/materialize.js.map +0 -1
- package/dist/integration/mcp-config.d.ts +0 -4
- package/dist/integration/mcp-config.d.ts.map +0 -1
- package/dist/integration/mcp-config.js +0 -11
- package/dist/integration/mcp-config.js.map +0 -1
- package/dist/integration/settings.d.ts +0 -7
- package/dist/integration/settings.d.ts.map +0 -1
- package/dist/integration/settings.js +0 -70
- package/dist/integration/settings.js.map +0 -1
- package/dist/integration/uninstall.d.ts +0 -10
- package/dist/integration/uninstall.d.ts.map +0 -1
- package/dist/integration/uninstall.js +0 -71
- package/dist/integration/uninstall.js.map +0 -1
- package/dist/launcher.d.ts +0 -8
- package/dist/launcher.d.ts.map +0 -1
- package/dist/launcher.js +0 -18
- package/dist/launcher.js.map +0 -1
- package/dist/mcp/server.d.ts +0 -27
- package/dist/mcp/server.d.ts.map +0 -1
- package/dist/mcp/server.js +0 -311
- package/dist/mcp/server.js.map +0 -1
- package/dist/plugin-content.d.ts +0 -21
- package/dist/plugin-content.d.ts.map +0 -1
- package/dist/plugin-content.js +0 -67
- package/dist/plugin-content.js.map +0 -1
- package/dist/rag/format.d.ts +0 -3
- package/dist/rag/format.d.ts.map +0 -1
- package/dist/rag/format.js +0 -12
- package/dist/rag/format.js.map +0 -1
- package/dist/retrieval-runtime.d.ts +0 -21
- package/dist/retrieval-runtime.d.ts.map +0 -1
- package/dist/retrieval-runtime.js +0 -47
- package/dist/retrieval-runtime.js.map +0 -1
- package/dist/status-line/git.d.ts +0 -3
- package/dist/status-line/git.d.ts.map +0 -1
- package/dist/status-line/git.js +0 -34
- package/dist/status-line/git.js.map +0 -1
- package/dist/status-line/render.d.ts +0 -29
- package/dist/status-line/render.d.ts.map +0 -1
- package/dist/status-line/render.js +0 -72
- package/dist/status-line/render.js.map +0 -1
- package/dist/swarm-runtime.d.ts +0 -11
- package/dist/swarm-runtime.d.ts.map +0 -1
- package/dist/swarm-runtime.js +0 -76
- package/dist/swarm-runtime.js.map +0 -1
- package/dist/version.d.ts +0 -2
- package/dist/version.d.ts.map +0 -1
- package/dist/version.js +0 -5
- package/dist/version.js.map +0 -1
|
@@ -0,0 +1,1769 @@
|
|
|
1
|
+
import { createRequire as __yandecodeCreateRequire } from 'node:module';
|
|
2
|
+
const require = __yandecodeCreateRequire(import.meta.url);
|
|
3
|
+
import {
|
|
4
|
+
CONFIG_FILENAME,
|
|
5
|
+
__commonJS,
|
|
6
|
+
__toESM,
|
|
7
|
+
openModuleDb,
|
|
8
|
+
splitIdentifier,
|
|
9
|
+
toFtsQuery
|
|
10
|
+
} from "./chunk-Y2MIXROU.js";
|
|
11
|
+
|
|
12
|
+
// node_modules/ignore/index.js
|
|
13
|
+
var require_ignore = __commonJS({
|
|
14
|
+
"node_modules/ignore/index.js"(exports, module) {
|
|
15
|
+
"use strict";
|
|
16
|
+
function makeArray(subject) {
|
|
17
|
+
return Array.isArray(subject) ? subject : [subject];
|
|
18
|
+
}
|
|
19
|
+
var EMPTY = "";
|
|
20
|
+
var SPACE = " ";
|
|
21
|
+
var ESCAPE = "\\";
|
|
22
|
+
var REGEX_TEST_BLANK_LINE = /^\s+$/;
|
|
23
|
+
var REGEX_INVALID_TRAILING_BACKSLASH = /(?:[^\\]|^)\\$/;
|
|
24
|
+
var REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION = /^\\!/;
|
|
25
|
+
var REGEX_REPLACE_LEADING_EXCAPED_HASH = /^\\#/;
|
|
26
|
+
var REGEX_SPLITALL_CRLF = /\r?\n/g;
|
|
27
|
+
var REGEX_TEST_INVALID_PATH = /^\.*\/|^\.+$/;
|
|
28
|
+
var SLASH = "/";
|
|
29
|
+
var TMP_KEY_IGNORE = "node-ignore";
|
|
30
|
+
if (typeof Symbol !== "undefined") {
|
|
31
|
+
TMP_KEY_IGNORE = /* @__PURE__ */ Symbol.for("node-ignore");
|
|
32
|
+
}
|
|
33
|
+
var KEY_IGNORE = TMP_KEY_IGNORE;
|
|
34
|
+
var define = (object, key, value) => Object.defineProperty(object, key, { value });
|
|
35
|
+
var REGEX_REGEXP_RANGE = /([0-z])-([0-z])/g;
|
|
36
|
+
var RETURN_FALSE = () => false;
|
|
37
|
+
var sanitizeRange = (range) => range.replace(
|
|
38
|
+
REGEX_REGEXP_RANGE,
|
|
39
|
+
(match, from, to) => from.charCodeAt(0) <= to.charCodeAt(0) ? match : EMPTY
|
|
40
|
+
);
|
|
41
|
+
var cleanRangeBackSlash = (slashes) => {
|
|
42
|
+
const { length } = slashes;
|
|
43
|
+
return slashes.slice(0, length - length % 2);
|
|
44
|
+
};
|
|
45
|
+
var REPLACERS = [
|
|
46
|
+
[
|
|
47
|
+
// remove BOM
|
|
48
|
+
// TODO:
|
|
49
|
+
// Other similar zero-width characters?
|
|
50
|
+
/^\uFEFF/,
|
|
51
|
+
() => EMPTY
|
|
52
|
+
],
|
|
53
|
+
// > Trailing spaces are ignored unless they are quoted with backslash ("\")
|
|
54
|
+
[
|
|
55
|
+
// (a\ ) -> (a )
|
|
56
|
+
// (a ) -> (a)
|
|
57
|
+
// (a ) -> (a)
|
|
58
|
+
// (a \ ) -> (a )
|
|
59
|
+
/((?:\\\\)*?)(\\?\s+)$/,
|
|
60
|
+
(_, m1, m2) => m1 + (m2.indexOf("\\") === 0 ? SPACE : EMPTY)
|
|
61
|
+
],
|
|
62
|
+
// replace (\ ) with ' '
|
|
63
|
+
// (\ ) -> ' '
|
|
64
|
+
// (\\ ) -> '\\ '
|
|
65
|
+
// (\\\ ) -> '\\ '
|
|
66
|
+
[
|
|
67
|
+
/(\\+?)\s/g,
|
|
68
|
+
(_, m1) => {
|
|
69
|
+
const { length } = m1;
|
|
70
|
+
return m1.slice(0, length - length % 2) + SPACE;
|
|
71
|
+
}
|
|
72
|
+
],
|
|
73
|
+
// Escape metacharacters
|
|
74
|
+
// which is written down by users but means special for regular expressions.
|
|
75
|
+
// > There are 12 characters with special meanings:
|
|
76
|
+
// > - the backslash \,
|
|
77
|
+
// > - the caret ^,
|
|
78
|
+
// > - the dollar sign $,
|
|
79
|
+
// > - the period or dot .,
|
|
80
|
+
// > - the vertical bar or pipe symbol |,
|
|
81
|
+
// > - the question mark ?,
|
|
82
|
+
// > - the asterisk or star *,
|
|
83
|
+
// > - the plus sign +,
|
|
84
|
+
// > - the opening parenthesis (,
|
|
85
|
+
// > - the closing parenthesis ),
|
|
86
|
+
// > - and the opening square bracket [,
|
|
87
|
+
// > - the opening curly brace {,
|
|
88
|
+
// > These special characters are often called "metacharacters".
|
|
89
|
+
[
|
|
90
|
+
/[\\$.|*+(){^]/g,
|
|
91
|
+
(match) => `\\${match}`
|
|
92
|
+
],
|
|
93
|
+
[
|
|
94
|
+
// > a question mark (?) matches a single character
|
|
95
|
+
/(?!\\)\?/g,
|
|
96
|
+
() => "[^/]"
|
|
97
|
+
],
|
|
98
|
+
// leading slash
|
|
99
|
+
[
|
|
100
|
+
// > A leading slash matches the beginning of the pathname.
|
|
101
|
+
// > For example, "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c".
|
|
102
|
+
// A leading slash matches the beginning of the pathname
|
|
103
|
+
/^\//,
|
|
104
|
+
() => "^"
|
|
105
|
+
],
|
|
106
|
+
// replace special metacharacter slash after the leading slash
|
|
107
|
+
[
|
|
108
|
+
/\//g,
|
|
109
|
+
() => "\\/"
|
|
110
|
+
],
|
|
111
|
+
[
|
|
112
|
+
// > A leading "**" followed by a slash means match in all directories.
|
|
113
|
+
// > For example, "**/foo" matches file or directory "foo" anywhere,
|
|
114
|
+
// > the same as pattern "foo".
|
|
115
|
+
// > "**/foo/bar" matches file or directory "bar" anywhere that is directly
|
|
116
|
+
// > under directory "foo".
|
|
117
|
+
// Notice that the '*'s have been replaced as '\\*'
|
|
118
|
+
/^\^*\\\*\\\*\\\//,
|
|
119
|
+
// '**/foo' <-> 'foo'
|
|
120
|
+
() => "^(?:.*\\/)?"
|
|
121
|
+
],
|
|
122
|
+
// starting
|
|
123
|
+
[
|
|
124
|
+
// there will be no leading '/'
|
|
125
|
+
// (which has been replaced by section "leading slash")
|
|
126
|
+
// If starts with '**', adding a '^' to the regular expression also works
|
|
127
|
+
/^(?=[^^])/,
|
|
128
|
+
function startingReplacer() {
|
|
129
|
+
return !/\/(?!$)/.test(this) ? "(?:^|\\/)" : "^";
|
|
130
|
+
}
|
|
131
|
+
],
|
|
132
|
+
// two globstars
|
|
133
|
+
[
|
|
134
|
+
// Use lookahead assertions so that we could match more than one `'/**'`
|
|
135
|
+
/\\\/\\\*\\\*(?=\\\/|$)/g,
|
|
136
|
+
// Zero, one or several directories
|
|
137
|
+
// should not use '*', or it will be replaced by the next replacer
|
|
138
|
+
// Check if it is not the last `'/**'`
|
|
139
|
+
(_, index, str) => index + 6 < str.length ? "(?:\\/[^\\/]+)*" : "\\/.+"
|
|
140
|
+
],
|
|
141
|
+
// normal intermediate wildcards
|
|
142
|
+
[
|
|
143
|
+
// Never replace escaped '*'
|
|
144
|
+
// ignore rule '\*' will match the path '*'
|
|
145
|
+
// 'abc.*/' -> go
|
|
146
|
+
// 'abc.*' -> skip this rule,
|
|
147
|
+
// coz trailing single wildcard will be handed by [trailing wildcard]
|
|
148
|
+
/(^|[^\\]+)(\\\*)+(?=.+)/g,
|
|
149
|
+
// '*.js' matches '.js'
|
|
150
|
+
// '*.js' doesn't match 'abc'
|
|
151
|
+
(_, p1, p2) => {
|
|
152
|
+
const unescaped = p2.replace(/\\\*/g, "[^\\/]*");
|
|
153
|
+
return p1 + unescaped;
|
|
154
|
+
}
|
|
155
|
+
],
|
|
156
|
+
[
|
|
157
|
+
// unescape, revert step 3 except for back slash
|
|
158
|
+
// For example, if a user escape a '\\*',
|
|
159
|
+
// after step 3, the result will be '\\\\\\*'
|
|
160
|
+
/\\\\\\(?=[$.|*+(){^])/g,
|
|
161
|
+
() => ESCAPE
|
|
162
|
+
],
|
|
163
|
+
[
|
|
164
|
+
// '\\\\' -> '\\'
|
|
165
|
+
/\\\\/g,
|
|
166
|
+
() => ESCAPE
|
|
167
|
+
],
|
|
168
|
+
[
|
|
169
|
+
// > The range notation, e.g. [a-zA-Z],
|
|
170
|
+
// > can be used to match one of the characters in a range.
|
|
171
|
+
// `\` is escaped by step 3
|
|
172
|
+
/(\\)?\[([^\]/]*?)(\\*)($|\])/g,
|
|
173
|
+
(match, leadEscape, range, endEscape, close) => leadEscape === ESCAPE ? `\\[${range}${cleanRangeBackSlash(endEscape)}${close}` : close === "]" ? endEscape.length % 2 === 0 ? `[${sanitizeRange(range)}${endEscape}]` : "[]" : "[]"
|
|
174
|
+
],
|
|
175
|
+
// ending
|
|
176
|
+
[
|
|
177
|
+
// 'js' will not match 'js.'
|
|
178
|
+
// 'ab' will not match 'abc'
|
|
179
|
+
/(?:[^*])$/,
|
|
180
|
+
// WTF!
|
|
181
|
+
// https://git-scm.com/docs/gitignore
|
|
182
|
+
// changes in [2.22.1](https://git-scm.com/docs/gitignore/2.22.1)
|
|
183
|
+
// which re-fixes #24, #38
|
|
184
|
+
// > If there is a separator at the end of the pattern then the pattern
|
|
185
|
+
// > will only match directories, otherwise the pattern can match both
|
|
186
|
+
// > files and directories.
|
|
187
|
+
// 'js*' will not match 'a.js'
|
|
188
|
+
// 'js/' will not match 'a.js'
|
|
189
|
+
// 'js' will match 'a.js' and 'a.js/'
|
|
190
|
+
(match) => /\/$/.test(match) ? `${match}$` : `${match}(?=$|\\/$)`
|
|
191
|
+
],
|
|
192
|
+
// trailing wildcard
|
|
193
|
+
[
|
|
194
|
+
/(\^|\\\/)?\\\*$/,
|
|
195
|
+
(_, p1) => {
|
|
196
|
+
const prefix = p1 ? `${p1}[^/]+` : "[^/]*";
|
|
197
|
+
return `${prefix}(?=$|\\/$)`;
|
|
198
|
+
}
|
|
199
|
+
]
|
|
200
|
+
];
|
|
201
|
+
var regexCache = /* @__PURE__ */ Object.create(null);
|
|
202
|
+
var makeRegex = (pattern, ignoreCase) => {
|
|
203
|
+
let source = regexCache[pattern];
|
|
204
|
+
if (!source) {
|
|
205
|
+
source = REPLACERS.reduce(
|
|
206
|
+
(prev, [matcher, replacer]) => prev.replace(matcher, replacer.bind(pattern)),
|
|
207
|
+
pattern
|
|
208
|
+
);
|
|
209
|
+
regexCache[pattern] = source;
|
|
210
|
+
}
|
|
211
|
+
return ignoreCase ? new RegExp(source, "i") : new RegExp(source);
|
|
212
|
+
};
|
|
213
|
+
var isString = (subject) => typeof subject === "string";
|
|
214
|
+
var checkPattern = (pattern) => pattern && isString(pattern) && !REGEX_TEST_BLANK_LINE.test(pattern) && !REGEX_INVALID_TRAILING_BACKSLASH.test(pattern) && pattern.indexOf("#") !== 0;
|
|
215
|
+
var splitPattern = (pattern) => pattern.split(REGEX_SPLITALL_CRLF);
|
|
216
|
+
var IgnoreRule = class {
|
|
217
|
+
constructor(origin, pattern, negative, regex) {
|
|
218
|
+
this.origin = origin;
|
|
219
|
+
this.pattern = pattern;
|
|
220
|
+
this.negative = negative;
|
|
221
|
+
this.regex = regex;
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
var createRule = (pattern, ignoreCase) => {
|
|
225
|
+
const origin = pattern;
|
|
226
|
+
let negative = false;
|
|
227
|
+
if (pattern.indexOf("!") === 0) {
|
|
228
|
+
negative = true;
|
|
229
|
+
pattern = pattern.substr(1);
|
|
230
|
+
}
|
|
231
|
+
pattern = pattern.replace(REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION, "!").replace(REGEX_REPLACE_LEADING_EXCAPED_HASH, "#");
|
|
232
|
+
const regex = makeRegex(pattern, ignoreCase);
|
|
233
|
+
return new IgnoreRule(
|
|
234
|
+
origin,
|
|
235
|
+
pattern,
|
|
236
|
+
negative,
|
|
237
|
+
regex
|
|
238
|
+
);
|
|
239
|
+
};
|
|
240
|
+
var throwError = (message, Ctor) => {
|
|
241
|
+
throw new Ctor(message);
|
|
242
|
+
};
|
|
243
|
+
var checkPath = (path, originalPath, doThrow) => {
|
|
244
|
+
if (!isString(path)) {
|
|
245
|
+
return doThrow(
|
|
246
|
+
`path must be a string, but got \`${originalPath}\``,
|
|
247
|
+
TypeError
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
if (!path) {
|
|
251
|
+
return doThrow(`path must not be empty`, TypeError);
|
|
252
|
+
}
|
|
253
|
+
if (checkPath.isNotRelative(path)) {
|
|
254
|
+
const r = "`path.relative()`d";
|
|
255
|
+
return doThrow(
|
|
256
|
+
`path should be a ${r} string, but got "${originalPath}"`,
|
|
257
|
+
RangeError
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
return true;
|
|
261
|
+
};
|
|
262
|
+
var isNotRelative = (path) => REGEX_TEST_INVALID_PATH.test(path);
|
|
263
|
+
checkPath.isNotRelative = isNotRelative;
|
|
264
|
+
checkPath.convert = (p) => p;
|
|
265
|
+
var Ignore = class {
|
|
266
|
+
constructor({
|
|
267
|
+
ignorecase = true,
|
|
268
|
+
ignoreCase = ignorecase,
|
|
269
|
+
allowRelativePaths = false
|
|
270
|
+
} = {}) {
|
|
271
|
+
define(this, KEY_IGNORE, true);
|
|
272
|
+
this._rules = [];
|
|
273
|
+
this._ignoreCase = ignoreCase;
|
|
274
|
+
this._allowRelativePaths = allowRelativePaths;
|
|
275
|
+
this._initCache();
|
|
276
|
+
}
|
|
277
|
+
_initCache() {
|
|
278
|
+
this._ignoreCache = /* @__PURE__ */ Object.create(null);
|
|
279
|
+
this._testCache = /* @__PURE__ */ Object.create(null);
|
|
280
|
+
}
|
|
281
|
+
_addPattern(pattern) {
|
|
282
|
+
if (pattern && pattern[KEY_IGNORE]) {
|
|
283
|
+
this._rules = this._rules.concat(pattern._rules);
|
|
284
|
+
this._added = true;
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
if (checkPattern(pattern)) {
|
|
288
|
+
const rule = createRule(pattern, this._ignoreCase);
|
|
289
|
+
this._added = true;
|
|
290
|
+
this._rules.push(rule);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
// @param {Array<string> | string | Ignore} pattern
|
|
294
|
+
add(pattern) {
|
|
295
|
+
this._added = false;
|
|
296
|
+
makeArray(
|
|
297
|
+
isString(pattern) ? splitPattern(pattern) : pattern
|
|
298
|
+
).forEach(this._addPattern, this);
|
|
299
|
+
if (this._added) {
|
|
300
|
+
this._initCache();
|
|
301
|
+
}
|
|
302
|
+
return this;
|
|
303
|
+
}
|
|
304
|
+
// legacy
|
|
305
|
+
addPattern(pattern) {
|
|
306
|
+
return this.add(pattern);
|
|
307
|
+
}
|
|
308
|
+
// | ignored : unignored
|
|
309
|
+
// negative | 0:0 | 0:1 | 1:0 | 1:1
|
|
310
|
+
// -------- | ------- | ------- | ------- | --------
|
|
311
|
+
// 0 | TEST | TEST | SKIP | X
|
|
312
|
+
// 1 | TESTIF | SKIP | TEST | X
|
|
313
|
+
// - SKIP: always skip
|
|
314
|
+
// - TEST: always test
|
|
315
|
+
// - TESTIF: only test if checkUnignored
|
|
316
|
+
// - X: that never happen
|
|
317
|
+
// @param {boolean} whether should check if the path is unignored,
|
|
318
|
+
// setting `checkUnignored` to `false` could reduce additional
|
|
319
|
+
// path matching.
|
|
320
|
+
// @returns {TestResult} true if a file is ignored
|
|
321
|
+
_testOne(path, checkUnignored) {
|
|
322
|
+
let ignored = false;
|
|
323
|
+
let unignored = false;
|
|
324
|
+
this._rules.forEach((rule) => {
|
|
325
|
+
const { negative } = rule;
|
|
326
|
+
if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
const matched = rule.regex.test(path);
|
|
330
|
+
if (matched) {
|
|
331
|
+
ignored = !negative;
|
|
332
|
+
unignored = negative;
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
return {
|
|
336
|
+
ignored,
|
|
337
|
+
unignored
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
// @returns {TestResult}
|
|
341
|
+
_test(originalPath, cache, checkUnignored, slices) {
|
|
342
|
+
const path = originalPath && checkPath.convert(originalPath);
|
|
343
|
+
checkPath(
|
|
344
|
+
path,
|
|
345
|
+
originalPath,
|
|
346
|
+
this._allowRelativePaths ? RETURN_FALSE : throwError
|
|
347
|
+
);
|
|
348
|
+
return this._t(path, cache, checkUnignored, slices);
|
|
349
|
+
}
|
|
350
|
+
_t(path, cache, checkUnignored, slices) {
|
|
351
|
+
if (path in cache) {
|
|
352
|
+
return cache[path];
|
|
353
|
+
}
|
|
354
|
+
if (!slices) {
|
|
355
|
+
slices = path.split(SLASH);
|
|
356
|
+
}
|
|
357
|
+
slices.pop();
|
|
358
|
+
if (!slices.length) {
|
|
359
|
+
return cache[path] = this._testOne(path, checkUnignored);
|
|
360
|
+
}
|
|
361
|
+
const parent = this._t(
|
|
362
|
+
slices.join(SLASH) + SLASH,
|
|
363
|
+
cache,
|
|
364
|
+
checkUnignored,
|
|
365
|
+
slices
|
|
366
|
+
);
|
|
367
|
+
return cache[path] = parent.ignored ? parent : this._testOne(path, checkUnignored);
|
|
368
|
+
}
|
|
369
|
+
ignores(path) {
|
|
370
|
+
return this._test(path, this._ignoreCache, false).ignored;
|
|
371
|
+
}
|
|
372
|
+
createFilter() {
|
|
373
|
+
return (path) => !this.ignores(path);
|
|
374
|
+
}
|
|
375
|
+
filter(paths) {
|
|
376
|
+
return makeArray(paths).filter(this.createFilter());
|
|
377
|
+
}
|
|
378
|
+
// @returns {TestResult}
|
|
379
|
+
test(path) {
|
|
380
|
+
return this._test(path, this._testCache, true);
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
var factory = (options) => new Ignore(options);
|
|
384
|
+
var isPathValid = (path) => checkPath(path && checkPath.convert(path), path, RETURN_FALSE);
|
|
385
|
+
factory.isPathValid = isPathValid;
|
|
386
|
+
factory.default = factory;
|
|
387
|
+
module.exports = factory;
|
|
388
|
+
if (
|
|
389
|
+
// Detect `process` so that it can run in browsers.
|
|
390
|
+
typeof process !== "undefined" && (process.env && process.env.IGNORE_TEST_WIN32 || process.platform === "win32")
|
|
391
|
+
) {
|
|
392
|
+
const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
|
|
393
|
+
checkPath.convert = makePosix;
|
|
394
|
+
const REGIX_IS_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
|
|
395
|
+
checkPath.isNotRelative = (path) => REGIX_IS_WINDOWS_PATH_ABSOLUTE.test(path) || isNotRelative(path);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
// packages/retrieval/src/chunking/languages.ts
|
|
401
|
+
import { basename, extname } from "node:path";
|
|
402
|
+
var BY_EXT = {
|
|
403
|
+
ts: "typescript",
|
|
404
|
+
mts: "typescript",
|
|
405
|
+
cts: "typescript",
|
|
406
|
+
tsx: "tsx",
|
|
407
|
+
js: "javascript",
|
|
408
|
+
mjs: "javascript",
|
|
409
|
+
cjs: "javascript",
|
|
410
|
+
jsx: "javascript",
|
|
411
|
+
py: "python",
|
|
412
|
+
go: "go",
|
|
413
|
+
java: "java",
|
|
414
|
+
rs: "rust",
|
|
415
|
+
md: "markdown",
|
|
416
|
+
mdx: "markdown",
|
|
417
|
+
json: "json",
|
|
418
|
+
yml: "yaml",
|
|
419
|
+
yaml: "yaml",
|
|
420
|
+
toml: "toml",
|
|
421
|
+
sh: "shell",
|
|
422
|
+
bash: "shell",
|
|
423
|
+
css: "css",
|
|
424
|
+
scss: "css",
|
|
425
|
+
html: "html",
|
|
426
|
+
sql: "sql",
|
|
427
|
+
txt: "text"
|
|
428
|
+
};
|
|
429
|
+
var BY_NAME = { Dockerfile: "text", Makefile: "text" };
|
|
430
|
+
function detectLanguage(path) {
|
|
431
|
+
const name2 = basename(path);
|
|
432
|
+
if (BY_NAME[name2]) return BY_NAME[name2];
|
|
433
|
+
const ext = extname(name2).slice(1).toLowerCase();
|
|
434
|
+
return BY_EXT[ext] ?? null;
|
|
435
|
+
}
|
|
436
|
+
var GRAMMAR_FILES = {
|
|
437
|
+
typescript: "tree-sitter-typescript.wasm",
|
|
438
|
+
tsx: "tree-sitter-tsx.wasm",
|
|
439
|
+
javascript: "tree-sitter-javascript.wasm",
|
|
440
|
+
python: "tree-sitter-python.wasm",
|
|
441
|
+
go: "tree-sitter-go.wasm",
|
|
442
|
+
java: "tree-sitter-java.wasm",
|
|
443
|
+
rust: "tree-sitter-rust.wasm"
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
// packages/retrieval/src/chunking/parser-loader.ts
|
|
447
|
+
import { createRequire } from "node:module";
|
|
448
|
+
import Parser from "web-tree-sitter";
|
|
449
|
+
var require2 = createRequire(import.meta.url);
|
|
450
|
+
var parserInit = null;
|
|
451
|
+
var languages = /* @__PURE__ */ new Map();
|
|
452
|
+
async function loadLanguage(language) {
|
|
453
|
+
parserInit ??= Parser.init();
|
|
454
|
+
await parserInit;
|
|
455
|
+
let lang = languages.get(language);
|
|
456
|
+
if (!lang) {
|
|
457
|
+
const file = GRAMMAR_FILES[language];
|
|
458
|
+
if (!file) throw new Error(`no grammar for ${language}`);
|
|
459
|
+
lang = Parser.Language.load(require2.resolve(`tree-sitter-wasms/out/${file}`));
|
|
460
|
+
languages.set(language, lang);
|
|
461
|
+
}
|
|
462
|
+
return lang;
|
|
463
|
+
}
|
|
464
|
+
async function createParser(language) {
|
|
465
|
+
const lang = await loadLanguage(language);
|
|
466
|
+
const parser = new Parser();
|
|
467
|
+
parser.setLanguage(lang);
|
|
468
|
+
return parser;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// packages/retrieval/src/symbols/extract.ts
|
|
472
|
+
function name(n) {
|
|
473
|
+
return n.childForFieldName("name")?.text ?? null;
|
|
474
|
+
}
|
|
475
|
+
function endRowOf(n) {
|
|
476
|
+
return n.endPosition.column === 0 && n.endPosition.row > n.startPosition.row ? n.endPosition.row - 1 : n.endPosition.row;
|
|
477
|
+
}
|
|
478
|
+
function unitOf(n, kind, symbol, members = []) {
|
|
479
|
+
return { kind, symbol, startRow: n.startPosition.row, endRow: endRowOf(n), members };
|
|
480
|
+
}
|
|
481
|
+
function firstStringArg(call) {
|
|
482
|
+
const args = call.childForFieldName("arguments");
|
|
483
|
+
const s = args?.namedChildren.find((c) => c.type === "string" || c.type === "template_string");
|
|
484
|
+
return s ? s.text.replace(/^['"`]|['"`]$/g, "") : null;
|
|
485
|
+
}
|
|
486
|
+
var TEST_FNS = /* @__PURE__ */ new Set(["describe", "it", "test", "context", "suite"]);
|
|
487
|
+
function jsMembers(body, parent) {
|
|
488
|
+
if (!body) return [];
|
|
489
|
+
const out = [];
|
|
490
|
+
for (const m of body.namedChildren) {
|
|
491
|
+
if (m.type === "method_definition" || m.type === "abstract_method_signature" || m.type === "method_signature") {
|
|
492
|
+
const n = name(m);
|
|
493
|
+
out.push(unitOf(m, "method", parent && n ? `${parent}/${n}` : n));
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
return out;
|
|
497
|
+
}
|
|
498
|
+
var JS_DECLARATIONS = {
|
|
499
|
+
class_declaration: "class",
|
|
500
|
+
abstract_class_declaration: "class",
|
|
501
|
+
interface_declaration: "interface",
|
|
502
|
+
type_alias_declaration: "type",
|
|
503
|
+
enum_declaration: "enum",
|
|
504
|
+
function_declaration: "function",
|
|
505
|
+
generator_function_declaration: "function",
|
|
506
|
+
/** `declare function f(): T;` in .d.ts files. */
|
|
507
|
+
function_signature: "function"
|
|
508
|
+
};
|
|
509
|
+
var FUNCTION_VALUES = /* @__PURE__ */ new Set(["arrow_function", "function", "function_expression"]);
|
|
510
|
+
function jsFunctionVariable(n) {
|
|
511
|
+
const decl = n.namedChildren.find((c) => c.type === "variable_declarator");
|
|
512
|
+
const value = decl?.childForFieldName("value");
|
|
513
|
+
if (!value || !FUNCTION_VALUES.has(value.type)) return null;
|
|
514
|
+
return { kind: "function", symbol: decl ? name(decl) : null };
|
|
515
|
+
}
|
|
516
|
+
function jsTestCall(n) {
|
|
517
|
+
const call = n.namedChildren[0];
|
|
518
|
+
if (call?.type !== "call_expression") return null;
|
|
519
|
+
const fn = call.childForFieldName("function")?.text ?? "";
|
|
520
|
+
return TEST_FNS.has(fn.split(".")[0] ?? "") ? { kind: "test", symbol: firstStringArg(call) } : null;
|
|
521
|
+
}
|
|
522
|
+
function jsUnwrap(n) {
|
|
523
|
+
const exported = n.type === "export_statement" ? n.childForFieldName("declaration") ?? n : n;
|
|
524
|
+
return exported.type === "ambient_declaration" ? exported.namedChildren[0] ?? exported : exported;
|
|
525
|
+
}
|
|
526
|
+
var JS_RULES = {
|
|
527
|
+
unwrap: jsUnwrap,
|
|
528
|
+
classify: (n) => {
|
|
529
|
+
const kind = JS_DECLARATIONS[n.type];
|
|
530
|
+
if (kind) return { kind, symbol: name(n) };
|
|
531
|
+
if (n.type === "lexical_declaration" || n.type === "variable_declaration") {
|
|
532
|
+
return jsFunctionVariable(n);
|
|
533
|
+
}
|
|
534
|
+
return n.type === "expression_statement" ? jsTestCall(n) : null;
|
|
535
|
+
},
|
|
536
|
+
members: (n, parent) => n.type.endsWith("class_declaration") ? jsMembers(n.childForFieldName("body"), parent) : []
|
|
537
|
+
};
|
|
538
|
+
var PY_RULES = {
|
|
539
|
+
unwrap: (n) => n.type === "decorated_definition" ? n.childForFieldName("definition") ?? n : n,
|
|
540
|
+
classify: (n) => {
|
|
541
|
+
if (n.type === "class_definition") return { kind: "class", symbol: name(n) };
|
|
542
|
+
if (n.type === "function_definition") return { kind: "function", symbol: name(n) };
|
|
543
|
+
return null;
|
|
544
|
+
},
|
|
545
|
+
members: (n, parent) => {
|
|
546
|
+
if (n.type !== "class_definition") return [];
|
|
547
|
+
const body = n.childForFieldName("body");
|
|
548
|
+
const out = [];
|
|
549
|
+
for (const raw of body?.namedChildren ?? []) {
|
|
550
|
+
const m = PY_RULES.unwrap(raw);
|
|
551
|
+
if (m.type === "function_definition") {
|
|
552
|
+
const nm = name(m);
|
|
553
|
+
out.push(unitOf(raw, "method", parent && nm ? `${parent}/${nm}` : nm));
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
return out;
|
|
557
|
+
}
|
|
558
|
+
};
|
|
559
|
+
function goMethod(n) {
|
|
560
|
+
const receiver = n.childForFieldName("receiver")?.namedChildren[0];
|
|
561
|
+
const receiverType = receiver?.childForFieldName("type")?.text.replace(/^\*/, "") ?? null;
|
|
562
|
+
const method = name(n);
|
|
563
|
+
return { kind: "method", symbol: receiverType && method ? `${receiverType}/${method}` : method };
|
|
564
|
+
}
|
|
565
|
+
var GO_TYPE_KINDS = {
|
|
566
|
+
struct_type: "struct",
|
|
567
|
+
interface_type: "interface"
|
|
568
|
+
};
|
|
569
|
+
function goType(n) {
|
|
570
|
+
const spec = n.namedChildren.find((c) => c.type === "type_spec");
|
|
571
|
+
const shape = spec?.childForFieldName("type")?.type ?? "";
|
|
572
|
+
return { kind: GO_TYPE_KINDS[shape] ?? "type", symbol: spec ? name(spec) : null };
|
|
573
|
+
}
|
|
574
|
+
var GO_RULES = {
|
|
575
|
+
unwrap: (n) => n,
|
|
576
|
+
classify: (n) => {
|
|
577
|
+
if (n.type === "function_declaration") return { kind: "function", symbol: name(n) };
|
|
578
|
+
if (n.type === "method_declaration") return goMethod(n);
|
|
579
|
+
return n.type === "type_declaration" ? goType(n) : null;
|
|
580
|
+
},
|
|
581
|
+
members: () => []
|
|
582
|
+
};
|
|
583
|
+
var JAVA_RULES = {
|
|
584
|
+
unwrap: (n) => n,
|
|
585
|
+
classify: (n) => {
|
|
586
|
+
if (n.type === "class_declaration") return { kind: "class", symbol: name(n) };
|
|
587
|
+
if (n.type === "interface_declaration") return { kind: "interface", symbol: name(n) };
|
|
588
|
+
if (n.type === "enum_declaration") return { kind: "enum", symbol: name(n) };
|
|
589
|
+
if (n.type === "record_declaration") return { kind: "class", symbol: name(n) };
|
|
590
|
+
return null;
|
|
591
|
+
},
|
|
592
|
+
members: (n, parent) => {
|
|
593
|
+
const body = n.childForFieldName("body");
|
|
594
|
+
const out = [];
|
|
595
|
+
for (const m of body?.namedChildren ?? []) {
|
|
596
|
+
if (m.type === "method_declaration" || m.type === "constructor_declaration") {
|
|
597
|
+
const nm = name(m);
|
|
598
|
+
out.push(unitOf(m, "method", parent && nm ? `${parent}/${nm}` : nm));
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
return out;
|
|
602
|
+
}
|
|
603
|
+
};
|
|
604
|
+
var RUST_DECLARATIONS = {
|
|
605
|
+
function_item: "function",
|
|
606
|
+
struct_item: "struct",
|
|
607
|
+
enum_item: "enum",
|
|
608
|
+
trait_item: "trait",
|
|
609
|
+
mod_item: "module"
|
|
610
|
+
};
|
|
611
|
+
var RUST_RULES = {
|
|
612
|
+
unwrap: (n) => n,
|
|
613
|
+
classify: (n) => {
|
|
614
|
+
if (n.type === "impl_item") {
|
|
615
|
+
return { kind: "impl", symbol: n.childForFieldName("type")?.text ?? null };
|
|
616
|
+
}
|
|
617
|
+
const kind = RUST_DECLARATIONS[n.type];
|
|
618
|
+
return kind ? { kind, symbol: name(n) } : null;
|
|
619
|
+
},
|
|
620
|
+
members: (n, parent) => {
|
|
621
|
+
if (n.type !== "impl_item" && n.type !== "trait_item") return [];
|
|
622
|
+
const body = n.childForFieldName("body");
|
|
623
|
+
const out = [];
|
|
624
|
+
for (const m of body?.namedChildren ?? []) {
|
|
625
|
+
if (m.type === "function_item" || m.type === "function_signature_item") {
|
|
626
|
+
const nm = name(m);
|
|
627
|
+
out.push(unitOf(m, "method", parent && nm ? `${parent}/${nm}` : nm));
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
return out;
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
var RULES = {
|
|
634
|
+
typescript: JS_RULES,
|
|
635
|
+
tsx: JS_RULES,
|
|
636
|
+
javascript: JS_RULES,
|
|
637
|
+
python: PY_RULES,
|
|
638
|
+
go: GO_RULES,
|
|
639
|
+
java: JAVA_RULES,
|
|
640
|
+
rust: RUST_RULES
|
|
641
|
+
};
|
|
642
|
+
function supportsSymbols(language) {
|
|
643
|
+
return language !== null && language in RULES;
|
|
644
|
+
}
|
|
645
|
+
async function extractUnits(content, language) {
|
|
646
|
+
const rules = RULES[language];
|
|
647
|
+
if (!rules) throw new Error(`unsupported language ${language}`);
|
|
648
|
+
const parser = await createParser(language);
|
|
649
|
+
const tree = parser.parse(content);
|
|
650
|
+
try {
|
|
651
|
+
const units = [];
|
|
652
|
+
for (const top of tree.rootNode.namedChildren) {
|
|
653
|
+
const inner = rules.unwrap(top);
|
|
654
|
+
const cls = rules.classify(inner);
|
|
655
|
+
if (!cls) continue;
|
|
656
|
+
units.push(unitOf(top, cls.kind, cls.symbol, rules.members(inner, cls.symbol)));
|
|
657
|
+
}
|
|
658
|
+
return units.sort((a, b) => a.startRow - b.startRow);
|
|
659
|
+
} finally {
|
|
660
|
+
tree.delete();
|
|
661
|
+
parser.delete();
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
function signatureOf(lines, row) {
|
|
665
|
+
const line = (lines[row] ?? "").trim().replace(/\s*\{\s*$/, "");
|
|
666
|
+
return line.length > 200 ? `${line.slice(0, 197)}...` : line;
|
|
667
|
+
}
|
|
668
|
+
async function extractSymbols(content, language) {
|
|
669
|
+
const lines = content.split("\n");
|
|
670
|
+
const out = [];
|
|
671
|
+
const visit = (unit, parent) => {
|
|
672
|
+
if (unit.symbol) {
|
|
673
|
+
out.push({
|
|
674
|
+
namePath: unit.symbol,
|
|
675
|
+
name: unit.symbol.split("/").pop(),
|
|
676
|
+
kind: unit.kind,
|
|
677
|
+
startLine: unit.startRow + 1,
|
|
678
|
+
endLine: unit.endRow + 1,
|
|
679
|
+
signature: signatureOf(lines, unit.startRow),
|
|
680
|
+
parent
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
for (const member of unit.members) visit(member, unit.symbol);
|
|
684
|
+
};
|
|
685
|
+
for (const unit of await extractUnits(content, language)) visit(unit, null);
|
|
686
|
+
return out;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// packages/retrieval/src/code-index/code-index.ts
|
|
690
|
+
import { createHash } from "node:crypto";
|
|
691
|
+
import { mkdirSync, readFileSync as readFileSync2, statSync as statSync2 } from "node:fs";
|
|
692
|
+
import { basename as basename2, dirname, join as join3 } from "node:path";
|
|
693
|
+
|
|
694
|
+
// packages/retrieval/src/chunking/tokens.ts
|
|
695
|
+
var ApproxTokenCounter = class {
|
|
696
|
+
countTokens(text) {
|
|
697
|
+
return Promise.resolve(Math.ceil(text.length / 4));
|
|
698
|
+
}
|
|
699
|
+
};
|
|
700
|
+
|
|
701
|
+
// packages/retrieval/src/chunking/types.ts
|
|
702
|
+
var DEFAULT_LIMITS = { targetTokens: 350, maxTokens: 450, overlapTokens: 50 };
|
|
703
|
+
|
|
704
|
+
// packages/retrieval/src/chunking/line-chunker.ts
|
|
705
|
+
var FRAGMENT_CHARS = 1600;
|
|
706
|
+
var LineChunker = class {
|
|
707
|
+
constructor(counter, limits = DEFAULT_LIMITS) {
|
|
708
|
+
this.counter = counter;
|
|
709
|
+
this.limits = limits;
|
|
710
|
+
}
|
|
711
|
+
counter;
|
|
712
|
+
limits;
|
|
713
|
+
chunk(_path, content, language) {
|
|
714
|
+
return this.chunkLines(content.split("\n"), 1, language ?? "text", null);
|
|
715
|
+
}
|
|
716
|
+
async chunkLines(lines, firstLine, kind, symbol) {
|
|
717
|
+
const tokens = await Promise.all(lines.map((l) => this.counter.countTokens(l)));
|
|
718
|
+
const out = [];
|
|
719
|
+
let current = [];
|
|
720
|
+
let currentTokens = 0;
|
|
721
|
+
const emit = (indices) => {
|
|
722
|
+
if (indices.length === 0) return;
|
|
723
|
+
const text = indices.map((i) => lines[i] ?? "").join("\n");
|
|
724
|
+
if (text.trim().length === 0) return;
|
|
725
|
+
out.push({
|
|
726
|
+
kind,
|
|
727
|
+
symbol,
|
|
728
|
+
startLine: firstLine + indices[0],
|
|
729
|
+
endLine: firstLine + indices[indices.length - 1],
|
|
730
|
+
content: text
|
|
731
|
+
});
|
|
732
|
+
};
|
|
733
|
+
for (let i = 0; i < lines.length; i++) {
|
|
734
|
+
const t = tokens[i] ?? 0;
|
|
735
|
+
if (t > this.limits.maxTokens) {
|
|
736
|
+
emit(current);
|
|
737
|
+
current = [];
|
|
738
|
+
currentTokens = 0;
|
|
739
|
+
const line = lines[i] ?? "";
|
|
740
|
+
for (let off = 0; off < line.length; off += FRAGMENT_CHARS) {
|
|
741
|
+
out.push({
|
|
742
|
+
kind: "fragment",
|
|
743
|
+
symbol,
|
|
744
|
+
startLine: firstLine + i,
|
|
745
|
+
endLine: firstLine + i,
|
|
746
|
+
content: line.slice(off, off + FRAGMENT_CHARS)
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
continue;
|
|
750
|
+
}
|
|
751
|
+
if (currentTokens + t > this.limits.targetTokens && current.length > 0) {
|
|
752
|
+
emit(current);
|
|
753
|
+
const overlap = [];
|
|
754
|
+
let overlapTokens = 0;
|
|
755
|
+
for (let j = current.length - 1; j >= 0; j--) {
|
|
756
|
+
const idx = current[j];
|
|
757
|
+
const lt = tokens[idx] ?? 0;
|
|
758
|
+
if (overlapTokens + lt > this.limits.overlapTokens) break;
|
|
759
|
+
overlap.unshift(idx);
|
|
760
|
+
overlapTokens += lt;
|
|
761
|
+
}
|
|
762
|
+
current = overlap;
|
|
763
|
+
currentTokens = overlapTokens;
|
|
764
|
+
}
|
|
765
|
+
current.push(i);
|
|
766
|
+
currentTokens += t;
|
|
767
|
+
}
|
|
768
|
+
emit(current);
|
|
769
|
+
return out;
|
|
770
|
+
}
|
|
771
|
+
};
|
|
772
|
+
|
|
773
|
+
// packages/retrieval/src/chunking/markdown-chunker.ts
|
|
774
|
+
var HEADING = /^#{1,6}\s+(.*)$/;
|
|
775
|
+
var MarkdownChunker = class {
|
|
776
|
+
constructor(counter, line, limits = DEFAULT_LIMITS) {
|
|
777
|
+
this.counter = counter;
|
|
778
|
+
this.line = line;
|
|
779
|
+
this.limits = limits;
|
|
780
|
+
}
|
|
781
|
+
counter;
|
|
782
|
+
line;
|
|
783
|
+
limits;
|
|
784
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
785
|
+
async chunk(_path, content, _language) {
|
|
786
|
+
const lines = content.split("\n");
|
|
787
|
+
const sections = [];
|
|
788
|
+
let current = { symbol: null, start: 0, end: -1 };
|
|
789
|
+
for (let i = 0; i < lines.length; i++) {
|
|
790
|
+
const m = HEADING.exec(lines[i] ?? "");
|
|
791
|
+
if (m) {
|
|
792
|
+
current.end = i - 1;
|
|
793
|
+
if (current.end >= current.start) sections.push(current);
|
|
794
|
+
current = { symbol: m[1].trim(), start: i, end: -1 };
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
current.end = lines.length - 1;
|
|
798
|
+
if (current.end >= current.start) sections.push(current);
|
|
799
|
+
const out = [];
|
|
800
|
+
for (const s of sections) {
|
|
801
|
+
const body = lines.slice(s.start, s.end + 1);
|
|
802
|
+
if (body.join("\n").trim().length === 0) continue;
|
|
803
|
+
const tokens = await this.counter.countTokens(body.join("\n"));
|
|
804
|
+
if (tokens <= this.limits.maxTokens) {
|
|
805
|
+
out.push({
|
|
806
|
+
kind: "section",
|
|
807
|
+
symbol: s.symbol,
|
|
808
|
+
startLine: s.start + 1,
|
|
809
|
+
endLine: s.end + 1,
|
|
810
|
+
content: body.join("\n")
|
|
811
|
+
});
|
|
812
|
+
} else {
|
|
813
|
+
out.push(...await this.line.chunkLines(body, s.start + 1, "section", s.symbol));
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
return out;
|
|
817
|
+
}
|
|
818
|
+
};
|
|
819
|
+
|
|
820
|
+
// packages/retrieval/src/chunking/tree-sitter-chunker.ts
|
|
821
|
+
var TreeSitterChunker = class {
|
|
822
|
+
constructor(counter, line, limits = DEFAULT_LIMITS) {
|
|
823
|
+
this.counter = counter;
|
|
824
|
+
this.line = line;
|
|
825
|
+
this.limits = limits;
|
|
826
|
+
}
|
|
827
|
+
counter;
|
|
828
|
+
line;
|
|
829
|
+
limits;
|
|
830
|
+
supports(language) {
|
|
831
|
+
return supportsSymbols(language);
|
|
832
|
+
}
|
|
833
|
+
async chunk(_path, content, language) {
|
|
834
|
+
if (!this.supports(language)) throw new Error(`unsupported language ${String(language)}`);
|
|
835
|
+
const units = await extractUnits(content, language);
|
|
836
|
+
const lines = content.split("\n");
|
|
837
|
+
const out = [];
|
|
838
|
+
let cursor = 0;
|
|
839
|
+
for (const u of units) {
|
|
840
|
+
if (u.startRow > cursor) out.push(...await this.gap(lines, cursor, u.startRow - 1));
|
|
841
|
+
out.push(...await this.unitChunks(lines, u));
|
|
842
|
+
cursor = Math.max(cursor, u.endRow + 1);
|
|
843
|
+
}
|
|
844
|
+
if (cursor <= lines.length - 1) out.push(...await this.gap(lines, cursor, lines.length - 1));
|
|
845
|
+
return out.sort((a, b) => a.startLine - b.startLine || a.endLine - b.endLine);
|
|
846
|
+
}
|
|
847
|
+
async gap(lines, startRow, endRow) {
|
|
848
|
+
let start = startRow;
|
|
849
|
+
let end = endRow;
|
|
850
|
+
while (start <= end && (lines[start] ?? "").trim().length === 0) start++;
|
|
851
|
+
while (end >= start && (lines[end] ?? "").trim().length === 0) end--;
|
|
852
|
+
if (start > end) return [];
|
|
853
|
+
const slice = lines.slice(start, end + 1);
|
|
854
|
+
return this.line.chunkLines(slice, start + 1, "module", null);
|
|
855
|
+
}
|
|
856
|
+
async unitChunks(lines, u) {
|
|
857
|
+
const text = lines.slice(u.startRow, u.endRow + 1).join("\n");
|
|
858
|
+
const tokens = await this.counter.countTokens(text);
|
|
859
|
+
if (tokens <= this.limits.maxTokens) {
|
|
860
|
+
return [
|
|
861
|
+
{
|
|
862
|
+
kind: u.kind,
|
|
863
|
+
symbol: u.symbol,
|
|
864
|
+
startLine: u.startRow + 1,
|
|
865
|
+
endLine: u.endRow + 1,
|
|
866
|
+
content: text
|
|
867
|
+
}
|
|
868
|
+
];
|
|
869
|
+
}
|
|
870
|
+
if (u.members.length === 0)
|
|
871
|
+
return this.line.chunkLines(
|
|
872
|
+
lines.slice(u.startRow, u.endRow + 1),
|
|
873
|
+
u.startRow + 1,
|
|
874
|
+
u.kind,
|
|
875
|
+
u.symbol
|
|
876
|
+
);
|
|
877
|
+
const out = [];
|
|
878
|
+
const covered = /* @__PURE__ */ new Set();
|
|
879
|
+
for (const m of u.members) {
|
|
880
|
+
for (let r = m.startRow; r <= m.endRow; r++) covered.add(r);
|
|
881
|
+
const mText = lines.slice(m.startRow, m.endRow + 1).join("\n");
|
|
882
|
+
const mTokens = await this.counter.countTokens(mText);
|
|
883
|
+
if (mTokens <= this.limits.maxTokens)
|
|
884
|
+
out.push({
|
|
885
|
+
kind: m.kind,
|
|
886
|
+
symbol: m.symbol,
|
|
887
|
+
startLine: m.startRow + 1,
|
|
888
|
+
endLine: m.endRow + 1,
|
|
889
|
+
content: mText
|
|
890
|
+
});
|
|
891
|
+
else
|
|
892
|
+
out.push(
|
|
893
|
+
...await this.line.chunkLines(
|
|
894
|
+
lines.slice(m.startRow, m.endRow + 1),
|
|
895
|
+
m.startRow + 1,
|
|
896
|
+
m.kind,
|
|
897
|
+
m.symbol
|
|
898
|
+
)
|
|
899
|
+
);
|
|
900
|
+
}
|
|
901
|
+
const shellRows = [];
|
|
902
|
+
for (let r = u.startRow; r <= u.endRow; r++) if (!covered.has(r)) shellRows.push(r);
|
|
903
|
+
const shellLines = shellRows.map((r) => lines[r] ?? "");
|
|
904
|
+
if (shellLines.some((l) => l.trim().length > 0)) {
|
|
905
|
+
const first = shellRows[0];
|
|
906
|
+
const shell = await this.line.chunkLines(shellLines, first + 1, u.kind, u.symbol);
|
|
907
|
+
out.push(...shell.map((c) => ({ ...c, startLine: u.startRow + 1, endLine: u.endRow + 1 })));
|
|
908
|
+
}
|
|
909
|
+
return out;
|
|
910
|
+
}
|
|
911
|
+
};
|
|
912
|
+
|
|
913
|
+
// packages/retrieval/src/chunking/router.ts
|
|
914
|
+
var ChunkerRouter = class {
|
|
915
|
+
constructor(treeSitter, markdown, line) {
|
|
916
|
+
this.treeSitter = treeSitter;
|
|
917
|
+
this.markdown = markdown;
|
|
918
|
+
this.line = line;
|
|
919
|
+
}
|
|
920
|
+
treeSitter;
|
|
921
|
+
markdown;
|
|
922
|
+
line;
|
|
923
|
+
async chunk(path, content, language) {
|
|
924
|
+
if (language === "markdown") return this.markdown.chunk(path, content, language);
|
|
925
|
+
if (this.treeSitter.supports(language)) {
|
|
926
|
+
try {
|
|
927
|
+
return await this.treeSitter.chunk(path, content, language);
|
|
928
|
+
} catch {
|
|
929
|
+
return this.line.chunk(path, content, language);
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
return this.line.chunk(path, content, language ?? "text");
|
|
933
|
+
}
|
|
934
|
+
};
|
|
935
|
+
function createDefaultChunker(counter, limits = DEFAULT_LIMITS) {
|
|
936
|
+
const line = new LineChunker(counter, limits);
|
|
937
|
+
return new ChunkerRouter(
|
|
938
|
+
new TreeSitterChunker(counter, line, limits),
|
|
939
|
+
new MarkdownChunker(counter, line, limits),
|
|
940
|
+
line
|
|
941
|
+
);
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
// packages/retrieval/src/graph/import-extractor.ts
|
|
945
|
+
var JS_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript"]);
|
|
946
|
+
function stripQuotes(text) {
|
|
947
|
+
return text.replace(/^['"`]|['"`]$/g, "");
|
|
948
|
+
}
|
|
949
|
+
function jsSpecifiers(root) {
|
|
950
|
+
const out = [];
|
|
951
|
+
for (const top of root.namedChildren) {
|
|
952
|
+
if (top.type !== "import_statement" && top.type !== "export_statement") continue;
|
|
953
|
+
const source = top.childForFieldName("source");
|
|
954
|
+
if (source) out.push(stripQuotes(source.text));
|
|
955
|
+
}
|
|
956
|
+
return out;
|
|
957
|
+
}
|
|
958
|
+
function pythonSpecifiers(root) {
|
|
959
|
+
const out = [];
|
|
960
|
+
for (const top of root.namedChildren) {
|
|
961
|
+
if (top.type === "import_statement") {
|
|
962
|
+
for (const child of top.namedChildren) {
|
|
963
|
+
if (child.type === "dotted_name") out.push(child.text);
|
|
964
|
+
else if (child.type === "aliased_import") {
|
|
965
|
+
const dotted = child.namedChildren.find((c) => c.type === "dotted_name");
|
|
966
|
+
if (dotted) out.push(dotted.text);
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
} else if (top.type === "import_from_statement") {
|
|
970
|
+
const moduleName = top.childForFieldName("module_name");
|
|
971
|
+
if (moduleName) out.push(moduleName.text);
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
return out;
|
|
975
|
+
}
|
|
976
|
+
async function extractImports(content, language) {
|
|
977
|
+
if (language === null || !(JS_LANGUAGES.has(language) || language === "python")) return [];
|
|
978
|
+
const parser = await createParser(language);
|
|
979
|
+
try {
|
|
980
|
+
const tree = parser.parse(content);
|
|
981
|
+
try {
|
|
982
|
+
return JS_LANGUAGES.has(language) ? jsSpecifiers(tree.rootNode) : pythonSpecifiers(tree.rootNode);
|
|
983
|
+
} finally {
|
|
984
|
+
tree.delete();
|
|
985
|
+
}
|
|
986
|
+
} finally {
|
|
987
|
+
parser.delete();
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
// packages/retrieval/src/graph/pagerank.ts
|
|
992
|
+
function adjacency(graph, nodes) {
|
|
993
|
+
const outDegree = /* @__PURE__ */ new Map();
|
|
994
|
+
const incoming = new Map(nodes.map((node) => [node, []]));
|
|
995
|
+
for (const node of nodes) {
|
|
996
|
+
const targets = [...graph.edges.get(node) ?? []];
|
|
997
|
+
outDegree.set(node, targets.length);
|
|
998
|
+
for (const target of targets) incoming.get(target)?.push(node);
|
|
999
|
+
}
|
|
1000
|
+
return { outDegree, incoming };
|
|
1001
|
+
}
|
|
1002
|
+
function pagerank(graph, options = {}) {
|
|
1003
|
+
const d = options.dampingFactor ?? 0.85;
|
|
1004
|
+
const iterations = options.iterations ?? 20;
|
|
1005
|
+
const nodes = [...graph.nodes];
|
|
1006
|
+
const n = nodes.length;
|
|
1007
|
+
if (n === 0) return /* @__PURE__ */ new Map();
|
|
1008
|
+
const { outDegree, incoming } = adjacency(graph, nodes);
|
|
1009
|
+
let scores = new Map(nodes.map((node) => [node, 1 / n]));
|
|
1010
|
+
for (let iter = 0; iter < iterations; iter += 1) {
|
|
1011
|
+
const danglingSum = nodes.filter((node) => (outDegree.get(node) ?? 0) === 0).reduce((sum, node) => sum + (scores.get(node) ?? 0), 0);
|
|
1012
|
+
const next = /* @__PURE__ */ new Map();
|
|
1013
|
+
for (const node of nodes) {
|
|
1014
|
+
let incomingSum = 0;
|
|
1015
|
+
for (const source of incoming.get(node) ?? []) {
|
|
1016
|
+
const degree = outDegree.get(source) ?? 0;
|
|
1017
|
+
if (degree > 0) incomingSum += (scores.get(source) ?? 0) / degree;
|
|
1018
|
+
}
|
|
1019
|
+
next.set(node, (1 - d) / n + d * (danglingSum / n + incomingSum));
|
|
1020
|
+
}
|
|
1021
|
+
scores = next;
|
|
1022
|
+
}
|
|
1023
|
+
return scores;
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
// packages/retrieval/src/graph/symbol-graph.ts
|
|
1027
|
+
import { posix } from "node:path";
|
|
1028
|
+
var JS_SUFFIXES = ["", ".ts", ".tsx", ".js", ".jsx"];
|
|
1029
|
+
var JS_INDEX_SUFFIXES = ["/index.ts", "/index.tsx", "/index.js", "/index.jsx"];
|
|
1030
|
+
var PY_SUFFIXES = ["", ".py", "/__init__.py"];
|
|
1031
|
+
function pythonSpecifierToRelativePath(specifier) {
|
|
1032
|
+
const dots = /^\.+/.exec(specifier)?.[0].length ?? 0;
|
|
1033
|
+
const rest = specifier.slice(dots).replace(/\./g, "/");
|
|
1034
|
+
const prefix = dots <= 1 ? "./" : "../".repeat(dots - 1);
|
|
1035
|
+
return rest.length > 0 ? `${prefix}${rest}` : prefix.replace(/\/$/, "") || ".";
|
|
1036
|
+
}
|
|
1037
|
+
function withoutJsExtension(path) {
|
|
1038
|
+
const match = /\.jsx?$/.exec(path);
|
|
1039
|
+
return match ? path.slice(0, -match[0].length) : null;
|
|
1040
|
+
}
|
|
1041
|
+
function resolveOne(fromPath, relativeSpecifier, known) {
|
|
1042
|
+
const base = posix.dirname(fromPath);
|
|
1043
|
+
const joined = posix.normalize(posix.join(base, relativeSpecifier));
|
|
1044
|
+
const suffixes = [...JS_SUFFIXES, ...JS_INDEX_SUFFIXES, ...PY_SUFFIXES];
|
|
1045
|
+
for (const suffix of suffixes) {
|
|
1046
|
+
const candidate = suffix ? `${joined}${suffix}` : joined;
|
|
1047
|
+
if (known.has(candidate)) return candidate;
|
|
1048
|
+
}
|
|
1049
|
+
const withoutExt = withoutJsExtension(joined);
|
|
1050
|
+
if (withoutExt) {
|
|
1051
|
+
for (const candidate of [`${withoutExt}.ts`, `${withoutExt}.tsx`]) {
|
|
1052
|
+
if (known.has(candidate)) return candidate;
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
return null;
|
|
1056
|
+
}
|
|
1057
|
+
function resolveSpecifier(fromPath, specifier, language, known) {
|
|
1058
|
+
if (language === "python") {
|
|
1059
|
+
if (!specifier.startsWith(".")) return null;
|
|
1060
|
+
return resolveOne(fromPath, pythonSpecifierToRelativePath(specifier), known);
|
|
1061
|
+
}
|
|
1062
|
+
if (!specifier.startsWith(".")) return null;
|
|
1063
|
+
return resolveOne(fromPath, specifier, known);
|
|
1064
|
+
}
|
|
1065
|
+
function buildFileGraph(files) {
|
|
1066
|
+
const known = new Set(files.map((f) => f.path));
|
|
1067
|
+
const edges = /* @__PURE__ */ new Map();
|
|
1068
|
+
for (const file of files) {
|
|
1069
|
+
const targets = /* @__PURE__ */ new Set();
|
|
1070
|
+
for (const specifier of file.imports) {
|
|
1071
|
+
const resolved = resolveSpecifier(file.path, specifier, file.language, known);
|
|
1072
|
+
if (resolved && resolved !== file.path) targets.add(resolved);
|
|
1073
|
+
}
|
|
1074
|
+
edges.set(file.path, targets);
|
|
1075
|
+
}
|
|
1076
|
+
return { nodes: known, edges };
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
// packages/retrieval/src/lexical/identifiers.ts
|
|
1080
|
+
var KEYWORDS = /* @__PURE__ */ new Set([
|
|
1081
|
+
"const",
|
|
1082
|
+
"let",
|
|
1083
|
+
"var",
|
|
1084
|
+
"function",
|
|
1085
|
+
"return",
|
|
1086
|
+
"import",
|
|
1087
|
+
"export",
|
|
1088
|
+
"from",
|
|
1089
|
+
"class",
|
|
1090
|
+
"this",
|
|
1091
|
+
"new",
|
|
1092
|
+
"if",
|
|
1093
|
+
"else",
|
|
1094
|
+
"for",
|
|
1095
|
+
"while",
|
|
1096
|
+
"true",
|
|
1097
|
+
"false",
|
|
1098
|
+
"null",
|
|
1099
|
+
"undefined",
|
|
1100
|
+
"async",
|
|
1101
|
+
"await",
|
|
1102
|
+
"public",
|
|
1103
|
+
"private",
|
|
1104
|
+
"protected",
|
|
1105
|
+
"static",
|
|
1106
|
+
"void",
|
|
1107
|
+
"string",
|
|
1108
|
+
"number",
|
|
1109
|
+
"boolean",
|
|
1110
|
+
"def",
|
|
1111
|
+
"self",
|
|
1112
|
+
"pub",
|
|
1113
|
+
"mut",
|
|
1114
|
+
"impl",
|
|
1115
|
+
"struct",
|
|
1116
|
+
"enum",
|
|
1117
|
+
"use",
|
|
1118
|
+
"package",
|
|
1119
|
+
"func",
|
|
1120
|
+
"type",
|
|
1121
|
+
"interface",
|
|
1122
|
+
"extends",
|
|
1123
|
+
"implements",
|
|
1124
|
+
"default",
|
|
1125
|
+
"switch",
|
|
1126
|
+
"case",
|
|
1127
|
+
"break",
|
|
1128
|
+
"continue",
|
|
1129
|
+
"try",
|
|
1130
|
+
"catch",
|
|
1131
|
+
"finally",
|
|
1132
|
+
"throw",
|
|
1133
|
+
"typeof",
|
|
1134
|
+
"instanceof"
|
|
1135
|
+
]);
|
|
1136
|
+
var MAX = 200;
|
|
1137
|
+
function identifiersOf(text) {
|
|
1138
|
+
const out = [];
|
|
1139
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1140
|
+
const push = (t) => {
|
|
1141
|
+
if (t.length < 3 || KEYWORDS.has(t) || seen.has(t) || out.length >= MAX) return;
|
|
1142
|
+
seen.add(t);
|
|
1143
|
+
out.push(t);
|
|
1144
|
+
};
|
|
1145
|
+
for (const id of text.match(/[A-Za-z_][A-Za-z0-9_]*/g) ?? []) {
|
|
1146
|
+
push(id.toLowerCase());
|
|
1147
|
+
const parts = splitIdentifier(id);
|
|
1148
|
+
if (parts.length > 1) for (const p of parts) push(p);
|
|
1149
|
+
}
|
|
1150
|
+
return out.join(" ");
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
// packages/retrieval/src/scanner/rules.ts
|
|
1154
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
1155
|
+
import { join } from "node:path";
|
|
1156
|
+
var import_ignore = __toESM(require_ignore(), 1);
|
|
1157
|
+
var DEFAULT_IGNORED_DIRS = [
|
|
1158
|
+
".git",
|
|
1159
|
+
"node_modules",
|
|
1160
|
+
"dist",
|
|
1161
|
+
"build",
|
|
1162
|
+
"coverage",
|
|
1163
|
+
"target",
|
|
1164
|
+
"vendor",
|
|
1165
|
+
".next",
|
|
1166
|
+
".cache",
|
|
1167
|
+
".yandecode"
|
|
1168
|
+
];
|
|
1169
|
+
var SECRET_PATTERNS = [
|
|
1170
|
+
".env",
|
|
1171
|
+
".env.*",
|
|
1172
|
+
"*.pem",
|
|
1173
|
+
"*.key",
|
|
1174
|
+
"id_rsa",
|
|
1175
|
+
"id_ed25519",
|
|
1176
|
+
// Data/config files only: `secrets.ts` or `credentials.py` are source code and must stay indexed.
|
|
1177
|
+
...["credentials", "secrets"].flatMap(
|
|
1178
|
+
(base) => ["json", "yml", "yaml", "toml", "ini", "env", "txt"].map((ext) => `${base}.${ext}`)
|
|
1179
|
+
)
|
|
1180
|
+
];
|
|
1181
|
+
var MAX_FILE_BYTES = 512 * 1024;
|
|
1182
|
+
function isProbablyBinary(buf) {
|
|
1183
|
+
return buf.subarray(0, 8192).includes(0);
|
|
1184
|
+
}
|
|
1185
|
+
function buildIgnore(root) {
|
|
1186
|
+
const ig = (0, import_ignore.default)();
|
|
1187
|
+
ig.add(DEFAULT_IGNORED_DIRS.map((dir) => `${dir}/`));
|
|
1188
|
+
ig.add([...SECRET_PATTERNS]);
|
|
1189
|
+
ig.add(`/${CONFIG_FILENAME}`);
|
|
1190
|
+
const custom = join(root, ".yandecodeignore");
|
|
1191
|
+
if (existsSync(custom)) ig.add(readFileSync(custom, "utf8"));
|
|
1192
|
+
return ig;
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
// packages/retrieval/src/scanner/scanner.ts
|
|
1196
|
+
import { spawnSync } from "node:child_process";
|
|
1197
|
+
import { lstatSync, readdirSync, realpathSync, statSync } from "node:fs";
|
|
1198
|
+
import { join as join2, relative, sep } from "node:path";
|
|
1199
|
+
function toPosix(p) {
|
|
1200
|
+
return p.split(sep).join("/");
|
|
1201
|
+
}
|
|
1202
|
+
function isInsideRoot(root, target) {
|
|
1203
|
+
const rel = relative(root, target);
|
|
1204
|
+
return rel !== "" && !rel.startsWith("..") && !rel.startsWith(`..${sep}`);
|
|
1205
|
+
}
|
|
1206
|
+
function isGitRepo(root) {
|
|
1207
|
+
const r = spawnSync("git", ["rev-parse", "--is-inside-work-tree"], {
|
|
1208
|
+
cwd: root,
|
|
1209
|
+
encoding: "utf8"
|
|
1210
|
+
});
|
|
1211
|
+
return r.status === 0 && r.stdout.trim() === "true";
|
|
1212
|
+
}
|
|
1213
|
+
function listViaGit(root) {
|
|
1214
|
+
const r = spawnSync(
|
|
1215
|
+
"git",
|
|
1216
|
+
["-C", root, "ls-files", "-z", "--cached", "--others", "--exclude-standard"],
|
|
1217
|
+
{
|
|
1218
|
+
encoding: "utf8",
|
|
1219
|
+
maxBuffer: 64 * 1024 * 1024
|
|
1220
|
+
}
|
|
1221
|
+
);
|
|
1222
|
+
return r.stdout.split("\0").filter((p) => p.length > 0).map(toPosix);
|
|
1223
|
+
}
|
|
1224
|
+
function listViaWalk(root) {
|
|
1225
|
+
const out = [];
|
|
1226
|
+
const stack = [root];
|
|
1227
|
+
while (stack.length > 0) {
|
|
1228
|
+
const dir = stack.pop();
|
|
1229
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
1230
|
+
if (entry.isDirectory()) {
|
|
1231
|
+
if (DEFAULT_IGNORED_DIRS.includes(entry.name)) continue;
|
|
1232
|
+
stack.push(join2(dir, entry.name));
|
|
1233
|
+
continue;
|
|
1234
|
+
}
|
|
1235
|
+
out.push(toPosix(relative(root, join2(dir, entry.name))));
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
return out;
|
|
1239
|
+
}
|
|
1240
|
+
function listCandidatePaths(root) {
|
|
1241
|
+
const raw = isGitRepo(root) ? listViaGit(root) : listViaWalk(root);
|
|
1242
|
+
const ig = buildIgnore(root);
|
|
1243
|
+
const kept = [];
|
|
1244
|
+
for (const relPath of ig.filter(raw)) {
|
|
1245
|
+
const absPath = join2(root, relPath);
|
|
1246
|
+
const lst = lstatSync(absPath, { throwIfNoEntry: false });
|
|
1247
|
+
if (!lst) continue;
|
|
1248
|
+
if (lst.isSymbolicLink()) {
|
|
1249
|
+
let real;
|
|
1250
|
+
try {
|
|
1251
|
+
real = realpathSync(absPath);
|
|
1252
|
+
} catch {
|
|
1253
|
+
continue;
|
|
1254
|
+
}
|
|
1255
|
+
if (!isInsideRoot(root, real)) continue;
|
|
1256
|
+
}
|
|
1257
|
+
const st = statSync(absPath, { throwIfNoEntry: false });
|
|
1258
|
+
if (!st || !st.isFile()) continue;
|
|
1259
|
+
if (st.size > MAX_FILE_BYTES) continue;
|
|
1260
|
+
kept.push(relPath);
|
|
1261
|
+
}
|
|
1262
|
+
return kept;
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
// packages/retrieval/src/code-index/schema.ts
|
|
1266
|
+
var CODE_INDEX_MIGRATIONS = [
|
|
1267
|
+
`
|
|
1268
|
+
CREATE TABLE files (
|
|
1269
|
+
path TEXT PRIMARY KEY,
|
|
1270
|
+
language TEXT,
|
|
1271
|
+
size INTEGER NOT NULL,
|
|
1272
|
+
mtime_ms INTEGER NOT NULL,
|
|
1273
|
+
hash TEXT NOT NULL,
|
|
1274
|
+
indexed_at TEXT NOT NULL
|
|
1275
|
+
);
|
|
1276
|
+
CREATE TABLE symbols (
|
|
1277
|
+
id INTEGER PRIMARY KEY,
|
|
1278
|
+
path TEXT NOT NULL,
|
|
1279
|
+
name TEXT NOT NULL,
|
|
1280
|
+
name_path TEXT NOT NULL,
|
|
1281
|
+
kind TEXT NOT NULL,
|
|
1282
|
+
start_line INTEGER NOT NULL,
|
|
1283
|
+
end_line INTEGER NOT NULL,
|
|
1284
|
+
signature TEXT NOT NULL,
|
|
1285
|
+
parent TEXT
|
|
1286
|
+
);
|
|
1287
|
+
CREATE INDEX symbols_name ON symbols (name COLLATE NOCASE);
|
|
1288
|
+
CREATE INDEX symbols_name_path ON symbols (name_path);
|
|
1289
|
+
CREATE INDEX symbols_path ON symbols (path);
|
|
1290
|
+
CREATE TABLE chunks (
|
|
1291
|
+
id INTEGER PRIMARY KEY,
|
|
1292
|
+
path TEXT NOT NULL,
|
|
1293
|
+
start_line INTEGER NOT NULL,
|
|
1294
|
+
end_line INTEGER NOT NULL,
|
|
1295
|
+
symbol TEXT,
|
|
1296
|
+
content TEXT NOT NULL
|
|
1297
|
+
);
|
|
1298
|
+
CREATE INDEX chunks_path ON chunks (path);
|
|
1299
|
+
CREATE VIRTUAL TABLE chunks_fts USING fts5 (body, idents, path, tokenize = 'porter unicode61');
|
|
1300
|
+
CREATE TABLE imports (path TEXT NOT NULL, specifier TEXT NOT NULL);
|
|
1301
|
+
CREATE INDEX imports_path ON imports (path);
|
|
1302
|
+
CREATE TABLE edges (src TEXT NOT NULL, dst TEXT NOT NULL);
|
|
1303
|
+
CREATE INDEX edges_dst ON edges (dst);
|
|
1304
|
+
CREATE INDEX edges_src ON edges (src);
|
|
1305
|
+
CREATE TABLE centrality (path TEXT PRIMARY KEY, score REAL NOT NULL);
|
|
1306
|
+
`,
|
|
1307
|
+
// v2: split symbol-name words (findOrCreateBySso → " find or create by sso ") for
|
|
1308
|
+
// natural-language symbol matching; clearing files forces a full re-index to fill them.
|
|
1309
|
+
`
|
|
1310
|
+
ALTER TABLE symbols ADD COLUMN name_terms TEXT NOT NULL DEFAULT '';
|
|
1311
|
+
DELETE FROM files;
|
|
1312
|
+
DELETE FROM symbols;
|
|
1313
|
+
DELETE FROM chunks;
|
|
1314
|
+
DELETE FROM chunks_fts;
|
|
1315
|
+
DELETE FROM imports;
|
|
1316
|
+
`
|
|
1317
|
+
];
|
|
1318
|
+
|
|
1319
|
+
// packages/retrieval/src/code-index/code-index.ts
|
|
1320
|
+
var RELATIVE_SCORE_FLOOR = 0.25;
|
|
1321
|
+
var STOPWORDS = /* @__PURE__ */ new Set([
|
|
1322
|
+
"the",
|
|
1323
|
+
"and",
|
|
1324
|
+
"for",
|
|
1325
|
+
"how",
|
|
1326
|
+
"does",
|
|
1327
|
+
"what",
|
|
1328
|
+
"where",
|
|
1329
|
+
"why",
|
|
1330
|
+
"when",
|
|
1331
|
+
"which",
|
|
1332
|
+
"who",
|
|
1333
|
+
"are",
|
|
1334
|
+
"was",
|
|
1335
|
+
"with",
|
|
1336
|
+
"from",
|
|
1337
|
+
"into",
|
|
1338
|
+
"that",
|
|
1339
|
+
"this",
|
|
1340
|
+
"there",
|
|
1341
|
+
"then",
|
|
1342
|
+
"than",
|
|
1343
|
+
"can",
|
|
1344
|
+
"should",
|
|
1345
|
+
"returned"
|
|
1346
|
+
]);
|
|
1347
|
+
function stem(word) {
|
|
1348
|
+
const w = word.toLowerCase();
|
|
1349
|
+
const stemmed = w.replace(/(ies|ied)$/, "y").replace(/(ing|ed|es|s|e)$/, "");
|
|
1350
|
+
return stemmed.length >= 3 ? stemmed : w;
|
|
1351
|
+
}
|
|
1352
|
+
function nameTerms(namePath) {
|
|
1353
|
+
const words = namePath.split(/[/.]/).flatMap((part) => splitIdentifier(part));
|
|
1354
|
+
return ` ${[...new Set(words.filter((w) => w.length >= 3).map(stem))].join(" ")} `;
|
|
1355
|
+
}
|
|
1356
|
+
var PROSE_LANGUAGES = /* @__PURE__ */ new Set(["markdown"]);
|
|
1357
|
+
function toSymbol(row) {
|
|
1358
|
+
return {
|
|
1359
|
+
path: row.path,
|
|
1360
|
+
name: row.name,
|
|
1361
|
+
namePath: row.name_path,
|
|
1362
|
+
kind: row.kind,
|
|
1363
|
+
startLine: row.start_line,
|
|
1364
|
+
endLine: row.end_line,
|
|
1365
|
+
signature: row.signature,
|
|
1366
|
+
parent: row.parent
|
|
1367
|
+
};
|
|
1368
|
+
}
|
|
1369
|
+
function escapeLike(text) {
|
|
1370
|
+
return text.replace(/[\\%_]/g, (c) => `\\${c}`);
|
|
1371
|
+
}
|
|
1372
|
+
function escapeRegExp(text) {
|
|
1373
|
+
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1374
|
+
}
|
|
1375
|
+
var CodeIndex = class _CodeIndex {
|
|
1376
|
+
constructor(db, root) {
|
|
1377
|
+
this.db = db;
|
|
1378
|
+
this.root = root;
|
|
1379
|
+
}
|
|
1380
|
+
db;
|
|
1381
|
+
root;
|
|
1382
|
+
chunker = createDefaultChunker(new ApproxTokenCounter());
|
|
1383
|
+
static open(dbFile, root) {
|
|
1384
|
+
mkdirSync(dirname(dbFile), { recursive: true });
|
|
1385
|
+
return new _CodeIndex(openModuleDb(dbFile, CODE_INDEX_MIGRATIONS), root);
|
|
1386
|
+
}
|
|
1387
|
+
close() {
|
|
1388
|
+
this.db.close();
|
|
1389
|
+
}
|
|
1390
|
+
stats() {
|
|
1391
|
+
const count = (table) => this.db.prepare(`SELECT COUNT(*) AS n FROM ${table}`).get().n;
|
|
1392
|
+
return { files: count("files"), symbols: count("symbols"), chunks: count("chunks") };
|
|
1393
|
+
}
|
|
1394
|
+
async sync() {
|
|
1395
|
+
const started = Date.now();
|
|
1396
|
+
const known = new Map(
|
|
1397
|
+
this.db.prepare("SELECT path, size, mtime_ms, hash FROM files").all().map(
|
|
1398
|
+
(r) => [r.path, r]
|
|
1399
|
+
)
|
|
1400
|
+
);
|
|
1401
|
+
const report = {
|
|
1402
|
+
added: 0,
|
|
1403
|
+
changed: 0,
|
|
1404
|
+
removed: 0,
|
|
1405
|
+
unchanged: 0,
|
|
1406
|
+
durationMs: 0,
|
|
1407
|
+
parseFailures: []
|
|
1408
|
+
};
|
|
1409
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1410
|
+
const toIndex = [];
|
|
1411
|
+
for (const path of listCandidatePaths(this.root)) {
|
|
1412
|
+
const language = detectLanguage(path);
|
|
1413
|
+
if (language !== null && PROSE_LANGUAGES.has(language)) continue;
|
|
1414
|
+
const abs = join3(this.root, path);
|
|
1415
|
+
const st = statSync2(abs, { throwIfNoEntry: false });
|
|
1416
|
+
if (!st) continue;
|
|
1417
|
+
const previous = known.get(path);
|
|
1418
|
+
if (previous && previous.size === st.size && previous.mtime_ms === Math.trunc(st.mtimeMs)) {
|
|
1419
|
+
seen.add(path);
|
|
1420
|
+
report.unchanged++;
|
|
1421
|
+
continue;
|
|
1422
|
+
}
|
|
1423
|
+
const buffer = readFileSync2(abs);
|
|
1424
|
+
if (isProbablyBinary(buffer)) continue;
|
|
1425
|
+
seen.add(path);
|
|
1426
|
+
const hash = createHash("sha256").update(buffer).digest("hex");
|
|
1427
|
+
if (previous && previous.hash === hash) {
|
|
1428
|
+
this.db.prepare("UPDATE files SET size = ?, mtime_ms = ? WHERE path = ?").run(st.size, Math.trunc(st.mtimeMs), path);
|
|
1429
|
+
report.unchanged++;
|
|
1430
|
+
continue;
|
|
1431
|
+
}
|
|
1432
|
+
if (previous) report.changed++;
|
|
1433
|
+
else report.added++;
|
|
1434
|
+
toIndex.push({
|
|
1435
|
+
path,
|
|
1436
|
+
size: st.size,
|
|
1437
|
+
mtimeMs: Math.trunc(st.mtimeMs),
|
|
1438
|
+
hash,
|
|
1439
|
+
content: buffer.toString("utf8")
|
|
1440
|
+
});
|
|
1441
|
+
}
|
|
1442
|
+
const removed = [...known.keys()].filter((p) => !seen.has(p));
|
|
1443
|
+
report.removed = removed.length;
|
|
1444
|
+
const prepared = [];
|
|
1445
|
+
for (const file of toIndex) {
|
|
1446
|
+
const language = detectLanguage(file.path);
|
|
1447
|
+
let symbols = [];
|
|
1448
|
+
let imports = [];
|
|
1449
|
+
try {
|
|
1450
|
+
if (supportsSymbols(language)) symbols = await extractSymbols(file.content, language);
|
|
1451
|
+
imports = await extractImports(file.content, language);
|
|
1452
|
+
} catch (error) {
|
|
1453
|
+
report.parseFailures.push({ path: file.path, error: error.message });
|
|
1454
|
+
}
|
|
1455
|
+
const chunks = await this.chunker.chunk(file.path, file.content, language);
|
|
1456
|
+
prepared.push({ ...file, language, symbols, chunks, imports });
|
|
1457
|
+
}
|
|
1458
|
+
const deleteFile = (path) => {
|
|
1459
|
+
this.db.prepare("DELETE FROM symbols WHERE path = ?").run(path);
|
|
1460
|
+
this.db.prepare("DELETE FROM chunks_fts WHERE path = ?").run(path);
|
|
1461
|
+
this.db.prepare("DELETE FROM chunks WHERE path = ?").run(path);
|
|
1462
|
+
this.db.prepare("DELETE FROM imports WHERE path = ?").run(path);
|
|
1463
|
+
this.db.prepare("DELETE FROM files WHERE path = ?").run(path);
|
|
1464
|
+
};
|
|
1465
|
+
this.db.transaction(() => {
|
|
1466
|
+
for (const path of removed) deleteFile(path);
|
|
1467
|
+
const insertSymbol = this.db.prepare(
|
|
1468
|
+
"INSERT INTO symbols (path, name, name_path, kind, start_line, end_line, signature, parent, name_terms) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
|
1469
|
+
);
|
|
1470
|
+
const insertChunk = this.db.prepare(
|
|
1471
|
+
"INSERT INTO chunks (path, start_line, end_line, symbol, content) VALUES (?, ?, ?, ?, ?)"
|
|
1472
|
+
);
|
|
1473
|
+
const insertFts = this.db.prepare(
|
|
1474
|
+
"INSERT INTO chunks_fts (rowid, body, idents, path) VALUES (?, ?, ?, ?)"
|
|
1475
|
+
);
|
|
1476
|
+
const insertImport = this.db.prepare("INSERT INTO imports (path, specifier) VALUES (?, ?)");
|
|
1477
|
+
for (const file of prepared) {
|
|
1478
|
+
deleteFile(file.path);
|
|
1479
|
+
this.db.prepare(
|
|
1480
|
+
"INSERT INTO files (path, language, size, mtime_ms, hash, indexed_at) VALUES (?, ?, ?, ?, ?, ?)"
|
|
1481
|
+
).run(
|
|
1482
|
+
file.path,
|
|
1483
|
+
file.language,
|
|
1484
|
+
file.size,
|
|
1485
|
+
file.mtimeMs,
|
|
1486
|
+
file.hash,
|
|
1487
|
+
(/* @__PURE__ */ new Date()).toISOString()
|
|
1488
|
+
);
|
|
1489
|
+
for (const s of file.symbols) {
|
|
1490
|
+
insertSymbol.run(
|
|
1491
|
+
file.path,
|
|
1492
|
+
s.name,
|
|
1493
|
+
s.namePath,
|
|
1494
|
+
s.kind,
|
|
1495
|
+
s.startLine,
|
|
1496
|
+
s.endLine,
|
|
1497
|
+
s.signature,
|
|
1498
|
+
s.parent,
|
|
1499
|
+
nameTerms(s.namePath)
|
|
1500
|
+
);
|
|
1501
|
+
}
|
|
1502
|
+
for (const c of file.chunks) {
|
|
1503
|
+
const id = insertChunk.run(
|
|
1504
|
+
file.path,
|
|
1505
|
+
c.startLine,
|
|
1506
|
+
c.endLine,
|
|
1507
|
+
c.symbol,
|
|
1508
|
+
c.content
|
|
1509
|
+
).lastInsertRowid;
|
|
1510
|
+
const idents = `${identifiersOf(c.content)} ${c.symbol ? identifiersOf(c.symbol) : ""}`;
|
|
1511
|
+
insertFts.run(
|
|
1512
|
+
id,
|
|
1513
|
+
c.content,
|
|
1514
|
+
idents,
|
|
1515
|
+
`${file.path} ${identifiersOf(basename2(file.path))}`
|
|
1516
|
+
);
|
|
1517
|
+
}
|
|
1518
|
+
for (const spec of new Set(file.imports)) insertImport.run(file.path, spec);
|
|
1519
|
+
}
|
|
1520
|
+
if (prepared.length > 0 || removed.length > 0) this.rebuildGraph();
|
|
1521
|
+
})();
|
|
1522
|
+
report.durationMs = Date.now() - started;
|
|
1523
|
+
return report;
|
|
1524
|
+
}
|
|
1525
|
+
rebuildGraph() {
|
|
1526
|
+
const files = this.db.prepare("SELECT path, language FROM files").all();
|
|
1527
|
+
const importsByPath = /* @__PURE__ */ new Map();
|
|
1528
|
+
for (const row of this.db.prepare("SELECT path, specifier FROM imports").all()) {
|
|
1529
|
+
const list = importsByPath.get(row.path) ?? [];
|
|
1530
|
+
list.push(row.specifier);
|
|
1531
|
+
importsByPath.set(row.path, list);
|
|
1532
|
+
}
|
|
1533
|
+
const graph = buildFileGraph(
|
|
1534
|
+
files.map((f) => ({
|
|
1535
|
+
path: f.path,
|
|
1536
|
+
language: f.language,
|
|
1537
|
+
imports: importsByPath.get(f.path) ?? []
|
|
1538
|
+
}))
|
|
1539
|
+
);
|
|
1540
|
+
const ranks = pagerank(graph);
|
|
1541
|
+
this.db.prepare("DELETE FROM edges").run();
|
|
1542
|
+
this.db.prepare("DELETE FROM centrality").run();
|
|
1543
|
+
const insertEdge = this.db.prepare("INSERT INTO edges (src, dst) VALUES (?, ?)");
|
|
1544
|
+
for (const [src, targets] of graph.edges) for (const dst of targets) insertEdge.run(src, dst);
|
|
1545
|
+
const insertRank = this.db.prepare("INSERT INTO centrality (path, score) VALUES (?, ?)");
|
|
1546
|
+
for (const [path, score] of ranks) insertRank.run(path, score);
|
|
1547
|
+
}
|
|
1548
|
+
findSymbols(pattern, options = {}) {
|
|
1549
|
+
const limit = options.limit ?? 20;
|
|
1550
|
+
const clauses = [];
|
|
1551
|
+
const params = [];
|
|
1552
|
+
if (pattern.includes("/")) {
|
|
1553
|
+
if (options.substring) {
|
|
1554
|
+
clauses.push("name_path LIKE ? ESCAPE '\\'");
|
|
1555
|
+
params.push(`%${escapeLike(pattern)}%`);
|
|
1556
|
+
} else {
|
|
1557
|
+
clauses.push("(name_path = ? OR name_path LIKE ? ESCAPE '\\')");
|
|
1558
|
+
params.push(pattern, `%/${escapeLike(pattern)}`);
|
|
1559
|
+
}
|
|
1560
|
+
} else if (options.substring) {
|
|
1561
|
+
clauses.push("name LIKE ? ESCAPE '\\'");
|
|
1562
|
+
params.push(`%${escapeLike(pattern)}%`);
|
|
1563
|
+
} else {
|
|
1564
|
+
clauses.push("name = ? COLLATE NOCASE");
|
|
1565
|
+
params.push(pattern);
|
|
1566
|
+
}
|
|
1567
|
+
if (options.path) {
|
|
1568
|
+
clauses.push("(path = ? OR path LIKE ? ESCAPE '\\')");
|
|
1569
|
+
params.push(options.path, `${escapeLike(options.path.replace(/\/$/, ""))}/%`);
|
|
1570
|
+
}
|
|
1571
|
+
const rows = this.db.prepare(
|
|
1572
|
+
`SELECT s.* FROM symbols s LEFT JOIN centrality c ON c.path = s.path
|
|
1573
|
+
WHERE ${clauses.join(" AND ")}
|
|
1574
|
+
ORDER BY (s.name = ?) DESC, COALESCE(c.score, 0) DESC, s.path, s.start_line LIMIT ?`
|
|
1575
|
+
).all(...params, pattern, limit);
|
|
1576
|
+
return rows.map(toSymbol);
|
|
1577
|
+
}
|
|
1578
|
+
/**
|
|
1579
|
+
* Symbols whose name shares at least `minMatches` words with a natural-language query
|
|
1580
|
+
* ("how does SSO login create or find a user" → `UserRepository/findOrCreateBySso`).
|
|
1581
|
+
*/
|
|
1582
|
+
findSymbolsByTerms(query, options = {}) {
|
|
1583
|
+
const terms = [
|
|
1584
|
+
...new Set(
|
|
1585
|
+
(query.toLowerCase().match(/[a-z0-9]{3,}/g) ?? []).filter((w) => !STOPWORDS.has(w)).map(stem)
|
|
1586
|
+
)
|
|
1587
|
+
];
|
|
1588
|
+
if (terms.length === 0) return [];
|
|
1589
|
+
const rows = this.db.prepare(
|
|
1590
|
+
`SELECT s.*, COALESCE(c.score, 0) AS centrality FROM symbols s LEFT JOIN centrality c ON c.path = s.path
|
|
1591
|
+
WHERE ${terms.map(() => "instr(s.name_terms, ' ' || ? || ' ') > 0").join(" OR ")}`
|
|
1592
|
+
).all(...terms);
|
|
1593
|
+
return rows.map((row) => ({
|
|
1594
|
+
row,
|
|
1595
|
+
matches: terms.filter((t) => row.name_terms.includes(` ${t} `)).length
|
|
1596
|
+
})).filter((r) => r.matches >= (options.minMatches ?? 2)).sort(
|
|
1597
|
+
(a, b) => b.matches - a.matches || b.row.centrality - a.row.centrality || a.row.name_path.length - b.row.name_path.length
|
|
1598
|
+
).slice(0, options.limit ?? 3).map((r) => toSymbol(r.row));
|
|
1599
|
+
}
|
|
1600
|
+
outline(path) {
|
|
1601
|
+
return this.db.prepare("SELECT * FROM symbols WHERE path = ? ORDER BY start_line, end_line DESC").all(path).map(toSymbol);
|
|
1602
|
+
}
|
|
1603
|
+
definition(namePath, path) {
|
|
1604
|
+
const symbol = this.findSymbols(namePath, path ? { path, limit: 1 } : { limit: 1 })[0];
|
|
1605
|
+
if (!symbol) return null;
|
|
1606
|
+
const lines = readFileSync2(join3(this.root, symbol.path), "utf8").split("\n");
|
|
1607
|
+
return { ...symbol, body: lines.slice(symbol.startLine - 1, symbol.endLine).join("\n") };
|
|
1608
|
+
}
|
|
1609
|
+
/** Distinct file paths whose body matches a bare identifier (FTS), for reference scanning. */
|
|
1610
|
+
pathsMatchingBody(identifier) {
|
|
1611
|
+
if (!toFtsQuery(identifier)) return [];
|
|
1612
|
+
return this.db.prepare("SELECT DISTINCT path FROM chunks_fts WHERE chunks_fts MATCH ?").all(`body:"${identifier.replace(/"/g, "")}"`).map((r) => r.path.split(" ")[0]);
|
|
1613
|
+
}
|
|
1614
|
+
/**
|
|
1615
|
+
* Lines that mention the symbol's name as a whole identifier, outside its own definition.
|
|
1616
|
+
*
|
|
1617
|
+
* This is a lexical approximation (the precise path is `lsp_references`). To avoid grepping the
|
|
1618
|
+
* bare last token, a qualified name path is anchored to a real definition first — like Serena,
|
|
1619
|
+
* which resolves the symbol before finding references. An unresolvable qualified name returns
|
|
1620
|
+
* nothing rather than every occurrence of the method name, and when the name is qualified the
|
|
1621
|
+
* scan is narrowed to files related to the target (its definition files and files that mention
|
|
1622
|
+
* the qualifying class), which cuts false positives for common method names.
|
|
1623
|
+
*/
|
|
1624
|
+
references(namePath, limit = 50) {
|
|
1625
|
+
const name2 = namePath.split("/").pop();
|
|
1626
|
+
const qualifier = namePath.includes("/") ? namePath.split("/").slice(0, -1).pop() : null;
|
|
1627
|
+
const definitions = this.findSymbols(namePath, { limit: 50 });
|
|
1628
|
+
if (qualifier !== null && definitions.length === 0) return [];
|
|
1629
|
+
const query = toFtsQuery(name2);
|
|
1630
|
+
if (!query) return [];
|
|
1631
|
+
let candidatePaths = this.pathsMatchingBody(name2);
|
|
1632
|
+
if (qualifier !== null) {
|
|
1633
|
+
const related = /* @__PURE__ */ new Set([
|
|
1634
|
+
...definitions.map((d) => d.path),
|
|
1635
|
+
...this.pathsMatchingBody(qualifier)
|
|
1636
|
+
]);
|
|
1637
|
+
candidatePaths = candidatePaths.filter((p) => related.has(p));
|
|
1638
|
+
}
|
|
1639
|
+
const pattern = new RegExp(`\\b${escapeRegExp(name2)}\\b`);
|
|
1640
|
+
const out = [];
|
|
1641
|
+
for (const path of [...new Set(candidatePaths)].sort()) {
|
|
1642
|
+
let lines;
|
|
1643
|
+
try {
|
|
1644
|
+
lines = readFileSync2(join3(this.root, path), "utf8").split("\n");
|
|
1645
|
+
} catch {
|
|
1646
|
+
continue;
|
|
1647
|
+
}
|
|
1648
|
+
lines.forEach((text, i) => {
|
|
1649
|
+
const line = i + 1;
|
|
1650
|
+
const insideDefinition = definitions.some(
|
|
1651
|
+
(d) => d.path === path && line >= d.startLine && line <= d.endLine
|
|
1652
|
+
);
|
|
1653
|
+
if (!insideDefinition && pattern.test(text) && out.length < limit) {
|
|
1654
|
+
out.push({ path, line, text: text.trim().slice(0, 200) });
|
|
1655
|
+
}
|
|
1656
|
+
});
|
|
1657
|
+
}
|
|
1658
|
+
return out;
|
|
1659
|
+
}
|
|
1660
|
+
searchText(query, limit = 8) {
|
|
1661
|
+
const fts = toFtsQuery(query);
|
|
1662
|
+
if (!fts) return [];
|
|
1663
|
+
const rows = this.db.prepare(
|
|
1664
|
+
`SELECT c.path, c.start_line, c.end_line, c.symbol,
|
|
1665
|
+
snippet(chunks_fts, 0, '', '', ' \u2026 ', 16) AS snippet,
|
|
1666
|
+
bm25(chunks_fts, 1.0, 1.5, 0.5) AS rank, COALESCE(k.score, 0) AS centrality
|
|
1667
|
+
FROM chunks_fts JOIN chunks c ON c.id = chunks_fts.rowid
|
|
1668
|
+
LEFT JOIN centrality k ON k.path = c.path
|
|
1669
|
+
WHERE chunks_fts MATCH ? ORDER BY rank LIMIT ?`
|
|
1670
|
+
).all(fts, limit * 4);
|
|
1671
|
+
const scored = rows.map((r) => ({ ...r, score: -r.rank * (1 + Math.min(r.centrality * 5, 0.5)) })).sort((a, b) => b.score - a.score);
|
|
1672
|
+
const floor = (scored[0]?.score ?? 0) * RELATIVE_SCORE_FLOOR;
|
|
1673
|
+
const seenFiles = /* @__PURE__ */ new Set();
|
|
1674
|
+
const hits = [];
|
|
1675
|
+
for (const row of scored) {
|
|
1676
|
+
if (row.score < floor || seenFiles.has(row.path)) continue;
|
|
1677
|
+
seenFiles.add(row.path);
|
|
1678
|
+
hits.push({
|
|
1679
|
+
path: row.path,
|
|
1680
|
+
startLine: row.start_line,
|
|
1681
|
+
endLine: row.end_line,
|
|
1682
|
+
symbol: row.symbol,
|
|
1683
|
+
snippet: row.snippet.replace(/\s+/g, " ").trim().slice(0, 140),
|
|
1684
|
+
score: row.score
|
|
1685
|
+
});
|
|
1686
|
+
if (hits.length >= limit) break;
|
|
1687
|
+
}
|
|
1688
|
+
return hits;
|
|
1689
|
+
}
|
|
1690
|
+
/** Paths whose characters contain the query as a subsequence, best (tightest, shortest) first. */
|
|
1691
|
+
searchPaths(query, limit = 10) {
|
|
1692
|
+
const needle = query.toLowerCase().replace(/\s+/g, "");
|
|
1693
|
+
if (!needle) return [];
|
|
1694
|
+
const paths = this.db.prepare("SELECT path FROM files").all().map(
|
|
1695
|
+
(r) => r.path
|
|
1696
|
+
);
|
|
1697
|
+
const scored = [];
|
|
1698
|
+
for (const path of paths) {
|
|
1699
|
+
const hay = path.toLowerCase();
|
|
1700
|
+
const base = basename2(hay);
|
|
1701
|
+
if (hay.includes(needle)) {
|
|
1702
|
+
scored.push({ path, score: (base.includes(needle) ? 0 : 1e3) + path.length });
|
|
1703
|
+
continue;
|
|
1704
|
+
}
|
|
1705
|
+
let from = 0;
|
|
1706
|
+
let first = -1;
|
|
1707
|
+
let last = -1;
|
|
1708
|
+
let ok = true;
|
|
1709
|
+
for (const ch of needle) {
|
|
1710
|
+
const at = hay.indexOf(ch, from);
|
|
1711
|
+
if (at < 0) {
|
|
1712
|
+
ok = false;
|
|
1713
|
+
break;
|
|
1714
|
+
}
|
|
1715
|
+
if (first < 0) first = at;
|
|
1716
|
+
last = at;
|
|
1717
|
+
from = at + 1;
|
|
1718
|
+
}
|
|
1719
|
+
if (ok) scored.push({ path, score: 2e3 + (last - first) * 10 + path.length });
|
|
1720
|
+
}
|
|
1721
|
+
return scored.sort((a, b) => a.score - b.score || a.path.localeCompare(b.path)).slice(0, limit).map((s) => s.path);
|
|
1722
|
+
}
|
|
1723
|
+
importersOf(path) {
|
|
1724
|
+
return this.db.prepare("SELECT src FROM edges WHERE dst = ? ORDER BY src").all(path).map((r) => r.src);
|
|
1725
|
+
}
|
|
1726
|
+
importsOf(path) {
|
|
1727
|
+
return this.db.prepare("SELECT dst FROM edges WHERE src = ? ORDER BY dst").all(path).map((r) => r.dst);
|
|
1728
|
+
}
|
|
1729
|
+
/**
|
|
1730
|
+
* Aider-style repository map: files by import centrality with their top-level declarations,
|
|
1731
|
+
* cut at whole-file boundaries to fit `maxChars`. `focus` restricts to paths under a prefix.
|
|
1732
|
+
*/
|
|
1733
|
+
repoMap(options) {
|
|
1734
|
+
const files = this.db.prepare(
|
|
1735
|
+
`SELECT f.path FROM files f LEFT JOIN centrality c ON c.path = f.path
|
|
1736
|
+
WHERE f.path LIKE ? ESCAPE '\\'
|
|
1737
|
+
ORDER BY COALESCE(c.score, 0) DESC, f.path`
|
|
1738
|
+
).all(`${escapeLike(options.focus ?? "")}%`);
|
|
1739
|
+
const topLevel = this.db.prepare(
|
|
1740
|
+
"SELECT signature FROM symbols WHERE path = ? AND parent IS NULL ORDER BY start_line LIMIT 12"
|
|
1741
|
+
);
|
|
1742
|
+
const blocks = [];
|
|
1743
|
+
let used = 0;
|
|
1744
|
+
let omitted = 0;
|
|
1745
|
+
for (const { path } of files) {
|
|
1746
|
+
const signatures = topLevel.all(path).map(
|
|
1747
|
+
(r) => ` ${r.signature}`
|
|
1748
|
+
);
|
|
1749
|
+
const block = [path, ...signatures].join("\n");
|
|
1750
|
+
if (used + block.length + 1 > options.maxChars - 60) {
|
|
1751
|
+
omitted++;
|
|
1752
|
+
continue;
|
|
1753
|
+
}
|
|
1754
|
+
blocks.push(block);
|
|
1755
|
+
used += block.length + 1;
|
|
1756
|
+
}
|
|
1757
|
+
if (omitted > 0) blocks.push(`\u2026 ${omitted} more file(s); narrow with focus=<dir>`);
|
|
1758
|
+
return blocks.join("\n");
|
|
1759
|
+
}
|
|
1760
|
+
};
|
|
1761
|
+
|
|
1762
|
+
export {
|
|
1763
|
+
detectLanguage,
|
|
1764
|
+
supportsSymbols,
|
|
1765
|
+
extractSymbols,
|
|
1766
|
+
listCandidatePaths,
|
|
1767
|
+
CodeIndex
|
|
1768
|
+
};
|
|
1769
|
+
//# sourceMappingURL=chunk-2S6B3DDQ.js.map
|