@mars167/git-ai 2.3.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 (122) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +364 -0
  3. package/README.zh-CN.md +361 -0
  4. package/assets/hooks/post-checkout +28 -0
  5. package/assets/hooks/post-merge +28 -0
  6. package/assets/hooks/pre-commit +17 -0
  7. package/assets/hooks/pre-push +29 -0
  8. package/dist/bin/git-ai.js +62 -0
  9. package/dist/src/commands/ai.js +30 -0
  10. package/dist/src/commands/checkIndex.js +19 -0
  11. package/dist/src/commands/dsr.js +156 -0
  12. package/dist/src/commands/graph.js +203 -0
  13. package/dist/src/commands/hooks.js +125 -0
  14. package/dist/src/commands/index.js +92 -0
  15. package/dist/src/commands/pack.js +31 -0
  16. package/dist/src/commands/query.js +139 -0
  17. package/dist/src/commands/semantic.js +134 -0
  18. package/dist/src/commands/serve.js +14 -0
  19. package/dist/src/commands/status.js +78 -0
  20. package/dist/src/commands/trae.js +75 -0
  21. package/dist/src/commands/unpack.js +28 -0
  22. package/dist/src/core/archive.js +91 -0
  23. package/dist/src/core/astGraph.js +127 -0
  24. package/dist/src/core/astGraphQuery.js +142 -0
  25. package/dist/src/core/cozo.js +266 -0
  26. package/dist/src/core/cpg/astLayer.js +56 -0
  27. package/dist/src/core/cpg/callGraph.js +483 -0
  28. package/dist/src/core/cpg/cfgLayer.js +490 -0
  29. package/dist/src/core/cpg/dfgLayer.js +237 -0
  30. package/dist/src/core/cpg/index.js +80 -0
  31. package/dist/src/core/cpg/types.js +108 -0
  32. package/dist/src/core/crypto.js +10 -0
  33. package/dist/src/core/dsr/generate.js +308 -0
  34. package/dist/src/core/dsr/gitContext.js +74 -0
  35. package/dist/src/core/dsr/indexMaterialize.js +106 -0
  36. package/dist/src/core/dsr/paths.js +26 -0
  37. package/dist/src/core/dsr/query.js +73 -0
  38. package/dist/src/core/dsr/snapshotParser.js +73 -0
  39. package/dist/src/core/dsr/state.js +27 -0
  40. package/dist/src/core/dsr/types.js +2 -0
  41. package/dist/src/core/embedding/fusion.js +52 -0
  42. package/dist/src/core/embedding/index.js +43 -0
  43. package/dist/src/core/embedding/parser.js +14 -0
  44. package/dist/src/core/embedding/semantic.js +254 -0
  45. package/dist/src/core/embedding/structural.js +97 -0
  46. package/dist/src/core/embedding/symbolic.js +117 -0
  47. package/dist/src/core/embedding/tokenizer.js +91 -0
  48. package/dist/src/core/embedding/types.js +2 -0
  49. package/dist/src/core/embedding.js +36 -0
  50. package/dist/src/core/git.js +49 -0
  51. package/dist/src/core/gitDiff.js +73 -0
  52. package/dist/src/core/indexCheck.js +131 -0
  53. package/dist/src/core/indexer.js +185 -0
  54. package/dist/src/core/indexerIncremental.js +303 -0
  55. package/dist/src/core/indexing/config.js +51 -0
  56. package/dist/src/core/indexing/hnsw.js +568 -0
  57. package/dist/src/core/indexing/index.js +17 -0
  58. package/dist/src/core/indexing/monitor.js +82 -0
  59. package/dist/src/core/indexing/parallel.js +252 -0
  60. package/dist/src/core/lancedb.js +111 -0
  61. package/dist/src/core/lfs.js +27 -0
  62. package/dist/src/core/log.js +62 -0
  63. package/dist/src/core/manifest.js +88 -0
  64. package/dist/src/core/parser/adapter.js +2 -0
  65. package/dist/src/core/parser/c.js +93 -0
  66. package/dist/src/core/parser/chunkRelations.js +178 -0
  67. package/dist/src/core/parser/chunker.js +274 -0
  68. package/dist/src/core/parser/go.js +98 -0
  69. package/dist/src/core/parser/java.js +80 -0
  70. package/dist/src/core/parser/markdown.js +76 -0
  71. package/dist/src/core/parser/python.js +81 -0
  72. package/dist/src/core/parser/rust.js +103 -0
  73. package/dist/src/core/parser/typescript.js +98 -0
  74. package/dist/src/core/parser/utils.js +62 -0
  75. package/dist/src/core/parser/yaml.js +53 -0
  76. package/dist/src/core/parser.js +75 -0
  77. package/dist/src/core/paths.js +10 -0
  78. package/dist/src/core/repoMap.js +164 -0
  79. package/dist/src/core/retrieval/cache.js +31 -0
  80. package/dist/src/core/retrieval/classifier.js +74 -0
  81. package/dist/src/core/retrieval/expander.js +80 -0
  82. package/dist/src/core/retrieval/fuser.js +40 -0
  83. package/dist/src/core/retrieval/index.js +32 -0
  84. package/dist/src/core/retrieval/reranker.js +304 -0
  85. package/dist/src/core/retrieval/types.js +2 -0
  86. package/dist/src/core/retrieval/weights.js +42 -0
  87. package/dist/src/core/search.js +41 -0
  88. package/dist/src/core/sq8.js +65 -0
  89. package/dist/src/core/symbolSearch.js +143 -0
  90. package/dist/src/core/types.js +2 -0
  91. package/dist/src/core/workspace.js +116 -0
  92. package/dist/src/mcp/server.js +794 -0
  93. package/docs/README.md +44 -0
  94. package/docs/cross-encoder.md +157 -0
  95. package/docs/embedding.md +158 -0
  96. package/docs/logo.png +0 -0
  97. package/docs/windows-setup.md +67 -0
  98. package/docs/zh-CN/DESIGN.md +102 -0
  99. package/docs/zh-CN/README.md +46 -0
  100. package/docs/zh-CN/advanced.md +26 -0
  101. package/docs/zh-CN/architecture_explained.md +116 -0
  102. package/docs/zh-CN/cli.md +109 -0
  103. package/docs/zh-CN/dsr.md +91 -0
  104. package/docs/zh-CN/graph_scenarios.md +173 -0
  105. package/docs/zh-CN/hooks.md +14 -0
  106. package/docs/zh-CN/manifests.md +136 -0
  107. package/docs/zh-CN/mcp.md +205 -0
  108. package/docs/zh-CN/quickstart.md +35 -0
  109. package/docs/zh-CN/rules.md +7 -0
  110. package/docs/zh-CN/technical-details.md +454 -0
  111. package/docs/zh-CN/troubleshooting.md +19 -0
  112. package/docs/zh-CN/windows-setup.md +67 -0
  113. package/install.sh +183 -0
  114. package/package.json +97 -0
  115. package/skills/git-ai-mcp/SKILL.md +86 -0
  116. package/skills/git-ai-mcp/references/constraints.md +143 -0
  117. package/skills/git-ai-mcp/references/tools.md +263 -0
  118. package/templates/agents/common/documents/Fix EISDIR error and enable multi-language indexing.md +14 -0
  119. package/templates/agents/common/documents/Fix git-ai index error in CodaGraph directory.md +13 -0
  120. package/templates/agents/common/skills/git-ai-mcp/SKILL.md +86 -0
  121. package/templates/agents/common/skills/git-ai-mcp/references/constraints.md +143 -0
  122. package/templates/agents/common/skills/git-ai-mcp/references/tools.md +263 -0
