@geoql/vue-doctor 1.3.2 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +90 -6
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { existsSync, mkdirSync, readFileSync, symlinkSync, writeFileSync } from
|
|
|
2
2
|
import { basename, dirname, join, resolve } from "node:path";
|
|
3
3
|
import process from "node:process";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { audit, detectProject, detectSummary, encodeAnnotations, filterReportByRules, filterRuleIdsByCategory, findMonorepoRoot, format, listChangedFiles, listRules, listWorkspacePackages, loadDoctorConfig, loadRuleDoc, mergeCliOverrides, normalizeInitAnswers, planInit, pushFindings, renderVerboseTrace, resolveCategoryScope, scoreDiagnostics } from "@geoql/doctor-core";
|
|
5
|
+
import { audit, detectProject, detectSummary, encodeAnnotations, filterReportByRules, filterRuleIdsByCategory, findMonorepoRoot, format, listChangedFiles, listRules, listWorkspacePackages, loadDoctorConfig, loadRuleDoc, mergeCliOverrides, normalizeInitAnswers, planInit, pushFindings, renderVerboseTrace, resolveCategoryScope, scaffoldCiWorkflow, scoreDiagnostics } from "@geoql/doctor-core";
|
|
6
6
|
import { cac } from "cac";
|
|
7
7
|
import prompts from "prompts";
|
|
8
8
|
//#region src/install/install-skill.ts
|
|
@@ -264,6 +264,12 @@ function resolveFormat(flags) {
|
|
|
264
264
|
function isFailOnLevel(v) {
|
|
265
265
|
return v === "error" || v === "warn" || v === "none";
|
|
266
266
|
}
|
|
267
|
+
function parseMaxDurationMs(value) {
|
|
268
|
+
if (value === void 0) return void 0;
|
|
269
|
+
const seconds = Number(value);
|
|
270
|
+
if (!Number.isInteger(seconds) || seconds < 0) throw new Error(`--max-duration must be a non-negative integer number of seconds, got "${value}".`);
|
|
271
|
+
return seconds * 1e3;
|
|
272
|
+
}
|
|
267
273
|
async function runSingleAudit(rootDir, flags, prepared) {
|
|
268
274
|
let scopeFiles;
|
|
269
275
|
if (!flags.full && (flags.diff || flags.staged)) scopeFiles = await listChangedFiles({
|
|
@@ -295,7 +301,8 @@ async function runSingleAudit(rootDir, flags, prepared) {
|
|
|
295
301
|
respectInlineDisables: flags.respectInlineDisables,
|
|
296
302
|
scopeFiles,
|
|
297
303
|
fix: flags.fix,
|
|
298
|
-
...merged.fixExcludes ? { fixExcludes: merged.fixExcludes } : {}
|
|
304
|
+
...merged.fixExcludes ? { fixExcludes: merged.fixExcludes } : {},
|
|
305
|
+
...prepared.maxDurationMs !== void 0 ? { maxDurationMs: prepared.maxDurationMs } : {}
|
|
299
306
|
});
|
|
300
307
|
let allowedRuleIds = new Set(Object.keys(merged.rules));
|
|
301
308
|
if (prepared.categoryScope) allowedRuleIds = filterRuleIdsByCategory(allowedRuleIds, prepared.categoryScope);
|
|
@@ -318,6 +325,7 @@ function aggregateReports(reports, rootDirectory) {
|
|
|
318
325
|
const ruleCounts = {};
|
|
319
326
|
for (const d of diagnostics) ruleCounts[d.ruleId] = (ruleCounts[d.ruleId] ?? 0) + 1;
|
|
320
327
|
const scoreResult = scoreDiagnostics(diagnostics, { threshold: first.scoreResult.threshold });
|
|
328
|
+
const skippedCheckReasons = reports.flatMap((r) => r.skippedCheckReasons ?? []);
|
|
321
329
|
return {
|
|
322
330
|
rootDir: rootDirectory,
|
|
323
331
|
filesScanned,
|
|
@@ -334,7 +342,9 @@ function aggregateReports(reports, rootDirectory) {
|
|
|
334
342
|
},
|
|
335
343
|
elapsedMs,
|
|
336
344
|
timings: first.timings,
|
|
337
|
-
ruleCounts
|
|
345
|
+
ruleCounts,
|
|
346
|
+
incomplete: reports.some((r) => r.incomplete),
|
|
347
|
+
...skippedCheckReasons.length > 0 ? { skippedCheckReasons } : {}
|
|
338
348
|
};
|
|
339
349
|
}
|
|
340
350
|
function emitReport(report, reporter, flags) {
|
|
@@ -346,7 +356,9 @@ function emitReport(report, reporter, flags) {
|
|
|
346
356
|
elapsedMs: report.elapsedMs,
|
|
347
357
|
diagnostics: report.diagnostics,
|
|
348
358
|
score: report.scoreResult,
|
|
349
|
-
projectInfo: report.projectInfo
|
|
359
|
+
projectInfo: report.projectInfo,
|
|
360
|
+
incomplete: report.incomplete,
|
|
361
|
+
...report.skippedCheckReasons ? { skippedCheckReasons: report.skippedCheckReasons } : {}
|
|
350
362
|
}, reporter, {
|
|
351
363
|
color: flags.color,
|
|
352
364
|
quiet: flags.quiet
|
|
@@ -521,9 +533,67 @@ async function runInteractiveInit(defaultFormat) {
|
|
|
521
533
|
exclude: answers.exclude
|
|
522
534
|
});
|
|
523
535
|
}
|
|
536
|
+
const VALID_CI_PROVIDERS = new Set([
|
|
537
|
+
"github",
|
|
538
|
+
"gitlab",
|
|
539
|
+
"auto"
|
|
540
|
+
]);
|
|
541
|
+
async function runCiInstall(dir, flags) {
|
|
542
|
+
const targetDir = resolve(dir);
|
|
543
|
+
const provider = flags.provider ?? "auto";
|
|
544
|
+
if (!VALID_CI_PROVIDERS.has(provider)) {
|
|
545
|
+
process.stderr.write(`vue-doctor ci install: --provider must be github | gitlab | auto, got '${flags.provider}'\n`);
|
|
546
|
+
process.exitCode = 2;
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
const plan = await scaffoldCiWorkflow({
|
|
550
|
+
bin: "vue-doctor",
|
|
551
|
+
framework: "vue",
|
|
552
|
+
dir: targetDir,
|
|
553
|
+
provider,
|
|
554
|
+
pr: flags.pr === true,
|
|
555
|
+
noComments: flags.comments === false,
|
|
556
|
+
force: flags.force === true
|
|
557
|
+
});
|
|
558
|
+
if (plan.conflict) {
|
|
559
|
+
process.stderr.write(`vue-doctor ci install: ${plan.conflictPath} already exists. Run with --force to overwrite.\n`);
|
|
560
|
+
process.exitCode = 2;
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
if (flags.dryRun) {
|
|
564
|
+
for (const write of plan.writes) {
|
|
565
|
+
process.stdout.write(`[dry-run] would write ${write.path}\n`);
|
|
566
|
+
process.stdout.write(`${write.content}\n`);
|
|
567
|
+
}
|
|
568
|
+
process.exitCode = 0;
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
if (!flags.yes) {
|
|
572
|
+
const answers = await prompts({
|
|
573
|
+
type: "confirm",
|
|
574
|
+
name: "confirm",
|
|
575
|
+
message: `Scaffold the ${plan.provider} CI workflow at ${plan.writes[0]?.path}?`,
|
|
576
|
+
initial: true
|
|
577
|
+
}, { onCancel: () => {
|
|
578
|
+
process.exitCode = 2;
|
|
579
|
+
} });
|
|
580
|
+
if (process.exitCode === 2) return;
|
|
581
|
+
if (!answers.confirm) {
|
|
582
|
+
process.stdout.write("vue-doctor ci install: cancelled\n");
|
|
583
|
+
process.exitCode = 0;
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
for (const write of plan.writes) {
|
|
588
|
+
mkdirSync(dirname(write.path), { recursive: true });
|
|
589
|
+
writeFileSync(write.path, write.content, "utf8");
|
|
590
|
+
process.stdout.write(`vue-doctor ci install: wrote ${write.path}\n`);
|
|
591
|
+
}
|
|
592
|
+
process.exitCode = 0;
|
|
593
|
+
}
|
|
524
594
|
async function run(argv = process.argv) {
|
|
525
595
|
const cli = cac("vue-doctor");
|
|
526
|
-
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("--category <name>", "Only score rules in this category (repeatable)").option("--dimension <name>", "Only score this dimension: performance|correctness|security|maintainability|nuxt|design (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.").option("--push", "After the audit, POST privacy-stripped findings to the SaaS. Requires --api-key. Negatable with --no-push.").option("--push-url <url>", "SaaS endpoint for --push (default: https://app.the-doctor.report/api/v1/findings)", { default: "https://app.the-doctor.report/api/v1/findings" }).option("--api-key <key>", "API key for the SaaS (sent as the x-api-key header for both --score-mode and --push).").option("--push-project <slug>", "SaaS dashboard project slug for --push (e.g. owner/repo). Defaults to the audited directory name.").action(async (path, flags) => {
|
|
596
|
+
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("--category <name>", "Only score rules in this category (repeatable)").option("--dimension <name>", "Only score this dimension: performance|correctness|security|maintainability|nuxt|design (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.").option("--push", "After the audit, POST privacy-stripped findings to the SaaS. Requires --api-key. Negatable with --no-push.").option("--push-url <url>", "SaaS endpoint for --push (default: https://app.the-doctor.report/api/v1/findings)", { default: "https://app.the-doctor.report/api/v1/findings" }).option("--api-key <key>", "API key for the SaaS (sent as the x-api-key header for both --score-mode and --push).").option("--push-project <slug>", "SaaS dashboard project slug for --push (e.g. owner/repo). Defaults to the audited directory name.").option("--max-duration <seconds>", "Abort remaining audit passes after N seconds; the report gains incomplete=true + skippedCheckReasons").action(async (path, flags) => {
|
|
527
597
|
const reporter = resolveFormat(flags);
|
|
528
598
|
if (flags.failOn !== void 0 && !isFailOnLevel(flags.failOn)) {
|
|
529
599
|
process.stderr.write(`vue-doctor: --fail-on must be 'error', 'warn', or 'none', got '${flags.failOn}'\n`);
|
|
@@ -548,7 +618,8 @@ async function run(argv = process.argv) {
|
|
|
548
618
|
categoryScope: resolveCategoryScope({
|
|
549
619
|
categories: toArray(flags.category),
|
|
550
620
|
dimensions: toArray(flags.dimension)
|
|
551
|
-
})
|
|
621
|
+
}),
|
|
622
|
+
maxDurationMs: parseMaxDurationMs(flags.maxDuration)
|
|
552
623
|
};
|
|
553
624
|
const workspacePackages = flags.project ? await listWorkspacePackages(rootDir) : [];
|
|
554
625
|
if (flags.project && workspacePackages.length === 0) process.stderr.write("vue-doctor: --project ignored: not a pnpm workspace\n");
|
|
@@ -642,6 +713,19 @@ async function run(argv = process.argv) {
|
|
|
642
713
|
process.exitCode = 2;
|
|
643
714
|
}
|
|
644
715
|
});
|
|
716
|
+
cli.command("ci <action> [dir]", "CI integrations. Action 'install' scaffolds a workflow referencing geoql/doctor-action@v2").option("--pr", "Also scaffold a PR-review workflow").option("--no-comments", "Disable PR comments (sets pr-comment=false)").option("--provider <name>", "CI provider: github | gitlab | auto (default: auto)").option("-y, --yes", "Non-interactive, write defaults without prompting").option("--force", "Overwrite an existing workflow").option("--dry-run", "Print what would be written, touch nothing").action(async (action, dir, flags) => {
|
|
717
|
+
if (action !== "install") {
|
|
718
|
+
process.stderr.write(`vue-doctor ci: unknown action '${action}' (expected: install)\n`);
|
|
719
|
+
process.exitCode = 2;
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
722
|
+
try {
|
|
723
|
+
await runCiInstall(dir ?? ".", flags);
|
|
724
|
+
} catch (err) {
|
|
725
|
+
process.stderr.write(`vue-doctor ci install: ${err instanceof Error ? err.message : err}\n`);
|
|
726
|
+
process.exitCode = 2;
|
|
727
|
+
}
|
|
728
|
+
});
|
|
645
729
|
cli.help();
|
|
646
730
|
cli.version(readVersion());
|
|
647
731
|
cli.parse(argv, { run: false });
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/install/install-skill.ts","../src/cli.ts"],"sourcesContent":["import { existsSync, mkdirSync, symlinkSync, writeFileSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\nimport process from 'node:process';\nimport prompts from 'prompts';\n\ninterface InstallSkillOptions {\n dir: string;\n version: string;\n yes?: boolean;\n dryRun?: boolean;\n force?: boolean;\n}\n\nconst PLUGIN = 'vue-doctor';\nconst PACKAGE = '@geoql/vue-doctor';\nconst FRAMEWORK = 'Vue';\nconst PLAYBOOK_URL = 'https://docs.the-doctor.report/prompts/doctor-agent.md';\nconst RULES_URL_BASE =\n 'https://docs.the-doctor.report/prompts/rules/vue-doctor/';\n\nexport function buildSkillMarkdown(version: string): string {\n return `---\nname: ${PLUGIN}\ndescription: Use when finishing a feature, fixing a bug, before committing ${FRAMEWORK} code, or when the user types \\`/doctor\\`, asks to scan, triage, or clean up ${FRAMEWORK} diagnostics. Covers reactivity, composition, performance, templates, and AI-slop. Includes a regression check and a full local-triage workflow that fetches the canonical playbook.\nversion: \"${version}\"\n---\n\n# ${FRAMEWORK} Doctor\n\nAudits ${FRAMEWORK} codebases for reactivity leaks, composition errors, performance traps, template issues, and AI-agent anti-patterns. Outputs a 0–100 health score.\n\n## After making ${FRAMEWORK} code changes:\n\nRun \\`npx ${PACKAGE}@latest --diff --verbose\\` and check the score did not regress. \\`--diff\\` reports only issues introduced versus the base branch.\n\nIf the score dropped, fix the regressions before committing.\n\n## For general cleanup or code improvement:\n\nRun \\`npx ${PACKAGE}@latest --verbose\\` to scan the full codebase. Fix issues by severity — errors first, then warnings.\n\n## /doctor — full local triage workflow\n\nWhen the user types \\`/doctor\\`, says \"run ${PLUGIN}\", or asks for a full triage / cleanup pass (not just a regression check), fetch the canonical local-triage playbook and follow every step in it:\n\n\\`\\`\\`bash\ncurl --fail --silent --show-error \\\\\n --header 'Cache-Control: no-cache' \\\\\n ${PLAYBOOK_URL}\n\\`\\`\\`\n\nThe playbook is the single source of truth — a scan → filter → triage → fix → validate loop that edits the working tree directly (never commits, never opens PRs). Updating the prompt at its source updates every agent on its next fetch — no skill reinstall needed.\n\nPair it with the matching per-rule prompts at \\`${RULES_URL_BASE}<rule>.md\\` (fetched on demand inside the playbook) so each fix uses the canonical, reviewer-tested recipe.\n\n## Configuring or explaining rules\n\nWhen the user wants to understand a rule, disagrees with one, or wants to tune which rules run (not fix code), start with \\`npx ${PACKAGE}@latest explain <rule>\\` (alias \\`why\\`), then list everything with \\`npx ${PACKAGE}@latest list-rules\\` (alias \\`rules\\`), and edit your \\`doctor.config.*\\` to disable or re-level a rule.\n\n## Command\n\n\\`\\`\\`bash\nnpx ${PACKAGE}@latest --diff --verbose\n\\`\\`\\`\n\n| Flag | Purpose |\n| ----------- | --------------------------------------------------------------- |\n| \\`.\\` | Scan current directory |\n| \\`--verbose\\` | Show affected files and line numbers per rule |\n| \\`--diff\\` | Only report issues introduced vs the base branch (default: full)|\n| \\`--score\\` | Output only the numeric score |\n| \\`--json\\` | Emit machine-readable JSON |\n`;\n}\n\nexport async function runInstallSkill(\n options: InstallSkillOptions,\n): Promise<void> {\n const targetDir = resolve(options.dir);\n const skillDir = join(targetDir, '.agents', 'skills', PLUGIN);\n const skillPath = join(skillDir, 'SKILL.md');\n const content = buildSkillMarkdown(options.version);\n\n if (options.dryRun) {\n process.stdout.write(`[dry-run] would write ${skillPath}\\n`);\n process.stdout.write(`${content}\\n`);\n process.exitCode = 0;\n return;\n }\n\n if (existsSync(skillPath) && !options.force) {\n process.stderr.write(\n `${PLUGIN} install: ${skillPath} already exists. Run with --force to overwrite.\\n`,\n );\n process.exitCode = 2;\n return;\n }\n\n if (!options.yes) {\n const answers = await prompts(\n {\n type: 'confirm',\n name: 'confirm',\n message: `Scaffold the ${PLUGIN} agent skill at ${skillPath}?`,\n initial: true,\n },\n {\n onCancel: () => {\n process.exitCode = 2;\n },\n },\n );\n if (process.exitCode === 2) return;\n if (!answers.confirm) {\n process.stdout.write(`${PLUGIN} install: cancelled\\n`);\n process.exitCode = 0;\n return;\n }\n }\n\n mkdirSync(skillDir, { recursive: true });\n writeFileSync(skillPath, content, 'utf8');\n process.stdout.write(`${PLUGIN} install: wrote ${skillPath}\\n`);\n\n const claudePath = join(targetDir, '.claude');\n if (!existsSync(claudePath)) {\n symlinkSync('.agents', claudePath);\n process.stdout.write(`${PLUGIN} install: linked .claude -> .agents\\n`);\n }\n\n process.exitCode = 0;\n}\n","import { readFileSync, writeFileSync } from 'node:fs';\nimport { basename, 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 pushFindings,\n renderVerboseTrace,\n scoreDiagnostics,\n filterReportByRules,\n filterRuleIdsByCategory,\n resolveCategoryScope,\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';\nimport { runInstallSkill } from './install/install-skill.js';\n\ninterface InstallCliFlags {\n yes?: boolean;\n force?: boolean;\n dryRun?: boolean;\n}\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 push?: boolean;\n pushUrl: string;\n apiKey?: string;\n pushProject?: string;\n category?: string | string[];\n dimension?: string | string[];\n}\n\ninterface PreparedOptions {\n ruleOverrides: Record<string, Severity | 'off'> | undefined;\n threshold: number | undefined;\n categoryScope: ReadonlySet<RuleCategory> | 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 let allowedRuleIds = new Set(Object.keys(merged.rules));\n if (prepared.categoryScope) {\n allowedRuleIds = filterRuleIdsByCategory(\n allowedRuleIds,\n prepared.categoryScope,\n );\n }\n const filtered = filterReportByRules(report, allowedRuleIds, merged.failOn);\n return { report: filtered, 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 maybePushFindings(\n report: AuditReport,\n flags: CliFlags,\n rootDir: string,\n): Promise<void> {\n if (flags.push !== true) return;\n if (!flags.apiKey) {\n process.stderr.write(\n 'vue-doctor: --push enabled but --api-key is not set; skipping push (no findings were sent).\\n',\n );\n return;\n }\n const url = flags.pushUrl;\n // --push-project is intentionally separate from --project (the #27 workspace\n // aggregation filter); reusing --project would make the CLI treat the SaaS\n // slug as a workspace package name and audit nothing.\n const project: string =\n flags.pushProject && flags.pushProject.length > 0\n ? flags.pushProject\n : basename(rootDir);\n const result = await pushFindings({\n project,\n score: report.score,\n errorCount: report.errorCount,\n warnCount: report.warnCount,\n infoCount: report.infoCount,\n diagnostics: report.diagnostics,\n rootDir,\n url,\n apiKey: flags.apiKey,\n });\n if (result.ok) {\n process.stderr.write(\n `vue-doctor: pushed ${report.diagnostics.length} finding${report.diagnostics.length === 1 ? '' : 's'} to ${url} (HTTP ${result.status}).\\n`,\n );\n } else {\n const detail =\n result.status !== undefined ? ` (HTTP ${result.status})` : '';\n process.stderr.write(\n `vue-doctor: --push failed${detail}: ${result.error}. Audit exit code is unchanged.\\n`,\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(\n '--category <name>',\n 'Only score rules in this category (repeatable)',\n )\n .option(\n '--dimension <name>',\n 'Only score this dimension: performance|correctness|security|maintainability|nuxt|design (repeatable)',\n )\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 .option(\n '--push',\n 'After the audit, POST privacy-stripped findings to the SaaS. Requires --api-key. Negatable with --no-push.',\n )\n .option(\n '--push-url <url>',\n 'SaaS endpoint for --push (default: https://app.the-doctor.report/api/v1/findings)',\n {\n default: 'https://app.the-doctor.report/api/v1/findings',\n },\n )\n .option(\n '--api-key <key>',\n 'API key for the SaaS (sent as the x-api-key header for both --score-mode and --push).',\n )\n .option(\n '--push-project <slug>',\n 'SaaS dashboard project slug for --push (e.g. owner/repo). Defaults to the audited directory name.',\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 categoryScope = resolveCategoryScope({\n categories: toArray(flags.category),\n dimensions: toArray(flags.dimension),\n });\n const prepared: PreparedOptions = {\n ruleOverrides,\n threshold,\n categoryScope,\n };\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 } else {\n emitReport(report, reporter, flags);\n }\n await maybePushFindings(report, flags, rootDir);\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 .alias('rules')\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 .alias('why')\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\n .command(\n 'install [dir]',\n 'Scaffold the vue-doctor agent skill so AI agents run doctor after changes',\n )\n .option('-y, --yes', 'Non-interactive, write defaults without prompting')\n .option('--force', 'Overwrite an existing skill')\n .option('--dry-run', 'Print what would be written, touch nothing')\n .action(async (dir: string | undefined, flags: InstallCliFlags) => {\n try {\n await runInstallSkill({\n dir: dir ?? '.',\n version: readVersion(),\n yes: flags.yes,\n force: flags.force,\n dryRun: flags.dryRun,\n });\n } catch (err) {\n process.stderr.write(\n `vue-doctor install: ${err instanceof Error ? err.message : err}\\n`,\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":";;;;;;;;AAaA,MAAM,SAAS;AACf,MAAM,UAAU;AAChB,MAAM,YAAY;AAClB,MAAM,eAAe;AACrB,MAAM,iBACJ;AAEF,SAAgB,mBAAmB,SAAyB;CAC1D,OAAO;QACD,OAAO;6EAC8D,UAAU,+EAA+E,UAAU;YACpK,QAAQ;;;IAGhB,UAAU;;SAEL,UAAU;;kBAED,UAAU;;YAEhB,QAAQ;;;;;;YAMR,QAAQ;;;;6CAIyB,OAAO;;;;;IAKhD,aAAa;;;;;kDAKiC,eAAe;;;;kIAIiE,QAAQ,4EAA4E,QAAQ;;;;;MAKxN,QAAQ;;;;;;;;;;;AAWd;AAEA,eAAsB,gBACpB,SACe;CACf,MAAM,YAAY,QAAQ,QAAQ,GAAG;CACrC,MAAM,WAAW,KAAK,WAAW,WAAW,UAAU,MAAM;CAC5D,MAAM,YAAY,KAAK,UAAU,UAAU;CAC3C,MAAM,UAAU,mBAAmB,QAAQ,OAAO;CAElD,IAAI,QAAQ,QAAQ;EAClB,QAAQ,OAAO,MAAM,yBAAyB,UAAU,GAAG;EAC3D,QAAQ,OAAO,MAAM,GAAG,QAAQ,GAAG;EACnC,QAAQ,WAAW;EACnB;CACF;CAEA,IAAI,WAAW,SAAS,KAAK,CAAC,QAAQ,OAAO;EAC3C,QAAQ,OAAO,MACb,GAAG,OAAO,YAAY,UAAU,kDAClC;EACA,QAAQ,WAAW;EACnB;CACF;CAEA,IAAI,CAAC,QAAQ,KAAK;EAChB,MAAM,UAAU,MAAM,QACpB;GACE,MAAM;GACN,MAAM;GACN,SAAS,gBAAgB,OAAO,kBAAkB,UAAU;GAC5D,SAAS;EACX,GACA,EACE,gBAAgB;GACd,QAAQ,WAAW;EACrB,EACF,CACF;EACA,IAAI,QAAQ,aAAa,GAAG;EAC5B,IAAI,CAAC,QAAQ,SAAS;GACpB,QAAQ,OAAO,MAAM,GAAG,OAAO,sBAAsB;GACrD,QAAQ,WAAW;GACnB;EACF;CACF;CAEA,UAAU,UAAU,EAAE,WAAW,KAAK,CAAC;CACvC,cAAc,WAAW,SAAS,MAAM;CACxC,QAAQ,OAAO,MAAM,GAAG,OAAO,kBAAkB,UAAU,GAAG;CAE9D,MAAM,aAAa,KAAK,WAAW,SAAS;CAC5C,IAAI,CAAC,WAAW,UAAU,GAAG;EAC3B,YAAY,WAAW,UAAU;EACjC,QAAQ,OAAO,MAAM,GAAG,OAAO,sCAAsC;CACvE;CAEA,QAAQ,WAAW;AACrB;;;AClFA,SAAS,cAAsB;CAC7B,IAAI;EACF,MAAM,OAAO,QAAQ,cAAc,OAAO,KAAK,GAAG,CAAC;EAInD,OAHY,KAAK,MACf,aAAa,QAAQ,MAAM,iBAAiB,GAAG,OAAO,CAE/C,CAAC,CAAC,WAAW;CACxB,QAAQ;EACN,OAAO;CACT;AACF;AA4CA,SAAS,kBAA2B;CAClC,MAAM,MAAM,QAAQ;CACpB,OAAO,QACL,IAAI,OAAO,UACX,IAAI,OAAO,OACX,IAAI,mBAAmB,UACvB,IAAI,cAAc,UAClB,IAAI,aAAa,UACjB,IAAI,WAAW,UACf,IAAI,cAAc,UAClB,IAAI,YACN;AACF;AAEA,SAAS,kBAAkB,GAAwC;CACjE,OAAO;EACL,WAAW,EAAE;EACb,eAAe,EAAE;EACjB,iBAAiB,EAAE;EACnB,YAAY,EAAE;EACd,aAAa,EAAE;EACf,0BAA0B,EAAE;EAC5B,aAAa,EAAE;EACf,mBAAmB,EAAE;EACrB,cAAc,EAAE;EAChB,gBAAgB,EAAE;EAClB,yBAAyB,EAAE;EAC3B,UAAU,EAAE;EACZ,cAAc,EAAE;EAChB,cAAc,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC,KAAK;CACzC;AACF;AAEA,SAAS,cAAc,GAAgB,SAAyB;CAC9D,MAAM,QAAkB,CAAC;CACzB,MAAM,KAAK,sBAAsB,YAAY,EAAE,wBAAwB;CACvE,MAAM,KAAK,EAAE;CACb,MAAM,KAAK,WAAW;CACtB,MAAM,KAAK,KAAK,EAAE,WAAW;CAC7B,MAAM,KAAK,UAAU,EAAE,cAAc,UAAU;CAC/C,MAAM,KAAK,iBAAiB,EAAE,qBAAqB,UAAU;CAC7D,MAAM,KAAK,EAAE;CACb,MAAM,KAAK,gBAAgB;CAC3B,MAAM,KAAK,oBAAoB,SAAS;CACxC,MAAM,KAAK,sBAAsB,EAAE,mBAAmB,UAAU;CAChE,MAAM,KAAK,mBAAmB,EAAE,gBAAgB,UAAU;CAC1D,MAAM,KAAK,EAAE;CACb,MAAM,KAAK,WAAW;CACtB,MAAM,KAAK,8BAA8B,EAAE,gBAAgB;CAC3D,MAAM,KAAK,8BAA8B,EAAE,yBAAyB;CACpE,MAAM,KAAK,8BAA8B,EAAE,UAAU;CACrD,MAAM,KAAK,8BAA8B,EAAE,cAAc;CACzD,MAAM,KAAK,EAAE;CACb,MAAM,KAAK,yCAAyC;CACpD,MAAM,KAAK,KAAK,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,MAAM,GAAG;CACzD,MAAM,KAAK,EAAE;CACb,MAAM,KAAK,oDAAoD;CAC/D,MAAM,KACJ,0EACF;CACA,OAAO,GAAG,MAAM,KAAK,IAAI,EAAE;AAC7B;AAaA,SAAS,cAAc,KAA6B;CAClD,MAAM,QAAkB,CAAC;CACzB,MAAM,KAAK,gBAAgB,IAAI,IAAI;CACnC,MAAM,KAAK,gBAAgB,IAAI,UAAU;CACzC,MAAM,KAAK,gBAAgB,IAAI,UAAU;CACzC,MAAM,KAAK,gBAAgB,IAAI,QAAQ;CACvC,MAAM,KACJ,gBAAgB,IAAI,cAAc,QAAQ,oCAC5C;CACA,MAAM,KAAK,gBAAgB,IAAI,SAAS;CACxC,MAAM,KAAK,EAAE;CACb,MAAM,KAAK,IAAI,WAAW;CAC1B,OAAO,GAAG,MAAM,KAAK,IAAI,EAAE;AAC7B;AAEA,MAAM,qBAAqB,IAAI,IAAI,CAAC,eAAe,KAAK,CAAC;AACzD,MAAM,mBAAmB,IAAI,IAAkB;CAC7C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AACD,MAAM,gBAAgB,IAAI,IAAgB;CACxC;CACA;CACA;AACF,CAAC;AACD,MAAM,uBAAuB,IAAI,IAAI;CAAC;CAAM;CAAQ;AAAc,CAAC;AAEnE,SAAS,qBAAqB,OAA2C;CACvE,MAAM,MAKF,CAAC;CACL,IAAI,MAAM,WAAW,KAAA,GAAW;EAC9B,IAAI,CAAC,mBAAmB,IAAI,MAAM,MAAM,GACtC,MAAM,IAAI,MACR,qBAAqB,MAAM,OAAO,gCACpC;EAEF,IAAI,SAAS,MAAM;CACrB;CACA,IAAI,MAAM,aAAa,KAAA,GAAW;EAChC,IAAI,CAAC,iBAAiB,IAAI,MAAM,QAAwB,GACtD,MAAM,IAAI,MAAM,uBAAuB,MAAM,SAAS,EAAE;EAE1D,IAAI,WAAW,MAAM;CACvB;CACA,IAAI,MAAM,WAAW,KAAA,GAAW;EAC9B,IAAI,CAAC,cAAc,IAAI,MAAM,MAAoB,GAC/C,MAAM,IAAI,MAAM,qBAAqB,MAAM,OAAO,EAAE;EAEtD,IAAI,SAAS,MAAM;CACrB;CACA,IAAI,MAAM,aAAa,KAAA,GAAW;EAChC,IACE,MAAM,aAAa,WACnB,MAAM,aAAa,UACnB,MAAM,aAAa,QAEnB,MAAM,IAAI,MACR,uBAAuB,MAAM,SAAS,kCACxC;EAEF,IAAI,WAAW,MAAM;CACvB;CACA,OAAO;AACT;AAEA,SAAS,iBAAiB,OAAiC;CACzD,IAAI,MAAM,WAAW,GAAG,OAAO;CAC/B,MAAM,QAAkB,CACtB,uBAAuB,MAAM,OAAO,OAAO,MAAM,WAAW,IAAI,KAAK,OACrE,EACF;CACA,MAAM,UAAU,KAAK,IAAI,GAAG,MAAM,KAAK,MAAM,EAAE,GAAG,MAAM,CAAC;CACzD,MAAM,WAAW,KAAK,IAAI,GAAG,MAAM,KAAK,MAAM,EAAE,SAAS,MAAM,CAAC;CAChE,MAAM,WAAW,KAAK,IAAI,GAAG,MAAM,KAAK,MAAM,EAAE,SAAS,MAAM,CAAC;CAChE,KAAK,MAAM,KAAK,OAAO;EACrB,MAAM,MAAM,EAAE,cAAc,kBAAkB;EAC9C,MAAM,KACJ,KAAK,EAAE,GAAG,OAAO,OAAO,EAAE,IAAI,EAAE,SAAS,OAAO,QAAQ,EAAE,IAAI,EAAE,SAAS,OAAO,QAAQ,EAAE,IAAI,EAAE,OAAO,IAAI,KAC7G;CACF;CACA,OAAO,GAAG,MAAM,KAAK,IAAI,EAAE;AAC7B;AA4CA,SAAS,QAAQ,OAA4D;CAC3E,IAAI,UAAU,KAAA,GAAW,OAAO,KAAA;CAChC,OAAO,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;AAC9C;AAEA,SAAS,mBACP,OAC8C;CAC9C,IAAI,CAAC,SAAS,MAAM,WAAW,GAAG,OAAO,KAAA;CACzC,MAAM,MAAwC,CAAC;CAC/C,KAAK,MAAM,SAAS,OAAO;EACzB,MAAM,MAAM,MAAM,YAAY,GAAG;EACjC,IAAI,QAAQ,IACV,MAAM,IAAI,MACR,mBAAmB,MAAM,mDAC3B;EAEF,MAAM,SAAS,MAAM,MAAM,GAAG,GAAG;EACjC,MAAM,QAAQ,MAAM,MAAM,MAAM,CAAC;EACjC,IACE,UAAU,WACV,UAAU,UACV,UAAU,UACV,UAAU,OAEV,MAAM,IAAI,MACR,qBAAqB,MAAM,gBAAgB,MAAM,iCACnD;EAEF,IAAI,OAAO,WAAW,GACpB,MAAM,IAAI,MAAM,mBAAmB,MAAM,8BAA8B;EAEzE,IAAI,UAAU;CAChB;CACA,OAAO;AACT;AAEA,SAAS,cAAc,OAAiC;CACtD,IAAI,MAAM,cAAc,KAAA,GAAW,OAAO;CAC1C,IAAI,MAAM,aAAa,OAAO;CAC9B,IAAI,MAAM,MAAM,OAAO;CACvB,MAAM,OAAO,MAAM;CAQnB,IANE,SAAS,YACT,SAAS,UACT,SAAS,kBACT,SAAS,WACT,SAAS,UACT,SAAS,cAET,OAAO;CAET,IACE,OAAO,MAAM,WAAW,YACxB,MAAM,OAAO,YAAY,CAAC,CAAC,SAAS,OAAO,GAE3C,OAAO;CAET,OAAO;AACT;AAEA,SAAS,cAAc,GAA2C;CAChE,OAAO,MAAM,WAAW,MAAM,UAAU,MAAM;AAChD;AAEA,eAAe,eACb,SACA,OACA,UACwD;CACxD,IAAI;CACJ,IAAI,CAAC,MAAM,SAAS,MAAM,QAAQ,MAAM,SACtC,aAAa,MAAM,iBAAiB;EAClC;EACA,MAAM,MAAM,SAAS,WAAW;CAClC,CAAC;CAEH,MAAM,WAAW,MAAM,iBAAiB,SAAS;EAC/C,GAAI,MAAM,SAAS,EAAE,cAAc,MAAM,OAAO,IAAI,CAAC;EACrD,GAAI,MAAM,SAAS,EAAE,gBAAgB,MAAM,OAAO,IAAI,CAAC;CACzD,CAAC;CACD,MAAM,aAAa,QAAQ,MAAM,UAAU;CAC3C,MAAM,SAA+B,kBAAkB,UAAU;EAC/D,QAAQ,MAAM;EACd,SAAS,QAAQ,MAAM,OAAO;EAC9B,SAAS,QAAQ,MAAM,OAAO;EAC9B,OAAO,SAAS;EAChB,WAAW,SAAS;EACpB,GAAI,aAAa,EAAE,aAAa,WAAW,IAAI,CAAC;CAClD,CAAC;CACD,MAAM,SAAS,MAAM,MAAM;EACzB,SAAS,OAAO;EAChB,SAAS,OAAO;EAChB,SAAS,OAAO;EAChB,OAAO,OAAO;EACd,QAAQ,OAAO;EACf,WAAW,OAAO;EAClB,UAAU,MAAM;EAChB,MAAM,MAAM;EACZ,uBAAuB,MAAM;EAC7B;EACA,KAAK,MAAM;EACX,GAAI,OAAO,cAAc,EAAE,aAAa,OAAO,YAAY,IAAI,CAAC;CAClE,CAAC;CACD,IAAI,iBAAiB,IAAI,IAAI,OAAO,KAAK,OAAO,KAAK,CAAC;CACtD,IAAI,SAAS,eACX,iBAAiB,wBACf,gBACA,SAAS,aACX;CAGF,OAAO;EAAE,QADQ,oBAAoB,QAAQ,gBAAgB,OAAO,MAC5C;EAAG,QAAQ,SAAS;CAAO;AACrD;AAEA,SAAS,iBACP,SACA,eACa;CACb,MAAM,QAAQ,QAAQ;CACtB,MAAM,cAAc,QAAQ,SAAS,MAAM,EAAE,WAAW;CACxD,IAAI,eAAe;CACnB,IAAI,YAAY;CAChB,IAAI,WAAsB;CAC1B,KAAK,MAAM,KAAK,SAAS;EACvB,gBAAgB,EAAE;EAClB,aAAa,EAAE;EACf,IAAI,EAAE,WAAW,UAAU,WAAW,EAAE;CAC1C;CACA,MAAM,aAAqC,CAAC;CAC5C,KAAK,MAAM,KAAK,aACd,WAAW,EAAE,WAAW,WAAW,EAAE,WAAW,KAAK;CAEvD,MAAM,cAAc,iBAAiB,aAAa,EAChD,WAAW,MAAM,YAAY,UAC/B,CAAC;CACD,OAAO;EACL,SAAS;EACT;EACA;EACA,OAAO,YAAY;EACnB,YAAY,YAAY;EACxB,WAAW,YAAY;EACvB,WAAW,YAAY;EACvB;EACA;EACA,aAAa;GAAE,GAAG,MAAM;GAAa;EAAc;EACnD;EACA,SAAS,MAAM;EACf;CACF;AACF;AAEA,SAAS,WACP,QACA,UACA,OACM;CAWN,MAAM,MAAM,OAAO;EATjB,UAAU;EACV,aAAa,YAAY;EACzB,eAAe,OAAO;EACtB,mBAAmB,OAAO;EAC1B,WAAW,OAAO;EAClB,aAAa,OAAO;EACpB,OAAO,OAAO;EACd,aAAa,OAAO;CAEC,GAAG,UAAU;EAClC,OAAO,MAAM;EACb,OAAO,MAAM;CACf,CAAC;CACD,IAAI,MAAM,QACR,cAAc,QAAQ,MAAM,MAAM,GAAG,GAAG;MAExC,QAAQ,OAAO,MAAM,GAAG;CAE1B,MAAM,mBAAmB,MAAM,OAAO;CACtC,MAAM,kBAAkB,MAAM,OAAO;CACrC,MAAM,iBAAiB,CAAC,oBAAoB,gBAAgB;CAK5D,KAHE,MAAM,gBAAgB,QAAQ,mBAAmB,oBAEjD,aAAa,WAAW,aAAa,aAIrC,CAAC,MAAM,SACP,OAAO,YAAY,SAAS,GAE5B,QAAQ,OAAO,MAAM,GAAG,kBAAkB,OAAO,WAAW,EAAE,GAAG;AAErE;AAEA,eAAe,kBACb,QACA,OACA,SACe;CACf,IAAI,MAAM,SAAS,MAAM;CACzB,IAAI,CAAC,MAAM,QAAQ;EACjB,QAAQ,OAAO,MACb,+FACF;EACA;CACF;CACA,MAAM,MAAM,MAAM;CAQlB,MAAM,SAAS,MAAM,aAAa;EAChC,SAJA,MAAM,eAAe,MAAM,YAAY,SAAS,IAC5C,MAAM,cACN,SAAS,OAAO;EAGpB,OAAO,OAAO;EACd,YAAY,OAAO;EACnB,WAAW,OAAO;EAClB,WAAW,OAAO;EAClB,aAAa,OAAO;EACpB;EACA;EACA,QAAQ,MAAM;CAChB,CAAC;CACD,IAAI,OAAO,IACT,QAAQ,OAAO,MACb,sBAAsB,OAAO,YAAY,OAAO,UAAU,OAAO,YAAY,WAAW,IAAI,KAAK,IAAI,MAAM,IAAI,SAAS,OAAO,OAAO,KACxI;MACK;EACL,MAAM,SACJ,OAAO,WAAW,KAAA,IAAY,UAAU,OAAO,OAAO,KAAK;EAC7D,QAAQ,OAAO,MACb,4BAA4B,OAAO,IAAI,OAAO,MAAM,kCACtD;CACF;AACF;AAEA,eAAe,iBACb,SACA,OACA,UACA,UAC+D;CAC/D,MAAM,YAAY,MACf,QAAS,MAAM,GAAG,CAAC,CACnB,KAAK,SAAS,KAAK,KAAK,CAAC,CAAC,CAC1B,QAAQ,SAAS,KAAK,SAAS,CAAC;CACnC,MAAM,SAAS,IAAI,IAAI,SAAS,KAAK,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;CACvD,MAAM,UAAyB,CAAC;CAChC,IAAI,SAAuB;CAC3B,KAAK,MAAM,QAAQ,WAAW;EAC5B,MAAM,QAAQ,OAAO,IAAI,IAAI;EAC7B,IAAI,CAAC,OAAO;GACV,QAAQ,OAAO,MACb,2CAA2C,KAAK,cAClD;GACA;EACF;EACA,MAAM,SAAS,MAAM,eAAe,MAAM,KAAK,OAAO,QAAQ;EAC9D,QAAQ,KAAK,OAAO,MAAM;EAC1B,SAAS,OAAO;CAClB;CACA,IAAI,QAAQ,WAAW,GAAG;EACxB,QAAQ,OAAO,MACb,yDACF;EACA,OAAO;CACT;CACA,MAAM,EAAE,SAAS,MAAM,iBAAiB,OAAO;CAC/C,OAAO;EAAE,QAAQ,iBAAiB,SAAS,IAAI;EAAG;CAAO;AAC3D;AAEA,eAAe,QAAQ,KAAa,OAAoC;CACtE,MAAM,YAAY,QAAQ,GAAG;CAC7B,MAAM,eAAgB,MAAM,gBAAgB;CAC5C,IAAI,CAAC,qBAAqB,IAAI,YAAY,GAAG;EAC3C,QAAQ,OAAO,MACb,2EAA2E,MAAM,aAAa,IAChG;EACA,QAAQ,WAAW;EACnB;CACF;CAEA,MAAM,kBAAkB,MAAM,MAC1B;EACE;EACA,QAAQ;EACR,WAAW,KAAA;EACX,SAAS,KAAA;CACX,IACA,MAAM,mBAAmB,YAAY;CAEzC,IAAI,QAAQ,aAAa,GAAG;CAE5B,MAAM,OAAO,MAAM,SAAS;EAC1B,KAAK;EACL,cAAc,gBAAgB;EAC9B,QAAQ,gBAAgB;EACxB,WAAW,gBAAgB;EAC3B,SAAS,gBAAgB;EACzB,SAAS;CACX,CAAC;CAED,MAAM,UAAU,MAAM,cAAc,SAAS;CAC7C,QAAQ,OAAO,MAAM,GAAG,QAAQ,GAAG;CAEnC,IAAI,KAAK,YAAY,CAAC,MAAM,OAAO;EACjC,QAAQ,OAAO,MACb,oBAAoB,KAAK,aAAa,kDACxC;EACA,QAAQ,WAAW;EACnB;CACF;CAEA,IAAI,MAAM,QAAQ;EAChB,KAAK,MAAM,SAAS,KAAK,QAAQ;GAC/B,QAAQ,OAAO,MAAM,yBAAyB,MAAM,KAAK,GAAG;GAC5D,QAAQ,OAAO,MAAM,GAAG,MAAM,QAAQ,GAAG;EAC3C;EACA,QAAQ,WAAW;EACnB;CACF;CAEA,KAAK,MAAM,SAAS,KAAK,QACvB,cAAc,MAAM,MAAM,MAAM,SAAS,MAAM;CAEjD,QAAQ,WAAW;AACrB;AAEA,eAAe,mBAAmB,eAK/B;CACD,MAAM,UAAU,MAAM,QACpB;EACE;GACE,MAAM;GACN,MAAM;GACN,SAAS;GACT,SAAS;IACP;KAAE,OAAO;KAAkC,OAAO;IAAK;IACvD;KAAE,OAAO;KAAsB,OAAO;IAAO;IAC7C;KAAE,OAAO;KAAuB,OAAO;IAAe;GACxD;GACA,SAAS,kBAAkB,iBAAiB,IAAI;EAClD;EACA;GACE,MAAM;GACN,MAAM;GACN,SAAS;GACT,SAAS;IACP;KAAE,OAAO;KAAmC,OAAO;IAAc;IACjE;KAAE,OAAO;KAA+B,OAAO;IAAS;IACxD;KAAE,OAAO;KAAyB,OAAO;IAAU;GACrD;GACA,SAAS;EACX;EACA;GACE,MAAM;GACN,MAAM;GACN,SAAS;GACT,KAAK;GACL,KAAK;GACL,SAAS;EACX;EACA;GACE,MAAM;GACN,MAAM;GACN,SAAS;GACT,SAAS;EACX;CACF,GACA,EACE,gBAAgB;EACd,QAAQ,OAAO,MAAM,8BAA8B;EACnD,QAAQ,WAAW;CACrB,EACF,CACF;CAEA,IAAI,QAAQ,aAAa,GACvB,OAAO;EACL,cAAc;EACd,QAAQ;EACR,WAAW,KAAA;EACX,SAAS,KAAA;CACX;CAEF,OAAO,qBAAqB;EAC1B,QAAQ,QAAQ;EAChB,QAAQ,QAAQ;EAChB,WAAW,QAAQ,aAAa,KAAA;EAChC,SAAS,QAAQ;CACnB,CAAC;AACH;AAEA,eAAsB,IAAI,OAAiB,QAAQ,MAAuB;CACxE,MAAM,MAAM,IAAI,YAAY;CAE5B,IACG,QAAQ,UAAU,qBAAqB,CAAC,CACxC,OACC,mBACA,wEACA,EACE,SAAS,QACX,CACF,CAAC,CACA,OAAO,UAAU,6BAA6B,CAAC,CAC/C,OAAO,kBAAkB,uBAAuB,CAAC,CACjD,OAAO,mBAAmB,0BAA0B,CAAC,CACrD,OAAO,mBAAmB,6CAA6C,CAAC,CACxE,OACC,qBACA,+DACA,EACE,SAAS,QACX,CACF,CAAC,CACA,OAAO,WAAW,uBAAuB,CAAC,CAC1C,OAAO,aAAa,qDAAqD,CAAC,CAC1E,OAAO,cAAc,wBAAwB,CAAC,CAC9C,OACC,qBACA,mDACF,CAAC,CACA,OAAO,oBAAoB,uCAAuC,CAAC,CACnE,OAAO,oBAAoB,uCAAuC,CAAC,CACnE,OACC,qBACA,gDACF,CAAC,CACA,OACC,sBACA,sGACF,CAAC,CACA,OAAO,kBAAkB,yCAAyC,CAAC,CACnE,OAAO,aAAa,4CAA4C,CAAC,CACjE,OACC,gCACA,sDACF,CAAC,CACA,OAAO,mBAAmB,+BAA+B,CAAC,CAC1D,OAAO,WAAW,4CAA4C,CAAC,CAC/D,OAAO,iBAAiB,iDAAiD,CAAC,CAC1E,OAAO,QAAQ,2DAA2D,CAAC,CAC3E,OAAO,WAAW,mDAAmD,CAAC,CACtE,OAAO,UAAU,+CAA+C,CAAC,CACjE,OAAO,YAAY,sCAAsC,CAAC,CAC1D,OAAO,UAAU,mDAAmD,CAAC,CACrE,OACC,oBACA,6DACF,CAAC,CACA,OAAO,mBAAmB,8CAA8C,CAAC,CACzE,OAAO,gBAAgB,yCAAyC,CAAC,CACjE,OACC,SACA,+JACF,CAAC,CACA,OACC,0BACA,iLACF,CAAC,CACA,OACC,UACA,4GACF,CAAC,CACA,OACC,oBACA,qFACA,EACE,SAAS,gDACX,CACF,CAAC,CACA,OACC,mBACA,uFACF,CAAC,CACA,OACC,yBACA,mGACF,CAAC,CACA,OAAO,OAAO,MAA0B,UAAoB;EAC3D,MAAM,WAAW,cAAc,KAAK;EACpC,IAAI,MAAM,WAAW,KAAA,KAAa,CAAC,cAAc,MAAM,MAAM,GAAG;GAC9D,QAAQ,OAAO,MACb,kEAAkE,MAAM,OAAO,IACjF;GACA,QAAQ,WAAW;GACnB;EACF;EACA,MAAM,UAAU,QAAQ,QAAQ,GAAG;EAEnC,IAAI;GACF,IAAI,MAAM,UAAU,MAAM,QAAQ,MAAM,cACtC,MAAM,IAAI,MACR,kFACF;GAEF,MAAM,gBAAgB,mBAAmB,QAAQ,MAAM,IAAI,CAAC;GAC5D,MAAM,YACJ,MAAM,cAAc,KAAA,IAAY,KAAA,IAAY,OAAO,MAAM,SAAS;GACpE,IACE,cAAc,KAAA,MACb,CAAC,OAAO,UAAU,SAAS,KAAK,YAAY,KAAK,YAAY,MAE9D,MAAM,IAAI,MACR,8CAA8C,MAAM,UAAU,GAChE;GAEF,IAAI,MAAM,QAAQ,MAAM,QACtB,MAAM,IAAI,MAAM,6CAA6C;GAE/D,IAAI,MAAM,WAAW,MAAM,OACzB,MAAM,IAAI,MAAM,+CAA+C;GAEjE,MAAM,YAAY,MAAM,QAAQ;GAChC,MAAM,qBACJ,aAAa,CAAC,MAAM,SAAS,MAAM,QAAQ,MAAM;GACnD,IAAI,oBACF,QAAQ,OAAO,MACb,oIACF;GAEF,IAAI,aAAa,MAAM,eAAe,KAAA,GACpC,QAAQ,OAAO,MACb,kKACF;GAMF,MAAM,WAA4B;IAChC;IACA;IACA,eAPoB,qBAAqB;KACzC,YAAY,QAAQ,MAAM,QAAQ;KAClC,YAAY,QAAQ,MAAM,SAAS;IACrC,CAIc;GACd;GAEA,MAAM,oBAAoB,MAAM,UAC5B,MAAM,sBAAsB,OAAO,IACnC,CAAC;GACL,IAAI,MAAM,WAAW,kBAAkB,WAAW,GAChD,QAAQ,OAAO,MACb,uDACF;GAGF,IAAI;GACJ,IAAI;GACJ,IAAI,MAAM,WAAW,kBAAkB,SAAS,GAAG;IACjD,MAAM,aAAa,MAAM,iBACvB,SACA,OACA,UACA,iBACF;IACA,IAAI,CAAC,YAAY;KACf,QAAQ,WAAW;KACnB;IACF;IACA,SAAS,WAAW;IACpB,eAAe,WAAW;GAC5B,OAAO;IACL,MAAM,SAAS,MAAM,eAAe,SAAS,OAAO,QAAQ;IAC5D,SAAS,OAAO;IAChB,eAAe,OAAO;GACxB;GAEA,IAAI,MAAM,SAAS;IACjB,MAAM,gBAAgB,mBAAmB,QAAQ,EAC/C,aACF,CAAC;IACD,QAAQ,OAAO,MAAM,GAAG,cAAc,GAAG;GAC3C;GACA,IAAI,aAAa,CAAC,oBAChB,QAAQ,OAAO,MACb,qCAAqC,OAAO,YAAY,OAAO,UAAU,OAAO,YAAY,WAAW,IAAI,KAAK,IAAI,gCACtH;GAEF,IAAI,MAAM,OACR,QAAQ,OAAO,MAAM,GAAG,OAAO,MAAM,GAAG;QAExC,WAAW,QAAQ,UAAU,KAAK;GAEpC,MAAM,kBAAkB,QAAQ,OAAO,OAAO;GAC9C,QAAQ,WAAW,OAAO;EAC5B,SAAS,KAAK;GACZ,MAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;GAC3D,QAAQ,OAAO,MAAM,eAAe,IAAI,GAAG;GAC3C,QAAQ,WAAW;EACrB;CACF,CAAC;CAEH,IACG,QACC,cACA,uFACF,CAAC,CACA,MAAM,OAAO,CAAC,CACd,OAAO,mBAAmB,8BAA8B,CAAC,CACzD,OAAO,qBAAqB,oBAAoB,CAAC,CACjD,OACC,mBACA,+DACF,CAAC,CACA,OAAO,sBAAsB,gCAAgC,CAAC,CAC9D,OAAO,UAAU,qCAAqC,CAAC,CACvD,OAAO,kBAAkB,iCAAiC,CAAC,CAC3D,OAAO,OAAO,UAA6B;EAC1C,IAAI;GAEF,MAAM,QAAQ,UADC,qBAAqB,KACP,CAAC;GAC9B,IAAI,MAAM,QAAQ,MAAM,aAAa;IACnC,MAAM,UAAU;KAAE,OAAO,MAAM;KAAQ;IAAM;IAC7C,QAAQ,OAAO,MACb,MAAM,cACF,KAAK,UAAU,OAAO,IACtB,GAAG,KAAK,UAAU,SAAS,MAAM,CAAC,EAAE,GAC1C;GACF,OACE,QAAQ,OAAO,MAAM,iBAAiB,KAAK,CAAC;GAE9C,QAAQ,WAAW;EACrB,SAAS,KAAK;GACZ,MAAM,MAAO,IAAc;GAC3B,QAAQ,OAAO,MAAM,0BAA0B,IAAI,GAAG;GACtD,QAAQ,WAAW;EACrB;CACF,CAAC;CAEH,IACG,QACC,oBACA,kEACF,CAAC,CACA,MAAM,KAAK,CAAC,CACZ,OAAO,UAAU,gDAAgD,CAAC,CAClE,OAAO,OAAO,QAAgB,UAA2B;EACxD,MAAM,MAAM,YAAY,MAAM;EAC9B,IAAI,CAAC,KAAK;GACR,QAAQ,OAAO,MACb,qCAAqC,OAAO,4DAC9C;GACA,QAAQ,WAAW;GACnB;EACF;EACA,IAAI,MAAM,MACR,QAAQ,OAAO,MAAM,GAAG,KAAK,UAAU,KAAK,MAAM,CAAC,EAAE,GAAG;OAExD,QAAQ,OAAO,MAAM,cAAc,GAAG,CAAC;EAEzC,QAAQ,WAAW;CACrB,CAAC;CAEH,IACG,QACC,iBACA,mEACF,CAAC,CACA,OAAO,UAAU,gDAAgD,CAAC,CAClE,OAAO,kBAAkB,yCAAyC,CAAC,CACnE,OAAO,OAAO,KAAyB,UAA2B;EACjE,MAAM,UAAU,QAAQ,OAAO,GAAG;EAClC,MAAM,UAAU,MAAM,cAAc,OAAO;EAC3C,IAAI,MAAM,QAAQ,MAAM,aAAa;GACnC,MAAM,UAAU,kBAAkB,OAAO;GACzC,MAAM,MAAM,MAAM,cACd,KAAK,UAAU,OAAO,IACtB,KAAK,UAAU,SAAS,MAAM,CAAC;GACnC,QAAQ,OAAO,MAAM,GAAG,IAAI,GAAG;EACjC,OACE,QAAQ,OAAO,MAAM,cAAc,SAAS,OAAO,CAAC;EAEtD,QAAQ,WAAW;CACrB,CAAC;CAEH,IACG,QAAQ,cAAc,0CAA0C,CAAC,CACjE,OAAO,aAAa,2CAA2C,CAAC,CAChE,OACC,0CACA,kCACF,CAAC,CACA,OAAO,WAAW,2BAA2B,CAAC,CAC9C,OAAO,aAAa,4CAA4C,CAAC,CACjE,OAAO,OAAO,KAAyB,UAAwB;EAC9D,IAAI;GACF,MAAM,QAAQ,OAAO,KAAK,KAAK;EACjC,SAAS,KAAK;GACZ,QAAQ,OAAO,MAAM,GAAG,eAAe,QAAQ,IAAI,UAAU,IAAI,GAAG;GACpE,QAAQ,WAAW;EACrB;CACF,CAAC;CAEH,IACG,QACC,iBACA,2EACF,CAAC,CACA,OAAO,aAAa,mDAAmD,CAAC,CACxE,OAAO,WAAW,6BAA6B,CAAC,CAChD,OAAO,aAAa,4CAA4C,CAAC,CACjE,OAAO,OAAO,KAAyB,UAA2B;EACjE,IAAI;GACF,MAAM,gBAAgB;IACpB,KAAK,OAAO;IACZ,SAAS,YAAY;IACrB,KAAK,MAAM;IACX,OAAO,MAAM;IACb,QAAQ,MAAM;GAChB,CAAC;EACH,SAAS,KAAK;GACZ,QAAQ,OAAO,MACb,uBAAuB,eAAe,QAAQ,IAAI,UAAU,IAAI,GAClE;GACA,QAAQ,WAAW;EACrB;CACF,CAAC;CAEH,IAAI,KAAK;CACT,IAAI,QAAQ,YAAY,CAAC;CACzB,IAAI,MAAM,MAAM,EAAE,KAAK,MAAM,CAAC;CAC9B,MAAM,IAAI,kBAAkB;CAC5B,OAAO,QAAQ,YAAY;AAC7B"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/install/install-skill.ts","../src/cli.ts"],"sourcesContent":["import { existsSync, mkdirSync, symlinkSync, writeFileSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\nimport process from 'node:process';\nimport prompts from 'prompts';\n\ninterface InstallSkillOptions {\n dir: string;\n version: string;\n yes?: boolean;\n dryRun?: boolean;\n force?: boolean;\n}\n\nconst PLUGIN = 'vue-doctor';\nconst PACKAGE = '@geoql/vue-doctor';\nconst FRAMEWORK = 'Vue';\nconst PLAYBOOK_URL = 'https://docs.the-doctor.report/prompts/doctor-agent.md';\nconst RULES_URL_BASE =\n 'https://docs.the-doctor.report/prompts/rules/vue-doctor/';\n\nexport function buildSkillMarkdown(version: string): string {\n return `---\nname: ${PLUGIN}\ndescription: Use when finishing a feature, fixing a bug, before committing ${FRAMEWORK} code, or when the user types \\`/doctor\\`, asks to scan, triage, or clean up ${FRAMEWORK} diagnostics. Covers reactivity, composition, performance, templates, and AI-slop. Includes a regression check and a full local-triage workflow that fetches the canonical playbook.\nversion: \"${version}\"\n---\n\n# ${FRAMEWORK} Doctor\n\nAudits ${FRAMEWORK} codebases for reactivity leaks, composition errors, performance traps, template issues, and AI-agent anti-patterns. Outputs a 0–100 health score.\n\n## After making ${FRAMEWORK} code changes:\n\nRun \\`npx ${PACKAGE}@latest --diff --verbose\\` and check the score did not regress. \\`--diff\\` reports only issues introduced versus the base branch.\n\nIf the score dropped, fix the regressions before committing.\n\n## For general cleanup or code improvement:\n\nRun \\`npx ${PACKAGE}@latest --verbose\\` to scan the full codebase. Fix issues by severity — errors first, then warnings.\n\n## /doctor — full local triage workflow\n\nWhen the user types \\`/doctor\\`, says \"run ${PLUGIN}\", or asks for a full triage / cleanup pass (not just a regression check), fetch the canonical local-triage playbook and follow every step in it:\n\n\\`\\`\\`bash\ncurl --fail --silent --show-error \\\\\n --header 'Cache-Control: no-cache' \\\\\n ${PLAYBOOK_URL}\n\\`\\`\\`\n\nThe playbook is the single source of truth — a scan → filter → triage → fix → validate loop that edits the working tree directly (never commits, never opens PRs). Updating the prompt at its source updates every agent on its next fetch — no skill reinstall needed.\n\nPair it with the matching per-rule prompts at \\`${RULES_URL_BASE}<rule>.md\\` (fetched on demand inside the playbook) so each fix uses the canonical, reviewer-tested recipe.\n\n## Configuring or explaining rules\n\nWhen the user wants to understand a rule, disagrees with one, or wants to tune which rules run (not fix code), start with \\`npx ${PACKAGE}@latest explain <rule>\\` (alias \\`why\\`), then list everything with \\`npx ${PACKAGE}@latest list-rules\\` (alias \\`rules\\`), and edit your \\`doctor.config.*\\` to disable or re-level a rule.\n\n## Command\n\n\\`\\`\\`bash\nnpx ${PACKAGE}@latest --diff --verbose\n\\`\\`\\`\n\n| Flag | Purpose |\n| ----------- | --------------------------------------------------------------- |\n| \\`.\\` | Scan current directory |\n| \\`--verbose\\` | Show affected files and line numbers per rule |\n| \\`--diff\\` | Only report issues introduced vs the base branch (default: full)|\n| \\`--score\\` | Output only the numeric score |\n| \\`--json\\` | Emit machine-readable JSON |\n`;\n}\n\nexport async function runInstallSkill(\n options: InstallSkillOptions,\n): Promise<void> {\n const targetDir = resolve(options.dir);\n const skillDir = join(targetDir, '.agents', 'skills', PLUGIN);\n const skillPath = join(skillDir, 'SKILL.md');\n const content = buildSkillMarkdown(options.version);\n\n if (options.dryRun) {\n process.stdout.write(`[dry-run] would write ${skillPath}\\n`);\n process.stdout.write(`${content}\\n`);\n process.exitCode = 0;\n return;\n }\n\n if (existsSync(skillPath) && !options.force) {\n process.stderr.write(\n `${PLUGIN} install: ${skillPath} already exists. Run with --force to overwrite.\\n`,\n );\n process.exitCode = 2;\n return;\n }\n\n if (!options.yes) {\n const answers = await prompts(\n {\n type: 'confirm',\n name: 'confirm',\n message: `Scaffold the ${PLUGIN} agent skill at ${skillPath}?`,\n initial: true,\n },\n {\n onCancel: () => {\n process.exitCode = 2;\n },\n },\n );\n if (process.exitCode === 2) return;\n if (!answers.confirm) {\n process.stdout.write(`${PLUGIN} install: cancelled\\n`);\n process.exitCode = 0;\n return;\n }\n }\n\n mkdirSync(skillDir, { recursive: true });\n writeFileSync(skillPath, content, 'utf8');\n process.stdout.write(`${PLUGIN} install: wrote ${skillPath}\\n`);\n\n const claudePath = join(targetDir, '.claude');\n if (!existsSync(claudePath)) {\n symlinkSync('.agents', claudePath);\n process.stdout.write(`${PLUGIN} install: linked .claude -> .agents\\n`);\n }\n\n process.exitCode = 0;\n}\n","import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { basename, 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 pushFindings,\n scaffoldCiWorkflow,\n renderVerboseTrace,\n scoreDiagnostics,\n filterReportByRules,\n filterRuleIdsByCategory,\n resolveCategoryScope,\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';\nimport { runInstallSkill } from './install/install-skill.js';\n\ninterface InstallCliFlags {\n yes?: boolean;\n force?: boolean;\n dryRun?: boolean;\n}\n\ninterface CiInstallCliFlags {\n pr?: boolean;\n comments?: boolean;\n provider?: string;\n yes?: boolean;\n force?: boolean;\n dryRun?: boolean;\n}\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 push?: boolean;\n pushUrl: string;\n apiKey?: string;\n pushProject?: string;\n category?: string | string[];\n dimension?: string | string[];\n maxDuration?: string | number;\n}\n\ninterface PreparedOptions {\n ruleOverrides: Record<string, Severity | 'off'> | undefined;\n threshold: number | undefined;\n categoryScope: ReadonlySet<RuleCategory> | undefined;\n maxDurationMs: 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\nfunction parseMaxDurationMs(\n value: string | number | undefined,\n): number | undefined {\n if (value === undefined) return undefined;\n const seconds = Number(value);\n if (!Number.isInteger(seconds) || seconds < 0) {\n throw new Error(\n `--max-duration must be a non-negative integer number of seconds, got \"${value}\".`,\n );\n }\n return seconds * 1000;\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 ...(prepared.maxDurationMs !== undefined\n ? { maxDurationMs: prepared.maxDurationMs }\n : {}),\n });\n let allowedRuleIds = new Set(Object.keys(merged.rules));\n if (prepared.categoryScope) {\n allowedRuleIds = filterRuleIdsByCategory(\n allowedRuleIds,\n prepared.categoryScope,\n );\n }\n const filtered = filterReportByRules(report, allowedRuleIds, merged.failOn);\n return { report: filtered, 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 const skippedCheckReasons = reports.flatMap(\n (r) => r.skippedCheckReasons ?? [],\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 incomplete: reports.some((r) => r.incomplete),\n ...(skippedCheckReasons.length > 0 ? { skippedCheckReasons } : {}),\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 incomplete: report.incomplete,\n ...(report.skippedCheckReasons\n ? { skippedCheckReasons: report.skippedCheckReasons }\n : {}),\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 maybePushFindings(\n report: AuditReport,\n flags: CliFlags,\n rootDir: string,\n): Promise<void> {\n if (flags.push !== true) return;\n if (!flags.apiKey) {\n process.stderr.write(\n 'vue-doctor: --push enabled but --api-key is not set; skipping push (no findings were sent).\\n',\n );\n return;\n }\n const url = flags.pushUrl;\n // --push-project is intentionally separate from --project (the #27 workspace\n // aggregation filter); reusing --project would make the CLI treat the SaaS\n // slug as a workspace package name and audit nothing.\n const project: string =\n flags.pushProject && flags.pushProject.length > 0\n ? flags.pushProject\n : basename(rootDir);\n const result = await pushFindings({\n project,\n score: report.score,\n errorCount: report.errorCount,\n warnCount: report.warnCount,\n infoCount: report.infoCount,\n diagnostics: report.diagnostics,\n rootDir,\n url,\n apiKey: flags.apiKey,\n });\n if (result.ok) {\n process.stderr.write(\n `vue-doctor: pushed ${report.diagnostics.length} finding${report.diagnostics.length === 1 ? '' : 's'} to ${url} (HTTP ${result.status}).\\n`,\n );\n } else {\n const detail =\n result.status !== undefined ? ` (HTTP ${result.status})` : '';\n process.stderr.write(\n `vue-doctor: --push failed${detail}: ${result.error}. Audit exit code is unchanged.\\n`,\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\nconst VALID_CI_PROVIDERS = new Set(['github', 'gitlab', 'auto']);\n\nasync function runCiInstall(\n dir: string,\n flags: CiInstallCliFlags,\n): Promise<void> {\n const targetDir = resolve(dir);\n const provider = flags.provider ?? 'auto';\n if (!VALID_CI_PROVIDERS.has(provider)) {\n process.stderr.write(\n `vue-doctor ci install: --provider must be github | gitlab | auto, got '${flags.provider}'\\n`,\n );\n process.exitCode = 2;\n return;\n }\n\n const plan = await scaffoldCiWorkflow({\n bin: 'vue-doctor',\n framework: 'vue',\n dir: targetDir,\n provider: provider as 'github' | 'gitlab' | 'auto',\n pr: flags.pr === true,\n noComments: flags.comments === false,\n force: flags.force === true,\n });\n\n if (plan.conflict) {\n process.stderr.write(\n `vue-doctor ci install: ${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 if (!flags.yes) {\n const answers = await prompts(\n {\n type: 'confirm',\n name: 'confirm',\n message: `Scaffold the ${plan.provider} CI workflow at ${plan.writes[0]?.path}?`,\n initial: true,\n },\n {\n onCancel: () => {\n process.exitCode = 2;\n },\n },\n );\n if (process.exitCode === 2) return;\n if (!answers.confirm) {\n process.stdout.write('vue-doctor ci install: cancelled\\n');\n process.exitCode = 0;\n return;\n }\n }\n\n for (const write of plan.writes) {\n mkdirSync(dirname(write.path), { recursive: true });\n writeFileSync(write.path, write.content, 'utf8');\n process.stdout.write(`vue-doctor ci install: wrote ${write.path}\\n`);\n }\n process.exitCode = 0;\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(\n '--category <name>',\n 'Only score rules in this category (repeatable)',\n )\n .option(\n '--dimension <name>',\n 'Only score this dimension: performance|correctness|security|maintainability|nuxt|design (repeatable)',\n )\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 .option(\n '--push',\n 'After the audit, POST privacy-stripped findings to the SaaS. Requires --api-key. Negatable with --no-push.',\n )\n .option(\n '--push-url <url>',\n 'SaaS endpoint for --push (default: https://app.the-doctor.report/api/v1/findings)',\n {\n default: 'https://app.the-doctor.report/api/v1/findings',\n },\n )\n .option(\n '--api-key <key>',\n 'API key for the SaaS (sent as the x-api-key header for both --score-mode and --push).',\n )\n .option(\n '--push-project <slug>',\n 'SaaS dashboard project slug for --push (e.g. owner/repo). Defaults to the audited directory name.',\n )\n .option(\n '--max-duration <seconds>',\n 'Abort remaining audit passes after N seconds; the report gains incomplete=true + skippedCheckReasons',\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 categoryScope = resolveCategoryScope({\n categories: toArray(flags.category),\n dimensions: toArray(flags.dimension),\n });\n const prepared: PreparedOptions = {\n ruleOverrides,\n threshold,\n categoryScope,\n maxDurationMs: parseMaxDurationMs(flags.maxDuration),\n };\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 } else {\n emitReport(report, reporter, flags);\n }\n await maybePushFindings(report, flags, rootDir);\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 .alias('rules')\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 .alias('why')\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\n .command(\n 'install [dir]',\n 'Scaffold the vue-doctor agent skill so AI agents run doctor after changes',\n )\n .option('-y, --yes', 'Non-interactive, write defaults without prompting')\n .option('--force', 'Overwrite an existing skill')\n .option('--dry-run', 'Print what would be written, touch nothing')\n .action(async (dir: string | undefined, flags: InstallCliFlags) => {\n try {\n await runInstallSkill({\n dir: dir ?? '.',\n version: readVersion(),\n yes: flags.yes,\n force: flags.force,\n dryRun: flags.dryRun,\n });\n } catch (err) {\n process.stderr.write(\n `vue-doctor install: ${err instanceof Error ? err.message : err}\\n`,\n );\n process.exitCode = 2;\n }\n });\n\n cli\n .command(\n 'ci <action> [dir]',\n \"CI integrations. Action 'install' scaffolds a workflow referencing geoql/doctor-action@v2\",\n )\n .option('--pr', 'Also scaffold a PR-review workflow')\n .option('--no-comments', 'Disable PR comments (sets pr-comment=false)')\n .option(\n '--provider <name>',\n 'CI provider: github | gitlab | auto (default: auto)',\n )\n .option('-y, --yes', 'Non-interactive, write defaults without prompting')\n .option('--force', 'Overwrite an existing workflow')\n .option('--dry-run', 'Print what would be written, touch nothing')\n .action(\n async (\n action: string,\n dir: string | undefined,\n flags: CiInstallCliFlags,\n ) => {\n if (action !== 'install') {\n process.stderr.write(\n `vue-doctor ci: unknown action '${action}' (expected: install)\\n`,\n );\n process.exitCode = 2;\n return;\n }\n try {\n await runCiInstall(dir ?? '.', flags);\n } catch (err) {\n process.stderr.write(\n `vue-doctor ci install: ${err instanceof Error ? err.message : err}\\n`,\n );\n process.exitCode = 2;\n }\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":";;;;;;;;AAaA,MAAM,SAAS;AACf,MAAM,UAAU;AAChB,MAAM,YAAY;AAClB,MAAM,eAAe;AACrB,MAAM,iBACJ;AAEF,SAAgB,mBAAmB,SAAyB;CAC1D,OAAO;QACD,OAAO;6EAC8D,UAAU,+EAA+E,UAAU;YACpK,QAAQ;;;IAGhB,UAAU;;SAEL,UAAU;;kBAED,UAAU;;YAEhB,QAAQ;;;;;;YAMR,QAAQ;;;;6CAIyB,OAAO;;;;;IAKhD,aAAa;;;;;kDAKiC,eAAe;;;;kIAIiE,QAAQ,4EAA4E,QAAQ;;;;;MAKxN,QAAQ;;;;;;;;;;;AAWd;AAEA,eAAsB,gBACpB,SACe;CACf,MAAM,YAAY,QAAQ,QAAQ,GAAG;CACrC,MAAM,WAAW,KAAK,WAAW,WAAW,UAAU,MAAM;CAC5D,MAAM,YAAY,KAAK,UAAU,UAAU;CAC3C,MAAM,UAAU,mBAAmB,QAAQ,OAAO;CAElD,IAAI,QAAQ,QAAQ;EAClB,QAAQ,OAAO,MAAM,yBAAyB,UAAU,GAAG;EAC3D,QAAQ,OAAO,MAAM,GAAG,QAAQ,GAAG;EACnC,QAAQ,WAAW;EACnB;CACF;CAEA,IAAI,WAAW,SAAS,KAAK,CAAC,QAAQ,OAAO;EAC3C,QAAQ,OAAO,MACb,GAAG,OAAO,YAAY,UAAU,kDAClC;EACA,QAAQ,WAAW;EACnB;CACF;CAEA,IAAI,CAAC,QAAQ,KAAK;EAChB,MAAM,UAAU,MAAM,QACpB;GACE,MAAM;GACN,MAAM;GACN,SAAS,gBAAgB,OAAO,kBAAkB,UAAU;GAC5D,SAAS;EACX,GACA,EACE,gBAAgB;GACd,QAAQ,WAAW;EACrB,EACF,CACF;EACA,IAAI,QAAQ,aAAa,GAAG;EAC5B,IAAI,CAAC,QAAQ,SAAS;GACpB,QAAQ,OAAO,MAAM,GAAG,OAAO,sBAAsB;GACrD,QAAQ,WAAW;GACnB;EACF;CACF;CAEA,UAAU,UAAU,EAAE,WAAW,KAAK,CAAC;CACvC,cAAc,WAAW,SAAS,MAAM;CACxC,QAAQ,OAAO,MAAM,GAAG,OAAO,kBAAkB,UAAU,GAAG;CAE9D,MAAM,aAAa,KAAK,WAAW,SAAS;CAC5C,IAAI,CAAC,WAAW,UAAU,GAAG;EAC3B,YAAY,WAAW,UAAU;EACjC,QAAQ,OAAO,MAAM,GAAG,OAAO,sCAAsC;CACvE;CAEA,QAAQ,WAAW;AACrB;;;ACxEA,SAAS,cAAsB;CAC7B,IAAI;EACF,MAAM,OAAO,QAAQ,cAAc,OAAO,KAAK,GAAG,CAAC;EAInD,OAHY,KAAK,MACf,aAAa,QAAQ,MAAM,iBAAiB,GAAG,OAAO,CAE/C,CAAC,CAAC,WAAW;CACxB,QAAQ;EACN,OAAO;CACT;AACF;AA4CA,SAAS,kBAA2B;CAClC,MAAM,MAAM,QAAQ;CACpB,OAAO,QACL,IAAI,OAAO,UACX,IAAI,OAAO,OACX,IAAI,mBAAmB,UACvB,IAAI,cAAc,UAClB,IAAI,aAAa,UACjB,IAAI,WAAW,UACf,IAAI,cAAc,UAClB,IAAI,YACN;AACF;AAEA,SAAS,kBAAkB,GAAwC;CACjE,OAAO;EACL,WAAW,EAAE;EACb,eAAe,EAAE;EACjB,iBAAiB,EAAE;EACnB,YAAY,EAAE;EACd,aAAa,EAAE;EACf,0BAA0B,EAAE;EAC5B,aAAa,EAAE;EACf,mBAAmB,EAAE;EACrB,cAAc,EAAE;EAChB,gBAAgB,EAAE;EAClB,yBAAyB,EAAE;EAC3B,UAAU,EAAE;EACZ,cAAc,EAAE;EAChB,cAAc,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC,KAAK;CACzC;AACF;AAEA,SAAS,cAAc,GAAgB,SAAyB;CAC9D,MAAM,QAAkB,CAAC;CACzB,MAAM,KAAK,sBAAsB,YAAY,EAAE,wBAAwB;CACvE,MAAM,KAAK,EAAE;CACb,MAAM,KAAK,WAAW;CACtB,MAAM,KAAK,KAAK,EAAE,WAAW;CAC7B,MAAM,KAAK,UAAU,EAAE,cAAc,UAAU;CAC/C,MAAM,KAAK,iBAAiB,EAAE,qBAAqB,UAAU;CAC7D,MAAM,KAAK,EAAE;CACb,MAAM,KAAK,gBAAgB;CAC3B,MAAM,KAAK,oBAAoB,SAAS;CACxC,MAAM,KAAK,sBAAsB,EAAE,mBAAmB,UAAU;CAChE,MAAM,KAAK,mBAAmB,EAAE,gBAAgB,UAAU;CAC1D,MAAM,KAAK,EAAE;CACb,MAAM,KAAK,WAAW;CACtB,MAAM,KAAK,8BAA8B,EAAE,gBAAgB;CAC3D,MAAM,KAAK,8BAA8B,EAAE,yBAAyB;CACpE,MAAM,KAAK,8BAA8B,EAAE,UAAU;CACrD,MAAM,KAAK,8BAA8B,EAAE,cAAc;CACzD,MAAM,KAAK,EAAE;CACb,MAAM,KAAK,yCAAyC;CACpD,MAAM,KAAK,KAAK,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,MAAM,GAAG;CACzD,MAAM,KAAK,EAAE;CACb,MAAM,KAAK,oDAAoD;CAC/D,MAAM,KACJ,0EACF;CACA,OAAO,GAAG,MAAM,KAAK,IAAI,EAAE;AAC7B;AAaA,SAAS,cAAc,KAA6B;CAClD,MAAM,QAAkB,CAAC;CACzB,MAAM,KAAK,gBAAgB,IAAI,IAAI;CACnC,MAAM,KAAK,gBAAgB,IAAI,UAAU;CACzC,MAAM,KAAK,gBAAgB,IAAI,UAAU;CACzC,MAAM,KAAK,gBAAgB,IAAI,QAAQ;CACvC,MAAM,KACJ,gBAAgB,IAAI,cAAc,QAAQ,oCAC5C;CACA,MAAM,KAAK,gBAAgB,IAAI,SAAS;CACxC,MAAM,KAAK,EAAE;CACb,MAAM,KAAK,IAAI,WAAW;CAC1B,OAAO,GAAG,MAAM,KAAK,IAAI,EAAE;AAC7B;AAEA,MAAM,qBAAqB,IAAI,IAAI,CAAC,eAAe,KAAK,CAAC;AACzD,MAAM,mBAAmB,IAAI,IAAkB;CAC7C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AACD,MAAM,gBAAgB,IAAI,IAAgB;CACxC;CACA;CACA;AACF,CAAC;AACD,MAAM,uBAAuB,IAAI,IAAI;CAAC;CAAM;CAAQ;AAAc,CAAC;AAEnE,SAAS,qBAAqB,OAA2C;CACvE,MAAM,MAKF,CAAC;CACL,IAAI,MAAM,WAAW,KAAA,GAAW;EAC9B,IAAI,CAAC,mBAAmB,IAAI,MAAM,MAAM,GACtC,MAAM,IAAI,MACR,qBAAqB,MAAM,OAAO,gCACpC;EAEF,IAAI,SAAS,MAAM;CACrB;CACA,IAAI,MAAM,aAAa,KAAA,GAAW;EAChC,IAAI,CAAC,iBAAiB,IAAI,MAAM,QAAwB,GACtD,MAAM,IAAI,MAAM,uBAAuB,MAAM,SAAS,EAAE;EAE1D,IAAI,WAAW,MAAM;CACvB;CACA,IAAI,MAAM,WAAW,KAAA,GAAW;EAC9B,IAAI,CAAC,cAAc,IAAI,MAAM,MAAoB,GAC/C,MAAM,IAAI,MAAM,qBAAqB,MAAM,OAAO,EAAE;EAEtD,IAAI,SAAS,MAAM;CACrB;CACA,IAAI,MAAM,aAAa,KAAA,GAAW;EAChC,IACE,MAAM,aAAa,WACnB,MAAM,aAAa,UACnB,MAAM,aAAa,QAEnB,MAAM,IAAI,MACR,uBAAuB,MAAM,SAAS,kCACxC;EAEF,IAAI,WAAW,MAAM;CACvB;CACA,OAAO;AACT;AAEA,SAAS,iBAAiB,OAAiC;CACzD,IAAI,MAAM,WAAW,GAAG,OAAO;CAC/B,MAAM,QAAkB,CACtB,uBAAuB,MAAM,OAAO,OAAO,MAAM,WAAW,IAAI,KAAK,OACrE,EACF;CACA,MAAM,UAAU,KAAK,IAAI,GAAG,MAAM,KAAK,MAAM,EAAE,GAAG,MAAM,CAAC;CACzD,MAAM,WAAW,KAAK,IAAI,GAAG,MAAM,KAAK,MAAM,EAAE,SAAS,MAAM,CAAC;CAChE,MAAM,WAAW,KAAK,IAAI,GAAG,MAAM,KAAK,MAAM,EAAE,SAAS,MAAM,CAAC;CAChE,KAAK,MAAM,KAAK,OAAO;EACrB,MAAM,MAAM,EAAE,cAAc,kBAAkB;EAC9C,MAAM,KACJ,KAAK,EAAE,GAAG,OAAO,OAAO,EAAE,IAAI,EAAE,SAAS,OAAO,QAAQ,EAAE,IAAI,EAAE,SAAS,OAAO,QAAQ,EAAE,IAAI,EAAE,OAAO,IAAI,KAC7G;CACF;CACA,OAAO,GAAG,MAAM,KAAK,IAAI,EAAE;AAC7B;AA8CA,SAAS,QAAQ,OAA4D;CAC3E,IAAI,UAAU,KAAA,GAAW,OAAO,KAAA;CAChC,OAAO,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;AAC9C;AAEA,SAAS,mBACP,OAC8C;CAC9C,IAAI,CAAC,SAAS,MAAM,WAAW,GAAG,OAAO,KAAA;CACzC,MAAM,MAAwC,CAAC;CAC/C,KAAK,MAAM,SAAS,OAAO;EACzB,MAAM,MAAM,MAAM,YAAY,GAAG;EACjC,IAAI,QAAQ,IACV,MAAM,IAAI,MACR,mBAAmB,MAAM,mDAC3B;EAEF,MAAM,SAAS,MAAM,MAAM,GAAG,GAAG;EACjC,MAAM,QAAQ,MAAM,MAAM,MAAM,CAAC;EACjC,IACE,UAAU,WACV,UAAU,UACV,UAAU,UACV,UAAU,OAEV,MAAM,IAAI,MACR,qBAAqB,MAAM,gBAAgB,MAAM,iCACnD;EAEF,IAAI,OAAO,WAAW,GACpB,MAAM,IAAI,MAAM,mBAAmB,MAAM,8BAA8B;EAEzE,IAAI,UAAU;CAChB;CACA,OAAO;AACT;AAEA,SAAS,cAAc,OAAiC;CACtD,IAAI,MAAM,cAAc,KAAA,GAAW,OAAO;CAC1C,IAAI,MAAM,aAAa,OAAO;CAC9B,IAAI,MAAM,MAAM,OAAO;CACvB,MAAM,OAAO,MAAM;CAQnB,IANE,SAAS,YACT,SAAS,UACT,SAAS,kBACT,SAAS,WACT,SAAS,UACT,SAAS,cAET,OAAO;CAET,IACE,OAAO,MAAM,WAAW,YACxB,MAAM,OAAO,YAAY,CAAC,CAAC,SAAS,OAAO,GAE3C,OAAO;CAET,OAAO;AACT;AAEA,SAAS,cAAc,GAA2C;CAChE,OAAO,MAAM,WAAW,MAAM,UAAU,MAAM;AAChD;AAEA,SAAS,mBACP,OACoB;CACpB,IAAI,UAAU,KAAA,GAAW,OAAO,KAAA;CAChC,MAAM,UAAU,OAAO,KAAK;CAC5B,IAAI,CAAC,OAAO,UAAU,OAAO,KAAK,UAAU,GAC1C,MAAM,IAAI,MACR,yEAAyE,MAAM,GACjF;CAEF,OAAO,UAAU;AACnB;AAEA,eAAe,eACb,SACA,OACA,UACwD;CACxD,IAAI;CACJ,IAAI,CAAC,MAAM,SAAS,MAAM,QAAQ,MAAM,SACtC,aAAa,MAAM,iBAAiB;EAClC;EACA,MAAM,MAAM,SAAS,WAAW;CAClC,CAAC;CAEH,MAAM,WAAW,MAAM,iBAAiB,SAAS;EAC/C,GAAI,MAAM,SAAS,EAAE,cAAc,MAAM,OAAO,IAAI,CAAC;EACrD,GAAI,MAAM,SAAS,EAAE,gBAAgB,MAAM,OAAO,IAAI,CAAC;CACzD,CAAC;CACD,MAAM,aAAa,QAAQ,MAAM,UAAU;CAC3C,MAAM,SAA+B,kBAAkB,UAAU;EAC/D,QAAQ,MAAM;EACd,SAAS,QAAQ,MAAM,OAAO;EAC9B,SAAS,QAAQ,MAAM,OAAO;EAC9B,OAAO,SAAS;EAChB,WAAW,SAAS;EACpB,GAAI,aAAa,EAAE,aAAa,WAAW,IAAI,CAAC;CAClD,CAAC;CACD,MAAM,SAAS,MAAM,MAAM;EACzB,SAAS,OAAO;EAChB,SAAS,OAAO;EAChB,SAAS,OAAO;EAChB,OAAO,OAAO;EACd,QAAQ,OAAO;EACf,WAAW,OAAO;EAClB,UAAU,MAAM;EAChB,MAAM,MAAM;EACZ,uBAAuB,MAAM;EAC7B;EACA,KAAK,MAAM;EACX,GAAI,OAAO,cAAc,EAAE,aAAa,OAAO,YAAY,IAAI,CAAC;EAChE,GAAI,SAAS,kBAAkB,KAAA,IAC3B,EAAE,eAAe,SAAS,cAAc,IACxC,CAAC;CACP,CAAC;CACD,IAAI,iBAAiB,IAAI,IAAI,OAAO,KAAK,OAAO,KAAK,CAAC;CACtD,IAAI,SAAS,eACX,iBAAiB,wBACf,gBACA,SAAS,aACX;CAGF,OAAO;EAAE,QADQ,oBAAoB,QAAQ,gBAAgB,OAAO,MAC5C;EAAG,QAAQ,SAAS;CAAO;AACrD;AAEA,SAAS,iBACP,SACA,eACa;CACb,MAAM,QAAQ,QAAQ;CACtB,MAAM,cAAc,QAAQ,SAAS,MAAM,EAAE,WAAW;CACxD,IAAI,eAAe;CACnB,IAAI,YAAY;CAChB,IAAI,WAAsB;CAC1B,KAAK,MAAM,KAAK,SAAS;EACvB,gBAAgB,EAAE;EAClB,aAAa,EAAE;EACf,IAAI,EAAE,WAAW,UAAU,WAAW,EAAE;CAC1C;CACA,MAAM,aAAqC,CAAC;CAC5C,KAAK,MAAM,KAAK,aACd,WAAW,EAAE,WAAW,WAAW,EAAE,WAAW,KAAK;CAEvD,MAAM,cAAc,iBAAiB,aAAa,EAChD,WAAW,MAAM,YAAY,UAC/B,CAAC;CACD,MAAM,sBAAsB,QAAQ,SACjC,MAAM,EAAE,uBAAuB,CAAC,CACnC;CACA,OAAO;EACL,SAAS;EACT;EACA;EACA,OAAO,YAAY;EACnB,YAAY,YAAY;EACxB,WAAW,YAAY;EACvB,WAAW,YAAY;EACvB;EACA;EACA,aAAa;GAAE,GAAG,MAAM;GAAa;EAAc;EACnD;EACA,SAAS,MAAM;EACf;EACA,YAAY,QAAQ,MAAM,MAAM,EAAE,UAAU;EAC5C,GAAI,oBAAoB,SAAS,IAAI,EAAE,oBAAoB,IAAI,CAAC;CAClE;AACF;AAEA,SAAS,WACP,QACA,UACA,OACM;CAeN,MAAM,MAAM,OAAO;EAbjB,UAAU;EACV,aAAa,YAAY;EACzB,eAAe,OAAO;EACtB,mBAAmB,OAAO;EAC1B,WAAW,OAAO;EAClB,aAAa,OAAO;EACpB,OAAO,OAAO;EACd,aAAa,OAAO;EACpB,YAAY,OAAO;EACnB,GAAI,OAAO,sBACP,EAAE,qBAAqB,OAAO,oBAAoB,IAClD,CAAC;CAEgB,GAAG,UAAU;EAClC,OAAO,MAAM;EACb,OAAO,MAAM;CACf,CAAC;CACD,IAAI,MAAM,QACR,cAAc,QAAQ,MAAM,MAAM,GAAG,GAAG;MAExC,QAAQ,OAAO,MAAM,GAAG;CAE1B,MAAM,mBAAmB,MAAM,OAAO;CACtC,MAAM,kBAAkB,MAAM,OAAO;CACrC,MAAM,iBAAiB,CAAC,oBAAoB,gBAAgB;CAK5D,KAHE,MAAM,gBAAgB,QAAQ,mBAAmB,oBAEjD,aAAa,WAAW,aAAa,aAIrC,CAAC,MAAM,SACP,OAAO,YAAY,SAAS,GAE5B,QAAQ,OAAO,MAAM,GAAG,kBAAkB,OAAO,WAAW,EAAE,GAAG;AAErE;AAEA,eAAe,kBACb,QACA,OACA,SACe;CACf,IAAI,MAAM,SAAS,MAAM;CACzB,IAAI,CAAC,MAAM,QAAQ;EACjB,QAAQ,OAAO,MACb,+FACF;EACA;CACF;CACA,MAAM,MAAM,MAAM;CAQlB,MAAM,SAAS,MAAM,aAAa;EAChC,SAJA,MAAM,eAAe,MAAM,YAAY,SAAS,IAC5C,MAAM,cACN,SAAS,OAAO;EAGpB,OAAO,OAAO;EACd,YAAY,OAAO;EACnB,WAAW,OAAO;EAClB,WAAW,OAAO;EAClB,aAAa,OAAO;EACpB;EACA;EACA,QAAQ,MAAM;CAChB,CAAC;CACD,IAAI,OAAO,IACT,QAAQ,OAAO,MACb,sBAAsB,OAAO,YAAY,OAAO,UAAU,OAAO,YAAY,WAAW,IAAI,KAAK,IAAI,MAAM,IAAI,SAAS,OAAO,OAAO,KACxI;MACK;EACL,MAAM,SACJ,OAAO,WAAW,KAAA,IAAY,UAAU,OAAO,OAAO,KAAK;EAC7D,QAAQ,OAAO,MACb,4BAA4B,OAAO,IAAI,OAAO,MAAM,kCACtD;CACF;AACF;AAEA,eAAe,iBACb,SACA,OACA,UACA,UAC+D;CAC/D,MAAM,YAAY,MACf,QAAS,MAAM,GAAG,CAAC,CACnB,KAAK,SAAS,KAAK,KAAK,CAAC,CAAC,CAC1B,QAAQ,SAAS,KAAK,SAAS,CAAC;CACnC,MAAM,SAAS,IAAI,IAAI,SAAS,KAAK,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;CACvD,MAAM,UAAyB,CAAC;CAChC,IAAI,SAAuB;CAC3B,KAAK,MAAM,QAAQ,WAAW;EAC5B,MAAM,QAAQ,OAAO,IAAI,IAAI;EAC7B,IAAI,CAAC,OAAO;GACV,QAAQ,OAAO,MACb,2CAA2C,KAAK,cAClD;GACA;EACF;EACA,MAAM,SAAS,MAAM,eAAe,MAAM,KAAK,OAAO,QAAQ;EAC9D,QAAQ,KAAK,OAAO,MAAM;EAC1B,SAAS,OAAO;CAClB;CACA,IAAI,QAAQ,WAAW,GAAG;EACxB,QAAQ,OAAO,MACb,yDACF;EACA,OAAO;CACT;CACA,MAAM,EAAE,SAAS,MAAM,iBAAiB,OAAO;CAC/C,OAAO;EAAE,QAAQ,iBAAiB,SAAS,IAAI;EAAG;CAAO;AAC3D;AAEA,eAAe,QAAQ,KAAa,OAAoC;CACtE,MAAM,YAAY,QAAQ,GAAG;CAC7B,MAAM,eAAgB,MAAM,gBAAgB;CAC5C,IAAI,CAAC,qBAAqB,IAAI,YAAY,GAAG;EAC3C,QAAQ,OAAO,MACb,2EAA2E,MAAM,aAAa,IAChG;EACA,QAAQ,WAAW;EACnB;CACF;CAEA,MAAM,kBAAkB,MAAM,MAC1B;EACE;EACA,QAAQ;EACR,WAAW,KAAA;EACX,SAAS,KAAA;CACX,IACA,MAAM,mBAAmB,YAAY;CAEzC,IAAI,QAAQ,aAAa,GAAG;CAE5B,MAAM,OAAO,MAAM,SAAS;EAC1B,KAAK;EACL,cAAc,gBAAgB;EAC9B,QAAQ,gBAAgB;EACxB,WAAW,gBAAgB;EAC3B,SAAS,gBAAgB;EACzB,SAAS;CACX,CAAC;CAED,MAAM,UAAU,MAAM,cAAc,SAAS;CAC7C,QAAQ,OAAO,MAAM,GAAG,QAAQ,GAAG;CAEnC,IAAI,KAAK,YAAY,CAAC,MAAM,OAAO;EACjC,QAAQ,OAAO,MACb,oBAAoB,KAAK,aAAa,kDACxC;EACA,QAAQ,WAAW;EACnB;CACF;CAEA,IAAI,MAAM,QAAQ;EAChB,KAAK,MAAM,SAAS,KAAK,QAAQ;GAC/B,QAAQ,OAAO,MAAM,yBAAyB,MAAM,KAAK,GAAG;GAC5D,QAAQ,OAAO,MAAM,GAAG,MAAM,QAAQ,GAAG;EAC3C;EACA,QAAQ,WAAW;EACnB;CACF;CAEA,KAAK,MAAM,SAAS,KAAK,QACvB,cAAc,MAAM,MAAM,MAAM,SAAS,MAAM;CAEjD,QAAQ,WAAW;AACrB;AAEA,eAAe,mBAAmB,eAK/B;CACD,MAAM,UAAU,MAAM,QACpB;EACE;GACE,MAAM;GACN,MAAM;GACN,SAAS;GACT,SAAS;IACP;KAAE,OAAO;KAAkC,OAAO;IAAK;IACvD;KAAE,OAAO;KAAsB,OAAO;IAAO;IAC7C;KAAE,OAAO;KAAuB,OAAO;IAAe;GACxD;GACA,SAAS,kBAAkB,iBAAiB,IAAI;EAClD;EACA;GACE,MAAM;GACN,MAAM;GACN,SAAS;GACT,SAAS;IACP;KAAE,OAAO;KAAmC,OAAO;IAAc;IACjE;KAAE,OAAO;KAA+B,OAAO;IAAS;IACxD;KAAE,OAAO;KAAyB,OAAO;IAAU;GACrD;GACA,SAAS;EACX;EACA;GACE,MAAM;GACN,MAAM;GACN,SAAS;GACT,KAAK;GACL,KAAK;GACL,SAAS;EACX;EACA;GACE,MAAM;GACN,MAAM;GACN,SAAS;GACT,SAAS;EACX;CACF,GACA,EACE,gBAAgB;EACd,QAAQ,OAAO,MAAM,8BAA8B;EACnD,QAAQ,WAAW;CACrB,EACF,CACF;CAEA,IAAI,QAAQ,aAAa,GACvB,OAAO;EACL,cAAc;EACd,QAAQ;EACR,WAAW,KAAA;EACX,SAAS,KAAA;CACX;CAEF,OAAO,qBAAqB;EAC1B,QAAQ,QAAQ;EAChB,QAAQ,QAAQ;EAChB,WAAW,QAAQ,aAAa,KAAA;EAChC,SAAS,QAAQ;CACnB,CAAC;AACH;AAEA,MAAM,qBAAqB,IAAI,IAAI;CAAC;CAAU;CAAU;AAAM,CAAC;AAE/D,eAAe,aACb,KACA,OACe;CACf,MAAM,YAAY,QAAQ,GAAG;CAC7B,MAAM,WAAW,MAAM,YAAY;CACnC,IAAI,CAAC,mBAAmB,IAAI,QAAQ,GAAG;EACrC,QAAQ,OAAO,MACb,0EAA0E,MAAM,SAAS,IAC3F;EACA,QAAQ,WAAW;EACnB;CACF;CAEA,MAAM,OAAO,MAAM,mBAAmB;EACpC,KAAK;EACL,WAAW;EACX,KAAK;EACK;EACV,IAAI,MAAM,OAAO;EACjB,YAAY,MAAM,aAAa;EAC/B,OAAO,MAAM,UAAU;CACzB,CAAC;CAED,IAAI,KAAK,UAAU;EACjB,QAAQ,OAAO,MACb,0BAA0B,KAAK,aAAa,kDAC9C;EACA,QAAQ,WAAW;EACnB;CACF;CAEA,IAAI,MAAM,QAAQ;EAChB,KAAK,MAAM,SAAS,KAAK,QAAQ;GAC/B,QAAQ,OAAO,MAAM,yBAAyB,MAAM,KAAK,GAAG;GAC5D,QAAQ,OAAO,MAAM,GAAG,MAAM,QAAQ,GAAG;EAC3C;EACA,QAAQ,WAAW;EACnB;CACF;CAEA,IAAI,CAAC,MAAM,KAAK;EACd,MAAM,UAAU,MAAM,QACpB;GACE,MAAM;GACN,MAAM;GACN,SAAS,gBAAgB,KAAK,SAAS,kBAAkB,KAAK,OAAO,EAAE,EAAE,KAAK;GAC9E,SAAS;EACX,GACA,EACE,gBAAgB;GACd,QAAQ,WAAW;EACrB,EACF,CACF;EACA,IAAI,QAAQ,aAAa,GAAG;EAC5B,IAAI,CAAC,QAAQ,SAAS;GACpB,QAAQ,OAAO,MAAM,oCAAoC;GACzD,QAAQ,WAAW;GACnB;EACF;CACF;CAEA,KAAK,MAAM,SAAS,KAAK,QAAQ;EAC/B,UAAU,QAAQ,MAAM,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;EAClD,cAAc,MAAM,MAAM,MAAM,SAAS,MAAM;EAC/C,QAAQ,OAAO,MAAM,gCAAgC,MAAM,KAAK,GAAG;CACrE;CACA,QAAQ,WAAW;AACrB;AAEA,eAAsB,IAAI,OAAiB,QAAQ,MAAuB;CACxE,MAAM,MAAM,IAAI,YAAY;CAE5B,IACG,QAAQ,UAAU,qBAAqB,CAAC,CACxC,OACC,mBACA,wEACA,EACE,SAAS,QACX,CACF,CAAC,CACA,OAAO,UAAU,6BAA6B,CAAC,CAC/C,OAAO,kBAAkB,uBAAuB,CAAC,CACjD,OAAO,mBAAmB,0BAA0B,CAAC,CACrD,OAAO,mBAAmB,6CAA6C,CAAC,CACxE,OACC,qBACA,+DACA,EACE,SAAS,QACX,CACF,CAAC,CACA,OAAO,WAAW,uBAAuB,CAAC,CAC1C,OAAO,aAAa,qDAAqD,CAAC,CAC1E,OAAO,cAAc,wBAAwB,CAAC,CAC9C,OACC,qBACA,mDACF,CAAC,CACA,OAAO,oBAAoB,uCAAuC,CAAC,CACnE,OAAO,oBAAoB,uCAAuC,CAAC,CACnE,OACC,qBACA,gDACF,CAAC,CACA,OACC,sBACA,sGACF,CAAC,CACA,OAAO,kBAAkB,yCAAyC,CAAC,CACnE,OAAO,aAAa,4CAA4C,CAAC,CACjE,OACC,gCACA,sDACF,CAAC,CACA,OAAO,mBAAmB,+BAA+B,CAAC,CAC1D,OAAO,WAAW,4CAA4C,CAAC,CAC/D,OAAO,iBAAiB,iDAAiD,CAAC,CAC1E,OAAO,QAAQ,2DAA2D,CAAC,CAC3E,OAAO,WAAW,mDAAmD,CAAC,CACtE,OAAO,UAAU,+CAA+C,CAAC,CACjE,OAAO,YAAY,sCAAsC,CAAC,CAC1D,OAAO,UAAU,mDAAmD,CAAC,CACrE,OACC,oBACA,6DACF,CAAC,CACA,OAAO,mBAAmB,8CAA8C,CAAC,CACzE,OAAO,gBAAgB,yCAAyC,CAAC,CACjE,OACC,SACA,+JACF,CAAC,CACA,OACC,0BACA,iLACF,CAAC,CACA,OACC,UACA,4GACF,CAAC,CACA,OACC,oBACA,qFACA,EACE,SAAS,gDACX,CACF,CAAC,CACA,OACC,mBACA,uFACF,CAAC,CACA,OACC,yBACA,mGACF,CAAC,CACA,OACC,4BACA,sGACF,CAAC,CACA,OAAO,OAAO,MAA0B,UAAoB;EAC3D,MAAM,WAAW,cAAc,KAAK;EACpC,IAAI,MAAM,WAAW,KAAA,KAAa,CAAC,cAAc,MAAM,MAAM,GAAG;GAC9D,QAAQ,OAAO,MACb,kEAAkE,MAAM,OAAO,IACjF;GACA,QAAQ,WAAW;GACnB;EACF;EACA,MAAM,UAAU,QAAQ,QAAQ,GAAG;EAEnC,IAAI;GACF,IAAI,MAAM,UAAU,MAAM,QAAQ,MAAM,cACtC,MAAM,IAAI,MACR,kFACF;GAEF,MAAM,gBAAgB,mBAAmB,QAAQ,MAAM,IAAI,CAAC;GAC5D,MAAM,YACJ,MAAM,cAAc,KAAA,IAAY,KAAA,IAAY,OAAO,MAAM,SAAS;GACpE,IACE,cAAc,KAAA,MACb,CAAC,OAAO,UAAU,SAAS,KAAK,YAAY,KAAK,YAAY,MAE9D,MAAM,IAAI,MACR,8CAA8C,MAAM,UAAU,GAChE;GAEF,IAAI,MAAM,QAAQ,MAAM,QACtB,MAAM,IAAI,MAAM,6CAA6C;GAE/D,IAAI,MAAM,WAAW,MAAM,OACzB,MAAM,IAAI,MAAM,+CAA+C;GAEjE,MAAM,YAAY,MAAM,QAAQ;GAChC,MAAM,qBACJ,aAAa,CAAC,MAAM,SAAS,MAAM,QAAQ,MAAM;GACnD,IAAI,oBACF,QAAQ,OAAO,MACb,oIACF;GAEF,IAAI,aAAa,MAAM,eAAe,KAAA,GACpC,QAAQ,OAAO,MACb,kKACF;GAMF,MAAM,WAA4B;IAChC;IACA;IACA,eAPoB,qBAAqB;KACzC,YAAY,QAAQ,MAAM,QAAQ;KAClC,YAAY,QAAQ,MAAM,SAAS;IACrC,CAIc;IACZ,eAAe,mBAAmB,MAAM,WAAW;GACrD;GAEA,MAAM,oBAAoB,MAAM,UAC5B,MAAM,sBAAsB,OAAO,IACnC,CAAC;GACL,IAAI,MAAM,WAAW,kBAAkB,WAAW,GAChD,QAAQ,OAAO,MACb,uDACF;GAGF,IAAI;GACJ,IAAI;GACJ,IAAI,MAAM,WAAW,kBAAkB,SAAS,GAAG;IACjD,MAAM,aAAa,MAAM,iBACvB,SACA,OACA,UACA,iBACF;IACA,IAAI,CAAC,YAAY;KACf,QAAQ,WAAW;KACnB;IACF;IACA,SAAS,WAAW;IACpB,eAAe,WAAW;GAC5B,OAAO;IACL,MAAM,SAAS,MAAM,eAAe,SAAS,OAAO,QAAQ;IAC5D,SAAS,OAAO;IAChB,eAAe,OAAO;GACxB;GAEA,IAAI,MAAM,SAAS;IACjB,MAAM,gBAAgB,mBAAmB,QAAQ,EAC/C,aACF,CAAC;IACD,QAAQ,OAAO,MAAM,GAAG,cAAc,GAAG;GAC3C;GACA,IAAI,aAAa,CAAC,oBAChB,QAAQ,OAAO,MACb,qCAAqC,OAAO,YAAY,OAAO,UAAU,OAAO,YAAY,WAAW,IAAI,KAAK,IAAI,gCACtH;GAEF,IAAI,MAAM,OACR,QAAQ,OAAO,MAAM,GAAG,OAAO,MAAM,GAAG;QAExC,WAAW,QAAQ,UAAU,KAAK;GAEpC,MAAM,kBAAkB,QAAQ,OAAO,OAAO;GAC9C,QAAQ,WAAW,OAAO;EAC5B,SAAS,KAAK;GACZ,MAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;GAC3D,QAAQ,OAAO,MAAM,eAAe,IAAI,GAAG;GAC3C,QAAQ,WAAW;EACrB;CACF,CAAC;CAEH,IACG,QACC,cACA,uFACF,CAAC,CACA,MAAM,OAAO,CAAC,CACd,OAAO,mBAAmB,8BAA8B,CAAC,CACzD,OAAO,qBAAqB,oBAAoB,CAAC,CACjD,OACC,mBACA,+DACF,CAAC,CACA,OAAO,sBAAsB,gCAAgC,CAAC,CAC9D,OAAO,UAAU,qCAAqC,CAAC,CACvD,OAAO,kBAAkB,iCAAiC,CAAC,CAC3D,OAAO,OAAO,UAA6B;EAC1C,IAAI;GAEF,MAAM,QAAQ,UADC,qBAAqB,KACP,CAAC;GAC9B,IAAI,MAAM,QAAQ,MAAM,aAAa;IACnC,MAAM,UAAU;KAAE,OAAO,MAAM;KAAQ;IAAM;IAC7C,QAAQ,OAAO,MACb,MAAM,cACF,KAAK,UAAU,OAAO,IACtB,GAAG,KAAK,UAAU,SAAS,MAAM,CAAC,EAAE,GAC1C;GACF,OACE,QAAQ,OAAO,MAAM,iBAAiB,KAAK,CAAC;GAE9C,QAAQ,WAAW;EACrB,SAAS,KAAK;GACZ,MAAM,MAAO,IAAc;GAC3B,QAAQ,OAAO,MAAM,0BAA0B,IAAI,GAAG;GACtD,QAAQ,WAAW;EACrB;CACF,CAAC;CAEH,IACG,QACC,oBACA,kEACF,CAAC,CACA,MAAM,KAAK,CAAC,CACZ,OAAO,UAAU,gDAAgD,CAAC,CAClE,OAAO,OAAO,QAAgB,UAA2B;EACxD,MAAM,MAAM,YAAY,MAAM;EAC9B,IAAI,CAAC,KAAK;GACR,QAAQ,OAAO,MACb,qCAAqC,OAAO,4DAC9C;GACA,QAAQ,WAAW;GACnB;EACF;EACA,IAAI,MAAM,MACR,QAAQ,OAAO,MAAM,GAAG,KAAK,UAAU,KAAK,MAAM,CAAC,EAAE,GAAG;OAExD,QAAQ,OAAO,MAAM,cAAc,GAAG,CAAC;EAEzC,QAAQ,WAAW;CACrB,CAAC;CAEH,IACG,QACC,iBACA,mEACF,CAAC,CACA,OAAO,UAAU,gDAAgD,CAAC,CAClE,OAAO,kBAAkB,yCAAyC,CAAC,CACnE,OAAO,OAAO,KAAyB,UAA2B;EACjE,MAAM,UAAU,QAAQ,OAAO,GAAG;EAClC,MAAM,UAAU,MAAM,cAAc,OAAO;EAC3C,IAAI,MAAM,QAAQ,MAAM,aAAa;GACnC,MAAM,UAAU,kBAAkB,OAAO;GACzC,MAAM,MAAM,MAAM,cACd,KAAK,UAAU,OAAO,IACtB,KAAK,UAAU,SAAS,MAAM,CAAC;GACnC,QAAQ,OAAO,MAAM,GAAG,IAAI,GAAG;EACjC,OACE,QAAQ,OAAO,MAAM,cAAc,SAAS,OAAO,CAAC;EAEtD,QAAQ,WAAW;CACrB,CAAC;CAEH,IACG,QAAQ,cAAc,0CAA0C,CAAC,CACjE,OAAO,aAAa,2CAA2C,CAAC,CAChE,OACC,0CACA,kCACF,CAAC,CACA,OAAO,WAAW,2BAA2B,CAAC,CAC9C,OAAO,aAAa,4CAA4C,CAAC,CACjE,OAAO,OAAO,KAAyB,UAAwB;EAC9D,IAAI;GACF,MAAM,QAAQ,OAAO,KAAK,KAAK;EACjC,SAAS,KAAK;GACZ,QAAQ,OAAO,MAAM,GAAG,eAAe,QAAQ,IAAI,UAAU,IAAI,GAAG;GACpE,QAAQ,WAAW;EACrB;CACF,CAAC;CAEH,IACG,QACC,iBACA,2EACF,CAAC,CACA,OAAO,aAAa,mDAAmD,CAAC,CACxE,OAAO,WAAW,6BAA6B,CAAC,CAChD,OAAO,aAAa,4CAA4C,CAAC,CACjE,OAAO,OAAO,KAAyB,UAA2B;EACjE,IAAI;GACF,MAAM,gBAAgB;IACpB,KAAK,OAAO;IACZ,SAAS,YAAY;IACrB,KAAK,MAAM;IACX,OAAO,MAAM;IACb,QAAQ,MAAM;GAChB,CAAC;EACH,SAAS,KAAK;GACZ,QAAQ,OAAO,MACb,uBAAuB,eAAe,QAAQ,IAAI,UAAU,IAAI,GAClE;GACA,QAAQ,WAAW;EACrB;CACF,CAAC;CAEH,IACG,QACC,qBACA,2FACF,CAAC,CACA,OAAO,QAAQ,oCAAoC,CAAC,CACpD,OAAO,iBAAiB,6CAA6C,CAAC,CACtE,OACC,qBACA,qDACF,CAAC,CACA,OAAO,aAAa,mDAAmD,CAAC,CACxE,OAAO,WAAW,gCAAgC,CAAC,CACnD,OAAO,aAAa,4CAA4C,CAAC,CACjE,OACC,OACE,QACA,KACA,UACG;EACH,IAAI,WAAW,WAAW;GACxB,QAAQ,OAAO,MACb,kCAAkC,OAAO,wBAC3C;GACA,QAAQ,WAAW;GACnB;EACF;EACA,IAAI;GACF,MAAM,aAAa,OAAO,KAAK,KAAK;EACtC,SAAS,KAAK;GACZ,QAAQ,OAAO,MACb,0BAA0B,eAAe,QAAQ,IAAI,UAAU,IAAI,GACrE;GACA,QAAQ,WAAW;EACrB;CACF,CACF;CAEF,IAAI,KAAK;CACT,IAAI,QAAQ,YAAY,CAAC;CACzB,IAAI,MAAM,MAAM,EAAE,KAAK,MAAM,CAAC;CAC9B,MAAM,IAAI,kBAAkB;CAC5B,OAAO,QAAQ,YAAY;AAC7B"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geoql/vue-doctor",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
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": [
|
|
@@ -48,16 +48,16 @@
|
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"cac": "^7.0.0",
|
|
50
50
|
"kolorist": "^1.8.0",
|
|
51
|
-
"oxlint": "^1.
|
|
51
|
+
"oxlint": "^1.72.0",
|
|
52
52
|
"prompts": "^2.4.2",
|
|
53
|
-
"@geoql/doctor-core": "^1.
|
|
53
|
+
"@geoql/doctor-core": "^1.4.0",
|
|
54
54
|
"@geoql/oxlint-plugin-vue-doctor": "1.2.1"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@types/node": "^25.9.4",
|
|
58
58
|
"@types/prompts": "^2.4.9",
|
|
59
|
-
"@vue/compiler-core": "^3.5.
|
|
60
|
-
"@vue/compiler-sfc": "^3.5.
|
|
59
|
+
"@vue/compiler-core": "^3.5.39",
|
|
60
|
+
"@vue/compiler-sfc": "^3.5.39",
|
|
61
61
|
"c12": "^4.0.0-beta.5",
|
|
62
62
|
"oxc-parser": "^0.137.0",
|
|
63
63
|
"picocolors": "^1.1.1",
|