@vibecheckai/cli 3.0.2 → 3.0.3
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.
- package/package.json +9 -1
- package/bin/cli-hygiene.js +0 -241
- package/bin/guardrail.js +0 -834
- package/bin/runners/cli-utils.js +0 -1070
- package/bin/runners/context/ai-task-decomposer.js +0 -337
- package/bin/runners/context/analyzer.js +0 -462
- package/bin/runners/context/api-contracts.js +0 -427
- package/bin/runners/context/context-diff.js +0 -342
- package/bin/runners/context/context-pruner.js +0 -291
- package/bin/runners/context/dependency-graph.js +0 -414
- package/bin/runners/context/generators/claude.js +0 -107
- package/bin/runners/context/generators/codex.js +0 -108
- package/bin/runners/context/generators/copilot.js +0 -119
- package/bin/runners/context/generators/cursor.js +0 -514
- package/bin/runners/context/generators/mcp.js +0 -151
- package/bin/runners/context/generators/windsurf.js +0 -180
- package/bin/runners/context/git-context.js +0 -302
- package/bin/runners/context/index.js +0 -1042
- package/bin/runners/context/insights.js +0 -173
- package/bin/runners/context/mcp-server/generate-rules.js +0 -337
- package/bin/runners/context/mcp-server/index.js +0 -1176
- package/bin/runners/context/mcp-server/package.json +0 -24
- package/bin/runners/context/memory.js +0 -200
- package/bin/runners/context/monorepo.js +0 -215
- package/bin/runners/context/multi-repo-federation.js +0 -404
- package/bin/runners/context/patterns.js +0 -253
- package/bin/runners/context/proof-context.js +0 -972
- package/bin/runners/context/security-scanner.js +0 -303
- package/bin/runners/context/semantic-search.js +0 -350
- package/bin/runners/context/shared.js +0 -264
- package/bin/runners/context/team-conventions.js +0 -310
- package/bin/runners/lib/ai-bridge.js +0 -416
- package/bin/runners/lib/analysis-core.js +0 -271
- package/bin/runners/lib/analyzers.js +0 -541
- package/bin/runners/lib/audit-bridge.js +0 -391
- package/bin/runners/lib/auth-truth.js +0 -193
- package/bin/runners/lib/auth.js +0 -215
- package/bin/runners/lib/backup.js +0 -62
- package/bin/runners/lib/billing.js +0 -107
- package/bin/runners/lib/claims.js +0 -118
- package/bin/runners/lib/cli-ui.js +0 -540
- package/bin/runners/lib/compliance-bridge-new.js +0 -0
- package/bin/runners/lib/compliance-bridge.js +0 -165
- package/bin/runners/lib/contracts/auth-contract.js +0 -194
- package/bin/runners/lib/contracts/env-contract.js +0 -178
- package/bin/runners/lib/contracts/external-contract.js +0 -198
- package/bin/runners/lib/contracts/guard.js +0 -168
- package/bin/runners/lib/contracts/index.js +0 -89
- package/bin/runners/lib/contracts/plan-validator.js +0 -311
- package/bin/runners/lib/contracts/route-contract.js +0 -192
- package/bin/runners/lib/detect.js +0 -89
- package/bin/runners/lib/doctor/autofix.js +0 -254
- package/bin/runners/lib/doctor/index.js +0 -37
- package/bin/runners/lib/doctor/modules/dependencies.js +0 -325
- package/bin/runners/lib/doctor/modules/index.js +0 -46
- package/bin/runners/lib/doctor/modules/network.js +0 -250
- package/bin/runners/lib/doctor/modules/project.js +0 -312
- package/bin/runners/lib/doctor/modules/runtime.js +0 -224
- package/bin/runners/lib/doctor/modules/security.js +0 -348
- package/bin/runners/lib/doctor/modules/system.js +0 -213
- package/bin/runners/lib/doctor/modules/vibecheck.js +0 -394
- package/bin/runners/lib/doctor/reporter.js +0 -262
- package/bin/runners/lib/doctor/service.js +0 -262
- package/bin/runners/lib/doctor/types.js +0 -113
- package/bin/runners/lib/doctor/ui.js +0 -263
- package/bin/runners/lib/doctor-enhanced.js +0 -233
- package/bin/runners/lib/doctor-v2.js +0 -608
- package/bin/runners/lib/enforcement.js +0 -72
|
@@ -1,264 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,310 +0,0 @@
|
|
|
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
|
-
};
|