@@ -0,0 +1,303 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.IncrementalIndexerV2 = void 0;
7
+ const fs_extra_1 = __importDefault(require("fs-extra"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const simple_git_1 = __importDefault(require("simple-git"));
10
+ const crypto_1 = require("./crypto");
11
+ const lancedb_1 = require("./lancedb");
12
+ const embedding_1 = require("./embedding");
13
+ const sq8_1 = require("./sq8");
14
+ const paths_1 = require("./paths");
15
+ const astGraph_1 = require("./astGraph");
16
+ const snapshotParser_1 = require("./dsr/snapshotParser");
17
+ const git_1 = require("./git");
18
+ function buildChunkText(file, symbol) {
19
+ return `file:${file}\nkind:${symbol.kind}\nname:${symbol.name}\nsignature:${symbol.signature}`;
20
+ }
21
+ function inferIndexLang(file) {
22
+ if (file.endsWith('.md') || file.endsWith('.mdx'))
23
+ return 'markdown';
24
+ if (file.endsWith('.yml') || file.endsWith('.yaml'))
25
+ return 'yaml';
26
+ if (file.endsWith('.java'))
27
+ return 'java';
28
+ if (file.endsWith('.c') || file.endsWith('.h'))
29
+ return 'c';
30
+ if (file.endsWith('.go'))
31
+ return 'go';
32
+ if (file.endsWith('.py'))
33
+ return 'python';
34
+ if (file.endsWith('.rs'))
35
+ return 'rust';
36
+ return 'ts';
37
+ }
38
+ function isIndexableFile(file) {
39
+ return /\.(ts|tsx|js|jsx|java|c|h|go|py|rs|md|mdx|yml|yaml)$/i.test(file);
40
+ }
41
+ function escapeQuotes(s) {
42
+ return s.replace(/'/g, "''");
43
+ }
44
+ async function readStagedFile(repoRoot, filePosix) {
45
+ const git = (0, simple_git_1.default)(repoRoot);
46
+ try {
47
+ return await git.raw(['show', `:${filePosix}`]);
48
+ }
49
+ catch {
50
+ return null;
51
+ }
52
+ }
53
+ async function readWorktreeFile(scanRoot, filePosix) {
54
+ const abs = path_1.default.join(scanRoot, filePosix);
55
+ try {
56
+ return await fs_extra_1.default.readFile(abs, 'utf-8');
57
+ }
58
+ catch {
59
+ return null;
60
+ }
61
+ }
62
+ async function deleteRefsForFile(byLang, filePosix) {
63
+ const safe = escapeQuotes(filePosix);
64
+ const pred = `file == '${safe}'`;
65
+ const langs = Object.keys(byLang);
66
+ for (const lang of langs) {
67
+ const t = byLang[lang];
68
+ if (!t)
69
+ continue;
70
+ if (typeof t.refs.delete === 'function') {
71
+ await t.refs.delete(pred);
72
+ }
73
+ }
74
+ }
75
+ class IncrementalIndexerV2 {
76
+ constructor(options) {
77
+ this.repoRoot = path_1.default.resolve(options.repoRoot);
78
+ this.scanRoot = path_1.default.resolve(options.scanRoot ?? options.repoRoot);
79
+ this.dim = options.dim;
80
+ this.source = options.source;
81
+ this.changes = options.changes;
82
+ this.onProgress = options.onProgress;
83
+ this.parser = new snapshotParser_1.SnapshotCodeParser();
84
+ }
85
+ async run() {
86
+ const gitAiDir = path_1.default.join(this.repoRoot, '.git-ai');
87
+ await fs_extra_1.default.ensureDir(gitAiDir);
88
+ const dbDir = (0, lancedb_1.defaultDbDir)(this.repoRoot);
89
+ const { byLang } = await (0, lancedb_1.openTablesByLang)({
90
+ dbDir,
91
+ dim: this.dim,
92
+ mode: 'create_if_missing',
93
+ languages: lancedb_1.ALL_INDEX_LANGS,
94
+ });
95
+ const chunkRowsByLang = {};
96
+ const refRowsByLang = {};
97
+ const candidateChunksByLang = {};
98
+ const astFiles = [];
99
+ const astSymbols = [];
100
+ const astContains = [];
101
+ const astExtendsName = [];
102
+ const astImplementsName = [];
103
+ const astRefsName = [];
104
+ const astCallsName = [];
105
+ const neededHashByLang = {};
106
+ const totalFiles = this.changes.length;
107
+ this.onProgress?.({ totalFiles, processedFiles: 0 });
108
+ let processed = 0;
109
+ for (const ch of this.changes) {
110
+ processed++;
111
+ const filePosix = (0, paths_1.toPosixPath)(ch.path);
112
+ this.onProgress?.({ totalFiles, processedFiles: processed, currentFile: filePosix });
113
+ if (ch.status === 'R' && ch.oldPath) {
114
+ const oldFile = (0, paths_1.toPosixPath)(ch.oldPath);
115
+ await deleteRefsForFile(byLang, oldFile);
116
+ await (0, astGraph_1.removeFileFromAstGraph)(this.repoRoot, oldFile);
117
+ }
118
+ if (ch.status === 'D') {
119
+ await deleteRefsForFile(byLang, filePosix);
120
+ await (0, astGraph_1.removeFileFromAstGraph)(this.repoRoot, filePosix);
121
+ continue;
122
+ }
123
+ await deleteRefsForFile(byLang, filePosix);
124
+ await (0, astGraph_1.removeFileFromAstGraph)(this.repoRoot, filePosix);
125
+ if (!isIndexableFile(filePosix))
126
+ continue;
127
+ const lang = inferIndexLang(filePosix);
128
+ if (!chunkRowsByLang[lang])
129
+ chunkRowsByLang[lang] = [];
130
+ if (!refRowsByLang[lang])
131
+ refRowsByLang[lang] = [];
132
+ if (!candidateChunksByLang[lang])
133
+ candidateChunksByLang[lang] = new Map();
134
+ if (!neededHashByLang[lang])
135
+ neededHashByLang[lang] = new Set();
136
+ const content = this.source === 'staged'
137
+ ? await readStagedFile(this.repoRoot, filePosix)
138
+ : await readWorktreeFile(this.scanRoot, filePosix);
139
+ if (content == null)
140
+ continue;
141
+ const parsed = this.parser.parseContent(filePosix, content);
142
+ const symbols = parsed.symbols;
143
+ const fileRefs = parsed.refs;
144
+ const fileId = (0, crypto_1.sha256Hex)(`file:${filePosix}`);
145
+ astFiles.push([fileId, filePosix, lang]);
146
+ const callableScopes = [];
147
+ for (const s of symbols) {
148
+ const text = buildChunkText(filePosix, s);
149
+ const contentHash = (0, crypto_1.sha256Hex)(text);
150
+ const refId = (0, crypto_1.sha256Hex)(`${filePosix}:${s.name}:${s.kind}:${s.startLine}:${s.endLine}:${contentHash}`);
151
+ astSymbols.push([refId, filePosix, lang, s.name, s.kind, s.signature, s.startLine, s.endLine]);
152
+ if (s.kind === 'function' || s.kind === 'method') {
153
+ callableScopes.push({ refId, startLine: s.startLine, endLine: s.endLine });
154
+ }
155
+ let parentId = fileId;
156
+ if (s.container) {
157
+ const cText = buildChunkText(filePosix, s.container);
158
+ const cHash = (0, crypto_1.sha256Hex)(cText);
159
+ parentId = (0, crypto_1.sha256Hex)(`${filePosix}:${s.container.name}:${s.container.kind}:${s.container.startLine}:${s.container.endLine}:${cHash}`);
160
+ }
161
+ astContains.push([parentId, refId]);
162
+ if (s.kind === 'class') {
163
+ if (s.extends)
164
+ for (const superName of s.extends)
165
+ astExtendsName.push([refId, superName]);
166
+ if (s.implements)
167
+ for (const ifaceName of s.implements)
168
+ astImplementsName.push([refId, ifaceName]);
169
+ }
170
+ neededHashByLang[lang].add(contentHash);
171
+ candidateChunksByLang[lang].set(contentHash, text);
172
+ const refRow = {
173
+ ref_id: refId,
174
+ content_hash: contentHash,
175
+ file: filePosix,
176
+ symbol: s.name,
177
+ kind: s.kind,
178
+ signature: s.signature,
179
+ start_line: s.startLine,
180
+ end_line: s.endLine,
181
+ };
182
+ refRowsByLang[lang].push(refRow);
183
+ }
184
+ const pickScope = (line) => {
185
+ let best = null;
186
+ for (const s of callableScopes) {
187
+ if (line < s.startLine || line > s.endLine)
188
+ continue;
189
+ const span = s.endLine - s.startLine;
190
+ if (!best || span < best.span)
191
+ best = { refId: s.refId, span };
192
+ }
193
+ return best ? best.refId : fileId;
194
+ };
195
+ for (const r of fileRefs) {
196
+ const fromId = pickScope(r.line);
197
+ astRefsName.push([fromId, lang, r.name, r.refKind, filePosix, r.line, r.column]);
198
+ if (r.refKind === 'call' || r.refKind === 'new') {
199
+ astCallsName.push([fromId, lang, r.name, filePosix, r.line, r.column]);
200
+ }
201
+ }
202
+ }
203
+ const existingChunkIdsByLang = {};
204
+ for (const lang of Object.keys(neededHashByLang)) {
205
+ const t = byLang[lang];
206
+ if (!t)
207
+ continue;
208
+ const needed = Array.from(neededHashByLang[lang] ?? []);
209
+ const existing = new Set();
210
+ for (let i = 0; i < needed.length; i += 400) {
211
+ const chunk = needed.slice(i, i + 400);
212
+ if (chunk.length === 0)
213
+ continue;
214
+ const pred = `content_hash IN (${chunk.map((h) => `'${escapeQuotes(h)}'`).join(',')})`;
215
+ const rows = await t.chunks.query().where(pred).select(['content_hash']).limit(chunk.length).toArray();
216
+ for (const row of rows) {
217
+ const id = String(row.content_hash ?? '');
218
+ if (id)
219
+ existing.add(id);
220
+ }
221
+ }
222
+ existingChunkIdsByLang[lang] = existing;
223
+ }
224
+ for (const lang of Object.keys(candidateChunksByLang)) {
225
+ const t = byLang[lang];
226
+ if (!t)
227
+ continue;
228
+ const existing = existingChunkIdsByLang[lang] ?? new Set();
229
+ const chunkRows = [];
230
+ const candidates = candidateChunksByLang[lang];
231
+ for (const [contentHash, text] of candidates.entries()) {
232
+ if (!contentHash || !text)
233
+ continue;
234
+ if (existing.has(contentHash))
235
+ continue;
236
+ const vec = (0, embedding_1.hashEmbedding)(text, { dim: this.dim });
237
+ const q = (0, sq8_1.quantizeSQ8)(vec);
238
+ chunkRows.push({
239
+ content_hash: contentHash,
240
+ text,
241
+ dim: q.dim,
242
+ scale: q.scale,
243
+ qvec_b64: Buffer.from(q.q).toString('base64'),
244
+ });
245
+ existing.add(contentHash);
246
+ }
247
+ chunkRowsByLang[lang] = chunkRows;
248
+ }
249
+ const addedByLang = {};
250
+ for (const lang of lancedb_1.ALL_INDEX_LANGS) {
251
+ const t = byLang[lang];
252
+ if (!t)
253
+ continue;
254
+ const chunkRows = chunkRowsByLang[lang] ?? [];
255
+ const refRows = refRowsByLang[lang] ?? [];
256
+ if (chunkRows.length > 0)
257
+ await t.chunks.add(chunkRows);
258
+ if (refRows.length > 0)
259
+ await t.refs.add(refRows);
260
+ if (chunkRows.length > 0 || refRows.length > 0) {
261
+ addedByLang[lang] = { chunksAdded: chunkRows.length, refsAdded: refRows.length };
262
+ }
263
+ }
264
+ const astGraph = await (0, astGraph_1.writeAstGraphToCozo)(this.repoRoot, {
265
+ files: astFiles,
266
+ symbols: astSymbols,
267
+ contains: astContains,
268
+ extends_name: astExtendsName,
269
+ implements_name: astImplementsName,
270
+ refs_name: astRefsName,
271
+ calls_name: astCallsName,
272
+ }, { mode: 'put' });
273
+ const metaPath = path_1.default.join(gitAiDir, 'meta.json');
274
+ const prev = await fs_extra_1.default.readJSON(metaPath).catch(() => null);
275
+ const commitHash = await (0, git_1.getCurrentCommitHash)(this.repoRoot);
276
+ const meta = {
277
+ ...(prev && typeof prev === 'object' ? prev : {}),
278
+ version: '2.1',
279
+ index_schema_version: 3,
280
+ dim: this.dim,
281
+ dbDir: path_1.default.relative(this.repoRoot, dbDir),
282
+ scanRoot: path_1.default.relative(this.repoRoot, this.scanRoot),
283
+ languages: lancedb_1.ALL_INDEX_LANGS,
284
+ byLang: addedByLang,
285
+ ...(commitHash ? { commit_hash: commitHash } : {}),
286
+ astGraph: astGraph.enabled
287
+ ? {
288
+ backend: 'cozo',
289
+ engine: astGraph.engine,
290
+ dbPath: astGraph.dbPath ? path_1.default.relative(this.repoRoot, astGraph.dbPath) : undefined,
291
+ counts: astGraph.counts,
292
+ }
293
+ : {
294
+ backend: 'cozo',
295
+ enabled: false,
296
+ skippedReason: astGraph.skippedReason,
297
+ },
298
+ };
299
+ await fs_extra_1.default.writeJSON(metaPath, meta, { spaces: 2 });
300
+ return { processed: this.changes.length, addedByLang };
301
+ }
302
+ }
303
+ exports.IncrementalIndexerV2 = IncrementalIndexerV2;
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.defaultIndexingConfig = defaultIndexingConfig;
7
+ exports.defaultErrorHandlingConfig = defaultErrorHandlingConfig;
8
+ exports.defaultIndexingRuntimeConfig = defaultIndexingRuntimeConfig;
9
+ exports.mergeRuntimeConfig = mergeRuntimeConfig;
10
+ const os_1 = __importDefault(require("os"));
11
+ const hnsw_1 = require("./hnsw");
12
+ function defaultIndexingConfig() {
13
+ const cpuCount = Math.max(1, os_1.default.cpus()?.length ?? 1);
14
+ return {
15
+ workerCount: Math.max(1, cpuCount - 1),
16
+ batchSize: 32,
17
+ memoryBudgetMb: 4096,
18
+ hnswConfig: (0, hnsw_1.clampHnswParameters)({
19
+ M: 16,
20
+ efConstruction: 200,
21
+ efSearch: 100,
22
+ quantizationBits: 8,
23
+ }),
24
+ };
25
+ }
26
+ function defaultErrorHandlingConfig() {
27
+ return {
28
+ parseFailureFallback: 'text-only',
29
+ largeFileThreshold: 1000000,
30
+ maxChunkSize: 10000,
31
+ memoryWarningThreshold: 0.8,
32
+ memoryCriticalThreshold: 0.95,
33
+ };
34
+ }
35
+ function defaultIndexingRuntimeConfig() {
36
+ return {
37
+ indexing: defaultIndexingConfig(),
38
+ errorHandling: defaultErrorHandlingConfig(),
39
+ };
40
+ }
41
+ function mergeRuntimeConfig(overrides) {
42
+ const defaults = defaultIndexingRuntimeConfig();
43
+ if (!overrides)
44
+ return defaults;
45
+ const merged = {
46
+ indexing: { ...defaults.indexing, ...overrides.indexing },
47
+ errorHandling: { ...defaults.errorHandling, ...overrides.errorHandling },
48
+ };
49
+ merged.indexing.hnswConfig = (0, hnsw_1.clampHnswParameters)(merged.indexing.hnswConfig);
50
+ return merged;
51
+ }