fivocell 1.0.5 → 2.0.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 (152) hide show
  1. package/README.md +697 -505
  2. package/bin/cell.js +2 -2
  3. package/dist/behavior-intelligence.d.ts +90 -0
  4. package/dist/behavior-intelligence.d.ts.map +1 -0
  5. package/dist/behavior-intelligence.js +595 -0
  6. package/dist/behavior-intelligence.js.map +1 -0
  7. package/dist/cli.d.ts +6 -1
  8. package/dist/cli.d.ts.map +1 -1
  9. package/dist/cli.js +2191 -1
  10. package/dist/cli.js.map +1 -1
  11. package/dist/cloud-sync.d.ts +66 -0
  12. package/dist/cloud-sync.d.ts.map +1 -0
  13. package/dist/cloud-sync.js +328 -0
  14. package/dist/cloud-sync.js.map +1 -0
  15. package/dist/community-intelligence.d.ts +42 -0
  16. package/dist/community-intelligence.d.ts.map +1 -0
  17. package/dist/community-intelligence.js +160 -0
  18. package/dist/community-intelligence.js.map +1 -0
  19. package/dist/community-v2.d.ts +106 -0
  20. package/dist/community-v2.d.ts.map +1 -0
  21. package/dist/community-v2.js +378 -0
  22. package/dist/community-v2.js.map +1 -0
  23. package/dist/core/__tests__/chapter8-hostile.test.js +1 -1
  24. package/dist/core/__tests__/chapter8-hostile.test.js.map +1 -1
  25. package/dist/core/__tests__/chapter9-hostile.test.js +1 -1
  26. package/dist/core/__tests__/chapter9-hostile.test.js.map +1 -1
  27. package/dist/core/__tests__/complexity-analyzer.test.js +15 -15
  28. package/dist/core/__tests__/team-composer.test.js +2 -1
  29. package/dist/core/__tests__/team-composer.test.js.map +1 -1
  30. package/dist/core/convention-detector.d.ts.map +1 -1
  31. package/dist/core/convention-detector.js +11 -23
  32. package/dist/core/convention-detector.js.map +1 -1
  33. package/dist/core/cost-optimizer.d.ts.map +1 -1
  34. package/dist/core/cost-optimizer.js +13 -25
  35. package/dist/core/cost-optimizer.js.map +1 -1
  36. package/dist/core/database.d.ts +28 -0
  37. package/dist/core/database.d.ts.map +1 -0
  38. package/dist/core/database.js +587 -0
  39. package/dist/core/database.js.map +1 -0
  40. package/dist/core/knowledge-graph.d.ts.map +1 -1
  41. package/dist/core/knowledge-graph.js +9 -24
  42. package/dist/core/knowledge-graph.js.map +1 -1
  43. package/dist/core/logger.d.ts +9 -0
  44. package/dist/core/logger.d.ts.map +1 -0
  45. package/dist/core/logger.js +26 -0
  46. package/dist/core/logger.js.map +1 -0
  47. package/dist/core/playbook-generator.js +48 -48
  48. package/dist/core/privacy-manager.d.ts.map +1 -1
  49. package/dist/core/privacy-manager.js +5 -0
  50. package/dist/core/privacy-manager.js.map +1 -1
  51. package/dist/core/project-dna.d.ts.map +1 -1
  52. package/dist/core/project-dna.js +6 -19
  53. package/dist/core/project-dna.js.map +1 -1
  54. package/dist/core/prompt-builder.d.ts +18 -0
  55. package/dist/core/prompt-builder.d.ts.map +1 -0
  56. package/dist/core/prompt-builder.js +325 -0
  57. package/dist/core/prompt-builder.js.map +1 -0
  58. package/dist/core/session-memory.d.ts +90 -0
  59. package/dist/core/session-memory.d.ts.map +1 -0
  60. package/dist/core/session-memory.js +229 -0
  61. package/dist/core/session-memory.js.map +1 -0
  62. package/dist/core/signal-capture.d.ts +5 -0
  63. package/dist/core/signal-capture.d.ts.map +1 -1
  64. package/dist/core/signal-capture.js +67 -0
  65. package/dist/core/signal-capture.js.map +1 -1
  66. package/dist/core/team-composer.d.ts.map +1 -1
  67. package/dist/core/team-composer.js +16 -6
  68. package/dist/core/team-composer.js.map +1 -1
  69. package/dist/cross-model-memory.d.ts +95 -0
  70. package/dist/cross-model-memory.d.ts.map +1 -0
  71. package/dist/cross-model-memory.js +229 -0
  72. package/dist/cross-model-memory.js.map +1 -0
  73. package/dist/daemon/lifecycle.d.ts +18 -0
  74. package/dist/daemon/lifecycle.d.ts.map +1 -1
  75. package/dist/daemon/lifecycle.js +182 -5
  76. package/dist/daemon/lifecycle.js.map +1 -1
  77. package/dist/daemon/server.d.ts.map +1 -1
  78. package/dist/daemon/server.js +269 -6
  79. package/dist/daemon/server.js.map +1 -1
  80. package/dist/first-run.d.ts +8 -0
  81. package/dist/first-run.d.ts.map +1 -0
  82. package/dist/first-run.js +182 -0
  83. package/dist/first-run.js.map +1 -0
  84. package/dist/focus-report.d.ts +32 -0
  85. package/dist/focus-report.d.ts.map +1 -0
  86. package/dist/focus-report.js +293 -0
  87. package/dist/focus-report.js.map +1 -0
  88. package/dist/ide-intelligence.d.ts +118 -0
  89. package/dist/ide-intelligence.d.ts.map +1 -0
  90. package/dist/ide-intelligence.js +284 -0
  91. package/dist/ide-intelligence.js.map +1 -0
  92. package/dist/index.d.ts +22 -0
  93. package/dist/index.d.ts.map +1 -1
  94. package/dist/index.js +119 -2
  95. package/dist/index.js.map +1 -1
  96. package/dist/insight-generator.d.ts +58 -0
  97. package/dist/insight-generator.d.ts.map +1 -0
  98. package/dist/insight-generator.js +314 -0
  99. package/dist/insight-generator.js.map +1 -0
  100. package/dist/journey-memory.d.ts +75 -0
  101. package/dist/journey-memory.d.ts.map +1 -0
  102. package/dist/journey-memory.js +360 -0
  103. package/dist/journey-memory.js.map +1 -0
  104. package/dist/mcp-server.d.ts +2054 -0
  105. package/dist/mcp-server.d.ts.map +1 -0
  106. package/dist/mcp-server.js +1120 -0
  107. package/dist/mcp-server.js.map +1 -0
  108. package/dist/onboarding-scan.d.ts +174 -0
  109. package/dist/onboarding-scan.d.ts.map +1 -0
  110. package/dist/onboarding-scan.js +1039 -0
  111. package/dist/onboarding-scan.js.map +1 -0
  112. package/dist/personal-intelligence.d.ts +97 -0
  113. package/dist/personal-intelligence.d.ts.map +1 -0
  114. package/dist/personal-intelligence.js +408 -0
  115. package/dist/personal-intelligence.js.map +1 -0
  116. package/dist/predictive-intelligence.d.ts +95 -0
  117. package/dist/predictive-intelligence.d.ts.map +1 -0
  118. package/dist/predictive-intelligence.js +544 -0
  119. package/dist/predictive-intelligence.js.map +1 -0
  120. package/dist/production.d.ts +67 -0
  121. package/dist/production.d.ts.map +1 -0
  122. package/dist/production.js +333 -0
  123. package/dist/production.js.map +1 -0
  124. package/dist/senior-features.d.ts +63 -0
  125. package/dist/senior-features.d.ts.map +1 -0
  126. package/dist/senior-features.js +325 -0
  127. package/dist/senior-features.js.map +1 -0
  128. package/dist/style-pull.d.ts +40 -0
  129. package/dist/style-pull.d.ts.map +1 -0
  130. package/dist/style-pull.js +385 -0
  131. package/dist/style-pull.js.map +1 -0
  132. package/dist/team-collaboration.d.ts +116 -0
  133. package/dist/team-collaboration.d.ts.map +1 -0
  134. package/dist/team-collaboration.js +375 -0
  135. package/dist/team-collaboration.js.map +1 -0
  136. package/dist/team-intelligence.d.ts +64 -0
  137. package/dist/team-intelligence.d.ts.map +1 -0
  138. package/dist/team-intelligence.js +289 -0
  139. package/dist/team-intelligence.js.map +1 -0
  140. package/dist/test-watch.d.ts +2 -0
  141. package/dist/test-watch.d.ts.map +1 -0
  142. package/dist/test-watch.js +8 -0
  143. package/dist/test-watch.js.map +1 -0
  144. package/dist/user-intelligence.d.ts +69 -0
  145. package/dist/user-intelligence.d.ts.map +1 -0
  146. package/dist/user-intelligence.js +553 -0
  147. package/dist/user-intelligence.js.map +1 -0
  148. package/dist/work-style.d.ts +49 -0
  149. package/dist/work-style.d.ts.map +1 -0
  150. package/dist/work-style.js +247 -0
  151. package/dist/work-style.js.map +1 -0
  152. package/package.json +3 -2
