fivocell 2.0.0 → 3.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/README.md +104 -697
  2. package/dist/behavioral-tracker.d.ts +90 -0
  3. package/dist/behavioral-tracker.d.ts.map +1 -0
  4. package/dist/behavioral-tracker.js +185 -0
  5. package/dist/behavioral-tracker.js.map +1 -0
  6. package/dist/cli.d.ts +1 -6
  7. package/dist/cli.d.ts.map +1 -1
  8. package/dist/cli.js +430 -3862
  9. package/dist/cli.js.map +1 -1
  10. package/dist/code-scanner.d.ts +51 -0
  11. package/dist/code-scanner.d.ts.map +1 -0
  12. package/dist/code-scanner.js +966 -0
  13. package/dist/code-scanner.js.map +1 -0
  14. package/dist/community-intel.d.ts +34 -0
  15. package/dist/community-intel.d.ts.map +1 -0
  16. package/dist/community-intel.js +148 -0
  17. package/dist/community-intel.js.map +1 -0
  18. package/dist/cross-model-engine.d.ts +38 -0
  19. package/dist/cross-model-engine.d.ts.map +1 -0
  20. package/dist/cross-model-engine.js +98 -0
  21. package/dist/cross-model-engine.js.map +1 -0
  22. package/dist/daemon/lifecycle.d.ts +0 -15
  23. package/dist/daemon/lifecycle.d.ts.map +1 -1
  24. package/dist/daemon/lifecycle.js +88 -231
  25. package/dist/daemon/lifecycle.js.map +1 -1
  26. package/dist/daemon/server.d.ts.map +1 -1
  27. package/dist/daemon/server.js +403 -19
  28. package/dist/daemon/server.js.map +1 -1
  29. package/dist/developer-intelligence.d.ts +18 -0
  30. package/dist/developer-intelligence.d.ts.map +1 -0
  31. package/dist/developer-intelligence.js +180 -0
  32. package/dist/developer-intelligence.js.map +1 -0
  33. package/dist/layers.d.ts +92 -0
  34. package/dist/layers.d.ts.map +1 -0
  35. package/dist/layers.js +226 -0
  36. package/dist/layers.js.map +1 -0
  37. package/dist/mcp-server.d.ts +194 -1842
  38. package/dist/mcp-server.d.ts.map +1 -1
  39. package/dist/mcp-server.js +169 -875
  40. package/dist/mcp-server.js.map +1 -1
  41. package/dist/pc-scanner.d.ts +46 -0
  42. package/dist/pc-scanner.d.ts.map +1 -0
  43. package/dist/pc-scanner.js +488 -0
  44. package/dist/pc-scanner.js.map +1 -0
  45. package/dist/predictive-engine.d.ts +19 -0
  46. package/dist/predictive-engine.d.ts.map +1 -0
  47. package/dist/predictive-engine.js +107 -0
  48. package/dist/predictive-engine.js.map +1 -0
  49. package/dist/style-pull.d.ts +1 -1
  50. package/dist/style-pull.js +2 -2
  51. package/dist/team-git.d.ts +47 -0
  52. package/dist/team-git.d.ts.map +1 -0
  53. package/dist/team-git.js +305 -0
  54. package/dist/team-git.js.map +1 -0
  55. package/package.json +1 -1
