bluera-knowledge 0.9.31 → 0.9.34
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/commands/code-review.md +15 -0
- package/.claude/hooks/post-edit-check.sh +5 -3
- package/.claude/skills/atomic-commits/SKILL.md +3 -1
- package/.claude/skills/code-review-repo/skill.md +62 -0
- package/.husky/pre-commit +3 -2
- package/.prettierrc +9 -0
- package/.versionrc.json +1 -1
- package/CHANGELOG.md +35 -0
- package/CLAUDE.md +6 -0
- package/README.md +25 -13
- package/bun.lock +277 -33
- package/dist/{chunk-L2YVNC63.js → chunk-6FHWC36B.js} +9 -1
- package/dist/chunk-6FHWC36B.js.map +1 -0
- package/dist/{chunk-2SJHNRXD.js → chunk-DC7CGSGT.js} +288 -241
- package/dist/chunk-DC7CGSGT.js.map +1 -0
- package/dist/{chunk-RWSXP3PQ.js → chunk-WFNPNAAP.js} +3194 -3024
- package/dist/chunk-WFNPNAAP.js.map +1 -0
- package/dist/{chunk-OGEY66FZ.js → chunk-Z2KKVH45.js} +548 -482
- package/dist/chunk-Z2KKVH45.js.map +1 -0
- package/dist/index.js +871 -754
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.js +3 -3
- package/dist/watch.service-BJV3TI3F.js +7 -0
- package/dist/workers/background-worker-cli.js +46 -45
- package/dist/workers/background-worker-cli.js.map +1 -1
- package/eslint.config.js +43 -1
- package/package.json +18 -11
- package/plugin.json +8 -0
- package/python/requirements.txt +1 -1
- package/src/analysis/ast-parser.test.ts +12 -11
- package/src/analysis/ast-parser.ts +28 -22
- package/src/analysis/code-graph.test.ts +52 -62
- package/src/analysis/code-graph.ts +9 -13
- package/src/analysis/dependency-usage-analyzer.test.ts +91 -271
- package/src/analysis/dependency-usage-analyzer.ts +52 -24
- package/src/analysis/go-ast-parser.test.ts +22 -22
- package/src/analysis/go-ast-parser.ts +18 -25
- package/src/analysis/parser-factory.test.ts +9 -9
- package/src/analysis/parser-factory.ts +3 -3
- package/src/analysis/python-ast-parser.test.ts +27 -27
- package/src/analysis/python-ast-parser.ts +2 -2
- package/src/analysis/repo-url-resolver.test.ts +82 -82
- package/src/analysis/rust-ast-parser.test.ts +19 -19
- package/src/analysis/rust-ast-parser.ts +17 -27
- package/src/analysis/tree-sitter-parser.test.ts +3 -3
- package/src/analysis/tree-sitter-parser.ts +10 -16
- package/src/cli/commands/crawl.test.ts +40 -24
- package/src/cli/commands/crawl.ts +186 -161
- package/src/cli/commands/index-cmd.test.ts +90 -90
- package/src/cli/commands/index-cmd.ts +52 -36
- package/src/cli/commands/mcp.test.ts +6 -6
- package/src/cli/commands/mcp.ts +2 -2
- package/src/cli/commands/plugin-api.test.ts +16 -18
- package/src/cli/commands/plugin-api.ts +9 -6
- package/src/cli/commands/search.test.ts +16 -7
- package/src/cli/commands/search.ts +124 -87
- package/src/cli/commands/serve.test.ts +67 -25
- package/src/cli/commands/serve.ts +18 -3
- package/src/cli/commands/setup.test.ts +176 -101
- package/src/cli/commands/setup.ts +140 -117
- package/src/cli/commands/store.test.ts +82 -53
- package/src/cli/commands/store.ts +56 -37
- package/src/cli/program.ts +2 -2
- package/src/crawl/article-converter.test.ts +4 -1
- package/src/crawl/article-converter.ts +46 -31
- package/src/crawl/bridge.test.ts +240 -132
- package/src/crawl/bridge.ts +87 -30
- package/src/crawl/claude-client.test.ts +124 -56
- package/src/crawl/claude-client.ts +7 -15
- package/src/crawl/intelligent-crawler.test.ts +65 -22
- package/src/crawl/intelligent-crawler.ts +86 -53
- package/src/crawl/markdown-utils.ts +1 -4
- package/src/db/embeddings.ts +4 -6
- package/src/db/lance.test.ts +63 -4
- package/src/db/lance.ts +31 -12
- package/src/index.ts +26 -17
- package/src/logging/index.ts +1 -5
- package/src/logging/logger.ts +3 -5
- package/src/logging/payload.test.ts +1 -1
- package/src/logging/payload.ts +3 -5
- package/src/mcp/commands/index.ts +2 -2
- package/src/mcp/commands/job.commands.ts +12 -18
- package/src/mcp/commands/meta.commands.ts +13 -13
- package/src/mcp/commands/registry.ts +5 -8
- package/src/mcp/commands/store.commands.ts +19 -19
- package/src/mcp/handlers/execute.handler.test.ts +10 -10
- package/src/mcp/handlers/execute.handler.ts +4 -5
- package/src/mcp/handlers/index.ts +10 -14
- package/src/mcp/handlers/job.handler.test.ts +10 -10
- package/src/mcp/handlers/job.handler.ts +22 -25
- package/src/mcp/handlers/search.handler.test.ts +36 -65
- package/src/mcp/handlers/search.handler.ts +135 -104
- package/src/mcp/handlers/store.handler.test.ts +41 -52
- package/src/mcp/handlers/store.handler.ts +108 -88
- package/src/mcp/schemas/index.test.ts +73 -68
- package/src/mcp/schemas/index.ts +18 -12
- package/src/mcp/server.test.ts +1 -1
- package/src/mcp/server.ts +59 -46
- package/src/plugin/commands.test.ts +230 -95
- package/src/plugin/commands.ts +24 -25
- package/src/plugin/dependency-analyzer.test.ts +52 -52
- package/src/plugin/dependency-analyzer.ts +85 -22
- package/src/plugin/git-clone.test.ts +24 -13
- package/src/plugin/git-clone.ts +3 -7
- package/src/server/app.test.ts +109 -109
- package/src/server/app.ts +32 -23
- package/src/server/index.test.ts +64 -66
- package/src/services/chunking.service.test.ts +32 -32
- package/src/services/chunking.service.ts +16 -9
- package/src/services/code-graph.service.test.ts +30 -36
- package/src/services/code-graph.service.ts +24 -10
- package/src/services/code-unit.service.test.ts +55 -11
- package/src/services/code-unit.service.ts +85 -11
- package/src/services/config.service.test.ts +37 -18
- package/src/services/config.service.ts +30 -7
- package/src/services/index.service.test.ts +49 -18
- package/src/services/index.service.ts +98 -48
- package/src/services/index.ts +8 -10
- package/src/services/job.service.test.ts +22 -22
- package/src/services/job.service.ts +18 -18
- package/src/services/project-root.service.test.ts +1 -3
- package/src/services/search.service.test.ts +248 -120
- package/src/services/search.service.ts +286 -156
- package/src/services/services.test.ts +36 -0
- package/src/services/snippet.service.test.ts +14 -6
- package/src/services/snippet.service.ts +7 -5
- package/src/services/store.service.test.ts +68 -29
- package/src/services/store.service.ts +41 -12
- package/src/services/watch.service.test.ts +34 -14
- package/src/services/watch.service.ts +11 -1
- package/src/types/brands.test.ts +3 -1
- package/src/types/index.ts +2 -13
- package/src/types/search.ts +10 -8
- package/src/utils/type-guards.test.ts +20 -15
- package/src/utils/type-guards.ts +1 -1
- package/src/workers/background-worker-cli.ts +2 -2
- package/src/workers/background-worker.test.ts +54 -40
- package/src/workers/background-worker.ts +76 -60
- package/src/workers/spawn-worker.test.ts +22 -10
- package/src/workers/spawn-worker.ts +6 -6
- package/tests/analysis/ast-parser.test.ts +3 -3
- package/tests/analysis/code-graph.test.ts +5 -5
- package/tests/fixtures/code-snippets/api/error-handling.ts +4 -15
- package/tests/fixtures/code-snippets/api/rest-controller.ts +3 -9
- package/tests/fixtures/code-snippets/auth/jwt-auth.ts +5 -21
- package/tests/fixtures/code-snippets/auth/oauth-flow.ts +4 -4
- package/tests/fixtures/code-snippets/database/repository-pattern.ts +11 -3
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/aws-lambda/handler.ts +2 -2
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/cloudflare-pages/handler.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/cloudflare-workers/serve-static.ts +2 -2
- package/tests/fixtures/corpus/oss-repos/hono/src/client/client.ts +2 -2
- package/tests/fixtures/corpus/oss-repos/hono/src/client/types.ts +22 -20
- package/tests/fixtures/corpus/oss-repos/hono/src/context.ts +13 -10
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/accepts/accepts.ts +10 -7
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/adapter/index.ts +2 -2
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/css/index.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/factory/index.ts +16 -16
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/ssg/ssg.ts +2 -2
- package/tests/fixtures/corpus/oss-repos/hono/src/hono-base.ts +3 -3
- package/tests/fixtures/corpus/oss-repos/hono/src/hono.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/css.ts +2 -2
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/intrinsic-element/components.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/render.ts +7 -7
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/hooks/index.ts +3 -3
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/intrinsic-element/components.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/utils.ts +6 -6
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/jsx-renderer/index.ts +3 -3
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/serve-static/index.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/preset/quick.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/preset/tiny.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/router/pattern-router/router.ts +2 -2
- package/tests/fixtures/corpus/oss-repos/hono/src/router/reg-exp-router/node.ts +4 -4
- package/tests/fixtures/corpus/oss-repos/hono/src/router/reg-exp-router/router.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/router/trie-router/node.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/types.ts +166 -169
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/body.ts +8 -8
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/color.ts +3 -3
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/cookie.ts +2 -2
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/encode.ts +2 -2
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/types.ts +30 -33
- package/tests/fixtures/corpus/oss-repos/hono/src/validator/validator.ts +2 -2
- package/tests/fixtures/test-server.ts +3 -2
- package/tests/helpers/performance-metrics.ts +8 -25
- package/tests/helpers/search-relevance.ts +14 -69
- package/tests/integration/cli-consistency.test.ts +5 -4
- package/tests/integration/e2e-workflow.test.ts +2 -0
- package/tests/integration/python-bridge.test.ts +13 -3
- package/tests/mcp/server.test.ts +1 -1
- package/tests/services/code-unit.service.test.ts +48 -0
- package/tests/services/job.service.test.ts +124 -0
- package/tests/services/search.progressive-context.test.ts +2 -2
- package/.claude-plugin/plugin.json +0 -13
- package/BUGS-FOUND.md +0 -71
- package/dist/chunk-2SJHNRXD.js.map +0 -1
- package/dist/chunk-L2YVNC63.js.map +0 -1
- package/dist/chunk-OGEY66FZ.js.map +0 -1
- package/dist/chunk-RWSXP3PQ.js.map +0 -1
- package/dist/watch.service-YAIKKDCF.js +0 -7
- package/skills/atomic-commits/SKILL.md +0 -77
- /package/dist/{watch.service-YAIKKDCF.js.map → watch.service-BJV3TI3F.js.map} +0 -0
package/src/plugin/commands.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
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
|
|
5
|
+
import { createServices } from '../services/index.js';
|
|
6
6
|
|
|
7
7
|
export async function handleSearch(args: {
|
|
8
8
|
query: string;
|
|
@@ -14,9 +14,8 @@ export async function handleSearch(args: {
|
|
|
14
14
|
const storeNames = args.stores?.split(',').map((s: string) => s.trim());
|
|
15
15
|
|
|
16
16
|
const allStores = await services.store.list();
|
|
17
|
-
const targetStores =
|
|
18
|
-
? allStores.filter((s) => storeNames.includes(s.name))
|
|
19
|
-
: allStores;
|
|
17
|
+
const targetStores =
|
|
18
|
+
storeNames !== undefined ? allStores.filter((s) => storeNames.includes(s.name)) : allStores;
|
|
20
19
|
|
|
21
20
|
if (targetStores.length === 0) {
|
|
22
21
|
console.error('No stores found to search');
|
|
@@ -33,7 +32,7 @@ export async function handleSearch(args: {
|
|
|
33
32
|
stores: targetStores.map((s) => s.id),
|
|
34
33
|
mode: 'hybrid',
|
|
35
34
|
limit: parseInt(args.limit ?? '10', 10),
|
|
36
|
-
detail: 'contextual'
|
|
35
|
+
detail: 'contextual',
|
|
37
36
|
});
|
|
38
37
|
|
|
39
38
|
console.log(`Found ${String(results.totalResults)} results:\n`);
|
|
@@ -61,7 +60,7 @@ export async function handleAddRepo(args: {
|
|
|
61
60
|
name: storeName,
|
|
62
61
|
type: 'repo',
|
|
63
62
|
url: args.url,
|
|
64
|
-
...(args.branch !== undefined ? { branch: args.branch } : {})
|
|
63
|
+
...(args.branch !== undefined ? { branch: args.branch } : {}),
|
|
65
64
|
});
|
|
66
65
|
|
|
67
66
|
if (!result.success) {
|
|
@@ -85,10 +84,7 @@ export async function handleAddRepo(args: {
|
|
|
85
84
|
}
|
|
86
85
|
}
|
|
87
86
|
|
|
88
|
-
export async function handleAddFolder(args: {
|
|
89
|
-
path: string;
|
|
90
|
-
name?: string;
|
|
91
|
-
}): Promise<void> {
|
|
87
|
+
export async function handleAddFolder(args: { path: string; name?: string }): Promise<void> {
|
|
92
88
|
// PWD is set by Claude Code to user's project directory
|
|
93
89
|
const services = await createServices(undefined, undefined, process.env['PWD']);
|
|
94
90
|
const { basename } = await import('node:path');
|
|
@@ -99,7 +95,7 @@ export async function handleAddFolder(args: {
|
|
|
99
95
|
const result = await services.store.create({
|
|
100
96
|
name: storeName,
|
|
101
97
|
type: 'file',
|
|
102
|
-
path: args.path
|
|
98
|
+
path: args.path,
|
|
103
99
|
});
|
|
104
100
|
|
|
105
101
|
if (!result.success) {
|
|
@@ -123,9 +119,7 @@ export async function handleAddFolder(args: {
|
|
|
123
119
|
}
|
|
124
120
|
}
|
|
125
121
|
|
|
126
|
-
export async function handleIndex(args: {
|
|
127
|
-
store: string;
|
|
128
|
-
}): Promise<void> {
|
|
122
|
+
export async function handleIndex(args: { store: string }): Promise<void> {
|
|
129
123
|
// PWD is set by Claude Code to user's project directory
|
|
130
124
|
const services = await createServices(undefined, undefined, process.env['PWD']);
|
|
131
125
|
const store = await services.store.getByIdOrName(args.store);
|
|
@@ -139,7 +133,9 @@ export async function handleIndex(args: {
|
|
|
139
133
|
const result = await services.index.indexStore(store);
|
|
140
134
|
|
|
141
135
|
if (result.success) {
|
|
142
|
-
console.log(
|
|
136
|
+
console.log(
|
|
137
|
+
`Indexed ${String(result.data.documentsIndexed)} documents in ${String(result.data.timeMs)}ms`
|
|
138
|
+
);
|
|
143
139
|
} else {
|
|
144
140
|
console.error(`Error: ${result.error.message}`);
|
|
145
141
|
process.exit(1);
|
|
@@ -207,7 +203,9 @@ export async function handleSuggest(): Promise<void> {
|
|
|
207
203
|
|
|
208
204
|
const { usages, totalFilesScanned, skippedFiles } = result.data;
|
|
209
205
|
|
|
210
|
-
console.log(
|
|
206
|
+
console.log(
|
|
207
|
+
`✔ Scanned ${String(totalFilesScanned)} files${skippedFiles > 0 ? ` (skipped ${String(skippedFiles)})` : ''}\n`
|
|
208
|
+
);
|
|
211
209
|
|
|
212
210
|
if (usages.length === 0) {
|
|
213
211
|
console.log('No external dependencies found in this project.');
|
|
@@ -217,9 +215,9 @@ export async function handleSuggest(): Promise<void> {
|
|
|
217
215
|
|
|
218
216
|
// Filter out packages already in stores
|
|
219
217
|
const existingStores = await services.store.list();
|
|
220
|
-
const existingRepoNames = new Set(existingStores.map(s => s.name));
|
|
218
|
+
const existingRepoNames = new Set(existingStores.map((s) => s.name));
|
|
221
219
|
|
|
222
|
-
const newUsages = usages.filter(u => !existingRepoNames.has(u.packageName));
|
|
220
|
+
const newUsages = usages.filter((u) => !existingRepoNames.has(u.packageName));
|
|
223
221
|
|
|
224
222
|
if (newUsages.length === 0) {
|
|
225
223
|
console.log('✔ All dependencies are already in knowledge stores!');
|
|
@@ -232,24 +230,25 @@ export async function handleSuggest(): Promise<void> {
|
|
|
232
230
|
console.log('Top dependencies by usage in this project:\n');
|
|
233
231
|
topSuggestions.forEach((usage, i) => {
|
|
234
232
|
console.log(`${String(i + 1)}. ${usage.packageName}`);
|
|
235
|
-
console.log(
|
|
233
|
+
console.log(
|
|
234
|
+
` ${String(usage.importCount)} imports across ${String(usage.fileCount)} files\n`
|
|
235
|
+
);
|
|
236
236
|
});
|
|
237
237
|
|
|
238
238
|
console.log('Searching for repository URLs...\n');
|
|
239
239
|
|
|
240
240
|
// For each package, find repo URL
|
|
241
241
|
for (const usage of topSuggestions) {
|
|
242
|
-
const repoResult = await resolver.findRepoUrl(
|
|
243
|
-
usage.packageName,
|
|
244
|
-
usage.language
|
|
245
|
-
);
|
|
242
|
+
const repoResult = await resolver.findRepoUrl(usage.packageName, usage.language);
|
|
246
243
|
|
|
247
244
|
if (repoResult.url !== null) {
|
|
248
245
|
console.log(`✔ ${usage.packageName}: ${repoResult.url}`);
|
|
249
246
|
console.log(` /bluera-knowledge:add-repo ${repoResult.url} --name=${usage.packageName}\n`);
|
|
250
247
|
} else {
|
|
251
248
|
console.log(`✗ ${usage.packageName}: Could not find repository URL`);
|
|
252
|
-
console.log(
|
|
249
|
+
console.log(
|
|
250
|
+
` You can manually add it: /bluera-knowledge:add-repo <url> --name=${usage.packageName}\n`
|
|
251
|
+
);
|
|
253
252
|
}
|
|
254
253
|
}
|
|
255
254
|
|
|
@@ -21,35 +21,35 @@ describe('DependencyAnalyzer - Node.js Projects', () => {
|
|
|
21
21
|
name: 'test-project',
|
|
22
22
|
dependencies: {
|
|
23
23
|
react: '^18.0.0',
|
|
24
|
-
vue: '^3.0.0'
|
|
25
|
-
}
|
|
24
|
+
vue: '^3.0.0',
|
|
25
|
+
},
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
await writeFile(join(tempDir, 'package.json'), JSON.stringify(packageJson));
|
|
29
29
|
|
|
30
30
|
const suggestions = await analyzeDependencies(tempDir);
|
|
31
31
|
|
|
32
|
-
expect(suggestions.some(s => s.name === 'react')).toBe(true);
|
|
33
|
-
expect(suggestions.some(s => s.name === 'vue')).toBe(true);
|
|
32
|
+
expect(suggestions.some((s) => s.name === 'react')).toBe(true);
|
|
33
|
+
expect(suggestions.some((s) => s.name === 'vue')).toBe(true);
|
|
34
34
|
});
|
|
35
35
|
|
|
36
36
|
it('includes devDependencies', async () => {
|
|
37
37
|
const packageJson = {
|
|
38
38
|
name: 'test-project',
|
|
39
39
|
dependencies: {
|
|
40
|
-
hono: '^3.0.0'
|
|
40
|
+
hono: '^3.0.0',
|
|
41
41
|
},
|
|
42
42
|
devDependencies: {
|
|
43
|
-
pino: '^8.0.0'
|
|
44
|
-
}
|
|
43
|
+
pino: '^8.0.0',
|
|
44
|
+
},
|
|
45
45
|
};
|
|
46
46
|
|
|
47
47
|
await writeFile(join(tempDir, 'package.json'), JSON.stringify(packageJson));
|
|
48
48
|
|
|
49
49
|
const suggestions = await analyzeDependencies(tempDir);
|
|
50
50
|
|
|
51
|
-
expect(suggestions.some(s => s.name === 'hono')).toBe(true);
|
|
52
|
-
expect(suggestions.some(s => s.name === 'pino')).toBe(true);
|
|
51
|
+
expect(suggestions.some((s) => s.name === 'hono')).toBe(true);
|
|
52
|
+
expect(suggestions.some((s) => s.name === 'pino')).toBe(true);
|
|
53
53
|
});
|
|
54
54
|
|
|
55
55
|
it('returns only known repositories', async () => {
|
|
@@ -57,31 +57,31 @@ describe('DependencyAnalyzer - Node.js Projects', () => {
|
|
|
57
57
|
name: 'test-project',
|
|
58
58
|
dependencies: {
|
|
59
59
|
react: '^18.0.0',
|
|
60
|
-
'some-unknown-package': '^1.0.0'
|
|
61
|
-
}
|
|
60
|
+
'some-unknown-package': '^1.0.0',
|
|
61
|
+
},
|
|
62
62
|
};
|
|
63
63
|
|
|
64
64
|
await writeFile(join(tempDir, 'package.json'), JSON.stringify(packageJson));
|
|
65
65
|
|
|
66
66
|
const suggestions = await analyzeDependencies(tempDir);
|
|
67
67
|
|
|
68
|
-
expect(suggestions.some(s => s.name === 'react')).toBe(true);
|
|
69
|
-
expect(suggestions.some(s => s.name === 'some-unknown-package')).toBe(false);
|
|
68
|
+
expect(suggestions.some((s) => s.name === 'react')).toBe(true);
|
|
69
|
+
expect(suggestions.some((s) => s.name === 'some-unknown-package')).toBe(false);
|
|
70
70
|
});
|
|
71
71
|
|
|
72
72
|
it('includes repository URL for known packages', async () => {
|
|
73
73
|
const packageJson = {
|
|
74
74
|
name: 'test-project',
|
|
75
75
|
dependencies: {
|
|
76
|
-
react: '^18.0.0'
|
|
77
|
-
}
|
|
76
|
+
react: '^18.0.0',
|
|
77
|
+
},
|
|
78
78
|
};
|
|
79
79
|
|
|
80
80
|
await writeFile(join(tempDir, 'package.json'), JSON.stringify(packageJson));
|
|
81
81
|
|
|
82
82
|
const suggestions = await analyzeDependencies(tempDir);
|
|
83
83
|
|
|
84
|
-
const reactSuggestion = suggestions.find(s => s.name === 'react');
|
|
84
|
+
const reactSuggestion = suggestions.find((s) => s.name === 'react');
|
|
85
85
|
expect(reactSuggestion).toBeDefined();
|
|
86
86
|
expect(reactSuggestion?.url).toContain('github.com');
|
|
87
87
|
});
|
|
@@ -91,16 +91,16 @@ describe('DependencyAnalyzer - Node.js Projects', () => {
|
|
|
91
91
|
name: 'test-project',
|
|
92
92
|
dependencies: {
|
|
93
93
|
react: '^18.0.0',
|
|
94
|
-
pino: '^8.0.0'
|
|
95
|
-
}
|
|
94
|
+
pino: '^8.0.0',
|
|
95
|
+
},
|
|
96
96
|
};
|
|
97
97
|
|
|
98
98
|
await writeFile(join(tempDir, 'package.json'), JSON.stringify(packageJson));
|
|
99
99
|
|
|
100
100
|
const suggestions = await analyzeDependencies(tempDir);
|
|
101
101
|
|
|
102
|
-
const reactSuggestion = suggestions.find(s => s.name === 'react');
|
|
103
|
-
const pinoSuggestion = suggestions.find(s => s.name === 'pino');
|
|
102
|
+
const reactSuggestion = suggestions.find((s) => s.name === 'react');
|
|
103
|
+
const pinoSuggestion = suggestions.find((s) => s.name === 'pino');
|
|
104
104
|
|
|
105
105
|
expect(reactSuggestion?.importance).toBe('critical');
|
|
106
106
|
expect(pinoSuggestion?.importance).toBe('medium');
|
|
@@ -110,15 +110,15 @@ describe('DependencyAnalyzer - Node.js Projects', () => {
|
|
|
110
110
|
const packageJson = {
|
|
111
111
|
name: 'test-project',
|
|
112
112
|
dependencies: {
|
|
113
|
-
react: '^18.0.0'
|
|
114
|
-
}
|
|
113
|
+
react: '^18.0.0',
|
|
114
|
+
},
|
|
115
115
|
};
|
|
116
116
|
|
|
117
117
|
await writeFile(join(tempDir, 'package.json'), JSON.stringify(packageJson));
|
|
118
118
|
|
|
119
119
|
const suggestions = await analyzeDependencies(tempDir);
|
|
120
120
|
|
|
121
|
-
const reactSuggestion = suggestions.find(s => s.name === 'react');
|
|
121
|
+
const reactSuggestion = suggestions.find((s) => s.name === 'react');
|
|
122
122
|
expect(reactSuggestion?.reason).toBeTruthy();
|
|
123
123
|
expect(reactSuggestion?.reason).toContain('framework');
|
|
124
124
|
});
|
|
@@ -172,9 +172,9 @@ django==4.2.0`;
|
|
|
172
172
|
|
|
173
173
|
const suggestions = await analyzeDependencies(tempDir);
|
|
174
174
|
|
|
175
|
-
expect(suggestions.some(s => s.name === 'fastapi')).toBe(true);
|
|
176
|
-
expect(suggestions.some(s => s.name === 'pydantic')).toBe(true);
|
|
177
|
-
expect(suggestions.some(s => s.name === 'django')).toBe(true);
|
|
175
|
+
expect(suggestions.some((s) => s.name === 'fastapi')).toBe(true);
|
|
176
|
+
expect(suggestions.some((s) => s.name === 'pydantic')).toBe(true);
|
|
177
|
+
expect(suggestions.some((s) => s.name === 'django')).toBe(true);
|
|
178
178
|
});
|
|
179
179
|
|
|
180
180
|
it('parses different version specifiers', async () => {
|
|
@@ -187,10 +187,10 @@ django>3.0`;
|
|
|
187
187
|
|
|
188
188
|
const suggestions = await analyzeDependencies(tempDir);
|
|
189
189
|
|
|
190
|
-
expect(suggestions.some(s => s.name === 'fastapi')).toBe(true);
|
|
191
|
-
expect(suggestions.some(s => s.name === 'pydantic')).toBe(true);
|
|
192
|
-
expect(suggestions.some(s => s.name === 'flask')).toBe(true);
|
|
193
|
-
expect(suggestions.some(s => s.name === 'django')).toBe(true);
|
|
190
|
+
expect(suggestions.some((s) => s.name === 'fastapi')).toBe(true);
|
|
191
|
+
expect(suggestions.some((s) => s.name === 'pydantic')).toBe(true);
|
|
192
|
+
expect(suggestions.some((s) => s.name === 'flask')).toBe(true);
|
|
193
|
+
expect(suggestions.some((s) => s.name === 'django')).toBe(true);
|
|
194
194
|
});
|
|
195
195
|
|
|
196
196
|
it('ignores comments and blank lines', async () => {
|
|
@@ -205,8 +205,8 @@ pydantic>=1.10.0
|
|
|
205
205
|
|
|
206
206
|
const suggestions = await analyzeDependencies(tempDir);
|
|
207
207
|
|
|
208
|
-
expect(suggestions.some(s => s.name === 'fastapi')).toBe(true);
|
|
209
|
-
expect(suggestions.some(s => s.name === 'pydantic')).toBe(true);
|
|
208
|
+
expect(suggestions.some((s) => s.name === 'fastapi')).toBe(true);
|
|
209
|
+
expect(suggestions.some((s) => s.name === 'pydantic')).toBe(true);
|
|
210
210
|
});
|
|
211
211
|
|
|
212
212
|
it('analyzes pyproject.toml', async () => {
|
|
@@ -221,8 +221,8 @@ dependencies = [
|
|
|
221
221
|
|
|
222
222
|
const suggestions = await analyzeDependencies(tempDir);
|
|
223
223
|
|
|
224
|
-
expect(suggestions.some(s => s.name === 'fastapi')).toBe(true);
|
|
225
|
-
expect(suggestions.some(s => s.name === 'pydantic')).toBe(true);
|
|
224
|
+
expect(suggestions.some((s) => s.name === 'fastapi')).toBe(true);
|
|
225
|
+
expect(suggestions.some((s) => s.name === 'pydantic')).toBe(true);
|
|
226
226
|
});
|
|
227
227
|
});
|
|
228
228
|
|
|
@@ -242,8 +242,8 @@ describe('DependencyAnalyzer - Mixed Projects', () => {
|
|
|
242
242
|
const packageJson = {
|
|
243
243
|
name: 'test-project',
|
|
244
244
|
dependencies: {
|
|
245
|
-
react: '^18.0.0'
|
|
246
|
-
}
|
|
245
|
+
react: '^18.0.0',
|
|
246
|
+
},
|
|
247
247
|
};
|
|
248
248
|
|
|
249
249
|
const requirements = 'fastapi==0.95.0';
|
|
@@ -253,26 +253,26 @@ describe('DependencyAnalyzer - Mixed Projects', () => {
|
|
|
253
253
|
|
|
254
254
|
const suggestions = await analyzeDependencies(tempDir);
|
|
255
255
|
|
|
256
|
-
expect(suggestions.some(s => s.name === 'react')).toBe(true);
|
|
257
|
-
expect(suggestions.some(s => s.name === 'fastapi')).toBe(true);
|
|
256
|
+
expect(suggestions.some((s) => s.name === 'react')).toBe(true);
|
|
257
|
+
expect(suggestions.some((s) => s.name === 'fastapi')).toBe(true);
|
|
258
258
|
});
|
|
259
259
|
|
|
260
260
|
it('removes duplicates across sources', async () => {
|
|
261
261
|
const packageJson = {
|
|
262
262
|
name: 'test-project',
|
|
263
263
|
dependencies: {
|
|
264
|
-
react: '^18.0.0'
|
|
264
|
+
react: '^18.0.0',
|
|
265
265
|
},
|
|
266
266
|
devDependencies: {
|
|
267
|
-
react: '^18.0.0' // Duplicate
|
|
268
|
-
}
|
|
267
|
+
react: '^18.0.0', // Duplicate
|
|
268
|
+
},
|
|
269
269
|
};
|
|
270
270
|
|
|
271
271
|
await writeFile(join(tempDir, 'package.json'), JSON.stringify(packageJson));
|
|
272
272
|
|
|
273
273
|
const suggestions = await analyzeDependencies(tempDir);
|
|
274
274
|
|
|
275
|
-
const reactSuggestions = suggestions.filter(s => s.name === 'react');
|
|
275
|
+
const reactSuggestions = suggestions.filter((s) => s.name === 'react');
|
|
276
276
|
expect(reactSuggestions).toHaveLength(1);
|
|
277
277
|
});
|
|
278
278
|
|
|
@@ -280,11 +280,11 @@ describe('DependencyAnalyzer - Mixed Projects', () => {
|
|
|
280
280
|
const packageJson = {
|
|
281
281
|
name: 'test-project',
|
|
282
282
|
dependencies: {
|
|
283
|
-
pino: '^8.0.0',
|
|
284
|
-
react: '^18.0.0',
|
|
285
|
-
zod: '^3.0.0',
|
|
286
|
-
express: '^4.0.0'
|
|
287
|
-
}
|
|
283
|
+
pino: '^8.0.0', // medium
|
|
284
|
+
react: '^18.0.0', // critical
|
|
285
|
+
zod: '^3.0.0', // high
|
|
286
|
+
express: '^4.0.0', // high
|
|
287
|
+
},
|
|
288
288
|
};
|
|
289
289
|
|
|
290
290
|
await writeFile(join(tempDir, 'package.json'), JSON.stringify(packageJson));
|
|
@@ -324,7 +324,7 @@ describe('DependencyAnalyzer - Edge Cases', () => {
|
|
|
324
324
|
it('handles empty dependencies object', async () => {
|
|
325
325
|
const packageJson = {
|
|
326
326
|
name: 'test-project',
|
|
327
|
-
dependencies: {}
|
|
327
|
+
dependencies: {},
|
|
328
328
|
};
|
|
329
329
|
|
|
330
330
|
await writeFile(join(tempDir, 'package.json'), JSON.stringify(packageJson));
|
|
@@ -337,7 +337,7 @@ describe('DependencyAnalyzer - Edge Cases', () => {
|
|
|
337
337
|
it('handles package.json without dependencies field', async () => {
|
|
338
338
|
const packageJson = {
|
|
339
339
|
name: 'test-project',
|
|
340
|
-
version: '1.0.0'
|
|
340
|
+
version: '1.0.0',
|
|
341
341
|
};
|
|
342
342
|
|
|
343
343
|
await writeFile(join(tempDir, 'package.json'), JSON.stringify(packageJson));
|
|
@@ -359,8 +359,8 @@ describe('DependencyAnalyzer - Edge Cases', () => {
|
|
|
359
359
|
const packageJson = {
|
|
360
360
|
name: 'test-project',
|
|
361
361
|
dependencies: {
|
|
362
|
-
react: '^18.0.0'
|
|
363
|
-
}
|
|
362
|
+
react: '^18.0.0',
|
|
363
|
+
},
|
|
364
364
|
};
|
|
365
365
|
|
|
366
366
|
await writeFile(join(tempDir, 'package.json'), JSON.stringify(packageJson));
|
|
@@ -372,7 +372,7 @@ describe('DependencyAnalyzer - Edge Cases', () => {
|
|
|
372
372
|
|
|
373
373
|
try {
|
|
374
374
|
const suggestions = await analyzeDependencies();
|
|
375
|
-
expect(suggestions.some(s => s.name === 'react')).toBe(true);
|
|
375
|
+
expect(suggestions.some((s) => s.name === 'react')).toBe(true);
|
|
376
376
|
} finally {
|
|
377
377
|
cwd.mockRestore();
|
|
378
378
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises';
|
|
2
1
|
import { existsSync } from 'node:fs';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
|
|
5
5
|
export interface DependencySuggestion {
|
|
@@ -14,22 +14,85 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
// Map of important libraries to their repos
|
|
17
|
-
const KNOWN_REPOS: Record<
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
17
|
+
const KNOWN_REPOS: Record<
|
|
18
|
+
string,
|
|
19
|
+
{ url: string; importance: 'critical' | 'high' | 'medium'; reason: string }
|
|
20
|
+
> = {
|
|
21
|
+
vue: {
|
|
22
|
+
url: 'https://github.com/vuejs/core',
|
|
23
|
+
importance: 'critical',
|
|
24
|
+
reason: 'Core framework - essential for Vue.js development',
|
|
25
|
+
},
|
|
26
|
+
react: {
|
|
27
|
+
url: 'https://github.com/facebook/react',
|
|
28
|
+
importance: 'critical',
|
|
29
|
+
reason: 'Core framework - essential for React development',
|
|
30
|
+
},
|
|
31
|
+
pydantic: {
|
|
32
|
+
url: 'https://github.com/pydantic/pydantic',
|
|
33
|
+
importance: 'critical',
|
|
34
|
+
reason: 'Core validation library - heavily used',
|
|
35
|
+
},
|
|
36
|
+
fastapi: {
|
|
37
|
+
url: 'https://github.com/tiangolo/fastapi',
|
|
38
|
+
importance: 'critical',
|
|
39
|
+
reason: 'Core web framework - central to this project',
|
|
40
|
+
},
|
|
41
|
+
hono: {
|
|
42
|
+
url: 'https://github.com/honojs/hono',
|
|
43
|
+
importance: 'critical',
|
|
44
|
+
reason: 'Core web framework - central to this project',
|
|
45
|
+
},
|
|
46
|
+
express: {
|
|
47
|
+
url: 'https://github.com/expressjs/express',
|
|
48
|
+
importance: 'high',
|
|
49
|
+
reason: 'Web framework - frequently referenced',
|
|
50
|
+
},
|
|
51
|
+
pinia: {
|
|
52
|
+
url: 'https://github.com/vuejs/pinia',
|
|
53
|
+
importance: 'high',
|
|
54
|
+
reason: 'State management - frequently used',
|
|
55
|
+
},
|
|
56
|
+
pino: {
|
|
57
|
+
url: 'https://github.com/pinojs/pino',
|
|
58
|
+
importance: 'medium',
|
|
59
|
+
reason: 'Logging library - commonly used',
|
|
60
|
+
},
|
|
61
|
+
zod: {
|
|
62
|
+
url: 'https://github.com/colinhacks/zod',
|
|
63
|
+
importance: 'high',
|
|
64
|
+
reason: 'Schema validation - frequently used',
|
|
65
|
+
},
|
|
66
|
+
next: {
|
|
67
|
+
url: 'https://github.com/vercel/next.js',
|
|
68
|
+
importance: 'critical',
|
|
69
|
+
reason: 'Core framework - essential for Next.js development',
|
|
70
|
+
},
|
|
71
|
+
nuxt: {
|
|
72
|
+
url: 'https://github.com/nuxt/nuxt',
|
|
73
|
+
importance: 'critical',
|
|
74
|
+
reason: 'Core framework - essential for Nuxt development',
|
|
75
|
+
},
|
|
76
|
+
svelte: {
|
|
77
|
+
url: 'https://github.com/sveltejs/svelte',
|
|
78
|
+
importance: 'critical',
|
|
79
|
+
reason: 'Core framework - essential for Svelte development',
|
|
80
|
+
},
|
|
81
|
+
django: {
|
|
82
|
+
url: 'https://github.com/django/django',
|
|
83
|
+
importance: 'critical',
|
|
84
|
+
reason: 'Core framework - essential for Django development',
|
|
85
|
+
},
|
|
86
|
+
flask: {
|
|
87
|
+
url: 'https://github.com/pallets/flask',
|
|
88
|
+
importance: 'critical',
|
|
89
|
+
reason: 'Core framework - essential for Flask development',
|
|
90
|
+
},
|
|
91
|
+
prisma: {
|
|
92
|
+
url: 'https://github.com/prisma/prisma',
|
|
93
|
+
importance: 'high',
|
|
94
|
+
reason: 'ORM - database interactions',
|
|
95
|
+
},
|
|
33
96
|
};
|
|
34
97
|
|
|
35
98
|
export async function analyzeDependencies(
|
|
@@ -62,7 +125,7 @@ export async function analyzeDependencies(
|
|
|
62
125
|
}
|
|
63
126
|
|
|
64
127
|
// Remove duplicates and sort by importance
|
|
65
|
-
const unique = Array.from(new Map(suggestions.map(s => [s.name, s])).values());
|
|
128
|
+
const unique = Array.from(new Map(suggestions.map((s) => [s.name, s])).values());
|
|
66
129
|
return unique.sort((a, b) => {
|
|
67
130
|
const order = { critical: 0, high: 1, medium: 2 };
|
|
68
131
|
return order[a.importance] - order[b.importance];
|
|
@@ -87,7 +150,7 @@ function analyzeNodeDependencies(pkg: Record<string, unknown>): DependencySugges
|
|
|
87
150
|
name,
|
|
88
151
|
url: known.url,
|
|
89
152
|
importance: known.importance,
|
|
90
|
-
reason: known.reason
|
|
153
|
+
reason: known.reason,
|
|
91
154
|
});
|
|
92
155
|
}
|
|
93
156
|
}
|
|
@@ -105,7 +168,7 @@ function analyzePythonDependencies(content: string): DependencySuggestion[] {
|
|
|
105
168
|
|
|
106
169
|
// Parse package name (before ==, >=, etc.)
|
|
107
170
|
const match = /^([a-zA-Z0-9_-]+)/.exec(trimmed);
|
|
108
|
-
if (match
|
|
171
|
+
if (match?.[1] !== undefined) {
|
|
109
172
|
const name = match[1].toLowerCase();
|
|
110
173
|
const known = KNOWN_REPOS[name];
|
|
111
174
|
if (known !== undefined) {
|
|
@@ -113,7 +176,7 @@ function analyzePythonDependencies(content: string): DependencySuggestion[] {
|
|
|
113
176
|
name,
|
|
114
177
|
url: known.url,
|
|
115
178
|
importance: known.importance,
|
|
116
|
-
reason: known.reason
|
|
179
|
+
reason: known.reason,
|
|
117
180
|
});
|
|
118
181
|
}
|
|
119
182
|
}
|
|
@@ -137,7 +200,7 @@ function analyzePyProjectDependencies(content: string): DependencySuggestion[] {
|
|
|
137
200
|
name,
|
|
138
201
|
url: known.url,
|
|
139
202
|
importance: known.importance,
|
|
140
|
-
reason: known.reason
|
|
203
|
+
reason: known.reason,
|
|
141
204
|
});
|
|
142
205
|
}
|
|
143
206
|
}
|
|
@@ -8,7 +8,7 @@ import { tmpdir } from 'node:os';
|
|
|
8
8
|
|
|
9
9
|
// Mock child_process
|
|
10
10
|
vi.mock('node:child_process', () => ({
|
|
11
|
-
spawn: vi.fn()
|
|
11
|
+
spawn: vi.fn(),
|
|
12
12
|
}));
|
|
13
13
|
|
|
14
14
|
describe('GitClone - cloneRepository', () => {
|
|
@@ -42,7 +42,7 @@ describe('GitClone - cloneRepository', () => {
|
|
|
42
42
|
|
|
43
43
|
const result = await cloneRepository({
|
|
44
44
|
url: 'https://github.com/user/repo.git',
|
|
45
|
-
targetDir: join(tempDir, 'repo')
|
|
45
|
+
targetDir: join(tempDir, 'repo'),
|
|
46
46
|
});
|
|
47
47
|
|
|
48
48
|
expect(result.success).toBe(true);
|
|
@@ -62,7 +62,7 @@ describe('GitClone - cloneRepository', () => {
|
|
|
62
62
|
|
|
63
63
|
await cloneRepository({
|
|
64
64
|
url: 'https://github.com/user/repo.git',
|
|
65
|
-
targetDir: join(tempDir, 'repo')
|
|
65
|
+
targetDir: join(tempDir, 'repo'),
|
|
66
66
|
});
|
|
67
67
|
|
|
68
68
|
expect(mockSpawn).toHaveBeenCalledWith(
|
|
@@ -84,12 +84,20 @@ describe('GitClone - cloneRepository', () => {
|
|
|
84
84
|
await cloneRepository({
|
|
85
85
|
url: 'https://github.com/user/repo.git',
|
|
86
86
|
targetDir: join(tempDir, 'repo'),
|
|
87
|
-
branch: 'develop'
|
|
87
|
+
branch: 'develop',
|
|
88
88
|
});
|
|
89
89
|
|
|
90
90
|
expect(mockSpawn).toHaveBeenCalledWith(
|
|
91
91
|
'git',
|
|
92
|
-
[
|
|
92
|
+
[
|
|
93
|
+
'clone',
|
|
94
|
+
'--depth',
|
|
95
|
+
'1',
|
|
96
|
+
'--branch',
|
|
97
|
+
'develop',
|
|
98
|
+
'https://github.com/user/repo.git',
|
|
99
|
+
join(tempDir, 'repo'),
|
|
100
|
+
],
|
|
93
101
|
expect.any(Object)
|
|
94
102
|
);
|
|
95
103
|
});
|
|
@@ -106,7 +114,7 @@ describe('GitClone - cloneRepository', () => {
|
|
|
106
114
|
await cloneRepository({
|
|
107
115
|
url: 'https://github.com/user/repo.git',
|
|
108
116
|
targetDir: join(tempDir, 'repo'),
|
|
109
|
-
depth: 10
|
|
117
|
+
depth: 10,
|
|
110
118
|
});
|
|
111
119
|
|
|
112
120
|
expect(mockSpawn).toHaveBeenCalledWith(
|
|
@@ -127,7 +135,7 @@ describe('GitClone - cloneRepository', () => {
|
|
|
127
135
|
|
|
128
136
|
await cloneRepository({
|
|
129
137
|
url: 'https://github.com/user/repo.git',
|
|
130
|
-
targetDir: join(tempDir, 'repo')
|
|
138
|
+
targetDir: join(tempDir, 'repo'),
|
|
131
139
|
});
|
|
132
140
|
|
|
133
141
|
expect(mockSpawn).toHaveBeenCalledWith(
|
|
@@ -151,7 +159,7 @@ describe('GitClone - cloneRepository', () => {
|
|
|
151
159
|
|
|
152
160
|
const result = await cloneRepository({
|
|
153
161
|
url: 'https://github.com/user/nonexistent.git',
|
|
154
|
-
targetDir: join(tempDir, 'repo')
|
|
162
|
+
targetDir: join(tempDir, 'repo'),
|
|
155
163
|
});
|
|
156
164
|
|
|
157
165
|
expect(result.success).toBe(false);
|
|
@@ -176,7 +184,7 @@ describe('GitClone - cloneRepository', () => {
|
|
|
176
184
|
|
|
177
185
|
const result = await cloneRepository({
|
|
178
186
|
url: 'https://github.com/user/repo.git',
|
|
179
|
-
targetDir: join(tempDir, 'repo')
|
|
187
|
+
targetDir: join(tempDir, 'repo'),
|
|
180
188
|
});
|
|
181
189
|
|
|
182
190
|
expect(result.success).toBe(false);
|
|
@@ -200,7 +208,7 @@ describe('GitClone - cloneRepository', () => {
|
|
|
200
208
|
|
|
201
209
|
const result = await cloneRepository({
|
|
202
210
|
url: 'https://github.com/user/repo.git',
|
|
203
|
-
targetDir: join(tempDir, 'repo')
|
|
211
|
+
targetDir: join(tempDir, 'repo'),
|
|
204
212
|
});
|
|
205
213
|
|
|
206
214
|
expect(result.success).toBe(false);
|
|
@@ -223,7 +231,7 @@ describe('GitClone - cloneRepository', () => {
|
|
|
223
231
|
|
|
224
232
|
const result = await cloneRepository({
|
|
225
233
|
url: 'https://github.com/user/repo.git',
|
|
226
|
-
targetDir: join(tempDir, 'repo')
|
|
234
|
+
targetDir: join(tempDir, 'repo'),
|
|
227
235
|
});
|
|
228
236
|
|
|
229
237
|
expect(result.success).toBe(false);
|
|
@@ -238,7 +246,10 @@ describe('GitClone - cloneRepository', () => {
|
|
|
238
246
|
|
|
239
247
|
mockSpawn.mockImplementation(() => {
|
|
240
248
|
setImmediate(() => {
|
|
241
|
-
(mockProcess.stderr as any).emit(
|
|
249
|
+
(mockProcess.stderr as any).emit(
|
|
250
|
+
'data',
|
|
251
|
+
Buffer.from('Remote branch nonexistent-branch not found')
|
|
252
|
+
);
|
|
242
253
|
mockProcess.emit('close', 128);
|
|
243
254
|
});
|
|
244
255
|
return mockProcess;
|
|
@@ -247,7 +258,7 @@ describe('GitClone - cloneRepository', () => {
|
|
|
247
258
|
const result = await cloneRepository({
|
|
248
259
|
url: 'https://github.com/user/repo.git',
|
|
249
260
|
targetDir: join(tempDir, 'repo'),
|
|
250
|
-
branch: 'nonexistent-branch'
|
|
261
|
+
branch: 'nonexistent-branch',
|
|
251
262
|
});
|
|
252
263
|
|
|
253
264
|
expect(result.success).toBe(false);
|