devbrain-cli 0.1.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.
- package/LICENSE +21 -0
- package/README.md +26 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +48 -0
- package/dist/commands/context.command.d.ts +16 -0
- package/dist/commands/context.command.js +36 -0
- package/dist/commands/init.command.d.ts +16 -0
- package/dist/commands/init.command.js +57 -0
- package/dist/commands/learn.command.d.ts +17 -0
- package/dist/commands/learn.command.js +72 -0
- package/dist/pipeline/command.pipeline.d.ts +28 -0
- package/dist/pipeline/command.pipeline.js +65 -0
- package/dist/ui/logger.d.ts +13 -0
- package/dist/ui/logger.js +31 -0
- package/node_modules/@devbrain/core/dist/analysis/analyzer.interface.d.ts +10 -0
- package/node_modules/@devbrain/core/dist/analysis/analyzer.interface.js +2 -0
- package/node_modules/@devbrain/core/dist/analysis/analyzer.registry.d.ts +20 -0
- package/node_modules/@devbrain/core/dist/analysis/analyzer.registry.js +67 -0
- package/node_modules/@devbrain/core/dist/analysis/analyzers/build-gradle.analyzer.d.ts +14 -0
- package/node_modules/@devbrain/core/dist/analysis/analyzers/build-gradle.analyzer.js +53 -0
- package/node_modules/@devbrain/core/dist/analysis/analyzers/docker.analyzer.d.ts +14 -0
- package/node_modules/@devbrain/core/dist/analysis/analyzers/docker.analyzer.js +38 -0
- package/node_modules/@devbrain/core/dist/analysis/analyzers/git.analyzer.d.ts +14 -0
- package/node_modules/@devbrain/core/dist/analysis/analyzers/git.analyzer.js +54 -0
- package/node_modules/@devbrain/core/dist/analysis/analyzers/package-json.analyzer.d.ts +14 -0
- package/node_modules/@devbrain/core/dist/analysis/analyzers/package-json.analyzer.js +72 -0
- package/node_modules/@devbrain/core/dist/analysis/analyzers/pom-xml.analyzer.d.ts +14 -0
- package/node_modules/@devbrain/core/dist/analysis/analyzers/pom-xml.analyzer.js +70 -0
- package/node_modules/@devbrain/core/dist/analysis/analyzers/readme.analyzer.d.ts +17 -0
- package/node_modules/@devbrain/core/dist/analysis/analyzers/readme.analyzer.js +65 -0
- package/node_modules/@devbrain/core/dist/analysis/analyzers/requirements-txt.analyzer.d.ts +14 -0
- package/node_modules/@devbrain/core/dist/analysis/analyzers/requirements-txt.analyzer.js +62 -0
- package/node_modules/@devbrain/core/dist/analysis/analyzers/tsconfig.analyzer.d.ts +14 -0
- package/node_modules/@devbrain/core/dist/analysis/analyzers/tsconfig.analyzer.js +47 -0
- package/node_modules/@devbrain/core/dist/analysis/scanner.d.ts +21 -0
- package/node_modules/@devbrain/core/dist/analysis/scanner.js +81 -0
- package/node_modules/@devbrain/core/dist/context/context.service.d.ts +17 -0
- package/node_modules/@devbrain/core/dist/context/context.service.js +72 -0
- package/node_modules/@devbrain/core/dist/filesystem/filesystem.service.d.ts +29 -0
- package/node_modules/@devbrain/core/dist/filesystem/filesystem.service.js +87 -0
- package/node_modules/@devbrain/core/dist/index.d.ts +14 -0
- package/node_modules/@devbrain/core/dist/index.js +16 -0
- package/node_modules/@devbrain/core/dist/memory/memory.service.d.ts +19 -0
- package/node_modules/@devbrain/core/dist/memory/memory.service.js +158 -0
- package/node_modules/@devbrain/core/package.json +22 -0
- package/node_modules/@devbrain/shared/dist/constants.d.ts +6 -0
- package/node_modules/@devbrain/shared/dist/constants.js +15 -0
- package/node_modules/@devbrain/shared/dist/errors.d.ts +38 -0
- package/node_modules/@devbrain/shared/dist/errors.js +64 -0
- package/node_modules/@devbrain/shared/dist/index.d.ts +3 -0
- package/node_modules/@devbrain/shared/dist/index.js +4 -0
- package/node_modules/@devbrain/shared/dist/types.d.ts +52 -0
- package/node_modules/@devbrain/shared/dist/types.js +2 -0
- package/node_modules/@devbrain/shared/package.json +14 -0
- package/package.json +65 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { FilesystemService } from '../../filesystem/filesystem.service.js';
|
|
3
|
+
/**
|
|
4
|
+
* Analyzer for Dockerfile.
|
|
5
|
+
*/
|
|
6
|
+
export class DockerfileAnalyzer {
|
|
7
|
+
fsService;
|
|
8
|
+
id = 'dockerfile';
|
|
9
|
+
name = 'Dockerfile Analyzer';
|
|
10
|
+
constructor(fsService = new FilesystemService()) {
|
|
11
|
+
this.fsService = fsService;
|
|
12
|
+
}
|
|
13
|
+
async supports(project) {
|
|
14
|
+
return project.files.some((f) => f === 'Dockerfile' || f.endsWith('.dockerfile'));
|
|
15
|
+
}
|
|
16
|
+
async analyze(project) {
|
|
17
|
+
const dockerfile = project.files.find((f) => f === 'Dockerfile' || f.endsWith('.dockerfile'));
|
|
18
|
+
const path = join(project.cwd, dockerfile);
|
|
19
|
+
const content = await this.fsService.read(path);
|
|
20
|
+
const fromMatch = content.match(/^FROM\s+(.+)$/im);
|
|
21
|
+
const baseImage = fromMatch ? fromMatch[1].trim() : 'unknown';
|
|
22
|
+
const ports = [];
|
|
23
|
+
const portMatches = content.matchAll(/^EXPOSE\s+(.+)$/gim);
|
|
24
|
+
for (const match of portMatches) {
|
|
25
|
+
ports.push(...match[1].trim().split(/\s+/));
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
analyzerId: this.id,
|
|
29
|
+
analyzerName: this.name,
|
|
30
|
+
data: {
|
|
31
|
+
baseImage,
|
|
32
|
+
exposedPorts: ports,
|
|
33
|
+
technologies: ['Docker'],
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=docker.analyzer.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ProjectContext, AnalysisResult } from '@devbrain/shared';
|
|
2
|
+
import { Analyzer } from '../analyzer.interface.js';
|
|
3
|
+
import { FilesystemService } from '../../filesystem/filesystem.service.js';
|
|
4
|
+
/**
|
|
5
|
+
* Analyzer for Git repository metrics.
|
|
6
|
+
*/
|
|
7
|
+
export declare class GitAnalyzer implements Analyzer {
|
|
8
|
+
private readonly fsService;
|
|
9
|
+
readonly id = "git";
|
|
10
|
+
readonly name = "Git Repository Analyzer";
|
|
11
|
+
constructor(fsService?: FilesystemService);
|
|
12
|
+
supports(project: ProjectContext): Promise<boolean>;
|
|
13
|
+
analyze(project: ProjectContext): Promise<AnalysisResult>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { simpleGit } from 'simple-git';
|
|
3
|
+
import { FilesystemService } from '../../filesystem/filesystem.service.js';
|
|
4
|
+
/**
|
|
5
|
+
* Analyzer for Git repository metrics.
|
|
6
|
+
*/
|
|
7
|
+
export class GitAnalyzer {
|
|
8
|
+
fsService;
|
|
9
|
+
id = 'git';
|
|
10
|
+
name = 'Git Repository Analyzer';
|
|
11
|
+
constructor(fsService = new FilesystemService()) {
|
|
12
|
+
this.fsService = fsService;
|
|
13
|
+
}
|
|
14
|
+
async supports(project) {
|
|
15
|
+
// Check if .git directory/metadata exists
|
|
16
|
+
const gitHeadPath = join(project.cwd, '.git/HEAD');
|
|
17
|
+
return await this.fsService.exists(gitHeadPath);
|
|
18
|
+
}
|
|
19
|
+
async analyze(project) {
|
|
20
|
+
const git = simpleGit({ baseDir: project.cwd });
|
|
21
|
+
let branch = 'unknown';
|
|
22
|
+
let commits = [];
|
|
23
|
+
let remotes = [];
|
|
24
|
+
try {
|
|
25
|
+
const status = await git.status();
|
|
26
|
+
branch = status.current || 'unknown';
|
|
27
|
+
const log = await git.log({ maxCount: 5 });
|
|
28
|
+
commits = log.all.map((c) => ({
|
|
29
|
+
hash: c.hash.substring(0, 7),
|
|
30
|
+
date: c.date,
|
|
31
|
+
message: c.message,
|
|
32
|
+
author: c.author_name,
|
|
33
|
+
}));
|
|
34
|
+
const rawRemotes = await git.getRemotes(true);
|
|
35
|
+
remotes = rawRemotes.map((r) => ({
|
|
36
|
+
name: r.name,
|
|
37
|
+
refs: r.refs,
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// Graceful fallback if git commands fail
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
analyzerId: this.id,
|
|
45
|
+
analyzerName: this.name,
|
|
46
|
+
data: {
|
|
47
|
+
branch,
|
|
48
|
+
recentCommits: commits,
|
|
49
|
+
remotes,
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=git.analyzer.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ProjectContext, AnalysisResult } from '@devbrain/shared';
|
|
2
|
+
import { Analyzer } from '../analyzer.interface.js';
|
|
3
|
+
import { FilesystemService } from '../../filesystem/filesystem.service.js';
|
|
4
|
+
/**
|
|
5
|
+
* Analyzer for package.json files.
|
|
6
|
+
*/
|
|
7
|
+
export declare class PackageJsonAnalyzer implements Analyzer {
|
|
8
|
+
private readonly fsService;
|
|
9
|
+
readonly id = "package-json";
|
|
10
|
+
readonly name = "package.json Analyzer";
|
|
11
|
+
constructor(fsService?: FilesystemService);
|
|
12
|
+
supports(project: ProjectContext): Promise<boolean>;
|
|
13
|
+
analyze(project: ProjectContext): Promise<AnalysisResult>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { FilesystemService } from '../../filesystem/filesystem.service.js';
|
|
3
|
+
/**
|
|
4
|
+
* Analyzer for package.json files.
|
|
5
|
+
*/
|
|
6
|
+
export class PackageJsonAnalyzer {
|
|
7
|
+
fsService;
|
|
8
|
+
id = 'package-json';
|
|
9
|
+
name = 'package.json Analyzer';
|
|
10
|
+
constructor(fsService = new FilesystemService()) {
|
|
11
|
+
this.fsService = fsService;
|
|
12
|
+
}
|
|
13
|
+
async supports(project) {
|
|
14
|
+
return project.files.some((f) => f === 'package.json');
|
|
15
|
+
}
|
|
16
|
+
async analyze(project) {
|
|
17
|
+
const packageJsonFile = project.files.find((f) => f === 'package.json');
|
|
18
|
+
const packageJsonPath = join(project.cwd, packageJsonFile);
|
|
19
|
+
const data = await this.fsService.readJson(packageJsonPath);
|
|
20
|
+
const name = data.name || 'unnamed';
|
|
21
|
+
const version = data.version || '0.0.0';
|
|
22
|
+
const scripts = Object.keys(data.scripts || {});
|
|
23
|
+
const dependencies = Object.keys(data.dependencies || {});
|
|
24
|
+
const devDependencies = Object.keys(data.devDependencies || {});
|
|
25
|
+
// Infer technologies and frameworks
|
|
26
|
+
const technologies = ['Node.js'];
|
|
27
|
+
const frameworks = [];
|
|
28
|
+
if (devDependencies.includes('typescript') || dependencies.includes('typescript')) {
|
|
29
|
+
technologies.push('TypeScript');
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
technologies.push('JavaScript');
|
|
33
|
+
}
|
|
34
|
+
const frameworkMap = {
|
|
35
|
+
next: 'Next.js',
|
|
36
|
+
react: 'React',
|
|
37
|
+
vue: 'Vue',
|
|
38
|
+
nuxt: 'Nuxt',
|
|
39
|
+
svelte: 'Svelte',
|
|
40
|
+
express: 'Express',
|
|
41
|
+
fastify: 'Fastify',
|
|
42
|
+
hono: 'Hono',
|
|
43
|
+
nest: 'NestJS',
|
|
44
|
+
};
|
|
45
|
+
const allDeps = [...dependencies, ...devDependencies];
|
|
46
|
+
for (const [depKey, fwName] of Object.entries(frameworkMap)) {
|
|
47
|
+
if (allDeps.some((d) => d.includes(depKey))) {
|
|
48
|
+
frameworks.push(fwName);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
analyzerId: this.id,
|
|
53
|
+
analyzerName: this.name,
|
|
54
|
+
data: {
|
|
55
|
+
name,
|
|
56
|
+
version,
|
|
57
|
+
scripts,
|
|
58
|
+
dependencies: data.dependencies || {},
|
|
59
|
+
devDependencies: data.devDependencies || {},
|
|
60
|
+
engines: data.engines || {},
|
|
61
|
+
workspaces: data.workspaces || null,
|
|
62
|
+
technologies,
|
|
63
|
+
frameworks,
|
|
64
|
+
metadata: {
|
|
65
|
+
projectLanguage: technologies.includes('TypeScript') ? 'TypeScript' : 'JavaScript',
|
|
66
|
+
projectFramework: frameworks[0] || 'Node.js base',
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=package-json.analyzer.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ProjectContext, AnalysisResult } from '@devbrain/shared';
|
|
2
|
+
import { Analyzer } from '../analyzer.interface.js';
|
|
3
|
+
import { FilesystemService } from '../../filesystem/filesystem.service.js';
|
|
4
|
+
/**
|
|
5
|
+
* Analyzer for Maven pom.xml configuration files.
|
|
6
|
+
*/
|
|
7
|
+
export declare class PomXmlAnalyzer implements Analyzer {
|
|
8
|
+
private readonly fsService;
|
|
9
|
+
readonly id = "pom-xml";
|
|
10
|
+
readonly name = "Maven POM Analyzer";
|
|
11
|
+
constructor(fsService?: FilesystemService);
|
|
12
|
+
supports(project: ProjectContext): Promise<boolean>;
|
|
13
|
+
analyze(project: ProjectContext): Promise<AnalysisResult>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { XMLParser } from 'fast-xml-parser';
|
|
3
|
+
import { FilesystemService } from '../../filesystem/filesystem.service.js';
|
|
4
|
+
/**
|
|
5
|
+
* Analyzer for Maven pom.xml configuration files.
|
|
6
|
+
*/
|
|
7
|
+
export class PomXmlAnalyzer {
|
|
8
|
+
fsService;
|
|
9
|
+
id = 'pom-xml';
|
|
10
|
+
name = 'Maven POM Analyzer';
|
|
11
|
+
constructor(fsService = new FilesystemService()) {
|
|
12
|
+
this.fsService = fsService;
|
|
13
|
+
}
|
|
14
|
+
async supports(project) {
|
|
15
|
+
return project.files.some((f) => f === 'pom.xml');
|
|
16
|
+
}
|
|
17
|
+
async analyze(project) {
|
|
18
|
+
const file = project.files.find((f) => f === 'pom.xml');
|
|
19
|
+
const path = join(project.cwd, file);
|
|
20
|
+
const content = await this.fsService.read(path);
|
|
21
|
+
let groupId = 'unknown';
|
|
22
|
+
let artifactId = 'unknown';
|
|
23
|
+
let version = 'unknown';
|
|
24
|
+
const dependencies = [];
|
|
25
|
+
const frameworks = [];
|
|
26
|
+
try {
|
|
27
|
+
const parser = new XMLParser();
|
|
28
|
+
const xml = parser.parse(content);
|
|
29
|
+
const projectNode = xml.project || {};
|
|
30
|
+
groupId = projectNode.groupId || projectNode.parent?.groupId || 'unknown';
|
|
31
|
+
artifactId = projectNode.artifactId || 'unknown';
|
|
32
|
+
version = projectNode.version || projectNode.parent?.version || 'unknown';
|
|
33
|
+
// Parse dependencies
|
|
34
|
+
let depsList = projectNode.dependencies?.dependency;
|
|
35
|
+
if (depsList) {
|
|
36
|
+
if (!Array.isArray(depsList)) {
|
|
37
|
+
depsList = [depsList];
|
|
38
|
+
}
|
|
39
|
+
for (const dep of depsList) {
|
|
40
|
+
if (dep.artifactId) {
|
|
41
|
+
dependencies.push(dep.artifactId);
|
|
42
|
+
if (dep.artifactId.includes('spring-boot')) {
|
|
43
|
+
frameworks.push('Spring Boot');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// Graceful fallback
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
analyzerId: this.id,
|
|
54
|
+
analyzerName: this.name,
|
|
55
|
+
data: {
|
|
56
|
+
groupId,
|
|
57
|
+
artifactId,
|
|
58
|
+
version,
|
|
59
|
+
dependencies,
|
|
60
|
+
frameworks,
|
|
61
|
+
technologies: ['Java', 'Maven'],
|
|
62
|
+
metadata: {
|
|
63
|
+
projectLanguage: 'Java',
|
|
64
|
+
projectFramework: frameworks[0] || 'Java Maven base',
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=pom-xml.analyzer.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ProjectContext, AnalysisResult } from '@devbrain/shared';
|
|
2
|
+
import { Analyzer } from '../analyzer.interface.js';
|
|
3
|
+
import { FilesystemService } from '../../filesystem/filesystem.service.js';
|
|
4
|
+
/**
|
|
5
|
+
* Analyzer for README files.
|
|
6
|
+
*/
|
|
7
|
+
export declare class ReadmeAnalyzer implements Analyzer {
|
|
8
|
+
private readonly fsService;
|
|
9
|
+
readonly id = "readme";
|
|
10
|
+
readonly name = "README.md Analyzer";
|
|
11
|
+
constructor(fsService?: FilesystemService);
|
|
12
|
+
supports(project: ProjectContext): Promise<boolean>;
|
|
13
|
+
analyze(project: ProjectContext): Promise<AnalysisResult>;
|
|
14
|
+
private extractTitle;
|
|
15
|
+
private extractSections;
|
|
16
|
+
private extractDescription;
|
|
17
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { FilesystemService } from '../../filesystem/filesystem.service.js';
|
|
3
|
+
/**
|
|
4
|
+
* Analyzer for README files.
|
|
5
|
+
*/
|
|
6
|
+
export class ReadmeAnalyzer {
|
|
7
|
+
fsService;
|
|
8
|
+
id = 'readme';
|
|
9
|
+
name = 'README.md Analyzer';
|
|
10
|
+
constructor(fsService = new FilesystemService()) {
|
|
11
|
+
this.fsService = fsService;
|
|
12
|
+
}
|
|
13
|
+
async supports(project) {
|
|
14
|
+
return project.files.some((f) => f.toLowerCase() === 'readme.md');
|
|
15
|
+
}
|
|
16
|
+
async analyze(project) {
|
|
17
|
+
const readmeFile = project.files.find((f) => f.toLowerCase() === 'readme.md');
|
|
18
|
+
const readmePath = join(project.cwd, readmeFile);
|
|
19
|
+
const content = await this.fsService.read(readmePath);
|
|
20
|
+
const title = this.extractTitle(content);
|
|
21
|
+
const sections = this.extractSections(content);
|
|
22
|
+
const description = this.extractDescription(content);
|
|
23
|
+
return {
|
|
24
|
+
analyzerId: this.id,
|
|
25
|
+
analyzerName: this.name,
|
|
26
|
+
data: {
|
|
27
|
+
title,
|
|
28
|
+
description,
|
|
29
|
+
sections,
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
extractTitle(content) {
|
|
34
|
+
const titleMatch = content.match(/^#\s+(.+)$/m);
|
|
35
|
+
return titleMatch ? titleMatch[1].trim() : 'Untitled Project';
|
|
36
|
+
}
|
|
37
|
+
extractSections(content) {
|
|
38
|
+
const sectionMatches = content.matchAll(/^##\s+(.+)$/gm);
|
|
39
|
+
return Array.from(sectionMatches).map((m) => m[1].trim());
|
|
40
|
+
}
|
|
41
|
+
extractDescription(content) {
|
|
42
|
+
const lines = content.split('\n');
|
|
43
|
+
let foundTitle = false;
|
|
44
|
+
const descriptionParagraphs = [];
|
|
45
|
+
for (const line of lines) {
|
|
46
|
+
const trimmed = line.trim();
|
|
47
|
+
if (trimmed.startsWith('# ')) {
|
|
48
|
+
foundTitle = true;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (foundTitle) {
|
|
52
|
+
if (trimmed.startsWith('## ') || trimmed.startsWith('# ')) {
|
|
53
|
+
break; // Next section starts, stop extracting description
|
|
54
|
+
}
|
|
55
|
+
if (trimmed && !trimmed.startsWith('!') && !trimmed.startsWith('[')) {
|
|
56
|
+
descriptionParagraphs.push(trimmed);
|
|
57
|
+
if (descriptionParagraphs.length >= 2)
|
|
58
|
+
break; // Limit to first two descriptive paragraphs
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return descriptionParagraphs.join(' ');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=readme.analyzer.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ProjectContext, AnalysisResult } from '@devbrain/shared';
|
|
2
|
+
import { Analyzer } from '../analyzer.interface.js';
|
|
3
|
+
import { FilesystemService } from '../../filesystem/filesystem.service.js';
|
|
4
|
+
/**
|
|
5
|
+
* Analyzer for Python requirements.txt files.
|
|
6
|
+
*/
|
|
7
|
+
export declare class RequirementsTxtAnalyzer implements Analyzer {
|
|
8
|
+
private readonly fsService;
|
|
9
|
+
readonly id = "requirements-txt";
|
|
10
|
+
readonly name = "Python Requirements Analyzer";
|
|
11
|
+
constructor(fsService?: FilesystemService);
|
|
12
|
+
supports(project: ProjectContext): Promise<boolean>;
|
|
13
|
+
analyze(project: ProjectContext): Promise<AnalysisResult>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { FilesystemService } from '../../filesystem/filesystem.service.js';
|
|
3
|
+
/**
|
|
4
|
+
* Analyzer for Python requirements.txt files.
|
|
5
|
+
*/
|
|
6
|
+
export class RequirementsTxtAnalyzer {
|
|
7
|
+
fsService;
|
|
8
|
+
id = 'requirements-txt';
|
|
9
|
+
name = 'Python Requirements Analyzer';
|
|
10
|
+
constructor(fsService = new FilesystemService()) {
|
|
11
|
+
this.fsService = fsService;
|
|
12
|
+
}
|
|
13
|
+
async supports(project) {
|
|
14
|
+
return project.files.some((f) => f === 'requirements.txt');
|
|
15
|
+
}
|
|
16
|
+
async analyze(project) {
|
|
17
|
+
const file = project.files.find((f) => f === 'requirements.txt');
|
|
18
|
+
const path = join(project.cwd, file);
|
|
19
|
+
const content = await this.fsService.read(path);
|
|
20
|
+
const dependencies = [];
|
|
21
|
+
const lines = content.split(/\r?\n/);
|
|
22
|
+
for (let line of lines) {
|
|
23
|
+
line = line.trim();
|
|
24
|
+
if (!line || line.startsWith('#')) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
// Simple parse: package==version, package>=version, etc.
|
|
28
|
+
const match = line.match(/^([a-zA-Z0-9_\-[\]]+)/);
|
|
29
|
+
if (match) {
|
|
30
|
+
dependencies.push(match[1]);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// Infer frameworks
|
|
34
|
+
const frameworks = [];
|
|
35
|
+
const frameworkMap = {
|
|
36
|
+
django: 'Django',
|
|
37
|
+
fastapi: 'FastAPI',
|
|
38
|
+
flask: 'Flask',
|
|
39
|
+
tornado: 'Tornado',
|
|
40
|
+
};
|
|
41
|
+
for (const dep of dependencies) {
|
|
42
|
+
const lowerDep = dep.toLowerCase();
|
|
43
|
+
if (frameworkMap[lowerDep]) {
|
|
44
|
+
frameworks.push(frameworkMap[lowerDep]);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
analyzerId: this.id,
|
|
49
|
+
analyzerName: this.name,
|
|
50
|
+
data: {
|
|
51
|
+
dependencies,
|
|
52
|
+
frameworks,
|
|
53
|
+
technologies: ['Python'],
|
|
54
|
+
metadata: {
|
|
55
|
+
projectLanguage: 'Python',
|
|
56
|
+
projectFramework: frameworks[0] || 'Python base',
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=requirements-txt.analyzer.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ProjectContext, AnalysisResult } from '@devbrain/shared';
|
|
2
|
+
import { Analyzer } from '../analyzer.interface.js';
|
|
3
|
+
import { FilesystemService } from '../../filesystem/filesystem.service.js';
|
|
4
|
+
/**
|
|
5
|
+
* Analyzer for tsconfig.json configurations.
|
|
6
|
+
*/
|
|
7
|
+
export declare class TsconfigAnalyzer implements Analyzer {
|
|
8
|
+
private readonly fsService;
|
|
9
|
+
readonly id = "tsconfig";
|
|
10
|
+
readonly name = "TypeScript Configuration Analyzer";
|
|
11
|
+
constructor(fsService?: FilesystemService);
|
|
12
|
+
supports(project: ProjectContext): Promise<boolean>;
|
|
13
|
+
analyze(project: ProjectContext): Promise<AnalysisResult>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { FilesystemService } from '../../filesystem/filesystem.service.js';
|
|
3
|
+
/**
|
|
4
|
+
* Analyzer for tsconfig.json configurations.
|
|
5
|
+
*/
|
|
6
|
+
export class TsconfigAnalyzer {
|
|
7
|
+
fsService;
|
|
8
|
+
id = 'tsconfig';
|
|
9
|
+
name = 'TypeScript Configuration Analyzer';
|
|
10
|
+
constructor(fsService = new FilesystemService()) {
|
|
11
|
+
this.fsService = fsService;
|
|
12
|
+
}
|
|
13
|
+
async supports(project) {
|
|
14
|
+
return project.files.some((f) => f === 'tsconfig.json');
|
|
15
|
+
}
|
|
16
|
+
async analyze(project) {
|
|
17
|
+
const tsconfigFile = project.files.find((f) => f === 'tsconfig.json');
|
|
18
|
+
const tsconfigPath = join(project.cwd, tsconfigFile);
|
|
19
|
+
const rawContent = await this.fsService.read(tsconfigPath);
|
|
20
|
+
// Strip comments to allow parsing json with comments
|
|
21
|
+
const cleanJson = rawContent.replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, '$1').trim();
|
|
22
|
+
let compilerOptions = {};
|
|
23
|
+
let references = [];
|
|
24
|
+
try {
|
|
25
|
+
const parsed = JSON.parse(cleanJson);
|
|
26
|
+
compilerOptions = parsed.compilerOptions || {};
|
|
27
|
+
references = parsed.references || [];
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// Fallback if parsing fails
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
analyzerId: this.id,
|
|
34
|
+
analyzerName: this.name,
|
|
35
|
+
data: {
|
|
36
|
+
target: compilerOptions.target || 'default',
|
|
37
|
+
module: compilerOptions.module || 'default',
|
|
38
|
+
moduleResolution: compilerOptions.moduleResolution || 'default',
|
|
39
|
+
strict: compilerOptions.strict ?? false,
|
|
40
|
+
jsx: compilerOptions.jsx || 'none',
|
|
41
|
+
referencesCount: references.length,
|
|
42
|
+
technologies: ['TypeScript'],
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=tsconfig.analyzer.js.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { FilesystemService } from '../filesystem/filesystem.service.js';
|
|
2
|
+
export interface ScanOptions {
|
|
3
|
+
ignore?: string[];
|
|
4
|
+
followSymlinks?: boolean;
|
|
5
|
+
respectGitignore?: boolean;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Service for scanning repositories and finding files while respecting configuration filters.
|
|
9
|
+
*/
|
|
10
|
+
export declare class RepositoryScanner {
|
|
11
|
+
private readonly fsService;
|
|
12
|
+
constructor(fsService?: FilesystemService);
|
|
13
|
+
/**
|
|
14
|
+
* Scans a workspace directory recursively and returns all matching relative file paths.
|
|
15
|
+
*/
|
|
16
|
+
scan(cwd: string, options?: ScanOptions): Promise<string[]>;
|
|
17
|
+
/**
|
|
18
|
+
* Helper to translate standard .gitignore patterns into fast-glob rules.
|
|
19
|
+
*/
|
|
20
|
+
private parseGitignore;
|
|
21
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import fg from 'fast-glob';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { FilesystemService } from '../filesystem/filesystem.service.js';
|
|
4
|
+
import { DEFAULT_IGNORE_PATTERNS } from '@devbrain/shared';
|
|
5
|
+
/**
|
|
6
|
+
* Service for scanning repositories and finding files while respecting configuration filters.
|
|
7
|
+
*/
|
|
8
|
+
export class RepositoryScanner {
|
|
9
|
+
fsService;
|
|
10
|
+
constructor(fsService = new FilesystemService()) {
|
|
11
|
+
this.fsService = fsService;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Scans a workspace directory recursively and returns all matching relative file paths.
|
|
15
|
+
*/
|
|
16
|
+
async scan(cwd, options = {}) {
|
|
17
|
+
const ignoreList = [...(options.ignore || DEFAULT_IGNORE_PATTERNS)];
|
|
18
|
+
// Respect .gitignore if configured
|
|
19
|
+
if (options.respectGitignore !== false) {
|
|
20
|
+
const gitignorePath = join(cwd, '.gitignore');
|
|
21
|
+
if (await this.fsService.exists(gitignorePath)) {
|
|
22
|
+
try {
|
|
23
|
+
const gitignoreContent = await this.fsService.read(gitignorePath);
|
|
24
|
+
const gitignoreRules = this.parseGitignore(gitignoreContent);
|
|
25
|
+
ignoreList.push(...gitignoreRules);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// Fallback silently if gitignore reading fails
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// fast-glob parameters
|
|
33
|
+
const files = await fg('**/*', {
|
|
34
|
+
cwd,
|
|
35
|
+
ignore: ignoreList,
|
|
36
|
+
followSymbolicLinks: options.followSymlinks ?? false,
|
|
37
|
+
dot: true, // Scan hidden files like .gitignore or .git
|
|
38
|
+
onlyFiles: true,
|
|
39
|
+
});
|
|
40
|
+
return files;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Helper to translate standard .gitignore patterns into fast-glob rules.
|
|
44
|
+
*/
|
|
45
|
+
parseGitignore(content) {
|
|
46
|
+
const rules = [];
|
|
47
|
+
const lines = content.split(/\r?\n/);
|
|
48
|
+
for (let line of lines) {
|
|
49
|
+
line = line.trim();
|
|
50
|
+
// Skip empty lines and comments
|
|
51
|
+
if (!line || line.startsWith('#')) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
// Negative patterns are not directly handled by basic fast-glob ignores,
|
|
55
|
+
// but simple exclusions are translated.
|
|
56
|
+
let pattern = line;
|
|
57
|
+
if (pattern.startsWith('!')) {
|
|
58
|
+
continue; // Skip negative ignores for simplicity in scanner v0.1
|
|
59
|
+
}
|
|
60
|
+
if (pattern.startsWith('/')) {
|
|
61
|
+
pattern = pattern.substring(1);
|
|
62
|
+
}
|
|
63
|
+
// If it has no slash and is not a wildcard, it can be anywhere in the directory hierarchy
|
|
64
|
+
if (!pattern.includes('/')) {
|
|
65
|
+
rules.push(`**/${pattern}`);
|
|
66
|
+
rules.push(`**/${pattern}/**`);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
if (pattern.endsWith('/')) {
|
|
70
|
+
rules.push(`${pattern}**`);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
rules.push(pattern);
|
|
74
|
+
rules.push(`${pattern}/**`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return rules;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=scanner.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { FilesystemService } from '../filesystem/filesystem.service.js';
|
|
2
|
+
/**
|
|
3
|
+
* Service responsible for reading generated project memory and consolidating it into an optimized AI prompt context.
|
|
4
|
+
*/
|
|
5
|
+
export declare class ContextService {
|
|
6
|
+
private readonly fsService;
|
|
7
|
+
constructor(fsService?: FilesystemService);
|
|
8
|
+
/**
|
|
9
|
+
* Reads memory markdown files, validates existence, and compiles them into a single token-efficient string.
|
|
10
|
+
*/
|
|
11
|
+
buildContext(memoryDir: string): Promise<string>;
|
|
12
|
+
/**
|
|
13
|
+
* Formats and converts absolute top-level headers (#) to sub-headers (##)
|
|
14
|
+
* to maintain a single root h1 structure and optimizes whitespaces.
|
|
15
|
+
*/
|
|
16
|
+
private normalizeHeadings;
|
|
17
|
+
}
|