@@ -0,0 +1,966 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.scanCodebase = scanCodebase;
37
+ exports.getDeveloperProfile = getDeveloperProfile;
38
+ exports.getCodePatterns = getCodePatterns;
39
+ exports.formatScanReport = formatScanReport;
40
+ exports.formatScanReportCompact = formatScanReportCompact;
41
+ exports.ensureCodePatternTable = ensureCodePatternTable;
42
+ const fs = __importStar(require("fs"));
43
+ const path = __importStar(require("path"));
44
+ const ts = __importStar(require("typescript"));
45
+ const database_1 = require("./core/database");
46
+ // ─── Language Detection ──────────────────────────────────────────────────────
47
+ const EXT_MAP = {
48
+ '.ts': 'typescript', '.tsx': 'typescript', '.js': 'javascript',
49
+ '.jsx': 'javascript', '.py': 'python', '.rb': 'ruby',
50
+ '.go': 'go', '.rs': 'rust', '.java': 'java',
51
+ '.cs': 'csharp', '.cpp': 'cpp', '.c': 'c',
52
+ '.php': 'php', '.swift': 'swift', '.kt': 'kotlin',
53
+ '.sql': 'sql', '.sh': 'shell', '.bash': 'shell',
54
+ '.yaml': 'yaml', '.yml': 'yaml', '.json': 'json',
55
+ '.md': 'markdown', '.html': 'html', '.css': 'css',
56
+ '.scss': 'scss', '.vue': 'vue', '.svelte': 'svelte',
57
+ };
58
+ const IGNORE_DIRS = new Set([
59
+ 'node_modules', '.git', 'dist', 'build', '.next', '.nuxt',
60
+ 'coverage', '.cache', '__pycache__', '.pytest_cache',
61
+ 'vendor', '.gradle', 'target', '.tox', 'venv', '.venv',
62
+ 'cell-data', '.fivo',
63
+ ]);
64
+ const MAX_FILE_SIZE = 500 * 1024;
65
+ // ─── Pattern Extraction ──────────────────────────────────────────────────────
66
+ function detectLanguage(filePath) {
67
+ const ext = path.extname(filePath).toLowerCase();
68
+ return EXT_MAP[ext] || 'unknown';
69
+ }
70
+ function countOccurrences(text, regex) {
71
+ return (text.match(regex) || []).length;
72
+ }
73
+ function extractImports(content, lang) {
74
+ const patterns = [];
75
+ const lines = content.split('\n');
76
+ let defaultImports = 0;
77
+ let namedImports = 0;
78
+ let sideEffectImports = 0;
79
+ let dynamicImports = 0;
80
+ for (const line of lines) {
81
+ const trimmed = line.trim();
82
+ if (lang === 'typescript' || lang === 'javascript') {
83
+ if (/^import\s+['"]/.test(trimmed))
84
+ sideEffectImports++;
85
+ else if (/^import\s+\{/.test(trimmed))
86
+ namedImports++;
87
+ else if (/^import\s+\w/.test(trimmed))
88
+ defaultImports++;
89
+ if (/import\s*\(/.test(trimmed))
90
+ dynamicImports++;
91
+ }
92
+ else if (lang === 'python') {
93
+ if (/^from\s+\S+\s+import\s+\*/.test(trimmed))
94
+ patterns.push({ type: 'import_wildcard', category: 'imports', value: 'wildcard', count: 1, trustScore: 80 });
95
+ if (/^import\s+/.test(trimmed) || /^from\s+/.test(trimmed))
96
+ namedImports++;
97
+ }
98
+ else if (lang === 'go') {
99
+ if (/^import\s+"/.test(trimmed))
100
+ defaultImports++;
101
+ if (/^import\s+\(/.test(trimmed))
102
+ namedImports++;
103
+ }
104
+ }
105
+ const total = defaultImports + namedImports + sideEffectImports + dynamicImports;
106
+ if (total > 0) {
107
+ if (namedImports > defaultImports)
108
+ patterns.push({ type: 'import_style', category: 'imports', value: 'named', count: namedImports, trustScore: 90 });
109
+ else
110
+ patterns.push({ type: 'import_style', category: 'imports', value: 'default', count: defaultImports, trustScore: 90 });
111
+ if (dynamicImports > 0)
112
+ patterns.push({ type: 'import_dynamic', category: 'imports', value: 'dynamic', count: dynamicImports, trustScore: 85 });
113
+ }
114
+ return patterns;
115
+ }
116
+ function extractNamingPatterns(content) {
117
+ const patterns = [];
118
+ const camelCase = (content.match(/\b[a-z][a-zA-Z0-9]*[A-Z][a-zA-Z0-9]*/g) || []).length;
119
+ const snakeCase = (content.match(/\b[a-z]+_[a-z]+(_[a-z]+)*/g) || []).length;
120
+ const PascalCase = (content.match(/\b[A-Z][a-zA-Z0-9]+[A-Z][a-zA-Z0-9]*/g) || []).length;
121
+ const SCREAMING = (content.match(/\b[A-Z][A-Z_]+[A-Z]\b/g) || []).length;
122
+ const kebab = (content.match(/\b[a-z]+-[a-z]+(-[a-z]+)*\b/g) || []).length;
123
+ const total = camelCase + snakeCase + PascalCase + SCREAMING + kebab;
124
+ if (total === 0)
125
+ return patterns;
126
+ if (camelCase > total * 0.4)
127
+ patterns.push({ type: 'naming_style', category: 'naming', value: 'camelCase', count: camelCase, trustScore: Math.min(0.95, camelCase / total) });
128
+ if (snakeCase > total * 0.4)
129
+ patterns.push({ type: 'naming_style', category: 'naming', value: 'snake_case', count: snakeCase, trustScore: Math.min(0.95, snakeCase / total) });
130
+ if (PascalCase > total * 0.2)
131
+ patterns.push({ type: 'naming_type', category: 'naming', value: 'PascalCase', count: PascalCase, trustScore: Math.min(0.9, PascalCase / total) });
132
+ if (SCREAMING > 2)
133
+ patterns.push({ type: 'naming_const', category: 'naming', value: 'SCREAMING_SNAKE', count: SCREAMING, trustScore: 85 });
134
+ if (kebab > total * 0.3)
135
+ patterns.push({ type: 'naming_style', category: 'naming', value: 'kebab-case', count: kebab, trustScore: Math.min(0.9, kebab / total) });
136
+ return patterns;
137
+ }
138
+ function extractErrorHandling(content, lang) {
139
+ const patterns = [];
140
+ if (lang === 'typescript' || lang === 'javascript') {
141
+ const tryCatch = countOccurrences(content, /\btry\s*\{/g);
142
+ const promiseCatch = countOccurrences(content, /\.catch\s*\(/g);
143
+ const optionalChain = countOccurrences(content, /\?\./g);
144
+ const nullCheck = countOccurrences(content, /===?\s*null|!==?\s*null/g);
145
+ const errorType = countOccurrences(content, /instanceof\s+Error/g);
146
+ if (tryCatch > 0)
147
+ patterns.push({ type: 'error_trycatch', category: 'error_handling', value: 'try-catch', count: tryCatch, trustScore: 90 });
148
+ if (promiseCatch > 0)
149
+ patterns.push({ type: 'error_promise', category: 'error_handling', value: 'promise-catch', count: promiseCatch, trustScore: 85 });
150
+ if (optionalChain > 0)
151
+ patterns.push({ type: 'error_optional', category: 'error_handling', value: 'optional-chaining', count: optionalChain, trustScore: 90 });
152
+ if (nullCheck > 0)
153
+ patterns.push({ type: 'error_nullcheck', category: 'error_handling', value: 'null-check', count: nullCheck, trustScore: 85 });
154
+ if (errorType > 0)
155
+ patterns.push({ type: 'error_typed', category: 'error_handling', value: 'typed-errors', count: errorType, trustScore: 80 });
156
+ }
157
+ else if (lang === 'python') {
158
+ const tryExcept = countOccurrences(content, /\btry\s*:/g);
159
+ const raise = countOccurrences(content, /\braise\s+/g);
160
+ const typeCheck = countOccurrences(content, /isinstance\s*\(/g);
161
+ if (tryExcept > 0)
162
+ patterns.push({ type: 'error_tryexcept', category: 'error_handling', value: 'try-except', count: tryExcept, trustScore: 90 });
163
+ if (raise > 0)
164
+ patterns.push({ type: 'error_explicit', category: 'error_handling', value: 'explicit-raise', count: raise, trustScore: 85 });
165
+ if (typeCheck > 0)
166
+ patterns.push({ type: 'error_typecheck', category: 'error_handling', value: 'type-checking', count: typeCheck, trustScore: 80 });
167
+ }
168
+ else if (lang === 'go') {
169
+ const ifErr = countOccurrences(content, /if\s+err\s*!=\s*nil/g);
170
+ if (ifErr > 0)
171
+ patterns.push({ type: 'error_iferr', category: 'error_handling', value: 'if-err-nil', count: ifErr, trustScore: 95 });
172
+ }
173
+ return patterns;
174
+ }
175
+ function extractFunctionPatterns(content, lang) {
176
+ const patterns = [];
177
+ if (lang === 'typescript' || lang === 'javascript') {
178
+ const arrowFunctions = countOccurrences(content, /(?:const|let|var)\s+\w+\s*=\s*(?:\([^)]*\)|[a-zA-Z_]\w*)\s*=>/g);
179
+ const functionDecls = countOccurrences(content, /\bfunction\s+\w+/g);
180
+ const asyncFunctions = countOccurrences(content, /\basync\s+(?:function|\()/g);
181
+ const generators = countOccurrences(content, /\bfunction\s*\*/g);
182
+ const methods = countOccurrences(content, /^\s*(?:public|private|protected|static)?\s*(?:async\s+)?[a-zA-Z_]\w*\s*\(/gm);
183
+ const constFunctions = countOccurrences(content, /(?:const|let)\s+\w+\s*=\s*(?:async\s+)?(?:\([^)]*\)|[a-zA-Z_]\w*)\s*=>/g);
184
+ if (arrowFunctions > functionDecls)
185
+ patterns.push({ type: 'function_style', category: 'functions', value: 'arrow', count: arrowFunctions, trustScore: 90 });
186
+ else
187
+ patterns.push({ type: 'function_style', category: 'functions', value: 'declaration', count: functionDecls, trustScore: 85 });
188
+ if (asyncFunctions > 0)
189
+ patterns.push({ type: 'function_async', category: 'functions', value: 'async', count: asyncFunctions, trustScore: 90 });
190
+ if (generators > 0)
191
+ patterns.push({ type: 'function_generator', category: 'functions', value: 'generator', count: generators, trustScore: 80 });
192
+ }
193
+ else if (lang === 'python') {
194
+ const defFunctions = countOccurrences(content, /\bdef\s+\w+/g);
195
+ const asyncDefs = countOccurrences(content, /\basync\s+def\s+/g);
196
+ const lambdas = countOccurrences(content, /\blambda\s+/g);
197
+ const decorators = countOccurrences(content, /@\w+/g);
198
+ if (asyncDefs > 0)
199
+ patterns.push({ type: 'function_async', category: 'functions', value: 'async-def', count: asyncDefs, trustScore: 90 });
200
+ if (decorators > 0)
201
+ patterns.push({ type: 'function_decorator', category: 'functions', value: 'decorated', count: decorators, trustScore: 85 });
202
+ if (lambdas > 0)
203
+ patterns.push({ type: 'function_lambda', category: 'functions', value: 'lambda', count: lambdas, trustScore: 80 });
204
+ }
205
+ return patterns;
206
+ }
207
+ function extractCodeStyle(content) {
208
+ const patterns = [];
209
+ const lines = content.split('\n');
210
+ let singleQuotes = 0;
211
+ let doubleQuotes = 0;
212
+ let backtickStrings = 0;
213
+ let semicolons = 0;
214
+ let noSemicolons = 0;
215
+ let tabs = 0;
216
+ let twoSpaces = 0;
217
+ let fourSpaces = 0;
218
+ let trailingCommas = 0;
219
+ let inlineComments = 0;
220
+ let blockComments = 0;
221
+ let jsdocComments = 0;
222
+ for (const line of lines) {
223
+ if (line.trim().startsWith('//'))
224
+ inlineComments++;
225
+ if (line.trim().startsWith('/*') || line.trim().startsWith('*'))
226
+ blockComments++;
227
+ if (line.trim().startsWith('/**'))
228
+ jsdocComments++;
229
+ const trimmed = line.trimEnd();
230
+ if (trimmed.length > 0 && !trimmed.startsWith('//') && !trimmed.startsWith('*')) {
231
+ const quoteMatches = trimmed.match(/['"]/g) || [];
232
+ for (const q of quoteMatches) {
233
+ if (q === "'" && !trimmed.includes(`\\'`))
234
+ singleQuotes++;
235
+ else if (q === '"')
236
+ doubleQuotes++;
237
+ }
238
+ if (trimmed.includes('`') && (trimmed.includes('${') || trimmed.split('`').length > 2))
239
+ backtickStrings++;
240
+ if (trimmed.endsWith(';'))
241
+ semicolons++;
242
+ else if (trimmed.length > 0 && /[a-zA-Z0-9"'})\]]/.test(trimmed))
243
+ noSemicolons++;
244
+ }
245
+ if (/^\t/.test(line))
246
+ tabs++;
247
+ else if (/^ \S/.test(line))
248
+ twoSpaces++;
249
+ else if (/^ \S/.test(line))
250
+ fourSpaces++;
251
+ if (/,\s*\)/.test(line) || /,\s*\}/.test(line) || /,\s*\]/.test(line))
252
+ trailingCommas++;
253
+ }
254
+ const totalQuotes = singleQuotes + doubleQuotes;
255
+ if (totalQuotes > 0) {
256
+ if (singleQuotes > doubleQuotes * 1.5)
257
+ patterns.push({ type: 'style_quotes', category: 'style', value: 'single', count: singleQuotes, trustScore: 90 });
258
+ else if (doubleQuotes > singleQuotes * 1.5)
259
+ patterns.push({ type: 'style_quotes', category: 'style', value: 'double', count: doubleQuotes, trustScore: 90 });
260
+ else
261
+ patterns.push({ type: 'style_quotes', category: 'style', value: 'mixed', count: totalQuotes, trustScore: 60 });
262
+ }
263
+ if (backtickStrings > 2)
264
+ patterns.push({ type: 'style_template', category: 'style', value: 'template-literals', count: backtickStrings, trustScore: 85 });
265
+ const codeLines = semicolons + noSemicolons;
266
+ if (codeLines > 0) {
267
+ if (semicolons > noSemicolons * 1.5)
268
+ patterns.push({ type: 'style_semicolons', category: 'style', value: 'always', count: semicolons, trustScore: 90 });
269
+ else if (noSemicolons > semicolons * 1.5)
270
+ patterns.push({ type: 'style_semicolons', category: 'style', value: 'never', count: noSemicolons, trustScore: 90 });
271
+ }
272
+ if (tabs > twoSpaces + fourSpaces)
273
+ patterns.push({ type: 'style_indent', category: 'style', value: 'tabs', count: tabs, trustScore: 90 });
274
+ else if (twoSpaces > fourSpaces)
275
+ patterns.push({ type: 'style_indent', category: 'style', value: '2-spaces', count: twoSpaces, trustScore: 85 });
276
+ else if (fourSpaces > twoSpaces)
277
+ patterns.push({ type: 'style_indent', category: 'style', value: '4-spaces', count: fourSpaces, trustScore: 85 });
278
+ if (trailingCommas > 2)
279
+ patterns.push({ type: 'style_trailing_comma', category: 'style', value: 'always', count: trailingCommas, trustScore: 80 });
280
+ const totalComments = inlineComments + blockComments + jsdocComments;
281
+ if (totalComments > 0) {
282
+ if (jsdocComments > inlineComments)
283
+ patterns.push({ type: 'style_docs', category: 'style', value: 'jsdoc', count: jsdocComments, trustScore: 85 });
284
+ else if (inlineComments > blockComments)
285
+ patterns.push({ type: 'style_docs', category: 'style', value: 'inline', count: inlineComments, trustScore: 80 });
286
+ }
287
+ return patterns;
288
+ }
289
+ function extractArchitecturePatterns(content, lang) {
290
+ const patterns = [];
291
+ if (lang === 'typescript' || lang === 'javascript') {
292
+ const classes = countOccurrences(content, /\bclass\s+\w+/g);
293
+ const interfaces = countOccurrences(content, /\binterface\s+\w+/g);
294
+ const types = countOccurrences(content, /\btype\s+\w+/g);
295
+ const enums = countOccurrences(content, /\benum\s+\w+/g);
296
+ const exports = countOccurrences(content, /\bexport\s+(?:default\s+)?(?:const|function|class|interface|type|enum)/g);
297
+ const defaultExports = countOccurrences(content, /\bexport\s+default\s+/g);
298
+ const barrelExports = countOccurrences(content, /export\s+\*\s+from/g);
299
+ const reactComponents = countOccurrences(content, /(?:const|function)\s+[A-Z]\w+\s*(?:=\s*)?(?:\([^)]*\)|\([^)]*\)\s*=>)\s*(?:=>\s*)?(?:\{|<)/g);
300
+ const hooks = countOccurrences(content, /\buse[A-Z]\w+\s*\(/g);
301
+ const useEffect = countOccurrences(content, /\buseEffect\s*\(/g);
302
+ const useState = countOccurrences(content, /\buseState\s*\(/g);
303
+ if (classes > 0 && interfaces > 0)
304
+ patterns.push({ type: 'arch_oo', category: 'architecture', value: 'class+interface', count: classes + interfaces, trustScore: 90 });
305
+ else if (classes > 2)
306
+ patterns.push({ type: 'arch_oo', category: 'architecture', value: 'class-based', count: classes, trustScore: 85 });
307
+ if (interfaces > 0)
308
+ patterns.push({ type: 'arch_types', category: 'architecture', value: 'interfaces', count: interfaces, trustScore: 90 });
309
+ if (types > 0)
310
+ patterns.push({ type: 'arch_types', category: 'architecture', value: 'type-aliases', count: types, trustScore: 85 });
311
+ if (enums > 0)
312
+ patterns.push({ type: 'arch_enum', category: 'architecture', value: 'enum', count: enums, trustScore: 80 });
313
+ if (defaultExports > 0)
314
+ patterns.push({ type: 'arch_export', category: 'architecture', value: 'default-export', count: defaultExports, trustScore: 85 });
315
+ if (barrelExports > 0)
316
+ patterns.push({ type: 'arch_barrel', category: 'architecture', value: 'barrel-export', count: barrelExports, trustScore: 80 });
317
+ if (reactComponents > 2)
318
+ patterns.push({ type: 'arch_react', category: 'architecture', value: 'functional-components', count: reactComponents, trustScore: 90 });
319
+ if (hooks > 0)
320
+ patterns.push({ type: 'arch_hooks', category: 'architecture', value: 'hooks', count: hooks, trustScore: 90 });
321
+ if (useEffect > 0)
322
+ patterns.push({ type: 'arch_effects', category: 'architecture', value: 'useEffect', count: useEffect, trustScore: 85 });
323
+ }
324
+ else if (lang === 'python') {
325
+ const classes = countOccurrences(content, /\bclass\s+\w+/g);
326
+ const dataclasses = countOccurrences(content, /@dataclass/g);
327
+ const typeHints = countOccurrences(content, /:\s*(?:str|int|float|bool|list|dict|tuple|None|Optional|Union|List|Dict|Tuple|Set|Any)\b/g);
328
+ const asyncDefs = countOccurrences(content, /\basync\s+def\s+/g);
329
+ const pydantic = countOccurrences(content, /class\s+\w+\(BaseModel\)/g);
330
+ const fastapi = countOccurrences(content, /@(?:app|router)\.(?:get|post|put|delete|patch)\s*\(/g);
331
+ if (dataclasses > 0)
332
+ patterns.push({ type: 'arch_dataclass', category: 'architecture', value: 'dataclass', count: dataclasses, trustScore: 90 });
333
+ if (pydantic > 0)
334
+ patterns.push({ type: 'arch_pydantic', category: 'architecture', value: 'pydantic', count: pydantic, trustScore: 90 });
335
+ if (fastapi > 0)
336
+ patterns.push({ type: 'arch_fastapi', category: 'architecture', value: 'fastapi', count: fastapi, trustScore: 90 });
337
+ if (typeHints > 3)
338
+ patterns.push({ type: 'arch_typehints', category: 'architecture', value: 'type-hints', count: typeHints, trustScore: 85 });
339
+ }
340
+ else if (lang === 'go') {
341
+ const structs = countOccurrences(content, /\btype\s+\w+\s+struct\b/g);
342
+ const interfaces = countOccurrences(content, /\btype\s+\w+\s+interface\b/g);
343
+ const goroutines = countOccurrences(content, /\bgo\s+\w+/g);
344
+ const channels = countOccurrences(content, /\b(?:make|<-)\s*\(\s*chan\b/g);
345
+ if (structs > 0)
346
+ patterns.push({ type: 'arch_struct', category: 'architecture', value: 'struct', count: structs, trustScore: 90 });
347
+ if (goroutines > 0)
348
+ patterns.push({ type: 'arch_goroutine', category: 'architecture', value: 'goroutine', count: goroutines, trustScore: 90 });
349
+ if (channels > 0)
350
+ patterns.push({ type: 'arch_channel', category: 'architecture', value: 'channel', count: channels, trustScore: 85 });
351
+ }
352
+ return patterns;
353
+ }
354
+ function extractTestPatterns(content, lang) {
355
+ const patterns = [];
356
+ if (lang === 'typescript' || lang === 'javascript') {
357
+ const jestDescribe = countOccurrences(content, /\bdescribe\s*\(/g);
358
+ const jestIt = countOccurrences(content, /\b(?:it|test)\s*\(/g);
359
+ const jestExpect = countOccurrences(content, /\bexpect\s*\(/g);
360
+ const vitest = content.includes('vitest');
361
+ const playwright = content.includes('@playwright');
362
+ const mocha = content.includes('mocha');
363
+ if (jestDescribe > 0 || jestIt > 0)
364
+ patterns.push({ type: 'test_framework', category: 'testing', value: vitest ? 'vitest' : playwright ? 'playwright' : mocha ? 'mocha' : 'jest', count: jestDescribe + jestIt, trustScore: 90 });
365
+ if (jestExpect > 0)
366
+ patterns.push({ type: 'test_assertion', category: 'testing', value: 'expect', count: jestExpect, trustScore: 85 });
367
+ }
368
+ else if (lang === 'python') {
369
+ const pytest = countOccurrences(content, /\bdef\s+test_\w+/g);
370
+ const unittest = countOccurrences(content, /\bself\.assert/g);
371
+ if (pytest > 0)
372
+ patterns.push({ type: 'test_framework', category: 'testing', value: 'pytest', count: pytest, trustScore: 90 });
373
+ if (unittest > 0)
374
+ patterns.push({ type: 'test_framework', category: 'testing', value: 'unittest', count: unittest, trustScore: 85 });
375
+ }
376
+ return patterns;
377
+ }
378
+ // ─── AST-Based Deep Analysis (TypeScript/JavaScript) ──────────────────────
379
+ function extractASTPatterns(content, filePath) {
380
+ const patterns = [];
381
+ try {
382
+ const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
383
+ if (!sourceFile)
384
+ return patterns;
385
+ let asyncNoTryCatch = 0;
386
+ let totalAsync = 0;
387
+ let missingReturnType = 0;
388
+ let functionsWithType = 0;
389
+ let emptyCatch = 0;
390
+ let totalCatch = 0;
391
+ let consoleLogs = 0;
392
+ let rawQueries = 0;
393
+ let anyTypeCount = 0;
394
+ let zodImports = false;
395
+ let nestDepth = 0;
396
+ let maxNestDepth = 0;
397
+ const visit = (node, depth) => {
398
+ nestDepth = Math.max(nestDepth, depth);
399
+ maxNestDepth = Math.max(maxNestDepth, depth);
400
+ // ── Async without try/catch ──────────────────────────────────────
401
+ if (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isArrowFunction(node) || ts.isMethodDeclaration(node)) {
402
+ const modifiers = ts.canHaveModifiers(node) ? ts.getModifiers(node) : undefined;
403
+ const isAsync = modifiers?.some(m => m.kind === ts.SyntaxKind.AsyncKeyword);
404
+ if (isAsync) {
405
+ totalAsync++;
406
+ let hasTryCatch = false;
407
+ const bodyVisitor = (n) => {
408
+ if (ts.isTryStatement(n))
409
+ hasTryCatch = true;
410
+ ts.forEachChild(n, bodyVisitor);
411
+ };
412
+ if (node.body)
413
+ bodyVisitor(node.body);
414
+ if (!hasTryCatch)
415
+ asyncNoTryCatch++;
416
+ // Return type check
417
+ if (ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node)) {
418
+ const hasReturnType = node.type !== undefined;
419
+ if (hasReturnType)
420
+ functionsWithType++;
421
+ else
422
+ missingReturnType++;
423
+ }
424
+ }
425
+ }
426
+ // ── Empty catch blocks ──────────────────────────────────────────
427
+ if (ts.isCatchClause(node)) {
428
+ totalCatch++;
429
+ if (!node.block || node.block.statements.length === 0)
430
+ emptyCatch++;
431
+ }
432
+ // ── Console.log ─────────────────────────────────────────────────
433
+ if (ts.isCallExpression(node)) {
434
+ const expr = node.expression;
435
+ if (ts.isPropertyAccessExpression(expr) && ts.isIdentifier(expr.expression) && expr.expression.text === 'console') {
436
+ consoleLogs++;
437
+ }
438
+ // Raw SQL queries (tagged templates like sql`...`)
439
+ if (ts.isTaggedTemplateExpression(node)) {
440
+ const tte = node;
441
+ if (ts.isIdentifier(tte.tag) && (tte.tag.text === 'sql' || tte.tag.text === 'raw' || tte.tag.text === 'query')) {
442
+ rawQueries++;
443
+ }
444
+ }
445
+ // Zod validation
446
+ if (ts.isIdentifier(expr)) {
447
+ const callName = expr.text;
448
+ if (callName === 'z' || callName.startsWith('z.'))
449
+ zodImports = true;
450
+ }
451
+ if (ts.isPropertyAccessExpression(expr)) {
452
+ if (expr.expression.getText().startsWith('z.'))
453
+ zodImports = true;
454
+ }
455
+ }
456
+ // ── any type usage ──────────────────────────────────────────────
457
+ if (ts.isToken(node) && node.kind === ts.SyntaxKind.AnyKeyword)
458
+ anyTypeCount++;
459
+ ts.forEachChild(node, (child) => visit(child, depth + 1));
460
+ };
461
+ visit(sourceFile, 0);
462
+ // Pattern outputs
463
+ if (totalAsync > 0 && asyncNoTryCatch > 0) {
464
+ patterns.push({
465
+ type: 'blind_spot_async_notry',
466
+ category: 'blind_spots',
467
+ value: `async_without_trycatch:${asyncNoTryCatch}/${totalAsync}`,
468
+ count: asyncNoTryCatch,
469
+ trustScore: 95,
470
+ });
471
+ }
472
+ if (totalCatch > 0 && emptyCatch > 0) {
473
+ patterns.push({
474
+ type: 'blind_spot_empty_catch',
475
+ category: 'blind_spots',
476
+ value: `empty_catch:${emptyCatch}/${totalCatch}`,
477
+ count: emptyCatch,
478
+ trustScore: 90,
479
+ });
480
+ }
481
+ if (functionsWithType + missingReturnType > 0 && missingReturnType > 0) {
482
+ patterns.push({
483
+ type: 'blind_spot_missing_types',
484
+ category: 'blind_spots',
485
+ value: `missing_return_types:${missingReturnType}`,
486
+ count: missingReturnType,
487
+ trustScore: 90,
488
+ });
489
+ }
490
+ if (consoleLogs > 0) {
491
+ patterns.push({
492
+ type: 'blind_spot_console_log',
493
+ category: 'blind_spots',
494
+ value: `console.log:${consoleLogs}`,
495
+ count: consoleLogs,
496
+ trustScore: 85,
497
+ });
498
+ }
499
+ if (anyTypeCount > 0) {
500
+ patterns.push({
501
+ type: 'blind_spot_any_type',
502
+ category: 'blind_spots',
503
+ value: `any_type:${anyTypeCount}`,
504
+ count: anyTypeCount,
505
+ trustScore: 85,
506
+ });
507
+ }
508
+ if (maxNestDepth > 4) {
509
+ patterns.push({
510
+ type: 'blind_spot_deep_nesting',
511
+ category: 'blind_spots',
512
+ value: `max_nesting:${maxNestDepth}`,
513
+ count: maxNestDepth,
514
+ trustScore: 80,
515
+ });
516
+ }
517
+ if (rawQueries > 0) {
518
+ patterns.push({
519
+ type: 'blind_spot_raw_sql',
520
+ category: 'blind_spots',
521
+ value: `raw_sql:${rawQueries}`,
522
+ count: rawQueries,
523
+ trustScore: 85,
524
+ });
525
+ }
526
+ if (zodImports) {
527
+ patterns.push({
528
+ type: 'pattern_validation',
529
+ category: 'security',
530
+ value: 'zod-validation',
531
+ count: 1,
532
+ trustScore: 90,
533
+ });
534
+ }
535
+ }
536
+ catch { /* AST parse may fail on broken files */ }
537
+ return patterns;
538
+ }
539
+ function analyzeFile(filePath, content) {
540
+ const lang = detectLanguage(filePath);
541
+ const lines = content.split('\n');
542
+ const patterns = [
543
+ ...extractImports(content, lang),
544
+ ...extractNamingPatterns(content),
545
+ ...extractErrorHandling(content, lang),
546
+ ...extractFunctionPatterns(content, lang),
547
+ ...extractCodeStyle(content),
548
+ ...extractArchitecturePatterns(content, lang),
549
+ ...extractTestPatterns(content, lang),
550
+ // AST-based deep analysis for TypeScript/JavaScript only
551
+ ...(lang === 'typescript' || lang === 'javascript' ? extractASTPatterns(content, filePath) : []),
552
+ ];
553
+ const functionCount = lang === 'python'
554
+ ? countOccurrences(content, /\bdef\s+\w+/g)
555
+ : countOccurrences(content, /\b(?:function|const|let|var)\s+\w+/g);
556
+ const classCount = countOccurrences(content, /\bclass\s+\w+/g);
557
+ const importCount = countOccurrences(content, /\b(?:import|from|require)\s/g);
558
+ const exportCount = countOccurrences(content, /\b(?:export|module\.exports|def\s|pub\s)/g);
559
+ return {
560
+ filePath,
561
+ language: lang,
562
+ lineCount: lines.length,
563
+ functionCount,
564
+ classCount,
565
+ importCount,
566
+ exportCount,
567
+ patterns,
568
+ };
569
+ }
570
+ // ─── Codebase Scanner ────────────────────────────────────────────────────────
571
+ function walkDir(dir, files = []) {
572
+ if (!fs.existsSync(dir))
573
+ return files;
574
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
575
+ for (const entry of entries) {
576
+ if (IGNORE_DIRS.has(entry.name))
577
+ continue;
578
+ if (entry.name.startsWith('.') && entry.name !== '.env.example')
579
+ continue;
580
+ const fullPath = path.join(dir, entry.name);
581
+ if (entry.isDirectory()) {
582
+ walkDir(fullPath, files);
583
+ }
584
+ else if (entry.isFile()) {
585
+ const ext = path.extname(entry.name).toLowerCase();
586
+ if (EXT_MAP[ext]) {
587
+ try {
588
+ const stat = fs.statSync(fullPath);
589
+ if (stat.size <= MAX_FILE_SIZE)
590
+ files.push(fullPath);
591
+ }
592
+ catch { /* skip */ }
593
+ }
594
+ }
595
+ }
596
+ return files;
597
+ }
598
+ function aggregatePatterns(analyses) {
599
+ const patternMap = new Map();
600
+ for (const analysis of analyses) {
601
+ for (const pattern of analysis.patterns) {
602
+ const key = `${pattern.type}:${pattern.value}`;
603
+ const existing = patternMap.get(key);
604
+ if (existing) {
605
+ existing.count += pattern.count;
606
+ existing.trustScore = Math.min(95, Math.round((existing.trustScore + pattern.trustScore) / 2));
607
+ }
608
+ else {
609
+ patternMap.set(key, { ...pattern });
610
+ }
611
+ }
612
+ }
613
+ return Array.from(patternMap.values()).sort((a, b) => b.count - a.count);
614
+ }
615
+ function buildDeveloperProfile(patterns) {
616
+ const getByType = (type) => patterns.filter(p => p.type === type);
617
+ const getTopValue = (type) => {
618
+ const matches = getByType(type);
619
+ return matches.length > 0 ? matches[0].value : 'unknown';
620
+ };
621
+ const namingPatterns = getByType('naming_style');
622
+ const namingStyle = namingPatterns.length > 0 ? namingPatterns[0].value : 'mixed';
623
+ const errorPatterns = getByType('error_*');
624
+ const errorHandling = errorPatterns.length > 0 ? errorPatterns[0].value : 'none';
625
+ const functionPatterns = getByType('function_style');
626
+ const functionStyle = functionPatterns.length > 0 ? functionPatterns[0].value : 'mixed';
627
+ const asyncPatterns = getByType('function_async');
628
+ const asyncStyle = asyncPatterns.length > 0 ? 'uses-async' : 'sync-only';
629
+ const archPatterns = getByType('arch_*');
630
+ const archStyle = archPatterns.length > 0 ? archPatterns[0].value : 'mixed';
631
+ const testPatterns = getByType('test_framework');
632
+ const testPattern = testPatterns.length > 0 ? testPatterns[0].value : 'none';
633
+ const docPatterns = getByType('style_docs');
634
+ const commentStyle = docPatterns.length > 0 ? docPatterns[0].value : 'none';
635
+ const strengths = [];
636
+ const improvements = [];
637
+ if (errorHandling !== 'none')
638
+ strengths.push('Uses structured error handling');
639
+ else
640
+ improvements.push('Add error handling (try-catch / if-err)');
641
+ if (asyncStyle === 'uses-async')
642
+ strengths.push('Uses async/await patterns');
643
+ if (testPattern !== 'none')
644
+ strengths.push(`Tests with ${testPattern}`);
645
+ else
646
+ improvements.push('Add unit tests');
647
+ const typePatterns = getByType('arch_types');
648
+ if (typePatterns.length > 0)
649
+ strengths.push('Uses type annotations');
650
+ const hookPatterns = getByType('arch_hooks');
651
+ if (hookPatterns.length > 0)
652
+ strengths.push('Uses React hooks');
653
+ if (commentStyle !== 'none')
654
+ strengths.push('Documents code');
655
+ else
656
+ improvements.push('Add code documentation');
657
+ // ─── AST-based blind spot detection ───────────────────────────────────────
658
+ const asyncNoTry = patterns.find(p => p.type === 'blind_spot_async_notry');
659
+ if (asyncNoTry && asyncNoTry.count > 0) {
660
+ improvements.push(`${asyncNoTry.count} async functions missing try/catch`);
661
+ }
662
+ const emptyCatchBlocks = patterns.find(p => p.type === 'blind_spot_empty_catch');
663
+ if (emptyCatchBlocks && emptyCatchBlocks.count > 0) {
664
+ improvements.push(`${emptyCatchBlocks.count} empty catch blocks — errors silently swallowed`);
665
+ }
666
+ const missingTypes = patterns.find(p => p.type === 'blind_spot_missing_types');
667
+ if (missingTypes && missingTypes.count > 0) {
668
+ improvements.push(`${missingTypes.count} functions missing return type annotations`);
669
+ }
670
+ const consoleLogCount = patterns.find(p => p.type === 'blind_spot_console_log');
671
+ if (consoleLogCount && consoleLogCount.count > 0) {
672
+ improvements.push(`${consoleLogCount.count} console.log statements — switch to structured logger`);
673
+ }
674
+ const anyTypeUsage = patterns.find(p => p.type === 'blind_spot_any_type');
675
+ if (anyTypeUsage && anyTypeUsage.count > 0) {
676
+ improvements.push(`${anyTypeUsage.count} 'any' type usages — use specific types`);
677
+ }
678
+ const deepNest = patterns.find(p => p.type === 'blind_spot_deep_nesting');
679
+ if (deepNest && deepNest.count > 4) {
680
+ improvements.push(`Deep nesting detected (max depth: ${deepNest.count}) — refactor to smaller functions`);
681
+ }
682
+ const rawSQL = patterns.find(p => p.type === 'blind_spot_raw_sql');
683
+ if (rawSQL && rawSQL.count > 0) {
684
+ improvements.push(`${rawSQL.count} raw SQL queries — use parameterized queries`);
685
+ }
686
+ const hasZod = patterns.find(p => p.type === 'pattern_validation');
687
+ if (hasZod)
688
+ strengths.push('Uses Zod validation for inputs');
689
+ else
690
+ improvements.push('Add input validation (Zod/Joi/Yup)');
691
+ if (asyncNoTry && asyncNoTry.count === 0 && patterns.find(p => p.type === 'error_trycatch')) {
692
+ strengths.push('All async functions have try/catch error handling');
693
+ }
694
+ const topPatterns = patterns.slice(0, 10).map(p => `${p.category}:${p.value}`);
695
+ return {
696
+ namingStyle,
697
+ importStyle: getTopValue('import_style'),
698
+ exportStyle: getTopValue('arch_export'),
699
+ errorHandling,
700
+ functionStyle,
701
+ asyncStyle,
702
+ commentStyle,
703
+ indentStyle: getTopValue('style_indent'),
704
+ quoteStyle: getTopValue('style_quotes'),
705
+ semicolonStyle: getTopValue('style_semicolons'),
706
+ architectureStyle: archStyle,
707
+ testPattern,
708
+ topPatterns,
709
+ strengths,
710
+ improvements,
711
+ };
712
+ }
713
+ // ─── Database Storage ────────────────────────────────────────────────────────
714
+ function ensureCodePatternTable() {
715
+ const db = (0, database_1.getDb)();
716
+ db.exec(`
717
+ CREATE TABLE IF NOT EXISTS code_patterns (
718
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
719
+ project TEXT NOT NULL,
720
+ file_path TEXT NOT NULL,
721
+ pattern_type TEXT NOT NULL,
722
+ pattern_value TEXT NOT NULL,
723
+ category TEXT NOT NULL,
724
+ count INTEGER DEFAULT 1,
725
+ trust_score INTEGER DEFAULT 50,
726
+ language TEXT,
727
+ created_at TEXT DEFAULT (datetime('now'))
728
+ );
729
+ CREATE INDEX IF NOT EXISTS idx_code_patterns_project ON code_patterns(project);
730
+ CREATE INDEX IF NOT EXISTS idx_code_patterns_type ON code_patterns(pattern_type);
731
+ CREATE INDEX IF NOT EXISTS idx_code_patterns_category ON code_patterns(category);
732
+
733
+ CREATE TABLE IF NOT EXISTS developer_profiles (
734
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
735
+ project TEXT NOT NULL UNIQUE,
736
+ naming_style TEXT,
737
+ import_style TEXT,
738
+ export_style TEXT,
739
+ error_handling TEXT,
740
+ function_style TEXT,
741
+ async_style TEXT,
742
+ comment_style TEXT,
743
+ indent_style TEXT,
744
+ quote_style TEXT,
745
+ semicolon_style TEXT,
746
+ architecture_style TEXT,
747
+ test_pattern TEXT,
748
+ top_patterns TEXT,
749
+ strengths TEXT,
750
+ improvements TEXT,
751
+ files_scanned INTEGER DEFAULT 0,
752
+ total_lines INTEGER DEFAULT 0,
753
+ languages TEXT,
754
+ scanned_at TEXT DEFAULT (datetime('now'))
755
+ );
756
+ CREATE INDEX IF NOT EXISTS idx_dev_profile_project ON developer_profiles(project);
757
+ `);
758
+ }
759
+ function saveToDatabase(project, result) {
760
+ const db = (0, database_1.getDb)();
761
+ ensureCodePatternTable();
762
+ db.prepare('DELETE FROM code_patterns WHERE project = ?').run(project);
763
+ const insertPattern = db.prepare('INSERT INTO code_patterns (project, file_path, pattern_type, pattern_value, category, count, confidence, language) VALUES (?, ?, ?, ?, ?, ?, ?, ?)');
764
+ const insertMany = db.transaction((patterns) => {
765
+ for (const p of patterns) {
766
+ insertPattern.run(project, 'scan', p.type, p.value, p.category, p.count, p.trustScore, '');
767
+ }
768
+ });
769
+ insertMany(result.patterns);
770
+ db.prepare('DELETE FROM developer_profiles WHERE project = ?').run(project);
771
+ db.prepare(`INSERT INTO developer_profiles
772
+ (project, naming_style, import_style, export_style, error_handling, function_style,
773
+ async_style, comment_style, indent_style, quote_style, semicolon_style,
774
+ architecture_style, test_pattern, top_patterns, strengths, improvements,
775
+ files_scanned, total_lines, languages, scanned_at)
776
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))`)
777
+ .run(project, result.profile.namingStyle, result.profile.importStyle, result.profile.exportStyle, result.profile.errorHandling, result.profile.functionStyle, result.profile.asyncStyle, result.profile.commentStyle, result.profile.indentStyle, result.profile.quoteStyle, result.profile.semicolonStyle, result.profile.architectureStyle, result.profile.testPattern, JSON.stringify(result.profile.topPatterns), JSON.stringify(result.profile.strengths), JSON.stringify(result.profile.improvements), result.filesScanned, result.totalLines, JSON.stringify(result.languages));
778
+ db.prepare(`INSERT OR REPLACE INTO scans (project, scanned_at, file_count, line_count, stack, patterns, hubs, issues)
779
+ VALUES (?, datetime('now'), ?, ?, ?, ?, ?, ?)`)
780
+ .run(project, result.filesScanned, result.totalLines, JSON.stringify(result.languages), JSON.stringify(result.patterns.slice(0, 20)), JSON.stringify({}), JSON.stringify(result.profile.improvements));
781
+ }
782
+ // ─── Public API ──────────────────────────────────────────────────────────────
783
+ function scanCodebase(dirPath, projectName) {
784
+ const project = projectName || path.basename(dirPath);
785
+ const files = walkDir(dirPath);
786
+ const analyses = [];
787
+ const languages = {};
788
+ let totalLines = 0;
789
+ for (const file of files) {
790
+ try {
791
+ const content = fs.readFileSync(file, 'utf-8');
792
+ const analysis = analyzeFile(file, content);
793
+ analyses.push(analysis);
794
+ totalLines += analysis.lineCount;
795
+ languages[analysis.language] = (languages[analysis.language] || 0) + 1;
796
+ }
797
+ catch { /* skip unreadable files */ }
798
+ }
799
+ const patterns = aggregatePatterns(analyses);
800
+ const profile = buildDeveloperProfile(patterns);
801
+ const result = {
802
+ project,
803
+ scannedAt: new Date().toISOString(),
804
+ filesScanned: files.length,
805
+ totalLines,
806
+ languages,
807
+ patterns,
808
+ profile,
809
+ };
810
+ saveToDatabase(project, result);
811
+ // ─── CONNECT: Scanner → Behavioral Tracker ───────────────────────────────
812
+ try {
813
+ const { logError, logDecision, logContext } = require('./behavioral-tracker');
814
+ // Log blind spots as errors
815
+ if (profile.improvements.length > 0) {
816
+ for (const imp of profile.improvements) {
817
+ logError({
818
+ project,
819
+ file: 'scan-auto',
820
+ errorType: 'blind_spot',
821
+ errorMessage: imp,
822
+ line: 0,
823
+ });
824
+ }
825
+ }
826
+ // Log architecture decision
827
+ logDecision({
828
+ project,
829
+ file: 'scan-auto',
830
+ decision: `Architecture: ${profile.architectureStyle}`,
831
+ approach: `Scan detected ${files.length} files, ${result.languages ? Object.keys(result.languages).join(', ') : 'unknown'}`,
832
+ worked: true,
833
+ });
834
+ // Log current context
835
+ logContext({
836
+ project,
837
+ task: `Codebase scan completed — ${files.length} files, ${totalLines.toLocaleString()} lines`,
838
+ files: [dirPath],
839
+ });
840
+ }
841
+ catch { /* behavioral tracker may not be initialized yet */ }
842
+ // ─── CONNECT: Git Intelligence (behavior + journey + user analysis) ──────
843
+ try {
844
+ const { buildDeveloperIntelligence } = require('./developer-intelligence');
845
+ const gitIntel = buildDeveloperIntelligence(dirPath);
846
+ if (gitIntel) {
847
+ console.log(`[cell] Git intelligence: ${gitIntel.topBlindSpots.length} blind spots, ${gitIntel.topMistakes.length} repeat mistakes, score: ${gitIntel.behavioralScore}/100`);
848
+ }
849
+ }
850
+ catch { /* git intelligence may not be available */ }
851
+ // ─── CONNECT: Style Pull → Generate .cursorrules, CLAUDE.md, etc. ────
852
+ try {
853
+ const { writeStyleFiles, buildStyleSummary } = require('./style-pull');
854
+ const summary = buildStyleSummary(dirPath);
855
+ const result = writeStyleFiles(dirPath, summary, { force: false });
856
+ if (result.written.length > 0) {
857
+ console.log(`[cell] Generated: ${result.written.join(', ')}`);
858
+ }
859
+ }
860
+ catch { /* style-pull may not be available */ }
861
+ return result;
862
+ }
863
+ function getDeveloperProfile(project) {
864
+ const db = (0, database_1.getDb)();
865
+ ensureCodePatternTable();
866
+ const row = db.prepare('SELECT * FROM developer_profiles WHERE project = ?').get(project);
867
+ if (!row)
868
+ return null;
869
+ return {
870
+ namingStyle: String(row.naming_style || 'unknown'),
871
+ importStyle: String(row.import_style || 'unknown'),
872
+ exportStyle: String(row.export_style || 'unknown'),
873
+ errorHandling: String(row.error_handling || 'unknown'),
874
+ functionStyle: String(row.function_style || 'unknown'),
875
+ asyncStyle: String(row.async_style || 'unknown'),
876
+ commentStyle: String(row.comment_style || 'unknown'),
877
+ indentStyle: String(row.indent_style || 'unknown'),
878
+ quoteStyle: String(row.quote_style || 'unknown'),
879
+ semicolonStyle: String(row.semicolon_style || 'unknown'),
880
+ architectureStyle: String(row.architecture_style || 'unknown'),
881
+ testPattern: String(row.test_pattern || 'unknown'),
882
+ topPatterns: JSON.parse(String(row.top_patterns || '[]')),
883
+ strengths: JSON.parse(String(row.strengths || '[]')),
884
+ improvements: JSON.parse(String(row.improvements || '[]')),
885
+ };
886
+ }
887
+ function getCodePatterns(project, category) {
888
+ const db = (0, database_1.getDb)();
889
+ ensureCodePatternTable();
890
+ let rows;
891
+ if (category) {
892
+ rows = db.prepare('SELECT * FROM code_patterns WHERE project = ? AND category = ? ORDER BY count DESC').all(project, category);
893
+ }
894
+ else {
895
+ rows = db.prepare('SELECT * FROM code_patterns WHERE project = ? ORDER BY count DESC').all(project);
896
+ }
897
+ return rows.map(r => ({
898
+ type: String(r.pattern_type),
899
+ category: String(r.category),
900
+ value: String(r.pattern_value),
901
+ count: Number(r.count),
902
+ trustScore: Number(r.trust_score),
903
+ }));
904
+ }
905
+ function formatScanReport(result) {
906
+ const lines = [];
907
+ lines.push(`Codebase Scan — ${result.project}`);
908
+ lines.push(`Scanned: ${result.scannedAt}`);
909
+ lines.push(`Files: ${result.filesScanned} | Lines: ${result.totalLines.toLocaleString()}`);
910
+ lines.push('');
911
+ const langEntries = Object.entries(result.languages).sort((a, b) => b[1] - a[1]);
912
+ lines.push('Languages:');
913
+ for (const [lang, count] of langEntries) {
914
+ lines.push(` ${lang}: ${count} files`);
915
+ }
916
+ lines.push('');
917
+ lines.push('Developer Profile:');
918
+ lines.push(` Naming: ${result.profile.namingStyle}`);
919
+ lines.push(` Style: ${result.profile.quoteStyle} quotes, ${result.profile.semicolonStyle} semicolons, ${result.profile.indentStyle}`);
920
+ lines.push(` Functions: ${result.profile.functionStyle}, async: ${result.profile.asyncStyle}`);
921
+ lines.push(` Errors: ${result.profile.errorHandling}`);
922
+ lines.push(` Architecture: ${result.profile.architectureStyle}`);
923
+ lines.push(` Testing: ${result.profile.testPattern}`);
924
+ lines.push(` Docs: ${result.profile.commentStyle}`);
925
+ lines.push('');
926
+ if (result.profile.strengths.length > 0) {
927
+ lines.push('Strengths:');
928
+ for (const s of result.profile.strengths)
929
+ lines.push(` + ${s}`);
930
+ }
931
+ if (result.profile.improvements.length > 0) {
932
+ lines.push('Improvements:');
933
+ for (const i of result.profile.improvements)
934
+ lines.push(` ! ${i}`);
935
+ }
936
+ lines.push('');
937
+ const topCategories = new Map();
938
+ for (const p of result.patterns) {
939
+ const existing = topCategories.get(p.category) || [];
940
+ existing.push(p);
941
+ topCategories.set(p.category, existing);
942
+ }
943
+ lines.push('Patterns by Category:');
944
+ for (const [cat, catPatterns] of topCategories) {
945
+ lines.push(` ${cat}:`);
946
+ for (const p of catPatterns.slice(0, 5)) {
947
+ lines.push(` ${p.value} (${p.count}x, ${p.trustScore}%)`);
948
+ }
949
+ }
950
+ return lines.join('\n');
951
+ }
952
+ function formatScanReportCompact(result) {
953
+ const lines = [];
954
+ lines.push(`${result.project}: ${result.filesScanned} files, ${result.totalLines.toLocaleString()} lines`);
955
+ const langEntries = Object.entries(result.languages).sort((a, b) => b[1] - a[1]).slice(0, 5);
956
+ lines.push(`Stack: ${langEntries.map(([l, c]) => `${l}(${c})`).join(', ')}`);
957
+ const p = result.profile;
958
+ lines.push(`Style: ${p.quoteStyle} quotes, ${p.semicolonStyle} semis, ${p.indentStyle}, ${p.functionStyle} functions`);
959
+ lines.push(`Errors: ${p.errorHandling} | Arch: ${p.architectureStyle} | Tests: ${p.testPattern}`);
960
+ if (p.strengths.length > 0)
961
+ lines.push(`Strengths: ${p.strengths.join(', ')}`);
962
+ if (p.improvements.length > 0)
963
+ lines.push(`Improve: ${p.improvements.join(', ')}`);
964
+ return lines.join('\n');
965
+ }
966
+ //# sourceMappingURL=code-scanner.js.map