@vibecheckai/cli 3.0.3 → 3.0.4

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 (69) hide show
  1. package/bin/cli-hygiene.js +241 -0
  2. package/bin/guardrail.js +834 -0
  3. package/bin/runners/cli-utils.js +1070 -0
  4. package/bin/runners/context/ai-task-decomposer.js +337 -0
  5. package/bin/runners/context/analyzer.js +462 -0
  6. package/bin/runners/context/api-contracts.js +427 -0
  7. package/bin/runners/context/context-diff.js +342 -0
  8. package/bin/runners/context/context-pruner.js +291 -0
  9. package/bin/runners/context/dependency-graph.js +414 -0
  10. package/bin/runners/context/generators/claude.js +107 -0
  11. package/bin/runners/context/generators/codex.js +108 -0
  12. package/bin/runners/context/generators/copilot.js +119 -0
  13. package/bin/runners/context/generators/cursor.js +514 -0
  14. package/bin/runners/context/generators/mcp.js +151 -0
  15. package/bin/runners/context/generators/windsurf.js +180 -0
  16. package/bin/runners/context/git-context.js +302 -0
  17. package/bin/runners/context/index.js +1042 -0
  18. package/bin/runners/context/insights.js +173 -0
  19. package/bin/runners/context/mcp-server/generate-rules.js +337 -0
  20. package/bin/runners/context/mcp-server/index.js +1176 -0
  21. package/bin/runners/context/mcp-server/package.json +24 -0
  22. package/bin/runners/context/memory.js +200 -0
  23. package/bin/runners/context/monorepo.js +215 -0
  24. package/bin/runners/context/multi-repo-federation.js +404 -0
  25. package/bin/runners/context/patterns.js +253 -0
  26. package/bin/runners/context/proof-context.js +972 -0
  27. package/bin/runners/context/security-scanner.js +303 -0
  28. package/bin/runners/context/semantic-search.js +350 -0
  29. package/bin/runners/context/shared.js +264 -0
  30. package/bin/runners/context/team-conventions.js +310 -0
  31. package/bin/runners/lib/ai-bridge.js +416 -0
  32. package/bin/runners/lib/analysis-core.js +271 -0
  33. package/bin/runners/lib/analyzers.js +541 -0
  34. package/bin/runners/lib/audit-bridge.js +391 -0
  35. package/bin/runners/lib/auth-truth.js +193 -0
  36. package/bin/runners/lib/auth.js +215 -0
  37. package/bin/runners/lib/backup.js +62 -0
  38. package/bin/runners/lib/billing.js +107 -0
  39. package/bin/runners/lib/claims.js +118 -0
  40. package/bin/runners/lib/cli-ui.js +540 -0
  41. package/bin/runners/lib/compliance-bridge-new.js +0 -0
  42. package/bin/runners/lib/compliance-bridge.js +165 -0
  43. package/bin/runners/lib/contracts/auth-contract.js +194 -0
  44. package/bin/runners/lib/contracts/env-contract.js +178 -0
  45. package/bin/runners/lib/contracts/external-contract.js +198 -0
  46. package/bin/runners/lib/contracts/guard.js +168 -0
  47. package/bin/runners/lib/contracts/index.js +89 -0
  48. package/bin/runners/lib/contracts/plan-validator.js +311 -0
  49. package/bin/runners/lib/contracts/route-contract.js +192 -0
  50. package/bin/runners/lib/detect.js +89 -0
  51. package/bin/runners/lib/doctor/autofix.js +254 -0
  52. package/bin/runners/lib/doctor/index.js +37 -0
  53. package/bin/runners/lib/doctor/modules/dependencies.js +325 -0
  54. package/bin/runners/lib/doctor/modules/index.js +46 -0
  55. package/bin/runners/lib/doctor/modules/network.js +250 -0
  56. package/bin/runners/lib/doctor/modules/project.js +312 -0
  57. package/bin/runners/lib/doctor/modules/runtime.js +224 -0
  58. package/bin/runners/lib/doctor/modules/security.js +348 -0
  59. package/bin/runners/lib/doctor/modules/system.js +213 -0
  60. package/bin/runners/lib/doctor/modules/vibecheck.js +394 -0
  61. package/bin/runners/lib/doctor/reporter.js +262 -0
  62. package/bin/runners/lib/doctor/service.js +262 -0
  63. package/bin/runners/lib/doctor/types.js +113 -0
  64. package/bin/runners/lib/doctor/ui.js +263 -0
  65. package/bin/runners/lib/doctor-enhanced.js +233 -0
  66. package/bin/runners/lib/doctor-v2.js +608 -0
  67. package/bin/runners/lib/enforcement.js +72 -0
  68. package/bin/vibecheck.js +0 -0
  69. package/package.json +8 -9
