neuronlayer 0.1.9 → 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.

Potentially problematic release.


This version of neuronlayer might be problematic. Click here for more details.

Files changed (81) hide show
  1. package/README.md +3 -2
  2. package/dist/index.js +172 -90
  3. package/dist/index.js.map +7 -0
  4. package/package.json +6 -1
  5. package/esbuild.config.js +0 -26
  6. package/src/cli/commands.ts +0 -573
  7. package/src/core/adr-exporter.ts +0 -253
  8. package/src/core/architecture/architecture-enforcement.ts +0 -228
  9. package/src/core/architecture/duplicate-detector.ts +0 -288
  10. package/src/core/architecture/index.ts +0 -6
  11. package/src/core/architecture/pattern-learner.ts +0 -306
  12. package/src/core/architecture/pattern-library.ts +0 -403
  13. package/src/core/architecture/pattern-validator.ts +0 -324
  14. package/src/core/change-intelligence/bug-correlator.ts +0 -544
  15. package/src/core/change-intelligence/change-intelligence.ts +0 -264
  16. package/src/core/change-intelligence/change-tracker.ts +0 -334
  17. package/src/core/change-intelligence/fix-suggester.ts +0 -340
  18. package/src/core/change-intelligence/index.ts +0 -5
  19. package/src/core/code-verifier.ts +0 -843
  20. package/src/core/confidence/confidence-scorer.ts +0 -251
  21. package/src/core/confidence/conflict-checker.ts +0 -289
  22. package/src/core/confidence/index.ts +0 -5
  23. package/src/core/confidence/source-tracker.ts +0 -263
  24. package/src/core/confidence/warning-detector.ts +0 -241
  25. package/src/core/context-rot/compaction.ts +0 -284
  26. package/src/core/context-rot/context-health.ts +0 -243
  27. package/src/core/context-rot/context-rot-prevention.ts +0 -213
  28. package/src/core/context-rot/critical-context.ts +0 -221
  29. package/src/core/context-rot/drift-detector.ts +0 -255
  30. package/src/core/context-rot/index.ts +0 -7
  31. package/src/core/context.ts +0 -263
  32. package/src/core/decision-extractor.ts +0 -339
  33. package/src/core/decisions.ts +0 -69
  34. package/src/core/deja-vu.ts +0 -421
  35. package/src/core/engine.ts +0 -1646
  36. package/src/core/feature-context.ts +0 -726
  37. package/src/core/ghost-mode.ts +0 -465
  38. package/src/core/learning.ts +0 -519
  39. package/src/core/living-docs/activity-tracker.ts +0 -296
  40. package/src/core/living-docs/architecture-generator.ts +0 -428
  41. package/src/core/living-docs/changelog-generator.ts +0 -348
  42. package/src/core/living-docs/component-generator.ts +0 -230
  43. package/src/core/living-docs/doc-engine.ts +0 -134
  44. package/src/core/living-docs/doc-validator.ts +0 -282
  45. package/src/core/living-docs/index.ts +0 -8
  46. package/src/core/project-manager.ts +0 -301
  47. package/src/core/refresh/activity-gate.ts +0 -256
  48. package/src/core/refresh/git-staleness-checker.ts +0 -108
  49. package/src/core/refresh/index.ts +0 -27
  50. package/src/core/summarizer.ts +0 -290
  51. package/src/core/test-awareness/change-validator.ts +0 -499
  52. package/src/core/test-awareness/index.ts +0 -5
  53. package/src/index.ts +0 -90
  54. package/src/indexing/ast.ts +0 -868
  55. package/src/indexing/embeddings.ts +0 -85
  56. package/src/indexing/indexer.ts +0 -270
  57. package/src/indexing/watcher.ts +0 -78
  58. package/src/server/gateways/aggregator.ts +0 -374
  59. package/src/server/gateways/index.ts +0 -473
  60. package/src/server/gateways/memory-ghost.ts +0 -343
  61. package/src/server/gateways/memory-query.ts +0 -452
  62. package/src/server/gateways/memory-record.ts +0 -346
  63. package/src/server/gateways/memory-review.ts +0 -410
  64. package/src/server/gateways/memory-status.ts +0 -517
  65. package/src/server/gateways/memory-verify.ts +0 -392
  66. package/src/server/gateways/router.ts +0 -434
  67. package/src/server/gateways/types.ts +0 -610
  68. package/src/server/http.ts +0 -228
  69. package/src/server/mcp.ts +0 -154
  70. package/src/server/resources.ts +0 -85
  71. package/src/server/tools.ts +0 -2460
  72. package/src/storage/database.ts +0 -271
  73. package/src/storage/tier1.ts +0 -135
  74. package/src/storage/tier2.ts +0 -972
  75. package/src/storage/tier3.ts +0 -123
  76. package/src/types/documentation.ts +0 -619
  77. package/src/types/index.ts +0 -222
  78. package/src/utils/config.ts +0 -194
  79. package/src/utils/files.ts +0 -117
  80. package/src/utils/time.ts +0 -37
  81. package/src/utils/tokens.ts +0 -52
