@geoql/vue-doctor 0.2.0-alpha.0 → 1.0.1

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
@@ -2,37 +2,106 @@
2
2
 
3
3
  > Your agent writes bad Vue. This catches it.
4
4
 
5
- CLI auditor for Vue 3 apps. Detects anti-patterns, AI-slop, and best-practice violations via a hybrid pass over `@vue/compiler-sfc` template ASTs and `oxlint` (with its built-in `vue` plugin) for script-level rules.
5
+ CLI auditor for Vue 3 apps. It runs a hybrid scan: a template-AST pass over `@vue/compiler-sfc` plus a script pass through `oxlint` (its native `vue` plugin and the doctor JS rules), then merges the findings into a deterministic 0–100 score. Built for catching anti-patterns, AI-slop, and best-practice violations, especially the ones coding agents leave behind.
6
6
 
7
- ## Quickstart
7
+ ## Install
8
8
 
9
- ```bash
10
- npx -y @geoql/vue-doctor ./src
9
+ ```sh
10
+ # one-off, no install
11
+ npx -y @geoql/vue-doctor
12
+
13
+ # or add it
14
+ npm i @geoql/vue-doctor
11
15
  ```
12
16
 
13
- ## CLI
17
+ Published on [npm](https://www.npmjs.com/package/@geoql/vue-doctor) and [JSR](https://jsr.io/@geoql/vue-doctor) at `v0.1.0` with provenance.
18
+
19
+ ## Usage
14
20
 
21
+ ```sh
22
+ vue-doctor [path] [options]
15
23
  ```
16
- vue-doctor [path] [--format text|json] [--config doctor.config.ts] [--fail-on error|warning]
24
+
25
+ `path` defaults to the current directory. The default output format is `agent` (compact, built for LLM consumption), not a verbose human report.
26
+
27
+ ```sh
28
+ # Audit ./src and print the agent report
29
+ vue-doctor ./src
30
+
31
+ # Human-readable output, fail the build on warnings
32
+ vue-doctor --format pretty --fail-on warn
33
+
34
+ # Machine-readable, only files changed vs HEAD, write to a file
35
+ vue-doctor --json --diff --output report.json
36
+
37
+ # Just the score, for piping into a gate
38
+ vue-doctor --score
17
39
  ```
18
40
 
19
- | Flag | Description |
20
- | ----------- | -------------------------------------------------------------------------- |
21
- | `--format` | `text` (default) or `json` |
22
- | `--config` | Path to `doctor.config.ts`. Defaults to `./doctor.config.ts` if it exists. |
23
- | `--fail-on` | `error` (default) or `warning` — threshold for non-zero exit code. |
41
+ ### Options
42
+
43
+ | Flag | Description |
44
+ | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------- |
45
+ | `--format <kind>` | Output format: `agent` (default), `pretty`, `json`, `json-compact`, `sarif`, `html`. |
46
+ | `--json` | Shorthand for `--format json`. |
47
+ | `--json-compact` | Single-line JSON. |
48
+ | `--config <path>` | Path to `doctor.config.ts`. |
49
+ | `--preset <name>` | Base preset: `minimal`, `recommended`, `strict`, `all`. |
50
+ | `--fail-on <level>` | Exit non-zero at this severity or worse: `error` (default), `warn`, `none`. |
51
+ | `--quiet` | Only show the summary. |
52
+ | `--verbose` | Emit per-pass timing and rule diagnostics to stderr. |
53
+ | `--no-color` | Disable colored output. |
54
+ | `--rule <id:level>` | Override one rule, repeatable. Levels: `error`, `warn`, `info`, `off`. e.g. `--rule vue-doctor/no-em-dash-in-string:off`. |
55
+ | `--include <glob>` | Glob of files to include, repeatable. |
56
+ | `--exclude <glob>` | Glob of files to exclude, repeatable. |
57
+ | `--no-dead-code` | Skip the dead-code (knip) analysis pass. |
58
+ | `--no-lint` | Skip the lint passes (template / SFC / oxlint). |
59
+ | `--no-respect-inline-disables` | Surface findings even inside `doctor-disable` comments. |
60
+ | `--threshold <n>` | Minimum passing score, integer `0`–`100`. |
61
+ | `--score` | Print only the numeric score, for piping. |
62
+ | `--annotations` | Emit GitHub Actions `::error::` / `::warning::` lines. |
63
+ | `--ci` | Auto-enable CI behavior (annotations on GitHub Actions). |
64
+ | `--no-ci` | Disable CI auto-detection even when a CI env is set. |
65
+ | `--diff` | Only report findings in files changed vs HEAD. |
66
+ | `--staged` | Only report findings in staged files. |
67
+ | `--full` | Force a complete scan, overriding `--diff` / `--staged`. |
68
+ | `--output <file>` | Write the report to a file instead of stdout. |
69
+
70
+ Severity is `error | warn | info` everywhere. `--diff`/`--staged`, `--verbose`/`--quiet`, and `--score`/`--json` are mutually exclusive.
71
+
72
+ ### Subcommands
73
+
74
+ | Command | Description |
75
+ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
76
+ | `list-rules` | List every registered rule with id, severity, category, source, and preset membership. Filters: `--preset <recommended\|all>`, `--category <name>`, `--source <doctor\|oxlint-builtin\|eslint-plugin-vue>`, `--severity <error\|warn\|info>`, `--json`, `--json-compact`. |
77
+ | `explain <ruleId>` | Print a rule's severity, category, recommendation, and help URL. `--json` for structured output. |
78
+ | `inspect [dir]` | Print the detected project capabilities doctor uses to gate rules. `--json` / `--json-compact`. |
79
+
80
+ ```sh
81
+ vue-doctor list-rules --category ai-slop
82
+ vue-doctor explain vue-doctor/no-em-dash-in-string
83
+ vue-doctor inspect --json
84
+ ```
85
+
86
+ ### Exit codes
87
+
88
+ | Code | Meaning |
89
+ | ---- | ----------------------------------------------------------------------------------------- |
90
+ | `0` | Clean, or score at/above `--threshold` and no findings at/above `--fail-on`. |
91
+ | `1` | Findings at/above the `--fail-on` level (default: `error`), or score below `--threshold`. |
92
+ | `2` | Configuration error or scan failure. |
93
+
94
+ ## Rules
95
+
96
+ `vue-doctor` ships ~24 Vue rules plus oxlint's native `vue` built-ins, grouped by category: `ai-slop`, `reactivity`, `composition`, `performance`, `template`, `template-perf`, `build-quality`, `deps`, `sfc`, and `dead-code`. Run `vue-doctor list-rules` for the live list. Most are on in the `recommended` preset.
24
97
 
25
- Exit codes:
98
+ ## Scope
26
99
 
27
- | Code | Meaning |
28
- | ---- | ------------------------------------------------------------ |
29
- | 0 | No findings at/above `--fail-on` threshold (default: error). |
30
- | 1 | Findings at/above threshold. |
31
- | 2 | Configuration error or scan failure. |
100
+ Vue 3 only. For Nuxt 4 projects, use [`@geoql/nuxt-doctor`](../nuxt-doctor), which layers Nuxt rules on top of this set.
32
101
 
33
102
  ## Architecture
34
103
 
35
- See [`docs/ARCHITECTURE.md`](../../docs/ARCHITECTURE.md).
104
+ See [`docs/SPEC.md`](../../docs/SPEC.md) §10.
36
105
 
37
106
  ## License
38
107
 
package/dist/index.js CHANGED
@@ -2,8 +2,9 @@ import { readFileSync, writeFileSync } from "node:fs";
2
2
  import { dirname, resolve } from "node:path";
3
3
  import process from "node:process";
4
4
  import { fileURLToPath } from "node:url";
5
- import { audit, detectProject, encodeAnnotations, format, listChangedFiles, listRules, loadDoctorConfig, loadRuleDoc, mergeCliOverrides, renderVerboseTrace } from "@geoql/doctor-core";
5
+ import { audit, detectProject, detectSummary, encodeAnnotations, findMonorepoRoot, format, listChangedFiles, listRules, listWorkspacePackages, loadDoctorConfig, loadRuleDoc, mergeCliOverrides, normalizeInitAnswers, planInit, renderVerboseTrace, scoreDiagnostics } from "@geoql/doctor-core";
6
6
  import { cac } from "cac";
7
+ import prompts from "prompts";
7
8
  //#region src/cli.ts
8
9
  function readVersion() {
9
10
  try {
@@ -93,6 +94,11 @@ const VALID_SOURCES = new Set([
93
94
  "oxlint-builtin",
94
95
  "eslint-plugin-vue"
95
96
  ]);
97
+ const VALID_CONFIG_FORMATS = new Set([
98
+ "ts",
99
+ "json",
100
+ "package-json"
101
+ ]);
96
102
  function buildListRulesFilter(flags) {
97
103
  const out = {};
98
104
  if (flags.preset !== void 0) {
@@ -144,19 +150,253 @@ function parseRuleOverrides(rules) {
144
150
  return out;
145
151
  }
146
152
  function resolveFormat(flags) {
153
+ if (flags.prComment !== void 0) return "pr-comment";
147
154
  if (flags.jsonCompact) return "json-compact";
148
155
  if (flags.json) return "json";
149
156
  const kind = flags.format;
150
- if (kind === "pretty" || kind === "json" || kind === "json-compact" || kind === "sarif" || kind === "html") return kind;
157
+ if (kind === "pretty" || kind === "json" || kind === "json-compact" || kind === "sarif" || kind === "html" || kind === "pr-comment") return kind;
151
158
  if (typeof flags.output === "string" && flags.output.toLowerCase().endsWith(".html")) return "html";
152
159
  return "agent";
153
160
  }
154
161
  function isFailOnLevel(v) {
155
162
  return v === "error" || v === "warn" || v === "none";
156
163
  }
164
+ async function runSingleAudit(rootDir, flags, prepared) {
165
+ let scopeFiles;
166
+ if (!flags.full && (flags.diff || flags.staged)) scopeFiles = await listChangedFiles({
167
+ rootDir,
168
+ mode: flags.staged ? "staged" : "diff"
169
+ });
170
+ const resolved = await loadDoctorConfig(rootDir, {
171
+ ...flags.config ? { explicitPath: flags.config } : {},
172
+ ...flags.preset ? { presetOverride: flags.preset } : {}
173
+ });
174
+ const fixExclude = toArray(flags.fixExclude);
175
+ const merged = mergeCliOverrides(resolved, {
176
+ failOn: flags.failOn,
177
+ include: toArray(flags.include),
178
+ exclude: toArray(flags.exclude),
179
+ rules: prepared.ruleOverrides,
180
+ threshold: prepared.threshold,
181
+ ...fixExclude ? { fixExcludes: fixExclude } : {}
182
+ });
183
+ const report = await audit({
184
+ rootDir: merged.rootDir,
185
+ include: merged.include,
186
+ exclude: merged.exclude,
187
+ rules: merged.rules,
188
+ failOn: merged.failOn,
189
+ threshold: merged.threshold,
190
+ deadCode: flags.deadCode,
191
+ lint: flags.lint,
192
+ respectInlineDisables: flags.respectInlineDisables,
193
+ scopeFiles,
194
+ fix: flags.fix,
195
+ ...merged.fixExcludes ? { fixExcludes: merged.fixExcludes } : {}
196
+ });
197
+ const allowedRuleIds = new Set(Object.keys(merged.rules));
198
+ report.diagnostics = report.diagnostics.filter((d) => allowedRuleIds.has(d.ruleId));
199
+ return {
200
+ report,
201
+ source: resolved.source
202
+ };
203
+ }
204
+ function aggregateReports(reports, rootDirectory) {
205
+ const first = reports[0];
206
+ const diagnostics = reports.flatMap((r) => r.diagnostics);
207
+ let filesScanned = 0;
208
+ let elapsedMs = 0;
209
+ let exitCode = 0;
210
+ for (const r of reports) {
211
+ filesScanned += r.filesScanned;
212
+ elapsedMs += r.elapsedMs;
213
+ if (r.exitCode > exitCode) exitCode = r.exitCode;
214
+ }
215
+ const ruleCounts = {};
216
+ for (const d of diagnostics) ruleCounts[d.ruleId] = (ruleCounts[d.ruleId] ?? 0) + 1;
217
+ const scoreResult = scoreDiagnostics(diagnostics, { threshold: first.scoreResult.threshold });
218
+ return {
219
+ rootDir: rootDirectory,
220
+ filesScanned,
221
+ diagnostics,
222
+ score: scoreResult.score,
223
+ errorCount: scoreResult.errorCount,
224
+ warnCount: scoreResult.warnCount,
225
+ infoCount: scoreResult.infoCount,
226
+ exitCode,
227
+ scoreResult,
228
+ projectInfo: {
229
+ ...first.projectInfo,
230
+ rootDirectory
231
+ },
232
+ elapsedMs,
233
+ timings: first.timings,
234
+ ruleCounts
235
+ };
236
+ }
237
+ function emitReport(report, reporter, flags) {
238
+ const out = format({
239
+ toolName: "@geoql/vue-doctor",
240
+ toolVersion: readVersion(),
241
+ rootDirectory: report.rootDir,
242
+ analyzedFileCount: report.filesScanned,
243
+ elapsedMs: report.elapsedMs,
244
+ diagnostics: report.diagnostics,
245
+ score: report.scoreResult,
246
+ projectInfo: report.projectInfo
247
+ }, reporter, {
248
+ color: flags.color,
249
+ quiet: flags.quiet
250
+ });
251
+ if (flags.output) writeFileSync(resolve(flags.output), out);
252
+ else process.stdout.write(out);
253
+ const ciExplicitOptOut = flags.ci === false;
254
+ const ciExplicitOptIn = flags.ci === true;
255
+ const ciAutoDetected = !ciExplicitOptOut && isCiEnvironment();
256
+ if ((flags.annotations === true || ciExplicitOptIn || ciAutoDetected) && (reporter === "agent" || reporter === "pretty") && !flags.quiet && report.diagnostics.length > 0) process.stdout.write(`${encodeAnnotations(report.diagnostics)}\n`);
257
+ }
258
+ async function runProjectAudits(rootDir, flags, prepared, packages) {
259
+ const requested = flags.project.split(",").map((name) => name.trim()).filter((name) => name.length > 0);
260
+ const byName = new Map(packages.map((p) => [p.name, p]));
261
+ const reports = [];
262
+ let source = "built-in";
263
+ for (const name of requested) {
264
+ const match = byName.get(name);
265
+ if (!match) {
266
+ process.stderr.write(`vue-doctor: --project: unknown project "${name}" (skipped)\n`);
267
+ continue;
268
+ }
269
+ const single = await runSingleAudit(match.dir, flags, prepared);
270
+ reports.push(single.report);
271
+ source = single.source;
272
+ }
273
+ if (reports.length === 0) {
274
+ process.stderr.write("vue-doctor: --project: no matching workspace projects\n");
275
+ return null;
276
+ }
277
+ const { root } = await findMonorepoRoot(rootDir);
278
+ return {
279
+ report: aggregateReports(reports, root),
280
+ source
281
+ };
282
+ }
283
+ async function runInit(dir, flags) {
284
+ const targetDir = resolve(dir);
285
+ const configFormat = flags.configFormat ?? "ts";
286
+ if (!VALID_CONFIG_FORMATS.has(configFormat)) {
287
+ process.stderr.write(`vue-doctor init: --config-format must be ts | json | package-json, got '${flags.configFormat}'\n`);
288
+ process.exitCode = 2;
289
+ return;
290
+ }
291
+ const resolvedAnswers = flags.yes ? {
292
+ configFormat,
293
+ preset: "recommended",
294
+ threshold: void 0,
295
+ exclude: void 0
296
+ } : await runInteractiveInit(configFormat);
297
+ if (process.exitCode === 2) return;
298
+ const plan = await planInit({
299
+ dir: targetDir,
300
+ configFormat: resolvedAnswers.configFormat,
301
+ preset: resolvedAnswers.preset,
302
+ threshold: resolvedAnswers.threshold,
303
+ exclude: resolvedAnswers.exclude,
304
+ binName: "vue-doctor"
305
+ });
306
+ const summary = await detectSummary(targetDir);
307
+ process.stdout.write(`${summary}\n`);
308
+ if (plan.conflict && !flags.force) {
309
+ process.stderr.write(`vue-doctor init: ${plan.conflictPath} already exists. Run with --force to overwrite.\n`);
310
+ process.exitCode = 2;
311
+ return;
312
+ }
313
+ if (flags.dryRun) {
314
+ for (const write of plan.writes) {
315
+ process.stdout.write(`[dry-run] would write ${write.path}\n`);
316
+ process.stdout.write(`${write.content}\n`);
317
+ }
318
+ process.exitCode = 0;
319
+ return;
320
+ }
321
+ for (const write of plan.writes) writeFileSync(write.path, write.content, "utf8");
322
+ process.exitCode = 0;
323
+ }
324
+ async function runInteractiveInit(defaultFormat) {
325
+ const answers = await prompts([
326
+ {
327
+ type: "select",
328
+ name: "target",
329
+ message: "Config target?",
330
+ choices: [
331
+ {
332
+ title: "doctor.config.ts (recommended)",
333
+ value: "ts"
334
+ },
335
+ {
336
+ title: "doctor.config.json",
337
+ value: "json"
338
+ },
339
+ {
340
+ title: "package.json#doctor",
341
+ value: "package-json"
342
+ }
343
+ ],
344
+ initial: defaultFormat === "package-json" ? 2 : 0
345
+ },
346
+ {
347
+ type: "select",
348
+ name: "preset",
349
+ message: "Base preset?",
350
+ choices: [
351
+ {
352
+ title: "recommended (errors + warnings)",
353
+ value: "recommended"
354
+ },
355
+ {
356
+ title: "strict (recommended + info)",
357
+ value: "strict"
358
+ },
359
+ {
360
+ title: "minimal (errors only)",
361
+ value: "minimal"
362
+ }
363
+ ],
364
+ initial: 0
365
+ },
366
+ {
367
+ type: "number",
368
+ name: "threshold",
369
+ message: "Minimum passing score (0-100, blank to skip)",
370
+ min: 0,
371
+ max: 100,
372
+ initial: ""
373
+ },
374
+ {
375
+ type: "text",
376
+ name: "exclude",
377
+ message: "Exclude glob patterns (comma-separated, blank to skip)",
378
+ initial: ""
379
+ }
380
+ ], { onCancel: () => {
381
+ process.stderr.write("vue-doctor init: cancelled\n");
382
+ process.exitCode = 2;
383
+ } });
384
+ if (process.exitCode === 2) return {
385
+ configFormat: "ts",
386
+ preset: "recommended",
387
+ threshold: void 0,
388
+ exclude: void 0
389
+ };
390
+ return normalizeInitAnswers({
391
+ target: answers.target,
392
+ preset: answers.preset,
393
+ threshold: answers.threshold || void 0,
394
+ exclude: answers.exclude
395
+ });
396
+ }
157
397
  async function run(argv = process.argv) {
158
398
  const cli = cac("vue-doctor");
159
- cli.command("[path]", "Audit a Vue project").option("--format <kind>", "Output format (agent|pretty|json|json-compact|sarif|html)", { default: "agent" }).option("--json", "Shorthand for --format json").option("--json-compact", "Emit single-line JSON").option("--config <path>", "Path to doctor.config.ts").option("--preset <name>", "Base preset: minimal|recommended|strict|all").option("--fail-on <level>", "Exit non-zero on this severity or worse (error|warn|none)", { default: "error" }).option("--quiet", "Only show the summary").option("--verbose", "Emit per-pass timing and rule diagnostics to stderr").option("--no-color", "Disable colored output").option("--rule <id:level>", "Override a rule (repeatable), e.g. --rule a/b:off").option("--include <glob>", "Glob of files to include (repeatable)").option("--exclude <glob>", "Glob of files to exclude (repeatable)").option("--no-dead-code", "Skip the dead-code (knip) analysis pass").option("--no-lint", "Skip the lint passes (template/SFC/oxlint)").option("--no-respect-inline-disables", "Surface findings even inside doctor-disable comments").option("--threshold <n>", "Minimum passing score (0-100)").option("--score", "Output only the numeric score (for piping)").option("--annotations", "Emit GitHub Actions ::error::/::warning:: lines").option("--ci", "Auto-enable CI behavior (--annotations on GitHub Actions)").option("--no-ci", "Disable CI auto-detection even when CI env is set").option("--diff", "Only report findings in files changed vs HEAD").option("--staged", "Only report findings in staged files").option("--full", "Force a complete scan (overrides --diff/--staged)").option("--output <file>", "Write the report to a file instead of stdout").action(async (path, flags) => {
399
+ cli.command("[path]", "Audit a Vue project").option("--format <kind>", "Output format (agent|pretty|json|json-compact|sarif|html|pr-comment)", { default: "agent" }).option("--json", "Shorthand for --format json").option("--json-compact", "Emit single-line JSON").option("--config <path>", "Path to doctor.config.ts").option("--preset <name>", "Base preset: minimal|recommended|strict|all").option("--fail-on <level>", "Exit non-zero on this severity or a worse (error|warn|none)", { default: "error" }).option("--quiet", "Only show the summary").option("--verbose", "Emit per-pass timing and rule diagnostics on stderr").option("--no-color", "Disable colored output").option("--rule <id:level>", "Override a rule (repeatable), e.g. --rule a/b:off").option("--include <glob>", "Glob of files to include (repeatable)").option("--exclude <glob>", "Glob of files to exclude (repeatable)").option("--no-dead-code", "Skip the dead-code (knip) analysis pass").option("--no-lint", "Skip the lint passes (template/SFC/oxlint)").option("--no-respect-inline-disables", "Surface findings even inside doctor-disable comments").option("--threshold <n>", "Minimum passing score (0-100)").option("--score", "Output only the numeric score (for piping)").option("--annotations", "Emit GitHub Actions ::error::/::warning:: lines").option("--ci", "Auto-enable CI behavior (--annotations on GitHub Actions)").option("--no-ci", "Disable CI auto-detection even when CI env is set").option("--diff", "Only report findings in files changed vs HEAD").option("--staged", "Only report findings in staged files").option("--full", "Force a complete scan (overrides --diff/--staged)").option("--project <name>", "Comma-separated workspace project names to audit (monorepo)").option("--output <file>", "Write the report to a file instead of stdout").option("--pr-comment", "Emit a focused Markdown PR-comment body").option("--fix", "Auto-fix oxlint-pass findings in place (built-in vue/* rules only; template/SFC/dead-code/project findings are not fixable). Skipped in --diff/--staged mode.").option("--fix-exclude <ruleId>", "Mark a rule as not-auto-fixable (repeatable). Note: oxlint applies built-in fixes globally, so this is enforced only for doctor plugin fixes; built-in vue/* fixes still apply.").action(async (path, flags) => {
160
400
  const reporter = resolveFormat(flags);
161
401
  if (flags.failOn !== void 0 && !isFailOnLevel(flags.failOn)) {
162
402
  process.stderr.write(`vue-doctor: --fail-on must be 'error', 'warn', or 'none', got '${flags.failOn}'\n`);
@@ -171,64 +411,42 @@ async function run(argv = process.argv) {
171
411
  if (threshold !== void 0 && (!Number.isInteger(threshold) || threshold < 0 || threshold > 100)) throw new Error(`--threshold must be an integer 0-100, got "${flags.threshold}".`);
172
412
  if (flags.diff && flags.staged) throw new Error("--diff and --staged are mutually exclusive.");
173
413
  if (flags.verbose && flags.quiet) throw new Error("--verbose and --quiet are mutually exclusive.");
174
- let scopeFiles;
175
- if (!flags.full && (flags.diff || flags.staged)) scopeFiles = await listChangedFiles({
176
- rootDir,
177
- mode: flags.staged ? "staged" : "diff"
178
- });
179
- const resolved = await loadDoctorConfig(rootDir, {
180
- ...flags.config ? { explicitPath: flags.config } : {},
181
- ...flags.preset ? { presetOverride: flags.preset } : {}
182
- });
183
- const merged = mergeCliOverrides(resolved, {
184
- failOn: flags.failOn,
185
- include: toArray(flags.include),
186
- exclude: toArray(flags.exclude),
187
- rules: ruleOverrides,
414
+ const fixActive = flags.fix === true;
415
+ const fixSkippedForScope = fixActive && !flags.full && (flags.diff || flags.staged);
416
+ if (fixSkippedForScope) process.stderr.write("vue-doctor: --fix skipped in --diff/--staged mode (auto-fix only runs on a full scan to avoid rewriting files outside the diff).\n");
417
+ if (fixActive && flags.fixExclude !== void 0) process.stderr.write("vue-doctor: --fix-exclude is not enforced for built-in vue/* rules (oxlint applies their fixes globally); it currently scopes only future doctor plugin fixes.\n");
418
+ const prepared = {
419
+ ruleOverrides,
188
420
  threshold
189
- });
190
- const report = await audit({
191
- rootDir: merged.rootDir,
192
- include: merged.include,
193
- exclude: merged.exclude,
194
- rules: merged.rules,
195
- failOn: merged.failOn,
196
- threshold: merged.threshold,
197
- deadCode: flags.deadCode,
198
- lint: flags.lint,
199
- respectInlineDisables: flags.respectInlineDisables,
200
- scopeFiles
201
- });
202
- const allowedRuleIds = new Set(Object.keys(merged.rules));
203
- report.diagnostics = report.diagnostics.filter((d) => allowedRuleIds.has(d.ruleId));
421
+ };
422
+ const workspacePackages = flags.project ? await listWorkspacePackages(rootDir) : [];
423
+ if (flags.project && workspacePackages.length === 0) process.stderr.write("vue-doctor: --project ignored: not a pnpm workspace\n");
424
+ let report;
425
+ let configSource;
426
+ if (flags.project && workspacePackages.length > 0) {
427
+ const aggregated = await runProjectAudits(rootDir, flags, prepared, workspacePackages);
428
+ if (!aggregated) {
429
+ process.exitCode = 2;
430
+ return;
431
+ }
432
+ report = aggregated.report;
433
+ configSource = aggregated.source;
434
+ } else {
435
+ const single = await runSingleAudit(rootDir, flags, prepared);
436
+ report = single.report;
437
+ configSource = single.source;
438
+ }
204
439
  if (flags.verbose) {
205
- const verboseOutput = renderVerboseTrace(report, { configSource: resolved.source });
440
+ const verboseOutput = renderVerboseTrace(report, { configSource });
206
441
  process.stderr.write(`${verboseOutput}\n`);
207
442
  }
443
+ if (fixActive && !fixSkippedForScope) process.stderr.write(`vue-doctor: applied oxlint --fix; ${report.diagnostics.length} finding${report.diagnostics.length === 1 ? "" : "s"} remain (re-run to see them).\n`);
208
444
  if (flags.score) {
209
445
  process.stdout.write(`${report.score}\n`);
210
446
  process.exitCode = report.exitCode;
211
447
  return;
212
448
  }
213
- const out = format({
214
- toolName: "@geoql/vue-doctor",
215
- toolVersion: readVersion(),
216
- rootDirectory: report.rootDir,
217
- analyzedFileCount: report.filesScanned,
218
- elapsedMs: report.elapsedMs,
219
- diagnostics: report.diagnostics,
220
- score: report.scoreResult,
221
- projectInfo: report.projectInfo
222
- }, reporter, {
223
- color: flags.color,
224
- quiet: flags.quiet
225
- });
226
- if (flags.output) writeFileSync(resolve(flags.output), out);
227
- else process.stdout.write(out);
228
- const ciExplicitOptOut = flags.ci === false;
229
- const ciExplicitOptIn = flags.ci === true;
230
- const ciAutoDetected = !ciExplicitOptOut && isCiEnvironment();
231
- if ((flags.annotations === true || ciExplicitOptIn || ciAutoDetected) && (reporter === "agent" || reporter === "pretty") && !flags.quiet && report.diagnostics.length > 0) process.stdout.write(`${encodeAnnotations(report.diagnostics)}\n`);
449
+ emitReport(report, reporter, flags);
232
450
  process.exitCode = report.exitCode;
233
451
  } catch (err) {
234
452
  const msg = err instanceof Error ? err.message : String(err);
@@ -274,6 +492,14 @@ async function run(argv = process.argv) {
274
492
  } else process.stdout.write(renderInspect(project, rootDir));
275
493
  process.exitCode = 0;
276
494
  });
495
+ cli.command("init [dir]", "Scaffold a doctor config for the project").option("-y, --yes", "Non-interactive, use recommended defaults").option("--config-format <ts|json|package-json>", "Config file format (default: ts)").option("--force", "Overwrite existing config").option("--dry-run", "Print what would be written, touch nothing").action(async (dir, flags) => {
496
+ try {
497
+ await runInit(dir ?? ".", flags);
498
+ } catch (err) {
499
+ process.stderr.write(`${err instanceof Error ? err.message : err}\n`);
500
+ process.exitCode = 2;
501
+ }
502
+ });
277
503
  cli.help();
278
504
  cli.version(readVersion());
279
505
  cli.parse(argv, { run: false });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["import { readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport process from 'node:process';\nimport { fileURLToPath } from 'node:url';\nimport {\n audit,\n detectProject,\n encodeAnnotations,\n format,\n listChangedFiles,\n listRules,\n loadDoctorConfig,\n loadRuleDoc,\n mergeCliOverrides,\n renderVerboseTrace,\n type ListRulesFilter,\n type ProjectInfo,\n type RegisteredRule,\n type ReporterFormat,\n type ReporterInput,\n type RuleCategory,\n type RuleSource,\n type Severity,\n} from '@geoql/doctor-core';\nimport { cac } from 'cac';\n\nfunction readVersion(): string {\n try {\n const here = dirname(fileURLToPath(import.meta.url));\n const pkg = JSON.parse(\n readFileSync(resolve(here, '../package.json'), 'utf-8'),\n ) as { version?: string };\n return pkg.version ?? '0.0.0';\n } catch {\n return '0.0.0';\n }\n}\n\ninterface ListRulesCliFlags {\n preset?: string;\n category?: string;\n source?: string;\n severity?: string;\n json?: boolean;\n jsonCompact?: boolean;\n}\n\ninterface ExplainCliFlags {\n json?: boolean;\n}\n\ninterface InspectCliFlags {\n json?: boolean;\n jsonCompact?: boolean;\n}\n\ninterface ProjectInfoJsonPayload {\n framework: string;\n rootDirectory: string;\n packageJsonPath: string | null;\n vueVersion: string | null;\n nuxtVersion: string | null;\n nuxtCompatibilityVersion: 3 | 4 | null;\n nitroPreset: string | null;\n typescriptVersion: string | null;\n monorepoKind: string | null;\n hasAutoImports: boolean;\n hasComponentsAutoImport: boolean;\n hasPinia: boolean;\n hasVueRouter: boolean;\n capabilities: string[];\n}\n\nfunction isCiEnvironment(): boolean {\n const env = process.env;\n return Boolean(\n env.CI === 'true' ||\n env.CI === '1' ||\n env.GITHUB_ACTIONS === 'true' ||\n env.GITLAB_CI === 'true' ||\n env.CIRCLECI === 'true' ||\n env.TRAVIS === 'true' ||\n env.BUILDKITE === 'true' ||\n env.JENKINS_HOME,\n );\n}\n\nfunction projectInfoToJson(p: ProjectInfo): ProjectInfoJsonPayload {\n return {\n framework: p.framework,\n rootDirectory: p.rootDirectory,\n packageJsonPath: p.packageJsonPath,\n vueVersion: p.vueVersion,\n nuxtVersion: p.nuxtVersion,\n nuxtCompatibilityVersion: p.nuxtCompatibilityVersion,\n nitroPreset: p.nitroPreset,\n typescriptVersion: p.typescriptVersion,\n monorepoKind: p.monorepoKind,\n hasAutoImports: p.hasAutoImports,\n hasComponentsAutoImport: p.hasComponentsAutoImport,\n hasPinia: p.hasPinia,\n hasVueRouter: p.hasVueRouter,\n capabilities: [...p.capabilities].sort(),\n };\n}\n\nfunction renderInspect(p: ProjectInfo, rootDir: string): string {\n const lines: string[] = [];\n lines.push(`@geoql/vue-doctor v${readVersion()} — project capabilities`);\n lines.push('');\n lines.push('framework');\n lines.push(` ${p.framework}`);\n lines.push(` vue: ${p.vueVersion ?? '(none)'}`);\n lines.push(` typescript: ${p.typescriptVersion ?? '(none)'}`);\n lines.push('');\n lines.push('project layout');\n lines.push(` rootDirectory: ${rootDir}`);\n lines.push(` packageJsonPath: ${p.packageJsonPath ?? '(none)'}`);\n lines.push(` monorepoKind: ${p.monorepoKind ?? '(none)'}`);\n lines.push('');\n lines.push('ecosystem');\n lines.push(` hasAutoImports: ${p.hasAutoImports}`);\n lines.push(` hasComponentsAutoImport: ${p.hasComponentsAutoImport}`);\n lines.push(` hasPinia: ${p.hasPinia}`);\n lines.push(` hasVueRouter: ${p.hasVueRouter}`);\n lines.push('');\n lines.push('capability tokens (used by rule gating)');\n lines.push(` ${[...p.capabilities].sort().join('\\n ')}`);\n lines.push('');\n lines.push(\"run 'vue-doctor inspect --json' for machine output\");\n lines.push(\n \"run 'vue-doctor list-rules' to see which rules these capabilities enable\",\n );\n return `${lines.join('\\n')}\\n`;\n}\n\ninterface ExplainableDoc {\n id: string;\n severity: string;\n category: string;\n source: string;\n recommended: boolean;\n helpUri: string;\n description: string;\n hasOverride: boolean;\n}\n\nfunction renderExplain(doc: ExplainableDoc): string {\n const lines: string[] = [];\n lines.push(`Rule: ${doc.id}`);\n lines.push(`Severity: ${doc.severity}`);\n lines.push(`Category: ${doc.category}`);\n lines.push(`Source: ${doc.source}`);\n lines.push(\n `Recommended: ${doc.recommended ? 'yes' : 'no (off in `recommended` preset)'}`,\n );\n lines.push(`Help: ${doc.helpUri}`);\n lines.push('');\n lines.push(doc.description);\n return `${lines.join('\\n')}\\n`;\n}\n\nconst VALID_LIST_PRESETS = new Set(['recommended', 'all']);\nconst VALID_CATEGORIES = new Set<RuleCategory>([\n 'ai-slop',\n 'reactivity',\n 'composition',\n 'performance',\n 'template',\n 'template-perf',\n 'build-quality',\n 'deps',\n 'dead-code',\n 'sfc',\n 'vue-builtin',\n]);\nconst VALID_SOURCES = new Set<RuleSource>([\n 'doctor',\n 'oxlint-builtin',\n 'eslint-plugin-vue',\n]);\n\nfunction buildListRulesFilter(flags: ListRulesCliFlags): ListRulesFilter {\n const out: {\n preset?: 'recommended' | 'all';\n category?: RuleCategory;\n source?: RuleSource;\n severity?: Severity;\n } = {};\n if (flags.preset !== undefined) {\n if (!VALID_LIST_PRESETS.has(flags.preset)) {\n throw new Error(\n `unknown --preset '${flags.preset}' (expected: recommended | all)`,\n );\n }\n out.preset = flags.preset as 'recommended' | 'all';\n }\n if (flags.category !== undefined) {\n if (!VALID_CATEGORIES.has(flags.category as RuleCategory)) {\n throw new Error(`unknown --category '${flags.category}'`);\n }\n out.category = flags.category as RuleCategory;\n }\n if (flags.source !== undefined) {\n if (!VALID_SOURCES.has(flags.source as RuleSource)) {\n throw new Error(`unknown --source '${flags.source}'`);\n }\n out.source = flags.source as RuleSource;\n }\n if (flags.severity !== undefined) {\n if (\n flags.severity !== 'error' &&\n flags.severity !== 'warn' &&\n flags.severity !== 'info'\n ) {\n throw new Error(\n `unknown --severity '${flags.severity}' (expected: error | warn | info)`,\n );\n }\n out.severity = flags.severity;\n }\n return out;\n}\n\nfunction renderRulesTable(rules: RegisteredRule[]): string {\n if (rules.length === 0) return 'No rules matched.\\n';\n const lines: string[] = [\n `@geoql/vue-doctor — ${rules.length} rule${rules.length === 1 ? '' : 's'}`,\n '',\n ];\n const idWidth = Math.max(...rules.map((r) => r.id.length));\n const sevWidth = Math.max(...rules.map((r) => r.severity.length));\n const catWidth = Math.max(...rules.map((r) => r.category.length));\n for (const r of rules) {\n const tag = r.recommended ? '[recommended]' : ' ';\n lines.push(\n ` ${r.id.padEnd(idWidth)} ${r.severity.padEnd(sevWidth)} ${r.category.padEnd(catWidth)} ${r.source} ${tag}`,\n );\n }\n return `${lines.join('\\n')}\\n`;\n}\n\ninterface CliFlags {\n format?: string;\n config?: string;\n preset?: string;\n failOn?: string;\n json?: boolean;\n jsonCompact?: boolean;\n color?: boolean;\n quiet?: boolean;\n verbose?: boolean;\n output?: string;\n rule?: string | string[];\n include?: string | string[];\n exclude?: string | string[];\n deadCode?: boolean;\n lint?: boolean;\n respectInlineDisables?: boolean;\n threshold?: string;\n score?: boolean;\n annotations?: boolean;\n ci?: boolean;\n diff?: boolean;\n staged?: boolean;\n full?: boolean;\n}\n\nfunction toArray(value: string | string[] | undefined): string[] | undefined {\n if (value === undefined) return undefined;\n return Array.isArray(value) ? value : [value];\n}\n\nfunction parseRuleOverrides(\n rules: string[] | undefined,\n): Record<string, Severity | 'off'> | undefined {\n if (!rules || rules.length === 0) return undefined;\n const out: Record<string, Severity | 'off'> = {};\n for (const entry of rules) {\n const idx = entry.lastIndexOf(':');\n if (idx === -1) {\n throw new Error(\n `Invalid --rule \"${entry}\". Expected format <ruleId>:<error|warn|info|off>.`,\n );\n }\n const ruleId = entry.slice(0, idx);\n const level = entry.slice(idx + 1);\n if (\n level !== 'error' &&\n level !== 'warn' &&\n level !== 'info' &&\n level !== 'off'\n ) {\n throw new Error(\n `Invalid severity \"${level}\" for --rule \"${entry}\". Expected error|warn|info|off.`,\n );\n }\n if (ruleId.length === 0) {\n throw new Error(`Invalid --rule \"${entry}\". Rule id must not be empty.`);\n }\n out[ruleId] = level;\n }\n return out;\n}\n\nfunction resolveFormat(flags: CliFlags): ReporterFormat {\n if (flags.jsonCompact) return 'json-compact';\n if (flags.json) return 'json';\n const kind = flags.format;\n const userPickedFormat =\n kind === 'pretty' ||\n kind === 'json' ||\n kind === 'json-compact' ||\n kind === 'sarif' ||\n kind === 'html';\n if (userPickedFormat) {\n return kind;\n }\n if (\n typeof flags.output === 'string' &&\n flags.output.toLowerCase().endsWith('.html')\n ) {\n return 'html';\n }\n return 'agent';\n}\n\nfunction isFailOnLevel(v: string): v is 'error' | 'warn' | 'none' {\n return v === 'error' || v === 'warn' || v === 'none';\n}\n\nexport async function run(argv: string[] = process.argv): Promise<number> {\n const cli = cac('vue-doctor');\n\n cli\n .command('[path]', 'Audit a Vue project')\n .option(\n '--format <kind>',\n 'Output format (agent|pretty|json|json-compact|sarif|html)',\n {\n default: 'agent',\n },\n )\n .option('--json', 'Shorthand for --format json')\n .option('--json-compact', 'Emit single-line JSON')\n .option('--config <path>', 'Path to doctor.config.ts')\n .option('--preset <name>', 'Base preset: minimal|recommended|strict|all')\n .option(\n '--fail-on <level>',\n 'Exit non-zero on this severity or worse (error|warn|none)',\n {\n default: 'error',\n },\n )\n .option('--quiet', 'Only show the summary')\n .option('--verbose', 'Emit per-pass timing and rule diagnostics to stderr')\n .option('--no-color', 'Disable colored output')\n .option(\n '--rule <id:level>',\n 'Override a rule (repeatable), e.g. --rule a/b:off',\n )\n .option('--include <glob>', 'Glob of files to include (repeatable)')\n .option('--exclude <glob>', 'Glob of files to exclude (repeatable)')\n .option('--no-dead-code', 'Skip the dead-code (knip) analysis pass')\n .option('--no-lint', 'Skip the lint passes (template/SFC/oxlint)')\n .option(\n '--no-respect-inline-disables',\n 'Surface findings even inside doctor-disable comments',\n )\n .option('--threshold <n>', 'Minimum passing score (0-100)')\n .option('--score', 'Output only the numeric score (for piping)')\n .option('--annotations', 'Emit GitHub Actions ::error::/::warning:: lines')\n .option('--ci', 'Auto-enable CI behavior (--annotations on GitHub Actions)')\n .option('--no-ci', 'Disable CI auto-detection even when CI env is set')\n .option('--diff', 'Only report findings in files changed vs HEAD')\n .option('--staged', 'Only report findings in staged files')\n .option('--full', 'Force a complete scan (overrides --diff/--staged)')\n .option('--output <file>', 'Write the report to a file instead of stdout')\n .action(async (path: string | undefined, flags: CliFlags) => {\n const reporter = resolveFormat(flags);\n if (flags.failOn !== undefined && !isFailOnLevel(flags.failOn)) {\n process.stderr.write(\n `vue-doctor: --fail-on must be 'error', 'warn', or 'none', got '${flags.failOn}'\\n`,\n );\n process.exitCode = 2;\n return;\n }\n const rootDir = resolve(path ?? '.');\n\n try {\n if (flags.score && (flags.json || flags.jsonCompact)) {\n throw new Error(\n '--score and --json are mutually exclusive (--score outputs a plaintext integer).',\n );\n }\n const ruleOverrides = parseRuleOverrides(toArray(flags.rule));\n const threshold =\n flags.threshold === undefined ? undefined : Number(flags.threshold);\n if (\n threshold !== undefined &&\n (!Number.isInteger(threshold) || threshold < 0 || threshold > 100)\n ) {\n throw new Error(\n `--threshold must be an integer 0-100, got \"${flags.threshold}\".`,\n );\n }\n if (flags.diff && flags.staged) {\n throw new Error('--diff and --staged are mutually exclusive.');\n }\n if (flags.verbose && flags.quiet) {\n throw new Error('--verbose and --quiet are mutually exclusive.');\n }\n let scopeFiles: string[] | undefined;\n if (!flags.full && (flags.diff || flags.staged)) {\n scopeFiles = await listChangedFiles({\n rootDir,\n mode: flags.staged ? 'staged' : 'diff',\n });\n }\n const resolved = await loadDoctorConfig(rootDir, {\n ...(flags.config ? { explicitPath: flags.config } : {}),\n ...(flags.preset ? { presetOverride: flags.preset } : {}),\n });\n const merged = mergeCliOverrides(resolved, {\n failOn: flags.failOn as 'error' | 'warn' | 'none' | undefined,\n include: toArray(flags.include),\n exclude: toArray(flags.exclude),\n rules: ruleOverrides,\n threshold,\n });\n const report = await audit({\n rootDir: merged.rootDir,\n include: merged.include,\n exclude: merged.exclude,\n rules: merged.rules,\n failOn: merged.failOn,\n threshold: merged.threshold,\n deadCode: flags.deadCode,\n lint: flags.lint,\n respectInlineDisables: flags.respectInlineDisables,\n scopeFiles,\n });\n const allowedRuleIds = new Set(Object.keys(merged.rules));\n report.diagnostics = report.diagnostics.filter((d) =>\n allowedRuleIds.has(d.ruleId),\n );\n if (flags.verbose) {\n const verboseOutput = renderVerboseTrace(report, {\n configSource: resolved.source,\n });\n process.stderr.write(`${verboseOutput}\\n`);\n }\n if (flags.score) {\n process.stdout.write(`${report.score}\\n`);\n process.exitCode = report.exitCode;\n return;\n }\n const input: ReporterInput = {\n toolName: '@geoql/vue-doctor',\n toolVersion: readVersion(),\n rootDirectory: report.rootDir,\n analyzedFileCount: report.filesScanned,\n elapsedMs: report.elapsedMs,\n diagnostics: report.diagnostics,\n score: report.scoreResult,\n projectInfo: report.projectInfo,\n };\n const out = format(input, reporter, {\n color: flags.color,\n quiet: flags.quiet,\n });\n if (flags.output) {\n writeFileSync(resolve(flags.output), out);\n } else {\n process.stdout.write(out);\n }\n const ciExplicitOptOut = flags.ci === false;\n const ciExplicitOptIn = flags.ci === true;\n const ciAutoDetected = !ciExplicitOptOut && isCiEnvironment();\n const wantsAnnotations =\n flags.annotations === true || ciExplicitOptIn || ciAutoDetected;\n const reporterCarriesAnnotations =\n reporter === 'agent' || reporter === 'pretty';\n if (\n wantsAnnotations &&\n reporterCarriesAnnotations &&\n !flags.quiet &&\n report.diagnostics.length > 0\n ) {\n process.stdout.write(`${encodeAnnotations(report.diagnostics)}\\n`);\n }\n process.exitCode = report.exitCode;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n process.stderr.write(`vue-doctor: ${msg}\\n`);\n process.exitCode = 2;\n }\n });\n\n cli\n .command(\n 'list-rules',\n 'List every registered rule with id, severity, category, source, and preset membership',\n )\n .option('--preset <name>', 'Filter to: recommended | all')\n .option('--category <name>', 'Filter by category')\n .option(\n '--source <name>',\n 'Filter by source: doctor | oxlint-builtin | eslint-plugin-vue',\n )\n .option('--severity <level>', 'Filter by: error | warn | info')\n .option('--json', 'Emit JSON instead of formatted text')\n .option('--json-compact', 'With --json, single-line output')\n .action(async (flags: ListRulesCliFlags) => {\n try {\n const filter = buildListRulesFilter(flags);\n const rules = listRules(filter);\n if (flags.json || flags.jsonCompact) {\n const payload = { count: rules.length, rules };\n process.stdout.write(\n flags.jsonCompact\n ? JSON.stringify(payload)\n : `${JSON.stringify(payload, null, 2)}\\n`,\n );\n } else {\n process.stdout.write(renderRulesTable(rules));\n }\n process.exitCode = 0;\n } catch (err) {\n const msg = (err as Error).message;\n process.stderr.write(`vue-doctor list-rules: ${msg}\\n`);\n process.exitCode = 2;\n }\n });\n\n cli\n .command(\n 'explain <ruleId>',\n \"Print the rule's severity, category, recommendation, and helpUri\",\n )\n .option('--json', 'Emit structured JSON instead of formatted text')\n .action(async (ruleId: string, flags: ExplainCliFlags) => {\n const doc = loadRuleDoc(ruleId);\n if (!doc) {\n process.stderr.write(\n `vue-doctor explain: unknown rule '${ruleId}'. Try \\`vue-doctor list-rules\\` to see registered rules.\\n`,\n );\n process.exitCode = 2;\n return;\n }\n if (flags.json) {\n process.stdout.write(`${JSON.stringify(doc, null, 2)}\\n`);\n } else {\n process.stdout.write(renderExplain(doc));\n }\n process.exitCode = 0;\n });\n\n cli\n .command(\n 'inspect [dir]',\n 'Print the detected project capabilities doctor uses to gate rules',\n )\n .option('--json', 'Emit structured JSON instead of formatted text')\n .option('--json-compact', 'With --json, emit a single-line payload')\n .action(async (dir: string | undefined, flags: InspectCliFlags) => {\n const rootDir = resolve(dir ?? '.');\n const project = await detectProject(rootDir);\n if (flags.json || flags.jsonCompact) {\n const payload = projectInfoToJson(project);\n const out = flags.jsonCompact\n ? JSON.stringify(payload)\n : JSON.stringify(payload, null, 2);\n process.stdout.write(`${out}\\n`);\n } else {\n process.stdout.write(renderInspect(project, rootDir));\n }\n process.exitCode = 0;\n });\n\n cli.help();\n cli.version(readVersion());\n cli.parse(argv, { run: false });\n await cli.runMatchedCommand();\n return process.exitCode ?? 0;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["import { readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport process from 'node:process';\nimport { fileURLToPath } from 'node:url';\nimport {\n audit,\n detectProject,\n detectSummary,\n encodeAnnotations,\n findMonorepoRoot,\n format,\n listChangedFiles,\n listRules,\n listWorkspacePackages,\n loadDoctorConfig,\n loadRuleDoc,\n mergeCliOverrides,\n normalizeInitAnswers,\n planInit,\n renderVerboseTrace,\n scoreDiagnostics,\n type AuditReport,\n type ConfigSource,\n type InitConfigFormat,\n type ListRulesFilter,\n type ProjectInfo,\n type RegisteredRule,\n type ReporterFormat,\n type ReporterInput,\n type ResolvedDoctorConfig,\n type RuleCategory,\n type RuleSource,\n type Severity,\n type WorkspacePackage,\n} from '@geoql/doctor-core';\nimport { cac } from 'cac';\nimport prompts from 'prompts';\n\nfunction readVersion(): string {\n try {\n const here = dirname(fileURLToPath(import.meta.url));\n const pkg = JSON.parse(\n readFileSync(resolve(here, '../package.json'), 'utf-8'),\n ) as { version?: string };\n return pkg.version ?? '0.0.0';\n } catch {\n return '0.0.0';\n }\n}\n\ninterface ListRulesCliFlags {\n preset?: string;\n category?: string;\n source?: string;\n severity?: string;\n json?: boolean;\n jsonCompact?: boolean;\n}\n\ninterface ExplainCliFlags {\n json?: boolean;\n}\n\ninterface InspectCliFlags {\n json?: boolean;\n jsonCompact?: boolean;\n}\n\ninterface InitCliFlags {\n yes?: boolean;\n configFormat?: string;\n force?: boolean;\n dryRun?: boolean;\n}\n\ninterface ProjectInfoJsonPayload {\n framework: string;\n rootDirectory: string;\n packageJsonPath: string | null;\n vueVersion: string | null;\n nuxtVersion: string | null;\n nuxtCompatibilityVersion: 3 | 4 | null;\n nitroPreset: string | null;\n typescriptVersion: string | null;\n monorepoKind: string | null;\n hasAutoImports: boolean;\n hasComponentsAutoImport: boolean;\n hasPinia: boolean;\n hasVueRouter: boolean;\n capabilities: string[];\n}\n\nfunction isCiEnvironment(): boolean {\n const env = process.env;\n return Boolean(\n env.CI === 'true' ||\n env.CI === '1' ||\n env.GITHUB_ACTIONS === 'true' ||\n env.GITLAB_CI === 'true' ||\n env.CIRCLECI === 'true' ||\n env.TRAVIS === 'true' ||\n env.BUILDKITE === 'true' ||\n env.JENKINS_HOME,\n );\n}\n\nfunction projectInfoToJson(p: ProjectInfo): ProjectInfoJsonPayload {\n return {\n framework: p.framework,\n rootDirectory: p.rootDirectory,\n packageJsonPath: p.packageJsonPath,\n vueVersion: p.vueVersion,\n nuxtVersion: p.nuxtVersion,\n nuxtCompatibilityVersion: p.nuxtCompatibilityVersion,\n nitroPreset: p.nitroPreset,\n typescriptVersion: p.typescriptVersion,\n monorepoKind: p.monorepoKind,\n hasAutoImports: p.hasAutoImports,\n hasComponentsAutoImport: p.hasComponentsAutoImport,\n hasPinia: p.hasPinia,\n hasVueRouter: p.hasVueRouter,\n capabilities: [...p.capabilities].sort(),\n };\n}\n\nfunction renderInspect(p: ProjectInfo, rootDir: string): string {\n const lines: string[] = [];\n lines.push(`@geoql/vue-doctor v${readVersion()} — project capabilities`);\n lines.push('');\n lines.push('framework');\n lines.push(` ${p.framework}`);\n lines.push(` vue: ${p.vueVersion ?? '(none)'}`);\n lines.push(` typescript: ${p.typescriptVersion ?? '(none)'}`);\n lines.push('');\n lines.push('project layout');\n lines.push(` rootDirectory: ${rootDir}`);\n lines.push(` packageJsonPath: ${p.packageJsonPath ?? '(none)'}`);\n lines.push(` monorepoKind: ${p.monorepoKind ?? '(none)'}`);\n lines.push('');\n lines.push('ecosystem');\n lines.push(` hasAutoImports: ${p.hasAutoImports}`);\n lines.push(` hasComponentsAutoImport: ${p.hasComponentsAutoImport}`);\n lines.push(` hasPinia: ${p.hasPinia}`);\n lines.push(` hasVueRouter: ${p.hasVueRouter}`);\n lines.push('');\n lines.push('capability tokens (used by rule gating)');\n lines.push(` ${[...p.capabilities].sort().join('\\n ')}`);\n lines.push('');\n lines.push(\"run 'vue-doctor inspect --json' for machine output\");\n lines.push(\n \"run 'vue-doctor list-rules' to see which rules these capabilities enable\",\n );\n return `${lines.join('\\n')}\\n`;\n}\n\ninterface ExplainableDoc {\n id: string;\n severity: string;\n category: string;\n source: string;\n recommended: boolean;\n helpUri: string;\n description: string;\n hasOverride: boolean;\n}\n\nfunction renderExplain(doc: ExplainableDoc): string {\n const lines: string[] = [];\n lines.push(`Rule: ${doc.id}`);\n lines.push(`Severity: ${doc.severity}`);\n lines.push(`Category: ${doc.category}`);\n lines.push(`Source: ${doc.source}`);\n lines.push(\n `Recommended: ${doc.recommended ? 'yes' : 'no (off in `recommended` preset)'}`,\n );\n lines.push(`Help: ${doc.helpUri}`);\n lines.push('');\n lines.push(doc.description);\n return `${lines.join('\\n')}\\n`;\n}\n\nconst VALID_LIST_PRESETS = new Set(['recommended', 'all']);\nconst VALID_CATEGORIES = new Set<RuleCategory>([\n 'ai-slop',\n 'reactivity',\n 'composition',\n 'performance',\n 'template',\n 'template-perf',\n 'build-quality',\n 'deps',\n 'dead-code',\n 'sfc',\n 'vue-builtin',\n]);\nconst VALID_SOURCES = new Set<RuleSource>([\n 'doctor',\n 'oxlint-builtin',\n 'eslint-plugin-vue',\n]);\nconst VALID_CONFIG_FORMATS = new Set(['ts', 'json', 'package-json']);\n\nfunction buildListRulesFilter(flags: ListRulesCliFlags): ListRulesFilter {\n const out: {\n preset?: 'recommended' | 'all';\n category?: RuleCategory;\n source?: RuleSource;\n severity?: Severity;\n } = {};\n if (flags.preset !== undefined) {\n if (!VALID_LIST_PRESETS.has(flags.preset)) {\n throw new Error(\n `unknown --preset '${flags.preset}' (expected: recommended | all)`,\n );\n }\n out.preset = flags.preset as 'recommended' | 'all';\n }\n if (flags.category !== undefined) {\n if (!VALID_CATEGORIES.has(flags.category as RuleCategory)) {\n throw new Error(`unknown --category '${flags.category}'`);\n }\n out.category = flags.category as RuleCategory;\n }\n if (flags.source !== undefined) {\n if (!VALID_SOURCES.has(flags.source as RuleSource)) {\n throw new Error(`unknown --source '${flags.source}'`);\n }\n out.source = flags.source as RuleSource;\n }\n if (flags.severity !== undefined) {\n if (\n flags.severity !== 'error' &&\n flags.severity !== 'warn' &&\n flags.severity !== 'info'\n ) {\n throw new Error(\n `unknown --severity '${flags.severity}' (expected: error | warn | info)`,\n );\n }\n out.severity = flags.severity;\n }\n return out;\n}\n\nfunction renderRulesTable(rules: RegisteredRule[]): string {\n if (rules.length === 0) return 'No rules matched.\\n';\n const lines: string[] = [\n `@geoql/vue-doctor — ${rules.length} rule${rules.length === 1 ? '' : 's'}`,\n '',\n ];\n const idWidth = Math.max(...rules.map((r) => r.id.length));\n const sevWidth = Math.max(...rules.map((r) => r.severity.length));\n const catWidth = Math.max(...rules.map((r) => r.category.length));\n for (const r of rules) {\n const tag = r.recommended ? '[recommended]' : ' ';\n lines.push(\n ` ${r.id.padEnd(idWidth)} ${r.severity.padEnd(sevWidth)} ${r.category.padEnd(catWidth)} ${r.source} ${tag}`,\n );\n }\n return `${lines.join('\\n')}\\n`;\n}\n\ninterface CliFlags {\n format?: string;\n config?: string;\n preset?: string;\n failOn?: string;\n json?: boolean;\n jsonCompact?: boolean;\n color?: boolean;\n quiet?: boolean;\n verbose?: boolean;\n output?: string;\n rule?: string | string[];\n include?: string | string[];\n exclude?: string | string[];\n deadCode?: boolean;\n lint?: boolean;\n respectInlineDisables?: boolean;\n threshold?: string;\n score?: boolean;\n annotations?: boolean;\n ci?: boolean;\n diff?: boolean;\n staged?: boolean;\n full?: boolean;\n project?: string;\n prComment?: string | true;\n fix?: boolean;\n fixExclude?: string | string[];\n}\n\ninterface PreparedOptions {\n ruleOverrides: Record<string, Severity | 'off'> | undefined;\n threshold: number | undefined;\n}\n\nfunction toArray(value: string | string[] | undefined): string[] | undefined {\n if (value === undefined) return undefined;\n return Array.isArray(value) ? value : [value];\n}\n\nfunction parseRuleOverrides(\n rules: string[] | undefined,\n): Record<string, Severity | 'off'> | undefined {\n if (!rules || rules.length === 0) return undefined;\n const out: Record<string, Severity | 'off'> = {};\n for (const entry of rules) {\n const idx = entry.lastIndexOf(':');\n if (idx === -1) {\n throw new Error(\n `Invalid --rule \"${entry}\". Expected format <ruleId>:<error|warn|info|off>.`,\n );\n }\n const ruleId = entry.slice(0, idx);\n const level = entry.slice(idx + 1);\n if (\n level !== 'error' &&\n level !== 'warn' &&\n level !== 'info' &&\n level !== 'off'\n ) {\n throw new Error(\n `Invalid severity \"${level}\" for --rule \"${entry}\". Expected error|warn|info|off.`,\n );\n }\n if (ruleId.length === 0) {\n throw new Error(`Invalid --rule \"${entry}\". Rule id must not be empty.`);\n }\n out[ruleId] = level;\n }\n return out;\n}\n\nfunction resolveFormat(flags: CliFlags): ReporterFormat {\n if (flags.prComment !== undefined) return 'pr-comment';\n if (flags.jsonCompact) return 'json-compact';\n if (flags.json) return 'json';\n const kind = flags.format;\n const userPickedFormat =\n kind === 'pretty' ||\n kind === 'json' ||\n kind === 'json-compact' ||\n kind === 'sarif' ||\n kind === 'html' ||\n kind === 'pr-comment';\n if (userPickedFormat) {\n return kind as ReporterFormat;\n }\n if (\n typeof flags.output === 'string' &&\n flags.output.toLowerCase().endsWith('.html')\n ) {\n return 'html';\n }\n return 'agent';\n}\n\nfunction isFailOnLevel(v: string): v is 'error' | 'warn' | 'none' {\n return v === 'error' || v === 'warn' || v === 'none';\n}\n\nasync function runSingleAudit(\n rootDir: string,\n flags: CliFlags,\n prepared: PreparedOptions,\n): Promise<{ report: AuditReport; source: ConfigSource }> {\n let scopeFiles: string[] | undefined;\n if (!flags.full && (flags.diff || flags.staged)) {\n scopeFiles = await listChangedFiles({\n rootDir,\n mode: flags.staged ? 'staged' : 'diff',\n });\n }\n const resolved = await loadDoctorConfig(rootDir, {\n ...(flags.config ? { explicitPath: flags.config } : {}),\n ...(flags.preset ? { presetOverride: flags.preset } : {}),\n });\n const fixExclude = toArray(flags.fixExclude);\n const merged: ResolvedDoctorConfig = mergeCliOverrides(resolved, {\n failOn: flags.failOn as 'error' | 'warn' | 'none' | undefined,\n include: toArray(flags.include),\n exclude: toArray(flags.exclude),\n rules: prepared.ruleOverrides,\n threshold: prepared.threshold,\n ...(fixExclude ? { fixExcludes: fixExclude } : {}),\n });\n const report = await audit({\n rootDir: merged.rootDir,\n include: merged.include,\n exclude: merged.exclude,\n rules: merged.rules,\n failOn: merged.failOn,\n threshold: merged.threshold,\n deadCode: flags.deadCode,\n lint: flags.lint,\n respectInlineDisables: flags.respectInlineDisables,\n scopeFiles,\n fix: flags.fix,\n ...(merged.fixExcludes ? { fixExcludes: merged.fixExcludes } : {}),\n });\n const allowedRuleIds = new Set(Object.keys(merged.rules));\n report.diagnostics = report.diagnostics.filter((d) =>\n allowedRuleIds.has(d.ruleId),\n );\n return { report, source: resolved.source };\n}\n\nfunction aggregateReports(\n reports: AuditReport[],\n rootDirectory: string,\n): AuditReport {\n const first = reports[0];\n const diagnostics = reports.flatMap((r) => r.diagnostics);\n let filesScanned = 0;\n let elapsedMs = 0;\n let exitCode: 0 | 1 | 2 = 0;\n for (const r of reports) {\n filesScanned += r.filesScanned;\n elapsedMs += r.elapsedMs;\n if (r.exitCode > exitCode) exitCode = r.exitCode;\n }\n const ruleCounts: Record<string, number> = {};\n for (const d of diagnostics) {\n ruleCounts[d.ruleId] = (ruleCounts[d.ruleId] ?? 0) + 1;\n }\n const scoreResult = scoreDiagnostics(diagnostics, {\n threshold: first.scoreResult.threshold,\n });\n return {\n rootDir: rootDirectory,\n filesScanned,\n diagnostics,\n score: scoreResult.score,\n errorCount: scoreResult.errorCount,\n warnCount: scoreResult.warnCount,\n infoCount: scoreResult.infoCount,\n exitCode,\n scoreResult,\n projectInfo: { ...first.projectInfo, rootDirectory },\n elapsedMs,\n timings: first.timings,\n ruleCounts,\n };\n}\n\nfunction emitReport(\n report: AuditReport,\n reporter: ReporterFormat,\n flags: CliFlags,\n): void {\n const input: ReporterInput = {\n toolName: '@geoql/vue-doctor',\n toolVersion: readVersion(),\n rootDirectory: report.rootDir,\n analyzedFileCount: report.filesScanned,\n elapsedMs: report.elapsedMs,\n diagnostics: report.diagnostics,\n score: report.scoreResult,\n projectInfo: report.projectInfo,\n };\n const out = format(input, reporter, {\n color: flags.color,\n quiet: flags.quiet,\n });\n if (flags.output) {\n writeFileSync(resolve(flags.output), out);\n } else {\n process.stdout.write(out);\n }\n const ciExplicitOptOut = flags.ci === false;\n const ciExplicitOptIn = flags.ci === true;\n const ciAutoDetected = !ciExplicitOptOut && isCiEnvironment();\n const wantsAnnotations =\n flags.annotations === true || ciExplicitOptIn || ciAutoDetected;\n const reporterCarriesAnnotations =\n reporter === 'agent' || reporter === 'pretty';\n if (\n wantsAnnotations &&\n reporterCarriesAnnotations &&\n !flags.quiet &&\n report.diagnostics.length > 0\n ) {\n process.stdout.write(`${encodeAnnotations(report.diagnostics)}\\n`);\n }\n}\n\nasync function runProjectAudits(\n rootDir: string,\n flags: CliFlags,\n prepared: PreparedOptions,\n packages: WorkspacePackage[],\n): Promise<{ report: AuditReport; source: ConfigSource } | null> {\n const requested = flags\n .project!.split(',')\n .map((name) => name.trim())\n .filter((name) => name.length > 0);\n const byName = new Map(packages.map((p) => [p.name, p]));\n const reports: AuditReport[] = [];\n let source: ConfigSource = 'built-in';\n for (const name of requested) {\n const match = byName.get(name);\n if (!match) {\n process.stderr.write(\n `vue-doctor: --project: unknown project \"${name}\" (skipped)\\n`,\n );\n continue;\n }\n const single = await runSingleAudit(match.dir, flags, prepared);\n reports.push(single.report);\n source = single.source;\n }\n if (reports.length === 0) {\n process.stderr.write(\n 'vue-doctor: --project: no matching workspace projects\\n',\n );\n return null;\n }\n const { root } = await findMonorepoRoot(rootDir);\n return { report: aggregateReports(reports, root), source };\n}\n\nasync function runInit(dir: string, flags: InitCliFlags): Promise<void> {\n const targetDir = resolve(dir);\n const configFormat = (flags.configFormat ?? 'ts') as InitConfigFormat;\n if (!VALID_CONFIG_FORMATS.has(configFormat)) {\n process.stderr.write(\n `vue-doctor init: --config-format must be ts | json | package-json, got '${flags.configFormat}'\\n`,\n );\n process.exitCode = 2;\n return;\n }\n\n const resolvedAnswers = flags.yes\n ? {\n configFormat,\n preset: 'recommended' as const,\n threshold: undefined,\n exclude: undefined,\n }\n : await runInteractiveInit(configFormat);\n\n if (process.exitCode === 2) return;\n\n const plan = await planInit({\n dir: targetDir,\n configFormat: resolvedAnswers.configFormat,\n preset: resolvedAnswers.preset,\n threshold: resolvedAnswers.threshold,\n exclude: resolvedAnswers.exclude,\n binName: 'vue-doctor',\n });\n\n const summary = await detectSummary(targetDir);\n process.stdout.write(`${summary}\\n`);\n\n if (plan.conflict && !flags.force) {\n process.stderr.write(\n `vue-doctor init: ${plan.conflictPath} already exists. Run with --force to overwrite.\\n`,\n );\n process.exitCode = 2;\n return;\n }\n\n if (flags.dryRun) {\n for (const write of plan.writes) {\n process.stdout.write(`[dry-run] would write ${write.path}\\n`);\n process.stdout.write(`${write.content}\\n`);\n }\n process.exitCode = 0;\n return;\n }\n\n for (const write of plan.writes) {\n writeFileSync(write.path, write.content, 'utf8');\n }\n process.exitCode = 0;\n}\n\nasync function runInteractiveInit(defaultFormat: InitConfigFormat): Promise<{\n configFormat: InitConfigFormat;\n preset: 'recommended' | 'strict' | 'minimal';\n threshold: number | undefined;\n exclude: string | undefined;\n}> {\n const answers = await prompts(\n [\n {\n type: 'select',\n name: 'target',\n message: 'Config target?',\n choices: [\n { title: 'doctor.config.ts (recommended)', value: 'ts' },\n { title: 'doctor.config.json', value: 'json' },\n { title: 'package.json#doctor', value: 'package-json' },\n ],\n initial: defaultFormat === 'package-json' ? 2 : 0,\n },\n {\n type: 'select',\n name: 'preset',\n message: 'Base preset?',\n choices: [\n { title: 'recommended (errors + warnings)', value: 'recommended' },\n { title: 'strict (recommended + info)', value: 'strict' },\n { title: 'minimal (errors only)', value: 'minimal' },\n ],\n initial: 0,\n },\n {\n type: 'number',\n name: 'threshold',\n message: 'Minimum passing score (0-100, blank to skip)',\n min: 0,\n max: 100,\n initial: '',\n },\n {\n type: 'text',\n name: 'exclude',\n message: 'Exclude glob patterns (comma-separated, blank to skip)',\n initial: '',\n },\n ],\n {\n onCancel: () => {\n process.stderr.write('vue-doctor init: cancelled\\n');\n process.exitCode = 2;\n },\n },\n );\n\n if (process.exitCode === 2)\n return {\n configFormat: 'ts' as InitConfigFormat,\n preset: 'recommended' as const,\n threshold: undefined,\n exclude: undefined,\n };\n\n return normalizeInitAnswers({\n target: answers.target as InitConfigFormat | undefined,\n preset: answers.preset as 'recommended' | 'strict' | 'minimal' | undefined,\n threshold: answers.threshold || undefined,\n exclude: answers.exclude as string | undefined,\n });\n}\n\nexport async function run(argv: string[] = process.argv): Promise<number> {\n const cli = cac('vue-doctor');\n\n cli\n .command('[path]', 'Audit a Vue project')\n .option(\n '--format <kind>',\n 'Output format (agent|pretty|json|json-compact|sarif|html|pr-comment)',\n {\n default: 'agent',\n },\n )\n .option('--json', 'Shorthand for --format json')\n .option('--json-compact', 'Emit single-line JSON')\n .option('--config <path>', 'Path to doctor.config.ts')\n .option('--preset <name>', 'Base preset: minimal|recommended|strict|all')\n .option(\n '--fail-on <level>',\n 'Exit non-zero on this severity or a worse (error|warn|none)',\n {\n default: 'error',\n },\n )\n .option('--quiet', 'Only show the summary')\n .option('--verbose', 'Emit per-pass timing and rule diagnostics on stderr')\n .option('--no-color', 'Disable colored output')\n .option(\n '--rule <id:level>',\n 'Override a rule (repeatable), e.g. --rule a/b:off',\n )\n .option('--include <glob>', 'Glob of files to include (repeatable)')\n .option('--exclude <glob>', 'Glob of files to exclude (repeatable)')\n .option('--no-dead-code', 'Skip the dead-code (knip) analysis pass')\n .option('--no-lint', 'Skip the lint passes (template/SFC/oxlint)')\n .option(\n '--no-respect-inline-disables',\n 'Surface findings even inside doctor-disable comments',\n )\n .option('--threshold <n>', 'Minimum passing score (0-100)')\n .option('--score', 'Output only the numeric score (for piping)')\n .option('--annotations', 'Emit GitHub Actions ::error::/::warning:: lines')\n .option('--ci', 'Auto-enable CI behavior (--annotations on GitHub Actions)')\n .option('--no-ci', 'Disable CI auto-detection even when CI env is set')\n .option('--diff', 'Only report findings in files changed vs HEAD')\n .option('--staged', 'Only report findings in staged files')\n .option('--full', 'Force a complete scan (overrides --diff/--staged)')\n .option(\n '--project <name>',\n 'Comma-separated workspace project names to audit (monorepo)',\n )\n .option('--output <file>', 'Write the report to a file instead of stdout')\n .option('--pr-comment', 'Emit a focused Markdown PR-comment body')\n .option(\n '--fix',\n 'Auto-fix oxlint-pass findings in place (built-in vue/* rules only; template/SFC/dead-code/project findings are not fixable). Skipped in --diff/--staged mode.',\n )\n .option(\n '--fix-exclude <ruleId>',\n 'Mark a rule as not-auto-fixable (repeatable). Note: oxlint applies built-in fixes globally, so this is enforced only for doctor plugin fixes; built-in vue/* fixes still apply.',\n )\n .action(async (path: string | undefined, flags: CliFlags) => {\n const reporter = resolveFormat(flags);\n if (flags.failOn !== undefined && !isFailOnLevel(flags.failOn)) {\n process.stderr.write(\n `vue-doctor: --fail-on must be 'error', 'warn', or 'none', got '${flags.failOn}'\\n`,\n );\n process.exitCode = 2;\n return;\n }\n const rootDir = resolve(path ?? '.');\n\n try {\n if (flags.score && (flags.json || flags.jsonCompact)) {\n throw new Error(\n '--score and --json are mutually exclusive (--score outputs a plaintext integer).',\n );\n }\n const ruleOverrides = parseRuleOverrides(toArray(flags.rule));\n const threshold =\n flags.threshold === undefined ? undefined : Number(flags.threshold);\n if (\n threshold !== undefined &&\n (!Number.isInteger(threshold) || threshold < 0 || threshold > 100)\n ) {\n throw new Error(\n `--threshold must be an integer 0-100, got \"${flags.threshold}\".`,\n );\n }\n if (flags.diff && flags.staged) {\n throw new Error('--diff and --staged are mutually exclusive.');\n }\n if (flags.verbose && flags.quiet) {\n throw new Error('--verbose and --quiet are mutually exclusive.');\n }\n const fixActive = flags.fix === true;\n const fixSkippedForScope =\n fixActive && !flags.full && (flags.diff || flags.staged);\n if (fixSkippedForScope) {\n process.stderr.write(\n 'vue-doctor: --fix skipped in --diff/--staged mode (auto-fix only runs on a full scan to avoid rewriting files outside the diff).\\n',\n );\n }\n if (fixActive && flags.fixExclude !== undefined) {\n process.stderr.write(\n 'vue-doctor: --fix-exclude is not enforced for built-in vue/* rules (oxlint applies their fixes globally); it currently scopes only future doctor plugin fixes.\\n',\n );\n }\n const prepared: PreparedOptions = { ruleOverrides, threshold };\n\n const workspacePackages = flags.project\n ? await listWorkspacePackages(rootDir)\n : [];\n if (flags.project && workspacePackages.length === 0) {\n process.stderr.write(\n 'vue-doctor: --project ignored: not a pnpm workspace\\n',\n );\n }\n\n let report: AuditReport;\n let configSource: ConfigSource;\n if (flags.project && workspacePackages.length > 0) {\n const aggregated = await runProjectAudits(\n rootDir,\n flags,\n prepared,\n workspacePackages,\n );\n if (!aggregated) {\n process.exitCode = 2;\n return;\n }\n report = aggregated.report;\n configSource = aggregated.source;\n } else {\n const single = await runSingleAudit(rootDir, flags, prepared);\n report = single.report;\n configSource = single.source;\n }\n\n if (flags.verbose) {\n const verboseOutput = renderVerboseTrace(report, {\n configSource,\n });\n process.stderr.write(`${verboseOutput}\\n`);\n }\n if (fixActive && !fixSkippedForScope) {\n process.stderr.write(\n `vue-doctor: applied oxlint --fix; ${report.diagnostics.length} finding${report.diagnostics.length === 1 ? '' : 's'} remain (re-run to see them).\\n`,\n );\n }\n if (flags.score) {\n process.stdout.write(`${report.score}\\n`);\n process.exitCode = report.exitCode;\n return;\n }\n emitReport(report, reporter, flags);\n process.exitCode = report.exitCode;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n process.stderr.write(`vue-doctor: ${msg}\\n`);\n process.exitCode = 2;\n }\n });\n\n cli\n .command(\n 'list-rules',\n 'List every registered rule with id, severity, category, source, and preset membership',\n )\n .option('--preset <name>', 'Filter to: recommended | all')\n .option('--category <name>', 'Filter by category')\n .option(\n '--source <name>',\n 'Filter by source: doctor | oxlint-builtin | eslint-plugin-vue',\n )\n .option('--severity <level>', 'Filter by: error | warn | info')\n .option('--json', 'Emit JSON instead of formatted text')\n .option('--json-compact', 'With --json, single-line output')\n .action(async (flags: ListRulesCliFlags) => {\n try {\n const filter = buildListRulesFilter(flags);\n const rules = listRules(filter);\n if (flags.json || flags.jsonCompact) {\n const payload = { count: rules.length, rules };\n process.stdout.write(\n flags.jsonCompact\n ? JSON.stringify(payload)\n : `${JSON.stringify(payload, null, 2)}\\n`,\n );\n } else {\n process.stdout.write(renderRulesTable(rules));\n }\n process.exitCode = 0;\n } catch (err) {\n const msg = (err as Error).message;\n process.stderr.write(`vue-doctor list-rules: ${msg}\\n`);\n process.exitCode = 2;\n }\n });\n\n cli\n .command(\n 'explain <ruleId>',\n \"Print the rule's severity, category, recommendation, and helpUri\",\n )\n .option('--json', 'Emit structured JSON instead of formatted text')\n .action(async (ruleId: string, flags: ExplainCliFlags) => {\n const doc = loadRuleDoc(ruleId);\n if (!doc) {\n process.stderr.write(\n `vue-doctor explain: unknown rule '${ruleId}'. Try \\`vue-doctor list-rules\\` to see registered rules.\\n`,\n );\n process.exitCode = 2;\n return;\n }\n if (flags.json) {\n process.stdout.write(`${JSON.stringify(doc, null, 2)}\\n`);\n } else {\n process.stdout.write(renderExplain(doc));\n }\n process.exitCode = 0;\n });\n\n cli\n .command(\n 'inspect [dir]',\n 'Print the detected project capabilities doctor uses to gate rules',\n )\n .option('--json', 'Emit structured JSON instead of formatted text')\n .option('--json-compact', 'With --json, emit a single-line payload')\n .action(async (dir: string | undefined, flags: InspectCliFlags) => {\n const rootDir = resolve(dir ?? '.');\n const project = await detectProject(rootDir);\n if (flags.json || flags.jsonCompact) {\n const payload = projectInfoToJson(project);\n const out = flags.jsonCompact\n ? JSON.stringify(payload)\n : JSON.stringify(payload, null, 2);\n process.stdout.write(`${out}\\n`);\n } else {\n process.stdout.write(renderInspect(project, rootDir));\n }\n process.exitCode = 0;\n });\n\n cli\n .command('init [dir]', 'Scaffold a doctor config for the project')\n .option('-y, --yes', 'Non-interactive, use recommended defaults')\n .option(\n '--config-format <ts|json|package-json>',\n 'Config file format (default: ts)',\n )\n .option('--force', 'Overwrite existing config')\n .option('--dry-run', 'Print what would be written, touch nothing')\n .action(async (dir: string | undefined, flags: InitCliFlags) => {\n try {\n await runInit(dir ?? '.', flags);\n } catch (err) {\n process.stderr.write(`${err instanceof Error ? err.message : err}\\n`);\n process.exitCode = 2;\n }\n });\n\n cli.help();\n cli.version(readVersion());\n cli.parse(argv, { run: false });\n await cli.runMatchedCommand();\n return process.exitCode ?? 0;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geoql/vue-doctor",
3
- "version": "0.2.0-alpha.0",
3
+ "version": "1.0.1",
4
4
  "private": false,
5
5
  "description": "CLI auditor for Vue 3 apps. Detects anti-patterns, AI-slop, and best-practice violations via @vue/compiler-sfc + oxlint. Run with `npx -y @geoql/vue-doctor`.",
6
6
  "keywords": [
@@ -49,11 +49,20 @@
49
49
  "cac": "^7.0.0",
50
50
  "kolorist": "^1.8.0",
51
51
  "oxlint": "^1.67.0",
52
- "@geoql/doctor-core": "^0.2.0-alpha.0",
53
- "@geoql/oxlint-plugin-vue-doctor": "0.1.1-alpha.0"
52
+ "prompts": "^2.4.2",
53
+ "@geoql/doctor-core": "^1.0.1",
54
+ "@geoql/oxlint-plugin-vue-doctor": "1.0.0"
54
55
  },
55
56
  "devDependencies": {
56
57
  "@types/node": "^25.9.1",
58
+ "@types/prompts": "^2.4.9",
59
+ "@vue/compiler-core": "^3.5.35",
60
+ "@vue/compiler-sfc": "^3.5.35",
61
+ "c12": "^4.0.0-beta.5",
62
+ "oxc-parser": "0.133.0",
63
+ "picocolors": "^1.1.1",
64
+ "semver": "^7.8.1",
65
+ "tinyglobby": "^0.2.16",
57
66
  "typescript": "^6.0.3",
58
67
  "vitest": "^4.1.7"
59
68
  },