@safetnsr/vet 0.3.0 → 0.5.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # vet
2
2
 
3
- vet your AI-generated code. one command, ten checks, zero config.
3
+ vet your AI-generated code. one command, eight checks, zero config.
4
4
 
5
5
  ```bash
6
6
  npx @safetnsr/vet
@@ -15,13 +15,11 @@ works with Claude Code, Cursor, Copilot, Codex, Aider, Windsurf, Cline — anyth
15
15
  | **ready** | is your codebase AI-friendly? | scans structure, docs, types, tests |
16
16
  | **diff** | did the AI leave anti-patterns? | AI-specific patterns: wholesale rewrites, orphaned imports, catch-alls, over-commenting, plus secrets & stubs |
17
17
  | **models** | using deprecated AI models? | scans code for sunset model strings across OpenAI, Anthropic, Google, Cohere |
18
- | **links** | broken markdown links? | validates relative links and wikilinks |
19
18
  | **config** | agent configs in place? | deep analysis of CLAUDE.md, .cursorrules, copilot-instructions — checks completeness, consistency, and specificity against your actual codebase |
20
19
  | **history** | git patterns healthy? | analyzes commit churn, AI attribution, large changes |
21
20
  | **scan** | malicious patterns in agent configs? | scans .claude/, .cursorrules, CLAUDE.md, .mcp/ for prompt injection, shell injection, exfiltration endpoints |
22
21
  | **secrets** | leaked secrets in build output? | scans dist/, build/, .next/ + .env files for API keys, tokens, connection strings using pattern + entropy analysis |
23
22
  | **receipt** | what did the last agent session do? | parses ~/.claude/projects/ JSONL session logs — files changed, commands run, packages installed, SHA256 integrity hash |
24
- | **edge** | how replaceable is your git history? | classifies commits by human-edge score: architecture (90) → debugging (85) → integration (80) → feature (60) → boilerplate (20) → cosmetic (10) |
25
23
 
26
24
  ## usage
27
25
 
@@ -53,23 +51,21 @@ npx @safetnsr/vet init
53
51
  # show last agent session receipt (ASCII or JSON)
54
52
  npx @safetnsr/vet receipt
55
53
  npx @safetnsr/vet receipt --json
56
-
57
- # show human-edge score for git history
58
- npx @safetnsr/vet edge
59
- npx @safetnsr/vet edge --explain
60
54
  ```
61
55
 
62
56
  ## output
63
57
 
64
58
  ```
65
- my-project 6.2/10
59
+ my-project 7.5/10
66
60
 
67
61
  ready ████░░░░░░ 4 3 readiness issues
68
62
  diff ████████░░ 8 3 issues (2 AI-specific) in 5 files
69
63
  models ██████████ 10 all models current
70
- links ██████░░░░ 6 3 broken links in docs/
71
64
  config ███░░░░░░░ 3 Cursor — needs work (3/10)
72
65
  history █████████░ 9 41 commits (~15% AI-attributed)
66
+ scan ██████████ 10 no malicious patterns found
67
+ secrets ██████████ 10 no leaked secrets
68
+ receipt ██████████ 10 last session: 3 files, 2 commands
73
69
 
74
70
  ✗ no README — AI agents have no project context
75
71
  ✗ no tests — AI agents produce better code when tests exist
@@ -81,7 +77,7 @@ npx @safetnsr/vet edge --explain
81
77
 
82
78
  ## --fix
83
79
 
84
- `vet --fix` doesn't just scaffold — it analyzes your codebase and generates project-specific configs:
80
+ `vet --fix` analyzes your codebase and generates project-specific configs:
85
81
 
86
82
  ```bash
87
83
  $ npx @safetnsr/vet --fix
@@ -95,12 +91,10 @@ $ npx @safetnsr/vet --fix
95
91
  fixed 3 issues
96
92
  ```
97
93
 
98
- the generated CLAUDE.md includes your actual stack, directory structure, and framework-specific rules — not generic boilerplate.
94
+ the generated CLAUDE.md includes your actual stack, directory structure, and framework-specific rules.
99
95
 
100
96
  ## AI-specific diff patterns
101
97
 
102
- vet catches things that are specific to AI-generated code:
103
-
104
98
  | pattern | what it catches |
105
99
  |---------|----------------|
106
100
  | `[ai] wholesale rewrite` | AI rewrote an entire function when a small edit would suffice |
@@ -144,34 +138,13 @@ Shows a receipt for the last Claude Code agent session — what files it touched
144
138
  ╚══════════════════════════════════════════════╝
145
139
  ```