@@ -0,0 +1,264 @@
1
+ /**
2
+ * Shared Context Module
3
+ * Cross-project pattern sharing and context registry
4
+ */
5
+
6
+ const fs = require("fs");
7
+ const path = require("path");
8
+ const os = require("os");
9
+ const crypto = require("crypto");
10
+
11
+ const VIBECHECK_HOME = path.join(os.homedir(), ".vibecheck");
12
+ const SHARED_CONTEXT_FILE = path.join(VIBECHECK_HOME, "shared-context.json");
13
+
14
+ /**
15
+ * Initialize shared context file
16
+ */
17
+ function initializeSharedContext() {
18
+ if (!fs.existsSync(VIBECHECK_HOME)) {
19
+ fs.mkdirSync(VIBECHECK_HOME, { recursive: true });
20
+ }
21
+
22
+ if (!fs.existsSync(SHARED_CONTEXT_FILE)) {
23
+ fs.writeFileSync(SHARED_CONTEXT_FILE, JSON.stringify({
24
+ version: "1.0.0",
25
+ projects: {},
26
+ sharedPatterns: {},
27
+ sharedHooks: {},
28
+ sharedComponents: {},
29
+ lastUpdated: null,
30
+ }, null, 2));
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Register project in shared context
36
+ */
37
+ function registerSharedContext(projectPath, analysis) {
38
+ initializeSharedContext();
39
+
40
+ let shared;
41
+ try {
42
+ shared = JSON.parse(fs.readFileSync(SHARED_CONTEXT_FILE, "utf-8"));
43
+ } catch {
44
+ shared = {
45
+ projects: {},
46
+ sharedPatterns: {},
47
+ sharedHooks: {},
48
+ sharedComponents: {},
49
+ lastUpdated: null
50
+ };
51
+ }
52
+
53
+ const projectId = crypto.createHash("md5").update(projectPath).digest("hex").slice(0, 8);
54
+
55
+ // Store project info
56
+ shared.projects[projectId] = {
57
+ path: projectPath,
58
+ name: analysis.name,
59
+ framework: analysis.framework,
60
+ language: analysis.language,
61
+ hooks: analysis.patterns?.hooks || [],
62
+ components: analysis.components?.slice(0, 30) || [],
63
+ models: analysis.models?.slice(0, 20) || [],
64
+ stateManagement: analysis.patterns?.stateManagement,
65
+ validation: analysis.patterns?.validation,
66
+ lastUpdated: new Date().toISOString(),
67
+ };
68
+
69
+ // Build shared hooks index
70
+ shared.sharedHooks = {};
71
+ for (const [id, proj] of Object.entries(shared.projects)) {
72
+ for (const hook of proj.hooks || []) {
73
+ shared.sharedHooks[hook] = shared.sharedHooks[hook] || [];
74
+ if (!shared.sharedHooks[hook].includes(proj.name)) {
75
+ shared.sharedHooks[hook].push(proj.name);
76
+ }
77
+ }
78
+ }
79
+
80
+ // Build shared components index
81
+ shared.sharedComponents = {};
82
+ for (const [id, proj] of Object.entries(shared.projects)) {
83
+ for (const component of proj.components || []) {
84
+ shared.sharedComponents[component] = shared.sharedComponents[component] || [];
85
+ if (!shared.sharedComponents[component].includes(proj.name)) {
86
+ shared.sharedComponents[component].push(proj.name);
87
+ }
88
+ }
89
+ }
90
+
91
+ // Build shared patterns index
92
+ shared.sharedPatterns = {};
93
+ for (const [id, proj] of Object.entries(shared.projects)) {
94
+ if (proj.stateManagement) {
95
+ shared.sharedPatterns[proj.stateManagement] = shared.sharedPatterns[proj.stateManagement] || [];
96
+ if (!shared.sharedPatterns[proj.stateManagement].includes(proj.name)) {
97
+ shared.sharedPatterns[proj.stateManagement].push(proj.name);
98
+ }
99
+ }
100
+ if (proj.validation) {
101
+ shared.sharedPatterns[proj.validation] = shared.sharedPatterns[proj.validation] || [];
102
+ if (!shared.sharedPatterns[proj.validation].includes(proj.name)) {
103
+ shared.sharedPatterns[proj.validation].push(proj.name);
104
+ }
105
+ }
106
+ }
107
+
108
+ shared.lastUpdated = new Date().toISOString();
109
+ fs.writeFileSync(SHARED_CONTEXT_FILE, JSON.stringify(shared, null, 2));
110
+
111
+ return shared;
112
+ }
113
+
114
+ /**
115
+ * Get patterns shared across projects
116
+ */
117
+ function getSharedPatterns() {
118
+ try {
119
+ if (!fs.existsSync(SHARED_CONTEXT_FILE)) return null;
120
+ const shared = JSON.parse(fs.readFileSync(SHARED_CONTEXT_FILE, "utf-8"));
121
+
122
+ // Find patterns used in multiple projects
123
+ const multiProjectPatterns = Object.entries(shared.sharedPatterns || {})
124
+ .filter(([_, projects]) => projects.length > 1)
125
+ .map(([pattern, projects]) => ({ pattern, projects, count: projects.length }))
126
+ .sort((a, b) => b.count - a.count);
127
+
128
+ return {
129
+ totalProjects: Object.keys(shared.projects || {}).length,
130
+ sharedPatterns: multiProjectPatterns.slice(0, 20),
131
+ projects: Object.values(shared.projects || {}).map(p => ({
132
+ name: p.name,
133
+ framework: p.framework,
134
+ lastUpdated: p.lastUpdated,
135
+ })),
136
+ };
137
+ } catch {
138
+ return null;
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Get hooks shared across projects
144
+ */
145
+ function getSharedHooks() {
146
+ try {
147
+ if (!fs.existsSync(SHARED_CONTEXT_FILE)) return [];
148
+ const shared = JSON.parse(fs.readFileSync(SHARED_CONTEXT_FILE, "utf-8"));
149
+
150
+ return Object.entries(shared.sharedHooks || {})
151
+ .filter(([_, projects]) => projects.length > 1)
152
+ .map(([hook, projects]) => ({ hook, projects, count: projects.length }))
153
+ .sort((a, b) => b.count - a.count)
154
+ .slice(0, 30);
155
+ } catch {
156
+ return [];
157
+ }
158
+ }
159
+
160
+ /**
161
+ * Get components shared across projects
162
+ */
163
+ function getSharedComponents() {
164
+ try {
165
+ if (!fs.existsSync(SHARED_CONTEXT_FILE)) return [];
166
+ const shared = JSON.parse(fs.readFileSync(SHARED_CONTEXT_FILE, "utf-8"));
167
+
168
+ return Object.entries(shared.sharedComponents || {})
169
+ .filter(([_, projects]) => projects.length > 1)
170
+ .map(([component, projects]) => ({ component, projects, count: projects.length }))
171
+ .sort((a, b) => b.count - a.count)
172
+ .slice(0, 30);
173
+ } catch {
174
+ return [];
175
+ }
176
+ }
177
+
178
+ /**
179
+ * Find similar projects based on tech stack
180
+ */
181
+ function findSimilarProjects(analysis) {
182
+ try {
183
+ if (!fs.existsSync(SHARED_CONTEXT_FILE)) return [];
184
+ const shared = JSON.parse(fs.readFileSync(SHARED_CONTEXT_FILE, "utf-8"));
185
+
186
+ const similar = [];
187
+ for (const [id, proj] of Object.entries(shared.projects || {})) {
188
+ let score = 0;
189
+ if (proj.framework === analysis.framework) score += 3;
190
+ if (proj.language === analysis.language) score += 2;
191
+ if (proj.stateManagement === analysis.patterns?.stateManagement) score += 2;
192
+ if (proj.validation === analysis.patterns?.validation) score += 1;
193
+
194
+ if (score > 0 && proj.name !== analysis.name) {
195
+ similar.push({ ...proj, similarityScore: score });
196
+ }
197
+ }
198
+
199
+ return similar.sort((a, b) => b.similarityScore - a.similarityScore).slice(0, 5);
200
+ } catch {
201
+ return [];
202
+ }
203
+ }
204
+
205
+ /**
206
+ * Get all registered projects
207
+ */
208
+ function getAllProjects() {
209
+ try {
210
+ if (!fs.existsSync(SHARED_CONTEXT_FILE)) return [];
211
+ const shared = JSON.parse(fs.readFileSync(SHARED_CONTEXT_FILE, "utf-8"));
212
+ return Object.values(shared.projects || {});
213
+ } catch {
214
+ return [];
215
+ }
216
+ }
217
+
218
+ /**
219
+ * Remove project from shared context
220
+ */
221
+ function removeProject(projectPath) {
222
+ try {
223
+ if (!fs.existsSync(SHARED_CONTEXT_FILE)) return false;
224
+ const shared = JSON.parse(fs.readFileSync(SHARED_CONTEXT_FILE, "utf-8"));
225
+ const projectId = crypto.createHash("md5").update(projectPath).digest("hex").slice(0, 8);
226
+
227
+ if (shared.projects[projectId]) {
228
+ delete shared.projects[projectId];
229
+ fs.writeFileSync(SHARED_CONTEXT_FILE, JSON.stringify(shared, null, 2));
230
+ return true;
231
+ }
232
+ return false;
233
+ } catch {
234
+ return false;
235
+ }
236
+ }
237
+
238
+ /**
239
+ * Clear all shared context
240
+ */
241
+ function clearSharedContext() {
242
+ const shared = {
243
+ version: "1.0.0",
244
+ projects: {},
245
+ sharedPatterns: {},
246
+ sharedHooks: {},
247
+ sharedComponents: {},
248
+ lastUpdated: new Date().toISOString(),
249
+ };
250
+ fs.writeFileSync(SHARED_CONTEXT_FILE, JSON.stringify(shared, null, 2));
251
+ }
252
+
253
+ module.exports = {
254
+ SHARED_CONTEXT_FILE,
255
+ initializeSharedContext,
256
+ registerSharedContext,
257
+ getSharedPatterns,
258
+ getSharedHooks,
259
+ getSharedComponents,
260
+ findSimilarProjects,
261
+ getAllProjects,
262
+ removeProject,
263
+ clearSharedContext,
264
+ };
@@ -0,0 +1,310 @@
1
+ /**
2
+ * Team Conventions Learning Module
3
+ * Learns individual developer styles from git blame
4
+ */
5
+
6
+ const fs = require("fs");
7
+ const path = require("path");
8
+ const { execSync } = require("child_process");
9
+
10
+ /**
11
+ * Execute git command safely
12
+ */
13
+ function execGit(command, cwd = process.cwd()) {
14
+ try {
15
+ return execSync(command, { cwd, encoding: "utf-8" }).trim();
16
+ } catch {
17
+ return null;
18
+ }
19
+ }
20
+
21
+ /**
22
+ * Analyze author's coding style from git blame
23
+ */
24
+ function analyzeAuthorStyle(author, projectPath, fileLimit = 20) {
25
+ // Get files touched by author
26
+ const output = execGit(`log --author="${author}" --name-only --pretty=format:"" | sort | uniq`, projectPath);
27
+ if (!output) return null;
28
+
29
+ const files = output.split("\n")
30
+ .filter(f => f && (f.endsWith(".ts") || f.endsWith(".tsx") || f.endsWith(".js") || f.endsWith(".jsx")))
31
+ .slice(0, fileLimit);
32
+
33
+ const style = {
34
+ author,
35
+ filesAnalyzed: files.length,
36
+ patterns: {
37
+ imports: {},
38
+ naming: {
39
+ components: {},
40
+ functions: {},
41
+ variables: {},
42
+ },
43
+ formatting: {
44
+ semicolons: 0,
45
+ quotes: { single: 0, double: 0 },
46
+ trailingCommas: 0,
47
+ },
48
+ comments: {
49
+ jsdoc: 0,
50
+ inline: 0,
51
+ block: 0,
52
+ },
53
+ react: {
54
+ functional: 0,
55
+ class: 0,
56
+ hooks: [],
57
+ },
58
+ typescript: {
59
+ explicitTypes: 0,
60
+ interfaces: 0,
61
+ types: 0,
62
+ },
63
+ },
64
+ };
65
+
66
+ for (const file of files) {
67
+ try {
68
+ const blameOutput = execGit(`blame --line-porcelain "${file}"`, projectPath);
69
+ if (!blameOutput) continue;
70
+
71
+ const lines = blameOutput.split("\n");
72
+ let currentAuthor = null;
73
+ let code = "";
74
+
75
+ for (const line of lines) {
76
+ if (line.startsWith(`author ${author}`)) {
77
+ currentAuthor = author;
78
+ } else if (line.startsWith("author ") && currentAuthor === author) {
79
+ currentAuthor = null;
80
+ } else if (line.startsWith("\t") && currentAuthor === author) {
81
+ code += line.slice(1) + "\n";
82
+ }
83
+ }
84
+
85
+ // Analyze the collected code
86
+ analyzeCodeStyle(code, style);
87
+ } catch {}
88
+ }
89
+
90
+ // Calculate percentages
91
+ const totalLines = style.patterns.formatting.semicolons +
92
+ style.patterns.formatting.quotes.single +
93
+ style.patterns.formatting.quotes.double;
94
+
95
+ if (totalLines > 0) {
96
+ style.patterns.formatting.semiconPercentage = (style.patterns.formatting.semicolons / totalLines) * 100;
97
+ style.patterns.formatting.singleQuotePercentage = (style.patterns.formatting.quotes.single / totalLines) * 100;
98
+ }
99
+
100
+ return style;
101
+ }
102
+
103
+ /**
104
+ * Analyze code patterns from content
105
+ */
106
+ function analyzeCodeStyle(content, style) {
107
+ const lines = content.split("\n");
108
+
109
+ for (const line of lines) {
110
+ const trimmed = line.trim();
111
+
112
+ // Import patterns
113
+ if (trimmed.startsWith("import ")) {
114
+ if (trimmed.includes(" { ")) {
115
+ style.patterns.imports.named = (style.patterns.imports.named || 0) + 1;
116
+ } else if (trimmed.includes(" * as ")) {
117
+ style.patterns.imports.namespace = (style.patterns.imports.namespace || 0) + 1;
118
+ } else {
119
+ style.patterns.imports.default = (style.patterns.imports.default || 0) + 1;
120
+ }
121
+ }
122
+
123
+ // Naming patterns
124
+ if (trimmed.includes("function ") || trimmed.includes("const ")) {
125
+ if (/^[A-Z]/.test(trimmed)) {
126
+ style.patterns.naming.components.pascalCase = (style.patterns.naming.components.pascalCase || 0) + 1;
127
+ } else if (/^[a-z][A-Z]/.test(trimmed)) {
128
+ style.patterns.naming.functions.camelCase = (style.patterns.naming.functions.camelCase || 0) + 1;
129
+ }
130
+ }
131
+
132
+ // Formatting
133
+ if (trimmed.endsWith(";")) {
134
+ style.patterns.formatting.semicolons++;
135
+ }
136
+ if (trimmed.includes("'")) {
137
+ style.patterns.formatting.quotes.single++;
138
+ }
139
+ if (trimmed.includes('"')) {
140
+ style.patterns.formatting.quotes.double++;
141
+ }
142
+ if (trimmed.includes(",") && !trimmed.includes(", ")) {
143
+ style.patterns.formatting.trailingCommas++;
144
+ }
145
+
146
+ // Comments
147
+ if (trimmed.startsWith("/**") || trimmed.startsWith(" *")) {
148
+ style.patterns.comments.jsdoc++;
149
+ } else if (trimmed.startsWith("//")) {
150
+ style.patterns.comments.inline++;
151
+ } else if (trimmed.startsWith("/*")) {
152
+ style.patterns.comments.block++;
153
+ }
154
+
155
+ // React patterns
156
+ if (trimmed.includes("function ") && /^[A-Z]/.test(trimmed)) {
157
+ style.patterns.react.functional++;
158
+ } else if (trimmed.includes("class ") && trimmed.includes("extends")) {
159
+ style.patterns.react.class++;
160
+ }
161
+
162
+ // Hooks
163
+ const hookMatch = trimmed.match(/(use[A-Z]\w+)/);
164
+ if (hookMatch) {
165
+ if (!style.patterns.react.hooks.includes(hookMatch[1])) {
166
+ style.patterns.react.hooks.push(hookMatch[1]);
167
+ }
168
+ }
169
+
170
+ // TypeScript
171
+ if (trimmed.includes(": ")) {
172
+ style.patterns.typescript.explicitTypes++;
173
+ }
174
+ if (trimmed.includes("interface ")) {
175
+ style.patterns.typescript.interfaces++;
176
+ }
177
+ if (trimmed.includes("type ")) {
178
+ style.patterns.typescript.types++;
179
+ }
180
+ }
181
+ }
182
+
183
+ /**
184
+ * Get team conventions summary
185
+ */
186
+ function getTeamConventions(projectPath) {
187
+ const authorsOutput = execGit("log --format='%an' | sort | uniq", projectPath);
188
+ if (!authorsOutput) return null;
189
+
190
+ const authors = authorsOutput.split("\n").filter(a => a && a !== "GitHub Actions");
191
+ const conventions = {
192
+ authors: [],
193
+ commonPatterns: {
194
+ imports: {},
195
+ formatting: {
196
+ semicolons: { with: 0, without: 0 },
197
+ quotes: { single: 0, double: 0 },
198
+ },
199
+ naming: {
200
+ components: "PascalCase",
201
+ functions: "camelCase",
202
+ },
203
+ },
204
+ recommendations: [],
205
+ };
206
+
207
+ // Analyze top contributors
208
+ for (const author of authors.slice(0, 5)) {
209
+ const style = analyzeAuthorStyle(author, projectPath, 10);
210
+ if (style) {
211
+ conventions.authors.push({
212
+ name: author,
213
+ filesContributed: style.filesAnalyzed,
214
+ patterns: style.patterns,
215
+ });
216
+ }
217
+ }
218
+
219
+ // Find common patterns
220
+ if (conventions.authors.length > 0) {
221
+ // Import preferences
222
+ const importCounts = { named: 0, default: 0, namespace: 0 };
223
+ for (const author of conventions.authors) {
224
+ importCounts.named += author.patterns.imports.named || 0;
225
+ importCounts.default += author.patterns.imports.default || 0;
226
+ importCounts.namespace += author.patterns.imports.namespace || 0;
227
+ }
228
+
229
+ const totalImports = importCounts.named + importCounts.default + importCounts.namespace;
230
+ if (totalImports > 0) {
231
+ if (importCounts.named / totalImports > 0.5) {
232
+ conventions.commonPatterns.imports.preferred = "named imports";
233
+ } else if (importCounts.default / totalImports > 0.5) {
234
+ conventions.commonPatterns.imports.preferred = "default imports";
235
+ }
236
+ }
237
+
238
+ // Formatting preferences
239
+ for (const author of conventions.authors) {
240
+ if (author.patterns.formatting.semiconPercentage > 70) {
241
+ conventions.commonPatterns.formatting.semicons.with++;
242
+ } else {
243
+ conventions.commonPatterns.formatting.semicons.without++;
244
+ }
245
+
246
+ if (author.patterns.formatting.singleQuotePercentage > 70) {
247
+ conventions.commonPatterns.formatting.quotes.single++;
248
+ } else {
249
+ conventions.commonPatterns.formatting.quotes.double++;
250
+ }
251
+ }
252
+
253
+ // Generate recommendations
254
+ if (conventions.commonPatterns.formatting.semicons.with > conventions.commonPatterns.formatting.semicons.without) {
255
+ conventions.recommendations.push("Use semicolons consistently (team preference)");
256
+ }
257
+
258
+ if (conventions.commonPatterns.formatting.quotes.single > conventions.commonPatterns.formatting.quotes.double) {
259
+ conventions.recommendations.push("Use single quotes for strings (team preference)");
260
+ }
261
+ }
262
+
263
+ return conventions;
264
+ }
265
+
266
+ /**
267
+ * Generate team conventions report
268
+ */
269
+ function generateTeamReport(projectPath) {
270
+ const conventions = getTeamConventions(projectPath);
271
+ if (!conventions) {
272
+ return {
273
+ available: false,
274
+ message: "No git history found",
275
+ };
276
+ }
277
+
278
+ const report = {
279
+ available: true,
280
+ summary: {
281
+ teamSize: conventions.authors.length,
282
+ totalFilesAnalyzed: conventions.authors.reduce((sum, a) => sum + a.filesContributed, 0),
283
+ },
284
+ conventions: conventions.commonPatterns,
285
+ contributors: conventions.authors.map(a => ({
286
+ name: a.name,
287
+ filesContributed: a.filesContributed,
288
+ style: {
289
+ imports: a.patterns.imports,
290
+ formatting: {
291
+ usesSemicolons: a.patterns.formatting.semiconPercentage > 70,
292
+ prefersSingleQuotes: a.patterns.formatting.singleQuotePercentage > 70,
293
+ },
294
+ typescript: {
295
+ usesExplicitTypes: a.patterns.typescript.explicitTypes > 0,
296
+ prefersInterfaces: a.patterns.typescript.interfaces > a.patterns.typescript.types,
297
+ },
298
+ },
299
+ })),
300
+ recommendations: conventions.recommendations,
301
+ };
302
+
303
+ return report;
304
+ }
305
+
306
+ module.exports = {
307
+ analyzeAuthorStyle,
308
+ getTeamConventions,
309
+ generateTeamReport,
310
+ };