@vibecodeqa/cli 0.20.0 → 0.21.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.
- package/dist/runners/best-practices.js +126 -0
- package/package.json +1 -1
|
@@ -178,6 +178,132 @@ export function runBestPractices(cwd) {
|
|
|
178
178
|
else {
|
|
179
179
|
issues.push({ severity: "info", message: "No Dependabot/Renovate — dependency updates are manual and often forgotten", rule: "automated-deps" });
|
|
180
180
|
}
|
|
181
|
+
// ── 5. Code Quality Tooling ──
|
|
182
|
+
// Linter configured
|
|
183
|
+
practices++;
|
|
184
|
+
if (has("biome.json") || has(".eslintrc.json") || has(".eslintrc.js") || has("eslint.config.js") || has("eslint.config.ts") || has("analysis_options.yaml")) {
|
|
185
|
+
followed++;
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
issues.push({ severity: "warning", message: "No linter config (ESLint/Biome/dart analyze) — code style not enforced", rule: "linter-config" });
|
|
189
|
+
}
|
|
190
|
+
// Formatter configured
|
|
191
|
+
practices++;
|
|
192
|
+
if (has("biome.json") || has(".prettierrc") || has(".prettierrc.json") || has("prettier.config.js") || has(".editorconfig")) {
|
|
193
|
+
followed++;
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
issues.push({ severity: "info", message: "No formatter config (Prettier/Biome/.editorconfig) — inconsistent code formatting", rule: "formatter-config" });
|
|
197
|
+
}
|
|
198
|
+
// TypeScript strict mode
|
|
199
|
+
practices++;
|
|
200
|
+
const tsconfig = read("tsconfig.json");
|
|
201
|
+
if (!tsconfig || tsconfig.includes('"strict": true') || tsconfig.includes('"strict":true')) {
|
|
202
|
+
followed++;
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
issues.push({ severity: "info", message: "TypeScript strict mode not enabled — allows implicit any and null errors", rule: "ts-strict-mode" });
|
|
206
|
+
}
|
|
207
|
+
// ── 6. Testing Best Practices ──
|
|
208
|
+
// Test script exists
|
|
209
|
+
practices++;
|
|
210
|
+
if (pkg.includes('"test"') || has("pubspec.yaml")) {
|
|
211
|
+
followed++;
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
issues.push({ severity: "warning", message: "No test script in package.json — testing not configured", rule: "test-script" });
|
|
215
|
+
}
|
|
216
|
+
// Coverage configured
|
|
217
|
+
practices++;
|
|
218
|
+
if (pkg.includes("coverage") || has("vitest.config.ts") || has("jest.config.ts") || has("jest.config.js")) {
|
|
219
|
+
followed++;
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
issues.push({ severity: "info", message: "No test coverage configuration — coverage thresholds not enforced", rule: "coverage-config" });
|
|
223
|
+
}
|
|
224
|
+
// ── 7. Docker / Deployment ──
|
|
225
|
+
// Dockerfile best practices (if Docker is used)
|
|
226
|
+
if (has("Dockerfile") || has("docker-compose.yml") || has("docker-compose.yaml")) {
|
|
227
|
+
practices++;
|
|
228
|
+
const dockerfile = read("Dockerfile");
|
|
229
|
+
if (dockerfile.includes("FROM") && !dockerfile.includes("latest")) {
|
|
230
|
+
followed++;
|
|
231
|
+
}
|
|
232
|
+
else if (dockerfile.includes(":latest")) {
|
|
233
|
+
issues.push({ severity: "warning", message: "Dockerfile uses :latest tag — pin to a specific version for reproducible builds", rule: "docker-pin-version" });
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
followed++;
|
|
237
|
+
}
|
|
238
|
+
// Multi-stage build
|
|
239
|
+
practices++;
|
|
240
|
+
const fromCount = (dockerfile.match(/^FROM /gm) || []).length;
|
|
241
|
+
if (fromCount >= 2) {
|
|
242
|
+
followed++;
|
|
243
|
+
}
|
|
244
|
+
else if (dockerfile.length > 100) {
|
|
245
|
+
issues.push({ severity: "info", message: "Dockerfile is single-stage — consider multi-stage to reduce image size", rule: "docker-multi-stage" });
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
followed++;
|
|
249
|
+
}
|
|
250
|
+
// .dockerignore
|
|
251
|
+
practices++;
|
|
252
|
+
if (has(".dockerignore")) {
|
|
253
|
+
followed++;
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
issues.push({ severity: "info", message: "No .dockerignore — node_modules and build artifacts will bloat Docker image", rule: "dockerignore" });
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
// ── 8. Git Practices ──
|
|
260
|
+
// .gitignore is comprehensive
|
|
261
|
+
practices++;
|
|
262
|
+
const gitignore = read(".gitignore");
|
|
263
|
+
if (gitignore.includes("node_modules") || gitignore.includes(".dart_tool") || gitignore.includes("build/")) {
|
|
264
|
+
followed++;
|
|
265
|
+
}
|
|
266
|
+
else if (gitignore) {
|
|
267
|
+
issues.push({ severity: "info", message: ".gitignore exists but may be incomplete — ensure build artifacts are excluded", rule: "gitignore-complete" });
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
followed++; // no gitignore = handled by structure check
|
|
271
|
+
}
|
|
272
|
+
// Conventional commits signal (commitlint or similar)
|
|
273
|
+
practices++;
|
|
274
|
+
if (deps.commitlint || deps["@commitlint/cli"] || has("commitlint.config.js") || has(".commitlintrc.json") || has(".changeset")) {
|
|
275
|
+
followed++;
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
issues.push({ severity: "info", message: "No commit convention enforcement (commitlint/changesets) — changelog generation is manual", rule: "conventional-commits" });
|
|
279
|
+
}
|
|
280
|
+
// ── 9. Monitoring & Observability ──
|
|
281
|
+
// Error tracking (Sentry, Bugsnag, etc.) — only for apps/servers, not CLI tools
|
|
282
|
+
const isApp = deps.react || deps.vue || deps.svelte || deps.express || deps.fastify || deps.hono || deps.next || deps.nuxt;
|
|
283
|
+
if (isApp) {
|
|
284
|
+
practices++;
|
|
285
|
+
if (deps["@sentry/node"] || deps["@sentry/react"] || deps["@sentry/browser"] || deps.bugsnag || deps["@bugsnag/js"]) {
|
|
286
|
+
followed++;
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
issues.push({ severity: "info", message: "No error tracking (Sentry/Bugsnag) — production errors may go unnoticed", rule: "error-tracking" });
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
// ── 10. API & Configuration ──
|
|
293
|
+
// Environment validation (zod, joi, envalid)
|
|
294
|
+
practices++;
|
|
295
|
+
if (deps.zod || deps.joi || deps.envalid || deps["@t3-oss/env-core"] || deps["@t3-oss/env-nextjs"]) {
|
|
296
|
+
followed++;
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
const hasEnvUsage = pkg.includes("process.env") || read("src/index.ts").includes("process.env") || read("src/main.ts").includes("process.env");
|
|
300
|
+
if (hasEnvUsage) {
|
|
301
|
+
issues.push({ severity: "info", message: "Uses env vars but no validation library (zod/envalid) — missing vars crash at runtime", rule: "env-validation" });
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
followed++;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
181
307
|
// ── Score ──
|
|
182
308
|
const pct = practices > 0 ? Math.round((followed / practices) * 100) : 100;
|
|
183
309
|
const score = pct;
|