devbrain-cli 0.1.0 → 0.2.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 (37) hide show
  1. package/dist/bin.js +50 -1
  2. package/dist/commands/history.command.d.ts +10 -0
  3. package/dist/commands/history.command.js +66 -0
  4. package/dist/commands/hook.command.d.ts +14 -0
  5. package/dist/commands/hook.command.js +40 -0
  6. package/dist/commands/init.command.d.ts +1 -0
  7. package/dist/commands/init.command.js +51 -15
  8. package/dist/commands/learn.command.d.ts +14 -4
  9. package/dist/commands/learn.command.js +148 -43
  10. package/dist/commands/status.command.d.ts +12 -0
  11. package/dist/commands/status.command.js +82 -0
  12. package/node_modules/@devbrain/core/dist/analysis/analyzers/source-code.analyzer.d.ts +24 -0
  13. package/node_modules/@devbrain/core/dist/analysis/analyzers/source-code.analyzer.js +126 -0
  14. package/node_modules/@devbrain/core/dist/context/context.service.d.ts +6 -7
  15. package/node_modules/@devbrain/core/dist/context/context.service.js +102 -16
  16. package/node_modules/@devbrain/core/dist/engine/dependency.resolver.d.ts +12 -0
  17. package/node_modules/@devbrain/core/dist/engine/dependency.resolver.js +101 -0
  18. package/node_modules/@devbrain/core/dist/engine/history.manager.d.ts +24 -0
  19. package/node_modules/@devbrain/core/dist/engine/history.manager.js +64 -0
  20. package/node_modules/@devbrain/core/dist/engine/lock.manager.d.ts +21 -0
  21. package/node_modules/@devbrain/core/dist/engine/lock.manager.js +89 -0
  22. package/node_modules/@devbrain/core/dist/engine/logger.service.d.ts +16 -0
  23. package/node_modules/@devbrain/core/dist/engine/logger.service.js +49 -0
  24. package/node_modules/@devbrain/core/dist/engine/memory.engine.d.ts +30 -0
  25. package/node_modules/@devbrain/core/dist/engine/memory.engine.js +200 -0
  26. package/node_modules/@devbrain/core/dist/git/git.service.d.ts +42 -0
  27. package/node_modules/@devbrain/core/dist/git/git.service.js +192 -0
  28. package/node_modules/@devbrain/core/dist/index.d.ts +7 -0
  29. package/node_modules/@devbrain/core/dist/index.js +7 -0
  30. package/node_modules/@devbrain/core/dist/memory/memory.service.d.ts +6 -3
  31. package/node_modules/@devbrain/core/dist/memory/memory.service.js +186 -6
  32. package/node_modules/@devbrain/core/package.json +2 -2
  33. package/node_modules/@devbrain/shared/dist/constants.d.ts +10 -2
  34. package/node_modules/@devbrain/shared/dist/constants.js +11 -2
  35. package/node_modules/@devbrain/shared/dist/types.d.ts +34 -0
  36. package/node_modules/@devbrain/shared/package.json +1 -1
  37. package/package.json +3 -3
@@ -1,8 +1,6 @@
1
- import { join } from 'node:path';
1
+ import { join, dirname, basename } from 'node:path';
2
+ import { PROJECT_MEMORY_FILE_NAME, CUSTOM_NOTES_FILE_NAME, } from '@devbrain/shared';
2
3
  import { FilesystemService } from '../filesystem/filesystem.service.js';