@@ -0,0 +1,1039 @@
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.discoverFiles = discoverFiles;
37
+ exports.detectStack = detectStack;
38
+ exports.extractPatterns = extractPatterns;
39
+ exports.buildHubMap = buildHubMap;
40
+ exports.detectIssues = detectIssues;
41
+ exports.scanCodebase = scanCodebase;
42
+ exports.saveOnboarding = saveOnboarding;
43
+ exports.loadOnboarding = loadOnboarding;
44
+ const fs = __importStar(require("fs"));
45
+ const path = __importStar(require("path"));
46
+ const os = __importStar(require("os"));
47
+ const SUPPORTED_EXTS = new Set([
48
+ '.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs',
49
+ '.py', '.go', '.html', '.htm',
50
+ ]);
51
+ const SKIP_DIRS = new Set([
52
+ 'node_modules', '.git', 'dist', 'build', 'coverage',
53
+ '.fivo', '.next', '.cache', 'vendor', 'target',
54
+ '__pycache__', '.venv', 'venv', 'env', '.env',
55
+ '.idea', '.vscode', '.pytest_cache', 'out',
56
+ 'sdks', 'sdk', 'examples', 'example', 'samples', 'sample',
57
+ 'demo', 'demos', 'tutorial', 'tutorials', 'docs', 'doc',
58
+ 'fixtures', 'mocks', 'mocks-examples',
59
+ ]);
60
+ const SKIP_PATH_PATTERNS = [
61
+ /[\\/]node_modules[\\/]/,
62
+ /[\\/]\.git[\\/]/,
63
+ /[\\/](dist|build|out|coverage|target|vendor|__pycache__|\.next|\.cache)[\\/]/,
64
+ /[\\/](sdks?|examples?|samples?|demos?|tutorials?|docs?|fixtures?|mocks?)[\\/]/i,
65
+ ];
66
+ const MAX_FILE_LINES = 5000;
67
+ const MAX_FILES = 2000;
68
+ function discoverFiles(rootDir, opts = {}) {
69
+ const max = opts.maxFiles || MAX_FILES;
70
+ const found = [];
71
+ function walk(dir) {
72
+ if (found.length >= max)
73
+ return;
74
+ let entries;
75
+ try {
76
+ entries = fs.readdirSync(dir, { withFileTypes: true });
77
+ }
78
+ catch {
79
+ return;
80
+ }
81
+ for (const e of entries) {
82
+ if (found.length >= max)
83
+ return;
84
+ if (e.name.startsWith('.') && e.name !== '.eslintrc' && e.name !== '.prettierrc') {
85
+ if (e.isDirectory() && SKIP_DIRS.has(e.name))
86
+ continue;
87
+ }
88
+ if (e.isDirectory()) {
89
+ if (SKIP_DIRS.has(e.name))
90
+ continue;
91
+ walk(path.join(dir, e.name));
92
+ }
93
+ else if (e.isFile()) {
94
+ const fullPath = path.join(dir, e.name);
95
+ const normalized = fullPath.replace(/\\/g, '/');
96
+ if (SKIP_PATH_PATTERNS.some(p => p.test(normalized)))
97
+ continue;
98
+ const ext = path.extname(e.name).toLowerCase();
99
+ if (SUPPORTED_EXTS.has(ext)) {
100
+ try {
101
+ const st = fs.statSync(path.join(dir, e.name));
102
+ found.push({ path: path.join(dir, e.name), ext, size: st.size });
103
+ }
104
+ catch { }
105
+ }
106
+ }
107
+ }
108
+ }
109
+ walk(rootDir);
110
+ return found;
111
+ }
112
+ function detectStack(rootDir) {
113
+ const stack = [];
114
+ const pkgPath = path.join(rootDir, 'package.json');
115
+ if (fs.existsSync(pkgPath)) {
116
+ try {
117
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
118
+ const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
119
+ const known = {
120
+ typescript: { name: 'TypeScript', language: 'typescript' },
121
+ react: { name: 'React', language: 'typescript' },
122
+ vue: { name: 'Vue', language: 'typescript' },
123
+ next: { name: 'Next.js', language: 'typescript' },
124
+ express: { name: 'Express', language: 'typescript' },
125
+ fastify: { name: 'Fastify', language: 'typescript' },
126
+ nestjs: { name: 'NestJS', language: 'typescript' },
127
+ '@nestjs/core': { name: 'NestJS', language: 'typescript' },
128
+ vitest: { name: 'Vitest', language: 'typescript' },
129
+ jest: { name: 'Jest', language: 'typescript' },
130
+ mocha: { name: 'Mocha', language: 'typescript' },
131
+ webpack: { name: 'Webpack', language: 'typescript' },
132
+ vite: { name: 'Vite', language: 'typescript' },
133
+ tailwindcss: { name: 'Tailwind CSS', language: 'typescript' },
134
+ prisma: { name: 'Prisma', language: 'typescript' },
135
+ typeorm: { name: 'TypeORM', language: 'typescript' },
136
+ sequelize: { name: 'Sequelize', language: 'typescript' },
137
+ mongoose: { name: 'Mongoose', language: 'typescript' },
138
+ socketio: { name: 'Socket.IO', language: 'typescript' },
139
+ 'socket.io': { name: 'Socket.IO', language: 'typescript' },
140
+ axios: { name: 'Axios', language: 'typescript' },
141
+ zod: { name: 'Zod', language: 'typescript' },
142
+ yup: { name: 'Yup', language: 'typescript' },
143
+ redux: { name: 'Redux', language: 'typescript' },
144
+ mobx: { name: 'MobX', language: 'typescript' },
145
+ rxjs: { name: 'RxJS', language: 'typescript' },
146
+ };
147
+ for (const [dep, version] of Object.entries(deps)) {
148
+ const k = known[dep];
149
+ if (k) {
150
+ const v = String(version).replace(/^[~^]/, '');
151
+ stack.push({ name: k.name, version: v, confidence: 1.0, language: k.language });
152
+ }
153
+ }
154
+ if (pkg.engines?.node) {
155
+ stack.push({ name: 'Node', version: String(pkg.engines.node).replace(/^[~^>=< ]+/, ''), confidence: 1.0, language: 'typescript' });
156
+ }
157
+ }
158
+ catch { }
159
+ }
160
+ const goMod = path.join(rootDir, 'go.mod');
161
+ if (fs.existsSync(goMod)) {
162
+ try {
163
+ const content = fs.readFileSync(goMod, 'utf-8');
164
+ const verMatch = content.match(/^go (\d+\.\d+)/m);
165
+ if (verMatch)
166
+ stack.push({ name: 'Go', version: verMatch[1], confidence: 1.0, language: 'go' });
167
+ }
168
+ catch { }
169
+ }
170
+ const reqTxt = path.join(rootDir, 'requirements.txt');
171
+ const pyproject = path.join(rootDir, 'pyproject.toml');
172
+ if (fs.existsSync(reqTxt) || fs.existsSync(pyproject)) {
173
+ stack.push({ name: 'Python', version: '3.x', confidence: 1.0, language: 'python' });
174
+ try {
175
+ if (fs.existsSync(reqTxt)) {
176
+ const reqs = fs.readFileSync(reqTxt, 'utf-8').toLowerCase();
177
+ if (reqs.includes('django'))
178
+ stack.push({ name: 'Django', version: '', confidence: 1.0, language: 'python' });
179
+ if (reqs.includes('flask'))
180
+ stack.push({ name: 'Flask', version: '', confidence: 1.0, language: 'python' });
181
+ if (reqs.includes('fastapi'))
182
+ stack.push({ name: 'FastAPI', version: '', confidence: 1.0, language: 'python' });
183
+ if (reqs.includes('pytest'))
184
+ stack.push({ name: 'pytest', version: '', confidence: 1.0, language: 'python' });
185
+ if (reqs.includes('numpy'))
186
+ stack.push({ name: 'NumPy', version: '', confidence: 1.0, language: 'python' });
187
+ if (reqs.includes('pandas'))
188
+ stack.push({ name: 'Pandas', version: '', confidence: 1.0, language: 'python' });
189
+ if (reqs.includes('sqlalchemy'))
190
+ stack.push({ name: 'SQLAlchemy', version: '', confidence: 1.0, language: 'python' });
191
+ if (reqs.includes('requests'))
192
+ stack.push({ name: 'Requests', version: '', confidence: 1.0, language: 'python' });
193
+ }
194
+ }
195
+ catch { }
196
+ }
197
+ return stack;
198
+ }
199
+ function emptyStats() {
200
+ return {
201
+ arrowFunctions: 0, functionDecls: 0, constDecls: 0, letDecls: 0,
202
+ singleQuotes: 0, doubleQuotes: 0, semicolons: 0, noSemicolons: 0,
203
+ asyncAwait: 0, thenChains: 0, tryCatch: 0, catchIgnored: 0,
204
+ destructureImports: 0, namespaceImports: 0, typeOnlyImports: 0, typeAny: 0,
205
+ typeUnknown: 0, typeNever: 0, typeVoid: 0, optionalChaining: 0, nullishCoalescing: 0,
206
+ templateLiterals: 0, templateStringsPy: 0, interfaces: 0, typeAliases: 0, generics: 0,
207
+ enums: 0, classes: 0, abstractClasses: 0, testFiles: 0, mockUsage: 0, describeBlocks: 0,
208
+ itBlocks: 0, customErrors: 0, consoleLog: 0, structuredLog: 0, jsdocComments: 0,
209
+ esmImports: 0, cjsRequires: 0, totalImports: 0, todoComments: 0, fixmeComments: 0,
210
+ xxxComments: 0, defaultExports: 0, namedExports: 0, promiseAll: 0, promiseRace: 0,
211
+ earlyReturns: 0, guardClauses: 0, shortFunctions: 0, longFunctions: 0,
212
+ restParams: 0, defaultParams: 0, destructuredParams: 0, readonlyModifier: 0,
213
+ privateModifier: 0, staticModifier: 0, barrelExports: 0, indexFiles: 0,
214
+ zodUsage: 0, yupUsage: 0, joiUsage: 0, expressMiddleware: 0, expressRoutes: 0,
215
+ processEnv: 0, envValidation: 0, parameterizedQueries: 0, regexPatterns: 0,
216
+ switchStatements: 0, forLoops: 0, forOfLoops: 0, whileLoops: 0, reduceUsage: 0,
217
+ mapUsage: 0, filterUsage: 0, spreadOperator: 0, indexedAccess: 0, lengthChecks: 0,
218
+ magicNumbers: 0, licenseHeaders: 0, deferUsage: 0, goroutineUsage: 0, channelUsage: 0,
219
+ semanticTags: 0, ariaAttributes: 0, altAttributes: 0, inlineStyles: 0, inlineScripts: 0,
220
+ };
221
+ }
222
+ function scanFileContent(content, ext) {
223
+ const s = emptyStats();
224
+ const lines = content.split('\n');
225
+ if (ext === '.ts' || ext === '.tsx' || ext === '.js' || ext === '.jsx' || ext === '.mjs' || ext === '.cjs') {
226
+ s.arrowFunctions = (content.match(/=>/g) || []).length;
227
+ s.functionDecls = (content.match(/function\s+\w+/g) || []).length;
228
+ s.constDecls = (content.match(/^\s*const\s+/gm) || []).length;
229
+ s.letDecls = (content.match(/^\s*let\s+/gm) || []).length;
230
+ s.singleQuotes = (content.match(/'/g) || []).length;
231
+ s.doubleQuotes = (content.match(/"/g) || []).length;
232
+ s.semicolons = lines.filter(l => l.trim().endsWith(';') && !l.trim().startsWith('//')).length;
233
+ s.noSemicolons = lines.filter(l => l.trim().length > 0 && !l.trim().endsWith(';') && !l.trim().endsWith('{') && !l.trim().endsWith('}') && !l.trim().startsWith('//') && !l.trim().startsWith('*') && !l.trim().endsWith(',') && !l.trim().endsWith('(') && !l.trim().endsWith(')')).length;
234
+ s.asyncAwait = (content.match(/await\s+/g) || []).length;
235
+ s.thenChains = (content.match(/\.then\(/g) || []).length;
236
+ s.tryCatch = (content.match(/try\s*\{/g) || []).length;
237
+ s.catchIgnored = (content.match(/catch\s*\([^)]*\)\s*\{\s*\}/g) || []).length;
238
+ s.destructureImports = (content.match(/import\s*\{/g) || []).length;
239
+ s.namespaceImports = (content.match(/import\s*\*\s+as/g) || []).length;
240
+ s.typeOnlyImports = (content.match(/import\s+type\s+/g) || []).length;
241
+ s.typeAny = (content.match(/:\s*any[\s,;)\]>]/g) || []).length;
242
+ s.typeUnknown = (content.match(/:\s*unknown[\s,;)\]>]/g) || []).length;
243
+ s.typeNever = (content.match(/:\s*never[\s,;)\]>]/g) || []).length;
244
+ s.typeVoid = (content.match(/:\s*void[\s,;)\]>]/g) || []).length;
245
+ s.optionalChaining = (content.match(/\?\./g) || []).length;
246
+ s.nullishCoalescing = (content.match(/\?\?/g) || []).length;
247
+ s.templateLiterals = (content.match(/`/g) || []).length / 2;
248
+ s.interfaces = (content.match(/^\s*(export\s+)?interface\s+\w+/gm) || []).length;
249
+ s.typeAliases = (content.match(/^\s*(export\s+)?type\s+\w+\s*=/gm) || []).length;
250
+ s.generics = (content.match(/<[A-Z]\w*>/g) || []).length;
251
+ s.enums = (content.match(/^\s*(export\s+)?enum\s+\w+/gm) || []).length;
252
+ s.classes = (content.match(/^\s*(export\s+)?class\s+\w+/gm) || []).length;
253
+ s.abstractClasses = (content.match(/^\s*(export\s+)?abstract\s+class\s+\w+/gm) || []).length;
254
+ s.testFiles = /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(content) ? 1 : 0;
255
+ s.mockUsage = (content.match(/vi\.mock\(|jest\.mock\(/g) || []).length;
256
+ s.describeBlocks = (content.match(/describe\s*\(/g) || []).length;
257
+ s.itBlocks = (content.match(/(it|test)\s*\(/g) || []).length;
258
+ s.customErrors = (content.match(/class\s+\w+Error\s+extends\s+(Error|BaseError)/g) || []).length;
259
+ s.consoleLog = (content.match(/console\.(log|debug|info)/g) || []).length;
260
+ s.structuredLog = (content.match(/logger\.(info|warn|error|debug)\(/g) || []).length;
261
+ s.jsdocComments = (content.match(/\/\*\*[\s\S]*?\*\//g) || []).length;
262
+ s.esmImports = (content.match(/^import\s+/gm) || []).length;
263
+ s.cjsRequires = (content.match(/require\(['"]/g) || []).length;
264
+ s.totalImports = s.esmImports + s.cjsRequires;
265
+ s.todoComments = (content.match(/\b(TODO|FIXME|XXX|HACK)\b/gi) || []).length;
266
+ s.fixmeComments = (content.match(/\b(FIXME|XXX)\b/g) || []).length;
267
+ s.xxxComments = (content.match(/\bXXX\b/g) || []).length;
268
+ s.defaultExports = (content.match(/export\s+default\s+/g) || []).length;
269
+ s.namedExports = (content.match(/^export\s+(?:const|let|var|function|class|interface|type|enum)\s+/gm) || []).length;
270
+ s.promiseAll = (content.match(/Promise\.all\(/g) || []).length;
271
+ s.promiseRace = (content.match(/Promise\.race\(/g) || []).length;
272
+ s.earlyReturns = (content.match(/^\s*(if\s*\([^)]+\)\s*\{\s*return[^}]*\}|return\s+null;|return\s+undefined;)/gm) || []).length;
273
+ s.guardClauses = (content.match(/^\s*if\s*\(![^)]+\)\s*(return|throw)/gm) || []).length;
274
+ s.restParams = (content.match(/\.\.\.\w+\s*:\s*\w+\[\]/g) || []).length;
275
+ s.defaultParams = (content.match(/\w+\s*:\s*\w+\s*=\s*[^,)]+/g) || []).length;
276
+ s.destructuredParams = (content.match(/\(\s*\{\s*\w+(:\s*\w+)?(\s*,\s*\w+(:\s*\w+)?)*\s*\}\s*[:)]/g) || []).length;
277
+ s.readonlyModifier = (content.match(/\breadonly\s+/g) || []).length;
278
+ s.privateModifier = (content.match(/\bprivate\s+/g) || []).length;
279
+ s.staticModifier = (content.match(/\bstatic\s+/g) || []).length;
280
+ s.zodUsage = (content.match(/from\s+['"]zod['"]/g) || []).length;
281
+ s.yupUsage = (content.match(/from\s+['"]yup['"]/g) || []).length;
282
+ s.joiUsage = (content.match(/from\s+['"]joi['"]/g) || []).length;
283
+ s.expressMiddleware = (content.match(/\.(use|get|post|put|delete|patch)\s*\(\s*['"`]/g) || []).length;
284
+ s.expressRoutes = (content.match(/\.(get|post|put|delete|patch)\s*\(\s*['"`][^'"`]+['"`]/g) || []).length;
285
+ s.processEnv = (content.match(/process\.env\.\w+/g) || []).length;
286
+ s.envValidation = (content.match(/z\.string\(\)\.parse\(process\.env|\.parse\(process\.env\)/g) || []).length;
287
+ s.parameterizedQueries = (content.match(/\$\{|\?\s*,\s*\[|sql\.query.*\?/g) || []).length;
288
+ s.regexPatterns = (content.match(/\/[^/\n]+\/[gimsuy]*/g) || []).length;
289
+ s.switchStatements = (content.match(/^\s*switch\s*\(/gm) || []).length;
290
+ s.forLoops = (content.match(/^\s*for\s*\(\s*(let|var)\s+\w+\s*=/gm) || []).length;
291
+ s.forOfLoops = (content.match(/^\s*for\s*\(\s*(const|let)\s+\w+\s+of\s+/gm) || []).length;
292
+ s.whileLoops = (content.match(/^\s*while\s*\(/gm) || []).length;
293
+ s.reduceUsage = (content.match(/\.reduce\(/g) || []).length;
294
+ s.mapUsage = (content.match(/\.map\(/g) || []).length;
295
+ s.filterUsage = (content.match(/\.filter\(/g) || []).length;
296
+ s.spreadOperator = (content.match(/\.\.\.[\w${]/g) || []).length;
297
+ s.indexedAccess = (content.match(/\[\d+\]/g) || []).length;
298
+ s.lengthChecks = (content.match(/\.length\s*[><=!]/g) || []).length;
299
+ s.licenseHeaders = (content.match(/Copyright|License|@license|Apache License|MIT License/im) || []).length > 0 ? 1 : 0;
300
+ s.barrelExports = (content.match(/^export\s*\{\s*\n/gm) || []).length;
301
+ }
302
+ else if (ext === '.py') {
303
+ s.functionDecls = (content.match(/^\s*def\s+\w+/gm) || []).length;
304
+ s.asyncAwait = (content.match(/^\s*async\s+def\s+/gm) || []).length;
305
+ s.classes = (content.match(/^\s*class\s+\w+/gm) || []).length;
306
+ s.typeAliases = (content.match(/^\s*from\s+typing\s+import/gm) || []).length;
307
+ s.interfaces = (content.match(/^\s*from\s+typing\s+import.*Protocol/gm) || []).length;
308
+ s.generics = (content.match(/^[^#]*def\s+\w+\[.*?\]/gm) || []).length;
309
+ s.testFiles = content.includes('pytest') || content.includes('def test_') || /test_\w+\(/.test(content) ? 1 : 0;
310
+ s.mockUsage = (content.match(/mock\.(Mock|MagicMock|patch)/g) || []).length;
311
+ s.describeBlocks = (content.match(/^\s*class\s+Test\w+/gm) || []).length;
312
+ s.itBlocks = (content.match(/^\s*def\s+test_\w+/gm) || []).length;
313
+ s.customErrors = (content.match(/class\s+\w+(Error|Exception)\b/g) || []).length;
314
+ s.consoleLog = (content.match(/print\(/g) || []).length;
315
+ s.structuredLog = (content.match(/logger\.(info|warn|error|debug)\(/g) || []).length;
316
+ s.jsdocComments = (content.match(/""".*?"""/gs) || []).length;
317
+ s.templateStringsPy = (content.match(/f['"]/g) || []).length;
318
+ s.tryCatch = (content.match(/^\s*try:/gm) || []).length;
319
+ s.catchIgnored = (content.match(/except[^:]*:\s*pass/g) || []).length;
320
+ s.destructureImports = (content.match(/from\s+\w+\s+import\s+\(/g) || []).length;
321
+ s.typeOnlyImports = (content.match(/from\s+typing\s+import/g) || []).length;
322
+ s.optionalChaining = (content.match(/getattr\(|\.get\(/g) || []).length;
323
+ s.totalImports = (content.match(/^\s*(import|from)\s+/gm) || []).length;
324
+ s.todoComments = (content.match(/#.*\b(TODO|FIXME|XXX|HACK)\b/gi) || []).length;
325
+ s.fixmeComments = (content.match(/#.*\bFIXME\b/gi) || []).length;
326
+ s.guardClauses = (content.match(/^\s*if\s+not\s+\w+.*:\s*\n\s*return/gm) || []).length;
327
+ s.defaultParams = (content.match(/def\s+\w+\([^)]*=\s*[^,)]+/g) || []).length;
328
+ s.zodUsage = (content.match(/from\s+pydantic\s+import|from\s+marshmallow\s+import/g) || []).length;
329
+ s.processEnv = (content.match(/os\.environ\[|os\.getenv\(/g) || []).length;
330
+ s.envValidation = (content.match(/BaseSettings|pydantic_settings|os\.getenv\(.*required/g) || []).length;
331
+ s.parameterizedQueries = (content.match(/cursor\.execute\([^,]+,\s*\(/g) || []).length;
332
+ s.regexPatterns = (content.match(/r['"][^'"]+['"]/g) || []).length;
333
+ s.forLoops = (content.match(/^\s*for\s+\w+\s+in\s+/gm) || []).length;
334
+ s.mapUsage = (content.match(/\.map\(|\blist\(map\(/g) || []).length;
335
+ s.filterUsage = (content.match(/\.filter\(|\blist\(filter\(/g) || []).length;
336
+ s.reduceUsage = (content.match(/from\s+functools\s+import\s+reduce|functools\.reduce/g) || []).length;
337
+ s.spreadOperator = (content.match(/\*\*?\w+/g) || []).length;
338
+ s.lengthChecks = (content.match(/len\([^)]+\)\s*[><=!]/g) || []).length;
339
+ s.licenseHeaders = (content.match(/Copyright|License|Apache License|MIT License/im) || []).length > 0 ? 1 : 0;
340
+ }
341
+ else if (ext === '.go') {
342
+ s.functionDecls = (content.match(/^\s*func\s+\w+/gm) || []).length;
343
+ s.classes = (content.match(/^\s*type\s+\w+\s+struct/gm) || []).length;
344
+ s.interfaces = (content.match(/^\s*type\s+\w+\s+interface/gm) || []).length;
345
+ s.enums = (content.match(/^\s*const\s*\(\s*$/gm) || []).length;
346
+ s.testFiles = content.includes('testing.T') || content.includes('func Test') ? 1 : 0;
347
+ s.itBlocks = (content.match(/func\s+Test\w+\(/g) || []).length;
348
+ s.customErrors = (content.match(/type\s+\w+Error\s+struct/g) || []).length;
349
+ s.tryCatch = (content.match(/if\s+err\s*!=\s*nil\s*\{/g) || []).length;
350
+ s.catchIgnored = (content.match(/_\s*=\s*\w+/g) || []).length;
351
+ s.jsdocComments = (content.match(/^\s*\/\/.*$/gm) || []).length;
352
+ s.totalImports = (content.match(/^\s*import\s*\(/gm) || []).length;
353
+ s.todoComments = (content.match(/\/\/.*\b(TODO|FIXME|XXX|HACK)\b/gi) || []).length;
354
+ s.fixmeComments = (content.match(/\/\/.*\bFIXME\b/gi) || []).length;
355
+ s.guardClauses = (content.match(/if\s+\w+\s*==\s*nil\s*\{[\s\S]*?return/g) || []).length;
356
+ s.deferUsage = (content.match(/\bdefer\s+/g) || []).length;
357
+ s.goroutineUsage = (content.match(/\bgo\s+\w+\(/g) || []).length;
358
+ s.channelUsage = (content.match(/\bchan\s+\w+/g) || []).length;
359
+ s.processEnv = (content.match(/os\.Getenv\(/g) || []).length;
360
+ s.regexPatterns = (content.match(/regexp\.MustCompile/g) || []).length;
361
+ s.forLoops = (content.match(/^\s*for\s+\w+\s*:=\s*range\s+/gm) || []).length;
362
+ s.lengthChecks = (content.match(/len\([^)]+\)\s*[><=!]/g) || []).length;
363
+ s.licenseHeaders = (content.match(/Copyright|License|Apache License|MIT License/im) || []).length > 0 ? 1 : 0;
364
+ }
365
+ else if (ext === '.html' || ext === '.htm') {
366
+ s.jsdocComments = (content.match(/<!--[\s\S]*?-->/g) || []).length;
367
+ s.semanticTags = (content.match(/<(header|footer|nav|main|article|section|aside)\b/gi) || []).length;
368
+ s.ariaAttributes = (content.match(/\baria-\w+=/g) || []).length;
369
+ s.altAttributes = (content.match(/<img[^>]+alt\s*=/gi) || []).length;
370
+ s.inlineStyles = (content.match(/style\s*=/gi) || []).length;
371
+ s.inlineScripts = (content.match(/<script(?!\s+src=)/gi) || []).length;
372
+ }
373
+ return s;
374
+ }
375
+ function aggregateStats(stats) {
376
+ const out = emptyStats();
377
+ for (const s of stats) {
378
+ for (const k of Object.keys(s)) {
379
+ out[k] += s[k];
380
+ }
381
+ }
382
+ return out;
383
+ }
384
+ function makePattern(category, description, occurrences, total, examples, language) {
385
+ if (total === 0 || occurrences === 0)
386
+ return null;
387
+ const confidence = Math.min(1, occurrences / Math.max(1, total));
388
+ if (confidence < 0.3)
389
+ return null;
390
+ return { category, description, confidence, evidence: occurrences, total, examples, language };
391
+ }
392
+ function extractPatterns(stats, perFile) {
393
+ const patterns = [];
394
+ const jsFiles = perFile.filter(f => ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'].includes(f.ext));
395
+ const pyFiles = perFile.filter(f => f.ext === '.py');
396
+ const goFiles = perFile.filter(f => f.ext === '.go');
397
+ const tsTotal = jsFiles.length;
398
+ if (tsTotal > 0) {
399
+ const arrowVsFunc = stats.arrowFunctions;
400
+ const funcDecl = stats.functionDecls;
401
+ if (arrowVsFunc > 0 && arrowVsFunc >= funcDecl) {
402
+ const examples = jsFiles.filter(f => f.stats.arrowFunctions > 0).slice(0, 3).map(f => path.basename(f.file));
403
+ const p = makePattern('style', 'Prefers arrow functions over function declarations', arrowVsFunc, arrowVsFunc + funcDecl, examples);
404
+ if (p)
405
+ patterns.push(p);
406
+ }
407
+ if (stats.constDecls + stats.letDecls > 0) {
408
+ const examples = jsFiles.filter(f => f.stats.constDecls > f.stats.letDecls).slice(0, 3).map(f => path.basename(f.file));
409
+ const p = makePattern('style', 'Uses const over let for variable declarations', stats.constDecls, stats.constDecls + stats.letDecls, examples);
410
+ if (p)
411
+ patterns.push(p);
412
+ }
413
+ if (stats.singleQuotes + stats.doubleQuotes > 50) {
414
+ const examples = jsFiles.filter(f => f.stats.singleQuotes > f.stats.doubleQuotes).slice(0, 3).map(f => path.basename(f.file));
415
+ const p = makePattern('style', 'Prefers single quotes over double quotes', stats.singleQuotes, stats.singleQuotes + stats.doubleQuotes, examples);
416
+ if (p)
417
+ patterns.push(p);
418
+ }
419
+ if (stats.semicolons + stats.noSemicolons > 50) {
420
+ const examples = jsFiles.filter(f => f.stats.semicolons > f.stats.noSemicolons).slice(0, 3).map(f => path.basename(f.file));
421
+ const p = makePattern('style', 'Uses semicolons to terminate statements', stats.semicolons, stats.semicolons + stats.noSemicolons, examples);
422
+ if (p)
423
+ patterns.push(p);
424
+ }
425
+ else if (stats.noSemicolons > stats.semicolons * 3) {
426
+ const examples = jsFiles.filter(f => f.stats.noSemicolons > f.stats.semicolons).slice(0, 3).map(f => path.basename(f.file));
427
+ const p = makePattern('style', 'Omits semicolons (Prettier-style)', stats.noSemicolons, stats.semicolons + stats.noSemicolons, examples);
428
+ if (p)
429
+ patterns.push(p);
430
+ }
431
+ if (stats.asyncAwait > 0) {
432
+ const examples = jsFiles.filter(f => f.stats.asyncAwait > 0).slice(0, 3).map(f => path.basename(f.file));
433
+ const p = makePattern('async', 'Uses async/await for asynchronous code', stats.asyncAwait, stats.asyncAwait + stats.thenChains * 3, examples);
434
+ if (p)
435
+ patterns.push(p);
436
+ }
437
+ if (stats.thenChains > 0 && stats.thenChains > stats.asyncAwait) {
438
+ const examples = jsFiles.filter(f => f.stats.thenChains > 0).slice(0, 3).map(f => path.basename(f.file));
439
+ const p = makePattern('async', 'Uses Promise.then() chains over async/await', stats.thenChains, stats.thenChains + stats.asyncAwait, examples);
440
+ if (p)
441
+ patterns.push(p);
442
+ }
443
+ if (stats.tryCatch > 0) {
444
+ const examples = jsFiles.filter(f => f.stats.tryCatch > 0).slice(0, 3).map(f => path.basename(f.file));
445
+ const p = makePattern('errors', 'Wraps risky operations in try/catch', stats.tryCatch, tsTotal, examples);
446
+ if (p)
447
+ patterns.push(p);
448
+ }
449
+ if (stats.destructureImports > 0) {
450
+ const examples = jsFiles.filter(f => f.stats.destructureImports > 0).slice(0, 3).map(f => path.basename(f.file));
451
+ const p = makePattern('imports', 'Destructures named imports', stats.destructureImports, tsTotal, examples);
452
+ if (p)
453
+ patterns.push(p);
454
+ }
455
+ if (stats.typeOnlyImports > 0) {
456
+ const examples = jsFiles.filter(f => f.stats.typeOnlyImports > 0).slice(0, 3).map(f => path.basename(f.file));
457
+ const p = makePattern('types', 'Uses type-only imports for type-only modules', stats.typeOnlyImports, tsTotal, examples);
458
+ if (p)
459
+ patterns.push(p);
460
+ }
461
+ if (stats.interfaces > 0) {
462
+ const examples = jsFiles.filter(f => f.stats.interfaces > 0).slice(0, 3).map(f => path.basename(f.file));
463
+ const p = makePattern('types', 'Defines object shapes with interfaces', stats.interfaces, tsTotal, examples);
464
+ if (p)
465
+ patterns.push(p);
466
+ }
467
+ if (stats.typeAliases > 0 && stats.typeAliases > stats.interfaces) {
468
+ const examples = jsFiles.filter(f => f.stats.typeAliases > 0).slice(0, 3).map(f => path.basename(f.file));
469
+ const p = makePattern('types', 'Prefers type aliases over interfaces for object shapes', stats.typeAliases, stats.typeAliases + stats.interfaces, examples);
470
+ if (p)
471
+ patterns.push(p);
472
+ }
473
+ if (stats.generics > 0) {
474
+ const examples = jsFiles.filter(f => f.stats.generics > 0).slice(0, 3).map(f => path.basename(f.file));
475
+ const p = makePattern('types', 'Uses generic type parameters', stats.generics, tsTotal, examples);
476
+ if (p)
477
+ patterns.push(p);
478
+ }
479
+ if (stats.optionalChaining > 0) {
480
+ const examples = jsFiles.filter(f => f.stats.optionalChaining > 0).slice(0, 3).map(f => path.basename(f.file));
481
+ const p = makePattern('types', 'Uses optional chaining (?.) for safe access', stats.optionalChaining, tsTotal, examples);
482
+ if (p)
483
+ patterns.push(p);
484
+ }
485
+ if (stats.nullishCoalescing > 0) {
486
+ const examples = jsFiles.filter(f => f.stats.nullishCoalescing > 0).slice(0, 3).map(f => path.basename(f.file));
487
+ const p = makePattern('types', 'Uses nullish coalescing (??) for default values', stats.nullishCoalescing, tsTotal, examples);
488
+ if (p)
489
+ patterns.push(p);
490
+ }
491
+ if (stats.typeUnknown > stats.typeAny && stats.typeUnknown > 0) {
492
+ const examples = jsFiles.filter(f => f.stats.typeUnknown > 0).slice(0, 3).map(f => path.basename(f.file));
493
+ const p = makePattern('types', 'Prefers `unknown` over `any` for type safety', stats.typeUnknown, stats.typeUnknown + stats.typeAny, examples);
494
+ if (p)
495
+ patterns.push(p);
496
+ }
497
+ else if (stats.typeAny > 0) {
498
+ const examples = jsFiles.filter(f => f.stats.typeAny > 0).slice(0, 3).map(f => path.basename(f.file));
499
+ const p = makePattern('types', 'Has some `any` types (consider replacing with `unknown`)', stats.typeAny, tsTotal, examples);
500
+ if (p)
501
+ patterns.push(p);
502
+ }
503
+ if (stats.templateLiterals > 0) {
504
+ const examples = jsFiles.filter(f => f.stats.templateLiterals > 0).slice(0, 3).map(f => path.basename(f.file));
505
+ const p = makePattern('style', 'Uses template literals for string interpolation', stats.templateLiterals, tsTotal, examples);
506
+ if (p)
507
+ patterns.push(p);
508
+ }
509
+ if (stats.customErrors > 0) {
510
+ const examples = jsFiles.filter(f => f.stats.customErrors > 0).slice(0, 3).map(f => path.basename(f.file));
511
+ const p = makePattern('errors', 'Defines custom error classes that extend Error', stats.customErrors, tsTotal, examples);
512
+ if (p)
513
+ patterns.push(p);
514
+ }
515
+ if (stats.classes > 0) {
516
+ const examples = jsFiles.filter(f => f.stats.classes > 0).slice(0, 3).map(f => path.basename(f.file));
517
+ const p = makePattern('architecture', 'Organizes behavior into classes', stats.classes, tsTotal, examples);
518
+ if (p)
519
+ patterns.push(p);
520
+ }
521
+ if (stats.esmImports > 0 && stats.cjsRequires === 0) {
522
+ const examples = jsFiles.filter(f => f.stats.esmImports > 0).slice(0, 3).map(f => path.basename(f.file));
523
+ const p = makePattern('imports', 'Uses ES Modules (import/export) exclusively', stats.esmImports, tsTotal, examples);
524
+ if (p)
525
+ patterns.push(p);
526
+ }
527
+ else if (stats.cjsRequires > 0 && stats.esmImports === 0) {
528
+ const examples = jsFiles.filter(f => f.stats.cjsRequires > 0).slice(0, 3).map(f => path.basename(f.file));
529
+ const p = makePattern('imports', 'Uses CommonJS (require) exclusively', stats.cjsRequires, tsTotal, examples);
530
+ if (p)
531
+ patterns.push(p);
532
+ }
533
+ else if (stats.esmImports > 0 && stats.cjsRequires > 0) {
534
+ const examples = jsFiles.filter(f => f.stats.cjsRequires > 0).slice(0, 3).map(f => path.basename(f.file));
535
+ const p = makePattern('imports', 'Mixes ESM and CommonJS modules', Math.max(stats.esmImports, stats.cjsRequires), tsTotal, examples);
536
+ if (p)
537
+ patterns.push(p);
538
+ }
539
+ if (stats.describeBlocks > 0) {
540
+ const examples = jsFiles.filter(f => f.stats.describeBlocks > 0).slice(0, 3).map(f => path.basename(f.file));
541
+ const p = makePattern('testing', 'Organizes tests in describe blocks', stats.describeBlocks, tsTotal, examples);
542
+ if (p)
543
+ patterns.push(p);
544
+ }
545
+ if (stats.itBlocks > 0) {
546
+ const examples = jsFiles.filter(f => f.stats.itBlocks > 0).slice(0, 3).map(f => path.basename(f.file));
547
+ const p = makePattern('testing', 'Writes many focused test cases (it/test blocks)', stats.itBlocks, tsTotal, examples);
548
+ if (p)
549
+ patterns.push(p);
550
+ }
551
+ if (stats.mockUsage > 0) {
552
+ const examples = jsFiles.filter(f => f.stats.mockUsage > 0).slice(0, 3).map(f => path.basename(f.file));
553
+ const p = makePattern('testing', 'Mocks dependencies in tests', stats.mockUsage, tsTotal, examples);
554
+ if (p)
555
+ patterns.push(p);
556
+ }
557
+ if (stats.structuredLog > stats.consoleLog) {
558
+ const examples = jsFiles.filter(f => f.stats.structuredLog > 0).slice(0, 3).map(f => path.basename(f.file));
559
+ const p = makePattern('quality', 'Uses a structured logger instead of console.log', stats.structuredLog, tsTotal, examples);
560
+ if (p)
561
+ patterns.push(p);
562
+ }
563
+ else if (stats.consoleLog > 0) {
564
+ const examples = jsFiles.filter(f => f.stats.consoleLog > 0).slice(0, 3).map(f => path.basename(f.file));
565
+ const p = makePattern('quality', 'Has some console.log usage (consider structured logger)', stats.consoleLog, tsTotal, examples);
566
+ if (p)
567
+ patterns.push(p);
568
+ }
569
+ if (stats.jsdocComments > 0) {
570
+ const examples = jsFiles.filter(f => f.stats.jsdocComments > 0).slice(0, 3).map(f => path.basename(f.file));
571
+ const p = makePattern('documentation', 'Documents functions with JSDoc/** comments', stats.jsdocComments, tsTotal, examples);
572
+ if (p)
573
+ patterns.push(p);
574
+ }
575
+ if (stats.abstractClasses > 0) {
576
+ const examples = jsFiles.filter(f => f.stats.abstractClasses > 0).slice(0, 3).map(f => path.basename(f.file));
577
+ const p = makePattern('architecture', 'Uses abstract classes for extensibility', stats.abstractClasses, tsTotal, examples);
578
+ if (p)
579
+ patterns.push(p);
580
+ }
581
+ if (stats.readonlyModifier > 0) {
582
+ const examples = jsFiles.filter(f => f.stats.readonlyModifier > 0).slice(0, 3).map(f => path.basename(f.file));
583
+ const p = makePattern('types', 'Uses readonly modifier for immutability', stats.readonlyModifier, tsTotal, examples);
584
+ if (p)
585
+ patterns.push(p);
586
+ }
587
+ if (stats.privateModifier > 0) {
588
+ const examples = jsFiles.filter(f => f.stats.privateModifier > 0).slice(0, 3).map(f => path.basename(f.file));
589
+ const p = makePattern('architecture', 'Marks class members as private for encapsulation', stats.privateModifier, tsTotal, examples);
590
+ if (p)
591
+ patterns.push(p);
592
+ }
593
+ if (stats.staticModifier > 0) {
594
+ const examples = jsFiles.filter(f => f.stats.staticModifier > 0).slice(0, 3).map(f => path.basename(f.file));
595
+ const p = makePattern('architecture', 'Uses static methods/properties on classes', stats.staticModifier, tsTotal, examples);
596
+ if (p)
597
+ patterns.push(p);
598
+ }
599
+ if (stats.defaultExports > stats.namedExports && stats.defaultExports > 0) {
600
+ const examples = jsFiles.filter(f => f.stats.defaultExports > 0).slice(0, 3).map(f => path.basename(f.file));
601
+ const p = makePattern('imports', 'Prefers default exports over named exports', stats.defaultExports, stats.defaultExports + stats.namedExports, examples);
602
+ if (p)
603
+ patterns.push(p);
604
+ }
605
+ else if (stats.namedExports > stats.defaultExports && stats.namedExports > 0) {
606
+ const examples = jsFiles.filter(f => f.stats.namedExports > 0).slice(0, 3).map(f => path.basename(f.file));
607
+ const p = makePattern('imports', 'Prefers named exports over default exports', stats.namedExports, stats.defaultExports + stats.namedExports, examples);
608
+ if (p)
609
+ patterns.push(p);
610
+ }
611
+ if (stats.promiseAll > 0) {
612
+ const examples = jsFiles.filter(f => f.stats.promiseAll > 0).slice(0, 3).map(f => path.basename(f.file));
613
+ const p = makePattern('async', 'Parallelizes async work using Promise.all()', stats.promiseAll, tsTotal, examples);
614
+ if (p)
615
+ patterns.push(p);
616
+ }
617
+ if (stats.guardClauses > 0) {
618
+ const examples = jsFiles.filter(f => f.stats.guardClauses > 0).slice(0, 3).map(f => path.basename(f.file));
619
+ const p = makePattern('style', 'Uses guard clauses for early returns', stats.guardClauses, tsTotal, examples);
620
+ if (p)
621
+ patterns.push(p);
622
+ }
623
+ if (stats.restParams > 0) {
624
+ const examples = jsFiles.filter(f => f.stats.restParams > 0).slice(0, 3).map(f => path.basename(f.file));
625
+ const p = makePattern('style', 'Uses rest parameters for variadic functions', stats.restParams, tsTotal, examples);
626
+ if (p)
627
+ patterns.push(p);
628
+ }
629
+ if (stats.defaultParams > 0) {
630
+ const examples = jsFiles.filter(f => f.stats.defaultParams > 0).slice(0, 3).map(f => path.basename(f.file));
631
+ const p = makePattern('style', 'Uses default parameter values', stats.defaultParams, tsTotal, examples);
632
+ if (p)
633
+ patterns.push(p);
634
+ }
635
+ if (stats.destructuredParams > 0) {
636
+ const examples = jsFiles.filter(f => f.stats.destructuredParams > 0).slice(0, 3).map(f => path.basename(f.file));
637
+ const p = makePattern('style', 'Destructures object parameters', stats.destructuredParams, tsTotal, examples);
638
+ if (p)
639
+ patterns.push(p);
640
+ }
641
+ if (stats.zodUsage > 0) {
642
+ const examples = jsFiles.filter(f => f.stats.zodUsage > 0).slice(0, 3).map(f => path.basename(f.file));
643
+ const p = makePattern('validation', 'Validates input with Zod schemas', stats.zodUsage, tsTotal, examples);
644
+ if (p)
645
+ patterns.push(p);
646
+ }
647
+ if (stats.yupUsage > 0) {
648
+ const examples = jsFiles.filter(f => f.stats.yupUsage > 0).slice(0, 3).map(f => path.basename(f.file));
649
+ const p = makePattern('validation', 'Validates input with Yup schemas', stats.yupUsage, tsTotal, examples);
650
+ if (p)
651
+ patterns.push(p);
652
+ }
653
+ if (stats.expressRoutes > 0) {
654
+ const examples = jsFiles.filter(f => f.stats.expressRoutes > 0).slice(0, 3).map(f => path.basename(f.file));
655
+ const p = makePattern('architecture', 'Defines HTTP routes with Express handlers', stats.expressRoutes, tsTotal, examples);
656
+ if (p)
657
+ patterns.push(p);
658
+ }
659
+ if (stats.expressMiddleware > 0) {
660
+ const examples = jsFiles.filter(f => f.stats.expressMiddleware > 0).slice(0, 3).map(f => path.basename(f.file));
661
+ const p = makePattern('architecture', 'Uses Express middleware for request processing', stats.expressMiddleware, tsTotal, examples);
662
+ if (p)
663
+ patterns.push(p);
664
+ }
665
+ if (stats.processEnv > 0) {
666
+ const examples = jsFiles.filter(f => f.stats.processEnv > 0).slice(0, 3).map(f => path.basename(f.file));
667
+ const p = makePattern('config', 'Reads configuration from process.env', stats.processEnv, tsTotal, examples);
668
+ if (p)
669
+ patterns.push(p);
670
+ }
671
+ if (stats.envValidation > 0) {
672
+ const examples = jsFiles.filter(f => f.stats.envValidation > 0).slice(0, 3).map(f => path.basename(f.file));
673
+ const p = makePattern('config', 'Validates environment variables at startup', stats.envValidation, tsTotal, examples);
674
+ if (p)
675
+ patterns.push(p);
676
+ }
677
+ if (stats.parameterizedQueries > 0) {
678
+ const examples = jsFiles.filter(f => f.stats.parameterizedQueries > 0).slice(0, 3).map(f => path.basename(f.file));
679
+ const p = makePattern('security', 'Uses parameterized queries (SQL injection safe)', stats.parameterizedQueries, tsTotal, examples);
680
+ if (p)
681
+ patterns.push(p);
682
+ }
683
+ if (stats.regexPatterns > 0) {
684
+ const examples = jsFiles.filter(f => f.stats.regexPatterns > 0).slice(0, 3).map(f => path.basename(f.file));
685
+ const p = makePattern('style', 'Uses regular expressions for pattern matching', stats.regexPatterns, tsTotal, examples);
686
+ if (p)
687
+ patterns.push(p);
688
+ }
689
+ if (stats.forOfLoops > stats.forLoops && stats.forOfLoops > 0) {
690
+ const examples = jsFiles.filter(f => f.stats.forOfLoops > 0).slice(0, 3).map(f => path.basename(f.file));
691
+ const p = makePattern('style', 'Prefers for-of over classic for loops', stats.forOfLoops, stats.forOfLoops + stats.forLoops, examples);
692
+ if (p)
693
+ patterns.push(p);
694
+ }
695
+ if (stats.mapUsage > 0) {
696
+ const examples = jsFiles.filter(f => f.stats.mapUsage > 0).slice(0, 3).map(f => path.basename(f.file));
697
+ const p = makePattern('style', 'Transforms collections with .map()', stats.mapUsage, tsTotal, examples);
698
+ if (p)
699
+ patterns.push(p);
700
+ }
701
+ if (stats.filterUsage > 0) {
702
+ const examples = jsFiles.filter(f => f.stats.filterUsage > 0).slice(0, 3).map(f => path.basename(f.file));
703
+ const p = makePattern('style', 'Filters collections with .filter()', stats.filterUsage, tsTotal, examples);
704
+ if (p)
705
+ patterns.push(p);
706
+ }
707
+ if (stats.reduceUsage > 0) {
708
+ const examples = jsFiles.filter(f => f.stats.reduceUsage > 0).slice(0, 3).map(f => path.basename(f.file));
709
+ const p = makePattern('style', 'Reduces collections with .reduce()', stats.reduceUsage, tsTotal, examples);
710
+ if (p)
711
+ patterns.push(p);
712
+ }
713
+ if (stats.spreadOperator > 0) {
714
+ const examples = jsFiles.filter(f => f.stats.spreadOperator > 0).slice(0, 3).map(f => path.basename(f.file));
715
+ const p = makePattern('style', 'Uses spread operator for shallow copies', stats.spreadOperator, tsTotal, examples);
716
+ if (p)
717
+ patterns.push(p);
718
+ }
719
+ if (stats.catchIgnored > 0) {
720
+ const examples = jsFiles.filter(f => f.stats.catchIgnored > 0).slice(0, 3).map(f => path.basename(f.file));
721
+ const p = makePattern('quality', 'Has some empty catch blocks (consider logging)', stats.catchIgnored, tsTotal, examples);
722
+ if (p)
723
+ patterns.push(p);
724
+ }
725
+ if (stats.todoComments > 0) {
726
+ const examples = jsFiles.filter(f => f.stats.todoComments > 0).slice(0, 3).map(f => path.basename(f.file));
727
+ const p = makePattern('quality', 'Has TODO/FIXME comments in the code', stats.todoComments, tsTotal, examples);
728
+ if (p)
729
+ patterns.push(p);
730
+ }
731
+ if (stats.barrelExports > 0) {
732
+ const examples = jsFiles.filter(f => f.stats.barrelExports > 0).slice(0, 3).map(f => path.basename(f.file));
733
+ const p = makePattern('architecture', 'Uses barrel exports (index.ts re-exports)', stats.barrelExports, tsTotal, examples);
734
+ if (p)
735
+ patterns.push(p);
736
+ }
737
+ }
738
+ if (pyFiles.length > 0) {
739
+ const examples = pyFiles.slice(0, 3).map(f => path.basename(f.file));
740
+ if (stats.asyncAwait > 0) {
741
+ patterns.push({ category: 'async', description: 'Python uses async def for coroutines', confidence: 0.9, evidence: stats.asyncAwait, total: pyFiles.length, examples, language: 'python' });
742
+ }
743
+ if (stats.typeAliases > 0) {
744
+ patterns.push({ category: 'types', description: 'Python uses typing module annotations', confidence: 0.8, evidence: stats.typeAliases, total: pyFiles.length, examples, language: 'python' });
745
+ }
746
+ if (stats.itBlocks > 0) {
747
+ patterns.push({ category: 'testing', description: 'Python uses pytest-style test_ functions', confidence: 0.9, evidence: stats.itBlocks, total: pyFiles.length, examples, language: 'python' });
748
+ }
749
+ if (stats.customErrors > 0) {
750
+ patterns.push({ category: 'errors', description: 'Python defines custom Exception classes', confidence: 0.7, evidence: stats.customErrors, total: pyFiles.length, examples, language: 'python' });
751
+ }
752
+ if (stats.jsdocComments > 0) {
753
+ patterns.push({ category: 'documentation', description: 'Python uses docstrings for documentation', confidence: 0.9, evidence: stats.jsdocComments, total: pyFiles.length, examples, language: 'python' });
754
+ }
755
+ }
756
+ if (goFiles.length > 0) {
757
+ const examples = goFiles.slice(0, 3).map(f => path.basename(f.file));
758
+ if (stats.interfaces > 0) {
759
+ patterns.push({ category: 'types', description: 'Go uses interfaces for behavior contracts', confidence: 0.95, evidence: stats.interfaces, total: goFiles.length, examples, language: 'go' });
760
+ }
761
+ if (stats.tryCatch > 0) {
762
+ patterns.push({ category: 'errors', description: 'Go checks errors explicitly (if err != nil)', confidence: 0.95, evidence: stats.tryCatch, total: goFiles.length, examples, language: 'go' });
763
+ }
764
+ if (stats.itBlocks > 0) {
765
+ patterns.push({ category: 'testing', description: 'Go uses standard testing package (func TestXxx)', confidence: 0.95, evidence: stats.itBlocks, total: goFiles.length, examples, language: 'go' });
766
+ }
767
+ if (stats.classes > 0) {
768
+ patterns.push({ category: 'architecture', description: 'Go models data with structs', confidence: 0.95, evidence: stats.classes, total: goFiles.length, examples, language: 'go' });
769
+ }
770
+ }
771
+ return patterns;
772
+ }
773
+ function buildHubMap(perFile) {
774
+ const importCounts = new Map();
775
+ const importPattern = /import\s+(?:\{[^}]*\}|\*\s+as\s+\w+|\w+)\s+from\s+['"]([^'"]+)['"]/g;
776
+ const requirePattern = /require\(['"]([^'"]+)['"]\)/g;
777
+ const pyImport = /(?:^|\n)\s*(?:from\s+([\w.]+)\s+import|import\s+([\w.]+))/g;
778
+ const goImport = /^\s*"([^"]+)"\s*$/gm;
779
+ for (const f of perFile) {
780
+ const imports = new Set();
781
+ let m;
782
+ importPattern.lastIndex = 0;
783
+ while ((m = importPattern.exec(f.content)) !== null) {
784
+ const imp = m[1];
785
+ if (imp.startsWith('.') || imp.startsWith('/')) {
786
+ imports.add(imp);
787
+ }
788
+ }
789
+ requirePattern.lastIndex = 0;
790
+ while ((m = requirePattern.exec(f.content)) !== null) {
791
+ const imp = m[1];
792
+ if (imp.startsWith('.'))
793
+ imports.add(imp);
794
+ }
795
+ pyImport.lastIndex = 0;
796
+ while ((m = pyImport.exec(f.content)) !== null) {
797
+ const imp = m[1] || m[2];
798
+ if (imp && imp.startsWith('.'))
799
+ imports.add(imp);
800
+ }
801
+ goImport.lastIndex = 0;
802
+ while ((m = goImport.exec(f.content)) !== null) {
803
+ if (m[1].startsWith('.'))
804
+ imports.add(m[1]);
805
+ }
806
+ for (const imp of imports) {
807
+ const resolved = path.resolve(path.dirname(f.file), imp);
808
+ importCounts.set(resolved, (importCounts.get(resolved) || 0) + 1);
809
+ }
810
+ }
811
+ const hubs = [];
812
+ for (const [file, count] of importCounts) {
813
+ if (count >= 2) {
814
+ hubs.push({ file: path.relative(process.cwd(), file), importedBy: count });
815
+ }
816
+ }
817
+ hubs.sort((a, b) => b.importedBy - a.importedBy);
818
+ return hubs.slice(0, 5);
819
+ }
820
+ function detectIssues(perFile, importMap) {
821
+ const issues = [];
822
+ const largeFiles = perFile.filter(f => f.lines > 500);
823
+ if (largeFiles.length > 0) {
824
+ issues.push({
825
+ severity: 'medium',
826
+ type: 'large_file',
827
+ description: `${largeFiles.length} file${largeFiles.length > 1 ? 's' : ''} >500 lines (consider splitting)`,
828
+ files: largeFiles.slice(0, 3).map(f => path.relative(process.cwd(), f.file)),
829
+ });
830
+ }
831
+ const circular = [];
832
+ function findCircular(start, current, path, visited) {
833
+ if (path.length > 5)
834
+ return;
835
+ const imports = importMap.get(current) || [];
836
+ for (const imp of imports) {
837
+ if (imp === start && path.length > 0) {
838
+ circular.push([...path, current, start]);
839
+ return;
840
+ }
841
+ if (!visited.has(imp) && path.length < 5) {
842
+ visited.add(imp);
843
+ findCircular(start, imp, [...path, current], visited);
844
+ }
845
+ }
846
+ }
847
+ let checked = 0;
848
+ for (const file of importMap.keys()) {
849
+ if (++checked > 100)
850
+ break;
851
+ findCircular(file, file, [], new Set([file]));
852
+ }
853
+ if (circular.length > 0) {
854
+ const seen = new Set();
855
+ const unique = [];
856
+ for (const c of circular) {
857
+ const key = c.slice(0, -1).sort().join('|');
858
+ if (!seen.has(key)) {
859
+ seen.add(key);
860
+ unique.push(c);
861
+ }
862
+ }
863
+ if (unique.length > 0) {
864
+ issues.push({
865
+ severity: 'high',
866
+ type: 'circular_dep',
867
+ description: `${unique.length} circular dependenc${unique.length > 1 ? 'ies' : 'y'} detected`,
868
+ files: unique.slice(0, 2).map(c => c.slice(0, -1).map(f => path.relative(process.cwd(), f)).join(' → ')),
869
+ });
870
+ }
871
+ }
872
+ const testCount = perFile.filter(f => /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(f.file) || /test_\w+\.py$/.test(f.file) || /_test\.go$/.test(f.file)).length;
873
+ const sourceCount = perFile.length - testCount;
874
+ if (sourceCount > 10 && testCount < sourceCount * 0.1) {
875
+ issues.push({
876
+ severity: 'low',
877
+ type: 'low_test_coverage',
878
+ description: `Test ratio is low: ${testCount} test files for ${sourceCount} source files`,
879
+ files: [],
880
+ });
881
+ }
882
+ return issues;
883
+ }
884
+ function buildImportMap(perFile) {
885
+ const m = new Map();
886
+ for (const f of perFile) {
887
+ const imports = [];
888
+ let match;
889
+ const importRe = /import\s+(?:\{[^}]*\}|\*\s+as\s+\w+|\w+)\s+from\s+['"]([^'"]+)['"]/g;
890
+ while ((match = importRe.exec(f.content)) !== null) {
891
+ if (match[1].startsWith('.') || match[1].startsWith('/')) {
892
+ try {
893
+ const resolved = path.resolve(path.dirname(f.file), match[1]);
894
+ imports.push(resolved);
895
+ }
896
+ catch { }
897
+ }
898
+ }
899
+ const requireRe = /require\(['"]([^'"]+)['"]\)/g;
900
+ while ((match = requireRe.exec(f.content)) !== null) {
901
+ if (match[1].startsWith('.')) {
902
+ try {
903
+ const resolved = path.resolve(path.dirname(f.file), match[1]);
904
+ imports.push(resolved);
905
+ }
906
+ catch { }
907
+ }
908
+ }
909
+ m.set(f.file, imports);
910
+ }
911
+ return m;
912
+ }
913
+ function scanCodebase(rootDir, opts = {}) {
914
+ const startTime = Date.now();
915
+ const budget = opts.timeBudgetMs || 5000;
916
+ const maxFiles = opts.maxFiles || (opts.detectAllStacks ? 20000 : 2000);
917
+ const files = discoverFiles(rootDir, { maxFiles });
918
+ if (opts.onProgress)
919
+ opts.onProgress({ phase: 'discover', progress: 1.0 });
920
+ let stack = detectStack(rootDir);
921
+ if (opts.detectAllStacks) {
922
+ const subStacks = [];
923
+ let entries;
924
+ try {
925
+ entries = fs.readdirSync(rootDir, { withFileTypes: true });
926
+ }
927
+ catch {
928
+ entries = [];
929
+ }
930
+ for (const e of entries) {
931
+ if (e.isDirectory() && !SKIP_DIRS.has(e.name) && !e.name.startsWith('.')) {
932
+ const subStack = detectStack(path.join(rootDir, e.name));
933
+ for (const s of subStack) {
934
+ if (!subStacks.some(x => x.name === s.name)) {
935
+ subStacks.push(s);
936
+ }
937
+ }
938
+ }
939
+ }
940
+ stack = subStacks.length > 0 ? subStacks : stack;
941
+ }
942
+ const perFile = [];
943
+ const languages = {};
944
+ let totalLines = 0;
945
+ let totalFunctions = 0;
946
+ const elapsed = () => Date.now() - startTime;
947
+ for (let i = 0; i < files.length; i++) {
948
+ if (elapsed() > budget && perFile.length > 0) {
949
+ break;
950
+ }
951
+ const f = files[i];
952
+ let content;
953
+ try {
954
+ content = fs.readFileSync(f.path, 'utf-8');
955
+ }
956
+ catch {
957
+ continue;
958
+ }
959
+ const lines = content.split('\n').length;
960
+ if (lines > MAX_FILE_LINES) {
961
+ const truncated = content.split('\n').slice(0, MAX_FILE_LINES).join('\n');
962
+ content = truncated;
963
+ }
964
+ const stats = scanFileContent(content, f.ext);
965
+ perFile.push({ file: f.path, content, ext: f.ext, lines, stats });
966
+ totalLines += lines;
967
+ totalFunctions += stats.functionDecls + stats.arrowFunctions + stats.classes;
968
+ const lang = f.ext === '.ts' || f.ext === '.tsx' ? 'typescript'
969
+ : f.ext === '.js' || f.ext === '.jsx' || f.ext === '.mjs' || f.ext === '.cjs' ? 'javascript'
970
+ : f.ext === '.py' ? 'python'
971
+ : f.ext === '.go' ? 'go'
972
+ : f.ext === '.html' || f.ext === '.htm' ? 'html'
973
+ : 'other';
974
+ languages[lang] = (languages[lang] || 0) + 1;
975
+ }
976
+ const allStats = aggregateStats(perFile.map(p => p.stats));
977
+ const patterns = extractPatterns(allStats, perFile);
978
+ const importMap = buildImportMap(perFile);
979
+ const hubs = buildHubMap(perFile);
980
+ const issues = detectIssues(perFile, importMap);
981
+ const fileSignatures = [];
982
+ for (const f of perFile) {
983
+ const aspects = [];
984
+ if (f.stats.arrowFunctions > f.stats.functionDecls)
985
+ aspects.push('arrow');
986
+ if (f.stats.asyncAwait > 0)
987
+ aspects.push('async');
988
+ if (f.stats.tryCatch > 0)
989
+ aspects.push('trycatch');
990
+ if (f.stats.optionalChaining > 0)
991
+ aspects.push('optional');
992
+ if (f.stats.interfaces > 0)
993
+ aspects.push('interface');
994
+ if (f.stats.classes > 0)
995
+ aspects.push('class');
996
+ if (f.stats.todoComments > 0)
997
+ aspects.push('todo');
998
+ if (aspects.length > 0) {
999
+ fileSignatures.push({
1000
+ category: 'file-signature',
1001
+ description: `${path.relative(process.cwd(), f.file)}: ${aspects.join('+')}`,
1002
+ confidence: 1.0,
1003
+ evidence: 1,
1004
+ total: 1,
1005
+ examples: [path.relative(process.cwd(), f.file)],
1006
+ language: f.ext,
1007
+ });
1008
+ }
1009
+ }
1010
+ return {
1011
+ duration: Date.now() - startTime,
1012
+ filesScanned: perFile.length,
1013
+ totalLines,
1014
+ totalFunctions,
1015
+ stack,
1016
+ patterns: [...patterns, ...fileSignatures],
1017
+ hubs,
1018
+ issues,
1019
+ languages,
1020
+ };
1021
+ }
1022
+ function saveOnboarding(result, dir = path.join(os.homedir(), '.fivo', 'cell')) {
1023
+ if (!fs.existsSync(dir))
1024
+ fs.mkdirSync(dir, { recursive: true });
1025
+ const file = path.join(dir, 'onboarding.json');
1026
+ fs.writeFileSync(file, JSON.stringify(result, null, 2), 'utf-8');
1027
+ }
1028
+ function loadOnboarding(dir = path.join(os.homedir(), '.fivo', 'cell')) {
1029
+ const file = path.join(dir, 'onboarding.json');
1030
+ if (!fs.existsSync(file))
1031
+ return null;
1032
+ try {
1033
+ return JSON.parse(fs.readFileSync(file, 'utf-8'));
1034
+ }
1035
+ catch {
1036
+ return null;
1037
+ }
1038
+ }
1039
+ //# sourceMappingURL=onboarding-scan.js.map