bluera-knowledge 0.11.9 → 0.11.10
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/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +15 -0
- package/dist/index.js +126 -110
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/plugin/commands.test.ts +2 -1
- package/src/plugin/commands.ts +194 -164
package/package.json
CHANGED
|
@@ -9,9 +9,10 @@ import {
|
|
|
9
9
|
} from './commands.js';
|
|
10
10
|
import type { ServiceContainer } from '../services/index.js';
|
|
11
11
|
|
|
12
|
-
// Mock the createServices
|
|
12
|
+
// Mock the createServices and destroyServices functions
|
|
13
13
|
vi.mock('../services/index.js', () => ({
|
|
14
14
|
createServices: vi.fn(),
|
|
15
|
+
destroyServices: vi.fn().mockResolvedValue(undefined),
|
|
15
16
|
}));
|
|
16
17
|
|
|
17
18
|
// Mock extractRepoName
|
package/src/plugin/commands.ts
CHANGED
|
@@ -2,7 +2,7 @@ import ora from 'ora';
|
|
|
2
2
|
import { extractRepoName } from './git-clone.js';
|
|
3
3
|
import { DependencyUsageAnalyzer } from '../analysis/dependency-usage-analyzer.js';
|
|
4
4
|
import { RepoUrlResolver } from '../analysis/repo-url-resolver.js';
|
|
5
|
-
import { createServices } from '../services/index.js';
|
|
5
|
+
import { createServices, destroyServices } from '../services/index.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Options passed from CLI global options to plugin command handlers.
|
|
@@ -22,37 +22,42 @@ export async function handleSearch(args: {
|
|
|
22
22
|
}): Promise<void> {
|
|
23
23
|
// PWD is set by Claude Code to user's project directory
|
|
24
24
|
const services = await createServices(undefined, undefined, process.env['PWD']);
|
|
25
|
-
const storeNames = args.stores?.split(',').map((s: string) => s.trim());
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
storeNames !== undefined ? allStores.filter((s) => storeNames.includes(s.name)) : allStores;
|
|
26
|
+
try {
|
|
27
|
+
const storeNames = args.stores?.split(',').map((s: string) => s.trim());
|
|
30
28
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
29
|
+
const allStores = await services.store.list();
|
|
30
|
+
const targetStores =
|
|
31
|
+
storeNames !== undefined ? allStores.filter((s) => storeNames.includes(s.name)) : allStores;
|
|
35
32
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
33
|
+
if (targetStores.length === 0) {
|
|
34
|
+
console.error('No stores found to search');
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
40
37
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
mode: 'hybrid',
|
|
45
|
-
limit: parseInt(args.limit ?? '10', 10),
|
|
46
|
-
detail: 'contextual',
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
console.log(`Found ${String(results.totalResults)} results:\n`);
|
|
50
|
-
for (const r of results.results) {
|
|
51
|
-
if (r.summary !== undefined) {
|
|
52
|
-
console.log(`Score: ${r.score.toFixed(2)} - ${r.summary.location}`);
|
|
53
|
-
console.log(r.summary.purpose);
|
|
38
|
+
// Initialize stores
|
|
39
|
+
for (const store of targetStores) {
|
|
40
|
+
await services.lance.initialize(store.id);
|
|
54
41
|
}
|
|
55
|
-
|
|
42
|
+
|
|
43
|
+
const results = await services.search.search({
|
|
44
|
+
query: args.query,
|
|
45
|
+
stores: targetStores.map((s) => s.id),
|
|
46
|
+
mode: 'hybrid',
|
|
47
|
+
limit: parseInt(args.limit ?? '10', 10),
|
|
48
|
+
detail: 'contextual',
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
console.log(`Found ${String(results.totalResults)} results:\n`);
|
|
52
|
+
for (const r of results.results) {
|
|
53
|
+
if (r.summary !== undefined) {
|
|
54
|
+
console.log(`Score: ${r.score.toFixed(2)} - ${r.summary.location}`);
|
|
55
|
+
console.log(r.summary.purpose);
|
|
56
|
+
}
|
|
57
|
+
console.log('---');
|
|
58
|
+
}
|
|
59
|
+
} finally {
|
|
60
|
+
await destroyServices(services);
|
|
56
61
|
}
|
|
57
62
|
}
|
|
58
63
|
|
|
@@ -69,35 +74,40 @@ export async function handleAddRepo(
|
|
|
69
74
|
options.dataDir,
|
|
70
75
|
options.projectRoot ?? process.env['PWD']
|
|
71
76
|
);
|
|
72
|
-
const storeName = args.name ?? extractRepoName(args.url);
|
|
73
77
|
|
|
74
|
-
|
|
78
|
+
try {
|
|
79
|
+
const storeName = args.name ?? extractRepoName(args.url);
|
|
75
80
|
|
|
76
|
-
|
|
77
|
-
name: storeName,
|
|
78
|
-
type: 'repo',
|
|
79
|
-
url: args.url,
|
|
80
|
-
...(args.branch !== undefined ? { branch: args.branch } : {}),
|
|
81
|
-
});
|
|
81
|
+
console.log(`Cloning ${args.url}...`);
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
83
|
+
const result = await services.store.create({
|
|
84
|
+
name: storeName,
|
|
85
|
+
type: 'repo',
|
|
86
|
+
url: args.url,
|
|
87
|
+
...(args.branch !== undefined ? { branch: args.branch } : {}),
|
|
88
|
+
});
|
|
87
89
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
if (!result.success) {
|
|
91
|
+
console.error(`Error: ${result.error.message}`);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
console.log(`Created store: ${storeName} (${result.data.id})`);
|
|
96
|
+
if ('path' in result.data) {
|
|
97
|
+
console.log(`Location: ${result.data.path}`);
|
|
98
|
+
}
|
|
92
99
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
100
|
+
// Auto-index
|
|
101
|
+
console.log('\nIndexing...');
|
|
102
|
+
const indexResult = await services.index.indexStore(result.data);
|
|
96
103
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
104
|
+
if (indexResult.success) {
|
|
105
|
+
console.log(`Indexed ${String(indexResult.data.documentsIndexed)} files`);
|
|
106
|
+
} else {
|
|
107
|
+
console.error(`Indexing failed: ${indexResult.error.message}`);
|
|
108
|
+
}
|
|
109
|
+
} finally {
|
|
110
|
+
await destroyServices(services);
|
|
101
111
|
}
|
|
102
112
|
}
|
|
103
113
|
|
|
@@ -110,58 +120,68 @@ export async function handleAddFolder(
|
|
|
110
120
|
options.dataDir,
|
|
111
121
|
options.projectRoot ?? process.env['PWD']
|
|
112
122
|
);
|
|
113
|
-
const { basename } = await import('node:path');
|
|
114
|
-
const storeName = args.name ?? basename(args.path);
|
|
115
123
|
|
|
116
|
-
|
|
124
|
+
try {
|
|
125
|
+
const { basename } = await import('node:path');
|
|
126
|
+
const storeName = args.name ?? basename(args.path);
|
|
117
127
|
|
|
118
|
-
|
|
119
|
-
name: storeName,
|
|
120
|
-
type: 'file',
|
|
121
|
-
path: args.path,
|
|
122
|
-
});
|
|
128
|
+
console.log(`Adding folder: ${args.path}...`);
|
|
123
129
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
130
|
+
const result = await services.store.create({
|
|
131
|
+
name: storeName,
|
|
132
|
+
type: 'file',
|
|
133
|
+
path: args.path,
|
|
134
|
+
});
|
|
128
135
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
136
|
+
if (!result.success) {
|
|
137
|
+
console.error(`Error: ${result.error.message}`);
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
console.log(`Created store: ${storeName} (${result.data.id})`);
|
|
142
|
+
if ('path' in result.data) {
|
|
143
|
+
console.log(`Location: ${result.data.path}`);
|
|
144
|
+
}
|
|
133
145
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
146
|
+
// Auto-index
|
|
147
|
+
console.log('\nIndexing...');
|
|
148
|
+
const indexResult = await services.index.indexStore(result.data);
|
|
137
149
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
150
|
+
if (indexResult.success) {
|
|
151
|
+
console.log(`Indexed ${String(indexResult.data.documentsIndexed)} files`);
|
|
152
|
+
} else {
|
|
153
|
+
console.error(`Indexing failed: ${indexResult.error.message}`);
|
|
154
|
+
}
|
|
155
|
+
} finally {
|
|
156
|
+
await destroyServices(services);
|
|
142
157
|
}
|
|
143
158
|
}
|
|
144
159
|
|
|
145
160
|
export async function handleIndex(args: { store: string }): Promise<void> {
|
|
146
161
|
// PWD is set by Claude Code to user's project directory
|
|
147
162
|
const services = await createServices(undefined, undefined, process.env['PWD']);
|
|
148
|
-
const store = await services.store.getByIdOrName(args.store);
|
|
149
163
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
process.exit(1);
|
|
153
|
-
}
|
|
164
|
+
try {
|
|
165
|
+
const store = await services.store.getByIdOrName(args.store);
|
|
154
166
|
|
|
155
|
-
|
|
156
|
-
|
|
167
|
+
if (store === undefined) {
|
|
168
|
+
console.error(`Store not found: ${args.store}`);
|
|
169
|
+
process.exit(1);
|
|
170
|
+
}
|
|
157
171
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
)
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
172
|
+
console.log(`Indexing ${store.name}...`);
|
|
173
|
+
const result = await services.index.indexStore(store);
|
|
174
|
+
|
|
175
|
+
if (result.success) {
|
|
176
|
+
console.log(
|
|
177
|
+
`Indexed ${String(result.data.documentsIndexed)} documents in ${String(result.data.timeMs)}ms`
|
|
178
|
+
);
|
|
179
|
+
} else {
|
|
180
|
+
console.error(`Error: ${result.error.message}`);
|
|
181
|
+
process.exit(1);
|
|
182
|
+
}
|
|
183
|
+
} finally {
|
|
184
|
+
await destroyServices(services);
|
|
165
185
|
}
|
|
166
186
|
}
|
|
167
187
|
|
|
@@ -171,34 +191,39 @@ export async function handleStores(options: CommandOptions = {}): Promise<void>
|
|
|
171
191
|
options.dataDir,
|
|
172
192
|
options.projectRoot ?? process.env['PWD']
|
|
173
193
|
);
|
|
174
|
-
const stores = await services.store.list();
|
|
175
|
-
|
|
176
|
-
if (stores.length === 0) {
|
|
177
|
-
console.log('No stores found.');
|
|
178
|
-
console.log('\nCreate a store with:');
|
|
179
|
-
console.log(' /bluera-knowledge:add-repo <url> --name=<name>');
|
|
180
|
-
console.log(' /bluera-knowledge:add-folder <path> --name=<name>');
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
194
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
let source = '';
|
|
194
|
-
|
|
195
|
-
if ('url' in store && store.url !== undefined) {
|
|
196
|
-
source = store.url;
|
|
197
|
-
} else if ('path' in store) {
|
|
198
|
-
source = store.path;
|
|
195
|
+
try {
|
|
196
|
+
const stores = await services.store.list();
|
|
197
|
+
|
|
198
|
+
if (stores.length === 0) {
|
|
199
|
+
console.log('No stores found.');
|
|
200
|
+
console.log('\nCreate a store with:');
|
|
201
|
+
console.log(' /bluera-knowledge:add-repo <url> --name=<name>');
|
|
202
|
+
console.log(' /bluera-knowledge:add-folder <path> --name=<name>');
|
|
203
|
+
return;
|
|
199
204
|
}
|
|
200
205
|
|
|
201
|
-
|
|
206
|
+
// Table header
|
|
207
|
+
console.log('| Name | Type | ID | Source |');
|
|
208
|
+
console.log('|------|------|----|--------------------|');
|
|
209
|
+
|
|
210
|
+
// Table rows
|
|
211
|
+
for (const store of stores) {
|
|
212
|
+
const name = store.name;
|
|
213
|
+
const type = store.type;
|
|
214
|
+
const id = store.id;
|
|
215
|
+
let source = '';
|
|
216
|
+
|
|
217
|
+
if ('url' in store && store.url !== undefined) {
|
|
218
|
+
source = store.url;
|
|
219
|
+
} else if ('path' in store) {
|
|
220
|
+
source = store.path;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
console.log(`| ${name} | ${type} | ${id.substring(0, 8)}... | ${source} |`);
|
|
224
|
+
}
|
|
225
|
+
} finally {
|
|
226
|
+
await destroyServices(services);
|
|
202
227
|
}
|
|
203
228
|
}
|
|
204
229
|
|
|
@@ -209,73 +234,78 @@ export async function handleSuggest(options: CommandOptions = {}): Promise<void>
|
|
|
209
234
|
|
|
210
235
|
// Create analyzer instance
|
|
211
236
|
const services = await createServices(options.config, options.dataDir, projectRoot);
|
|
212
|
-
const analyzer = new DependencyUsageAnalyzer();
|
|
213
|
-
const resolver = new RepoUrlResolver();
|
|
214
|
-
|
|
215
|
-
// Analyze with progress indicator
|
|
216
|
-
const spinner = ora('Scanning source files...').start();
|
|
217
237
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
238
|
+
try {
|
|
239
|
+
const analyzer = new DependencyUsageAnalyzer();
|
|
240
|
+
const resolver = new RepoUrlResolver();
|
|
221
241
|
|
|
222
|
-
|
|
242
|
+
// Analyze with progress indicator
|
|
243
|
+
const spinner = ora('Scanning source files...').start();
|
|
223
244
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
const { usages, totalFilesScanned, skippedFiles } = result.data;
|
|
245
|
+
const result = await analyzer.analyze(projectRoot, (current, total, message) => {
|
|
246
|
+
spinner.text = `${message} (${String(current)}/${String(total)})`;
|
|
247
|
+
});
|
|
230
248
|
|
|
231
|
-
|
|
232
|
-
`✔ Scanned ${String(totalFilesScanned)} files${skippedFiles > 0 ? ` (skipped ${String(skippedFiles)})` : ''}\n`
|
|
233
|
-
);
|
|
249
|
+
spinner.stop();
|
|
234
250
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
}
|
|
251
|
+
if (!result.success) {
|
|
252
|
+
console.error(`Error: ${result.error.message}`);
|
|
253
|
+
process.exit(1);
|
|
254
|
+
}
|
|
240
255
|
|
|
241
|
-
|
|
242
|
-
const existingStores = await services.store.list();
|
|
243
|
-
const existingRepoNames = new Set(existingStores.map((s) => s.name));
|
|
256
|
+
const { usages, totalFilesScanned, skippedFiles } = result.data;
|
|
244
257
|
|
|
245
|
-
|
|
258
|
+
console.log(
|
|
259
|
+
`✔ Scanned ${String(totalFilesScanned)} files${skippedFiles > 0 ? ` (skipped ${String(skippedFiles)})` : ''}\n`
|
|
260
|
+
);
|
|
246
261
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
262
|
+
if (usages.length === 0) {
|
|
263
|
+
console.log('No external dependencies found in this project.');
|
|
264
|
+
console.log('\nMake sure you have a package.json or requirements.txt file.');
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
251
267
|
|
|
252
|
-
|
|
253
|
-
|
|
268
|
+
// Filter out packages already in stores
|
|
269
|
+
const existingStores = await services.store.list();
|
|
270
|
+
const existingRepoNames = new Set(existingStores.map((s) => s.name));
|
|
254
271
|
|
|
255
|
-
|
|
256
|
-
topSuggestions.forEach((usage, i) => {
|
|
257
|
-
console.log(`${String(i + 1)}. ${usage.packageName}`);
|
|
258
|
-
console.log(
|
|
259
|
-
` ${String(usage.importCount)} imports across ${String(usage.fileCount)} files\n`
|
|
260
|
-
);
|
|
261
|
-
});
|
|
272
|
+
const newUsages = usages.filter((u) => !existingRepoNames.has(u.packageName));
|
|
262
273
|
|
|
263
|
-
|
|
274
|
+
if (newUsages.length === 0) {
|
|
275
|
+
console.log('✔ All dependencies are already in knowledge stores!');
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
264
278
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
const repoResult = await resolver.findRepoUrl(usage.packageName, usage.language);
|
|
279
|
+
// Show top 5 suggestions
|
|
280
|
+
const topSuggestions = newUsages.slice(0, 5);
|
|
268
281
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
console.log(
|
|
272
|
-
} else {
|
|
273
|
-
console.log(`✗ ${usage.packageName}: Could not find repository URL`);
|
|
282
|
+
console.log('Top dependencies by usage in this project:\n');
|
|
283
|
+
topSuggestions.forEach((usage, i) => {
|
|
284
|
+
console.log(`${String(i + 1)}. ${usage.packageName}`);
|
|
274
285
|
console.log(
|
|
275
|
-
`
|
|
286
|
+
` ${String(usage.importCount)} imports across ${String(usage.fileCount)} files\n`
|
|
276
287
|
);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
console.log('Searching for repository URLs...\n');
|
|
291
|
+
|
|
292
|
+
// For each package, find repo URL
|
|
293
|
+
for (const usage of topSuggestions) {
|
|
294
|
+
const repoResult = await resolver.findRepoUrl(usage.packageName, usage.language);
|
|
295
|
+
|
|
296
|
+
if (repoResult.url !== null) {
|
|
297
|
+
console.log(`✔ ${usage.packageName}: ${repoResult.url}`);
|
|
298
|
+
console.log(` /bluera-knowledge:add-repo ${repoResult.url} --name=${usage.packageName}\n`);
|
|
299
|
+
} else {
|
|
300
|
+
console.log(`✗ ${usage.packageName}: Could not find repository URL`);
|
|
301
|
+
console.log(
|
|
302
|
+
` You can manually add it: /bluera-knowledge:add-repo <url> --name=${usage.packageName}\n`
|
|
303
|
+
);
|
|
304
|
+
}
|
|
277
305
|
}
|
|
278
|
-
}
|
|
279
306
|
|
|
280
|
-
|
|
307
|
+
console.log('Use the commands above to add repositories to your knowledge stores.');
|
|
308
|
+
} finally {
|
|
309
|
+
await destroyServices(services);
|
|
310
|
+
}
|
|
281
311
|
}
|