3
- /**
4
- * Service responsible for generating deterministic, human-readable markdown project documentation.
5
- */
6
4
  export class MemoryService {
7
5
  fsService;
8
6
  constructor(fsService = new FilesystemService()) {
@@ -12,6 +10,24 @@ export class MemoryService {
12
10
  * Generates memory markdown files and writes them to the specified directory.
13
11
  */
14
12
  async generateMemory(analysis, memoryDir) {
13
+ const targetDir = basename(memoryDir) === 'memory' ? dirname(memoryDir) : memoryDir;
14
+ const projectMemoryPath = join(targetDir, PROJECT_MEMORY_FILE_NAME);
15
+ const customNotesPath = join(targetDir, CUSTOM_NOTES_FILE_NAME);
16
+ // Ensure custom_notes.md exists
17
+ if (!(await this.fsService.exists(customNotesPath))) {
18
+ const defaultCustomNotes = [
19
+ '# Custom Developer Notes',
20
+ '',
21
+ 'Use this file to document manual project conventions, architecture diagrams, decision logs, or any other notes.',
22
+ 'This file will NEVER be overwritten by DevBrain.',
23
+ '',
24
+ ].join('\n');
25
+ await this.fsService.write(customNotesPath, defaultCustomNotes);
26
+ }
27
+ // Build the new unified project memory contents
28
+ const newMarkdown = this.buildProjectMemory(analysis);
29
+ await this.writeSectionBasedMarkdown(projectMemoryPath, newMarkdown);
30
+ // Generate legacy v0.1 files inside memoryDir for 100% backward compatibility
15
31
  const summary = this.buildSummary(analysis);
16
32
  const stack = this.buildStack(analysis);
17
33
  const architecture = this.buildArchitecture(analysis);
@@ -25,6 +41,171 @@ export class MemoryService {
25
41
  await this.fsService.write(join(memoryDir, 'rules.md'), rules);
26
42
  await this.fsService.write(join(memoryDir, 'timeline.md'), timeline);
27
43
  }
44
+ /**
45
+ * Section-based markdown writer to prevent redundant Git diffs.
46
+ */
47
+ async writeSectionBasedMarkdown(filePath, newContent) {
48
+ if (!(await this.fsService.exists(filePath))) {
49
+ await this.fsService.write(filePath, newContent);
50
+ return;
51
+ }
52
+ try {
53
+ const oldContent = await this.fsService.read(filePath);
54
+ const oldSections = this.parseSections(oldContent);
55
+ const newSections = this.parseSections(newContent);
56
+ let hasChanges = false;
57
+ const updatedSections = [];
58
+ const oldPreamble = oldSections.get('__PREAMBLE__') || '';
59
+ const newPreamble = newSections.get('__PREAMBLE__') || '';
60
+ if (oldPreamble !== newPreamble) {
61
+ hasChanges = true;
62
+ }
63
+ updatedSections.push(newPreamble);
64
+ for (const [header, newSecContent] of newSections.entries()) {
65
+ if (header === '__PREAMBLE__')
66
+ continue;
67
+ const oldSecContent = oldSections.get(header);
68
+ if (oldSecContent !== newSecContent) {
69
+ hasChanges = true;
70
+ }
71
+ updatedSections.push(newSecContent);
72
+ }
73
+ for (const oldHeader of oldSections.keys()) {
74
+ if (oldHeader !== '__PREAMBLE__' && !newSections.has(oldHeader)) {
75
+ hasChanges = true;
76
+ }
77
+ }
78
+ if (hasChanges) {
79
+ const compiled = updatedSections.join('\n').trim() + '\n';
80
+ await this.fsService.writeAtomic(filePath, compiled);
81
+ }
82
+ }
83
+ catch {
84
+ await this.fsService.writeAtomic(filePath, newContent);
85
+ }
86
+ }
87
+ parseSections(content) {
88
+ const sections = new Map();
89
+ const parts = content.split(/\n(?=## )/);
90
+ let preamble = '';
91
+ for (const part of parts) {
92
+ if (part.startsWith('## ') || part.trim().startsWith('## ')) {
93
+ const lines = part.trim().split('\n');
94
+ const header = lines[0].trim();
95
+ sections.set(header, part);
96
+ }
97
+ else {
98
+ preamble = part;
99
+ }
100
+ }
101
+ if (preamble) {
102
+ sections.set('__PREAMBLE__', preamble);
103
+ }
104
+ return sections;
105
+ }
106
+ buildProjectMemory(analysis) {
107
+ const readmeData = analysis.analyzers.readme || {};
108
+ const packageJsonData = analysis.analyzers['package-json'] || {};
109
+ const tsconfigData = analysis.analyzers.tsconfig || {};
110
+ const gitData = analysis.analyzers.git || {};
111
+ const dockerfileData = analysis.analyzers.dockerfile || {};
112
+ const sourceCodeData = analysis.analyzers['source-code'] || {};
113
+ const overview = readmeData.description || 'No project description available.';
114
+ const projectName = analysis.projectName || packageJsonData.name || 'unnamed-project';
115
+ const fileList = [...analysis.files].sort();
116
+ const treeLines = fileList.slice(0, 100).map((f) => `- \`${f}\``).join('\n') +
117
+ (fileList.length > 100 ? `\n- *...and ${fileList.length - 100} more files*` : '');
118
+ const techList = [...analysis.technologies]
119
+ .sort()
120
+ .map((t) => `- **${t}**`)
121
+ .join('\n') || '- None detected';
122
+ const fwList = [...analysis.frameworks]
123
+ .sort()
124
+ .map((f) => `- **${f}**`)
125
+ .join('\n') || '- None detected';
126
+ const modulesList = (sourceCodeData.modules || [])
127
+ .map((m) => `- \`${m.filePath}\` - **${m.name}** (${m.type})`)
128
+ .join('\n') || '- No source modules analyzed.';
129
+ const apisList = (sourceCodeData.apis || [])
130
+ .map((a) => `- **${a.method}** \`${a.path}\` (defined in \`${a.file}\`)`)
131
+ .join('\n') || '- No API endpoints detected.';
132
+ const dbList = (sourceCodeData.databaseEntities || [])
133
+ .map((d) => `- **${d}**`)
134
+ .join('\n') || '- No entities or model schemas detected.';
135
+ const authList = (sourceCodeData.authMethods || [])
136
+ .map((a) => `- **${a}**`)
137
+ .join('\n') || '- No explicit auth pattern identified (defaults to basic credentials/tokens).';
138
+ const extList = (sourceCodeData.externalServices || [])
139
+ .map((e) => `- **${e}**`)
140
+ .join('\n') || '- No third-party API integrations detected.';
141
+ const tsconfigOptions = tsconfigData.target
142
+ ? [
143
+ `- **TypeScript Target**: ${tsconfigData.target}`,
144
+ `- **Module System**: ${tsconfigData.module}`,
145
+ `- **Strict Mode**: ${tsconfigData.strict ? 'Enabled' : 'Disabled'}`,
146
+ ].join('\n')
147
+ : '- Standard JavaScript style conventions apply.';
148
+ const commits = gitData.recentCommits || [];
149
+ const commitLines = commits
150
+ .map((c) => `- **${c.hash}** - ${c.message} (${c.author})`)
151
+ .join('\n') || '- No commits found or git history unavailable';
152
+ return [
153
+ '<!-- ⚠️ THIS FILE IS AUTO-GENERATED BY DEVBRAIN. -->',
154
+ '<!-- DO NOT EDIT. YOUR CHANGES WILL BE OVERWRITTEN. -->',
155
+ '',
156
+ `# Project Memory: ${projectName}`,
157
+ '',
158
+ '## Project Overview',
159
+ overview,
160
+ '',
161
+ '## Tech Stack',
162
+ '### Core Languages & Runtimes',
163
+ techList,
164
+ '',
165
+ '### Frameworks & Libraries',
166
+ fwList,
167
+ '',
168
+ '## Folder Structure',
169
+ `Total Files Tracked: ${analysis.files.length}`,
170
+ '',
171
+ treeLines,
172
+ '',
173
+ '## Features',
174
+ '- Scan file structures recursively',
175
+ '- Map project dependencies',
176
+ '- Perform incremental parsing on change commit events',
177
+ '',
178
+ '## Modules',
179
+ modulesList,
180
+ '',
181
+ '## APIs',
182
+ apisList,
183
+ '',
184
+ '## Database',
185
+ dbList,
186
+ '',
187
+ '## Authentication',
188
+ authList,
189
+ '',
190
+ '## External Services',
191
+ extList,
192
+ '',
193
+ '## Configuration',
194
+ `- Container base image: ${dockerfileData.baseImage || 'None'}`,
195
+ `- Package version: ${packageJsonData.version || '0.0.1'}`,
196
+ '',
197
+ '## Coding Standards',
198
+ tsconfigOptions,
199
+ '',
200
+ '## Recent Changes',
201
+ commitLines,
202
+ '',
203
+ '## Custom Notes',
204
+ `See [custom_notes.md](${CUSTOM_NOTES_FILE_NAME}) for hand-written notes.`,
205
+ '',
206
+ ].join('\n');
207
+ }
208
+ // Legacy generators for 100% backward compatibility
28
209
  buildSummary(analysis) {
29
210
  const readmeData = analysis.analyzers.readme || {};
30
211
  const packageJsonData = analysis.analyzers['package-json'] || {};
@@ -90,7 +271,6 @@ export class MemoryService {
90
271
  buildFeatures(analysis) {
91
272
  const packageJsonData = analysis.analyzers['package-json'] || {};
92
273
  const scripts = packageJsonData.scripts || [];
93
- // Scan file paths to infer feature areas
94
274
  const features = [];
95
275
  const files = analysis.files;
96
276
  if (files.some((f) => f.includes('controller')))
@@ -106,7 +286,7 @@ export class MemoryService {
106
286
  if (files.some((f) => f.includes('tests/') || f.endsWith('.test.ts') || f.endsWith('.spec.ts')))
107
287
  features.push('Unit/Integration Testing suite');
108
288
  const featureList = features.map((f) => `- ${f}`).join('\n') || '- Basic codebase files structure';
109
- const scriptsList = scripts.map((s) => `- \`npm run ${s}\``).join('\n') || '- None declared';
289
+ const scriptsList = Object.keys(scripts).map((s) => `- \`npm run ${s}\``).join('\n') || '- None declared';
110
290
  return [
111
291
  '# Product Features',
112
292
  '',
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devbrain/core",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "private": true,
5
5
  "files": [
6
6
  "dist"
@@ -12,7 +12,7 @@
12
12
  "build": "tsc --build"
13
13
  },
14
14
  "dependencies": {
15
- "@devbrain/shared": "^0.1.0"
15
+ "@devbrain/shared": "^0.2.0"
16
16
  },
17
17
  "devDependencies": {
18
18
  "fast-glob": "^3.3.2",
@@ -1,6 +1,14 @@
1
- export declare const DEVBRAIN_VERSION = "0.1.0";
1
+ export declare const DEVBRAIN_VERSION = "0.2.0";
2
2
  export declare const DEFAULT_IGNORE_PATTERNS: string[];
3
3
  export declare const CONFIG_FILE_NAME = "config.json";
4
- export declare const MEMORY_DIR_NAME = "memory";
5
4
  export declare const DEVBRAIN_DIR_NAME = ".devbrain";
5
+ export declare const VERSION_FILE_NAME = "version.json";
6
+ export declare const HISTORY_FILE_NAME = "history.json";
7
+ export declare const MEMORY_FILE_NAME = "memory.json";
8
+ export declare const PROJECT_MEMORY_FILE_NAME = "PROJECT_MEMORY.md";
9
+ export declare const CONTEXT_FILE_NAME = "context.md";
10
+ export declare const CUSTOM_NOTES_FILE_NAME = "custom_notes.md";
11
+ export declare const LOCK_FILE_NAME = ".lock";
12
+ export declare const LOGS_DIR_NAME = "logs";
13
+ export declare const MEMORY_DIR_NAME = "memory";
6
14
  export declare const DEFAULT_MEMORY_DIR = ".devbrain/memory";
@@ -1,4 +1,4 @@
1
- export const DEVBRAIN_VERSION = '0.1.0';
1
+ export const DEVBRAIN_VERSION = '0.2.0';
2
2
  export const DEFAULT_IGNORE_PATTERNS = [
3
3
  '**/node_modules/**',
4
4
  '**/.git/**',
@@ -9,7 +9,16 @@ export const DEFAULT_IGNORE_PATTERNS = [
9
9
  '**/.cache/**',
10
10
  ];
11
11
  export const CONFIG_FILE_NAME = 'config.json';
12
- export const MEMORY_DIR_NAME = 'memory';
13
12
  export const DEVBRAIN_DIR_NAME = '.devbrain';
13
+ export const VERSION_FILE_NAME = 'version.json';
14
+ export const HISTORY_FILE_NAME = 'history.json';
15
+ export const MEMORY_FILE_NAME = 'memory.json';
16
+ export const PROJECT_MEMORY_FILE_NAME = 'PROJECT_MEMORY.md';
17
+ export const CONTEXT_FILE_NAME = 'context.md';
18
+ export const CUSTOM_NOTES_FILE_NAME = 'custom_notes.md';
19
+ export const LOCK_FILE_NAME = '.lock';
20
+ export const LOGS_DIR_NAME = 'logs';
21
+ // Deprecated in v0.2, kept for backward compatibility if referenced
22
+ export const MEMORY_DIR_NAME = 'memory';
14
23
  export const DEFAULT_MEMORY_DIR = `${DEVBRAIN_DIR_NAME}/${MEMORY_DIR_NAME}`;
15
24
  //# sourceMappingURL=constants.js.map
@@ -17,6 +17,40 @@ export interface DevBrainConfig {
17
17
  format: 'markdown';
18
18
  directory: string;
19
19
  };
20
+ auto_update?: boolean;
21
+ enabled_hooks?: string[];
22
+ ignored_directories?: string[];
23
+ ignored_extensions?: string[];
24
+ maximum_incremental_files?: number;
25
+ documentation_generation?: boolean;
26
+ context_generation?: boolean;
27
+ verbose_logging?: boolean;
28
+ safe_mode?: boolean;
29
+ }
30
+ /**
31
+ * Entry schema for commits history tracking.
32
+ */
33
+ export interface CommitHistoryEntry {
34
+ commitHash: string;
35
+ timestamp: string;
36
+ status: 'success' | 'failed' | 'skipped';
37
+ filesAnalyzed: string[];
38
+ commitMessage: string;
39
+ }
40
+ /**
41
+ * Schema for history.json.
42
+ */
43
+ export interface HistorySchema {
44
+ lastProcessedCommit: string;
45
+ commits: CommitHistoryEntry[];
46
+ }
47
+ /**
48
+ * Schema for version.json.
49
+ */
50
+ export interface VersionSchema {
51
+ version: string;
52
+ memorySchema: number;
53
+ lastMigration: string;
20
54
  }
21
55
  /**
22
56
  * Context that is passed through the command lifecycle pipeline.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devbrain/shared",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "private": true,
5
5
  "files": [
6
6
  "dist"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devbrain-cli",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Offline-first CLI tool providing persistent, deterministic project memory for AI assistants.",
5
5
  "keywords": [
6
6
  "devbrain",
@@ -46,8 +46,8 @@
46
46
  "prepack": "node ../../scripts/prepare-cli.js"
47
47
  },
48
48
  "dependencies": {
49
- "@devbrain/core": "^0.1.0",
50
- "@devbrain/shared": "^0.1.0",
49
+ "@devbrain/core": "^0.2.0",
50
+ "@devbrain/shared": "^0.2.0",
51
51
  "commander": "^12.0.0",
52
52
  "chalk": "^5.3.0",
53
53
  "ora": "^8.0.1",