@mcampa/ai-context-cli 0.0.1-beta.c518c19
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/LICENSE +21 -0
- package/README.md +257 -0
- package/bin/ai-context-index.js +46 -0
- package/dist/config-loader.d.ts +13 -0
- package/dist/config-loader.d.ts.map +1 -0
- package/dist/config-loader.js +196 -0
- package/dist/config-loader.js.map +1 -0
- package/dist/config-loader.test.d.ts +2 -0
- package/dist/config-loader.test.d.ts.map +1 -0
- package/dist/config-loader.test.js +236 -0
- package/dist/config-loader.test.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +59 -0
- package/dist/index.js.map +1 -0
- package/dist/indexer.d.ts +9 -0
- package/dist/indexer.d.ts.map +1 -0
- package/dist/indexer.js +97 -0
- package/dist/indexer.js.map +1 -0
- package/dist/indexer.test.d.ts +2 -0
- package/dist/indexer.test.d.ts.map +1 -0
- package/dist/indexer.test.js +160 -0
- package/dist/indexer.test.js.map +1 -0
- package/dist/types.d.ts +78 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/types.test.d.ts +2 -0
- package/dist/types.test.d.ts.map +1 -0
- package/dist/types.test.js +137 -0
- package/dist/types.test.js.map +1 -0
- package/package.json +50 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
2
|
+
import { existsSync, mkdirSync, writeFileSync, rmSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { findConfigFile, loadConfig } from "./config-loader.js";
|
|
5
|
+
// Test directory for temporary files
|
|
6
|
+
const TEST_DIR = join(process.cwd(), ".test-temp");
|
|
7
|
+
// Counter for unique file names to avoid ESM import caching
|
|
8
|
+
let testCounter = 0;
|
|
9
|
+
function getUniqueConfigPath(ext = "js") {
|
|
10
|
+
testCounter++;
|
|
11
|
+
return join(TEST_DIR, `test-config-${testCounter}.${ext}`);
|
|
12
|
+
}
|
|
13
|
+
describe("config-loader", () => {
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
// Create test directory
|
|
16
|
+
if (!existsSync(TEST_DIR)) {
|
|
17
|
+
mkdirSync(TEST_DIR, { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
// Clean up test directory
|
|
22
|
+
if (existsSync(TEST_DIR)) {
|
|
23
|
+
rmSync(TEST_DIR, { recursive: true, force: true });
|
|
24
|
+
}
|
|
25
|
+
vi.restoreAllMocks();
|
|
26
|
+
});
|
|
27
|
+
describe("findConfigFile", () => {
|
|
28
|
+
it("should return null when no config file exists", () => {
|
|
29
|
+
const result = findConfigFile(TEST_DIR);
|
|
30
|
+
expect(result).toBeNull();
|
|
31
|
+
});
|
|
32
|
+
it("should find ai-context.config.ts", () => {
|
|
33
|
+
const configPath = join(TEST_DIR, "ai-context.config.ts");
|
|
34
|
+
writeFileSync(configPath, "export default {};");
|
|
35
|
+
const result = findConfigFile(TEST_DIR);
|
|
36
|
+
expect(result).toBe(configPath);
|
|
37
|
+
});
|
|
38
|
+
it("should find ai-context.config.js", () => {
|
|
39
|
+
const configPath = join(TEST_DIR, "ai-context.config.js");
|
|
40
|
+
writeFileSync(configPath, "export default {};");
|
|
41
|
+
const result = findConfigFile(TEST_DIR);
|
|
42
|
+
expect(result).toBe(configPath);
|
|
43
|
+
});
|
|
44
|
+
it("should prefer .ts over .js", () => {
|
|
45
|
+
const tsConfigPath = join(TEST_DIR, "ai-context.config.ts");
|
|
46
|
+
const jsConfigPath = join(TEST_DIR, "ai-context.config.js");
|
|
47
|
+
writeFileSync(tsConfigPath, "export default {};");
|
|
48
|
+
writeFileSync(jsConfigPath, "export default {};");
|
|
49
|
+
const result = findConfigFile(TEST_DIR);
|
|
50
|
+
expect(result).toBe(tsConfigPath);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
describe("loadConfig", () => {
|
|
54
|
+
it("should throw error when no config file found", async () => {
|
|
55
|
+
const originalCwd = process.cwd();
|
|
56
|
+
process.chdir(TEST_DIR);
|
|
57
|
+
try {
|
|
58
|
+
await expect(loadConfig()).rejects.toThrow("No config file found");
|
|
59
|
+
}
|
|
60
|
+
finally {
|
|
61
|
+
process.chdir(originalCwd);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
it("should throw error when specified config file not found", async () => {
|
|
65
|
+
await expect(loadConfig("/nonexistent/config.ts")).rejects.toThrow("Config file not found");
|
|
66
|
+
});
|
|
67
|
+
it("should load and validate a valid JavaScript config", async () => {
|
|
68
|
+
const configPath = getUniqueConfigPath("js");
|
|
69
|
+
const configContent = `
|
|
70
|
+
export default {
|
|
71
|
+
name: "test-project",
|
|
72
|
+
embeddingConfig: {
|
|
73
|
+
apiKey: "test-key",
|
|
74
|
+
model: "text-embedding-3-small",
|
|
75
|
+
},
|
|
76
|
+
vectorDatabaseConfig: {
|
|
77
|
+
address: "localhost:19530",
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
`;
|
|
81
|
+
writeFileSync(configPath, configContent);
|
|
82
|
+
const config = await loadConfig(configPath);
|
|
83
|
+
expect(config.name).toBe("test-project");
|
|
84
|
+
expect(config.embeddingConfig?.apiKey).toBe("test-key");
|
|
85
|
+
expect(config.embeddingConfig?.model).toBe("text-embedding-3-small");
|
|
86
|
+
expect(config.vectorDatabaseConfig?.address).toBe("localhost:19530");
|
|
87
|
+
});
|
|
88
|
+
it("should throw error for missing embeddingConfig", async () => {
|
|
89
|
+
const configPath = getUniqueConfigPath("js");
|
|
90
|
+
const configContent = `
|
|
91
|
+
export default {
|
|
92
|
+
vectorDatabaseConfig: {
|
|
93
|
+
address: "localhost:19530",
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
`;
|
|
97
|
+
writeFileSync(configPath, configContent);
|
|
98
|
+
await expect(loadConfig(configPath)).rejects.toThrow("Missing required field: embeddingConfig");
|
|
99
|
+
});
|
|
100
|
+
it("should throw error for missing embeddingConfig.apiKey", async () => {
|
|
101
|
+
const configPath = getUniqueConfigPath("js");
|
|
102
|
+
const configContent = `
|
|
103
|
+
export default {
|
|
104
|
+
embeddingConfig: {
|
|
105
|
+
model: "text-embedding-3-small",
|
|
106
|
+
},
|
|
107
|
+
vectorDatabaseConfig: {
|
|
108
|
+
address: "localhost:19530",
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
`;
|
|
112
|
+
writeFileSync(configPath, configContent);
|
|
113
|
+
await expect(loadConfig(configPath)).rejects.toThrow("Missing required field: embeddingConfig.apiKey");
|
|
114
|
+
});
|
|
115
|
+
it("should throw error for missing embeddingConfig.model", async () => {
|
|
116
|
+
const configPath = getUniqueConfigPath("js");
|
|
117
|
+
const configContent = `
|
|
118
|
+
export default {
|
|
119
|
+
embeddingConfig: {
|
|
120
|
+
apiKey: "test-key",
|
|
121
|
+
},
|
|
122
|
+
vectorDatabaseConfig: {
|
|
123
|
+
address: "localhost:19530",
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
`;
|
|
127
|
+
writeFileSync(configPath, configContent);
|
|
128
|
+
await expect(loadConfig(configPath)).rejects.toThrow("Missing required field: embeddingConfig.model");
|
|
129
|
+
});
|
|
130
|
+
it("should throw error for missing vectorDatabaseConfig", async () => {
|
|
131
|
+
const configPath = getUniqueConfigPath("js");
|
|
132
|
+
const configContent = `
|
|
133
|
+
export default {
|
|
134
|
+
embeddingConfig: {
|
|
135
|
+
apiKey: "test-key",
|
|
136
|
+
model: "text-embedding-3-small",
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
`;
|
|
140
|
+
writeFileSync(configPath, configContent);
|
|
141
|
+
await expect(loadConfig(configPath)).rejects.toThrow("Missing required field: vectorDatabaseConfig");
|
|
142
|
+
});
|
|
143
|
+
it("should throw error when vectorDatabaseConfig has neither address nor token", async () => {
|
|
144
|
+
const configPath = getUniqueConfigPath("js");
|
|
145
|
+
const configContent = `
|
|
146
|
+
export default {
|
|
147
|
+
embeddingConfig: {
|
|
148
|
+
apiKey: "test-key",
|
|
149
|
+
model: "text-embedding-3-small",
|
|
150
|
+
},
|
|
151
|
+
vectorDatabaseConfig: {
|
|
152
|
+
ssl: true,
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
`;
|
|
156
|
+
writeFileSync(configPath, configContent);
|
|
157
|
+
await expect(loadConfig(configPath)).rejects.toThrow("vectorDatabaseConfig requires either 'address' or 'token' to be set");
|
|
158
|
+
});
|
|
159
|
+
it("should accept config with token instead of address", async () => {
|
|
160
|
+
const configPath = getUniqueConfigPath("js");
|
|
161
|
+
const configContent = `
|
|
162
|
+
export default {
|
|
163
|
+
embeddingConfig: {
|
|
164
|
+
apiKey: "test-key",
|
|
165
|
+
model: "text-embedding-3-small",
|
|
166
|
+
},
|
|
167
|
+
vectorDatabaseConfig: {
|
|
168
|
+
token: "zilliz-token",
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
`;
|
|
172
|
+
writeFileSync(configPath, configContent);
|
|
173
|
+
const config = await loadConfig(configPath);
|
|
174
|
+
expect(config.vectorDatabaseConfig?.token).toBe("zilliz-token");
|
|
175
|
+
});
|
|
176
|
+
it("should throw error for invalid supportedExtensions type", async () => {
|
|
177
|
+
const configPath = getUniqueConfigPath("js");
|
|
178
|
+
const configContent = `
|
|
179
|
+
export default {
|
|
180
|
+
embeddingConfig: {
|
|
181
|
+
apiKey: "test-key",
|
|
182
|
+
model: "text-embedding-3-small",
|
|
183
|
+
},
|
|
184
|
+
vectorDatabaseConfig: {
|
|
185
|
+
address: "localhost:19530",
|
|
186
|
+
},
|
|
187
|
+
supportedExtensions: "not-an-array",
|
|
188
|
+
};
|
|
189
|
+
`;
|
|
190
|
+
writeFileSync(configPath, configContent);
|
|
191
|
+
await expect(loadConfig(configPath)).rejects.toThrow("supportedExtensions must be an array of strings");
|
|
192
|
+
});
|
|
193
|
+
it("should throw error for non-string values in supportedExtensions", async () => {
|
|
194
|
+
const configPath = getUniqueConfigPath("js");
|
|
195
|
+
const configContent = `
|
|
196
|
+
export default {
|
|
197
|
+
embeddingConfig: {
|
|
198
|
+
apiKey: "test-key",
|
|
199
|
+
model: "text-embedding-3-small",
|
|
200
|
+
},
|
|
201
|
+
vectorDatabaseConfig: {
|
|
202
|
+
address: "localhost:19530",
|
|
203
|
+
},
|
|
204
|
+
supportedExtensions: [".ts", 123],
|
|
205
|
+
};
|
|
206
|
+
`;
|
|
207
|
+
writeFileSync(configPath, configContent);
|
|
208
|
+
await expect(loadConfig(configPath)).rejects.toThrow("supportedExtensions contains non-string value");
|
|
209
|
+
});
|
|
210
|
+
it("should accept valid optional arrays", async () => {
|
|
211
|
+
const configPath = getUniqueConfigPath("js");
|
|
212
|
+
const configContent = `
|
|
213
|
+
export default {
|
|
214
|
+
embeddingConfig: {
|
|
215
|
+
apiKey: "test-key",
|
|
216
|
+
model: "text-embedding-3-small",
|
|
217
|
+
},
|
|
218
|
+
vectorDatabaseConfig: {
|
|
219
|
+
address: "localhost:19530",
|
|
220
|
+
},
|
|
221
|
+
supportedExtensions: [".ts", ".js"],
|
|
222
|
+
ignorePatterns: ["node_modules/**"],
|
|
223
|
+
customExtensions: [".vue"],
|
|
224
|
+
customIgnorePatterns: ["*.test.ts"],
|
|
225
|
+
};
|
|
226
|
+
`;
|
|
227
|
+
writeFileSync(configPath, configContent);
|
|
228
|
+
const config = await loadConfig(configPath);
|
|
229
|
+
expect(config.supportedExtensions).toEqual([".ts", ".js"]);
|
|
230
|
+
expect(config.ignorePatterns).toEqual(["node_modules/**"]);
|
|
231
|
+
expect(config.customExtensions).toEqual([".vue"]);
|
|
232
|
+
expect(config.customIgnorePatterns).toEqual(["*.test.ts"]);
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
//# sourceMappingURL=config-loader.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-loader.test.js","sourceRoot":"","sources":["../src/config-loader.test.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,YAAY,EACV,aAAa,EACb,qBAAqB,EACrB,YAAY,EACZ,UAAU,EACV,WAAW,GACZ,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { readFileSync } from "fs";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
import { dirname, join } from "path";
|
|
5
|
+
import { loadConfig } from "./config-loader.js";
|
|
6
|
+
import { runIndex } from "./indexer.js";
|
|
7
|
+
// Read version from package.json to keep it in sync
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = dirname(__filename);
|
|
10
|
+
const packageJson = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"));
|
|
11
|
+
const VERSION = packageJson.version;
|
|
12
|
+
const program = new Command();
|
|
13
|
+
program
|
|
14
|
+
.name("ai-context-index")
|
|
15
|
+
.description("Index your codebase for AI-powered semantic search")
|
|
16
|
+
.version(VERSION)
|
|
17
|
+
.option("-c, --config <path>", "Path to config file (default: ai-context.config.ts/js)")
|
|
18
|
+
.option("-f, --force", "Force reindex even if collection already exists")
|
|
19
|
+
.action(async (options) => {
|
|
20
|
+
try {
|
|
21
|
+
console.log("");
|
|
22
|
+
console.log("╔════════════════════════════════════════════════════════════╗");
|
|
23
|
+
console.log("║ AI Context CLI - Codebase Indexer ║");
|
|
24
|
+
console.log("╚════════════════════════════════════════════════════════════╝");
|
|
25
|
+
console.log("");
|
|
26
|
+
// Load and validate config
|
|
27
|
+
const config = await loadConfig(options.config);
|
|
28
|
+
// Run the indexing operation
|
|
29
|
+
await runIndex(config, options.force);
|
|
30
|
+
process.exit(0);
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
console.error("");
|
|
34
|
+
console.error("❌ Error:", error instanceof Error ? error.message : String(error));
|
|
35
|
+
console.error("");
|
|
36
|
+
if (error instanceof Error && error.stack && process.env.DEBUG) {
|
|
37
|
+
console.error("Stack trace:");
|
|
38
|
+
console.error(error.stack);
|
|
39
|
+
console.error("");
|
|
40
|
+
}
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
// Handle uncaught errors
|
|
45
|
+
process.on("uncaughtException", (error) => {
|
|
46
|
+
console.error("");
|
|
47
|
+
console.error("❌ Uncaught error:", error.message);
|
|
48
|
+
if (process.env.DEBUG) {
|
|
49
|
+
console.error(error.stack);
|
|
50
|
+
}
|
|
51
|
+
process.exit(1);
|
|
52
|
+
});
|
|
53
|
+
process.on("unhandledRejection", (reason) => {
|
|
54
|
+
console.error("");
|
|
55
|
+
console.error("❌ Unhandled rejection:", reason);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
});
|
|
58
|
+
program.parse();
|
|
59
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ContextConfig, IndexResult } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Run the indexing operation with the provided configuration
|
|
4
|
+
* @param config Validated configuration object
|
|
5
|
+
* @param forceReindex Whether to force reindex even if collection exists
|
|
6
|
+
* @returns Index result with statistics
|
|
7
|
+
*/
|
|
8
|
+
export declare function runIndex(config: ContextConfig, forceReindex?: boolean): Promise<IndexResult>;
|
|
9
|
+
//# sourceMappingURL=indexer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indexer.d.ts","sourceRoot":"","sources":["../src/indexer.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AA2B7D;;;;;GAKG;AACH,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,aAAa,EACrB,YAAY,GAAE,OAAe,GAC5B,OAAO,CAAC,WAAW,CAAC,CAmFtB"}
|
package/dist/indexer.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { Context, MilvusVectorDatabase, OpenAIEmbedding, } from "@mcampa/ai-context-core";
|
|
2
|
+
/**
|
|
3
|
+
* Format a progress bar string
|
|
4
|
+
*/
|
|
5
|
+
function formatProgressBar(percentage, width = 30) {
|
|
6
|
+
const filled = Math.round((percentage / 100) * width);
|
|
7
|
+
const empty = width - filled;
|
|
8
|
+
return `[${"█".repeat(filled)}${"░".repeat(empty)}]`;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Format duration in human-readable format
|
|
12
|
+
*/
|
|
13
|
+
function formatDuration(ms) {
|
|
14
|
+
if (ms < 1000) {
|
|
15
|
+
return `${ms}ms`;
|
|
16
|
+
}
|
|
17
|
+
const seconds = Math.floor(ms / 1000);
|
|
18
|
+
if (seconds < 60) {
|
|
19
|
+
return `${seconds}s`;
|
|
20
|
+
}
|
|
21
|
+
const minutes = Math.floor(seconds / 60);
|
|
22
|
+
const remainingSeconds = seconds % 60;
|
|
23
|
+
return `${minutes}m ${remainingSeconds}s`;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Run the indexing operation with the provided configuration
|
|
27
|
+
* @param config Validated configuration object
|
|
28
|
+
* @param forceReindex Whether to force reindex even if collection exists
|
|
29
|
+
* @returns Index result with statistics
|
|
30
|
+
*/
|
|
31
|
+
export async function runIndex(config, forceReindex = false) {
|
|
32
|
+
const startTime = Date.now();
|
|
33
|
+
const cwd = process.cwd();
|
|
34
|
+
console.log("\n🚀 Starting codebase indexing...\n");
|
|
35
|
+
console.log(`📁 Target directory: ${cwd}`);
|
|
36
|
+
console.log(`📛 Context name: ${config.name || "my-context"}`);
|
|
37
|
+
if (forceReindex) {
|
|
38
|
+
console.log(`⚡ Force reindex: enabled`);
|
|
39
|
+
}
|
|
40
|
+
console.log("");
|
|
41
|
+
// Initialize embedding provider
|
|
42
|
+
console.log("🔧 Initializing OpenAI embedding provider...");
|
|
43
|
+
const embedding = new OpenAIEmbedding({
|
|
44
|
+
apiKey: config.embeddingConfig.apiKey,
|
|
45
|
+
model: config.embeddingConfig.model,
|
|
46
|
+
baseURL: config.embeddingConfig.baseURL,
|
|
47
|
+
});
|
|
48
|
+
console.log(` Model: ${config.embeddingConfig.model}`);
|
|
49
|
+
// Initialize vector database
|
|
50
|
+
console.log("🔧 Initializing Milvus vector database...");
|
|
51
|
+
const vectorDatabase = new MilvusVectorDatabase({
|
|
52
|
+
address: config.vectorDatabaseConfig.address,
|
|
53
|
+
token: config.vectorDatabaseConfig.token,
|
|
54
|
+
username: config.vectorDatabaseConfig.username,
|
|
55
|
+
password: config.vectorDatabaseConfig.password,
|
|
56
|
+
ssl: config.vectorDatabaseConfig.ssl,
|
|
57
|
+
});
|
|
58
|
+
// Create context instance
|
|
59
|
+
console.log("🔧 Creating context instance...\n");
|
|
60
|
+
const context = new Context({
|
|
61
|
+
name: config.name,
|
|
62
|
+
embedding,
|
|
63
|
+
vectorDatabase,
|
|
64
|
+
supportedExtensions: config.supportedExtensions,
|
|
65
|
+
ignorePatterns: config.ignorePatterns,
|
|
66
|
+
customExtensions: config.customExtensions,
|
|
67
|
+
customIgnorePatterns: config.customIgnorePatterns,
|
|
68
|
+
});
|
|
69
|
+
// Track last progress update to avoid console spam
|
|
70
|
+
let lastProgressLine = "";
|
|
71
|
+
// Index the codebase
|
|
72
|
+
const stats = await context.indexCodebase(cwd, (progress) => {
|
|
73
|
+
const progressLine = `${formatProgressBar(progress.percentage)} ${progress.percentage}% - ${progress.phase}`;
|
|
74
|
+
// Only update if the line changed (to reduce console noise)
|
|
75
|
+
if (progressLine !== lastProgressLine) {
|
|
76
|
+
// Clear previous line and write new one
|
|
77
|
+
process.stdout.write(`\r${progressLine.padEnd(80)}`);
|
|
78
|
+
lastProgressLine = progressLine;
|
|
79
|
+
}
|
|
80
|
+
}, forceReindex);
|
|
81
|
+
// Clear the progress line and print summary
|
|
82
|
+
process.stdout.write("\r" + " ".repeat(80) + "\r");
|
|
83
|
+
const duration = Date.now() - startTime;
|
|
84
|
+
console.log("\n✅ Indexing completed successfully!\n");
|
|
85
|
+
console.log("📊 Summary:");
|
|
86
|
+
console.log(` Files indexed: ${stats.indexedFiles}`);
|
|
87
|
+
console.log(` Total chunks: ${stats.totalChunks}`);
|
|
88
|
+
console.log(` Status: ${stats.status}`);
|
|
89
|
+
console.log(` Duration: ${formatDuration(duration)}`);
|
|
90
|
+
console.log("");
|
|
91
|
+
if (stats.status === "limit_reached") {
|
|
92
|
+
console.log("⚠️ Warning: Chunk limit was reached. Some files may not have been fully indexed.");
|
|
93
|
+
console.log("");
|
|
94
|
+
}
|
|
95
|
+
return stats;
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=indexer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indexer.js","sourceRoot":"","sources":["../src/indexer.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EACP,oBAAoB,EACpB,eAAe,GAChB,MAAM,yBAAyB,CAAC;AAGjC;;GAEG;AACH,SAAS,iBAAiB,CAAC,UAAkB,EAAE,QAAgB,EAAE;IAC/D,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,UAAU,GAAG,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC;IACtD,MAAM,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;IAC7B,OAAO,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC;AACvD,CAAC;AAED;;GAEG;AACH,SAAS,cAAc,CAAC,EAAU;IAChC,IAAI,EAAE,GAAG,IAAI,EAAE,CAAC;QACd,OAAO,GAAG,EAAE,IAAI,CAAC;IACnB,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC;IACtC,IAAI,OAAO,GAAG,EAAE,EAAE,CAAC;QACjB,OAAO,GAAG,OAAO,GAAG,CAAC;IACvB,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IACzC,MAAM,gBAAgB,GAAG,OAAO,GAAG,EAAE,CAAC;IACtC,OAAO,GAAG,OAAO,KAAK,gBAAgB,GAAG,CAAC;AAC5C,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,MAAqB,EACrB,eAAwB,KAAK;IAE7B,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAE1B,OAAO,CAAC,GAAG,CAAC,sCAAsC,CAAC,CAAC;IACpD,OAAO,CAAC,GAAG,CAAC,wBAAwB,GAAG,EAAE,CAAC,CAAC;IAC3C,OAAO,CAAC,GAAG,CAAC,oBAAoB,MAAM,CAAC,IAAI,IAAI,YAAY,EAAE,CAAC,CAAC;IAC/D,IAAI,YAAY,EAAE,CAAC;QACjB,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,gCAAgC;IAChC,OAAO,CAAC,GAAG,CAAC,8CAA8C,CAAC,CAAC;IAC5D,MAAM,SAAS,GAAG,IAAI,eAAe,CAAC;QACpC,MAAM,EAAE,MAAM,CAAC,eAAgB,CAAC,MAAM;QACtC,KAAK,EAAE,MAAM,CAAC,eAAgB,CAAC,KAAK;QACpC,OAAO,EAAE,MAAM,CAAC,eAAgB,CAAC,OAAO;KACzC,CAAC,CAAC;IACH,OAAO,CAAC,GAAG,CAAC,aAAa,MAAM,CAAC,eAAgB,CAAC,KAAK,EAAE,CAAC,CAAC;IAE1D,6BAA6B;IAC7B,OAAO,CAAC,GAAG,CAAC,2CAA2C,CAAC,CAAC;IACzD,MAAM,cAAc,GAAG,IAAI,oBAAoB,CAAC;QAC9C,OAAO,EAAE,MAAM,CAAC,oBAAqB,CAAC,OAAO;QAC7C,KAAK,EAAE,MAAM,CAAC,oBAAqB,CAAC,KAAK;QACzC,QAAQ,EAAE,MAAM,CAAC,oBAAqB,CAAC,QAAQ;QAC/C,QAAQ,EAAE,MAAM,CAAC,oBAAqB,CAAC,QAAQ;QAC/C,GAAG,EAAE,MAAM,CAAC,oBAAqB,CAAC,GAAG;KACtC,CAAC,CAAC;IAEH,0BAA0B;IAC1B,OAAO,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAC;IACjD,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC;QAC1B,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,SAAS;QACT,cAAc;QACd,mBAAmB,EAAE,MAAM,CAAC,mBAAmB;QAC/C,cAAc,EAAE,MAAM,CAAC,cAAc;QACrC,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;QACzC,oBAAoB,EAAE,MAAM,CAAC,oBAAoB;KAClD,CAAC,CAAC;IAEH,mDAAmD;IACnD,IAAI,gBAAgB,GAAG,EAAE,CAAC;IAE1B,qBAAqB;IACrB,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,aAAa,CACvC,GAAG,EACH,CAAC,QAAQ,EAAE,EAAE;QACX,MAAM,YAAY,GAAG,GAAG,iBAAiB,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,QAAQ,CAAC,UAAU,OAAO,QAAQ,CAAC,KAAK,EAAE,CAAC;QAE7G,4DAA4D;QAC5D,IAAI,YAAY,KAAK,gBAAgB,EAAE,CAAC;YACtC,wCAAwC;YACxC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;YACrD,gBAAgB,GAAG,YAAY,CAAC;QAClC,CAAC;IACH,CAAC,EACD,YAAY,CACb,CAAC;IAEF,4CAA4C;IAC5C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;IAEnD,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;IAExC,OAAO,CAAC,GAAG,CAAC,wCAAwC,CAAC,CAAC;IACtD,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IAC3B,OAAO,CAAC,GAAG,CAAC,qBAAqB,KAAK,CAAC,YAAY,EAAE,CAAC,CAAC;IACvD,OAAO,CAAC,GAAG,CAAC,oBAAoB,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC;IACrD,OAAO,CAAC,GAAG,CAAC,cAAc,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;IAC1C,OAAO,CAAC,GAAG,CAAC,gBAAgB,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACxD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,IAAI,KAAK,CAAC,MAAM,KAAK,eAAe,EAAE,CAAC;QACrC,OAAO,CAAC,GAAG,CACT,mFAAmF,CACpF,CAAC;QACF,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indexer.test.d.ts","sourceRoot":"","sources":["../src/indexer.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
// Create hoisted mock functions
|
|
3
|
+
const { mockIndexCodebase, mockContext, mockOpenAIEmbedding, mockMilvusVectorDatabase, } = vi.hoisted(() => {
|
|
4
|
+
const mockIndexCodebase = vi.fn();
|
|
5
|
+
const mockContext = vi.fn(() => ({
|
|
6
|
+
indexCodebase: mockIndexCodebase,
|
|
7
|
+
}));
|
|
8
|
+
const mockOpenAIEmbedding = vi.fn(() => ({}));
|
|
9
|
+
const mockMilvusVectorDatabase = vi.fn(() => ({}));
|
|
10
|
+
return {
|
|
11
|
+
mockIndexCodebase,
|
|
12
|
+
mockContext,
|
|
13
|
+
mockOpenAIEmbedding,
|
|
14
|
+
mockMilvusVectorDatabase,
|
|
15
|
+
};
|
|
16
|
+
});
|
|
17
|
+
// Mock the core package
|
|
18
|
+
vi.mock("@mcampa/ai-context-core", () => ({
|
|
19
|
+
Context: mockContext,
|
|
20
|
+
OpenAIEmbedding: mockOpenAIEmbedding,
|
|
21
|
+
MilvusVectorDatabase: mockMilvusVectorDatabase,
|
|
22
|
+
}));
|
|
23
|
+
// Import after mocking
|
|
24
|
+
import { runIndex } from "./indexer.js";
|
|
25
|
+
describe("indexer", () => {
|
|
26
|
+
const validConfig = {
|
|
27
|
+
name: "test-project",
|
|
28
|
+
embeddingConfig: {
|
|
29
|
+
apiKey: "test-api-key",
|
|
30
|
+
model: "text-embedding-3-small",
|
|
31
|
+
},
|
|
32
|
+
vectorDatabaseConfig: {
|
|
33
|
+
address: "localhost:19530",
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
beforeEach(() => {
|
|
37
|
+
vi.clearAllMocks();
|
|
38
|
+
// Default mock implementation
|
|
39
|
+
mockIndexCodebase.mockResolvedValue({
|
|
40
|
+
indexedFiles: 10,
|
|
41
|
+
totalChunks: 50,
|
|
42
|
+
status: "completed",
|
|
43
|
+
});
|
|
44
|
+
// Suppress console output during tests
|
|
45
|
+
vi.spyOn(console, "log").mockImplementation(() => { });
|
|
46
|
+
vi.spyOn(process.stdout, "write").mockImplementation(() => true);
|
|
47
|
+
});
|
|
48
|
+
afterEach(() => {
|
|
49
|
+
vi.restoreAllMocks();
|
|
50
|
+
});
|
|
51
|
+
describe("runIndex", () => {
|
|
52
|
+
it("should initialize OpenAIEmbedding with config", async () => {
|
|
53
|
+
await runIndex(validConfig);
|
|
54
|
+
expect(mockOpenAIEmbedding).toHaveBeenCalledWith({
|
|
55
|
+
apiKey: "test-api-key",
|
|
56
|
+
model: "text-embedding-3-small",
|
|
57
|
+
baseURL: undefined,
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
it("should initialize OpenAIEmbedding with baseURL when provided", async () => {
|
|
61
|
+
const configWithBaseURL = {
|
|
62
|
+
...validConfig,
|
|
63
|
+
embeddingConfig: {
|
|
64
|
+
...validConfig.embeddingConfig,
|
|
65
|
+
baseURL: "https://custom.api.com",
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
await runIndex(configWithBaseURL);
|
|
69
|
+
expect(mockOpenAIEmbedding).toHaveBeenCalledWith({
|
|
70
|
+
apiKey: "test-api-key",
|
|
71
|
+
model: "text-embedding-3-small",
|
|
72
|
+
baseURL: "https://custom.api.com",
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
it("should initialize MilvusVectorDatabase with config", async () => {
|
|
76
|
+
await runIndex(validConfig);
|
|
77
|
+
expect(mockMilvusVectorDatabase).toHaveBeenCalledWith({
|
|
78
|
+
address: "localhost:19530",
|
|
79
|
+
token: undefined,
|
|
80
|
+
username: undefined,
|
|
81
|
+
password: undefined,
|
|
82
|
+
ssl: undefined,
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
it("should initialize MilvusVectorDatabase with all options", async () => {
|
|
86
|
+
const fullConfig = {
|
|
87
|
+
...validConfig,
|
|
88
|
+
vectorDatabaseConfig: {
|
|
89
|
+
address: "localhost:19530",
|
|
90
|
+
token: "my-token",
|
|
91
|
+
username: "user",
|
|
92
|
+
password: "pass",
|
|
93
|
+
ssl: true,
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
await runIndex(fullConfig);
|
|
97
|
+
expect(mockMilvusVectorDatabase).toHaveBeenCalledWith({
|
|
98
|
+
address: "localhost:19530",
|
|
99
|
+
token: "my-token",
|
|
100
|
+
username: "user",
|
|
101
|
+
password: "pass",
|
|
102
|
+
ssl: true,
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
it("should create Context with all config options", async () => {
|
|
106
|
+
const fullConfig = {
|
|
107
|
+
name: "my-project",
|
|
108
|
+
embeddingConfig: {
|
|
109
|
+
apiKey: "test-key",
|
|
110
|
+
model: "text-embedding-3-small",
|
|
111
|
+
},
|
|
112
|
+
vectorDatabaseConfig: {
|
|
113
|
+
address: "localhost:19530",
|
|
114
|
+
},
|
|
115
|
+
supportedExtensions: [".ts", ".js"],
|
|
116
|
+
ignorePatterns: ["node_modules/**"],
|
|
117
|
+
customExtensions: [".vue"],
|
|
118
|
+
customIgnorePatterns: ["*.test.ts"],
|
|
119
|
+
};
|
|
120
|
+
await runIndex(fullConfig);
|
|
121
|
+
expect(mockContext).toHaveBeenCalledWith(expect.objectContaining({
|
|
122
|
+
name: "my-project",
|
|
123
|
+
supportedExtensions: [".ts", ".js"],
|
|
124
|
+
ignorePatterns: ["node_modules/**"],
|
|
125
|
+
customExtensions: [".vue"],
|
|
126
|
+
customIgnorePatterns: ["*.test.ts"],
|
|
127
|
+
}));
|
|
128
|
+
});
|
|
129
|
+
it("should return indexing statistics", async () => {
|
|
130
|
+
const result = await runIndex(validConfig);
|
|
131
|
+
expect(result).toEqual({
|
|
132
|
+
indexedFiles: 10,
|
|
133
|
+
totalChunks: 50,
|
|
134
|
+
status: "completed",
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
it("should pass forceReindex flag to indexCodebase", async () => {
|
|
138
|
+
await runIndex(validConfig, true);
|
|
139
|
+
expect(mockIndexCodebase).toHaveBeenCalledWith(expect.any(String), expect.any(Function), true);
|
|
140
|
+
});
|
|
141
|
+
it("should call indexCodebase with progress callback", async () => {
|
|
142
|
+
await runIndex(validConfig);
|
|
143
|
+
expect(mockIndexCodebase).toHaveBeenCalledWith(expect.any(String), expect.any(Function), false);
|
|
144
|
+
});
|
|
145
|
+
it("should handle limit_reached status", async () => {
|
|
146
|
+
mockIndexCodebase.mockResolvedValue({
|
|
147
|
+
indexedFiles: 100,
|
|
148
|
+
totalChunks: 450000,
|
|
149
|
+
status: "limit_reached",
|
|
150
|
+
});
|
|
151
|
+
const result = await runIndex(validConfig);
|
|
152
|
+
expect(result.status).toBe("limit_reached");
|
|
153
|
+
});
|
|
154
|
+
it("should use process.cwd() as the indexing path", async () => {
|
|
155
|
+
await runIndex(validConfig);
|
|
156
|
+
expect(mockIndexCodebase).toHaveBeenCalledWith(process.cwd(), expect.any(Function), false);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
//# sourceMappingURL=indexer.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indexer.test.js","sourceRoot":"","sources":["../src/indexer.test.ts"],"names":[],"mappings":"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"}
|