@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,139 @@
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.queryCommand = void 0;
7
+ const commander_1 = require("commander");
8
+ const path_1 = __importDefault(require("path"));
9
+ const fs_extra_1 = __importDefault(require("fs-extra"));
10
+ const git_1 = require("../core/git");
11
+ const lancedb_1 = require("../core/lancedb");
12
+ const workspace_1 = require("../core/workspace");
13
+ const symbolSearch_1 = require("../core/symbolSearch");
14
+ const log_1 = require("../core/log");
15
+ const indexCheck_1 = require("../core/indexCheck");
16
+ const repoMap_1 = require("../core/repoMap");
17
+ exports.queryCommand = new commander_1.Command('query')
18
+ .description('Query refs table by symbol match (substring/prefix/wildcard/regex/fuzzy)')
19
+ .argument('<keyword>', 'Symbol substring')
20
+ .option('-p, --path <path>', 'Path inside the repository', '.')
21
+ .option('--limit <n>', 'Limit results', '50')
22
+ .option('--mode <mode>', 'Mode: substring|prefix|wildcard|regex|fuzzy (default: auto)')
23
+ .option('--case-insensitive', 'Case-insensitive matching', false)
24
+ .option('--max-candidates <n>', 'Max candidates to fetch before filtering', '1000')
25
+ .option('--lang <lang>', 'Language: auto|all|java|ts|python|go|rust|c|markdown|yaml', 'auto')
26
+ .option('--with-repo-map', 'Attach a lightweight repo map (ranked files + top symbols + wiki links)', false)
27
+ .option('--repo-map-files <n>', 'Max repo map files', '20')
28
+ .option('--repo-map-symbols <n>', 'Max repo map symbols per file', '5')
29
+ .option('--wiki <dir>', 'Wiki directory (default: docs/wiki or wiki)', '')
30
+ .action(async (keyword, options) => {
31
+ const log = (0, log_1.createLogger)({ component: 'cli', cmd: 'ai query' });
32
+ const startedAt = Date.now();
33
+ try {
34
+ const repoRoot = await (0, git_1.resolveGitRoot)(path_1.default.resolve(options.path));
35
+ const limit = Number(options.limit);
36
+ const q = String(keyword);
37
+ const mode = (0, symbolSearch_1.inferSymbolSearchMode)(q, options.mode);
38
+ const caseInsensitive = Boolean(options.caseInsensitive ?? false);
39
+ const maxCandidates = Math.max(limit, Number(options.maxCandidates ?? Math.min(2000, limit * 20)));
40
+ const langSel = String(options.lang ?? 'auto');
41
+ const withRepoMap = Boolean(options.withRepoMap ?? false);
42
+ if ((0, git_1.inferWorkspaceRoot)(repoRoot)) {
43
+ const coarse = (mode === 'substring' || mode === 'prefix') ? q : (0, symbolSearch_1.pickCoarseToken)(q);
44
+ const res = await (0, workspace_1.queryManifestWorkspace)({ manifestRepoRoot: repoRoot, keyword: coarse, limit: maxCandidates });
45
+ const filteredByLang = filterWorkspaceRowsByLang(res.rows, langSel);
46
+ const rows = (0, symbolSearch_1.filterAndRankSymbolRows)(filteredByLang, { query: q, mode, caseInsensitive, limit });
47
+ log.info('query_symbols', { ok: true, repoRoot, workspace: true, mode, case_insensitive: caseInsensitive, limit, max_candidates: maxCandidates, candidates: res.rows.length, rows: rows.length, duration_ms: Date.now() - startedAt });
48
+ const repoMap = withRepoMap ? { enabled: false, skippedReason: 'workspace_mode_not_supported' } : undefined;
49
+ console.log(JSON.stringify({ ...res, rows, ...(repoMap ? { repo_map: repoMap } : {}) }, null, 2));
50
+ return;
51
+ }
52
+ const status = await (0, indexCheck_1.checkIndex)(repoRoot);
53
+ if (!status.ok) {
54
+ process.stderr.write(JSON.stringify({ ...status, ok: false, reason: 'index_incompatible' }, null, 2) + '\n');
55
+ process.exit(2);
56
+ return;
57
+ }
58
+ const langs = (0, indexCheck_1.resolveLangs)(status.found.meta ?? null, langSel);
59
+ if (langs.length === 0) {
60
+ process.stderr.write(JSON.stringify({ ok: false, reason: 'lang_not_available', lang: langSel, available: status.found.meta?.languages ?? [] }, null, 2) + '\n');
61
+ process.exit(2);
62
+ return;
63
+ }
64
+ const dbDir = (0, lancedb_1.defaultDbDir)(repoRoot);
65
+ const dim = typeof status.found.meta?.dim === 'number' ? status.found.meta.dim : 256;
66
+ const { byLang } = await (0, lancedb_1.openTablesByLang)({ dbDir, dim, mode: 'open_only', languages: langs });
67
+ const where = (0, symbolSearch_1.buildCoarseWhere)({ query: q, mode, caseInsensitive });
68
+ const candidates = [];
69
+ for (const lang of langs) {
70
+ const t = byLang[lang];
71
+ if (!t)
72
+ continue;
73
+ const rows = where
74
+ ? await t.refs.query().where(where).limit(maxCandidates).toArray()
75
+ : await t.refs.query().limit(maxCandidates).toArray();
76
+ for (const r of rows)
77
+ candidates.push({ ...r, lang });
78
+ }
79
+ const rows = (0, symbolSearch_1.filterAndRankSymbolRows)(candidates, { query: q, mode, caseInsensitive, limit });
80
+ log.info('query_symbols', { ok: true, repoRoot, workspace: false, lang: langSel, langs, mode, case_insensitive: caseInsensitive, limit, max_candidates: maxCandidates, candidates: candidates.length, rows: rows.length, duration_ms: Date.now() - startedAt });
81
+ const repoMap = withRepoMap ? await buildRepoMapAttachment(repoRoot, options) : undefined;
82
+ console.log(JSON.stringify({ repoRoot, count: rows.length, lang: langSel, rows, ...(repoMap ? { repo_map: repoMap } : {}) }, null, 2));
83
+ }
84
+ catch (e) {
85
+ log.error('query_symbols', { ok: false, duration_ms: Date.now() - startedAt, err: e instanceof Error ? { name: e.name, message: e.message, stack: e.stack } : { message: String(e) } });
86
+ process.exit(1);
87
+ }
88
+ });
89
+ async function buildRepoMapAttachment(repoRoot, options) {
90
+ try {
91
+ const wikiDir = resolveWikiDir(repoRoot, String(options.wiki ?? ''));
92
+ const files = await (0, repoMap_1.generateRepoMap)({
93
+ repoRoot,
94
+ maxFiles: Number(options.repoMapFiles ?? 20),
95
+ maxSymbolsPerFile: Number(options.repoMapSymbols ?? 5),
96
+ wikiDir,
97
+ });
98
+ return { enabled: true, wikiDir, files };
99
+ }
100
+ catch (e) {
101
+ return { enabled: false, skippedReason: String(e?.message ?? e) };
102
+ }
103
+ }
104
+ function resolveWikiDir(repoRoot, wikiOpt) {
105
+ const w = String(wikiOpt ?? '').trim();
106
+ if (w)
107
+ return path_1.default.resolve(repoRoot, w);
108
+ const candidates = [path_1.default.join(repoRoot, 'docs', 'wiki'), path_1.default.join(repoRoot, 'wiki')];
109
+ for (const c of candidates) {
110
+ if (fs_extra_1.default.existsSync(c))
111
+ return c;
112
+ }
113
+ return '';
114
+ }
115
+ function inferLangFromFile(file) {
116
+ const f = String(file);
117
+ if (f.endsWith('.md') || f.endsWith('.mdx'))
118
+ return 'markdown';
119
+ if (f.endsWith('.yml') || f.endsWith('.yaml'))
120
+ return 'yaml';
121
+ if (f.endsWith('.java'))
122
+ return 'java';
123
+ if (f.endsWith('.c') || f.endsWith('.h'))
124
+ return 'c';
125
+ if (f.endsWith('.go'))
126
+ return 'go';
127
+ if (f.endsWith('.py'))
128
+ return 'python';
129
+ if (f.endsWith('.rs'))
130
+ return 'rust';
131
+ return 'ts';
132
+ }
133
+ function filterWorkspaceRowsByLang(rows, langSel) {
134
+ const sel = String(langSel ?? 'auto');
135
+ if (sel === 'auto' || sel === 'all')
136
+ return rows;
137
+ const target = sel;
138
+ return rows.filter(r => inferLangFromFile(String(r.file ?? '')) === target);
139
+ }
@@ -0,0 +1,134 @@
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.semanticCommand = void 0;
7
+ const commander_1 = require("commander");
8
+ const path_1 = __importDefault(require("path"));
9
+ const fs_extra_1 = __importDefault(require("fs-extra"));
10
+ const git_1 = require("../core/git");
11
+ const lancedb_1 = require("../core/lancedb");
12
+ const search_1 = require("../core/search");
13
+ const log_1 = require("../core/log");
14
+ const indexCheck_1 = require("../core/indexCheck");
15
+ const repoMap_1 = require("../core/repoMap");
16
+ exports.semanticCommand = new commander_1.Command('semantic')
17
+ .description('Semantic search using SQ8 vectors (brute-force over chunks)')
18
+ .argument('<text>', 'Query text')
19
+ .option('-p, --path <path>', 'Path inside the repository', '.')
20
+ .option('-k, --topk <k>', 'Top K results', '10')
21
+ .option('--lang <lang>', 'Language: auto|all|java|ts|python|go|rust|c|markdown|yaml', 'auto')
22
+ .option('--with-repo-map', 'Attach a lightweight repo map (ranked files + top symbols + wiki links)', false)
23
+ .option('--repo-map-files <n>', 'Max repo map files', '20')
24
+ .option('--repo-map-symbols <n>', 'Max repo map symbols per file', '5')
25
+ .option('--wiki <dir>', 'Wiki directory (default: docs/wiki or wiki)', '')
26
+ .action(async (text, options) => {
27
+ const log = (0, log_1.createLogger)({ component: 'cli', cmd: 'ai semantic' });
28
+ const startedAt = Date.now();
29
+ try {
30
+ const repoRoot = await (0, git_1.resolveGitRoot)(path_1.default.resolve(options.path));
31
+ const withRepoMap = Boolean(options.withRepoMap ?? false);
32
+ const status = await (0, indexCheck_1.checkIndex)(repoRoot);
33
+ if (!status.ok) {
34
+ process.stderr.write(JSON.stringify({ ...status, ok: false, reason: 'index_incompatible' }, null, 2) + '\n');
35
+ process.exit(2);
36
+ return;
37
+ }
38
+ const dbDir = (0, lancedb_1.defaultDbDir)(repoRoot);
39
+ const k = Number(options.topk);
40
+ const dim = typeof status.found.meta?.dim === 'number' ? status.found.meta.dim : 256;
41
+ const q = (0, search_1.buildQueryVector)(String(text), dim);
42
+ const langSel = String(options.lang ?? 'auto');
43
+ const langs = (0, indexCheck_1.resolveLangs)(status.found.meta ?? null, langSel);
44
+ if (langs.length === 0) {
45
+ process.stderr.write(JSON.stringify({ ok: false, reason: 'lang_not_available', lang: langSel, available: status.found.meta?.languages ?? [] }, null, 2) + '\n');
46
+ process.exit(2);
47
+ return;
48
+ }
49
+ const { byLang } = await (0, lancedb_1.openTablesByLang)({ dbDir, dim, mode: 'open_only', languages: langs });
50
+ const allScored = [];
51
+ let totalChunks = 0;
52
+ for (const lang of langs) {
53
+ const t = byLang[lang];
54
+ if (!t)
55
+ continue;
56
+ const chunkRows = await t.chunks.query().select(['content_hash', 'text', 'dim', 'scale', 'qvec_b64']).limit(1000000).toArray();
57
+ totalChunks += chunkRows.length;
58
+ for (const r of chunkRows) {
59
+ allScored.push({
60
+ lang,
61
+ content_hash: String(r.content_hash),
62
+ score: (0, search_1.scoreAgainst)(q, { dim: Number(r.dim), scale: Number(r.scale), qvec: new Int8Array(Buffer.from(String(r.qvec_b64), 'base64')) }),
63
+ text: String(r.text),
64
+ });
65
+ }
66
+ }
67
+ const scored = allScored.sort((a, b) => b.score - a.score).slice(0, k);
68
+ const neededByLang = {};
69
+ for (const s of scored) {
70
+ if (!neededByLang[s.lang])
71
+ neededByLang[s.lang] = new Set();
72
+ neededByLang[s.lang].add(String(s.content_hash));
73
+ }
74
+ const refsByLangAndId = new Map();
75
+ for (const lang of langs) {
76
+ const t = byLang[lang];
77
+ if (!t)
78
+ continue;
79
+ const needed = neededByLang[lang];
80
+ if (!needed || needed.size === 0)
81
+ continue;
82
+ const refsRows = await t.refs.query().select(['content_hash', 'file', 'symbol', 'kind', 'signature', 'start_line', 'end_line'])
83
+ .limit(1000000)
84
+ .toArray();
85
+ const byId = new Map();
86
+ for (const r of refsRows) {
87
+ const id = String(r.content_hash);
88
+ if (!needed.has(id))
89
+ continue;
90
+ if (!byId.has(id))
91
+ byId.set(id, []);
92
+ byId.get(id).push(r);
93
+ }
94
+ refsByLangAndId.set(lang, byId);
95
+ }
96
+ const hits = scored.map(s => ({
97
+ ...s,
98
+ refs: (refsByLangAndId.get(s.lang)?.get(s.content_hash) || []).slice(0, 5),
99
+ }));
100
+ log.info('semantic_search', { ok: true, repoRoot, topk: k, lang: langSel, langs, chunks: totalChunks, hits: hits.length, duration_ms: Date.now() - startedAt });
101
+ const repoMap = withRepoMap ? await buildRepoMapAttachment(repoRoot, options) : undefined;
102
+ console.log(JSON.stringify({ repoRoot, topk: k, lang: langSel, hits, ...(repoMap ? { repo_map: repoMap } : {}) }, null, 2));
103
+ }
104
+ catch (e) {
105
+ log.error('semantic_search', { ok: false, duration_ms: Date.now() - startedAt, err: e instanceof Error ? { name: e.name, message: e.message, stack: e.stack } : { message: String(e) } });
106
+ process.exit(1);
107
+ }
108
+ });
109
+ async function buildRepoMapAttachment(repoRoot, options) {
110
+ try {
111
+ const wikiDir = resolveWikiDir(repoRoot, String(options.wiki ?? ''));
112
+ const files = await (0, repoMap_1.generateRepoMap)({
113
+ repoRoot,
114
+ maxFiles: Number(options.repoMapFiles ?? 20),
115
+ maxSymbolsPerFile: Number(options.repoMapSymbols ?? 5),
116
+ wikiDir,
117
+ });
118
+ return { enabled: true, wikiDir, files };
119
+ }
120
+ catch (e) {
121
+ return { enabled: false, skippedReason: String(e?.message ?? e) };
122
+ }
123
+ }
124
+ function resolveWikiDir(repoRoot, wikiOpt) {
125
+ const w = String(wikiOpt ?? '').trim();
126
+ if (w)
127
+ return path_1.default.resolve(repoRoot, w);
128
+ const candidates = [path_1.default.join(repoRoot, 'docs', 'wiki'), path_1.default.join(repoRoot, 'wiki')];
129
+ for (const c of candidates) {
130
+ if (fs_extra_1.default.existsSync(c))
131
+ return c;
132
+ }
133
+ return '';
134
+ }
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.serveCommand = void 0;
4
+ const commander_1 = require("commander");
5
+ const server_1 = require("../mcp/server");
6
+ exports.serveCommand = new commander_1.Command('serve')
7
+ .description('Start MCP server (stdio). Repository is specified by path in each tool call.')
8
+ .option('--disable-mcp-log', 'Disable MCP access logging')
9
+ .action(async (options) => {
10
+ const server = new server_1.GitAIV2MCPServer(process.cwd(), {
11
+ disableAccessLog: !!options.disableMcpLog,
12
+ });
13
+ await server.start();
14
+ });
@@ -0,0 +1,78 @@
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.statusCommand = void 0;
7
+ const commander_1 = require("commander");
8
+ const path_1 = __importDefault(require("path"));
9
+ const git_1 = require("../core/git");
10
+ const indexCheck_1 = require("../core/indexCheck");
11
+ const lancedb_1 = require("../core/lancedb");
12
+ exports.statusCommand = new commander_1.Command('status')
13
+ .description('Show repository index status')
14
+ .option('-p, --path <path>', 'Path inside the repository', '.')
15
+ .option('--json', 'Output machine-readable JSON', false)
16
+ .action(async (options) => {
17
+ const repoRoot = await (0, git_1.resolveGitRoot)(path_1.default.resolve(options.path));
18
+ const res = await (0, indexCheck_1.checkIndex)(repoRoot);
19
+ if (options.json) {
20
+ console.log(JSON.stringify({ repoRoot, ...res }, null, 2));
21
+ process.exit(res.ok ? 0 : 2);
22
+ }
23
+ const meta = res.found.meta ?? null;
24
+ const lines = [];
25
+ lines.push(`repo: ${repoRoot}`);
26
+ lines.push(`index: ${res.ok ? 'ok' : 'not_ready'}`);
27
+ if (meta) {
28
+ lines.push(`schema: ${String(meta.index_schema_version ?? 'unknown')} (expected ${res.expected.index_schema_version})`);
29
+ if (meta.dim !== undefined)
30
+ lines.push(`dim: ${String(meta.dim)}`);
31
+ const rawLangs = Array.isArray(meta.languages) ? meta.languages.map((v) => String(v)) : [];
32
+ const supported = rawLangs.filter((l) => lancedb_1.ALL_INDEX_LANGS.includes(l));
33
+ const unsupported = rawLangs.filter((l) => !lancedb_1.ALL_INDEX_LANGS.includes(l));
34
+ if (supported.length > 0)
35
+ lines.push(`languages: ${supported.join(', ')}`);
36
+ if (unsupported.length > 0)
37
+ lines.push(`unsupportedLanguages: ${unsupported.join(', ')}`);
38
+ if (meta.dbDir)
39
+ lines.push(`db: ${meta.dbDir}`);
40
+ if (meta.scanRoot)
41
+ lines.push(`scanRoot: ${meta.scanRoot}`);
42
+ // Display commit information
43
+ if (meta.commit_hash) {
44
+ const shortHash = meta.commit_hash.slice(0, 7);
45
+ const currentHash = res.found.currentCommitHash;
46
+ if (currentHash) {
47
+ const currentShort = currentHash.slice(0, 7);
48
+ if (meta.commit_hash === currentHash) {
49
+ lines.push(`commit: ${shortHash} (up-to-date)`);
50
+ }
51
+ else {
52
+ lines.push(`commit: ${shortHash} (HEAD is ${currentShort})`);
53
+ }
54
+ }
55
+ else {
56
+ lines.push(`commit: ${shortHash}`);
57
+ }
58
+ }
59
+ else if (res.found.currentCommitHash) {
60
+ lines.push(`commit: not recorded (HEAD is ${res.found.currentCommitHash.slice(0, 7)})`);
61
+ }
62
+ }
63
+ else {
64
+ lines.push(`meta: missing (${res.found.metaPath})`);
65
+ }
66
+ if (!res.ok) {
67
+ lines.push(`problems: ${res.problems.join(', ')}`);
68
+ lines.push(`hint: ${res.hint}`);
69
+ }
70
+ if (res.warnings.length > 0) {
71
+ lines.push(`warnings: ${res.warnings.join(', ')}`);
72
+ if (res.warnings.some(w => w.startsWith('index_commit_mismatch'))) {
73
+ lines.push(`hint: Index may be out of date. Run: git-ai ai index --incremental`);
74
+ }
75
+ }
76
+ console.log(lines.join('\n'));
77
+ process.exit(res.ok ? 0 : 2);
78
+ });
@@ -0,0 +1,75 @@
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.agentCommand = void 0;
7
+ const commander_1 = require("commander");
8
+ const path_1 = __importDefault(require("path"));
9
+ const fs_extra_1 = __importDefault(require("fs-extra"));
10
+ const git_1 = require("../core/git");
11
+ async function findPackageRoot(startDir) {
12
+ let cur = path_1.default.resolve(startDir);
13
+ for (let i = 0; i < 12; i++) {
14
+ const pj = path_1.default.join(cur, 'package.json');
15
+ if (await fs_extra_1.default.pathExists(pj))
16
+ return cur;
17
+ const parent = path_1.default.dirname(cur);
18
+ if (parent === cur)
19
+ break;
20
+ cur = parent;
21
+ }
22
+ return path_1.default.resolve(startDir);
23
+ }
24
+ async function listDirNames(p) {
25
+ if (!await fs_extra_1.default.pathExists(p))
26
+ return [];
27
+ const entries = await fs_extra_1.default.readdir(p);
28
+ const out = [];
29
+ for (const n of entries) {
30
+ const full = path_1.default.join(p, n);
31
+ try {
32
+ const st = await fs_extra_1.default.stat(full);
33
+ if (st.isDirectory())
34
+ out.push(n);
35
+ }
36
+ catch {
37
+ }
38
+ }
39
+ return out.sort();
40
+ }
41
+ exports.agentCommand = new commander_1.Command('agent')
42
+ .description('Install Agent skills/rules templates into a target directory')
43
+ .alias('trae')
44
+ .addCommand(new commander_1.Command('install')
45
+ .description('Install skills/rules templates (default: <repoRoot>/.agents)')
46
+ .option('-p, --path <path>', 'Path inside the repository', '.')
47
+ .option('--to <dir>', 'Destination directory (overrides default)', '')
48
+ .option('--agent <agent>', 'Template layout: agents|trae', 'agents')
49
+ .option('--overwrite', 'Overwrite existing files', false)
50
+ .action(async (options) => {
51
+ const repoRoot = await (0, git_1.resolveGitRoot)(path_1.default.resolve(options.path));
52
+ const agent = String(options.agent ?? 'agents').trim().toLowerCase();
53
+ const defaultDirName = agent === 'trae' ? '.trae' : '.agents';
54
+ const destDir = String(options.to ?? '').trim() ? path_1.default.resolve(String(options.to)) : path_1.default.join(repoRoot, defaultDirName);
55
+ const overwrite = Boolean(options.overwrite ?? false);
56
+ const packageRoot = await findPackageRoot(__dirname);
57
+ const srcTemplateDir = path_1.default.join(packageRoot, 'templates', 'agents', 'common');
58
+ const srcSkillsDir = path_1.default.join(srcTemplateDir, 'skills');
59
+ const srcRulesDir = path_1.default.join(srcTemplateDir, 'rules');
60
+ if (!await fs_extra_1.default.pathExists(srcSkillsDir) || !await fs_extra_1.default.pathExists(srcRulesDir)) {
61
+ console.log(JSON.stringify({ ok: false, repoRoot, error: 'template_missing', srcTemplateDir }, null, 2));
62
+ process.exitCode = 2;
63
+ return;
64
+ }
65
+ const dstSkillsDir = path_1.default.join(destDir, 'skills');
66
+ const dstRulesDir = path_1.default.join(destDir, 'rules');
67
+ await fs_extra_1.default.ensureDir(destDir);
68
+ await fs_extra_1.default.copy(srcSkillsDir, dstSkillsDir, { overwrite });
69
+ await fs_extra_1.default.copy(srcRulesDir, dstRulesDir, { overwrite });
70
+ const installed = {
71
+ skills: await listDirNames(dstSkillsDir),
72
+ rules: await listDirNames(dstRulesDir),
73
+ };
74
+ console.log(JSON.stringify({ ok: true, repoRoot, agent, destDir, overwrite, installed }, null, 2));
75
+ }));
@@ -0,0 +1,28 @@
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.unpackCommand = void 0;
7
+ const commander_1 = require("commander");
8
+ const path_1 = __importDefault(require("path"));
9
+ const git_1 = require("../core/git");
10
+ const archive_1 = require("../core/archive");
11
+ const log_1 = require("../core/log");
12
+ exports.unpackCommand = new commander_1.Command('unpack')
13
+ .description('Unpack .git-ai/lancedb.tar.gz into .git-ai/lancedb')
14
+ .option('-p, --path <path>', 'Path inside the repository', '.')
15
+ .action(async (options) => {
16
+ const log = (0, log_1.createLogger)({ component: 'cli', cmd: 'ai unpack' });
17
+ const startedAt = Date.now();
18
+ try {
19
+ const repoRoot = await (0, git_1.resolveGitRoot)(path_1.default.resolve(options.path));
20
+ const unpacked = await (0, archive_1.unpackLanceDb)(repoRoot);
21
+ log.info('unpack_index', { ok: true, repoRoot, unpacked: unpacked.unpacked, duration_ms: Date.now() - startedAt });
22
+ console.log(JSON.stringify({ repoRoot, ...unpacked }, null, 2));
23
+ }
24
+ catch (e) {
25
+ log.error('unpack_index', { ok: false, duration_ms: Date.now() - startedAt, err: e instanceof Error ? { name: e.name, message: e.message, stack: e.stack } : { message: String(e) } });
26
+ process.exit(1);
27
+ }
28
+ });
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.getArchivePaths = getArchivePaths;
40
+ exports.packLanceDb = packLanceDb;
41
+ exports.unpackLanceDb = unpackLanceDb;
42
+ const fs_extra_1 = __importDefault(require("fs-extra"));
43
+ const path_1 = __importDefault(require("path"));
44
+ const tar = __importStar(require("tar"));
45
+ const paths_1 = require("./paths");
46
+ function getArchivePaths(repoRoot) {
47
+ const gitAiDir = path_1.default.join(repoRoot, '.git-ai');
48
+ const lancedbDir = path_1.default.join(gitAiDir, 'lancedb');
49
+ const archivePath = path_1.default.join(gitAiDir, 'lancedb.tar.gz');
50
+ return { gitAiDir, lancedbDir, archivePath };
51
+ }
52
+ async function packLanceDb(repoRoot) {
53
+ const { gitAiDir, lancedbDir, archivePath } = getArchivePaths(repoRoot);
54
+ await fs_extra_1.default.ensureDir(gitAiDir);
55
+ if (!await fs_extra_1.default.pathExists(lancedbDir)) {
56
+ return { archivePath, packed: false };
57
+ }
58
+ await fs_extra_1.default.remove(archivePath);
59
+ const entries = [];
60
+ const walk = async (relPosix) => {
61
+ const abs = path_1.default.join(gitAiDir, ...(0, paths_1.splitPosixPath)(relPosix));
62
+ const stat = await fs_extra_1.default.stat(abs);
63
+ if (stat.isDirectory()) {
64
+ const children = (await fs_extra_1.default.readdir(abs)).sort((a, b) => a.localeCompare(b));
65
+ for (const c of children) {
66
+ await walk(path_1.default.posix.join(relPosix, (0, paths_1.toPosixPath)(c)));
67
+ }
68
+ return;
69
+ }
70
+ entries.push(relPosix);
71
+ };
72
+ await walk('lancedb');
73
+ await tar.c({
74
+ gzip: { portable: true },
75
+ file: archivePath,
76
+ cwd: gitAiDir,
77
+ portable: true,
78
+ mtime: new Date(0),
79
+ }, entries.sort((a, b) => a.localeCompare(b)));
80
+ return { archivePath, packed: true };
81
+ }
82
+ async function unpackLanceDb(repoRoot) {
83
+ const { gitAiDir, lancedbDir, archivePath } = getArchivePaths(repoRoot);
84
+ if (!await fs_extra_1.default.pathExists(archivePath)) {
85
+ return { archivePath, unpacked: false };
86
+ }
87
+ await fs_extra_1.default.ensureDir(gitAiDir);
88
+ await fs_extra_1.default.remove(lancedbDir);
89
+ await tar.x({ file: archivePath, cwd: gitAiDir });
90
+ return { archivePath, unpacked: true };
91
+ }