@@ -1,348 +0,0 @@
1
- import { execSync } from 'child_process';
2
- import { existsSync } from 'fs';
3
- import { join } from 'path';
4
- import type Database from 'better-sqlite3';
5
- import type {
6
- DailyChangelog,
7
- ChangeEntry,
8
- FileChangeInfo,
9
- ChangeMetrics,
10
- ChangelogOptions
11
- } from '../../types/documentation.js';
12
-
13
- interface CommitInfo {
14
- hash: string;
15
- subject: string;
16
- author: string;
17
- date: Date;
18
- files: string[];
19
- additions: number;
20
- deletions: number;
21
- }
22
-
23
- interface DayGroup {
24
- date: Date;
25
- commits: CommitInfo[];
26
- entries: ChangeEntry[];
27
- }
28
-
29
- export class ChangelogGenerator {
30
- private projectPath: string;
31
- private db: Database.Database;
32
- private isGitRepo: boolean;
33
-
34
- constructor(projectPath: string, db: Database.Database) {
35
- this.projectPath = projectPath;
36
- this.db = db;
37
- this.isGitRepo = existsSync(join(projectPath, '.git'));
38
- }
39
-
40
- async generate(options: ChangelogOptions = {}): Promise<DailyChangelog[]> {
41
- if (!this.isGitRepo) {
42
- return [];
43
- }
44
-
45
- const since = this.parseSinceString(options.since || 'this week');
46
- const until = options.until || new Date();
47
-
48
- const commits = this.getCommitsInRange(since, until);
49
- const grouped = this.groupByDay(commits);
50
-
51
- return grouped.map(day => this.createDailyChangelog(day, options.includeDecisions ?? false));
52
- }
53
-
54
- private parseSinceString(since: Date | string): Date {
55
- if (since instanceof Date) {
56
- return since;
57
- }
58
-
59
- const now = new Date();
60
- const lower = since.toLowerCase();
61
-
62
- if (lower === 'yesterday') {
63
- const yesterday = new Date(now);
64
- yesterday.setDate(yesterday.getDate() - 1);
65
- yesterday.setHours(0, 0, 0, 0);
66
- return yesterday;
67
- }
68
- if (lower === 'today') {
69
- const today = new Date(now);
70
- today.setHours(0, 0, 0, 0);
71
- return today;
72
- }
73
- if (lower === 'this week') {
74
- const dayOfWeek = now.getDay();
75
- const startOfWeek = new Date(now);
76
- startOfWeek.setDate(now.getDate() - dayOfWeek);
77
- startOfWeek.setHours(0, 0, 0, 0);
78
- return startOfWeek;
79
- }
80
- if (lower === 'this month') {
81
- return new Date(now.getFullYear(), now.getMonth(), 1);
82
- }
83
- if (lower === 'last week') {
84
- const dayOfWeek = now.getDay();
85
- const startOfLastWeek = new Date(now);
86
- startOfLastWeek.setDate(now.getDate() - dayOfWeek - 7);
87
- startOfLastWeek.setHours(0, 0, 0, 0);
88
- return startOfLastWeek;
89
- }
90
-
91
- // Try to parse as date string
92
- const parsed = new Date(since);
93
- return isNaN(parsed.getTime()) ? new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000) : parsed;
94
- }
95
-
96
- private getCommitsInRange(since: Date, until: Date): CommitInfo[] {
97
- const commits: CommitInfo[] = [];
98
-
99
- try {
100
- const sinceStr = since.toISOString().split('T')[0];
101
- const untilStr = until.toISOString().split('T')[0];
102
-
103
- // Get commit list with basic info
104
- const output = execSync(
105
- `git log --since="${sinceStr}" --until="${untilStr}" --format="%H|%s|%an|%ad" --date=iso-strict`,
106
- { cwd: this.projectPath, encoding: 'utf-8', maxBuffer: 5 * 1024 * 1024 }
107
- );
108
-
109
- const lines = output.trim().split('\n').filter(Boolean);
110
-
111
- for (const line of lines) {
112
- const [hash, subject, author, dateStr] = line.split('|');
113
- if (!hash || !subject) continue;
114
-
115
- // Get file stats for this commit
116
- const { files, additions, deletions } = this.getCommitStats(hash);
117
-
118
- commits.push({
119
- hash: hash.slice(0, 8),
120
- subject: subject || '',
121
- author: author || 'Unknown',
122
- date: new Date(dateStr || Date.now()),
123
- files,
124
- additions,
125
- deletions
126
- });
127
- }
128
- } catch (error) {
129
- // Git command failed
130
- }
131
-
132
- return commits;
133
- }
134
-
135
- private getCommitStats(hash: string): { files: string[]; additions: number; deletions: number } {
136
- try {
137
- const output = execSync(
138
- `git show --stat --format="" "${hash}"`,
139
- { cwd: this.projectPath, encoding: 'utf-8', maxBuffer: 1024 * 1024 }
140
- );
141
-
142
- const files: string[] = [];
143
- let additions = 0;
144
- let deletions = 0;
145
-
146
- const lines = output.trim().split('\n');
147
- for (const line of lines) {
148
- // Match file lines like: src/file.ts | 10 +++++-----
149
- const fileMatch = line.match(/^\s*([^\|]+)\s*\|/);
150
- if (fileMatch && fileMatch[1]) {
151
- files.push(fileMatch[1].trim());
152
- }
153
-
154
- // Match insertions/deletions from summary line
155
- const insertMatch = line.match(/(\d+)\s+insertion/);
156
- const deleteMatch = line.match(/(\d+)\s+deletion/);
157
- if (insertMatch) additions = parseInt(insertMatch[1]!, 10);
158
- if (deleteMatch) deletions = parseInt(deleteMatch[1]!, 10);
159
- }
160
-
161
- return { files, additions, deletions };
162
- } catch {
163
- return { files: [], additions: 0, deletions: 0 };
164
- }
165
- }
166
-
167
- private groupByDay(commits: CommitInfo[]): DayGroup[] {
168
- const groups = new Map<string, DayGroup>();
169
-
170
- for (const commit of commits) {
171
- const dateKey = commit.date.toISOString().split('T')[0]!;
172
-
173
- if (!groups.has(dateKey)) {
174
- const dayDate = new Date(dateKey);
175
- groups.set(dateKey, {
176
- date: dayDate,
177
- commits: [],
178
- entries: []
179
- });
180
- }
181
-
182
- const group = groups.get(dateKey)!;
183
- group.commits.push(commit);
184
- group.entries.push(this.commitToEntry(commit));
185
- }
186
-
187
- // Sort by date descending (most recent first)
188
- return Array.from(groups.values()).sort((a, b) => b.date.getTime() - a.date.getTime());
189
- }
190
-
191
- private commitToEntry(commit: CommitInfo): ChangeEntry {
192
- return {
193
- type: this.categorizeCommit(commit.subject),
194
- description: this.cleanCommitMessage(commit.subject),
195
- files: commit.files,
196
- commit: commit.hash
197
- };
198
- }
199
-
200
- private categorizeCommit(subject: string): ChangeEntry['type'] {
201
- const lower = subject.toLowerCase();
202
-
203
- // Check conventional commit prefixes
204
- if (lower.startsWith('feat') || lower.includes('add ') || lower.includes('implement')) {
205
- return 'feature';
206
- }
207
- if (lower.startsWith('fix') || lower.includes('bug') || lower.includes('resolve')) {
208
- return 'fix';
209
- }
210
- if (lower.startsWith('refactor') || lower.includes('cleanup') || lower.includes('reorganize')) {
211
- return 'refactor';
212
- }
213
- if (lower.startsWith('docs') || lower.includes('documentation') || lower.includes('readme')) {
214
- return 'docs';
215
- }
216
- if (lower.startsWith('test') || lower.includes('spec') || lower.includes('coverage')) {
217
- return 'test';
218
- }
219
-
220
- return 'chore';
221
- }
222
-
223
- private cleanCommitMessage(subject: string): string {
224
- // Remove conventional commit prefix
225
- return subject
226
- .replace(/^(?:feat|fix|refactor|docs|test|chore|perf|build|ci|style)\([^)]*\):\s*/i, '')
227
- .replace(/^(?:feat|fix|refactor|docs|test|chore|perf|build|ci|style):\s*/i, '')
228
- .trim();
229
- }
230
-
231
- private createDailyChangelog(day: DayGroup, includeDecisions: boolean): DailyChangelog {
232
- const features = day.entries.filter(e => e.type === 'feature');
233
- const fixes = day.entries.filter(e => e.type === 'fix');
234
- const refactors = day.entries.filter(e => e.type === 'refactor');
235
-
236
- const filesModified = this.aggregateFileChanges(day.commits);
237
- const metrics = this.calculateMetrics(day.commits);
238
- const decisions = includeDecisions ? this.getDecisionsForDate(day.date) : [];
239
-
240
- return {
241
- date: day.date,
242
- summary: this.generateSummary(day.entries, metrics),
243
- features,
244
- fixes,
245
- refactors,
246
- filesModified,
247
- decisions,
248
- metrics
249
- };
250
- }
251
-
252
- private aggregateFileChanges(commits: CommitInfo[]): FileChangeInfo[] {
253
- const fileMap = new Map<string, FileChangeInfo>();
254
-
255
- for (const commit of commits) {
256
- for (const file of commit.files) {
257
- if (!fileMap.has(file)) {
258
- fileMap.set(file, {
259
- file,
260
- added: 0,
261
- removed: 0,
262
- type: 'modified'
263
- });
264
- }
265
- }
266
- }
267
-
268
- // Distribute additions/deletions proportionally (rough estimate)
269
- for (const commit of commits) {
270
- const fileCount = commit.files.length || 1;
271
- const addPerFile = Math.round(commit.additions / fileCount);
272
- const delPerFile = Math.round(commit.deletions / fileCount);
273
-
274
- for (const file of commit.files) {
275
- const info = fileMap.get(file);
276
- if (info) {
277
- info.added += addPerFile;
278
- info.removed += delPerFile;
279
- }
280
- }
281
- }
282
-
283
- return Array.from(fileMap.values());
284
- }
285
-
286
- private calculateMetrics(commits: CommitInfo[]): ChangeMetrics {
287
- const filesSet = new Set<string>();
288
- let linesAdded = 0;
289
- let linesRemoved = 0;
290
-
291
- for (const commit of commits) {
292
- for (const file of commit.files) {
293
- filesSet.add(file);
294
- }
295
- linesAdded += commit.additions;
296
- linesRemoved += commit.deletions;
297
- }
298
-
299
- return {
300
- commits: commits.length,
301
- filesChanged: filesSet.size,
302
- linesAdded,
303
- linesRemoved
304
- };
305
- }
306
-
307
- private generateSummary(entries: ChangeEntry[], metrics: ChangeMetrics): string {
308
- const parts: string[] = [];
309
-
310
- const features = entries.filter(e => e.type === 'feature').length;
311
- const fixes = entries.filter(e => e.type === 'fix').length;
312
- const refactors = entries.filter(e => e.type === 'refactor').length;
313
-
314
- if (features > 0) {
315
- parts.push(`${features} feature${features > 1 ? 's' : ''}`);
316
- }
317
- if (fixes > 0) {
318
- parts.push(`${fixes} fix${fixes > 1 ? 'es' : ''}`);
319
- }
320
- if (refactors > 0) {
321
- parts.push(`${refactors} refactor${refactors > 1 ? 's' : ''}`);
322
- }
323
-
324
- if (parts.length === 0) {
325
- return `${metrics.commits} commit${metrics.commits !== 1 ? 's' : ''} affecting ${metrics.filesChanged} file${metrics.filesChanged !== 1 ? 's' : ''}`;
326
- }
327
-
328
- return `${parts.join(', ')} across ${metrics.filesChanged} file${metrics.filesChanged !== 1 ? 's' : ''}`;
329
- }
330
-
331
- private getDecisionsForDate(date: Date): string[] {
332
- try {
333
- const startOfDay = Math.floor(new Date(date).setHours(0, 0, 0, 0) / 1000);
334
- const endOfDay = Math.floor(new Date(date).setHours(23, 59, 59, 999) / 1000);
335
-
336
- const stmt = this.db.prepare(`
337
- SELECT title FROM decisions
338
- WHERE created_at >= ? AND created_at <= ?
339
- ORDER BY created_at
340
- `);
341
-
342
- const rows = stmt.all(startOfDay, endOfDay) as Array<{ title: string }>;
343
- return rows.map(r => r.title);
344
- } catch {
345
- return [];
346
- }
347
- }
348
- }
@@ -1,230 +0,0 @@
1
- import { execSync } from 'child_process';
2
- import { existsSync } from 'fs';
3
- import { basename, extname, join } from 'path';
4
- import type { Tier2Storage } from '../../storage/tier2.js';
5
- import type { CodeSymbol } from '../../types/index.js';
6
- import type {
7
- ComponentDoc,
8
- SymbolDoc,
9
- DependencyDoc,
10
- DependentDoc,
11
- ChangeHistoryEntry
12
- } from '../../types/documentation.js';
13
-
14
- export class ComponentGenerator {
15
- private projectPath: string;
16
- private tier2: Tier2Storage;
17
- private isGitRepo: boolean;
18
-
19
- constructor(projectPath: string, tier2: Tier2Storage) {
20
- this.projectPath = projectPath;
21
- this.tier2 = tier2;
22
- this.isGitRepo = existsSync(join(projectPath, '.git'));
23
- }
24
-
25
- async generate(filePath: string): Promise<ComponentDoc> {
26
- const file = this.tier2.getFile(filePath);
27
- if (!file) {
28
- throw new Error(`File not found: ${filePath}`);
29
- }
30
-
31
- const symbols = this.tier2.getSymbolsByFile(file.id);
32
- const imports = this.tier2.getImportsByFile(file.id);
33
- const dependents = this.tier2.getFileDependents(filePath);
34
- const history = this.getChangeHistory(filePath);
35
-
36
- return {
37
- file: filePath,
38
- name: basename(filePath, extname(filePath)),
39
- purpose: this.inferPurpose(filePath, symbols),
40
- lastModified: new Date(file.lastModified * 1000),
41
- publicInterface: this.extractPublicInterface(symbols),
42
- dependencies: this.formatDependencies(imports),
43
- dependents: this.formatDependents(dependents),
44
- changeHistory: history,
45
- contributors: this.extractContributors(history),
46
- complexity: this.calculateComplexity(symbols),
47
- documentationScore: this.calculateDocScore(symbols)
48
- };
49
- }
50
-
51
- private inferPurpose(filePath: string, symbols: CodeSymbol[]): string {
52
- const name = basename(filePath, extname(filePath));
53
- const parts: string[] = [];
54
-
55
- // Infer from filename
56
- if (name.toLowerCase().includes('test')) {
57
- return `Test file for ${name.replace(/\.test|\.spec|Test|Spec/gi, '')}`;
58
- }
59
- if (name.toLowerCase().includes('util') || name.toLowerCase().includes('helper')) {
60
- return 'Utility functions and helpers';
61
- }
62
- if (name === 'index') {
63
- return 'Module entry point / barrel export';
64
- }
65
-
66
- // Infer from exported symbols
67
- const exported = symbols.filter(s => s.exported);
68
- const classes = exported.filter(s => s.kind === 'class');
69
- const interfaces = exported.filter(s => s.kind === 'interface');
70
- const functions = exported.filter(s => s.kind === 'function');
71
-
72
- if (classes.length === 1) {
73
- return `Defines the ${classes[0]!.name} class`;
74
- }
75
- if (interfaces.length > 0 && functions.length === 0) {
76
- return `Type definitions: ${interfaces.map(i => i.name).slice(0, 3).join(', ')}`;
77
- }
78
- if (functions.length > 0) {
79
- parts.push(`Functions: ${functions.map(f => f.name).slice(0, 3).join(', ')}`);
80
- }
81
-
82
- // Infer from directory
83
- const pathParts = filePath.split(/[/\\]/);
84
- const parentDir = pathParts[pathParts.length - 2];
85
- if (parentDir) {
86
- const dirPurposes: Record<string, string> = {
87
- 'server': 'Server-side code',
88
- 'api': 'API layer',
89
- 'core': 'Core business logic',
90
- 'storage': 'Data storage',
91
- 'utils': 'Utilities',
92
- 'types': 'Type definitions',
93
- 'components': 'UI components',
94
- 'hooks': 'React hooks',
95
- 'services': 'Service layer'
96
- };
97
- if (dirPurposes[parentDir]) {
98
- parts.unshift(dirPurposes[parentDir]);
99
- }
100
- }
101
-
102
- return parts.length > 0 ? parts.join('. ') : `Module: ${name}`;
103
- }
104
-
105
- private extractPublicInterface(symbols: CodeSymbol[]): SymbolDoc[] {
106
- return symbols
107
- .filter(s => s.exported)
108
- .map(s => ({
109
- name: s.name,
110
- kind: s.kind,
111
- signature: s.signature,
112
- description: s.docstring,
113
- lineStart: s.lineStart,
114
- lineEnd: s.lineEnd,
115
- exported: true
116
- }));
117
- }
118
-
119
- private formatDependencies(imports: Array<{
120
- importedFrom: string;
121
- importedSymbols: string[];
122
- }>): DependencyDoc[] {
123
- return imports.map(i => ({
124
- file: i.importedFrom,
125
- symbols: i.importedSymbols
126
- }));
127
- }
128
-
129
- private formatDependents(dependents: Array<{
130
- file: string;
131
- imports: string[];
132
- }>): DependentDoc[] {
133
- return dependents.map(d => ({
134
- file: d.file,
135
- symbols: d.imports
136
- }));
137
- }
138
-
139
- private getChangeHistory(filePath: string): ChangeHistoryEntry[] {
140
- const history: ChangeHistoryEntry[] = [];
141
-
142
- if (!this.isGitRepo) {
143
- return history;
144
- }
145
-
146
- try {
147
- const output = execSync(
148
- `git log --oneline -20 --format="%H|%s|%an|%ad" --date=short -- "${filePath}"`,
149
- { cwd: this.projectPath, encoding: 'utf-8', maxBuffer: 1024 * 1024 }
150
- );
151
-
152
- const lines = output.trim().split('\n').filter(Boolean);
153
-
154
- for (const line of lines) {
155
- const [hash, subject, author, dateStr] = line.split('|');
156
- if (!hash || !subject) continue;
157
-
158
- const { added, removed } = this.getCommitLineChanges(hash, filePath);
159
-
160
- history.push({
161
- date: new Date(dateStr || Date.now()),
162
- change: subject,
163
- author: author || 'Unknown',
164
- commit: hash.slice(0, 8),
165
- linesChanged: { added, removed }
166
- });
167
- }
168
- } catch {
169
- // Git command failed
170
- }
171
-
172
- return history;
173
- }
174
-
175
- private getCommitLineChanges(hash: string, filePath: string): { added: number; removed: number } {
176
- try {
177
- const output = execSync(
178
- `git show --numstat --format="" "${hash}" -- "${filePath}"`,
179
- { cwd: this.projectPath, encoding: 'utf-8', maxBuffer: 1024 * 1024 }
180
- );
181
-
182
- const match = output.trim().match(/^(\d+)\s+(\d+)/);
183
- if (match) {
184
- return {
185
- added: parseInt(match[1]!, 10),
186
- removed: parseInt(match[2]!, 10)
187
- };
188
- }
189
- } catch {
190
- // Git command failed
191
- }
192
-
193
- return { added: 0, removed: 0 };
194
- }
195
-
196
- private extractContributors(history: ChangeHistoryEntry[]): string[] {
197
- const contributors = new Set<string>();
198
- for (const entry of history) {
199
- contributors.add(entry.author);
200
- }
201
- return Array.from(contributors);
202
- }
203
-
204
- private calculateComplexity(symbols: CodeSymbol[]): 'low' | 'medium' | 'high' {
205
- const totalSymbols = symbols.length;
206
- const exportedSymbols = symbols.filter(s => s.exported).length;
207
- const avgLineSpan = symbols.length > 0
208
- ? symbols.reduce((sum, s) => sum + (s.lineEnd - s.lineStart), 0) / symbols.length
209
- : 0;
210
-
211
- // Simple heuristic based on symbol count and size
212
- if (totalSymbols > 20 || avgLineSpan > 50) {
213
- return 'high';
214
- }
215
- if (totalSymbols > 8 || avgLineSpan > 25) {
216
- return 'medium';
217
- }
218
- return 'low';
219
- }
220
-
221
- private calculateDocScore(symbols: CodeSymbol[]): number {
222
- const exported = symbols.filter(s => s.exported);
223
- if (exported.length === 0) {
224
- return 100; // No public API, considered fully documented
225
- }
226
-
227
- const documented = exported.filter(s => s.docstring && s.docstring.length > 0);
228
- return Math.round((documented.length / exported.length) * 100);
229
- }
230
- }
@@ -1,134 +0,0 @@
1
- import type Database from 'better-sqlite3';
2
- import type { Tier2Storage } from '../../storage/tier2.js';
3
- import { ArchitectureGenerator } from './architecture-generator.js';
4
- import { ComponentGenerator } from './component-generator.js';
5
- import { ChangelogGenerator } from './changelog-generator.js';
6
- import { DocValidator } from './doc-validator.js';
7
- import { ActivityTracker } from './activity-tracker.js';
8
- import type {
9
- ArchitectureDoc,
10
- ComponentDoc,
11
- DailyChangelog,
12
- ChangelogOptions,
13
- ValidationResult,
14
- ActivityResult,
15
- UndocumentedItem
16
- } from '../../types/documentation.js';
17
-
18
- export class LivingDocumentationEngine {
19
- private archGen: ArchitectureGenerator;
20
- private compGen: ComponentGenerator;
21
- private changeGen: ChangelogGenerator;
22
- private validator: DocValidator;
23
- private activityTracker: ActivityTracker;
24
- private db: Database.Database;
25
-
26
- constructor(
27
- projectPath: string,
28
- dataDir: string,
29
- db: Database.Database,
30
- tier2: Tier2Storage
31
- ) {
32
- this.db = db;
33
- this.archGen = new ArchitectureGenerator(projectPath, tier2);
34
- this.compGen = new ComponentGenerator(projectPath, tier2);
35
- this.changeGen = new ChangelogGenerator(projectPath, db);
36
- this.validator = new DocValidator(projectPath, tier2, db);
37
- this.activityTracker = new ActivityTracker(projectPath, db, tier2);
38
- }
39
-
40
- async generateArchitectureDocs(): Promise<ArchitectureDoc> {
41
- const doc = await this.archGen.generate();
42
-
43
- // Store architecture docs in documentation table
44
- this.storeDocumentation('_architecture', 'architecture', JSON.stringify(doc));
45
-
46
- // Log activity
47
- this.activityTracker.logActivity(
48
- 'doc_generation',
49
- 'Generated architecture documentation',
50
- undefined,
51
- { type: 'architecture' }
52
- );
53
-
54
- return doc;
55
- }
56
-
57
- /**
58
- * Get the activity tracker for external use
59
- */
60
- getActivityTracker(): ActivityTracker {
61
- return this.activityTracker;
62
- }
63
-
64
- async generateComponentDoc(filePath: string): Promise<ComponentDoc> {
65
- const doc = await this.compGen.generate(filePath);
66
-
67
- // Store in documentation table for tracking
68
- this.storeDocumentation(filePath, 'component', JSON.stringify(doc));
69
-
70
- // Log activity
71
- this.activityTracker.logActivity(
72
- 'doc_generation',
73
- `Generated component documentation for ${filePath}`,
74
- filePath,
75
- { type: 'component' }
76
- );
77
-
78
- return doc;
79
- }
80
-
81
- async generateChangelog(options: ChangelogOptions = {}): Promise<DailyChangelog[]> {
82
- return this.changeGen.generate(options);
83
- }
84
-
85
- async validateDocs(): Promise<ValidationResult> {
86
- return this.validator.validate();
87
- }
88
-
89
- async whatHappened(since: string, scope?: string): Promise<ActivityResult> {
90
- return this.activityTracker.whatHappened(since, scope);
91
- }
92
-
93
- async findUndocumented(options?: {
94
- importance?: 'low' | 'medium' | 'high' | 'all';
95
- type?: 'file' | 'function' | 'class' | 'interface' | 'all';
96
- }): Promise<UndocumentedItem[]> {
97
- return this.validator.findUndocumented(options);
98
- }
99
-
100
- private storeDocumentation(filePath: string, docType: string, content: string): void {
101
- try {
102
- // Special handling for architecture docs (no file ID)
103
- if (filePath === '_architecture') {
104
- // Use file_id = 0 for special docs like architecture
105
- const stmt = this.db.prepare(`
106
- INSERT INTO documentation (file_id, doc_type, content, generated_at)
107
- VALUES (0, ?, ?, unixepoch())
108
- ON CONFLICT(file_id, doc_type) DO UPDATE SET
109
- content = excluded.content,
110
- generated_at = unixepoch()
111
- `);
112
- stmt.run(docType, content);
113
- return;
114
- }
115
-
116
- // Get file ID
117
- const fileStmt = this.db.prepare('SELECT id FROM files WHERE path = ?');
118
- const fileRow = fileStmt.get(filePath) as { id: number } | undefined;
119
-
120
- if (fileRow) {
121
- const stmt = this.db.prepare(`
122
- INSERT INTO documentation (file_id, doc_type, content, generated_at)
123
- VALUES (?, ?, ?, unixepoch())
124
- ON CONFLICT(file_id, doc_type) DO UPDATE SET
125
- content = excluded.content,
126
- generated_at = unixepoch()
127
- `);
128
- stmt.run(fileRow.id, docType, content);
129
- }
130
- } catch {
131
- // Ignore storage errors
132
- }
133
- }
134
- }