@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,151 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP Context JSON Generator
|
|
3
|
-
* Generates .vibecheck/context.json for MCP servers
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Generate universal context JSON for MCP
|
|
8
|
-
*/
|
|
9
|
-
function generateContextJson(analysis, projectPath) {
|
|
10
|
-
const p = analysis.patterns || {};
|
|
11
|
-
const m = analysis.monorepo || {};
|
|
12
|
-
|
|
13
|
-
return JSON.stringify({
|
|
14
|
-
version: "3.0.0",
|
|
15
|
-
generatedAt: new Date().toISOString(),
|
|
16
|
-
generator: "vibecheck-context",
|
|
17
|
-
|
|
18
|
-
project: {
|
|
19
|
-
name: analysis.name,
|
|
20
|
-
path: projectPath,
|
|
21
|
-
framework: analysis.framework,
|
|
22
|
-
language: analysis.language,
|
|
23
|
-
architecture: analysis.architecture,
|
|
24
|
-
},
|
|
25
|
-
|
|
26
|
-
techStack: {
|
|
27
|
-
nextjs: analysis.hasNextjs,
|
|
28
|
-
react: analysis.hasReact,
|
|
29
|
-
typescript: analysis.hasTypescript,
|
|
30
|
-
prisma: analysis.hasPrisma,
|
|
31
|
-
tailwind: analysis.hasTailwind,
|
|
32
|
-
stateManagement: p.stateManagement,
|
|
33
|
-
validation: p.validation,
|
|
34
|
-
authentication: p.authentication,
|
|
35
|
-
dataFetching: p.dataFetching || [],
|
|
36
|
-
testing: p.testing || [],
|
|
37
|
-
styling: p.styling || [],
|
|
38
|
-
},
|
|
39
|
-
|
|
40
|
-
structure: {
|
|
41
|
-
directories: analysis.directories,
|
|
42
|
-
components: analysis.components,
|
|
43
|
-
apiRoutes: analysis.apiRoutes,
|
|
44
|
-
models: analysis.models,
|
|
45
|
-
},
|
|
46
|
-
|
|
47
|
-
patterns: {
|
|
48
|
-
hooks: p.hooks || [],
|
|
49
|
-
stateManagement: p.stateManagement,
|
|
50
|
-
validation: p.validation,
|
|
51
|
-
authentication: p.authentication,
|
|
52
|
-
dataFetching: p.dataFetching || [],
|
|
53
|
-
styling: p.styling || [],
|
|
54
|
-
testing: p.testing || [],
|
|
55
|
-
codeExamples: p.codeExamples || {},
|
|
56
|
-
},
|
|
57
|
-
|
|
58
|
-
antiPatterns: p.antiPatterns || [],
|
|
59
|
-
|
|
60
|
-
types: {
|
|
61
|
-
interfaces: analysis.types?.interfaces || [],
|
|
62
|
-
types: analysis.types?.types || [],
|
|
63
|
-
enums: analysis.types?.enums || [],
|
|
64
|
-
},
|
|
65
|
-
|
|
66
|
-
environment: {
|
|
67
|
-
files: analysis.envVars?.files || [],
|
|
68
|
-
variables: analysis.envVars?.variables || [],
|
|
69
|
-
},
|
|
70
|
-
|
|
71
|
-
scripts: analysis.scripts || [],
|
|
72
|
-
|
|
73
|
-
conventions: {
|
|
74
|
-
naming: analysis.conventions?.naming || {},
|
|
75
|
-
imports: analysis.imports?.importPatterns || [],
|
|
76
|
-
},
|
|
77
|
-
|
|
78
|
-
monorepo: m.isMonorepo ? {
|
|
79
|
-
type: m.type,
|
|
80
|
-
tools: m.tools || [],
|
|
81
|
-
workspaces: m.workspaces?.map(w => ({
|
|
82
|
-
name: w.name,
|
|
83
|
-
path: w.path,
|
|
84
|
-
description: w.description,
|
|
85
|
-
})) || [],
|
|
86
|
-
sharedPackages: m.sharedPackages || [],
|
|
87
|
-
} : null,
|
|
88
|
-
|
|
89
|
-
stats: {
|
|
90
|
-
totalFiles: analysis.stats?.totalFiles || 0,
|
|
91
|
-
totalLines: analysis.stats?.totalLines || 0,
|
|
92
|
-
byExtension: analysis.stats?.byExtension || {},
|
|
93
|
-
largestFiles: analysis.stats?.largestFiles || [],
|
|
94
|
-
},
|
|
95
|
-
|
|
96
|
-
rules: {
|
|
97
|
-
critical: [
|
|
98
|
-
"Never hardcode secrets - use environment variables",
|
|
99
|
-
"No mock data in production code",
|
|
100
|
-
`No 'any' types - use proper TypeScript types`,
|
|
101
|
-
"Follow existing patterns in the codebase",
|
|
102
|
-
"Use existing components before creating new ones",
|
|
103
|
-
"API routes must validate all input",
|
|
104
|
-
],
|
|
105
|
-
style: [
|
|
106
|
-
`File naming: ${analysis.conventions?.naming?.components || "PascalCase"} for components`,
|
|
107
|
-
"Use path aliases (@/) for imports",
|
|
108
|
-
analysis.hasTailwind ? "Use Tailwind CSS for styling" : null,
|
|
109
|
-
p.stateManagement ? `Use ${p.stateManagement} for state management` : null,
|
|
110
|
-
p.validation ? `Use ${p.validation} for validation` : null,
|
|
111
|
-
].filter(Boolean),
|
|
112
|
-
},
|
|
113
|
-
|
|
114
|
-
mcp: {
|
|
115
|
-
resources: [
|
|
116
|
-
{
|
|
117
|
-
uri: `file://${projectPath}/.vibecheck/context.json`,
|
|
118
|
-
name: "Project Context",
|
|
119
|
-
description: "Full project analysis and context",
|
|
120
|
-
mimeType: "application/json",
|
|
121
|
-
},
|
|
122
|
-
{
|
|
123
|
-
uri: `file://${projectPath}/.vibecheck/project-map.json`,
|
|
124
|
-
name: "Project Map",
|
|
125
|
-
description: "Complete project structure map",
|
|
126
|
-
mimeType: "application/json",
|
|
127
|
-
},
|
|
128
|
-
{
|
|
129
|
-
uri: `file://${projectPath}/.vibecheck/memory.json`,
|
|
130
|
-
name: "AI Memory",
|
|
131
|
-
description: "AI learning memory for this project",
|
|
132
|
-
mimeType: "application/json",
|
|
133
|
-
},
|
|
134
|
-
],
|
|
135
|
-
tools: [
|
|
136
|
-
{
|
|
137
|
-
name: "vibecheck.context",
|
|
138
|
-
description: "Regenerate project context",
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
name: "vibecheck.analyze",
|
|
142
|
-
description: "Analyze specific file or directory",
|
|
143
|
-
},
|
|
144
|
-
],
|
|
145
|
-
},
|
|
146
|
-
}, null, 2);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
module.exports = {
|
|
150
|
-
generateContextJson,
|
|
151
|
-
};
|
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Windsurf Rules Generator
|
|
3
|
-
* Generates .windsurf/rules/*.md files for Windsurf Cascade
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Generate Windsurf rules files
|
|
8
|
-
*/
|
|
9
|
-
function generateWindsurfRules(analysis) {
|
|
10
|
-
const rules = {};
|
|
11
|
-
const p = analysis.patterns || {};
|
|
12
|
-
const m = analysis.monorepo || {};
|
|
13
|
-
|
|
14
|
-
// Project context
|
|
15
|
-
rules["project-context"] = `# Project Context - ${analysis.name}
|
|
16
|
-
|
|
17
|
-
## Overview
|
|
18
|
-
- **Framework:** ${analysis.framework || "Unknown"}
|
|
19
|
-
- **Language:** ${analysis.language || "JavaScript"}
|
|
20
|
-
- **Architecture:** ${analysis.architecture}
|
|
21
|
-
${m.isMonorepo ? `- **Monorepo:** ${m.type} with ${m.workspaces?.length || 0} workspaces` : ""}
|
|
22
|
-
|
|
23
|
-
## Tech Stack
|
|
24
|
-
${analysis.hasNextjs ? "- Next.js" : ""}
|
|
25
|
-
${analysis.hasReact ? "- React" : ""}
|
|
26
|
-
${analysis.hasTypescript ? "- TypeScript" : ""}
|
|
27
|
-
${analysis.hasPrisma ? "- Prisma ORM" : ""}
|
|
28
|
-
${analysis.hasTailwind ? "- Tailwind CSS" : ""}
|
|
29
|
-
${p.stateManagement ? `- ${p.stateManagement}` : ""}
|
|
30
|
-
${p.validation ? `- ${p.validation}` : ""}
|
|
31
|
-
${p.authentication ? `- ${p.authentication}` : ""}
|
|
32
|
-
|
|
33
|
-
## Directory Structure
|
|
34
|
-
${analysis.directories.map(d => `- \`${d}/\``).join("\n")}
|
|
35
|
-
|
|
36
|
-
${m.isMonorepo && m.workspaces?.length > 0 ? `## Workspaces
|
|
37
|
-
${m.workspaces.slice(0, 10).map(w => `- \`${w.path}\` - ${w.name}`).join("\n")}
|
|
38
|
-
` : ""}
|
|
39
|
-
|
|
40
|
-
## Components (${analysis.components.length})
|
|
41
|
-
${analysis.components.slice(0, 25).map(c => `- ${c}`).join("\n") || "None detected"}
|
|
42
|
-
|
|
43
|
-
${p.hooks?.length > 0 ? `## Custom Hooks (${p.hooks.length})
|
|
44
|
-
${p.hooks.slice(0, 15).map(h => `- \`${h}\``).join("\n")}
|
|
45
|
-
` : ""}
|
|
46
|
-
|
|
47
|
-
## API Routes (${analysis.apiRoutes.length})
|
|
48
|
-
${analysis.apiRoutes.slice(0, 15).map(r => `- ${r}`).join("\n") || "None detected"}
|
|
49
|
-
|
|
50
|
-
${analysis.models.length > 0 ? `## Data Models
|
|
51
|
-
${analysis.models.map(m => `- ${m}`).join("\n")}
|
|
52
|
-
` : ""}
|
|
53
|
-
|
|
54
|
-
${analysis.envVars?.variables?.length > 0 ? `## Environment Variables
|
|
55
|
-
${analysis.envVars.variables.slice(0, 15).map(v => `- \`${v}\``).join("\n")}
|
|
56
|
-
` : ""}
|
|
57
|
-
|
|
58
|
-
---
|
|
59
|
-
*Context Enhanced by vibecheck AI*
|
|
60
|
-
`;
|
|
61
|
-
|
|
62
|
-
// Coding standards
|
|
63
|
-
rules["coding-standards"] = `# Coding Standards
|
|
64
|
-
|
|
65
|
-
## File Naming
|
|
66
|
-
- Components: ${analysis.conventions.naming.components || "PascalCase"} (e.g., \`Button.tsx\`)
|
|
67
|
-
- Utilities: camelCase (e.g., \`formatDate.ts\`)
|
|
68
|
-
- Types: \`.types.ts\` or \`.d.ts\` suffix
|
|
69
|
-
|
|
70
|
-
## Import Order
|
|
71
|
-
1. React/Next.js imports
|
|
72
|
-
2. Third-party libraries
|
|
73
|
-
3. Internal components (\`@/components/\`)
|
|
74
|
-
4. Internal utilities (\`@/lib/\`, \`@/utils/\`)
|
|
75
|
-
5. Types
|
|
76
|
-
6. Styles
|
|
77
|
-
|
|
78
|
-
## Code Style
|
|
79
|
-
${analysis.hasTypescript ? "- TypeScript with strict mode enabled" : "- JavaScript with JSDoc comments"}
|
|
80
|
-
- Functional components with hooks
|
|
81
|
-
- Path aliases (\`@/\`) for imports
|
|
82
|
-
${analysis.hasTailwind ? "- Tailwind CSS for styling" : ""}
|
|
83
|
-
${p.stateManagement ? `- ${p.stateManagement} for state management` : ""}
|
|
84
|
-
${p.validation ? `- ${p.validation} for validation` : ""}
|
|
85
|
-
|
|
86
|
-
## Critical Rules
|
|
87
|
-
|
|
88
|
-
1. **No hardcoded secrets** - Use environment variables
|
|
89
|
-
2. **No \`any\` types** - Use proper TypeScript types
|
|
90
|
-
3. **No mock data in production** - Real API endpoints only
|
|
91
|
-
4. **Validate all inputs** - Never trust client data
|
|
92
|
-
5. **Use existing components** - Check before creating new ones
|
|
93
|
-
${p.hooks?.length ? `6. **Use existing hooks** - ${p.hooks.slice(0, 3).join(", ")}...` : ""}
|
|
94
|
-
|
|
95
|
-
${p.antiPatterns?.length > 0 ? `## ⚠️ Avoid These
|
|
96
|
-
${p.antiPatterns.map(ap => `- ${ap.message}: ${ap.suggestion}`).join("\n")}
|
|
97
|
-
` : ""}
|
|
98
|
-
|
|
99
|
-
## When Creating New Files
|
|
100
|
-
1. Check if similar file exists
|
|
101
|
-
2. Place in correct directory
|
|
102
|
-
3. Follow naming conventions
|
|
103
|
-
4. Add proper types
|
|
104
|
-
5. Use existing patterns
|
|
105
|
-
`;
|
|
106
|
-
|
|
107
|
-
// API patterns
|
|
108
|
-
if (analysis.apiRoutes.length > 0 || analysis.hasPrisma) {
|
|
109
|
-
rules["api-patterns"] = `# API & Data Patterns
|
|
110
|
-
|
|
111
|
-
${analysis.hasPrisma ? `## Database (Prisma)
|
|
112
|
-
|
|
113
|
-
### Available Models
|
|
114
|
-
${analysis.models.map(m => `- ${m}`).join("\n")}
|
|
115
|
-
|
|
116
|
-
### Usage
|
|
117
|
-
\`\`\`typescript
|
|
118
|
-
import { prisma } from '@/lib/prisma'
|
|
119
|
-
|
|
120
|
-
// Query
|
|
121
|
-
const users = await prisma.user.findMany()
|
|
122
|
-
|
|
123
|
-
// Create with validation
|
|
124
|
-
const user = await prisma.user.create({
|
|
125
|
-
data: validatedInput
|
|
126
|
-
})
|
|
127
|
-
\`\`\`
|
|
128
|
-
|
|
129
|
-
### Rules
|
|
130
|
-
- Always import from \`@/lib/prisma\`
|
|
131
|
-
- Use transactions for multi-step operations
|
|
132
|
-
- Handle errors gracefully
|
|
133
|
-
- Never expose raw errors to client
|
|
134
|
-
` : ""}
|
|
135
|
-
|
|
136
|
-
${analysis.apiRoutes.length > 0 ? `## API Routes
|
|
137
|
-
|
|
138
|
-
### Existing Endpoints
|
|
139
|
-
${analysis.apiRoutes.slice(0, 20).map(r => `- \`${r}\``).join("\n")}
|
|
140
|
-
|
|
141
|
-
### API Response Pattern
|
|
142
|
-
\`\`\`typescript
|
|
143
|
-
// Success
|
|
144
|
-
return Response.json({ data, success: true })
|
|
145
|
-
|
|
146
|
-
// Error
|
|
147
|
-
return Response.json({ error: message }, { status: 400 })
|
|
148
|
-
\`\`\`
|
|
149
|
-
|
|
150
|
-
### Validation
|
|
151
|
-
${p.validation ? `Use ${p.validation} for input validation:
|
|
152
|
-
\`\`\`typescript
|
|
153
|
-
const schema = z.object({ name: z.string() })
|
|
154
|
-
const data = schema.parse(await req.json())
|
|
155
|
-
\`\`\`` : "Always validate input before processing"}
|
|
156
|
-
` : ""}
|
|
157
|
-
|
|
158
|
-
${p.dataFetching?.length ? `## Data Fetching: ${p.dataFetching.join(", ")}
|
|
159
|
-
|
|
160
|
-
${p.dataFetching.includes("TanStack Query") ? `### TanStack Query
|
|
161
|
-
\`\`\`typescript
|
|
162
|
-
const { data, isLoading } = useQuery({
|
|
163
|
-
queryKey: ['resource'],
|
|
164
|
-
queryFn: fetchResource,
|
|
165
|
-
})
|
|
166
|
-
\`\`\`` : ""}
|
|
167
|
-
${p.dataFetching.includes("SWR") ? `### SWR
|
|
168
|
-
\`\`\`typescript
|
|
169
|
-
const { data, error } = useSWR('/api/resource', fetcher)
|
|
170
|
-
\`\`\`` : ""}
|
|
171
|
-
` : ""}
|
|
172
|
-
`;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
return rules;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
module.exports = {
|
|
179
|
-
generateWindsurfRules,
|
|
180
|
-
};
|
|
@@ -1,302 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Git-Aware Context Module
|
|
3
|
-
* Extracts commit patterns, branch conventions, and PR templates
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const fs = require("fs");
|
|
7
|
-
const path = require("path");
|
|
8
|
-
const { execSync } = require("child_process");
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Check if directory is a git repository
|
|
12
|
-
*/
|
|
13
|
-
function isGitRepo(projectPath) {
|
|
14
|
-
return fs.existsSync(path.join(projectPath, ".git"));
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Execute git command safely
|
|
19
|
-
*/
|
|
20
|
-
function execGit(command, cwd = process.cwd()) {
|
|
21
|
-
try {
|
|
22
|
-
return execSync(command, { cwd, encoding: "utf-8" }).trim();
|
|
23
|
-
} catch {
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Get recent commits with patterns
|
|
30
|
-
*/
|
|
31
|
-
function getRecentCommits(projectPath, limit = 20) {
|
|
32
|
-
if (!isGitRepo(projectPath)) return null;
|
|
33
|
-
|
|
34
|
-
const output = execGit(`log --oneline -${limit}`, projectPath);
|
|
35
|
-
if (!output) return null;
|
|
36
|
-
|
|
37
|
-
const commits = output.split("\n").map(line => {
|
|
38
|
-
const [hash, ...messageParts] = line.split(" ");
|
|
39
|
-
const message = messageParts.join(" ");
|
|
40
|
-
|
|
41
|
-
// Detect patterns
|
|
42
|
-
const patterns = {
|
|
43
|
-
hasTicket: /(FEAT|FIX|CHORE|DOCS|REFACTOR|TEST|PERF)-\d+/i.test(message),
|
|
44
|
-
ticketType: message.match(/(FEAT|FIX|CHORE|DOCS|REFACTOR|TEST|PERF)-\d+/i)?.[1],
|
|
45
|
-
isBreaking: /BREAKING CHANGE/i.test(message),
|
|
46
|
-
hasScope: /\([^)]+\):/.test(message),
|
|
47
|
-
scope: message.match(/\(([^)]+)\):/)?.[1],
|
|
48
|
-
isWIP: /wip|work in progress/i.test(message),
|
|
49
|
-
isMerge: /^Merge /i.test(message),
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
return {
|
|
53
|
-
hash,
|
|
54
|
-
message,
|
|
55
|
-
patterns,
|
|
56
|
-
};
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
// Analyze patterns
|
|
60
|
-
const analysis = {
|
|
61
|
-
totalCommits: commits.length,
|
|
62
|
-
withTickets: commits.filter(c => c.patterns.hasTicket).length,
|
|
63
|
-
breakingChanges: commits.filter(c => c.patterns.isBreaking).length,
|
|
64
|
-
withScopes: commits.filter(c => c.patterns.hasScope).length,
|
|
65
|
-
commonScopes: {},
|
|
66
|
-
ticketTypes: {},
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
// Count common patterns
|
|
70
|
-
for (const commit of commits) {
|
|
71
|
-
if (commit.patterns.scope) {
|
|
72
|
-
analysis.commonScopes[commit.patterns.scope] = (analysis.commonScopes[commit.patterns.scope] || 0) + 1;
|
|
73
|
-
}
|
|
74
|
-
if (commit.patterns.ticketType) {
|
|
75
|
-
analysis.ticketTypes[commit.patterns.ticketType] = (analysis.ticketTypes[commit.patterns.ticketType] || 0) + 1;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return {
|
|
80
|
-
commits,
|
|
81
|
-
analysis,
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Get branch information and conventions
|
|
87
|
-
*/
|
|
88
|
-
function getBranchInfo(projectPath) {
|
|
89
|
-
if (!isGitRepo(projectPath)) return null;
|
|
90
|
-
|
|
91
|
-
const currentBranch = execGit("branch --show-current", projectPath);
|
|
92
|
-
const allBranches = execGit("branch -a", projectPath);
|
|
93
|
-
const defaultBranch = execGit("symbolic-ref refs/remotes/origin/HEAD | sed 's@^.*/@@'", projectPath);
|
|
94
|
-
|
|
95
|
-
if (!currentBranch || !allBranches) return null;
|
|
96
|
-
|
|
97
|
-
const branches = allBranches.split("\n").map(b => b.replace("*", "").trim());
|
|
98
|
-
const localBranches = branches.filter(b => !b.startsWith("remotes/"));
|
|
99
|
-
const remoteBranches = branches.filter(b => b.startsWith("remotes/")).map(b => b.replace("remotes/origin/", ""));
|
|
100
|
-
|
|
101
|
-
// Analyze naming patterns
|
|
102
|
-
const patterns = {
|
|
103
|
-
hasFeaturePrefix: localBranches.some(b => /^feature\//.test(b)),
|
|
104
|
-
hasBugfixPrefix: localBranches.some(b => /^bugfix\//.test(b) || /^fix\//.test(b)),
|
|
105
|
-
hasHotfixPrefix: localBranches.some(b => /^hotfix\//.test(b)),
|
|
106
|
-
hasIssueNumber: localBranches.some(b => /\d+$/.test(b)),
|
|
107
|
-
usesSlashes: localBranches.some(b => b.includes("/")),
|
|
108
|
-
usesKebabCase: localBranches.some(b => /-/.test(b)),
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
// Common prefixes
|
|
112
|
-
const prefixes = {};
|
|
113
|
-
for (const branch of localBranches) {
|
|
114
|
-
if (branch.includes("/")) {
|
|
115
|
-
const prefix = branch.split("/")[0];
|
|
116
|
-
prefixes[prefix] = (prefixes[prefix] || 0) + 1;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return {
|
|
121
|
-
current: currentBranch,
|
|
122
|
-
default: defaultBranch,
|
|
123
|
-
total: localBranches.length,
|
|
124
|
-
local: localBranches,
|
|
125
|
-
remote: remoteBranches,
|
|
126
|
-
patterns,
|
|
127
|
-
commonPrefixes: Object.entries(prefixes)
|
|
128
|
-
.sort((a, b) => b[1] - a[1])
|
|
129
|
-
.slice(0, 5)
|
|
130
|
-
.map(([name, count]) => ({ name, count })),
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Get PR template information
|
|
136
|
-
*/
|
|
137
|
-
function getPRTemplate(projectPath) {
|
|
138
|
-
const templates = [
|
|
139
|
-
".github/pull_request_template.md",
|
|
140
|
-
".github/PULL_REQUEST_TEMPLATE.md",
|
|
141
|
-
"PULL_REQUEST_TEMPLATE.md",
|
|
142
|
-
];
|
|
143
|
-
|
|
144
|
-
for (const template of templates) {
|
|
145
|
-
const templatePath = path.join(projectPath, template);
|
|
146
|
-
if (fs.existsSync(templatePath)) {
|
|
147
|
-
try {
|
|
148
|
-
const content = fs.readFileSync(templatePath, "utf-8");
|
|
149
|
-
|
|
150
|
-
// Analyze template sections
|
|
151
|
-
const sections = [];
|
|
152
|
-
const lines = content.split("\n");
|
|
153
|
-
let currentSection = null;
|
|
154
|
-
|
|
155
|
-
for (const line of lines) {
|
|
156
|
-
if (line.startsWith("##") || line.startsWith("###")) {
|
|
157
|
-
if (currentSection) {
|
|
158
|
-
sections.push(currentSection);
|
|
159
|
-
}
|
|
160
|
-
currentSection = {
|
|
161
|
-
title: line.replace(/^#+\s*/, ""),
|
|
162
|
-
required: line.includes("Required") || line.includes("MUST"),
|
|
163
|
-
content: [],
|
|
164
|
-
};
|
|
165
|
-
} else if (currentSection && line.trim()) {
|
|
166
|
-
currentSection.content.push(line);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if (currentSection) {
|
|
171
|
-
sections.push(currentSection);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
return {
|
|
175
|
-
file: template,
|
|
176
|
-
sections,
|
|
177
|
-
hasRequiredSections: sections.some(s => s.required),
|
|
178
|
-
sectionCount: sections.length,
|
|
179
|
-
};
|
|
180
|
-
} catch {}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
return null;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Get git blame information for team patterns
|
|
189
|
-
*/
|
|
190
|
-
function getTeamPatterns(projectPath, fileLimit = 10) {
|
|
191
|
-
if (!isGitRepo(projectPath)) return null;
|
|
192
|
-
|
|
193
|
-
// Get recent files
|
|
194
|
-
const output = execGit("ls-files -z", projectPath);
|
|
195
|
-
if (!output) return null;
|
|
196
|
-
|
|
197
|
-
const files = output.split("\0")
|
|
198
|
-
.filter(f => f && (f.endsWith(".ts") || f.endsWith(".tsx") || f.endsWith(".js")))
|
|
199
|
-
.slice(0, fileLimit);
|
|
200
|
-
|
|
201
|
-
const authorStats = {};
|
|
202
|
-
const patterns = {
|
|
203
|
-
commonImports: {},
|
|
204
|
-
commentStyles: {},
|
|
205
|
-
functionPatterns: {},
|
|
206
|
-
};
|
|
207
|
-
|
|
208
|
-
for (const file of files) {
|
|
209
|
-
const blameOutput = execGit(`blame --line-porcelain "${file}"`, projectPath);
|
|
210
|
-
if (!blameOutput) continue;
|
|
211
|
-
|
|
212
|
-
const lines = blameOutput.split("\n");
|
|
213
|
-
let currentAuthor = null;
|
|
214
|
-
|
|
215
|
-
for (const line of lines) {
|
|
216
|
-
if (line.startsWith("author ")) {
|
|
217
|
-
currentAuthor = line.slice(7);
|
|
218
|
-
authorStats[currentAuthor] = (authorStats[currentAuthor] || 0) + 1;
|
|
219
|
-
} else if (line.startsWith("\t") && currentAuthor) {
|
|
220
|
-
const codeLine = line.slice(1);
|
|
221
|
-
|
|
222
|
-
// Track patterns by author
|
|
223
|
-
if (!patterns.authorPatterns) patterns.authorPatterns = {};
|
|
224
|
-
if (!patterns.authorPatterns[currentAuthor]) {
|
|
225
|
-
patterns.authorPatterns[currentAuthor] = {
|
|
226
|
-
imports: [],
|
|
227
|
-
comments: [],
|
|
228
|
-
functions: [],
|
|
229
|
-
};
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// Import patterns
|
|
233
|
-
if (codeLine.includes("import ")) {
|
|
234
|
-
const importType = codeLine.includes(" from ") ? "named" : "default";
|
|
235
|
-
patterns.authorPatterns[currentAuthor].imports.push(importType);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// Comment patterns
|
|
239
|
-
if (codeLine.includes("//")) {
|
|
240
|
-
patterns.authorPatterns[currentAuthor].comments.push(codeLine);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// Function patterns
|
|
244
|
-
if (codeLine.includes("function ") || codeLine.includes("=>")) {
|
|
245
|
-
patterns.authorPatterns[currentAuthor].functions.push(codeLine);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// Analyze top contributors
|
|
252
|
-
const topAuthors = Object.entries(authorStats)
|
|
253
|
-
.sort((a, b) => b[1] - a[1])
|
|
254
|
-
.slice(0, 5)
|
|
255
|
-
.map(([name, lines]) => ({ name, lines }));
|
|
256
|
-
|
|
257
|
-
return {
|
|
258
|
-
topAuthors,
|
|
259
|
-
totalAuthors: Object.keys(authorStats).length,
|
|
260
|
-
patterns,
|
|
261
|
-
};
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
/**
|
|
265
|
-
* Get complete git context
|
|
266
|
-
*/
|
|
267
|
-
function getGitContext(projectPath) {
|
|
268
|
-
if (!isGitRepo(projectPath)) {
|
|
269
|
-
return {
|
|
270
|
-
isRepo: false,
|
|
271
|
-
message: "Not a git repository",
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
const commits = getRecentCommits(projectPath);
|
|
276
|
-
const branches = getBranchInfo(projectPath);
|
|
277
|
-
const prTemplate = getPRTemplate(projectPath);
|
|
278
|
-
const teamPatterns = getTeamPatterns(projectPath);
|
|
279
|
-
|
|
280
|
-
return {
|
|
281
|
-
isRepo: true,
|
|
282
|
-
commits,
|
|
283
|
-
branches,
|
|
284
|
-
prTemplate,
|
|
285
|
-
teamPatterns,
|
|
286
|
-
conventions: {
|
|
287
|
-
commitMessages: commits?.analysis || null,
|
|
288
|
-
branchNaming: branches?.patterns || null,
|
|
289
|
-
hasPRTemplate: !!prTemplate,
|
|
290
|
-
teamSize: teamPatterns?.totalAuthors || 0,
|
|
291
|
-
},
|
|
292
|
-
};
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
module.exports = {
|
|
296
|
-
isGitRepo,
|
|
297
|
-
getGitContext,
|
|
298
|
-
getRecentCommits,
|
|
299
|
-
getBranchInfo,
|
|
300
|
-
getPRTemplate,
|
|
301
|
-
getTeamPatterns,
|
|
302
|
-
};
|