146
140
 
147
- ### `vet edge`
148
-
149
- Analyzes git history and scores how AI-replaceable your contributions are:
150
-
151
- ```
152
- Human Edge Report 72/100
153
-
154
- 🏗️ Architecture 12 (28%) ████████████
155
- 🔍 Debugging 8 (19%) ████████
156
- 🔗 Integration 6 (14%) ██████
157
- ⚡ Feature 10 (24%) ██████████
158
- 📋 Boilerplate 5 (12%) █████
159
- 🎨 Cosmetic 1 (2%) █
160
-
161
- Top commits
162
- 90 a3f2b1c refactor: extract auth middleware across all routes
163
- 85 7e8d9f1 fix: resolve race condition in session cleanup
164
-
165
- → Strong position. Your work is deeply contextual and hard to automate.
166
- ```
167
-
168
141
  ## config
169
142
 
170
143
  create `.vetrc` in your project root (optional):
171
144
 
172
145
  ```json
173
146
  {
174
- "checks": ["ready", "diff", "models", "links", "config", "history", "scan", "secrets", "receipt", "edge"],
147
+ "checks": ["ready", "diff", "models", "config", "history", "scan", "secrets", "receipt"],
175
148
  "ignore": ["vendor/", "generated/"],
176
149
  "thresholds": { "min": 6 }
177
150
  }
@@ -0,0 +1,6 @@
1
+ import type { CheckResult } from '../types.js';
2
+ export declare function levenshtein(a: string, b: string): number;
3
+ export declare function extractImports(source: string): string[];
4
+ export declare function extractPackageName(specifier: string): string | null;
5
+ export declare function isBuiltin(specifier: string): boolean;
6
+ export declare function checkDeps(cwd: string): Promise<CheckResult>;
@@ -0,0 +1,276 @@
1
+ import { join } from 'node:path';
2
+ import { readFileSync } from 'node:fs';
3
+ import { walkFiles, readFile } from '../util.js';
4
+ // ── Top packages list (~150 popular npm packages) ────────────────────────────
5
+ const TOP_PACKAGES = [
6
+ 'react', 'react-dom', 'next', 'vue', 'angular', 'express', 'koa', 'fastify', 'hono',
7
+ 'axios', 'node-fetch', 'chalk', 'commander', 'yargs', 'inquirer', 'lodash', 'underscore',
8
+ 'ramda', 'moment', 'dayjs', 'date-fns', 'uuid', 'nanoid', 'dotenv', 'cors', 'helmet',
9
+ 'morgan', 'winston', 'pino', 'debug', 'zod', 'joi', 'yup', 'ajv', 'prettier', 'eslint',
10
+ 'typescript', 'webpack', 'vite', 'rollup', 'esbuild', 'swc', 'babel', 'jest', 'vitest',
11
+ 'mocha', 'chai', 'sinon', 'supertest', 'playwright', 'puppeteer', 'cypress', 'mongoose',
12
+ 'prisma', 'drizzle-orm', 'knex', 'sequelize', 'pg', 'mysql2', 'better-sqlite3', 'redis',
13
+ 'ioredis', 'bullmq', 'sharp', 'jimp', 'multer', 'formidable', 'nodemailer', 'socket.io',
14
+ 'ws', 'mqtt', 'graphql', 'apollo-server', 'trpc', 'stripe', 'aws-sdk', 'firebase',
15
+ 'supabase', 'openai', 'langchain', 'oclif', 'glob', 'minimatch', 'micromatch', 'semver',
16
+ 'minimist', 'cross-env', 'concurrently', 'tsx', 'ts-node', 'rimraf', 'mkdirp', 'fs-extra',
17
+ 'chokidar', 'ora', 'listr2', 'boxen', 'figlet', 'gradient-string', 'conf', 'cosmiconfig',
18
+ 'execa', 'got', 'ky', 'undici', 'cheerio', 'jsdom', 'marked', 'gray-matter', 'unified',
19
+ 'rehype', 'remark', 'mdast', 'hast', 'three', 'd3', 'chart.js', 'tailwindcss', 'postcss',
20
+ 'sass', 'less', 'styled-components', 'emotion',
21
+ ];
22
+ // ── Node.js builtins ─────────────────────────────────────────────────────────
23
+ const NODE_BUILTINS = new Set([
24
+ 'assert', 'async_hooks', 'buffer', 'child_process', 'cluster', 'console', 'constants',
25
+ 'crypto', 'dgram', 'diagnostics_channel', 'dns', 'domain', 'events', 'fs', 'http',
26
+ 'http2', 'https', 'inspector', 'module', 'net', 'os', 'path', 'perf_hooks',
27
+ 'process', 'punycode', 'querystring', 'readline', 'repl', 'stream', 'string_decoder',
28
+ 'sys', 'timers', 'tls', 'trace_events', 'tty', 'url', 'util', 'v8', 'vm', 'wasi',
29
+ 'worker_threads', 'zlib', 'test',
30
+ // also with node: prefix variants handled separately
31
+ 'fs/promises', 'stream/promises', 'timers/promises', 'dns/promises',
32
+ 'stream/web', 'stream/consumers', 'readline/promises', 'util/types',
33
+ ]);
34
+ // ── Levenshtein distance ─────────────────────────────────────────────────────
35
+ export function levenshtein(a, b) {
36
+ const m = a.length;
37
+ const n = b.length;
38
+ if (m === 0)
39
+ return n;
40
+ if (n === 0)
41
+ return m;
42
+ const dp = [];
43
+ for (let i = 0; i <= m; i++) {
44
+ dp[i] = [i];
45
+ }
46
+ for (let j = 1; j <= n; j++) {
47
+ dp[0][j] = j;
48
+ }
49
+ for (let i = 1; i <= m; i++) {
50
+ for (let j = 1; j <= n; j++) {
51
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
52
+ dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + cost);
53
+ }
54
+ }
55
+ return dp[m][n];
56
+ }
57
+ // ── Import extraction ────────────────────────────────────────────────────────
58
+ export function extractImports(source) {
59
+ const imports = new Set();
60
+ // import ... from 'pkg'
61
+ const importFrom = /import\s+(?:[\s\S]*?\s+from\s+)?['"]([^'"]+)['"]/g;
62
+ let match;
63
+ while ((match = importFrom.exec(source)) !== null) {
64
+ imports.add(match[1]);
65
+ }
66
+ // require('pkg')
67
+ const requirePat = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
68
+ while ((match = requirePat.exec(source)) !== null) {
69
+ imports.add(match[1]);
70
+ }
71
+ // import('pkg')
72
+ const dynamicImport = /import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
73
+ while ((match = dynamicImport.exec(source)) !== null) {
74
+ imports.add(match[1]);
75
+ }
76
+ return [...imports];
77
+ }
78
+ // ── Package name extraction ──────────────────────────────────────────────────
79
+ export function extractPackageName(specifier) {
80
+ // Skip relative imports
81
+ if (specifier.startsWith('.') || specifier.startsWith('/'))
82
+ return null;
83
+ // Skip node: builtins
84
+ if (specifier.startsWith('node:'))
85
+ return null;
86
+ // Scoped packages: @scope/name or @scope/name/sub
87
+ if (specifier.startsWith('@')) {
88
+ const parts = specifier.split('/');
89
+ if (parts.length < 2)
90
+ return null;
91
+ return `${parts[0]}/${parts[1]}`;
92
+ }
93
+ // Regular package: name or name/sub
94
+ return specifier.split('/')[0];
95
+ }
96
+ // ── Builtin check ────────────────────────────────────────────────────────────
97
+ export function isBuiltin(specifier) {
98
+ if (specifier.startsWith('node:'))
99
+ return true;
100
+ const name = specifier.split('/')[0];
101
+ if (NODE_BUILTINS.has(name))
102
+ return true;
103
+ // Also check full specifier for subpath builtins
104
+ if (NODE_BUILTINS.has(specifier))
105
+ return true;
106
+ return false;
107
+ }
108
+ // ── Registry check with concurrency limit ────────────────────────────────────
109
+ async function checkRegistry(packages) {
110
+ const results = new Map();
111
+ const queue = [...packages];
112
+ let networkError = false;
113
+ async function checkOne(pkg) {
114
+ try {
115
+ const controller = new AbortController();
116
+ const timeout = setTimeout(() => controller.abort(), 5000);
117
+ const res = await fetch(`https://registry.npmjs.org/${pkg}`, {
118
+ method: 'HEAD',
119
+ signal: controller.signal,
120
+ });
121
+ clearTimeout(timeout);
122
+ results.set(pkg, res.status !== 404);
123
+ }
124
+ catch {
125
+ networkError = true;
126
+ results.set(pkg, true); // assume exists on error
127
+ }
128
+ }
129
+ // Process in batches of 5
130
+ const concurrency = 5;
131
+ for (let i = 0; i < queue.length; i += concurrency) {
132
+ const batch = queue.slice(i, i + concurrency);
133
+ await Promise.all(batch.map(checkOne));
134
+ }
135
+ if (networkError) {
136
+ results.set('__network_error__', true);
137
+ }
138
+ return results;
139
+ }
140
+ // ── Main check ───────────────────────────────────────────────────────────────
141
+ export async function checkDeps(cwd) {
142
+ const issues = [];
143
+ // Read package.json
144
+ let declaredDeps = {};
145
+ let hasPkgJson = false;
146
+ try {
147
+ const pkgRaw = readFile(join(cwd, 'package.json'));
148
+ if (pkgRaw) {
149
+ const pkg = JSON.parse(pkgRaw);
150
+ hasPkgJson = true;
151
+ declaredDeps = { ...pkg.dependencies, ...pkg.devDependencies };
152
+ }
153
+ }
154
+ catch { /* skip */ }
155
+ if (!hasPkgJson) {
156
+ return {
157
+ name: 'deps',
158
+ score: 10,
159
+ maxScore: 10,
160
+ issues: [],
161
+ summary: 'no package.json found',
162
+ };
163
+ }
164
+ const declaredNames = Object.keys(declaredDeps);
165
+ // ── 1. Registry check (nonexistent packages) ──────────────────────────────
166
+ const registryResults = await checkRegistry(declaredNames);
167
+ if (registryResults.get('__network_error__')) {
168
+ issues.push({
169
+ severity: 'info',
170
+ message: 'could not reach npm registry — skipping existence checks',
171
+ fixable: false,
172
+ });
173
+ }
174
+ for (const pkg of declaredNames) {
175
+ if (registryResults.get(pkg) === false) {
176
+ issues.push({
177
+ severity: 'error',
178
+ message: `phantom dependency: "${pkg}" does not exist on npm`,
179
+ file: 'package.json',
180
+ fixable: true,
181
+ fixHint: 'remove from package.json',
182
+ });
183
+ }
184
+ }
185
+ // ── 2. Typosquat detection ─────────────────────────────────────────────────
186
+ const topSet = new Set(TOP_PACKAGES);
187
+ for (const pkg of declaredNames) {
188
+ if (topSet.has(pkg))
189
+ continue; // it IS the popular package
190
+ for (const top of TOP_PACKAGES) {
191
+ const dist = levenshtein(pkg, top);
192
+ if (dist >= 1 && dist <= 2) {
193
+ issues.push({
194
+ severity: 'error',
195
+ message: `possible typosquat: "${pkg}" is ${dist} edit${dist > 1 ? 's' : ''} from "${top}"`,
196
+ file: 'package.json',
197
+ fixable: true,
198
+ fixHint: `did you mean "${top}"?`,
199
+ });
200
+ break; // one match is enough
201
+ }
202
+ }
203
+ }
204
+ // ── 3 & 4. Dead deps + phantom imports ─────────────────────────────────────
205
+ const sourceExts = new Set(['.ts', '.js', '.tsx', '.jsx', '.mts', '.mjs', '.cts', '.cjs']);
206
+ const allFiles = walkFiles(cwd);
207
+ const sourceFiles = allFiles.filter(f => {
208
+ const ext = f.substring(f.lastIndexOf('.'));
209
+ return sourceExts.has(ext);
210
+ });
211
+ const importedPackages = new Set();
212
+ for (const file of sourceFiles) {
213
+ try {
214
+ const content = readFileSync(join(cwd, file), 'utf-8');
215
+ const rawImports = extractImports(content);
216
+ for (const imp of rawImports) {
217
+ if (isBuiltin(imp))
218
+ continue;
219
+ const pkg = extractPackageName(imp);
220
+ if (pkg)
221
+ importedPackages.add(pkg);
222
+ }
223
+ }
224
+ catch { /* skip unreadable files */ }
225
+ }
226
+ // Dead deps: declared but never imported
227
+ const declaredSet = new Set(declaredNames);
228
+ for (const pkg of declaredNames) {
229
+ if (!importedPackages.has(pkg)) {
230
+ // Check if it's a CLI tool / plugin / type package (common false positives)
231
+ // Still flag it, but as info
232
+ issues.push({
233
+ severity: 'info',
234
+ message: `unused dependency: "${pkg}" is declared but never imported`,
235
+ file: 'package.json',
236
+ fixable: true,
237
+ fixHint: 'remove from package.json',
238
+ });
239
+ }
240
+ }
241
+ // Phantom imports: imported but not declared
242
+ for (const pkg of importedPackages) {
243
+ if (!declaredSet.has(pkg)) {
244
+ issues.push({
245
+ severity: 'warning',
246
+ message: `phantom import: "${pkg}" is imported but not in package.json`,
247
+ fixable: true,
248
+ fixHint: `run: npm install ${pkg}`,
249
+ });
250
+ }
251
+ }
252
+ // ── Scoring ────────────────────────────────────────────────────────────────
253
+ const errors = issues.filter(i => i.severity === 'error').length;
254
+ const warnings = issues.filter(i => i.severity === 'warning').length;
255
+ const rawScore = 10 - (errors * 3) - (warnings * 1);
256
+ const finalScore = Math.max(0, Math.min(10, rawScore));
257
+ // ── Summary ────────────────────────────────────────────────────────────────
258
+ const parts = [];
259
+ if (errors > 0)
260
+ parts.push(`${errors} error${errors !== 1 ? 's' : ''}`);
261
+ if (warnings > 0)
262
+ parts.push(`${warnings} warning${warnings !== 1 ? 's' : ''}`);
263
+ const infos = issues.filter(i => i.severity === 'info').length;
264
+ if (infos > 0)
265
+ parts.push(`${infos} info`);
266
+ const summary = parts.length === 0
267
+ ? `${declaredNames.length} dependencies checked, all clean`
268
+ : `${declaredNames.length} dependencies: ${parts.join(', ')}`;
269
+ return {
270
+ name: 'deps',
271
+ score: finalScore,
272
+ maxScore: 10,
273
+ issues,
274
+ summary,
275
+ };
276
+ }
package/dist/cli.js CHANGED
@@ -5,13 +5,12 @@ import { isGitRepo, readFile, c } from './util.js';
5
5
  import { checkReady } from './checks/ready.js';
