@safetnsr/vet 1.8.2 → 1.8.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/dist/checks/config.js +7 -1
- package/dist/checks/debt.js +26 -5
- package/dist/checks/diff.js +1 -1
- package/dist/checks/history.js +1 -1
- package/dist/cli.js +6 -6
- package/package.json +1 -1
package/dist/checks/config.js
CHANGED
|
@@ -61,6 +61,10 @@ function analyzeConfig(cwd, configFile, agentName, files) {
|
|
|
61
61
|
if (completenessChecks > 0) {
|
|
62
62
|
completenessScore = Math.round((completenessHits / completenessChecks) * 10);
|
|
63
63
|
}
|
|
64
|
+
else {
|
|
65
|
+
// No framework dependencies detected — completeness is not applicable, don't penalize
|
|
66
|
+
completenessScore = 10;
|
|
67
|
+
}
|
|
64
68
|
// Consistency: cross-reference with actual project config
|
|
65
69
|
let consistencyScore = 10;
|
|
66
70
|
const tsconfig = readFile(join(cwd, 'tsconfig.json'));
|
|
@@ -80,7 +84,9 @@ function analyzeConfig(cwd, configFile, agentName, files) {
|
|
|
80
84
|
catch { /* */ }
|
|
81
85
|
}
|
|
82
86
|
// Check if config mentions testing but no test framework installed
|
|
83
|
-
|
|
87
|
+
// Also check if using Node's built-in test runner (node:test)
|
|
88
|
+
const usesNodeTest = contentLower.includes('node:test') || contentLower.includes('node test runner') || contentLower.includes('node built-in test');
|
|
89
|
+
if ((contentLower.includes('test') || contentLower.includes('spec')) && !deps.vitest && !deps.jest && !deps.mocha && !deps.ava && !usesNodeTest) {
|
|
84
90
|
consistencyScore -= 2;
|
|
85
91
|
suggestions.push('config mentions tests but no test framework in dependencies');
|
|
86
92
|
}
|
package/dist/checks/debt.js
CHANGED
|
@@ -128,6 +128,18 @@ function extractFunctions(source, file) {
|
|
|
128
128
|
return fns;
|
|
129
129
|
}
|
|
130
130
|
// ── A) Near-duplicate detection ──────────────────────────────────────────────
|
|
131
|
+
/** Check if functions are in a numbered spec implementation pattern (e.g. asi01, asi02...) */
|
|
132
|
+
function isSpecPattern(group) {
|
|
133
|
+
if (group.length < 3)
|
|
134
|
+
return false;
|
|
135
|
+
const dirs = new Set(group.map(f => f.file.substring(0, f.file.lastIndexOf('/'))));
|
|
136
|
+
if (dirs.size !== 1)
|
|
137
|
+
return false; // must be same directory
|
|
138
|
+
// Check if filenames follow a numbered pattern
|
|
139
|
+
const bases = group.map(f => f.file.substring(f.file.lastIndexOf('/') + 1));
|
|
140
|
+
const numbered = bases.filter(b => /\d{2}/.test(b));
|
|
141
|
+
return numbered.length >= 3;
|
|
142
|
+
}
|
|
131
143
|
function findDuplicates(allFuncs) {
|
|
132
144
|
const issues = [];
|
|
133
145
|
const groups = new Map();
|
|
@@ -138,15 +150,21 @@ function findDuplicates(allFuncs) {
|
|
|
138
150
|
groups.set(fn.hash, existing);
|
|
139
151
|
}
|
|
140
152
|
const reported = new Set();
|
|
141
|
-
// Exact duplicates
|
|
153
|
+
// Exact duplicates (only flag if normalized body is substantial)
|
|
142
154
|
for (const [, group] of groups) {
|
|
143
155
|
if (group.length < 2)
|
|
144
156
|
continue;
|
|
157
|
+
// Skip if the normalized body is too generic (short functions normalize to same hash easily)
|
|
158
|
+
if (group[0].normalized.length < 65)
|
|
159
|
+
continue;
|
|
145
160
|
// Deduplicate by name+file
|
|
146
161
|
const key = group.map(f => `${f.file}:${f.name}`).sort().join('|');
|
|
147
162
|
if (reported.has(key))
|
|
148
163
|
continue;
|
|
149
164
|
reported.add(key);
|
|
165
|
+
// Skip groups that follow a numbered spec pattern (e.g., ASI01-ASI10 checks)
|
|
166
|
+
if (isSpecPattern(group))
|
|
167
|
+
continue;
|
|
150
168
|
const locations = group.map(f => `${f.name} (${f.file}:${f.line})`).join(', ');
|
|
151
169
|
issues.push({
|
|
152
170
|
severity: 'warning',
|
|
@@ -170,7 +188,7 @@ function findDuplicates(allFuncs) {
|
|
|
170
188
|
if (a.normalized.length < 30 || b.normalized.length < 30)
|
|
171
189
|
continue;
|
|
172
190
|
const sim = similarity(a.normalized, b.normalized);
|
|
173
|
-
if (sim > 0.
|
|
191
|
+
if (sim > 0.92) {
|
|
174
192
|
const key = [a.file + ':' + a.name, b.file + ':' + b.name].sort().join('|');
|
|
175
193
|
if (reported.has(key))
|
|
176
194
|
continue;
|
|
@@ -358,9 +376,12 @@ export async function checkDebt(cwd, ignore) {
|
|
|
358
376
|
issues.push(...driftIssues);
|
|
359
377
|
// ── Scoring ──────────────────────────────────────────────────────────────
|
|
360
378
|
const dupPenalty = Math.min(50, dupIssues.length * 8);
|
|
361
|
-
const
|
|
362
|
-
const
|
|
363
|
-
const
|
|
379
|
+
const orphanWarnings = orphanIssues.filter(i => i.severity === 'warning');
|
|
380
|
+
const orphanPenalty = Math.min(30, orphanWarnings.length * 5);
|
|
381
|
+
const wrapperWarnings = wrapperIssues.filter(i => i.severity === 'warning');
|
|
382
|
+
const driftWarnings = driftIssues.filter(i => i.severity === 'warning');
|
|
383
|
+
const wrapperPenalty = Math.min(15, wrapperWarnings.length * 3);
|
|
384
|
+
const driftPenalty = Math.min(10, driftWarnings.length * 2);
|
|
364
385
|
const rawScore = 100 - dupPenalty - orphanPenalty - wrapperPenalty - driftPenalty;
|
|
365
386
|
const finalScore = Math.max(0, Math.round(rawScore));
|
|
366
387
|
// ── Summary ──────────────────────────────────────────────────────────────
|
package/dist/checks/diff.js
CHANGED
|
@@ -124,7 +124,7 @@ export function checkDiff(cwd, opts = {}) {
|
|
|
124
124
|
// Extract imported name
|
|
125
125
|
const nameMatch = imp.text.match(/import\s+(?:\{([^}]+)\}|(\w+))/);
|
|
126
126
|
if (nameMatch) {
|
|
127
|
-
const names = (nameMatch[1] || nameMatch[2] || '').split(',').map(n => n.trim().split(' as ').pop()?.trim()).filter(Boolean);
|
|
127
|
+
const names = (nameMatch[1] || nameMatch[2] || '').split(',').map(n => n.trim().replace(/^type\s+/, '').split(' as ').pop()?.trim()).filter(Boolean);
|
|
128
128
|
for (const name of names) {
|
|
129
129
|
if (!name || name.length < 2)
|
|
130
130
|
continue;
|
package/dist/checks/history.js
CHANGED
|
@@ -66,7 +66,7 @@ export function checkHistory(cwd) {
|
|
|
66
66
|
const aiPct = commits.length > 0 ? Math.round((aiCommits / commits.length) * 100) : 0;
|
|
67
67
|
const infos = issues.filter(i => i.severity === 'info').length;
|
|
68
68
|
const warnings = issues.filter(i => i.severity === 'warning').length;
|
|
69
|
-
const score = Math.max(0, Math.min(100, 100 - warnings * 10
|
|
69
|
+
const score = Math.max(0, Math.min(100, 100 - warnings * 10));
|
|
70
70
|
return {
|
|
71
71
|
name: 'history',
|
|
72
72
|
score: Math.round(score),
|
package/dist/cli.js
CHANGED
|
@@ -250,13 +250,13 @@ if (isBadge && !isWatch) {
|
|
|
250
250
|
}
|
|
251
251
|
// --watch mode
|
|
252
252
|
if (isWatch) {
|
|
253
|
-
console.clear();
|
|
254
|
-
let result = await runChecks();
|
|
255
|
-
console.log(reportPretty(result));
|
|
256
|
-
console.log(` ${c.dim}watching for changes... (ctrl+c to stop)${c.reset}\n`);
|
|
257
|
-
let debounce = null;
|
|
258
|
-
const { watch } = await import('node:fs');
|
|
259
253
|
try {
|
|
254
|
+
console.clear();
|
|
255
|
+
let result = await runChecks();
|
|
256
|
+
console.log(reportPretty(result));
|
|
257
|
+
console.log(` ${c.dim}watching for changes... (ctrl+c to stop)${c.reset}\n`);
|
|
258
|
+
let debounce = null;
|
|
259
|
+
const { watch } = await import('node:fs');
|
|
260
260
|
const watcher = watch(cwd, { recursive: true }, (event, filename) => {
|
|
261
261
|
if (!filename)
|
|
262
262
|
return;
|