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.
Files changed (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +26 -0
  3. package/dist/bin.d.ts +2 -0
  4. package/dist/bin.js +48 -0
  5. package/dist/commands/context.command.d.ts +16 -0
  6. package/dist/commands/context.command.js +36 -0
  7. package/dist/commands/init.command.d.ts +16 -0
  8. package/dist/commands/init.command.js +57 -0
  9. package/dist/commands/learn.command.d.ts +17 -0
  10. package/dist/commands/learn.command.js +72 -0
  11. package/dist/pipeline/command.pipeline.d.ts +28 -0
  12. package/dist/pipeline/command.pipeline.js +65 -0
  13. package/dist/ui/logger.d.ts +13 -0
  14. package/dist/ui/logger.js +31 -0
  15. package/node_modules/@devbrain/core/dist/analysis/analyzer.interface.d.ts +10 -0
  16. package/node_modules/@devbrain/core/dist/analysis/analyzer.interface.js +2 -0
  17. package/node_modules/@devbrain/core/dist/analysis/analyzer.registry.d.ts +20 -0
  18. package/node_modules/@devbrain/core/dist/analysis/analyzer.registry.js +67 -0
  19. package/node_modules/@devbrain/core/dist/analysis/analyzers/build-gradle.analyzer.d.ts +14 -0
  20. package/node_modules/@devbrain/core/dist/analysis/analyzers/build-gradle.analyzer.js +53 -0
  21. package/node_modules/@devbrain/core/dist/analysis/analyzers/docker.analyzer.d.ts +14 -0
  22. package/node_modules/@devbrain/core/dist/analysis/analyzers/docker.analyzer.js +38 -0
  23. package/node_modules/@devbrain/core/dist/analysis/analyzers/git.analyzer.d.ts +14 -0
  24. package/node_modules/@devbrain/core/dist/analysis/analyzers/git.analyzer.js +54 -0
  25. package/node_modules/@devbrain/core/dist/analysis/analyzers/package-json.analyzer.d.ts +14 -0
  26. package/node_modules/@devbrain/core/dist/analysis/analyzers/package-json.analyzer.js +72 -0
  27. package/node_modules/@devbrain/core/dist/analysis/analyzers/pom-xml.analyzer.d.ts +14 -0
  28. package/node_modules/@devbrain/core/dist/analysis/analyzers/pom-xml.analyzer.js +70 -0
  29. package/node_modules/@devbrain/core/dist/analysis/analyzers/readme.analyzer.d.ts +17 -0
  30. package/node_modules/@devbrain/core/dist/analysis/analyzers/readme.analyzer.js +65 -0
  31. package/node_modules/@devbrain/core/dist/analysis/analyzers/requirements-txt.analyzer.d.ts +14 -0
  32. package/node_modules/@devbrain/core/dist/analysis/analyzers/requirements-txt.analyzer.js +62 -0
  33. package/node_modules/@devbrain/core/dist/analysis/analyzers/tsconfig.analyzer.d.ts +14 -0
  34. package/node_modules/@devbrain/core/dist/analysis/analyzers/tsconfig.analyzer.js +47 -0
  35. package/node_modules/@devbrain/core/dist/analysis/scanner.d.ts +21 -0
  36. package/node_modules/@devbrain/core/dist/analysis/scanner.js +81 -0
  37. package/node_modules/@devbrain/core/dist/context/context.service.d.ts +17 -0
  38. package/node_modules/@devbrain/core/dist/context/context.service.js +72 -0
  39. package/node_modules/@devbrain/core/dist/filesystem/filesystem.service.d.ts +29 -0
  40. package/node_modules/@devbrain/core/dist/filesystem/filesystem.service.js +87 -0
  41. package/node_modules/@devbrain/core/dist/index.d.ts +14 -0
  42. package/node_modules/@devbrain/core/dist/index.js +16 -0
  43. package/node_modules/@devbrain/core/dist/memory/memory.service.d.ts +19 -0
  44. package/node_modules/@devbrain/core/dist/memory/memory.service.js +158 -0
  45. package/node_modules/@devbrain/core/package.json +22 -0
  46. package/node_modules/@devbrain/shared/dist/constants.d.ts +6 -0
  47. package/node_modules/@devbrain/shared/dist/constants.js +15 -0
  48. package/node_modules/@devbrain/shared/dist/errors.d.ts +38 -0
  49. package/node_modules/@devbrain/shared/dist/errors.js +64 -0
  50. package/node_modules/@devbrain/shared/dist/index.d.ts +3 -0
  51. package/node_modules/@devbrain/shared/dist/index.js +4 -0
  52. package/node_modules/@devbrain/shared/dist/types.d.ts +52 -0
  53. package/node_modules/@devbrain/shared/dist/types.js +2 -0
  54. package/node_modules/@devbrain/shared/package.json +14 -0
  55. package/package.json +65 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DevBrain Authors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,26 @@
1
+ # devbrain
2
+
3
+ DevBrain is an offline-first CLI tool designed to provide persistent, deterministic project memory for AI assistants.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install -g devbrain-cli
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ 1. Initialize DevBrain in your project:
14
+ ```bash
15
+ devbrain init
16
+ ```
17
+ 2. Scan the repository and generate memory:
18
+ ```bash
19
+ devbrain learn
20
+ ```
21
+ 3. Generate LLM-ready consolidated context output:
22
+ ```bash
23
+ devbrain context
24
+ ```
25
+
26
+ For detailed documentation, community guidelines, and license details, please visit the [DevBrain GitHub repository](https://github.com/CodeItAlone/devbrain#readme).
package/dist/bin.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/bin.js ADDED
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { InitCommand } from './commands/init.command.js';
4
+ import { LearnCommand } from './commands/learn.command.js';
5
+ import { ContextCommand } from './commands/context.command.js';
6
+ import { DEVBRAIN_VERSION } from '@devbrain/shared';
7
+ const program = new Command();
8
+ program
9
+ .name('devbrain')
10
+ .description('DevBrain CLI: Persistent project memory for AI-assisted software development')
11
+ .version(DEVBRAIN_VERSION);
12
+ program
13
+ .command('init')
14
+ .description('Initialize DevBrain project memory in the current workspace')
15
+ .option('-f, --force', 'Force reinitialization and overwrite existing config')
16
+ .option('-d, --debug', 'Enable verbose debug outputs and print stack traces')
17
+ .action(async (options) => {
18
+ const cwd = process.cwd();
19
+ const cmd = new InitCommand();
20
+ await cmd.execute(cwd, !!options.debug, { force: !!options.force });
21
+ });
22
+ program
23
+ .command('learn')
24
+ .description('Scan repository files and update documentation memory deterministically')
25
+ .option('-d, --debug', 'Enable verbose debug outputs and print stack traces')
26
+ .option('--no-gitignore', 'Skip matching .gitignore exclude rules')
27
+ .action(async (options) => {
28
+ const cwd = process.cwd();
29
+ const cmd = new LearnCommand();
30
+ await cmd.execute(cwd, !!options.debug, { respectGitignore: options.gitignore !== false });
31
+ });
32
+ program
33
+ .command('context')
34
+ .description('Consolidate persistent documentation memory into an optimized prompt context')
35
+ .option('-d, --debug', 'Enable verbose debug outputs and print stack traces')
36
+ .option('-r, --raw', 'Omit debug and formatting statistics in output')
37
+ .action(async (options) => {
38
+ const cwd = process.cwd();
39
+ const cmd = new ContextCommand();
40
+ await cmd.execute(cwd, !!options.debug, { raw: !!options.raw });
41
+ });
42
+ // Handle unknown commands
43
+ program.on('command:*', () => {
44
+ console.error('Invalid command: %s\nUse --help for a list of available commands.', program.args.join(' '));
45
+ process.exit(1);
46
+ });
47
+ program.parse(process.argv);
48
+ //# sourceMappingURL=bin.js.map
@@ -0,0 +1,16 @@
1
+ import { CommandContext } from '@devbrain/shared';
2
+ import { CommandPipeline } from '../pipeline/command.pipeline.js';
3
+ interface ContextArgs {
4
+ raw?: boolean;
5
+ }
6
+ /**
7
+ * Command pipeline for "devbrain context".
8
+ */
9
+ export declare class ContextCommand extends CommandPipeline<ContextArgs> {
10
+ protected validate(context: CommandContext, _args: ContextArgs): Promise<void>;
11
+ protected createContext(_context: CommandContext, _args: ContextArgs): Promise<void>;
12
+ protected executeService(context: CommandContext, _args: ContextArgs): Promise<string>;
13
+ protected writeOutput(_context: CommandContext, output: string, _args: ContextArgs): Promise<void>;
14
+ protected reportResult(_context: CommandContext, output: string, args: ContextArgs): Promise<void>;
15
+ }
16
+ export {};
@@ -0,0 +1,36 @@
1
+ import { join } from 'node:path';
2
+ import { ValidationError, DEVBRAIN_DIR_NAME, CONFIG_FILE_NAME, } from '@devbrain/shared';
3
+ import { ContextService } from '@devbrain/core';
4
+ import { CommandPipeline } from '../pipeline/command.pipeline.js';
5
+ /**
6
+ * Command pipeline for "devbrain context".
7
+ */
8
+ export class ContextCommand extends CommandPipeline {
9
+ async validate(context, _args) {
10
+ const configPath = join(context.cwd, DEVBRAIN_DIR_NAME, CONFIG_FILE_NAME);
11
+ if (!(await this.fsService.exists(configPath))) {
12
+ throw new ValidationError('DevBrain is not initialized.', 'Configuration file config.json not found.', 'Run "devbrain init" before executing "devbrain context".');
13
+ }
14
+ }
15
+ async createContext(_context, _args) {
16
+ // No-op: context built from memory files
17
+ }
18
+ async executeService(context, _args) {
19
+ const memoryDir = join(context.cwd, context.config.memory.directory);
20
+ const contextService = new ContextService(this.fsService);
21
+ return await contextService.buildContext(memoryDir);
22
+ }
23
+ async writeOutput(_context, output, _args) {
24
+ // Output prompt directly to stdout so it can be piped or copied
25
+ console.log(output);
26
+ }
27
+ async reportResult(_context, output, args) {
28
+ if (!args.raw) {
29
+ const lineCount = output.split('\n').length;
30
+ const charCount = output.length;
31
+ const approximateTokens = Math.ceil(charCount / 4);
32
+ this.logger.debug(`Consolidated AI Context Details: ${lineCount} lines, ${charCount} chars, ~${approximateTokens} tokens.`);
33
+ }
34
+ }
35
+ }
36
+ //# sourceMappingURL=context.command.js.map
@@ -0,0 +1,16 @@
1
+ import { CommandContext } from '@devbrain/shared';
2
+ import { CommandPipeline } from '../pipeline/command.pipeline.js';
3
+ interface InitArgs {
4
+ force?: boolean;
5
+ }
6
+ /**
7
+ * Command pipeline for "devbrain init".
8
+ */
9
+ export declare class InitCommand extends CommandPipeline<InitArgs> {
10
+ protected validate(context: CommandContext, args: InitArgs): Promise<void>;
11
+ protected createContext(context: CommandContext, _args: InitArgs): Promise<void>;
12
+ protected executeService(context: CommandContext, _args: InitArgs): Promise<void>;
13
+ protected writeOutput(_context: CommandContext, _output: any, _args: InitArgs): Promise<void>;
14
+ protected reportResult(context: CommandContext, _output: any, _args: InitArgs): Promise<void>;
15
+ }
16
+ export {};
@@ -0,0 +1,57 @@
1
+ import { join, basename } from 'node:path';
2
+ import { ValidationError, DEVBRAIN_VERSION, DEFAULT_IGNORE_PATTERNS, DEVBRAIN_DIR_NAME, CONFIG_FILE_NAME, DEFAULT_MEMORY_DIR, } from '@devbrain/shared';
3
+ import { CommandPipeline } from '../pipeline/command.pipeline.js';
4
+ /**
5
+ * Command pipeline for "devbrain init".
6
+ */
7
+ export class InitCommand extends CommandPipeline {
8
+ async validate(context, args) {
9
+ const configPath = join(context.cwd, DEVBRAIN_DIR_NAME, CONFIG_FILE_NAME);
10
+ if (!args.force && (await this.fsService.exists(configPath))) {
11
+ throw new ValidationError('DevBrain project is already initialized in this repository.', `Found configuration file at ${configPath}.`, 'Use the "--force" flag to reinitialize and overwrite the config.');
12
+ }
13
+ }
14
+ async createContext(context, _args) {
15
+ // Setup default config parameters
16
+ const projectName = basename(context.cwd);
17
+ context.config = {
18
+ version: DEVBRAIN_VERSION,
19
+ project: {
20
+ name: projectName.replace(/[-_]/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase()),
21
+ },
22
+ scanner: {
23
+ ignore: DEFAULT_IGNORE_PATTERNS,
24
+ followSymlinks: false,
25
+ respectGitignore: true,
26
+ },
27
+ memory: {
28
+ format: 'markdown',
29
+ directory: DEFAULT_MEMORY_DIR,
30
+ },
31
+ };
32
+ }
33
+ async executeService(context, _args) {
34
+ const devbrainPath = join(context.cwd, DEVBRAIN_DIR_NAME);
35
+ const configPath = join(devbrainPath, CONFIG_FILE_NAME);
36
+ const memoryPath = join(context.cwd, context.config.memory.directory);
37
+ // Create directories
38
+ await this.fsService.writeJson(configPath, context.config);
39
+ // Generate starter memory summary
40
+ const summaryPath = join(memoryPath, 'summary.md');
41
+ const initialSummary = [
42
+ `# Project Summary: ${context.config.project.name}`,
43
+ '',
44
+ '## Overview',
45
+ 'This project has been initialized with DevBrain. Run "devbrain learn" to populate structured memory.',
46
+ '',
47
+ ].join('\n');
48
+ await this.fsService.write(summaryPath, initialSummary);
49
+ }
50
+ async writeOutput(_context, _output, _args) {
51
+ // No-op: files written in execution service
52
+ }
53
+ async reportResult(context, _output, _args) {
54
+ this.logger.success(`Initialized DevBrain project memory directory at ${join(context.cwd, DEVBRAIN_DIR_NAME)}`);
55
+ }
56
+ }
57
+ //# sourceMappingURL=init.command.js.map
@@ -0,0 +1,17 @@
1
+ import { CommandContext } from '@devbrain/shared';
2
+ import { CommandPipeline } from '../pipeline/command.pipeline.js';
3
+ interface LearnArgs {
4
+ respectGitignore?: boolean;
5
+ }
6
+ /**
7
+ * Command pipeline for "devbrain learn".
8
+ */
9
+ export declare class LearnCommand extends CommandPipeline<LearnArgs> {
10
+ private filesList;
11
+ protected validate(context: CommandContext, _args: LearnArgs): Promise<void>;
12
+ protected createContext(context: CommandContext, args: LearnArgs): Promise<void>;
13
+ protected executeService(context: CommandContext, _args: LearnArgs): Promise<void>;
14
+ protected writeOutput(_context: CommandContext, _output: any, _args: LearnArgs): Promise<void>;
15
+ protected reportResult(context: CommandContext, _output: any, _args: LearnArgs): Promise<void>;
16
+ }
17
+ export {};
@@ -0,0 +1,72 @@
1
+ import { join } from 'node:path';
2
+ import { ValidationError, DEVBRAIN_DIR_NAME, CONFIG_FILE_NAME, } from '@devbrain/shared';
3
+ import { RepositoryScanner, AnalyzerRegistry, MemoryService, ReadmeAnalyzer, PackageJsonAnalyzer, TsconfigAnalyzer, GitAnalyzer, DockerfileAnalyzer, RequirementsTxtAnalyzer, PomXmlAnalyzer, BuildGradleAnalyzer, } from '@devbrain/core';
4
+ import { CommandPipeline } from '../pipeline/command.pipeline.js';
5
+ import ora from 'ora';
6
+ /**
7
+ * Command pipeline for "devbrain learn".
8
+ */
9
+ export class LearnCommand extends CommandPipeline {
10
+ filesList = [];
11
+ async validate(context, _args) {
12
+ const configPath = join(context.cwd, DEVBRAIN_DIR_NAME, CONFIG_FILE_NAME);
13
+ if (!(await this.fsService.exists(configPath))) {
14
+ throw new ValidationError('DevBrain is not initialized in this repository.', 'Configuration file config.json not found.', 'Run "devbrain init" before executing "devbrain learn".');
15
+ }
16
+ }
17
+ async createContext(context, args) {
18
+ const spinner = ora('Scanning repository files...').start();
19
+ try {
20
+ const scanner = new RepositoryScanner(this.fsService);
21
+ const respectGitignore = args.respectGitignore ?? context.config?.scanner.respectGitignore;
22
+ this.filesList = await scanner.scan(context.cwd, {
23
+ ignore: context.config?.scanner.ignore,
24
+ followSymlinks: context.config?.scanner.followSymlinks,
25
+ respectGitignore,
26
+ });
27
+ spinner.succeed(`Scanned repository: found ${this.filesList.length} files.`);
28
+ }
29
+ catch (error) {
30
+ spinner.fail('Scanning failed.');
31
+ throw error;
32
+ }
33
+ }
34
+ async executeService(context, _args) {
35
+ const spinner = ora('Analyzing code and generating memory files...').start();
36
+ try {
37
+ // Setup registry with default analyzers
38
+ const registry = new AnalyzerRegistry();
39
+ registry.register(new ReadmeAnalyzer(this.fsService));
40
+ registry.register(new PackageJsonAnalyzer(this.fsService));
41
+ registry.register(new TsconfigAnalyzer(this.fsService));
42
+ registry.register(new GitAnalyzer(this.fsService));
43
+ registry.register(new DockerfileAnalyzer(this.fsService));
44
+ registry.register(new RequirementsTxtAnalyzer(this.fsService));
45
+ registry.register(new PomXmlAnalyzer(this.fsService));
46
+ registry.register(new BuildGradleAnalyzer(this.fsService));
47
+ const projectContext = {
48
+ cwd: context.cwd,
49
+ files: this.filesList,
50
+ config: context.config,
51
+ };
52
+ // Execute analyzers
53
+ const analysis = await registry.runAll(projectContext);
54
+ // Generate memory files
55
+ const memoryService = new MemoryService(this.fsService);
56
+ const memoryDir = join(context.cwd, context.config.memory.directory);
57
+ await memoryService.generateMemory(analysis, memoryDir);
58
+ spinner.succeed('Memory documentation generated successfully.');
59
+ }
60
+ catch (error) {
61
+ spinner.fail('Analysis execution failed.');
62
+ throw error;
63
+ }
64
+ }
65
+ async writeOutput(_context, _output, _args) {
66
+ // No-op: files written in execution service
67
+ }
68
+ async reportResult(context, _output, _args) {
69
+ this.logger.success(`Repository documentation written to ${join(context.cwd, context.config.memory.directory)}`);
70
+ }
71
+ }
72
+ //# sourceMappingURL=learn.command.js.map
@@ -0,0 +1,28 @@
1
+ import { CommandContext, DevBrainConfig } from '@devbrain/shared';
2
+ import { FilesystemService } from '@devbrain/core';
3
+ import { Logger } from '../ui/logger.js';
4
+ /**
5
+ * Standard Command Pipeline lifecycle.
6
+ * Order: Validate -> Load Configuration -> Create Context -> Execute Service -> Write Output -> Report Result.
7
+ */
8
+ export declare abstract class CommandPipeline<TArgs = any> {
9
+ protected readonly fsService: FilesystemService;
10
+ protected logger: Logger;
11
+ /**
12
+ * Runs the complete command lifecycle with error trapping and performance boundaries.
13
+ */
14
+ execute(cwd: string, debug: boolean, args: TArgs): Promise<void>;
15
+ protected abstract validate(context: CommandContext, args: TArgs): Promise<void>;
16
+ /**
17
+ * Helper that reads the local config.json.
18
+ */
19
+ protected loadConfiguration(context: CommandContext): Promise<DevBrainConfig | undefined>;
20
+ protected abstract createContext(context: CommandContext, args: TArgs): Promise<void>;
21
+ protected abstract executeService(context: CommandContext, args: TArgs): Promise<any>;
22
+ protected abstract writeOutput(context: CommandContext, output: any, args: TArgs): Promise<void>;
23
+ protected abstract reportResult(context: CommandContext, output: any, args: TArgs): Promise<void>;
24
+ /**
25
+ * Safely formats and displays errors. Prints stack traces only if debug mode is active.
26
+ */
27
+ protected handleError(error: any, debug: boolean): void;
28
+ }
@@ -0,0 +1,65 @@
1
+ import { FilesystemService } from '@devbrain/core';
2
+ import { Logger } from '../ui/logger.js';
3
+ import { join } from 'node:path';
4
+ import { CONFIG_FILE_NAME, DEVBRAIN_DIR_NAME } from '@devbrain/shared';
5
+ /**
6
+ * Standard Command Pipeline lifecycle.
7
+ * Order: Validate -> Load Configuration -> Create Context -> Execute Service -> Write Output -> Report Result.
8
+ */
9
+ export class CommandPipeline {
10
+ fsService = new FilesystemService();
11
+ logger = new Logger();
12
+ /**
13
+ * Runs the complete command lifecycle with error trapping and performance boundaries.
14
+ */
15
+ async execute(cwd, debug, args) {
16
+ this.logger = new Logger(debug);
17
+ const context = { cwd, debug };
18
+ try {
19
+ this.logger.debug('1. Running validations...');
20
+ await this.validate(context, args);
21
+ this.logger.debug('2. Loading configuration...');
22
+ context.config = await this.loadConfiguration(context);
23
+ this.logger.debug('3. Establishing context parameters...');
24
+ await this.createContext(context, args);
25
+ this.logger.debug('4. Executing business services...');
26
+ const startTime = Date.now();
27
+ const output = await this.executeService(context, args);
28
+ const durationMs = Date.now() - startTime;
29
+ this.logger.debug(`Execution completed in ${durationMs}ms`);
30
+ this.logger.debug('5. Persisting command output...');
31
+ await this.writeOutput(context, output, args);
32
+ this.logger.debug('6. Writing user progress report...');
33
+ await this.reportResult(context, output, args);
34
+ }
35
+ catch (error) {
36
+ this.handleError(error, debug);
37
+ process.exit(1);
38
+ }
39
+ }
40
+ /**
41
+ * Helper that reads the local config.json.
42
+ */
43
+ async loadConfiguration(context) {
44
+ const configPath = join(context.cwd, DEVBRAIN_DIR_NAME, CONFIG_FILE_NAME);
45
+ if (await this.fsService.exists(configPath)) {
46
+ return await this.fsService.readJson(configPath);
47
+ }
48
+ return undefined;
49
+ }
50
+ /**
51
+ * Safely formats and displays errors. Prints stack traces only if debug mode is active.
52
+ */
53
+ handleError(error, debug) {
54
+ if (error.formatReport && typeof error.formatReport === 'function') {
55
+ this.logger.error(error.formatReport());
56
+ }
57
+ else {
58
+ this.logger.error(error.message || 'An unexpected internal error occurred.');
59
+ }
60
+ if (debug && error.stack) {
61
+ console.error('\n' + error.stack);
62
+ }
63
+ }
64
+ }
65
+ //# sourceMappingURL=command.pipeline.js.map
@@ -0,0 +1,13 @@
1
+ /**
2
+ * CLI coloring and logging utility.
3
+ */
4
+ export declare class Logger {
5
+ private readonly isDebugEnabled;
6
+ constructor(isDebugEnabled?: boolean);
7
+ info(message: string): void;
8
+ success(message: string): void;
9
+ warn(message: string): void;
10
+ error(message: string): void;
11
+ debug(message: string): void;
12
+ header(title: string): void;
13
+ }
@@ -0,0 +1,31 @@
1
+ import chalk from 'chalk';
2
+ /**
3
+ * CLI coloring and logging utility.
4
+ */
5
+ export class Logger {
6
+ isDebugEnabled;
7
+ constructor(isDebugEnabled = false) {
8
+ this.isDebugEnabled = isDebugEnabled;
9
+ }
10
+ info(message) {
11
+ console.log(chalk.blue('ℹ') + ' ' + message);
12
+ }
13
+ success(message) {
14
+ console.log(chalk.green('✔') + ' ' + chalk.bold(message));
15
+ }
16
+ warn(message) {
17
+ console.log(chalk.yellow('⚠') + ' ' + chalk.yellow(message));
18
+ }
19
+ error(message) {
20
+ console.error(chalk.red('✖') + ' ' + chalk.red.bold(message));
21
+ }
22
+ debug(message) {
23
+ if (this.isDebugEnabled) {
24
+ console.log(chalk.gray('[DEBUG]') + ' ' + message);
25
+ }
26
+ }
27
+ header(title) {
28
+ console.log('\n' + chalk.magenta.bold('=== ' + title + ' ===') + '\n');
29
+ }
30
+ }
31
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1,10 @@
1
+ import { ProjectContext, AnalysisResult } from '@devbrain/shared';
2
+ /**
3
+ * Interface definition for repository analyzers.
4
+ */
5
+ export interface Analyzer {
6
+ readonly id: string;
7
+ readonly name: string;
8
+ supports(project: ProjectContext): Promise<boolean>;
9
+ analyze(project: ProjectContext): Promise<AnalysisResult>;
10
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=analyzer.interface.js.map
@@ -0,0 +1,20 @@
1
+ import { ProjectContext, AggregatedAnalysis } from '@devbrain/shared';
2
+ import { Analyzer } from './analyzer.interface.js';
3
+ /**
4
+ * Registry for managing and executing repository analyzer plugins.
5
+ */
6
+ export declare class AnalyzerRegistry {
7
+ private readonly analyzers;
8
+ /**
9
+ * Registers a new analyzer plugin.
10
+ */
11
+ register(analyzer: Analyzer): void;
12
+ /**
13
+ * Returns copy of registered analyzers.
14
+ */
15
+ getAnalyzers(): Analyzer[];
16
+ /**
17
+ * Runs all registered analyzers that support the current project context and aggregates their results.
18
+ */
19
+ runAll(project: ProjectContext): Promise<AggregatedAnalysis>;
20
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Registry for managing and executing repository analyzer plugins.
3
+ */
4
+ export class AnalyzerRegistry {
5
+ analyzers = [];
6
+ /**
7
+ * Registers a new analyzer plugin.
8
+ */
9
+ register(analyzer) {
10
+ // Avoid duplicate registration
11
+ if (!this.analyzers.some((a) => a.id === analyzer.id)) {
12
+ this.analyzers.push(analyzer);
13
+ }
14
+ }
15
+ /**
16
+ * Returns copy of registered analyzers.
17
+ */
18
+ getAnalyzers() {
19
+ return [...this.analyzers];
20
+ }
21
+ /**
22
+ * Runs all registered analyzers that support the current project context and aggregates their results.
23
+ */
24
+ async runAll(project) {
25
+ const activeAnalyzersData = {};
26
+ const technologies = new Set();
27
+ const frameworks = new Set();
28
+ const metadata = {};
29
+ for (const analyzer of this.analyzers) {
30
+ try {
31
+ const isSupported = await analyzer.supports(project);
32
+ if (isSupported) {
33
+ const result = await analyzer.analyze(project);
34
+ activeAnalyzersData[analyzer.id] = result.data;
35
+ // Pull dynamic attributes from analyzer output
36
+ if (result.data.technologies && Array.isArray(result.data.technologies)) {
37
+ for (const tech of result.data.technologies) {
38
+ technologies.add(tech);
39
+ }
40
+ }
41
+ if (result.data.frameworks && Array.isArray(result.data.frameworks)) {
42
+ for (const fw of result.data.frameworks) {
43
+ frameworks.add(fw);
44
+ }
45
+ }
46
+ if (result.data.metadata && typeof result.data.metadata === 'object') {
47
+ Object.assign(metadata, result.data.metadata);
48
+ }
49
+ }
50
+ }
51
+ catch (error) {
52
+ activeAnalyzersData[analyzer.id] = {
53
+ error: error.message || 'Analyzer failed during execution',
54
+ };
55
+ }
56
+ }
57
+ return {
58
+ projectName: project.config.project.name,
59
+ technologies: Array.from(technologies),
60
+ frameworks: Array.from(frameworks),
61
+ metadata,
62
+ files: project.files,
63
+ analyzers: activeAnalyzersData,
64
+ };
65
+ }
66
+ }
67
+ //# sourceMappingURL=analyzer.registry.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 Gradle build.gradle configuration files.
6
+ */
7
+ export declare class BuildGradleAnalyzer implements Analyzer {
8
+ private readonly fsService;
9
+ readonly id = "build-gradle";
10
+ readonly name = "Gradle Build Analyzer";
11
+ constructor(fsService?: FilesystemService);
12
+ supports(project: ProjectContext): Promise<boolean>;
13
+ analyze(project: ProjectContext): Promise<AnalysisResult>;
14
+ }
@@ -0,0 +1,53 @@
1
+ import { join } from 'node:path';
2
+ import { FilesystemService } from '../../filesystem/filesystem.service.js';
3
+ /**
4
+ * Analyzer for Gradle build.gradle configuration files.
5
+ */
6
+ export class BuildGradleAnalyzer {
7
+ fsService;
8
+ id = 'build-gradle';
9
+ name = 'Gradle Build Analyzer';
10
+ constructor(fsService = new FilesystemService()) {
11
+ this.fsService = fsService;
12
+ }
13
+ async supports(project) {
14
+ return project.files.some((f) => f === 'build.gradle' || f === 'build.gradle.kts');
15
+ }
16
+ async analyze(project) {
17
+ const file = project.files.find((f) => f === 'build.gradle' || f === 'build.gradle.kts');
18
+ const path = join(project.cwd, file);
19
+ const content = await this.fsService.read(path);
20
+ const dependencies = [];
21
+ const frameworks = [];
22
+ // Extract dependencies using simple regex matches (e.g. implementation 'group:name:version' or implementation("group:name:version"))
23
+ const depRegex = /(?:implementation|api|compileOnly|runtimeOnly)\s*\(?\s*['"]([^'"]+)['"]\s*\)?/g;
24
+ let match;
25
+ while ((match = depRegex.exec(content)) !== null) {
26
+ const depString = match[1];
27
+ dependencies.push(depString);
28
+ if (depString.includes('spring-boot') || depString.includes('springboot')) {
29
+ frameworks.push('Spring Boot');
30
+ }
31
+ }
32
+ const groupMatch = content.match(/group\s*=\s*['"]([^'"]+)['"]/);
33
+ const group = groupMatch ? groupMatch[1] : 'unknown';
34
+ const versionMatch = content.match(/version\s*=\s*['"]([^'"]+)['"]/);
35
+ const version = versionMatch ? versionMatch[1] : 'unknown';
36
+ return {
37
+ analyzerId: this.id,
38
+ analyzerName: this.name,
39
+ data: {
40
+ group,
41
+ version,
42
+ dependencies,
43
+ frameworks,
44
+ technologies: ['Java', 'Gradle'],
45
+ metadata: {
46
+ projectLanguage: 'Java',
47
+ projectFramework: frameworks[0] || 'Java Gradle base',
48
+ },
49
+ },
50
+ };
51
+ }
52
+ }
53
+ //# sourceMappingURL=build-gradle.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 Dockerfile.
6
+ */
7
+ export declare class DockerfileAnalyzer implements Analyzer {
8
+ private readonly fsService;
9
+ readonly id = "dockerfile";
10
+ readonly name = "Dockerfile Analyzer";
11
+ constructor(fsService?: FilesystemService);
12
+ supports(project: ProjectContext): Promise<boolean>;
13
+ analyze(project: ProjectContext): Promise<AnalysisResult>;
14
+ }