6
6
  import { checkDiff } from './checks/diff.js';
7
7
  import { checkModels } from './checks/models.js';
8
- import { checkLinks } from './checks/links.js';
9
8
  import { checkConfig } from './checks/config.js';
10
9
  import { checkHistory } from './checks/history.js';
11
10
  import { checkScan } from './checks/scan.js';
12
11
  import { checkSecrets } from './checks/secrets.js';
12
+ import { checkDeps } from './checks/deps.js';
13
13
  import { checkReceipt, runReceiptCommand } from './checks/receipt.js';
14
- import { checkEdge, runEdgeCommand } from './checks/edge.js';
15
14
  import { score } from './scorer.js';
16
15
  import { reportPretty, reportJSON } from './reporter.js';
17
16
  const args = process.argv.slice(2);
@@ -40,28 +39,25 @@ if (flags.has('--help') || flags.has('-h')) {
40
39
  npx @safetnsr/vet --watch live monitoring during AI sessions
41
40
  npx @safetnsr/vet init generate configs + hooks
42
41
  npx @safetnsr/vet receipt show last agent session receipt
43
- npx @safetnsr/vet edge show human-edge score for git history
44
42
 
45
43
  ${c.dim}checks:${c.reset}
46
44
  ready codebase readiness for AI agents
47
45
  diff AI-specific anti-patterns in recent changes
48
46
  models deprecated/risky model usage
49
- links dead markdown links
50
47
  config agent config hygiene
51
48
  history git history quality
52
49
  scan malicious patterns in agent config files
53
50
  secrets leaked secrets in build output and .env files
54
51
  receipt last agent session audit (informational)
55
- edge human replaceability score from git history
52
+ deps phantom/hallucinated dependency detection
56
53
 
57
54
  ${c.dim}options:${c.reset}
58
55
  --ci CI mode (exit 1 if score < threshold)
59
- --fix auto-fix configs, models, links
56
+ --fix auto-fix configs, models
60
57
  --since REF diff against specific commit/range
61
58
  --watch re-run on file changes
62
59
  --json JSON output
63
60
  --pretty force pretty output (even in pipes)
64
- --explain show detailed reasoning (edge subcommand)
65
61
  -h, --help show this help
66
62
  -v, --version show version
67
63
  `);
@@ -77,7 +73,7 @@ if (flags.has('--version') || flags.has('-v')) {
77
73
  }
78
74
  process.exit(0);
79
75
  }
80
- const COMMANDS = ['init', 'receipt', 'edge'];
76
+ const COMMANDS = ['init', 'receipt'];
81
77
  const command = COMMANDS.includes(positional[0]) ? positional[0] : undefined;
82
78
  const cwd = resolve(positional.find(p => !COMMANDS.includes(p)) || '.');
83
79
  const isCI = flags.has('--ci');
@@ -105,11 +101,6 @@ if (command === 'receipt') {
105
101
  await runReceiptCommand(format);
106
102
  process.exit(0);
107
103
  }
108
- if (command === 'edge') {
109
- const explain = flags.has('--explain');
110
- runEdgeCommand(cwd, explain);
111
- process.exit(0);
112
- }
113
104
  if (!isGitRepo(cwd)) {
114
105
  console.error(`${c.red}not a git repository${c.reset}. vet operates on git repos.`);
115
106
  process.exit(1);
@@ -119,12 +110,10 @@ if (isFix) {
119
110
  console.log(`\n ${c.bold}vet --fix${c.reset}\n`);
120
111
  const { fixConfig } = await import('./fix/config.js');
121
112
  const { fixModels } = await import('./fix/models.js');
122
- const { fixLinks } = await import('./fix/links.js');
123
113
  const configResult = fixConfig(cwd);
124
114
  const modelsResult = fixModels(cwd, ignore);
125
- const linksResult = fixLinks(cwd, ignore);
126
- const allMessages = [...configResult.messages, ...modelsResult.messages, ...linksResult.messages];
127
- const totalFixed = configResult.fixed + modelsResult.fixed + linksResult.fixed;
115
+ const allMessages = [...configResult.messages, ...modelsResult.messages];
116
+ const totalFixed = configResult.fixed + modelsResult.fixed;
128
117
  if (allMessages.length > 0) {
129
118
  for (const msg of allMessages)
130
119
  console.log(msg);
@@ -133,7 +122,7 @@ if (isFix) {
133
122
  process.exit(0);
134
123
  }
135
124
  async function runChecks() {
136
- const allChecks = ['ready', 'diff', 'models', 'links', 'config', 'history', 'scan', 'secrets', 'receipt', 'edge'];
125
+ const allChecks = ['ready', 'diff', 'models', 'config', 'history', 'scan', 'secrets', 'receipt', 'deps'];
137
126
  const enabledChecks = config.checks || allChecks;
138
127
  const results = [];
139
128
  // ready and models are async (try rich subpackages first, fallback to built-in)
@@ -143,8 +132,6 @@ async function runChecks() {
143
132
  results.push(checkDiff(cwd, { since }));
144
133
  if (enabledChecks.includes('models'))
145
134
  results.push(await checkModels(cwd, ignore));
146
- if (enabledChecks.includes('links'))
147
- results.push(checkLinks(cwd, ignore));
148
135
  if (enabledChecks.includes('config'))
149
136
  results.push(checkConfig(cwd, ignore));
150
137
  if (enabledChecks.includes('history'))
@@ -155,8 +142,8 @@ async function runChecks() {
155
142
  results.push(await checkSecrets(cwd));
156
143
  if (enabledChecks.includes('receipt'))
157
144
  results.push(await checkReceipt(cwd));
158
- if (enabledChecks.includes('edge'))
159
- results.push(checkEdge(cwd));
145
+ if (enabledChecks.includes('deps'))
146
+ results.push(await checkDeps(cwd));
160
147
  return score(cwd, results);
161
148
  }
162
149
  // --watch mode
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@safetnsr/vet",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "vet your AI-generated code — one command, six checks, zero config",
5
5
  "type": "module",
6
6
  "bin": {