@titan-design/style-checker 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/dist/index.d.ts +55 -2
- package/dist/index.js +392 -15
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -52,6 +52,15 @@ const { diagnostics, failures, skippedRules, summary } = await orchestrate({
|
|
|
52
52
|
(`"code": "invalid-syntax"` in ruff 0.16.8, a null `code` in 0.9.10) are not
|
|
53
53
|
diagnostics; the runners report them as `file-not-checked` failures.
|
|
54
54
|
- `formatDiagnostic(d)` prints `file:line:column severity message [category.rule]`.
|
|
55
|
+
- Python audit runners, independent of any profile: `runRuffAudit` (the pinned
|
|
56
|
+
`AUDIT_RUFF_RULES` selection from `generateRuffAuditConfig()`), `runVultureAudit`
|
|
57
|
+
(dead code, rule `vulture/<kind>`), `runPydoclintAudit` (docstring drift, rule
|
|
58
|
+
`pydoclint/DOCnnn`), `runPyrightAudit` (four `reportUnnecessary*` checks under a
|
|
59
|
+
temporary config from `generatePyrightAuditConfig`), and `runImportLinter` (only when
|
|
60
|
+
the repo configures import-linter). `countSuppressions`, `findSuppressions` and
|
|
61
|
+
`suppressionTotals` count `# noqa` and `# type: ignore` markers by text scan. A runner
|
|
62
|
+
whose binary is absent returns an empty result with a warning naming its `pip install`
|
|
63
|
+
command, and never throws for it. The reference page lists each runner's options.
|
|
55
64
|
- `diffAgainstProfile(profile, observations)` compares each observation's value with the
|
|
56
65
|
profile's convention for its `type` and returns `{ deviations, summary }`. Observations
|
|
57
66
|
whose type has no profile rule count toward `total` but neither match nor deviate.
|
package/dist/index.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ interface CheckResult {
|
|
|
23
23
|
tool: "eslint" | "ruff";
|
|
24
24
|
exitCode: number;
|
|
25
25
|
}
|
|
26
|
-
type ToolName = "eslint" | "ruff";
|
|
26
|
+
type ToolName = "eslint" | "ruff" | "vulture" | "pydoclint" | "pyright" | "import-linter" | "suppressions";
|
|
27
27
|
type ToolFailureKind = "spawn-failed" | "timeout" | "signal" | "exit-code" | "unparseable-output" | "file-not-checked" | "missing-dependency";
|
|
28
28
|
interface ToolFailure {
|
|
29
29
|
tool: ToolName;
|
|
@@ -98,6 +98,8 @@ interface RunnerResult {
|
|
|
98
98
|
exitCode: number | null;
|
|
99
99
|
failures: ToolFailure[];
|
|
100
100
|
skippedRules: SkippedRule[];
|
|
101
|
+
/** Non-fatal notes, such as a tool that is not installed; absent from runners that never warn. */
|
|
102
|
+
warnings?: string[];
|
|
101
103
|
}
|
|
102
104
|
interface RunnerOptions {
|
|
103
105
|
fix?: boolean;
|
|
@@ -108,6 +110,57 @@ interface RunnerOptions {
|
|
|
108
110
|
/** Runs the pinned audit rule set; diagnostics carry paths relative to `cwd`, since ruff reports absolute ones. */
|
|
109
111
|
declare function runRuffAudit(files: string[], options?: Omit<RunnerOptions, "fix">): Promise<RunnerResult>;
|
|
110
112
|
|
|
113
|
+
/** The audit's pinned pyright selection: checks whose finding is a guard or conversion the types already make redundant. */
|
|
114
|
+
declare const AUDIT_PYRIGHT_RULES: readonly string[];
|
|
115
|
+
type PyrightConfig = Record<string, string | string[]>;
|
|
116
|
+
/** Everything off except the audit rules, as warnings; `root` goes on extraPaths so the project's own imports resolve. */
|
|
117
|
+
declare function generatePyrightAuditConfig(root: string): PyrightConfig;
|
|
118
|
+
|
|
119
|
+
type AuditOptions = Omit<RunnerOptions, "fix">;
|
|
120
|
+
|
|
121
|
+
interface VultureOptions extends AuditOptions {
|
|
122
|
+
/** vulture's --min-confidence; 60 keeps its uncertain unused-code guesses, 100 keeps only unreachable code. */
|
|
123
|
+
minConfidence?: number;
|
|
124
|
+
}
|
|
125
|
+
/** Dead-code candidates from vulture at `minConfidence` (default 60); rule ids are vulture/<kind>. */
|
|
126
|
+
declare function runVultureAudit(files: string[], options?: VultureOptions): Promise<RunnerResult>;
|
|
127
|
+
|
|
128
|
+
type DocstringStyle = "numpy" | "google" | "sphinx";
|
|
129
|
+
interface PydoclintOptions extends AuditOptions {
|
|
130
|
+
style?: DocstringStyle;
|
|
131
|
+
}
|
|
132
|
+
/** Docstring-versus-signature drift from pydoclint in `style` (default numpy); rule ids are pydoclint/DOCnnn. */
|
|
133
|
+
declare function runPydoclintAudit(files: string[], options?: PydoclintOptions): Promise<RunnerResult>;
|
|
134
|
+
|
|
135
|
+
interface PyrightOptions extends AuditOptions {
|
|
136
|
+
/** The interpreter whose site-packages pyright resolves third-party imports from. */
|
|
137
|
+
pythonPath?: string;
|
|
138
|
+
}
|
|
139
|
+
/** Redundant isinstance, comparison, contains and cast findings from pyright; rule ids are pyright/<rule>. */
|
|
140
|
+
declare function runPyrightAudit(files: string[], options?: PyrightOptions): Promise<RunnerResult>;
|
|
141
|
+
|
|
142
|
+
declare const NO_IMPORT_LINTER_CONFIG = "no import-linter config";
|
|
143
|
+
/** One diagnostic per broken import chain; runs only when the project configures import-linter. */
|
|
144
|
+
declare function runImportLinter(options?: AuditOptions): Promise<RunnerResult>;
|
|
145
|
+
|
|
146
|
+
declare const NOQA_RULE = "suppression/noqa";
|
|
147
|
+
declare const TYPE_IGNORE_RULE = "suppression/type-ignore";
|
|
148
|
+
/** Every `# noqa` and `# type: ignore` marker in one file's text, one diagnostic per occurrence. */
|
|
149
|
+
declare function findSuppressions(file: string, text: string): CheckDiagnostic[];
|
|
150
|
+
/** Suppression markers across `files`, read from disk relative to `cwd`; no tool is run. */
|
|
151
|
+
declare function countSuppressions(files: string[], options?: {
|
|
152
|
+
cwd?: string;
|
|
153
|
+
}): RunnerResult;
|
|
154
|
+
interface SuppressionCounts {
|
|
155
|
+
noqa: number;
|
|
156
|
+
typeIgnore: number;
|
|
157
|
+
}
|
|
158
|
+
interface SuppressionTotals extends SuppressionCounts {
|
|
159
|
+
byFile: Record<string, SuppressionCounts>;
|
|
160
|
+
}
|
|
161
|
+
/** Totals of countSuppressions' diagnostics, overall and per file; other diagnostics are ignored. */
|
|
162
|
+
declare function suppressionTotals(diagnostics: CheckDiagnostic[]): SuppressionTotals;
|
|
163
|
+
|
|
111
164
|
declare function parseEslintJsonOutput(jsonStr: string): CheckDiagnostic[];
|
|
112
165
|
declare function parseRuffJsonOutput(jsonStr: string): CheckDiagnostic[];
|
|
113
166
|
declare function formatDiagnostic(d: CheckDiagnostic): string;
|
|
@@ -130,4 +183,4 @@ interface DiffResult {
|
|
|
130
183
|
}
|
|
131
184
|
declare function diffAgainstProfile(profile: Profile, observations: Observation[]): DiffResult;
|
|
132
185
|
|
|
133
|
-
export { AUDIT_RUFF_RULES, type CheckDiagnostic, type CheckResult, type Deviation, type DiffResult, type EslintFlatConfigEntry, type OrchestratorOptions, type OrchestratorResult, type RuffConfig, type RunnerOptions, type RunnerResult, type SkippedRule, type ToolFailure, type ToolFailureKind, type ToolName, diffAgainstProfile, formatDiagnostic, generateEslintConfig, generateRuffAuditConfig, generateRuffConfig, orchestrate, parseEslintJsonOutput, parseRuffJsonOutput, runRuffAudit };
|
|
186
|
+
export { AUDIT_PYRIGHT_RULES, AUDIT_RUFF_RULES, type AuditOptions, type CheckDiagnostic, type CheckResult, type Deviation, type DiffResult, type DocstringStyle, type EslintFlatConfigEntry, NOQA_RULE, NO_IMPORT_LINTER_CONFIG, type OrchestratorOptions, type OrchestratorResult, type PydoclintOptions, type PyrightConfig, type PyrightOptions, type RuffConfig, type RunnerOptions, type RunnerResult, type SkippedRule, type SuppressionCounts, type SuppressionTotals, TYPE_IGNORE_RULE, type ToolFailure, type ToolFailureKind, type ToolName, type VultureOptions, countSuppressions, diffAgainstProfile, findSuppressions, formatDiagnostic, generateEslintConfig, generatePyrightAuditConfig, generateRuffAuditConfig, generateRuffConfig, orchestrate, parseEslintJsonOutput, parseRuffJsonOutput, runImportLinter, runPydoclintAudit, runPyrightAudit, runRuffAudit, runVultureAudit, suppressionTotals };
|
package/dist/index.js
CHANGED
|
@@ -98,7 +98,7 @@ import { tmpdir } from "os";
|
|
|
98
98
|
// src/runners/tool-runner.ts
|
|
99
99
|
import { spawn } from "child_process";
|
|
100
100
|
function runTool(command, args, options) {
|
|
101
|
-
return new Promise((
|
|
101
|
+
return new Promise((resolve2, reject) => {
|
|
102
102
|
const child = spawn(command, args, {
|
|
103
103
|
cwd: options?.cwd,
|
|
104
104
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -118,7 +118,7 @@ function runTool(command, args, options) {
|
|
|
118
118
|
});
|
|
119
119
|
child.on("close", (code, signal) => {
|
|
120
120
|
clearTimeout(timer);
|
|
121
|
-
|
|
121
|
+
resolve2({
|
|
122
122
|
stdout: Buffer.concat(chunks).toString("utf-8"),
|
|
123
123
|
stderr: Buffer.concat(errChunks).toString("utf-8"),
|
|
124
124
|
exitCode: code,
|
|
@@ -139,7 +139,7 @@ function withStderr(message, stderr) {
|
|
|
139
139
|
const detail = excerpt(stderr);
|
|
140
140
|
return detail ? `${message}: ${detail}` : message;
|
|
141
141
|
}
|
|
142
|
-
function classifyRun(tool, run) {
|
|
142
|
+
function classifyRun(tool, run, expect = {}) {
|
|
143
143
|
const fail = (kind, message) => ({
|
|
144
144
|
ok: false,
|
|
145
145
|
failure: { tool, kind, message: withStderr(message, run.stderr) },
|
|
@@ -149,17 +149,17 @@ function classifyRun(tool, run) {
|
|
|
149
149
|
if (run.exitCode === null) {
|
|
150
150
|
return fail("signal", `${tool} was killed by ${run.signal ?? "a signal"}`);
|
|
151
151
|
}
|
|
152
|
-
if (!SUCCESS_EXIT_CODES.has(run.exitCode)) {
|
|
152
|
+
if (!(expect.successCodes ?? SUCCESS_EXIT_CODES).has(run.exitCode)) {
|
|
153
153
|
return fail("exit-code", `${tool} exited with code ${run.exitCode}`);
|
|
154
154
|
}
|
|
155
|
-
if (!run.stdout.trim()) {
|
|
155
|
+
if (!expect.allowEmptyStdout && !run.stdout.trim()) {
|
|
156
156
|
return fail("unparseable-output", `${tool} exited ${run.exitCode} with no output`);
|
|
157
157
|
}
|
|
158
158
|
return { ok: true, stdout: run.stdout, stderr: run.stderr, exitCode: run.exitCode };
|
|
159
159
|
}
|
|
160
|
-
async function runAndClassify(tool, command, args, options) {
|
|
160
|
+
async function runAndClassify(tool, command, args, options, expect) {
|
|
161
161
|
try {
|
|
162
|
-
return classifyRun(tool, await runTool(command, args, options));
|
|
162
|
+
return classifyRun(tool, await runTool(command, args, options), expect);
|
|
163
163
|
} catch (err) {
|
|
164
164
|
const message = err instanceof Error ? err.message : String(err);
|
|
165
165
|
return { ok: false, failure: { tool, kind: "spawn-failed", message }, exitCode: null };
|
|
@@ -251,13 +251,13 @@ function renderEntry(ctx, entry, parserExpr, skipped) {
|
|
|
251
251
|
return { text, ruleCount: Object.keys(rules).length };
|
|
252
252
|
}
|
|
253
253
|
function buildEslintConfigModule(config, fromDir) {
|
|
254
|
-
const
|
|
255
|
-
if (!
|
|
254
|
+
const parser2 = resolveModule(PARSER_SOURCES, fromDir);
|
|
255
|
+
if (!parser2) {
|
|
256
256
|
const message = `${describeSources(PARSER_SOURCES)} is not installed in ${fromDir}, so ESLint cannot parse TypeScript`;
|
|
257
257
|
return { ok: false, failure: { tool: "eslint", kind: "missing-dependency", message } };
|
|
258
258
|
}
|
|
259
259
|
const ctx = { fromDir, imports: [], ids: /* @__PURE__ */ new Map(), plugins: /* @__PURE__ */ new Map() };
|
|
260
|
-
const parserExpr = importExpr(ctx,
|
|
260
|
+
const parserExpr = importExpr(ctx, parser2);
|
|
261
261
|
const skippedRules = [];
|
|
262
262
|
const entries = config.map((entry) => renderEntry(ctx, entry, parserExpr, skippedRules));
|
|
263
263
|
const ruleCount = entries.reduce((sum, e) => sum + e.ruleCount, 0);
|
|
@@ -564,17 +564,382 @@ function generateRuffAuditConfig() {
|
|
|
564
564
|
return { preview: true, lint: { select: [...AUDIT_RUFF_RULES] } };
|
|
565
565
|
}
|
|
566
566
|
|
|
567
|
-
// src/runners/
|
|
567
|
+
// src/runners/paths.ts
|
|
568
568
|
import { realpathSync } from "fs";
|
|
569
569
|
import { isAbsolute, relative } from "path";
|
|
570
|
+
function auditRoot(cwd) {
|
|
571
|
+
return realpathSync(cwd ?? process.cwd());
|
|
572
|
+
}
|
|
573
|
+
function relativeTo(root, file) {
|
|
574
|
+
return isAbsolute(file) ? relative(root, file) : file;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// src/runners/ruff-audit.ts
|
|
570
578
|
async function runRuffAudit(files, options) {
|
|
571
579
|
const result = await runRuff(generateRuffAuditConfig(), files, options);
|
|
572
|
-
const root =
|
|
580
|
+
const root = auditRoot(options?.cwd);
|
|
573
581
|
const diagnostics = result.diagnostics.map((d) => ({ ...d, file: relativeTo(root, d.file) }));
|
|
574
582
|
return { ...result, diagnostics };
|
|
575
583
|
}
|
|
576
|
-
|
|
577
|
-
|
|
584
|
+
|
|
585
|
+
// src/generators/pyright-audit.ts
|
|
586
|
+
var AUDIT_PYRIGHT_RULES = [
|
|
587
|
+
"reportUnnecessaryIsInstance",
|
|
588
|
+
"reportUnnecessaryComparison",
|
|
589
|
+
"reportUnnecessaryContains",
|
|
590
|
+
"reportUnnecessaryCast"
|
|
591
|
+
];
|
|
592
|
+
var STILL_ON_WHEN_OFF = ["reportMissingImports", "reportMissingModuleSource", "reportUndefinedVariable"];
|
|
593
|
+
function generatePyrightAuditConfig(root) {
|
|
594
|
+
const config = { typeCheckingMode: "off", extraPaths: [root] };
|
|
595
|
+
for (const rule of STILL_ON_WHEN_OFF) config[rule] = "none";
|
|
596
|
+
for (const rule of AUDIT_PYRIGHT_RULES) config[rule] = "warning";
|
|
597
|
+
return config;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// src/runners/python-tool.ts
|
|
601
|
+
function emptyResult(exitCode, extra = {}) {
|
|
602
|
+
return { diagnostics: [], exitCode, failures: [], skippedRules: [], warnings: [], ...extra };
|
|
603
|
+
}
|
|
604
|
+
function missingToolWarning(spec) {
|
|
605
|
+
const note = spec.installNote ? ` (${spec.installNote})` : "";
|
|
606
|
+
return `${spec.tool} not found; install with \`${spec.install}\`${note}`;
|
|
607
|
+
}
|
|
608
|
+
function isMissingBinary(run) {
|
|
609
|
+
const { kind, message } = run.failure;
|
|
610
|
+
if (kind === "spawn-failed" && message.includes("ENOENT")) return true;
|
|
611
|
+
return run.exitCode === 127 || /command not found/i.test(message);
|
|
612
|
+
}
|
|
613
|
+
function relativise(root, parsed) {
|
|
614
|
+
return {
|
|
615
|
+
diagnostics: parsed.diagnostics.map((d) => ({ ...d, file: relativeTo(root, d.file) })),
|
|
616
|
+
failures: parsed.failures.map((f) => f.file ? { ...f, file: relativeTo(root, f.file) } : f)
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
async function runPythonTool(spec, args, options, parse) {
|
|
620
|
+
const runOptions = { cwd: options?.cwd, timeout: options?.timeout };
|
|
621
|
+
const run = await runAndClassify(spec.tool, spec.command, args, runOptions, spec.expect);
|
|
622
|
+
if (!run.ok && isMissingBinary(run)) return emptyResult(null, { warnings: [missingToolWarning(spec)] });
|
|
623
|
+
if (!run.ok) return emptyResult(run.exitCode, { failures: [run.failure] });
|
|
624
|
+
const parsed = parseOrFail(spec.tool, () => parse(run));
|
|
625
|
+
if (!parsed.ok) return emptyResult(run.exitCode, { failures: [parsed.failure] });
|
|
626
|
+
return emptyResult(run.exitCode, relativise(auditRoot(options?.cwd), parsed.value));
|
|
627
|
+
}
|
|
628
|
+
function unrecognisedStderr(tool, lines) {
|
|
629
|
+
if (lines.length === 0) return [];
|
|
630
|
+
const message = lines.join("\n");
|
|
631
|
+
return [{ tool, kind: "unparseable-output", message: message.length > 500 ? `${message.slice(0, 500)}\u2026` : message }];
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// src/runners/vulture-audit.ts
|
|
635
|
+
var VULTURE = {
|
|
636
|
+
tool: "vulture",
|
|
637
|
+
command: "vulture",
|
|
638
|
+
install: "pip install vulture",
|
|
639
|
+
expect: { successCodes: /* @__PURE__ */ new Set([0, 1, 3]), allowEmptyStdout: true }
|
|
640
|
+
};
|
|
641
|
+
var FINDING = /^(.+?):(\d+): (.+) \((\d+)% confidence(?:, \d+ lines?)?\)$/;
|
|
642
|
+
var INVALID_INPUT = /^(.+?):(\d+): (.+)$/;
|
|
643
|
+
var NOT_FOUND = /^Error: (.+) could not be found\.$/;
|
|
644
|
+
function vultureKind(text) {
|
|
645
|
+
const unused = /^unused (\w+)/.exec(text);
|
|
646
|
+
return unused ? `unused-${unused[1]}` : "unreachable";
|
|
647
|
+
}
|
|
648
|
+
function toDiagnostic(m) {
|
|
649
|
+
const [, file, line, text, confidence] = m;
|
|
650
|
+
return {
|
|
651
|
+
file,
|
|
652
|
+
line: Number(line),
|
|
653
|
+
column: 1,
|
|
654
|
+
severity: "warn",
|
|
655
|
+
message: `${text} (${confidence}% confidence)`,
|
|
656
|
+
category: "dead-code",
|
|
657
|
+
rule: `vulture/${vultureKind(text)}`,
|
|
658
|
+
fixable: false
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
function parseStdout(stdout) {
|
|
662
|
+
return stdout.split("\n").filter((l) => l.trim()).map((line) => {
|
|
663
|
+
const m = FINDING.exec(line);
|
|
664
|
+
if (!m) throw new Error(`Unrecognised vulture line: ${line}`);
|
|
665
|
+
return toDiagnostic(m);
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
function parseStderr(stderr) {
|
|
669
|
+
const failures = [];
|
|
670
|
+
const unknown = [];
|
|
671
|
+
for (const line of stderr.split("\n").filter((l) => l.trim())) {
|
|
672
|
+
const missing = NOT_FOUND.exec(line);
|
|
673
|
+
const invalid = INVALID_INPUT.exec(line);
|
|
674
|
+
if (missing) failures.push({ tool: "vulture", kind: "file-not-checked", file: missing[1], message: "could not be found" });
|
|
675
|
+
else if (invalid) failures.push({ tool: "vulture", kind: "file-not-checked", file: invalid[1], message: invalid[3] });
|
|
676
|
+
else unknown.push(line);
|
|
677
|
+
}
|
|
678
|
+
return [...failures, ...unrecognisedStderr("vulture", unknown)];
|
|
679
|
+
}
|
|
680
|
+
function parseVultureOutput({ stdout, stderr }) {
|
|
681
|
+
return { diagnostics: parseStdout(stdout), failures: parseStderr(stderr) };
|
|
682
|
+
}
|
|
683
|
+
function runVultureAudit(files, options) {
|
|
684
|
+
const args = ["--min-confidence", String(options?.minConfidence ?? 60), ...files];
|
|
685
|
+
return runPythonTool(VULTURE, args, options, parseVultureOutput);
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
// src/runners/pydoclint-audit.ts
|
|
689
|
+
var PYDOCLINT = {
|
|
690
|
+
tool: "pydoclint",
|
|
691
|
+
command: "pydoclint",
|
|
692
|
+
install: "pip install pydoclint",
|
|
693
|
+
expect: { successCodes: /* @__PURE__ */ new Set([0, 1]), allowEmptyStdout: true }
|
|
694
|
+
};
|
|
695
|
+
var VIOLATION = /^\s+(\d+): (DOC\d+): (.*)$/;
|
|
696
|
+
var SYNTAX_ERROR = "DOC002";
|
|
697
|
+
function toDiagnostic2(file, m) {
|
|
698
|
+
return {
|
|
699
|
+
file,
|
|
700
|
+
line: Math.max(1, Number(m[1])),
|
|
701
|
+
column: 1,
|
|
702
|
+
severity: "warn",
|
|
703
|
+
message: m[3],
|
|
704
|
+
category: "documentation",
|
|
705
|
+
rule: `pydoclint/${m[2]}`,
|
|
706
|
+
fixable: false
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
function parsePydoclintOutput({ stdout, stderr }) {
|
|
710
|
+
const parsed = { diagnostics: [], failures: [] };
|
|
711
|
+
let file;
|
|
712
|
+
for (const line of `${stdout}
|
|
713
|
+
${stderr}`.split("\n").filter((l) => l.trim())) {
|
|
714
|
+
const m = VIOLATION.exec(line);
|
|
715
|
+
if (!m && /^\S/.test(line)) file = line.trim();
|
|
716
|
+
else if (!m || !file) throw new Error(`Unrecognised pydoclint line: ${line}`);
|
|
717
|
+
else if (m[2] === SYNTAX_ERROR) parsed.failures.push(notChecked2(file, m[3]));
|
|
718
|
+
else parsed.diagnostics.push(toDiagnostic2(file, m));
|
|
719
|
+
}
|
|
720
|
+
return parsed;
|
|
721
|
+
}
|
|
722
|
+
function notChecked2(file, message) {
|
|
723
|
+
return { tool: "pydoclint", kind: "file-not-checked", file, message };
|
|
724
|
+
}
|
|
725
|
+
function runPydoclintAudit(files, options) {
|
|
726
|
+
const args = ["--style", options?.style ?? "numpy", "--quiet", ...files];
|
|
727
|
+
return runPythonTool(PYDOCLINT, args, options, parsePydoclintOutput);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// src/runners/pyright-audit.ts
|
|
731
|
+
import { mkdtempSync as mkdtempSync3, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
732
|
+
import { tmpdir as tmpdir3 } from "os";
|
|
733
|
+
import { join as join4 } from "path";
|
|
734
|
+
var PYRIGHT = {
|
|
735
|
+
tool: "pyright",
|
|
736
|
+
command: "pyright",
|
|
737
|
+
install: "pip install pyright",
|
|
738
|
+
installNote: "the pip wrapper fetches pyright's npm package on first run and needs Node on PATH; `npm install -g pyright` also works",
|
|
739
|
+
expect: { successCodes: /* @__PURE__ */ new Set([0, 1]) }
|
|
740
|
+
};
|
|
741
|
+
function toDiagnostic3(d) {
|
|
742
|
+
return {
|
|
743
|
+
file: d.file,
|
|
744
|
+
line: d.range.start.line + 1,
|
|
745
|
+
endLine: d.range.end.line + 1,
|
|
746
|
+
column: d.range.start.character + 1,
|
|
747
|
+
severity: "warn",
|
|
748
|
+
message: d.message,
|
|
749
|
+
category: "redundant-check",
|
|
750
|
+
rule: `pyright/${d.rule}`,
|
|
751
|
+
fixable: false
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
function unparsedFiles2(entries) {
|
|
755
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
756
|
+
for (const e of entries.filter((d) => !d.rule && d.severity === "error")) {
|
|
757
|
+
byFile.set(e.file, [...byFile.get(e.file) ?? [], e.message]);
|
|
758
|
+
}
|
|
759
|
+
return [...byFile].map(([file, messages]) => ({ tool: "pyright", kind: "file-not-checked", file, message: messages.join("; ") }));
|
|
760
|
+
}
|
|
761
|
+
function parsePyrightOutput({ stdout }) {
|
|
762
|
+
const entries = JSON.parse(stdout).generalDiagnostics;
|
|
763
|
+
const audited = entries.filter((d) => AUDIT_PYRIGHT_RULES.includes(d.rule ?? ""));
|
|
764
|
+
return { diagnostics: audited.map(toDiagnostic3), failures: unparsedFiles2(entries) };
|
|
765
|
+
}
|
|
766
|
+
async function runPyrightAudit(files, options) {
|
|
767
|
+
const tempDir = mkdtempSync3(join4(tmpdir3(), "style-checker-pyright-"));
|
|
768
|
+
try {
|
|
769
|
+
const configPath = join4(tempDir, "pyrightconfig.json");
|
|
770
|
+
writeFileSync3(configPath, JSON.stringify(generatePyrightAuditConfig(auditRoot(options?.cwd))), "utf-8");
|
|
771
|
+
const python = options?.pythonPath ? ["--pythonpath", options.pythonPath] : [];
|
|
772
|
+
return await runPythonTool(PYRIGHT, ["--outputjson", "-p", configPath, ...python, ...files], options, parsePyrightOutput);
|
|
773
|
+
} finally {
|
|
774
|
+
rmSync3(tempDir, { recursive: true, force: true });
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// src/runners/import-linter.ts
|
|
779
|
+
import { existsSync as existsSync2 } from "fs";
|
|
780
|
+
import { join as join6 } from "path";
|
|
781
|
+
|
|
782
|
+
// src/runners/import-linter-config.ts
|
|
783
|
+
import { existsSync, readFileSync } from "fs";
|
|
784
|
+
import { join as join5 } from "path";
|
|
785
|
+
var INI_CONTRACT = /^\[importlinter:(?:.*:)?([^:\]]+)\]$/;
|
|
786
|
+
function iniContractIds(text) {
|
|
787
|
+
const ids = /* @__PURE__ */ new Map();
|
|
788
|
+
let current;
|
|
789
|
+
for (const line of text.split("\n").map((l) => l.trim())) {
|
|
790
|
+
if (line.startsWith("[")) current = INI_CONTRACT.exec(line)?.[1];
|
|
791
|
+
const name = current && /^name\s*[=:]\s*(.+)$/.exec(line);
|
|
792
|
+
if (current && name) ids.set(name[1].trim(), current);
|
|
793
|
+
}
|
|
794
|
+
return ids;
|
|
795
|
+
}
|
|
796
|
+
function tomlValue(block, key) {
|
|
797
|
+
return new RegExp(`^${key}\\s*=\\s*["'](.+?)["']\\s*$`, "m").exec(block)?.[1];
|
|
798
|
+
}
|
|
799
|
+
function tomlContractIds(text) {
|
|
800
|
+
const ids = /* @__PURE__ */ new Map();
|
|
801
|
+
for (const chunk of text.split(/^\[\[tool\.importlinter\.contracts\]\]\s*$/m).slice(1)) {
|
|
802
|
+
const block = chunk.split(/^\[/m)[0];
|
|
803
|
+
const name = tomlValue(block, "name");
|
|
804
|
+
const id = tomlValue(block, "id");
|
|
805
|
+
if (name && id) ids.set(name, id);
|
|
806
|
+
}
|
|
807
|
+
return ids;
|
|
808
|
+
}
|
|
809
|
+
function readIfContains(path, marker2) {
|
|
810
|
+
if (!existsSync(path)) return void 0;
|
|
811
|
+
const text = readFileSync(path, "utf-8");
|
|
812
|
+
return marker2.test(text) ? text : void 0;
|
|
813
|
+
}
|
|
814
|
+
function findImportLinterConfig(cwd) {
|
|
815
|
+
for (const name of ["setup.cfg", ".importlinter"]) {
|
|
816
|
+
const ini = readIfContains(join5(cwd, name), /^\[importlinter\]/m);
|
|
817
|
+
if (ini !== void 0) return { path: join5(cwd, name), contractIds: iniContractIds(ini) };
|
|
818
|
+
}
|
|
819
|
+
const toml = readIfContains(join5(cwd, "pyproject.toml"), /^\[tool\.importlinter\]/m);
|
|
820
|
+
if (toml !== void 0) return { path: join5(cwd, "pyproject.toml"), contractIds: tomlContractIds(toml) };
|
|
821
|
+
return void 0;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
// src/runners/import-linter-output.ts
|
|
825
|
+
var LINK = /^(-)?\s+(\S+) -> (\S+) \(l\.([\d, ?]+)\)$/;
|
|
826
|
+
var SUMMARY = /^(\S.* is not allowed to import \S.*):$/;
|
|
827
|
+
function isUnderline(line, heading) {
|
|
828
|
+
return line !== void 0 && /^-+$/.test(line) && line.length === heading.length;
|
|
829
|
+
}
|
|
830
|
+
function toLink(m) {
|
|
831
|
+
const lines = m[4].split(",").map((n) => Number(n.trim())).filter((n) => Number.isInteger(n) && n > 0);
|
|
832
|
+
return { importer: m[2], imported: m[3], lines };
|
|
833
|
+
}
|
|
834
|
+
function parseBrokenChains(stdout) {
|
|
835
|
+
const lines = stdout.split("\n");
|
|
836
|
+
const start = lines.findIndex((l, i) => l === "Broken contracts" && isUnderline(lines[i + 1], l));
|
|
837
|
+
if (start < 0) return [];
|
|
838
|
+
const chains = [];
|
|
839
|
+
let contract = "";
|
|
840
|
+
let summary = "";
|
|
841
|
+
for (let i = start + 2; i < lines.length; i++) {
|
|
842
|
+
const line = lines[i];
|
|
843
|
+
const link = LINK.exec(line);
|
|
844
|
+
if (isUnderline(lines[i + 1], line) && line.trim()) contract = line;
|
|
845
|
+
else if (SUMMARY.test(line)) summary = SUMMARY.exec(line)[1];
|
|
846
|
+
else if (link?.[1]) chains.push({ contract, summary, links: [toLink(link)] });
|
|
847
|
+
else if (link && chains.length > 0) chains.at(-1).links.push(toLink(link));
|
|
848
|
+
}
|
|
849
|
+
return chains;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
// src/runners/import-linter.ts
|
|
853
|
+
var IMPORT_LINTER = {
|
|
854
|
+
tool: "import-linter",
|
|
855
|
+
command: "lint-imports",
|
|
856
|
+
install: "pip install import-linter",
|
|
857
|
+
expect: { successCodes: /* @__PURE__ */ new Set([0, 1]) }
|
|
858
|
+
};
|
|
859
|
+
var NO_IMPORT_LINTER_CONFIG = "no import-linter config";
|
|
860
|
+
function slug(name) {
|
|
861
|
+
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
862
|
+
}
|
|
863
|
+
function moduleFile(root, module) {
|
|
864
|
+
const base = module.replace(/\./g, "/");
|
|
865
|
+
const candidates = ["", "src/"].flatMap((dir) => [`${dir}${base}.py`, `${dir}${base}/__init__.py`]);
|
|
866
|
+
return candidates.find((c) => existsSync2(join6(root, c)));
|
|
867
|
+
}
|
|
868
|
+
function toDiagnostic4(chain, config, root) {
|
|
869
|
+
const first = chain.links[0];
|
|
870
|
+
const file = moduleFile(root, first.importer);
|
|
871
|
+
const path = [first.importer, ...chain.links.map((l) => l.imported)].join(" -> ");
|
|
872
|
+
return {
|
|
873
|
+
file: file ?? chain.contract,
|
|
874
|
+
line: file ? first.lines[0] ?? 1 : 1,
|
|
875
|
+
column: 1,
|
|
876
|
+
severity: "error",
|
|
877
|
+
message: `${chain.summary}: ${path}`,
|
|
878
|
+
category: "architecture",
|
|
879
|
+
rule: `import-linter/${config.contractIds.get(chain.contract) ?? slug(chain.contract)}`,
|
|
880
|
+
fixable: false
|
|
881
|
+
};
|
|
882
|
+
}
|
|
883
|
+
function parser(config, root) {
|
|
884
|
+
return ({ stdout }) => {
|
|
885
|
+
const chains = parseBrokenChains(stdout);
|
|
886
|
+
if (chains.length === 0 && /\bBROKEN$/m.test(stdout)) throw new Error("lint-imports reported a broken contract but no import chain could be read");
|
|
887
|
+
return { diagnostics: chains.map((c) => toDiagnostic4(c, config, root)), failures: [] };
|
|
888
|
+
};
|
|
889
|
+
}
|
|
890
|
+
function runImportLinter(options) {
|
|
891
|
+
const root = auditRoot(options?.cwd);
|
|
892
|
+
const config = findImportLinterConfig(root);
|
|
893
|
+
if (!config) return Promise.resolve(emptyResult(null, { warnings: [NO_IMPORT_LINTER_CONFIG] }));
|
|
894
|
+
const args = ["--config", config.path, "--no-logo", "--no-cache"];
|
|
895
|
+
return runPythonTool(IMPORT_LINTER, args, options, parser(config, root));
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
// src/runners/suppressions.ts
|
|
899
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
900
|
+
import { resolve } from "path";
|
|
901
|
+
var NOQA = /#\s*noqa\b(?::\s*([A-Z]+\d+(?:[\s,]+[A-Z]+\d+)*))?/gi;
|
|
902
|
+
var TYPE_IGNORE = /#\s*type:\s*ignore\b(?:\[([^\]]*)\])?/g;
|
|
903
|
+
var NOQA_RULE = "suppression/noqa";
|
|
904
|
+
var TYPE_IGNORE_RULE = "suppression/type-ignore";
|
|
905
|
+
function codesOf(raw) {
|
|
906
|
+
return raw ? raw.split(/[\s,]+/).filter(Boolean) : [];
|
|
907
|
+
}
|
|
908
|
+
function marker(file, line, m, rule, label) {
|
|
909
|
+
const codes = codesOf(m[1]);
|
|
910
|
+
const message = codes.length ? `${label} suppresses ${codes.join(", ")}` : `blanket ${label} suppresses every check on this line`;
|
|
911
|
+
return { file, line, column: (m.index ?? 0) + 1, severity: "warn", message, category: "suppression", rule, fixable: false };
|
|
912
|
+
}
|
|
913
|
+
function findSuppressions(file, text) {
|
|
914
|
+
return text.split("\n").flatMap((content, i) => [
|
|
915
|
+
...[...content.matchAll(NOQA)].map((m) => marker(file, i + 1, m, NOQA_RULE, "noqa")),
|
|
916
|
+
...[...content.matchAll(TYPE_IGNORE)].map((m) => marker(file, i + 1, m, TYPE_IGNORE_RULE, "type: ignore"))
|
|
917
|
+
]);
|
|
918
|
+
}
|
|
919
|
+
function countSuppressions(files, options) {
|
|
920
|
+
const root = auditRoot(options?.cwd);
|
|
921
|
+
const diagnostics = [];
|
|
922
|
+
const failures = [];
|
|
923
|
+
for (const file of files) {
|
|
924
|
+
const shown = relativeTo(root, resolve(root, file));
|
|
925
|
+
try {
|
|
926
|
+
diagnostics.push(...findSuppressions(shown, readFileSync2(resolve(root, file), "utf-8")));
|
|
927
|
+
} catch (err) {
|
|
928
|
+
failures.push({ tool: "suppressions", kind: "file-not-checked", file: shown, message: err.message });
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
return emptyResult(0, { diagnostics, failures });
|
|
932
|
+
}
|
|
933
|
+
function suppressionTotals(diagnostics) {
|
|
934
|
+
const totals = { noqa: 0, typeIgnore: 0, byFile: {} };
|
|
935
|
+
for (const d of diagnostics) {
|
|
936
|
+
const key = d.rule === NOQA_RULE ? "noqa" : d.rule === TYPE_IGNORE_RULE ? "typeIgnore" : void 0;
|
|
937
|
+
if (!key) continue;
|
|
938
|
+
const file = totals.byFile[d.file] ??= { noqa: 0, typeIgnore: 0 };
|
|
939
|
+
file[key] += 1;
|
|
940
|
+
totals[key] += 1;
|
|
941
|
+
}
|
|
942
|
+
return totals;
|
|
578
943
|
}
|
|
579
944
|
|
|
580
945
|
// src/profile-diff/diff-against-profile.ts
|
|
@@ -630,15 +995,27 @@ function diffAgainstProfile(profile, observations) {
|
|
|
630
995
|
};
|
|
631
996
|
}
|
|
632
997
|
export {
|
|
998
|
+
AUDIT_PYRIGHT_RULES,
|
|
633
999
|
AUDIT_RUFF_RULES,
|
|
1000
|
+
NOQA_RULE,
|
|
1001
|
+
NO_IMPORT_LINTER_CONFIG,
|
|
1002
|
+
TYPE_IGNORE_RULE,
|
|
1003
|
+
countSuppressions,
|
|
634
1004
|
diffAgainstProfile,
|
|
1005
|
+
findSuppressions,
|
|
635
1006
|
formatDiagnostic,
|
|
636
1007
|
generateEslintConfig,
|
|
1008
|
+
generatePyrightAuditConfig,
|
|
637
1009
|
generateRuffAuditConfig,
|
|
638
1010
|
generateRuffConfig,
|
|
639
1011
|
orchestrate,
|
|
640
1012
|
parseEslintJsonOutput,
|
|
641
1013
|
parseRuffJsonOutput,
|
|
642
|
-
|
|
1014
|
+
runImportLinter,
|
|
1015
|
+
runPydoclintAudit,
|
|
1016
|
+
runPyrightAudit,
|
|
1017
|
+
runRuffAudit,
|
|
1018
|
+
runVultureAudit,
|
|
1019
|
+
suppressionTotals
|
|
643
1020
|
};
|
|
644
1021
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/generators/eslint.ts","../src/generators/ruff.ts","../src/runners/eslint-runner.ts","../src/runners/tool-runner.ts","../src/runners/failures.ts","../src/runners/eslint-plugins.ts","../src/runners/eslint-config-module.ts","../src/formatters/unified.ts","../src/runners/ruff-runner.ts","../src/orchestrator/orchestrate.ts","../src/generators/ruff-audit.ts","../src/runners/ruff-audit.ts","../src/profile-diff/diff-against-profile.ts"],"sourcesContent":["import type { Profile } from \"@titan-design/style-profile\";\nimport {\n buildNamingConventionRule,\n buildImportOrderRule,\n buildFunctionLengthRule,\n buildFileNamingRule,\n buildJsdocRules,\n} from \"@titan-design/style-profile\";\n\nexport interface EslintFlatConfigEntry {\n plugins?: Record<string, unknown>;\n rules?: Record<string, unknown>;\n files?: string[];\n}\n\n// ESLint accepts only off, warn and error; style-profile's \"info\" tier becomes warn, as its own exporter does.\nfunction withEslintSeverity(value: unknown): unknown {\n if (Array.isArray(value) && value[0] === \"info\") return [\"warn\", ...value.slice(1)];\n return value === \"info\" ? \"warn\" : value;\n}\n\nfunction collectRules(profile: Profile): Record<string, unknown> {\n const rules: Record<string, unknown> = {};\n\n const namingRule = buildNamingConventionRule(profile);\n if (namingRule) rules[namingRule[0]] = namingRule[1];\n\n const importRule = buildImportOrderRule(profile);\n if (importRule) rules[importRule[0]] = importRule[1];\n\n const fnLengthRule = buildFunctionLengthRule(profile);\n if (fnLengthRule) rules[fnLengthRule[0]] = fnLengthRule[1];\n\n const fileNamingRule = buildFileNamingRule(profile);\n if (fileNamingRule) rules[fileNamingRule[0]] = fileNamingRule[1];\n\n const jsdocRules = buildJsdocRules(profile);\n for (const [name, value] of jsdocRules) {\n rules[name] = value;\n }\n\n for (const name of Object.keys(rules)) rules[name] = withEslintSeverity(rules[name]);\n return rules;\n}\n\nexport function generateEslintConfig(profile: Profile): EslintFlatConfigEntry[] {\n const entries: EslintFlatConfigEntry[] = [];\n const rules = collectRules(profile);\n\n if (Object.keys(rules).length > 0) {\n entries.push({\n files: [\"**/*.ts\", \"**/*.tsx\"],\n rules,\n });\n }\n\n return entries;\n}\n","import type { Profile } from \"@titan-design/style-profile\";\n\nexport interface RuffConfig {\n lint?: {\n select?: string[];\n ignore?: string[];\n fixable?: string[];\n \"per-file-ignores\"?: Record<string, string[]>;\n mccabe?: { \"max-complexity\"?: number };\n pydocstyle?: { convention?: string };\n isort?: {\n \"known-first-party\"?: string[];\n \"section-order\"?: string[];\n };\n };\n \"line-length\"?: number;\n preview?: boolean;\n}\n\ntype RuffLint = NonNullable<RuffConfig[\"lint\"]> & { select: string[] };\n\nfunction addNaming(profile: Profile, lint: RuffLint): void {\n if (profile.naming) {\n const hasNamingRules = Object.values(profile.naming).some(\n (rule) => rule.confidence >= profile.severityThresholds.info,\n );\n if (hasNamingRules) lint.select.push(\"N\");\n }\n}\n\nfunction addImportOrder(profile: Profile, lint: RuffLint): void {\n const importOrder = profile.structure?.importOrder;\n if (importOrder && importOrder.confidence >= profile.severityThresholds.info) {\n lint.select.push(\"I\");\n if (Array.isArray(importOrder.convention)) {\n lint.isort = {\n \"section-order\": importOrder.convention as string[],\n };\n }\n }\n}\n\nfunction addFunctionDocs(profile: Profile, lint: RuffLint): void {\n const functionDocs = profile.documentation?.functionDocs;\n if (functionDocs && functionDocs.confidence >= profile.severityThresholds.info) {\n lint.select.push(\"D\");\n const conv = functionDocs.convention;\n if (\n typeof conv === \"string\" &&\n conv !== \"jsdoc-selective\" &&\n conv !== \"jsdoc-all\"\n ) {\n lint.pydocstyle = { convention: conv };\n }\n }\n}\n\nfunction addComplexity(profile: Profile, lint: RuffLint): void {\n const maxLines = profile.structure?.functionMaxLines;\n if (\n maxLines &&\n maxLines.confidence >= profile.severityThresholds.info &&\n typeof maxLines.convention === \"number\"\n ) {\n lint.select.push(\"C90\");\n lint.mccabe = { \"max-complexity\": maxLines.convention };\n }\n}\n\nexport function generateRuffConfig(profile: Profile): RuffConfig {\n const lint: RuffLint = { select: [] };\n const config: RuffConfig = { lint };\n\n addNaming(profile, lint);\n addImportOrder(profile, lint);\n addFunctionDocs(profile, lint);\n addComplexity(profile, lint);\n\n const lineLength = profile.formatting?.lineLength;\n if (lineLength && typeof lineLength.convention === \"number\") {\n config[\"line-length\"] = lineLength.convention;\n }\n\n return config;\n}\n","import { writeFileSync, mkdtempSync, rmSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { tmpdir } from \"node:os\";\nimport { runAndClassify, parseOrFail } from \"./failures.js\";\nimport { buildEslintConfigModule } from \"./eslint-config-module.js\";\nimport { parseEslintJsonOutput } from \"../formatters/unified.js\";\nimport type { CheckDiagnostic, ToolFailure } from \"../orchestrator/types.js\";\nimport type { EslintFlatConfigEntry } from \"../generators/eslint.js\";\nimport type { RunnerOptions, RunnerResult } from \"./types.js\";\n\ninterface EslintJsonMessage {\n ruleId: string | null;\n fatal?: boolean;\n message: string;\n}\n\n// ESLint reports a file it could not lint as a rule-less message: a fatal parse error, or \"File ignored ...\".\nfunction uncheckedFiles(stdout: string): ToolFailure[] {\n const entries = JSON.parse(stdout) as Array<{ filePath: string; messages: EslintJsonMessage[] }>;\n return entries.flatMap((entry) =>\n entry.messages\n .filter((m) => m.ruleId === null && (m.fatal === true || m.message.startsWith(\"File ignored\")))\n .map((m) => ({ tool: \"eslint\" as const, kind: \"file-not-checked\" as const, file: entry.filePath, message: m.message })),\n );\n}\n\nfunction readEslintOutput(stdout: string): { diagnostics: CheckDiagnostic[]; failures: ToolFailure[] } {\n const parsed = parseOrFail(\"eslint\", () => ({\n diagnostics: parseEslintJsonOutput(stdout),\n failures: uncheckedFiles(stdout),\n }));\n return parsed.ok ? parsed.value : { diagnostics: [], failures: [parsed.failure] };\n}\n\nfunction eslintArgs(configPath: string, files: string[], fix?: boolean): string[] {\n // --no stops npx from downloading an ESLint the project has not installed.\n return [\"--no\", \"--\", \"eslint\", \"--config\", configPath, \"--format\", \"json\", ...(fix ? [\"--fix\"] : []), ...files];\n}\n\nexport async function runEslint(\n config: EslintFlatConfigEntry[],\n files: string[],\n options?: RunnerOptions,\n): Promise<RunnerResult> {\n const cwd = options?.cwd ?? process.cwd();\n const built = buildEslintConfigModule(config, cwd);\n if (!built.ok) return { diagnostics: [], exitCode: null, failures: [built.failure], skippedRules: [] };\n const { skippedRules } = built;\n if (built.ruleCount === 0) return { diagnostics: [], exitCode: null, failures: [], skippedRules };\n\n const tempDir = mkdtempSync(join(tmpdir(), \"codewatch-eslint-\"));\n const configPath = join(tempDir, \"eslint.config.mjs\");\n try {\n writeFileSync(configPath, built.source, \"utf-8\");\n const run = await runAndClassify(\"eslint\", \"npx\", eslintArgs(configPath, files, options?.fix), {\n cwd,\n timeout: options?.timeout,\n });\n if (!run.ok) return { diagnostics: [], exitCode: run.exitCode, failures: [run.failure], skippedRules };\n return { ...readEslintOutput(run.stdout), exitCode: run.exitCode, skippedRules };\n } finally {\n rmSync(tempDir, { recursive: true, force: true });\n }\n}\n","import { spawn } from \"node:child_process\";\n\nexport interface ToolRunResult {\n stdout: string;\n stderr: string;\n /** Null when the child was killed by a signal; see `signal`. */\n exitCode: number | null;\n signal: NodeJS.Signals | null;\n timedOut: boolean;\n}\n\nexport function runTool(\n command: string,\n args: string[],\n options?: { cwd?: string; timeout?: number },\n): Promise<ToolRunResult> {\n return new Promise((resolve, reject) => {\n const child = spawn(command, args, {\n cwd: options?.cwd,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n const chunks: Buffer[] = [];\n const errChunks: Buffer[] = [];\n let timedOut = false;\n // Our own timer, because spawn's `timeout` option kills without telling us it did.\n const timer = setTimeout(() => {\n timedOut = true;\n child.kill(\"SIGKILL\");\n }, options?.timeout ?? 60_000);\n\n child.stdout.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n child.stderr.on(\"data\", (chunk: Buffer) => errChunks.push(chunk));\n\n child.on(\"error\", (err) => {\n clearTimeout(timer);\n reject(new Error(`Failed to spawn ${command}: ${err.message}`));\n });\n\n child.on(\"close\", (code, signal) => {\n clearTimeout(timer);\n resolve({\n stdout: Buffer.concat(chunks).toString(\"utf-8\"),\n stderr: Buffer.concat(errChunks).toString(\"utf-8\"),\n exitCode: code,\n signal,\n timedOut,\n });\n });\n });\n}\n","import { runTool } from \"./tool-runner.js\";\nimport type { ToolRunResult } from \"./tool-runner.js\";\nimport type { ToolFailure, ToolName } from \"../orchestrator/types.js\";\n\n// ESLint and ruff both document 0 (clean) and 1 (problems found) as successful runs.\nconst SUCCESS_EXIT_CODES = new Set([0, 1]);\n\nexport type ToolRun =\n | { ok: true; stdout: string; stderr: string; exitCode: number }\n | { ok: false; failure: ToolFailure; exitCode: number | null };\n\nfunction excerpt(text: string): string {\n const trimmed = text.trim();\n return trimmed.length > 500 ? `${trimmed.slice(0, 500)}…` : trimmed;\n}\n\nfunction withStderr(message: string, stderr: string): string {\n const detail = excerpt(stderr);\n return detail ? `${message}: ${detail}` : message;\n}\n\nexport function classifyRun(tool: ToolName, run: ToolRunResult): ToolRun {\n const fail = (kind: ToolFailure[\"kind\"], message: string): ToolRun => ({\n ok: false,\n failure: { tool, kind, message: withStderr(message, run.stderr) },\n exitCode: run.exitCode,\n });\n if (run.timedOut) return fail(\"timeout\", `${tool} timed out and was killed`);\n if (run.exitCode === null) {\n return fail(\"signal\", `${tool} was killed by ${run.signal ?? \"a signal\"}`);\n }\n if (!SUCCESS_EXIT_CODES.has(run.exitCode)) {\n return fail(\"exit-code\", `${tool} exited with code ${run.exitCode}`);\n }\n if (!run.stdout.trim()) {\n return fail(\"unparseable-output\", `${tool} exited ${run.exitCode} with no output`);\n }\n return { ok: true, stdout: run.stdout, stderr: run.stderr, exitCode: run.exitCode };\n}\n\nexport async function runAndClassify(\n tool: ToolName,\n command: string,\n args: string[],\n options?: { cwd?: string; timeout?: number },\n): Promise<ToolRun> {\n try {\n return classifyRun(tool, await runTool(command, args, options));\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return { ok: false, failure: { tool, kind: \"spawn-failed\", message }, exitCode: null };\n }\n}\n\nexport function parseOrFail<T>(\n tool: ToolName,\n parse: () => T,\n): { ok: true; value: T } | { ok: false; failure: ToolFailure } {\n try {\n return { ok: true, value: parse() };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return { ok: false, failure: { tool, kind: \"unparseable-output\", message } };\n }\n}\n","import { createRequire } from \"node:module\";\nimport { join } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\n\nexport interface ModuleSource {\n specifier: string;\n member?: \"plugin\" | \"parser\";\n}\n\nexport interface ResolvedModule {\n url: string;\n member?: \"plugin\" | \"parser\";\n}\n\n// The typescript-eslint meta-package is the fallback because under pnpm it is often the only one a project can resolve.\nexport const PLUGIN_SOURCES: Record<string, ModuleSource[]> = {\n \"@typescript-eslint\": [\n { specifier: \"@typescript-eslint/eslint-plugin\" },\n { specifier: \"typescript-eslint\", member: \"plugin\" },\n ],\n perfectionist: [{ specifier: \"eslint-plugin-perfectionist\" }],\n unicorn: [{ specifier: \"eslint-plugin-unicorn\" }],\n jsdoc: [{ specifier: \"eslint-plugin-jsdoc\" }],\n};\n\nexport const PARSER_SOURCES: ModuleSource[] = [\n { specifier: \"@typescript-eslint/parser\" },\n { specifier: \"typescript-eslint\", member: \"parser\" },\n];\n\nexport function pluginNamespace(rule: string): string | null {\n const slash = rule.lastIndexOf(\"/\");\n return slash === -1 ? null : rule.slice(0, slash);\n}\n\nexport function describeSources(sources: ModuleSource[]): string {\n return sources.map((s) => s.specifier).join(\" or \");\n}\n\nexport function resolveModule(\n sources: ModuleSource[],\n fromDir: string,\n): ResolvedModule | null {\n // Resolve from the project, not from this package: ESLint runs there and loads plugins from there.\n const require = createRequire(join(fromDir, \"package.json\"));\n for (const source of sources) {\n try {\n const url = pathToFileURL(require.resolve(source.specifier)).href;\n return { url, member: source.member };\n } catch {\n continue;\n }\n }\n return null;\n}\n","import {\n PARSER_SOURCES,\n PLUGIN_SOURCES,\n describeSources,\n pluginNamespace,\n resolveModule,\n} from \"./eslint-plugins.js\";\nimport type { ResolvedModule } from \"./eslint-plugins.js\";\nimport type { EslintFlatConfigEntry } from \"../generators/eslint.js\";\nimport type { SkippedRule, ToolFailure } from \"../orchestrator/types.js\";\n\nexport type EslintConfigModule =\n | { ok: true; source: string; ruleCount: number; skippedRules: SkippedRule[] }\n | { ok: false; failure: ToolFailure };\n\ninterface ModuleContext {\n fromDir: string;\n imports: string[];\n ids: Map<string, string>;\n plugins: Map<string, ResolvedModule | null>;\n}\n\nfunction importExpr(ctx: ModuleContext, mod: ResolvedModule): string {\n let id = ctx.ids.get(mod.url);\n if (!id) {\n id = `m${ctx.ids.size}`;\n ctx.ids.set(mod.url, id);\n ctx.imports.push(`import ${id} from ${JSON.stringify(mod.url)};`);\n }\n return mod.member ? `${id}.${mod.member}` : id;\n}\n\nfunction resolvePlugin(ctx: ModuleContext, namespace: string): ResolvedModule | null {\n if (!ctx.plugins.has(namespace)) {\n const sources = PLUGIN_SOURCES[namespace];\n ctx.plugins.set(namespace, sources ? resolveModule(sources, ctx.fromDir) : null);\n }\n return ctx.plugins.get(namespace) ?? null;\n}\n\nfunction skipReason(ctx: ModuleContext, namespace: string): string {\n const sources = PLUGIN_SOURCES[namespace];\n if (!sources) return `no known package provides the ESLint plugin \"${namespace}\"`;\n return `${describeSources(sources)} is not installed in ${ctx.fromDir}`;\n}\n\nfunction renderEntry(\n ctx: ModuleContext,\n entry: EslintFlatConfigEntry,\n parserExpr: string,\n skipped: SkippedRule[],\n): { text: string; ruleCount: number } {\n const rules: Record<string, unknown> = {};\n const plugins: string[] = [];\n for (const [rule, value] of Object.entries(entry.rules ?? {})) {\n const namespace = pluginNamespace(rule);\n const plugin = namespace === null ? null : resolvePlugin(ctx, namespace);\n if (namespace !== null && !plugin) {\n skipped.push({ tool: \"eslint\", rule, plugin: namespace, reason: skipReason(ctx, namespace) });\n continue;\n }\n rules[rule] = value;\n if (namespace !== null && plugin) plugins.push(`${JSON.stringify(namespace)}: ${importExpr(ctx, plugin)}`);\n }\n const data = JSON.stringify({ files: entry.files, rules });\n const text = `{ ...${data}, languageOptions: { parser: ${parserExpr} }, plugins: { ${[...new Set(plugins)].join(\", \")} } }`;\n return { text, ruleCount: Object.keys(rules).length };\n}\n\nexport function buildEslintConfigModule(\n config: EslintFlatConfigEntry[],\n fromDir: string,\n): EslintConfigModule {\n const parser = resolveModule(PARSER_SOURCES, fromDir);\n if (!parser) {\n const message = `${describeSources(PARSER_SOURCES)} is not installed in ${fromDir}, so ESLint cannot parse TypeScript`;\n return { ok: false, failure: { tool: \"eslint\", kind: \"missing-dependency\", message } };\n }\n const ctx: ModuleContext = { fromDir, imports: [], ids: new Map(), plugins: new Map() };\n const parserExpr = importExpr(ctx, parser);\n const skippedRules: SkippedRule[] = [];\n const entries = config.map((entry) => renderEntry(ctx, entry, parserExpr, skippedRules));\n const ruleCount = entries.reduce((sum, e) => sum + e.ruleCount, 0);\n const body = entries.map((e) => e.text).join(\",\\n \");\n const source = `${ctx.imports.join(\"\\n\")}\\n\\nexport default [\\n ${body},\\n];\\n`;\n return { ok: true, source, ruleCount, skippedRules };\n}\n","import type { CheckDiagnostic } from \"../orchestrator/types.js\";\n\nconst RUFF_CODE_CATEGORY: Record<string, string> = {\n N: \"naming\",\n I: \"imports\",\n D: \"documentation\",\n C: \"complexity\",\n E: \"formatting\",\n W: \"formatting\",\n F: \"errors\",\n};\n\nconst ESLINT_RULE_CATEGORY: Record<string, string> = {\n \"@typescript-eslint/naming-convention\": \"naming\",\n \"unicorn/filename-case\": \"naming\",\n \"perfectionist/sort-imports\": \"imports\",\n \"import/order\": \"imports\",\n \"max-lines-per-function\": \"structure\",\n \"max-depth\": \"structure\",\n \"jsdoc/require-jsdoc\": \"documentation\",\n};\n\nfunction eslintSeverityToSeverity(eslintSeverity: number): \"error\" | \"warn\" {\n return eslintSeverity === 2 ? \"error\" : \"warn\";\n}\n\nfunction categorizeEslintRule(ruleId: string): string {\n if (ESLINT_RULE_CATEGORY[ruleId]) return ESLINT_RULE_CATEGORY[ruleId];\n if (ruleId.startsWith(\"@typescript-eslint/\")) return \"typescript\";\n if (ruleId.startsWith(\"jsdoc/\")) return \"documentation\";\n if (ruleId.startsWith(\"unicorn/\")) return \"style\";\n if (ruleId.startsWith(\"import/\")) return \"imports\";\n return \"other\";\n}\n\nfunction categorizeRuffCode(code: string): string {\n const prefix = code.replace(/[0-9]/g, \"\");\n return RUFF_CODE_CATEGORY[prefix] ?? \"other\";\n}\n\ninterface EslintJsonEntry {\n filePath: string;\n messages: Array<{\n ruleId: string | null;\n severity: number;\n message: string;\n line: number;\n column: number;\n fix?: { range: [number, number]; text: string } | null;\n }>;\n}\n\ninterface RuffJsonEntry {\n code: string | null;\n message: string;\n filename: string;\n location: { row: number; column: number };\n end_location: { row: number; column: number };\n fix?: {\n edits: Array<{\n content: string;\n location: { row: number; column: number };\n end_location: { row: number; column: number };\n }>;\n } | null;\n}\n\nexport function parseEslintJsonOutput(jsonStr: string): CheckDiagnostic[] {\n let entries: EslintJsonEntry[];\n try {\n entries = JSON.parse(jsonStr);\n } catch {\n throw new Error(\n `Failed to parse ESLint JSON output. Raw output:\\n${jsonStr.slice(0, 500)}`,\n );\n }\n const diagnostics: CheckDiagnostic[] = [];\n\n for (const entry of entries) {\n for (const msg of entry.messages) {\n if (!msg.ruleId) continue;\n diagnostics.push({\n file: entry.filePath,\n line: msg.line,\n column: msg.column,\n severity: eslintSeverityToSeverity(msg.severity),\n message: msg.message,\n category: categorizeEslintRule(msg.ruleId),\n rule: msg.ruleId,\n fixable: msg.fix != null,\n fix: msg.fix ?? undefined,\n });\n }\n }\n\n return diagnostics;\n}\n\n// ruff 0.16.8 reports a syntax error as code \"invalid-syntax\"; 0.9.10 used a null code.\nexport function isRuffSyntaxError(code: string | null): code is \"invalid-syntax\" | null {\n return code === null || code === \"invalid-syntax\";\n}\n\nexport function parseRuffJsonOutput(jsonStr: string): CheckDiagnostic[] {\n let entries: RuffJsonEntry[];\n try {\n entries = JSON.parse(jsonStr);\n } catch {\n throw new Error(\n `Failed to parse Ruff JSON output. Raw output:\\n${jsonStr.slice(0, 500)}`,\n );\n }\n return entries.flatMap((entry) => isRuffSyntaxError(entry.code) ? [] : [{\n file: entry.filename,\n line: entry.location.row,\n endLine: entry.end_location.row,\n column: entry.location.column,\n severity: \"warn\" as const,\n message: entry.message,\n category: categorizeRuffCode(entry.code),\n rule: entry.code,\n fixable: entry.fix != null,\n }]);\n}\n\nexport function formatDiagnostic(d: CheckDiagnostic): string {\n return `${d.file}:${d.line}:${d.column} ${d.severity} ${d.message} [${d.category}.${d.rule}]`;\n}\n","import { writeFileSync, mkdtempSync, rmSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { tmpdir } from \"node:os\";\nimport { runAndClassify, parseOrFail } from \"./failures.js\";\nimport { isRuffSyntaxError, parseRuffJsonOutput } from \"../formatters/unified.js\";\nimport type { CheckDiagnostic, ToolFailure } from \"../orchestrator/types.js\";\nimport type { RunnerOptions, RunnerResult } from \"./types.js\";\nimport type { RuffConfig } from \"../generators/ruff.js\";\n\ntype RuffLint = NonNullable<RuffConfig[\"lint\"]>;\n\nfunction quoteList(values: string[]): string {\n return `[${values.map((s) => `\"${s}\"`).join(\", \")}]`;\n}\n\nfunction lintSelectionLines(lint: RuffLint): string[] {\n const lines: string[] = [];\n if (lint.select) lines.push(`select = ${quoteList(lint.select)}`);\n if (lint.ignore) lines.push(`ignore = ${quoteList(lint.ignore)}`);\n return lines;\n}\n\nfunction lintSubsectionLines(lint: RuffLint): string[] {\n const lines: string[] = [];\n if (lint.mccabe) {\n lines.push(\"[lint.mccabe]\");\n if (lint.mccabe[\"max-complexity\"]) {\n lines.push(`max-complexity = ${lint.mccabe[\"max-complexity\"]}`);\n }\n }\n if (lint.pydocstyle) {\n lines.push(\"[lint.pydocstyle]\");\n if (lint.pydocstyle.convention) {\n lines.push(`convention = \"${lint.pydocstyle.convention}\"`);\n }\n }\n if (lint.isort) {\n lines.push(\"[lint.isort]\");\n if (lint.isort[\"section-order\"]) {\n lines.push(`section-order = ${quoteList(lint.isort[\"section-order\"])}`);\n }\n }\n return lines;\n}\n\nfunction toToml(config: RuffConfig): string {\n const lines: string[] = [];\n\n if (config[\"line-length\"]) {\n lines.push(`line-length = ${config[\"line-length\"]}`);\n }\n if (config.preview) lines.push(\"preview = true\");\n\n if (config.lint) {\n lines.push(\"[lint]\", ...lintSelectionLines(config.lint), ...lintSubsectionLines(config.lint));\n }\n\n return lines.join(\"\\n\") + \"\\n\";\n}\n\ninterface RuffJsonMessage {\n code: string | null;\n filename: string;\n message: string;\n}\n\nfunction notChecked(file: string, message: string): ToolFailure {\n return { tool: \"ruff\", kind: \"file-not-checked\", file, message };\n}\n\n// ruff emits one entry per syntax error; a file that fails to parse is reported once.\nfunction unparsedFiles(stdout: string): ToolFailure[] {\n const byFile = new Map<string, string[]>();\n for (const e of JSON.parse(stdout) as RuffJsonMessage[]) {\n if (!isRuffSyntaxError(e.code)) continue;\n byFile.set(e.filename, [...(byFile.get(e.filename) ?? []), e.message]);\n }\n return [...byFile].map(([file, messages]) => notChecked(file, messages.join(\"; \")));\n}\n\n// ruff 0.16.8 flags an unreadable path (exit 0, \"[]\") only in this stderr wording; if ruff rewords it, the case goes silent again.\nfunction unreadFiles(stderr: string): ToolFailure[] {\n return [...stderr.matchAll(/^warning: Failed to lint (.+?): (.+)$/gm)].map((m) => notChecked(m[1]!, m[2]!));\n}\n\nfunction readRuffOutput(stdout: string, stderr: string): { diagnostics: CheckDiagnostic[]; failures: ToolFailure[] } {\n const parsed = parseOrFail(\"ruff\", () => ({\n diagnostics: parseRuffJsonOutput(stdout),\n failures: [...unparsedFiles(stdout), ...unreadFiles(stderr)],\n }));\n return parsed.ok ? parsed.value : { diagnostics: [], failures: [parsed.failure] };\n}\n\nexport async function runRuff(\n config: RuffConfig,\n files: string[],\n options?: RunnerOptions,\n): Promise<RunnerResult> {\n const tempDir = mkdtempSync(join(tmpdir(), \"codewatch-ruff-\"));\n const configPath = join(tempDir, \"ruff.toml\");\n\n try {\n writeFileSync(configPath, toToml(config), \"utf-8\");\n\n const args = [\n \"check\",\n \"--config\",\n configPath,\n \"--output-format\",\n \"json\",\n ...(options?.fix ? [\"--fix\"] : []),\n ...files,\n ];\n\n const run = await runAndClassify(\"ruff\", \"ruff\", args, { cwd: options?.cwd, timeout: options?.timeout });\n if (!run.ok) return { diagnostics: [], exitCode: run.exitCode, failures: [run.failure], skippedRules: [] };\n return { ...readRuffOutput(run.stdout, run.stderr), exitCode: run.exitCode, skippedRules: [] };\n } finally {\n rmSync(tempDir, { recursive: true, force: true });\n }\n}\n","import { generateEslintConfig } from \"../generators/eslint.js\";\nimport { generateRuffConfig } from \"../generators/ruff.js\";\nimport { runEslint } from \"../runners/eslint-runner.js\";\nimport { runRuff } from \"../runners/ruff-runner.js\";\nimport type { RunnerResult } from \"../runners/types.js\";\nimport type {\n OrchestratorOptions,\n OrchestratorResult,\n CheckDiagnostic,\n} from \"./types.js\";\n\nfunction detectLanguage(files: string[]): \"typescript\" | \"python\" | \"mixed\" {\n const tsFiles = files.filter((f) => /\\.[tj]sx?$/.test(f));\n const pyFiles = files.filter((f) => /\\.py$/.test(f));\n if (tsFiles.length > 0 && pyFiles.length === 0) return \"typescript\";\n if (pyFiles.length > 0 && tsFiles.length === 0) return \"python\";\n return \"mixed\";\n}\n\nfunction buildSummary(\n diagnostics: CheckDiagnostic[],\n): OrchestratorResult[\"summary\"] {\n return {\n total: diagnostics.length,\n errors: diagnostics.filter((d) => d.severity === \"error\").length,\n warnings: diagnostics.filter((d) => d.severity === \"warn\").length,\n infos: diagnostics.filter((d) => d.severity === \"info\").length,\n fixed: 0,\n };\n}\n\nasync function runTools(options: OrchestratorOptions): Promise<RunnerResult[]> {\n const { profile, files, fix } = options;\n const language = options.language ?? detectLanguage(files);\n const tsFiles = files.filter((f) => /\\.[tj]sx?$/.test(f));\n const pyFiles = files.filter((f) => /\\.py$/.test(f));\n const results: RunnerResult[] = [];\n\n if ((language === \"typescript\" || language === \"mixed\") && tsFiles.length > 0) {\n const eslintConfig = generateEslintConfig(profile);\n if (eslintConfig.length > 0) results.push(await runEslint(eslintConfig, tsFiles, { fix }));\n }\n\n if ((language === \"python\" || language === \"mixed\") && pyFiles.length > 0) {\n const ruffConfig = generateRuffConfig(profile);\n if (ruffConfig.lint?.select && ruffConfig.lint.select.length > 0) {\n results.push(await runRuff(ruffConfig, pyFiles, { fix }));\n }\n }\n return results;\n}\n\nexport async function orchestrate(\n options: OrchestratorOptions,\n): Promise<OrchestratorResult> {\n const results = await runTools(options);\n const diagnostics = results.flatMap((r) => r.diagnostics);\n return {\n diagnostics,\n failures: results.flatMap((r) => r.failures),\n skippedRules: results.flatMap((r) => r.skippedRules),\n summary: buildSummary(diagnostics),\n };\n}\n","import type { RuffConfig } from \"./ruff.js\";\n\n/** The audit's pinned ruff selection: complexity, size, dead code, and defensive-bloat signals. */\nexport const AUDIT_RUFF_RULES: readonly string[] = [\n \"C901\",\n \"PLR0904\",\n \"PLR0911\",\n \"PLR0912\",\n \"PLR0913\",\n \"PLR0914\",\n \"PLR0915\",\n \"PLR0916\",\n \"PLR0917\",\n \"PLR1702\",\n \"ARG\",\n \"FBT\",\n \"ERA001\",\n \"BLE001\",\n \"S110\",\n \"TRY203\",\n \"SIM105\",\n \"F401\",\n \"F841\",\n \"PIE790\",\n \"RUF100\",\n];\n\n/** Preview is on because ruff 0.16.8 silently skips PLR0904, PLR0914, PLR0916 and PLR1702 without it. */\nexport function generateRuffAuditConfig(): RuffConfig {\n return { preview: true, lint: { select: [...AUDIT_RUFF_RULES] } };\n}\n","import { realpathSync } from \"node:fs\";\nimport { isAbsolute, relative } from \"node:path\";\nimport { generateRuffAuditConfig } from \"../generators/ruff-audit.js\";\nimport { runRuff } from \"./ruff-runner.js\";\nimport type { RunnerOptions, RunnerResult } from \"./types.js\";\n\n/** Runs the pinned audit rule set; diagnostics carry paths relative to `cwd`, since ruff reports absolute ones. */\nexport async function runRuffAudit(files: string[], options?: Omit<RunnerOptions, \"fix\">): Promise<RunnerResult> {\n const result = await runRuff(generateRuffAuditConfig(), files, options);\n const root = realpathSync(options?.cwd ?? process.cwd());\n const diagnostics = result.diagnostics.map((d) => ({ ...d, file: relativeTo(root, d.file) }));\n return { ...result, diagnostics };\n}\n\nfunction relativeTo(root: string, file: string): string {\n return isAbsolute(file) ? relative(root, file) : file;\n}\n","import { PROFILE_CATEGORIES } from \"@titan-design/style-profile\";\nimport type { Profile, ProfileCategory } from \"@titan-design/style-profile\";\nimport type { Observation } from \"@titan-design/style-analyzer\";\n\nexport interface Deviation {\n file: string;\n line: number;\n rule: string;\n expected: string;\n found: string;\n severity: \"error\" | \"warn\" | \"info\";\n}\n\nexport interface DiffResult {\n deviations: Deviation[];\n summary: {\n total: number;\n matching: number;\n deviating: number;\n };\n}\n\nfunction getSeverity(\n confidence: number,\n thresholds: Profile[\"severityThresholds\"],\n): \"error\" | \"warn\" | \"info\" {\n if (confidence >= thresholds.error) return \"error\";\n if (confidence >= thresholds.warn) return \"warn\";\n return \"info\";\n}\n\nfunction resolveProfileRule(\n profile: Profile,\n observationType: string,\n): { convention: unknown; confidence: number } | undefined {\n const [category, rule] = observationType.split(\".\");\n if (!PROFILE_CATEGORIES.includes(category as ProfileCategory)) return undefined;\n const section = profile[category as ProfileCategory];\n if (!section || typeof section !== \"object\") return undefined;\n const ruleObj = rule === undefined ? undefined : section[rule];\n if (!ruleObj || typeof ruleObj !== \"object\") return undefined;\n if (ruleObj.convention === undefined || ruleObj.confidence === undefined) {\n return undefined;\n }\n return { convention: ruleObj.convention, confidence: ruleObj.confidence };\n}\n\nexport function diffAgainstProfile(\n profile: Profile,\n observations: Observation[],\n): DiffResult {\n const deviations: Deviation[] = [];\n let matching = 0;\n\n for (const obs of observations) {\n const profileRule = resolveProfileRule(profile, obs.type);\n if (!profileRule) continue;\n\n const expected = String(profileRule.convention);\n const found = String(obs.value);\n\n if (found === expected) {\n matching++;\n } else {\n deviations.push({\n file: obs.file,\n line: obs.line,\n rule: obs.type,\n expected,\n found,\n severity: getSeverity(\n profileRule.confidence,\n profile.severityThresholds,\n ),\n });\n }\n }\n\n return {\n deviations,\n summary: {\n total: observations.length,\n matching,\n deviating: deviations.length,\n },\n };\n}\n"],"mappings":"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","names":["join","require","join","writeFileSync","mkdtempSync","rmSync","join","tmpdir","mkdtempSync","join","tmpdir","writeFileSync","rmSync"]}
|
|
1
|
+
{"version":3,"sources":["../src/generators/eslint.ts","../src/generators/ruff.ts","../src/runners/eslint-runner.ts","../src/runners/tool-runner.ts","../src/runners/failures.ts","../src/runners/eslint-plugins.ts","../src/runners/eslint-config-module.ts","../src/formatters/unified.ts","../src/runners/ruff-runner.ts","../src/orchestrator/orchestrate.ts","../src/generators/ruff-audit.ts","../src/runners/paths.ts","../src/runners/ruff-audit.ts","../src/generators/pyright-audit.ts","../src/runners/python-tool.ts","../src/runners/vulture-audit.ts","../src/runners/pydoclint-audit.ts","../src/runners/pyright-audit.ts","../src/runners/import-linter.ts","../src/runners/import-linter-config.ts","../src/runners/import-linter-output.ts","../src/runners/suppressions.ts","../src/profile-diff/diff-against-profile.ts"],"sourcesContent":["import type { Profile } from \"@titan-design/style-profile\";\nimport {\n buildNamingConventionRule,\n buildImportOrderRule,\n buildFunctionLengthRule,\n buildFileNamingRule,\n buildJsdocRules,\n} from \"@titan-design/style-profile\";\n\nexport interface EslintFlatConfigEntry {\n plugins?: Record<string, unknown>;\n rules?: Record<string, unknown>;\n files?: string[];\n}\n\n// ESLint accepts only off, warn and error; style-profile's \"info\" tier becomes warn, as its own exporter does.\nfunction withEslintSeverity(value: unknown): unknown {\n if (Array.isArray(value) && value[0] === \"info\") return [\"warn\", ...value.slice(1)];\n return value === \"info\" ? \"warn\" : value;\n}\n\nfunction collectRules(profile: Profile): Record<string, unknown> {\n const rules: Record<string, unknown> = {};\n\n const namingRule = buildNamingConventionRule(profile);\n if (namingRule) rules[namingRule[0]] = namingRule[1];\n\n const importRule = buildImportOrderRule(profile);\n if (importRule) rules[importRule[0]] = importRule[1];\n\n const fnLengthRule = buildFunctionLengthRule(profile);\n if (fnLengthRule) rules[fnLengthRule[0]] = fnLengthRule[1];\n\n const fileNamingRule = buildFileNamingRule(profile);\n if (fileNamingRule) rules[fileNamingRule[0]] = fileNamingRule[1];\n\n const jsdocRules = buildJsdocRules(profile);\n for (const [name, value] of jsdocRules) {\n rules[name] = value;\n }\n\n for (const name of Object.keys(rules)) rules[name] = withEslintSeverity(rules[name]);\n return rules;\n}\n\nexport function generateEslintConfig(profile: Profile): EslintFlatConfigEntry[] {\n const entries: EslintFlatConfigEntry[] = [];\n const rules = collectRules(profile);\n\n if (Object.keys(rules).length > 0) {\n entries.push({\n files: [\"**/*.ts\", \"**/*.tsx\"],\n rules,\n });\n }\n\n return entries;\n}\n","import type { Profile } from \"@titan-design/style-profile\";\n\nexport interface RuffConfig {\n lint?: {\n select?: string[];\n ignore?: string[];\n fixable?: string[];\n \"per-file-ignores\"?: Record<string, string[]>;\n mccabe?: { \"max-complexity\"?: number };\n pydocstyle?: { convention?: string };\n isort?: {\n \"known-first-party\"?: string[];\n \"section-order\"?: string[];\n };\n };\n \"line-length\"?: number;\n preview?: boolean;\n}\n\ntype RuffLint = NonNullable<RuffConfig[\"lint\"]> & { select: string[] };\n\nfunction addNaming(profile: Profile, lint: RuffLint): void {\n if (profile.naming) {\n const hasNamingRules = Object.values(profile.naming).some(\n (rule) => rule.confidence >= profile.severityThresholds.info,\n );\n if (hasNamingRules) lint.select.push(\"N\");\n }\n}\n\nfunction addImportOrder(profile: Profile, lint: RuffLint): void {\n const importOrder = profile.structure?.importOrder;\n if (importOrder && importOrder.confidence >= profile.severityThresholds.info) {\n lint.select.push(\"I\");\n if (Array.isArray(importOrder.convention)) {\n lint.isort = {\n \"section-order\": importOrder.convention as string[],\n };\n }\n }\n}\n\nfunction addFunctionDocs(profile: Profile, lint: RuffLint): void {\n const functionDocs = profile.documentation?.functionDocs;\n if (functionDocs && functionDocs.confidence >= profile.severityThresholds.info) {\n lint.select.push(\"D\");\n const conv = functionDocs.convention;\n if (\n typeof conv === \"string\" &&\n conv !== \"jsdoc-selective\" &&\n conv !== \"jsdoc-all\"\n ) {\n lint.pydocstyle = { convention: conv };\n }\n }\n}\n\nfunction addComplexity(profile: Profile, lint: RuffLint): void {\n const maxLines = profile.structure?.functionMaxLines;\n if (\n maxLines &&\n maxLines.confidence >= profile.severityThresholds.info &&\n typeof maxLines.convention === \"number\"\n ) {\n lint.select.push(\"C90\");\n lint.mccabe = { \"max-complexity\": maxLines.convention };\n }\n}\n\nexport function generateRuffConfig(profile: Profile): RuffConfig {\n const lint: RuffLint = { select: [] };\n const config: RuffConfig = { lint };\n\n addNaming(profile, lint);\n addImportOrder(profile, lint);\n addFunctionDocs(profile, lint);\n addComplexity(profile, lint);\n\n const lineLength = profile.formatting?.lineLength;\n if (lineLength && typeof lineLength.convention === \"number\") {\n config[\"line-length\"] = lineLength.convention;\n }\n\n return config;\n}\n","import { writeFileSync, mkdtempSync, rmSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { tmpdir } from \"node:os\";\nimport { runAndClassify, parseOrFail } from \"./failures.js\";\nimport { buildEslintConfigModule } from \"./eslint-config-module.js\";\nimport { parseEslintJsonOutput } from \"../formatters/unified.js\";\nimport type { CheckDiagnostic, ToolFailure } from \"../orchestrator/types.js\";\nimport type { EslintFlatConfigEntry } from \"../generators/eslint.js\";\nimport type { RunnerOptions, RunnerResult } from \"./types.js\";\n\ninterface EslintJsonMessage {\n ruleId: string | null;\n fatal?: boolean;\n message: string;\n}\n\n// ESLint reports a file it could not lint as a rule-less message: a fatal parse error, or \"File ignored ...\".\nfunction uncheckedFiles(stdout: string): ToolFailure[] {\n const entries = JSON.parse(stdout) as Array<{ filePath: string; messages: EslintJsonMessage[] }>;\n return entries.flatMap((entry) =>\n entry.messages\n .filter((m) => m.ruleId === null && (m.fatal === true || m.message.startsWith(\"File ignored\")))\n .map((m) => ({ tool: \"eslint\" as const, kind: \"file-not-checked\" as const, file: entry.filePath, message: m.message })),\n );\n}\n\nfunction readEslintOutput(stdout: string): { diagnostics: CheckDiagnostic[]; failures: ToolFailure[] } {\n const parsed = parseOrFail(\"eslint\", () => ({\n diagnostics: parseEslintJsonOutput(stdout),\n failures: uncheckedFiles(stdout),\n }));\n return parsed.ok ? parsed.value : { diagnostics: [], failures: [parsed.failure] };\n}\n\nfunction eslintArgs(configPath: string, files: string[], fix?: boolean): string[] {\n // --no stops npx from downloading an ESLint the project has not installed.\n return [\"--no\", \"--\", \"eslint\", \"--config\", configPath, \"--format\", \"json\", ...(fix ? [\"--fix\"] : []), ...files];\n}\n\nexport async function runEslint(\n config: EslintFlatConfigEntry[],\n files: string[],\n options?: RunnerOptions,\n): Promise<RunnerResult> {\n const cwd = options?.cwd ?? process.cwd();\n const built = buildEslintConfigModule(config, cwd);\n if (!built.ok) return { diagnostics: [], exitCode: null, failures: [built.failure], skippedRules: [] };\n const { skippedRules } = built;\n if (built.ruleCount === 0) return { diagnostics: [], exitCode: null, failures: [], skippedRules };\n\n const tempDir = mkdtempSync(join(tmpdir(), \"codewatch-eslint-\"));\n const configPath = join(tempDir, \"eslint.config.mjs\");\n try {\n writeFileSync(configPath, built.source, \"utf-8\");\n const run = await runAndClassify(\"eslint\", \"npx\", eslintArgs(configPath, files, options?.fix), {\n cwd,\n timeout: options?.timeout,\n });\n if (!run.ok) return { diagnostics: [], exitCode: run.exitCode, failures: [run.failure], skippedRules };\n return { ...readEslintOutput(run.stdout), exitCode: run.exitCode, skippedRules };\n } finally {\n rmSync(tempDir, { recursive: true, force: true });\n }\n}\n","import { spawn } from \"node:child_process\";\n\nexport interface ToolRunResult {\n stdout: string;\n stderr: string;\n /** Null when the child was killed by a signal; see `signal`. */\n exitCode: number | null;\n signal: NodeJS.Signals | null;\n timedOut: boolean;\n}\n\nexport function runTool(\n command: string,\n args: string[],\n options?: { cwd?: string; timeout?: number },\n): Promise<ToolRunResult> {\n return new Promise((resolve, reject) => {\n const child = spawn(command, args, {\n cwd: options?.cwd,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n const chunks: Buffer[] = [];\n const errChunks: Buffer[] = [];\n let timedOut = false;\n // Our own timer, because spawn's `timeout` option kills without telling us it did.\n const timer = setTimeout(() => {\n timedOut = true;\n child.kill(\"SIGKILL\");\n }, options?.timeout ?? 60_000);\n\n child.stdout.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n child.stderr.on(\"data\", (chunk: Buffer) => errChunks.push(chunk));\n\n child.on(\"error\", (err) => {\n clearTimeout(timer);\n reject(new Error(`Failed to spawn ${command}: ${err.message}`));\n });\n\n child.on(\"close\", (code, signal) => {\n clearTimeout(timer);\n resolve({\n stdout: Buffer.concat(chunks).toString(\"utf-8\"),\n stderr: Buffer.concat(errChunks).toString(\"utf-8\"),\n exitCode: code,\n signal,\n timedOut,\n });\n });\n });\n}\n","import { runTool } from \"./tool-runner.js\";\nimport type { ToolRunResult } from \"./tool-runner.js\";\nimport type { ToolFailure, ToolName } from \"../orchestrator/types.js\";\n\n// ESLint and ruff both document 0 (clean) and 1 (problems found) as successful runs.\nconst SUCCESS_EXIT_CODES = new Set([0, 1]);\n\nexport type ToolRun =\n | { ok: true; stdout: string; stderr: string; exitCode: number }\n | { ok: false; failure: ToolFailure; exitCode: number | null };\n\nfunction excerpt(text: string): string {\n const trimmed = text.trim();\n return trimmed.length > 500 ? `${trimmed.slice(0, 500)}…` : trimmed;\n}\n\nfunction withStderr(message: string, stderr: string): string {\n const detail = excerpt(stderr);\n return detail ? `${message}: ${detail}` : message;\n}\n\n/** What counts as a successful run; vulture exits 3 on findings, and a clean vulture or pydoclint run prints nothing. */\nexport interface RunExpectations {\n successCodes?: ReadonlySet<number>;\n allowEmptyStdout?: boolean;\n}\n\nexport function classifyRun(tool: ToolName, run: ToolRunResult, expect: RunExpectations = {}): ToolRun {\n const fail = (kind: ToolFailure[\"kind\"], message: string): ToolRun => ({\n ok: false,\n failure: { tool, kind, message: withStderr(message, run.stderr) },\n exitCode: run.exitCode,\n });\n if (run.timedOut) return fail(\"timeout\", `${tool} timed out and was killed`);\n if (run.exitCode === null) {\n return fail(\"signal\", `${tool} was killed by ${run.signal ?? \"a signal\"}`);\n }\n if (!(expect.successCodes ?? SUCCESS_EXIT_CODES).has(run.exitCode)) {\n return fail(\"exit-code\", `${tool} exited with code ${run.exitCode}`);\n }\n if (!expect.allowEmptyStdout && !run.stdout.trim()) {\n return fail(\"unparseable-output\", `${tool} exited ${run.exitCode} with no output`);\n }\n return { ok: true, stdout: run.stdout, stderr: run.stderr, exitCode: run.exitCode };\n}\n\nexport async function runAndClassify(\n tool: ToolName,\n command: string,\n args: string[],\n options?: { cwd?: string; timeout?: number },\n expect?: RunExpectations,\n): Promise<ToolRun> {\n try {\n return classifyRun(tool, await runTool(command, args, options), expect);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return { ok: false, failure: { tool, kind: \"spawn-failed\", message }, exitCode: null };\n }\n}\n\nexport function parseOrFail<T>(\n tool: ToolName,\n parse: () => T,\n): { ok: true; value: T } | { ok: false; failure: ToolFailure } {\n try {\n return { ok: true, value: parse() };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return { ok: false, failure: { tool, kind: \"unparseable-output\", message } };\n }\n}\n","import { createRequire } from \"node:module\";\nimport { join } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\n\nexport interface ModuleSource {\n specifier: string;\n member?: \"plugin\" | \"parser\";\n}\n\nexport interface ResolvedModule {\n url: string;\n member?: \"plugin\" | \"parser\";\n}\n\n// The typescript-eslint meta-package is the fallback because under pnpm it is often the only one a project can resolve.\nexport const PLUGIN_SOURCES: Record<string, ModuleSource[]> = {\n \"@typescript-eslint\": [\n { specifier: \"@typescript-eslint/eslint-plugin\" },\n { specifier: \"typescript-eslint\", member: \"plugin\" },\n ],\n perfectionist: [{ specifier: \"eslint-plugin-perfectionist\" }],\n unicorn: [{ specifier: \"eslint-plugin-unicorn\" }],\n jsdoc: [{ specifier: \"eslint-plugin-jsdoc\" }],\n};\n\nexport const PARSER_SOURCES: ModuleSource[] = [\n { specifier: \"@typescript-eslint/parser\" },\n { specifier: \"typescript-eslint\", member: \"parser\" },\n];\n\nexport function pluginNamespace(rule: string): string | null {\n const slash = rule.lastIndexOf(\"/\");\n return slash === -1 ? null : rule.slice(0, slash);\n}\n\nexport function describeSources(sources: ModuleSource[]): string {\n return sources.map((s) => s.specifier).join(\" or \");\n}\n\nexport function resolveModule(\n sources: ModuleSource[],\n fromDir: string,\n): ResolvedModule | null {\n // Resolve from the project, not from this package: ESLint runs there and loads plugins from there.\n const require = createRequire(join(fromDir, \"package.json\"));\n for (const source of sources) {\n try {\n const url = pathToFileURL(require.resolve(source.specifier)).href;\n return { url, member: source.member };\n } catch {\n continue;\n }\n }\n return null;\n}\n","import {\n PARSER_SOURCES,\n PLUGIN_SOURCES,\n describeSources,\n pluginNamespace,\n resolveModule,\n} from \"./eslint-plugins.js\";\nimport type { ResolvedModule } from \"./eslint-plugins.js\";\nimport type { EslintFlatConfigEntry } from \"../generators/eslint.js\";\nimport type { SkippedRule, ToolFailure } from \"../orchestrator/types.js\";\n\nexport type EslintConfigModule =\n | { ok: true; source: string; ruleCount: number; skippedRules: SkippedRule[] }\n | { ok: false; failure: ToolFailure };\n\ninterface ModuleContext {\n fromDir: string;\n imports: string[];\n ids: Map<string, string>;\n plugins: Map<string, ResolvedModule | null>;\n}\n\nfunction importExpr(ctx: ModuleContext, mod: ResolvedModule): string {\n let id = ctx.ids.get(mod.url);\n if (!id) {\n id = `m${ctx.ids.size}`;\n ctx.ids.set(mod.url, id);\n ctx.imports.push(`import ${id} from ${JSON.stringify(mod.url)};`);\n }\n return mod.member ? `${id}.${mod.member}` : id;\n}\n\nfunction resolvePlugin(ctx: ModuleContext, namespace: string): ResolvedModule | null {\n if (!ctx.plugins.has(namespace)) {\n const sources = PLUGIN_SOURCES[namespace];\n ctx.plugins.set(namespace, sources ? resolveModule(sources, ctx.fromDir) : null);\n }\n return ctx.plugins.get(namespace) ?? null;\n}\n\nfunction skipReason(ctx: ModuleContext, namespace: string): string {\n const sources = PLUGIN_SOURCES[namespace];\n if (!sources) return `no known package provides the ESLint plugin \"${namespace}\"`;\n return `${describeSources(sources)} is not installed in ${ctx.fromDir}`;\n}\n\nfunction renderEntry(\n ctx: ModuleContext,\n entry: EslintFlatConfigEntry,\n parserExpr: string,\n skipped: SkippedRule[],\n): { text: string; ruleCount: number } {\n const rules: Record<string, unknown> = {};\n const plugins: string[] = [];\n for (const [rule, value] of Object.entries(entry.rules ?? {})) {\n const namespace = pluginNamespace(rule);\n const plugin = namespace === null ? null : resolvePlugin(ctx, namespace);\n if (namespace !== null && !plugin) {\n skipped.push({ tool: \"eslint\", rule, plugin: namespace, reason: skipReason(ctx, namespace) });\n continue;\n }\n rules[rule] = value;\n if (namespace !== null && plugin) plugins.push(`${JSON.stringify(namespace)}: ${importExpr(ctx, plugin)}`);\n }\n const data = JSON.stringify({ files: entry.files, rules });\n const text = `{ ...${data}, languageOptions: { parser: ${parserExpr} }, plugins: { ${[...new Set(plugins)].join(\", \")} } }`;\n return { text, ruleCount: Object.keys(rules).length };\n}\n\nexport function buildEslintConfigModule(\n config: EslintFlatConfigEntry[],\n fromDir: string,\n): EslintConfigModule {\n const parser = resolveModule(PARSER_SOURCES, fromDir);\n if (!parser) {\n const message = `${describeSources(PARSER_SOURCES)} is not installed in ${fromDir}, so ESLint cannot parse TypeScript`;\n return { ok: false, failure: { tool: \"eslint\", kind: \"missing-dependency\", message } };\n }\n const ctx: ModuleContext = { fromDir, imports: [], ids: new Map(), plugins: new Map() };\n const parserExpr = importExpr(ctx, parser);\n const skippedRules: SkippedRule[] = [];\n const entries = config.map((entry) => renderEntry(ctx, entry, parserExpr, skippedRules));\n const ruleCount = entries.reduce((sum, e) => sum + e.ruleCount, 0);\n const body = entries.map((e) => e.text).join(\",\\n \");\n const source = `${ctx.imports.join(\"\\n\")}\\n\\nexport default [\\n ${body},\\n];\\n`;\n return { ok: true, source, ruleCount, skippedRules };\n}\n","import type { CheckDiagnostic } from \"../orchestrator/types.js\";\n\nconst RUFF_CODE_CATEGORY: Record<string, string> = {\n N: \"naming\",\n I: \"imports\",\n D: \"documentation\",\n C: \"complexity\",\n E: \"formatting\",\n W: \"formatting\",\n F: \"errors\",\n};\n\nconst ESLINT_RULE_CATEGORY: Record<string, string> = {\n \"@typescript-eslint/naming-convention\": \"naming\",\n \"unicorn/filename-case\": \"naming\",\n \"perfectionist/sort-imports\": \"imports\",\n \"import/order\": \"imports\",\n \"max-lines-per-function\": \"structure\",\n \"max-depth\": \"structure\",\n \"jsdoc/require-jsdoc\": \"documentation\",\n};\n\nfunction eslintSeverityToSeverity(eslintSeverity: number): \"error\" | \"warn\" {\n return eslintSeverity === 2 ? \"error\" : \"warn\";\n}\n\nfunction categorizeEslintRule(ruleId: string): string {\n if (ESLINT_RULE_CATEGORY[ruleId]) return ESLINT_RULE_CATEGORY[ruleId];\n if (ruleId.startsWith(\"@typescript-eslint/\")) return \"typescript\";\n if (ruleId.startsWith(\"jsdoc/\")) return \"documentation\";\n if (ruleId.startsWith(\"unicorn/\")) return \"style\";\n if (ruleId.startsWith(\"import/\")) return \"imports\";\n return \"other\";\n}\n\nfunction categorizeRuffCode(code: string): string {\n const prefix = code.replace(/[0-9]/g, \"\");\n return RUFF_CODE_CATEGORY[prefix] ?? \"other\";\n}\n\ninterface EslintJsonEntry {\n filePath: string;\n messages: Array<{\n ruleId: string | null;\n severity: number;\n message: string;\n line: number;\n column: number;\n fix?: { range: [number, number]; text: string } | null;\n }>;\n}\n\ninterface RuffJsonEntry {\n code: string | null;\n message: string;\n filename: string;\n location: { row: number; column: number };\n end_location: { row: number; column: number };\n fix?: {\n edits: Array<{\n content: string;\n location: { row: number; column: number };\n end_location: { row: number; column: number };\n }>;\n } | null;\n}\n\nexport function parseEslintJsonOutput(jsonStr: string): CheckDiagnostic[] {\n let entries: EslintJsonEntry[];\n try {\n entries = JSON.parse(jsonStr);\n } catch {\n throw new Error(\n `Failed to parse ESLint JSON output. Raw output:\\n${jsonStr.slice(0, 500)}`,\n );\n }\n const diagnostics: CheckDiagnostic[] = [];\n\n for (const entry of entries) {\n for (const msg of entry.messages) {\n if (!msg.ruleId) continue;\n diagnostics.push({\n file: entry.filePath,\n line: msg.line,\n column: msg.column,\n severity: eslintSeverityToSeverity(msg.severity),\n message: msg.message,\n category: categorizeEslintRule(msg.ruleId),\n rule: msg.ruleId,\n fixable: msg.fix != null,\n fix: msg.fix ?? undefined,\n });\n }\n }\n\n return diagnostics;\n}\n\n// ruff 0.16.8 reports a syntax error as code \"invalid-syntax\"; 0.9.10 used a null code.\nexport function isRuffSyntaxError(code: string | null): code is \"invalid-syntax\" | null {\n return code === null || code === \"invalid-syntax\";\n}\n\nexport function parseRuffJsonOutput(jsonStr: string): CheckDiagnostic[] {\n let entries: RuffJsonEntry[];\n try {\n entries = JSON.parse(jsonStr);\n } catch {\n throw new Error(\n `Failed to parse Ruff JSON output. Raw output:\\n${jsonStr.slice(0, 500)}`,\n );\n }\n return entries.flatMap((entry) => isRuffSyntaxError(entry.code) ? [] : [{\n file: entry.filename,\n line: entry.location.row,\n endLine: entry.end_location.row,\n column: entry.location.column,\n severity: \"warn\" as const,\n message: entry.message,\n category: categorizeRuffCode(entry.code),\n rule: entry.code,\n fixable: entry.fix != null,\n }]);\n}\n\nexport function formatDiagnostic(d: CheckDiagnostic): string {\n return `${d.file}:${d.line}:${d.column} ${d.severity} ${d.message} [${d.category}.${d.rule}]`;\n}\n","import { writeFileSync, mkdtempSync, rmSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { tmpdir } from \"node:os\";\nimport { runAndClassify, parseOrFail } from \"./failures.js\";\nimport { isRuffSyntaxError, parseRuffJsonOutput } from \"../formatters/unified.js\";\nimport type { CheckDiagnostic, ToolFailure } from \"../orchestrator/types.js\";\nimport type { RunnerOptions, RunnerResult } from \"./types.js\";\nimport type { RuffConfig } from \"../generators/ruff.js\";\n\ntype RuffLint = NonNullable<RuffConfig[\"lint\"]>;\n\nfunction quoteList(values: string[]): string {\n return `[${values.map((s) => `\"${s}\"`).join(\", \")}]`;\n}\n\nfunction lintSelectionLines(lint: RuffLint): string[] {\n const lines: string[] = [];\n if (lint.select) lines.push(`select = ${quoteList(lint.select)}`);\n if (lint.ignore) lines.push(`ignore = ${quoteList(lint.ignore)}`);\n return lines;\n}\n\nfunction lintSubsectionLines(lint: RuffLint): string[] {\n const lines: string[] = [];\n if (lint.mccabe) {\n lines.push(\"[lint.mccabe]\");\n if (lint.mccabe[\"max-complexity\"]) {\n lines.push(`max-complexity = ${lint.mccabe[\"max-complexity\"]}`);\n }\n }\n if (lint.pydocstyle) {\n lines.push(\"[lint.pydocstyle]\");\n if (lint.pydocstyle.convention) {\n lines.push(`convention = \"${lint.pydocstyle.convention}\"`);\n }\n }\n if (lint.isort) {\n lines.push(\"[lint.isort]\");\n if (lint.isort[\"section-order\"]) {\n lines.push(`section-order = ${quoteList(lint.isort[\"section-order\"])}`);\n }\n }\n return lines;\n}\n\nfunction toToml(config: RuffConfig): string {\n const lines: string[] = [];\n\n if (config[\"line-length\"]) {\n lines.push(`line-length = ${config[\"line-length\"]}`);\n }\n if (config.preview) lines.push(\"preview = true\");\n\n if (config.lint) {\n lines.push(\"[lint]\", ...lintSelectionLines(config.lint), ...lintSubsectionLines(config.lint));\n }\n\n return lines.join(\"\\n\") + \"\\n\";\n}\n\ninterface RuffJsonMessage {\n code: string | null;\n filename: string;\n message: string;\n}\n\nfunction notChecked(file: string, message: string): ToolFailure {\n return { tool: \"ruff\", kind: \"file-not-checked\", file, message };\n}\n\n// ruff emits one entry per syntax error; a file that fails to parse is reported once.\nfunction unparsedFiles(stdout: string): ToolFailure[] {\n const byFile = new Map<string, string[]>();\n for (const e of JSON.parse(stdout) as RuffJsonMessage[]) {\n if (!isRuffSyntaxError(e.code)) continue;\n byFile.set(e.filename, [...(byFile.get(e.filename) ?? []), e.message]);\n }\n return [...byFile].map(([file, messages]) => notChecked(file, messages.join(\"; \")));\n}\n\n// ruff 0.16.8 flags an unreadable path (exit 0, \"[]\") only in this stderr wording; if ruff rewords it, the case goes silent again.\nfunction unreadFiles(stderr: string): ToolFailure[] {\n return [...stderr.matchAll(/^warning: Failed to lint (.+?): (.+)$/gm)].map((m) => notChecked(m[1]!, m[2]!));\n}\n\nfunction readRuffOutput(stdout: string, stderr: string): { diagnostics: CheckDiagnostic[]; failures: ToolFailure[] } {\n const parsed = parseOrFail(\"ruff\", () => ({\n diagnostics: parseRuffJsonOutput(stdout),\n failures: [...unparsedFiles(stdout), ...unreadFiles(stderr)],\n }));\n return parsed.ok ? parsed.value : { diagnostics: [], failures: [parsed.failure] };\n}\n\nexport async function runRuff(\n config: RuffConfig,\n files: string[],\n options?: RunnerOptions,\n): Promise<RunnerResult> {\n const tempDir = mkdtempSync(join(tmpdir(), \"codewatch-ruff-\"));\n const configPath = join(tempDir, \"ruff.toml\");\n\n try {\n writeFileSync(configPath, toToml(config), \"utf-8\");\n\n const args = [\n \"check\",\n \"--config\",\n configPath,\n \"--output-format\",\n \"json\",\n ...(options?.fix ? [\"--fix\"] : []),\n ...files,\n ];\n\n const run = await runAndClassify(\"ruff\", \"ruff\", args, { cwd: options?.cwd, timeout: options?.timeout });\n if (!run.ok) return { diagnostics: [], exitCode: run.exitCode, failures: [run.failure], skippedRules: [] };\n return { ...readRuffOutput(run.stdout, run.stderr), exitCode: run.exitCode, skippedRules: [] };\n } finally {\n rmSync(tempDir, { recursive: true, force: true });\n }\n}\n","import { generateEslintConfig } from \"../generators/eslint.js\";\nimport { generateRuffConfig } from \"../generators/ruff.js\";\nimport { runEslint } from \"../runners/eslint-runner.js\";\nimport { runRuff } from \"../runners/ruff-runner.js\";\nimport type { RunnerResult } from \"../runners/types.js\";\nimport type {\n OrchestratorOptions,\n OrchestratorResult,\n CheckDiagnostic,\n} from \"./types.js\";\n\nfunction detectLanguage(files: string[]): \"typescript\" | \"python\" | \"mixed\" {\n const tsFiles = files.filter((f) => /\\.[tj]sx?$/.test(f));\n const pyFiles = files.filter((f) => /\\.py$/.test(f));\n if (tsFiles.length > 0 && pyFiles.length === 0) return \"typescript\";\n if (pyFiles.length > 0 && tsFiles.length === 0) return \"python\";\n return \"mixed\";\n}\n\nfunction buildSummary(\n diagnostics: CheckDiagnostic[],\n): OrchestratorResult[\"summary\"] {\n return {\n total: diagnostics.length,\n errors: diagnostics.filter((d) => d.severity === \"error\").length,\n warnings: diagnostics.filter((d) => d.severity === \"warn\").length,\n infos: diagnostics.filter((d) => d.severity === \"info\").length,\n fixed: 0,\n };\n}\n\nasync function runTools(options: OrchestratorOptions): Promise<RunnerResult[]> {\n const { profile, files, fix } = options;\n const language = options.language ?? detectLanguage(files);\n const tsFiles = files.filter((f) => /\\.[tj]sx?$/.test(f));\n const pyFiles = files.filter((f) => /\\.py$/.test(f));\n const results: RunnerResult[] = [];\n\n if ((language === \"typescript\" || language === \"mixed\") && tsFiles.length > 0) {\n const eslintConfig = generateEslintConfig(profile);\n if (eslintConfig.length > 0) results.push(await runEslint(eslintConfig, tsFiles, { fix }));\n }\n\n if ((language === \"python\" || language === \"mixed\") && pyFiles.length > 0) {\n const ruffConfig = generateRuffConfig(profile);\n if (ruffConfig.lint?.select && ruffConfig.lint.select.length > 0) {\n results.push(await runRuff(ruffConfig, pyFiles, { fix }));\n }\n }\n return results;\n}\n\nexport async function orchestrate(\n options: OrchestratorOptions,\n): Promise<OrchestratorResult> {\n const results = await runTools(options);\n const diagnostics = results.flatMap((r) => r.diagnostics);\n return {\n diagnostics,\n failures: results.flatMap((r) => r.failures),\n skippedRules: results.flatMap((r) => r.skippedRules),\n summary: buildSummary(diagnostics),\n };\n}\n","import type { RuffConfig } from \"./ruff.js\";\n\n/** The audit's pinned ruff selection: complexity, size, dead code, and defensive-bloat signals. */\nexport const AUDIT_RUFF_RULES: readonly string[] = [\n \"C901\",\n \"PLR0904\",\n \"PLR0911\",\n \"PLR0912\",\n \"PLR0913\",\n \"PLR0914\",\n \"PLR0915\",\n \"PLR0916\",\n \"PLR0917\",\n \"PLR1702\",\n \"ARG\",\n \"FBT\",\n \"ERA001\",\n \"BLE001\",\n \"S110\",\n \"TRY203\",\n \"SIM105\",\n \"F401\",\n \"F841\",\n \"PIE790\",\n \"RUF100\",\n];\n\n/** Preview is on because ruff 0.16.8 silently skips PLR0904, PLR0914, PLR0916 and PLR1702 without it. */\nexport function generateRuffAuditConfig(): RuffConfig {\n return { preview: true, lint: { select: [...AUDIT_RUFF_RULES] } };\n}\n","import { realpathSync } from \"node:fs\";\nimport { isAbsolute, relative } from \"node:path\";\n\n/** The directory diagnostics are reported relative to, resolved so it matches the real paths tools print. */\nexport function auditRoot(cwd?: string): string {\n return realpathSync(cwd ?? process.cwd());\n}\n\nexport function relativeTo(root: string, file: string): string {\n return isAbsolute(file) ? relative(root, file) : file;\n}\n","import { generateRuffAuditConfig } from \"../generators/ruff-audit.js\";\nimport { auditRoot, relativeTo } from \"./paths.js\";\nimport { runRuff } from \"./ruff-runner.js\";\nimport type { RunnerOptions, RunnerResult } from \"./types.js\";\n\n/** Runs the pinned audit rule set; diagnostics carry paths relative to `cwd`, since ruff reports absolute ones. */\nexport async function runRuffAudit(files: string[], options?: Omit<RunnerOptions, \"fix\">): Promise<RunnerResult> {\n const result = await runRuff(generateRuffAuditConfig(), files, options);\n const root = auditRoot(options?.cwd);\n const diagnostics = result.diagnostics.map((d) => ({ ...d, file: relativeTo(root, d.file) }));\n return { ...result, diagnostics };\n}\n","/** The audit's pinned pyright selection: checks whose finding is a guard or conversion the types already make redundant. */\nexport const AUDIT_PYRIGHT_RULES: readonly string[] = [\n \"reportUnnecessaryIsInstance\",\n \"reportUnnecessaryComparison\",\n \"reportUnnecessaryContains\",\n \"reportUnnecessaryCast\",\n];\n\n// typeCheckingMode \"off\" still leaves these on, and an audit that cannot see the project's dependencies would drown in them.\nconst STILL_ON_WHEN_OFF = [\"reportMissingImports\", \"reportMissingModuleSource\", \"reportUndefinedVariable\"];\n\nexport type PyrightConfig = Record<string, string | string[]>;\n\n/** Everything off except the audit rules, as warnings; `root` goes on extraPaths so the project's own imports resolve. */\nexport function generatePyrightAuditConfig(root: string): PyrightConfig {\n const config: PyrightConfig = { typeCheckingMode: \"off\", extraPaths: [root] };\n for (const rule of STILL_ON_WHEN_OFF) config[rule] = \"none\";\n for (const rule of AUDIT_PYRIGHT_RULES) config[rule] = \"warning\";\n return config;\n}\n","import { parseOrFail, runAndClassify } from \"./failures.js\";\nimport type { RunExpectations, ToolRun } from \"./failures.js\";\nimport { auditRoot, relativeTo } from \"./paths.js\";\nimport type { CheckDiagnostic, ToolFailure, ToolName } from \"../orchestrator/types.js\";\nimport type { RunnerOptions, RunnerResult } from \"./types.js\";\n\n/** How to invoke one audit tool and what to tell the user when it is not installed. */\nexport interface PythonToolSpec {\n tool: ToolName;\n command: string;\n install: string;\n installNote?: string;\n expect: RunExpectations;\n}\n\nexport interface ToolOutput {\n stdout: string;\n stderr: string;\n}\n\nexport interface ParsedOutput {\n diagnostics: CheckDiagnostic[];\n failures: ToolFailure[];\n}\n\nexport type AuditOptions = Omit<RunnerOptions, \"fix\">;\n\nexport function emptyResult(exitCode: number | null, extra: Partial<RunnerResult> = {}): RunnerResult {\n return { diagnostics: [], exitCode, failures: [], skippedRules: [], warnings: [], ...extra };\n}\n\nexport function missingToolWarning(spec: PythonToolSpec): string {\n const note = spec.installNote ? ` (${spec.installNote})` : \"\";\n return `${spec.tool} not found; install with \\`${spec.install}\\`${note}`;\n}\n\nfunction isMissingBinary(run: Extract<ToolRun, { ok: false }>): boolean {\n const { kind, message } = run.failure;\n if (kind === \"spawn-failed\" && message.includes(\"ENOENT\")) return true;\n return run.exitCode === 127 || /command not found/i.test(message);\n}\n\nfunction relativise(root: string, parsed: ParsedOutput): ParsedOutput {\n return {\n diagnostics: parsed.diagnostics.map((d) => ({ ...d, file: relativeTo(root, d.file) })),\n failures: parsed.failures.map((f) => (f.file ? { ...f, file: relativeTo(root, f.file) } : f)),\n };\n}\n\n/** Runs one tool, maps an absent binary to a warning, and reports paths relative to `cwd`. */\nexport async function runPythonTool(\n spec: PythonToolSpec,\n args: string[],\n options: AuditOptions | undefined,\n parse: (output: ToolOutput) => ParsedOutput,\n): Promise<RunnerResult> {\n const runOptions = { cwd: options?.cwd, timeout: options?.timeout };\n const run = await runAndClassify(spec.tool, spec.command, args, runOptions, spec.expect);\n if (!run.ok && isMissingBinary(run)) return emptyResult(null, { warnings: [missingToolWarning(spec)] });\n if (!run.ok) return emptyResult(run.exitCode, { failures: [run.failure] });\n const parsed = parseOrFail(spec.tool, () => parse(run));\n if (!parsed.ok) return emptyResult(run.exitCode, { failures: [parsed.failure] });\n return emptyResult(run.exitCode, relativise(auditRoot(options?.cwd), parsed.value));\n}\n\n/** Stderr lines a parser did not recognise, folded into one failure so a crash is never silent. */\nexport function unrecognisedStderr(tool: ToolName, lines: string[]): ToolFailure[] {\n if (lines.length === 0) return [];\n const message = lines.join(\"\\n\");\n return [{ tool, kind: \"unparseable-output\", message: message.length > 500 ? `${message.slice(0, 500)}…` : message }];\n}\n","import { runPythonTool, unrecognisedStderr } from \"./python-tool.js\";\nimport type { AuditOptions, ParsedOutput, PythonToolSpec, ToolOutput } from \"./python-tool.js\";\nimport type { CheckDiagnostic, ToolFailure } from \"../orchestrator/types.js\";\nimport type { RunnerResult } from \"./types.js\";\n\n// vulture exits 1 on invalid input (a syntax error) and 3 when it found dead code; both still carry findings.\nconst VULTURE: PythonToolSpec = {\n tool: \"vulture\",\n command: \"vulture\",\n install: \"pip install vulture\",\n expect: { successCodes: new Set([0, 1, 3]), allowEmptyStdout: true },\n};\n\nconst FINDING = /^(.+?):(\\d+): (.+) \\((\\d+)% confidence(?:, \\d+ lines?)?\\)$/;\nconst INVALID_INPUT = /^(.+?):(\\d+): (.+)$/;\nconst NOT_FOUND = /^Error: (.+) could not be found\\.$/;\n\nexport interface VultureOptions extends AuditOptions {\n /** vulture's --min-confidence; 60 keeps its uncertain unused-code guesses, 100 keeps only unreachable code. */\n minConfidence?: number;\n}\n\n/** Rule suffix from vulture's wording: \"unused function 'x'\" is unused-function; every other finding is unreachable code. */\nexport function vultureKind(text: string): string {\n const unused = /^unused (\\w+)/.exec(text);\n return unused ? `unused-${unused[1]}` : \"unreachable\";\n}\n\nfunction toDiagnostic(m: RegExpExecArray): CheckDiagnostic {\n const [, file, line, text, confidence] = m as unknown as [string, string, string, string, string];\n return {\n file,\n line: Number(line),\n column: 1,\n severity: \"warn\",\n message: `${text} (${confidence}% confidence)`,\n category: \"dead-code\",\n rule: `vulture/${vultureKind(text)}`,\n fixable: false,\n };\n}\n\nfunction parseStdout(stdout: string): CheckDiagnostic[] {\n return stdout.split(\"\\n\").filter((l) => l.trim()).map((line) => {\n const m = FINDING.exec(line);\n if (!m) throw new Error(`Unrecognised vulture line: ${line}`);\n return toDiagnostic(m);\n });\n}\n\nfunction parseStderr(stderr: string): ToolFailure[] {\n const failures: ToolFailure[] = [];\n const unknown: string[] = [];\n for (const line of stderr.split(\"\\n\").filter((l) => l.trim())) {\n const missing = NOT_FOUND.exec(line);\n const invalid = INVALID_INPUT.exec(line);\n if (missing) failures.push({ tool: \"vulture\", kind: \"file-not-checked\", file: missing[1]!, message: \"could not be found\" });\n else if (invalid) failures.push({ tool: \"vulture\", kind: \"file-not-checked\", file: invalid[1]!, message: invalid[3]! });\n else unknown.push(line);\n }\n return [...failures, ...unrecognisedStderr(\"vulture\", unknown)];\n}\n\nexport function parseVultureOutput({ stdout, stderr }: ToolOutput): ParsedOutput {\n return { diagnostics: parseStdout(stdout), failures: parseStderr(stderr) };\n}\n\n/** Dead-code candidates from vulture at `minConfidence` (default 60); rule ids are vulture/<kind>. */\nexport function runVultureAudit(files: string[], options?: VultureOptions): Promise<RunnerResult> {\n const args = [\"--min-confidence\", String(options?.minConfidence ?? 60), ...files];\n return runPythonTool(VULTURE, args, options, parseVultureOutput);\n}\n","import { runPythonTool } from \"./python-tool.js\";\nimport type { AuditOptions, ParsedOutput, PythonToolSpec, ToolOutput } from \"./python-tool.js\";\nimport type { CheckDiagnostic, ToolFailure } from \"../orchestrator/types.js\";\nimport type { RunnerResult } from \"./types.js\";\n\nconst PYDOCLINT: PythonToolSpec = {\n tool: \"pydoclint\",\n command: \"pydoclint\",\n install: \"pip install pydoclint\",\n expect: { successCodes: new Set([0, 1]), allowEmptyStdout: true },\n};\n\nconst VIOLATION = /^\\s+(\\d+): (DOC\\d+): (.*)$/;\n// pydoclint reports a file it cannot parse as a DOC002 violation at line 0.\nconst SYNTAX_ERROR = \"DOC002\";\n\nexport type DocstringStyle = \"numpy\" | \"google\" | \"sphinx\";\n\nexport interface PydoclintOptions extends AuditOptions {\n style?: DocstringStyle;\n}\n\nfunction toDiagnostic(file: string, m: RegExpExecArray): CheckDiagnostic {\n return {\n file,\n line: Math.max(1, Number(m[1])),\n column: 1,\n severity: \"warn\",\n message: m[3]!,\n category: \"documentation\",\n rule: `pydoclint/${m[2]}`,\n fixable: false,\n };\n}\n\n/** pydoclint prints each file's path unindented, then its violations indented beneath it, all on stderr. */\nexport function parsePydoclintOutput({ stdout, stderr }: ToolOutput): ParsedOutput {\n const parsed: ParsedOutput = { diagnostics: [], failures: [] };\n let file: string | undefined;\n for (const line of `${stdout}\\n${stderr}`.split(\"\\n\").filter((l) => l.trim())) {\n const m = VIOLATION.exec(line);\n if (!m && /^\\S/.test(line)) file = line.trim();\n else if (!m || !file) throw new Error(`Unrecognised pydoclint line: ${line}`);\n else if (m[2] === SYNTAX_ERROR) parsed.failures.push(notChecked(file, m[3]!));\n else parsed.diagnostics.push(toDiagnostic(file, m));\n }\n return parsed;\n}\n\nfunction notChecked(file: string, message: string): ToolFailure {\n return { tool: \"pydoclint\", kind: \"file-not-checked\", file, message };\n}\n\n/** Docstring-versus-signature drift from pydoclint in `style` (default numpy); rule ids are pydoclint/DOCnnn. */\nexport function runPydoclintAudit(files: string[], options?: PydoclintOptions): Promise<RunnerResult> {\n const args = [\"--style\", options?.style ?? \"numpy\", \"--quiet\", ...files];\n return runPythonTool(PYDOCLINT, args, options, parsePydoclintOutput);\n}\n","import { mkdtempSync, rmSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { AUDIT_PYRIGHT_RULES, generatePyrightAuditConfig } from \"../generators/pyright-audit.js\";\nimport { auditRoot } from \"./paths.js\";\nimport { runPythonTool } from \"./python-tool.js\";\nimport type { AuditOptions, ParsedOutput, PythonToolSpec, ToolOutput } from \"./python-tool.js\";\nimport type { CheckDiagnostic, ToolFailure } from \"../orchestrator/types.js\";\nimport type { RunnerResult } from \"./types.js\";\n\nconst PYRIGHT: PythonToolSpec = {\n tool: \"pyright\",\n command: \"pyright\",\n install: \"pip install pyright\",\n installNote: \"the pip wrapper fetches pyright's npm package on first run and needs Node on PATH; `npm install -g pyright` also works\",\n expect: { successCodes: new Set([0, 1]) },\n};\n\ninterface PyrightDiagnostic {\n file: string;\n severity: string;\n message: string;\n rule?: string;\n range: { start: { line: number; character: number }; end: { line: number } };\n}\n\nexport interface PyrightOptions extends AuditOptions {\n /** The interpreter whose site-packages pyright resolves third-party imports from. */\n pythonPath?: string;\n}\n\nfunction toDiagnostic(d: PyrightDiagnostic & { rule: string }): CheckDiagnostic {\n return {\n file: d.file,\n line: d.range.start.line + 1,\n endLine: d.range.end.line + 1,\n column: d.range.start.character + 1,\n severity: \"warn\",\n message: d.message,\n category: \"redundant-check\",\n rule: `pyright/${d.rule}`,\n fixable: false,\n };\n}\n\n// A diagnostic without a rule is a syntax error; the file is reported once, as ruff's runner does.\nfunction unparsedFiles(entries: PyrightDiagnostic[]): ToolFailure[] {\n const byFile = new Map<string, string[]>();\n for (const e of entries.filter((d) => !d.rule && d.severity === \"error\")) {\n byFile.set(e.file, [...(byFile.get(e.file) ?? []), e.message]);\n }\n return [...byFile].map(([file, messages]) => ({ tool: \"pyright\", kind: \"file-not-checked\", file, message: messages.join(\"; \") }));\n}\n\nexport function parsePyrightOutput({ stdout }: ToolOutput): ParsedOutput {\n const entries = (JSON.parse(stdout) as { generalDiagnostics: PyrightDiagnostic[] }).generalDiagnostics;\n const audited = entries.filter((d): d is PyrightDiagnostic & { rule: string } => AUDIT_PYRIGHT_RULES.includes(d.rule ?? \"\"));\n return { diagnostics: audited.map(toDiagnostic), failures: unparsedFiles(entries) };\n}\n\n/** Redundant isinstance, comparison, contains and cast findings from pyright; rule ids are pyright/<rule>. */\nexport async function runPyrightAudit(files: string[], options?: PyrightOptions): Promise<RunnerResult> {\n const tempDir = mkdtempSync(join(tmpdir(), \"style-checker-pyright-\"));\n try {\n const configPath = join(tempDir, \"pyrightconfig.json\");\n writeFileSync(configPath, JSON.stringify(generatePyrightAuditConfig(auditRoot(options?.cwd))), \"utf-8\");\n const python = options?.pythonPath ? [\"--pythonpath\", options.pythonPath] : [];\n return await runPythonTool(PYRIGHT, [\"--outputjson\", \"-p\", configPath, ...python, ...files], options, parsePyrightOutput);\n } finally {\n rmSync(tempDir, { recursive: true, force: true });\n }\n}\n","import { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { findImportLinterConfig } from \"./import-linter-config.js\";\nimport type { ImportLinterConfig } from \"./import-linter-config.js\";\nimport { parseBrokenChains } from \"./import-linter-output.js\";\nimport type { BrokenChain } from \"./import-linter-output.js\";\nimport { auditRoot } from \"./paths.js\";\nimport { emptyResult, runPythonTool } from \"./python-tool.js\";\nimport type { AuditOptions, ParsedOutput, PythonToolSpec, ToolOutput } from \"./python-tool.js\";\nimport type { CheckDiagnostic } from \"../orchestrator/types.js\";\nimport type { RunnerResult } from \"./types.js\";\n\n// lint-imports exits 1 when a contract is broken.\nconst IMPORT_LINTER: PythonToolSpec = {\n tool: \"import-linter\",\n command: \"lint-imports\",\n install: \"pip install import-linter\",\n expect: { successCodes: new Set([0, 1]) },\n};\n\nexport const NO_IMPORT_LINTER_CONFIG = \"no import-linter config\";\n\nfunction slug(name: string): string {\n return name.toLowerCase().replace(/[^a-z0-9]+/g, \"-\").replace(/^-|-$/g, \"\");\n}\n\n/** The importing module's source file, tried at the root and under src/. */\nexport function moduleFile(root: string, module: string): string | undefined {\n const base = module.replace(/\\./g, \"/\");\n const candidates = [\"\", \"src/\"].flatMap((dir) => [`${dir}${base}.py`, `${dir}${base}/__init__.py`]);\n return candidates.find((c) => existsSync(join(root, c)));\n}\n\nfunction toDiagnostic(chain: BrokenChain, config: ImportLinterConfig, root: string): CheckDiagnostic {\n const first = chain.links[0]!;\n const file = moduleFile(root, first.importer);\n const path = [first.importer, ...chain.links.map((l) => l.imported)].join(\" -> \");\n return {\n file: file ?? chain.contract,\n line: file ? (first.lines[0] ?? 1) : 1,\n column: 1,\n severity: \"error\",\n message: `${chain.summary}: ${path}`,\n category: \"architecture\",\n rule: `import-linter/${config.contractIds.get(chain.contract) ?? slug(chain.contract)}`,\n fixable: false,\n };\n}\n\nfunction parser(config: ImportLinterConfig, root: string): (output: ToolOutput) => ParsedOutput {\n return ({ stdout }) => {\n const chains = parseBrokenChains(stdout);\n if (chains.length === 0 && /\\bBROKEN$/m.test(stdout)) throw new Error(\"lint-imports reported a broken contract but no import chain could be read\");\n return { diagnostics: chains.map((c) => toDiagnostic(c, config, root)), failures: [] };\n };\n}\n\n/** One diagnostic per broken import chain; runs only when the project configures import-linter. */\nexport function runImportLinter(options?: AuditOptions): Promise<RunnerResult> {\n const root = auditRoot(options?.cwd);\n const config = findImportLinterConfig(root);\n if (!config) return Promise.resolve(emptyResult(null, { warnings: [NO_IMPORT_LINTER_CONFIG] }));\n const args = [\"--config\", config.path, \"--no-logo\", \"--no-cache\"];\n return runPythonTool(IMPORT_LINTER, args, options, parser(config, root));\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nexport interface ImportLinterConfig {\n path: string;\n /** Contract name to contract id; lint-imports prints names, the rule id wants the id. */\n contractIds: Map<string, string>;\n}\n\n// import-linter takes the id from the last colon-separated part of an importlinter: section name.\nconst INI_CONTRACT = /^\\[importlinter:(?:.*:)?([^:\\]]+)\\]$/;\n\nfunction iniContractIds(text: string): Map<string, string> {\n const ids = new Map<string, string>();\n let current: string | undefined;\n for (const line of text.split(\"\\n\").map((l) => l.trim())) {\n if (line.startsWith(\"[\")) current = INI_CONTRACT.exec(line)?.[1];\n const name = current && /^name\\s*[=:]\\s*(.+)$/.exec(line);\n if (current && name) ids.set(name[1]!.trim(), current);\n }\n return ids;\n}\n\nfunction tomlValue(block: string, key: string): string | undefined {\n return new RegExp(`^${key}\\\\s*=\\\\s*[\"'](.+?)[\"']\\\\s*$`, \"m\").exec(block)?.[1];\n}\n\nfunction tomlContractIds(text: string): Map<string, string> {\n const ids = new Map<string, string>();\n for (const chunk of text.split(/^\\[\\[tool\\.importlinter\\.contracts\\]\\]\\s*$/m).slice(1)) {\n const block = chunk.split(/^\\[/m)[0]!;\n const name = tomlValue(block, \"name\");\n const id = tomlValue(block, \"id\");\n if (name && id) ids.set(name, id);\n }\n return ids;\n}\n\nfunction readIfContains(path: string, marker: RegExp): string | undefined {\n if (!existsSync(path)) return undefined;\n const text = readFileSync(path, \"utf-8\");\n return marker.test(text) ? text : undefined;\n}\n\n/** The config lint-imports would use, in its own lookup order: setup.cfg, .importlinter, then pyproject.toml. */\nexport function findImportLinterConfig(cwd: string): ImportLinterConfig | undefined {\n for (const name of [\"setup.cfg\", \".importlinter\"]) {\n const ini = readIfContains(join(cwd, name), /^\\[importlinter\\]/m);\n if (ini !== undefined) return { path: join(cwd, name), contractIds: iniContractIds(ini) };\n }\n const toml = readIfContains(join(cwd, \"pyproject.toml\"), /^\\[tool\\.importlinter\\]/m);\n if (toml !== undefined) return { path: join(cwd, \"pyproject.toml\"), contractIds: tomlContractIds(toml) };\n return undefined;\n}\n","/** One import in a chain: `importer` imports `imported` on the given lines. */\nexport interface ImportLink {\n importer: string;\n imported: string;\n lines: number[];\n}\n\n/** One broken import chain, under the contract name and the sentence lint-imports prints above it. */\nexport interface BrokenChain {\n contract: string;\n summary: string;\n links: ImportLink[];\n}\n\nconst LINK = /^(-)?\\s+(\\S+) -> (\\S+) \\(l\\.([\\d, ?]+)\\)$/;\nconst SUMMARY = /^(\\S.* is not allowed to import \\S.*):$/;\n\nfunction isUnderline(line: string | undefined, heading: string): boolean {\n return line !== undefined && /^-+$/.test(line) && line.length === heading.length;\n}\n\nfunction toLink(m: RegExpExecArray): ImportLink {\n const lines = m[4]!.split(\",\").map((n) => Number(n.trim())).filter((n) => Number.isInteger(n) && n > 0);\n return { importer: m[2]!, imported: m[3]!, lines };\n}\n\n/** Reads the \"Broken contracts\" section; a chain starts at a \"-\" line and continues on indented lines. */\nexport function parseBrokenChains(stdout: string): BrokenChain[] {\n const lines = stdout.split(\"\\n\");\n const start = lines.findIndex((l, i) => l === \"Broken contracts\" && isUnderline(lines[i + 1], l));\n if (start < 0) return [];\n const chains: BrokenChain[] = [];\n let contract = \"\";\n let summary = \"\";\n for (let i = start + 2; i < lines.length; i++) {\n const line = lines[i]!;\n const link = LINK.exec(line);\n if (isUnderline(lines[i + 1], line) && line.trim()) contract = line;\n else if (SUMMARY.test(line)) summary = SUMMARY.exec(line)![1]!;\n else if (link?.[1]) chains.push({ contract, summary, links: [toLink(link)] });\n else if (link && chains.length > 0) chains.at(-1)!.links.push(toLink(link));\n }\n return chains;\n}\n","import { readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { emptyResult } from \"./python-tool.js\";\nimport { auditRoot, relativeTo } from \"./paths.js\";\nimport type { CheckDiagnostic, ToolFailure } from \"../orchestrator/types.js\";\nimport type { RunnerResult } from \"./types.js\";\n\n// A text scan, not a tokenizer: a marker inside a string literal counts too.\nconst NOQA = /#\\s*noqa\\b(?::\\s*([A-Z]+\\d+(?:[\\s,]+[A-Z]+\\d+)*))?/gi;\nconst TYPE_IGNORE = /#\\s*type:\\s*ignore\\b(?:\\[([^\\]]*)\\])?/g;\n\nexport const NOQA_RULE = \"suppression/noqa\";\nexport const TYPE_IGNORE_RULE = \"suppression/type-ignore\";\n\nfunction codesOf(raw: string | undefined): string[] {\n return raw ? raw.split(/[\\s,]+/).filter(Boolean) : [];\n}\n\nfunction marker(file: string, line: number, m: RegExpMatchArray, rule: string, label: string): CheckDiagnostic {\n const codes = codesOf(m[1]);\n const message = codes.length ? `${label} suppresses ${codes.join(\", \")}` : `blanket ${label} suppresses every check on this line`;\n return { file, line, column: (m.index ?? 0) + 1, severity: \"warn\", message, category: \"suppression\", rule, fixable: false };\n}\n\n/** Every `# noqa` and `# type: ignore` marker in one file's text, one diagnostic per occurrence. */\nexport function findSuppressions(file: string, text: string): CheckDiagnostic[] {\n return text.split(\"\\n\").flatMap((content, i) => [\n ...[...content.matchAll(NOQA)].map((m) => marker(file, i + 1, m, NOQA_RULE, \"noqa\")),\n ...[...content.matchAll(TYPE_IGNORE)].map((m) => marker(file, i + 1, m, TYPE_IGNORE_RULE, \"type: ignore\")),\n ]);\n}\n\n/** Suppression markers across `files`, read from disk relative to `cwd`; no tool is run. */\nexport function countSuppressions(files: string[], options?: { cwd?: string }): RunnerResult {\n const root = auditRoot(options?.cwd);\n const diagnostics: CheckDiagnostic[] = [];\n const failures: ToolFailure[] = [];\n for (const file of files) {\n const shown = relativeTo(root, resolve(root, file));\n try {\n diagnostics.push(...findSuppressions(shown, readFileSync(resolve(root, file), \"utf-8\")));\n } catch (err) {\n failures.push({ tool: \"suppressions\", kind: \"file-not-checked\", file: shown, message: (err as Error).message });\n }\n }\n return emptyResult(0, { diagnostics, failures });\n}\n\nexport interface SuppressionCounts {\n noqa: number;\n typeIgnore: number;\n}\n\nexport interface SuppressionTotals extends SuppressionCounts {\n byFile: Record<string, SuppressionCounts>;\n}\n\n/** Totals of countSuppressions' diagnostics, overall and per file; other diagnostics are ignored. */\nexport function suppressionTotals(diagnostics: CheckDiagnostic[]): SuppressionTotals {\n const totals: SuppressionTotals = { noqa: 0, typeIgnore: 0, byFile: {} };\n for (const d of diagnostics) {\n const key = d.rule === NOQA_RULE ? \"noqa\" : d.rule === TYPE_IGNORE_RULE ? \"typeIgnore\" : undefined;\n if (!key) continue;\n const file = (totals.byFile[d.file] ??= { noqa: 0, typeIgnore: 0 });\n file[key] += 1;\n totals[key] += 1;\n }\n return totals;\n}\n","import { PROFILE_CATEGORIES } from \"@titan-design/style-profile\";\nimport type { Profile, ProfileCategory } from \"@titan-design/style-profile\";\nimport type { Observation } from \"@titan-design/style-analyzer\";\n\nexport interface Deviation {\n file: string;\n line: number;\n rule: string;\n expected: string;\n found: string;\n severity: \"error\" | \"warn\" | \"info\";\n}\n\nexport interface DiffResult {\n deviations: Deviation[];\n summary: {\n total: number;\n matching: number;\n deviating: number;\n };\n}\n\nfunction getSeverity(\n confidence: number,\n thresholds: Profile[\"severityThresholds\"],\n): \"error\" | \"warn\" | \"info\" {\n if (confidence >= thresholds.error) return \"error\";\n if (confidence >= thresholds.warn) return \"warn\";\n return \"info\";\n}\n\nfunction resolveProfileRule(\n profile: Profile,\n observationType: string,\n): { convention: unknown; confidence: number } | undefined {\n const [category, rule] = observationType.split(\".\");\n if (!PROFILE_CATEGORIES.includes(category as ProfileCategory)) return undefined;\n const section = profile[category as ProfileCategory];\n if (!section || typeof section !== \"object\") return undefined;\n const ruleObj = rule === undefined ? undefined : section[rule];\n if (!ruleObj || typeof ruleObj !== \"object\") return undefined;\n if (ruleObj.convention === undefined || ruleObj.confidence === undefined) {\n return undefined;\n }\n return { convention: ruleObj.convention, confidence: ruleObj.confidence };\n}\n\nexport function diffAgainstProfile(\n profile: Profile,\n observations: Observation[],\n): DiffResult {\n const deviations: Deviation[] = [];\n let matching = 0;\n\n for (const obs of observations) {\n const profileRule = resolveProfileRule(profile, obs.type);\n if (!profileRule) continue;\n\n const expected = String(profileRule.convention);\n const found = String(obs.value);\n\n if (found === expected) {\n matching++;\n } else {\n deviations.push({\n file: obs.file,\n line: obs.line,\n rule: obs.type,\n expected,\n found,\n severity: getSeverity(\n profileRule.confidence,\n profile.severityThresholds,\n ),\n });\n }\n }\n\n return {\n deviations,\n summary: {\n total: observations.length,\n matching,\n deviating: deviations.length,\n },\n };\n}\n"],"mappings":";AACA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AASP,SAAS,mBAAmB,OAAyB;AACnD,MAAI,MAAM,QAAQ,KAAK,KAAK,MAAM,CAAC,MAAM,OAAQ,QAAO,CAAC,QAAQ,GAAG,MAAM,MAAM,CAAC,CAAC;AAClF,SAAO,UAAU,SAAS,SAAS;AACrC;AAEA,SAAS,aAAa,SAA2C;AAC/D,QAAM,QAAiC,CAAC;AAExC,QAAM,aAAa,0BAA0B,OAAO;AACpD,MAAI,WAAY,OAAM,WAAW,CAAC,CAAC,IAAI,WAAW,CAAC;AAEnD,QAAM,aAAa,qBAAqB,OAAO;AAC/C,MAAI,WAAY,OAAM,WAAW,CAAC,CAAC,IAAI,WAAW,CAAC;AAEnD,QAAM,eAAe,wBAAwB,OAAO;AACpD,MAAI,aAAc,OAAM,aAAa,CAAC,CAAC,IAAI,aAAa,CAAC;AAEzD,QAAM,iBAAiB,oBAAoB,OAAO;AAClD,MAAI,eAAgB,OAAM,eAAe,CAAC,CAAC,IAAI,eAAe,CAAC;AAE/D,QAAM,aAAa,gBAAgB,OAAO;AAC1C,aAAW,CAAC,MAAM,KAAK,KAAK,YAAY;AACtC,UAAM,IAAI,IAAI;AAAA,EAChB;AAEA,aAAW,QAAQ,OAAO,KAAK,KAAK,EAAG,OAAM,IAAI,IAAI,mBAAmB,MAAM,IAAI,CAAC;AACnF,SAAO;AACT;AAEO,SAAS,qBAAqB,SAA2C;AAC9E,QAAM,UAAmC,CAAC;AAC1C,QAAM,QAAQ,aAAa,OAAO;AAElC,MAAI,OAAO,KAAK,KAAK,EAAE,SAAS,GAAG;AACjC,YAAQ,KAAK;AAAA,MACX,OAAO,CAAC,WAAW,UAAU;AAAA,MAC7B;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO;AACT;;;ACpCA,SAAS,UAAU,SAAkB,MAAsB;AACzD,MAAI,QAAQ,QAAQ;AAClB,UAAM,iBAAiB,OAAO,OAAO,QAAQ,MAAM,EAAE;AAAA,MACnD,CAAC,SAAS,KAAK,cAAc,QAAQ,mBAAmB;AAAA,IAC1D;AACA,QAAI,eAAgB,MAAK,OAAO,KAAK,GAAG;AAAA,EAC1C;AACF;AAEA,SAAS,eAAe,SAAkB,MAAsB;AAC9D,QAAM,cAAc,QAAQ,WAAW;AACvC,MAAI,eAAe,YAAY,cAAc,QAAQ,mBAAmB,MAAM;AAC5E,SAAK,OAAO,KAAK,GAAG;AACpB,QAAI,MAAM,QAAQ,YAAY,UAAU,GAAG;AACzC,WAAK,QAAQ;AAAA,QACX,iBAAiB,YAAY;AAAA,MAC/B;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,gBAAgB,SAAkB,MAAsB;AAC/D,QAAM,eAAe,QAAQ,eAAe;AAC5C,MAAI,gBAAgB,aAAa,cAAc,QAAQ,mBAAmB,MAAM;AAC9E,SAAK,OAAO,KAAK,GAAG;AACpB,UAAM,OAAO,aAAa;AAC1B,QACE,OAAO,SAAS,YAChB,SAAS,qBACT,SAAS,aACT;AACA,WAAK,aAAa,EAAE,YAAY,KAAK;AAAA,IACvC;AAAA,EACF;AACF;AAEA,SAAS,cAAc,SAAkB,MAAsB;AAC7D,QAAM,WAAW,QAAQ,WAAW;AACpC,MACE,YACA,SAAS,cAAc,QAAQ,mBAAmB,QAClD,OAAO,SAAS,eAAe,UAC/B;AACA,SAAK,OAAO,KAAK,KAAK;AACtB,SAAK,SAAS,EAAE,kBAAkB,SAAS,WAAW;AAAA,EACxD;AACF;AAEO,SAAS,mBAAmB,SAA8B;AAC/D,QAAM,OAAiB,EAAE,QAAQ,CAAC,EAAE;AACpC,QAAM,SAAqB,EAAE,KAAK;AAElC,YAAU,SAAS,IAAI;AACvB,iBAAe,SAAS,IAAI;AAC5B,kBAAgB,SAAS,IAAI;AAC7B,gBAAc,SAAS,IAAI;AAE3B,QAAM,aAAa,QAAQ,YAAY;AACvC,MAAI,cAAc,OAAO,WAAW,eAAe,UAAU;AAC3D,WAAO,aAAa,IAAI,WAAW;AAAA,EACrC;AAEA,SAAO;AACT;;;ACpFA,SAAS,eAAe,aAAa,cAAc;AACnD,SAAS,QAAAA,aAAY;AACrB,SAAS,cAAc;;;ACFvB,SAAS,aAAa;AAWf,SAAS,QACd,SACA,MACA,SACwB;AACxB,SAAO,IAAI,QAAQ,CAACC,UAAS,WAAW;AACtC,UAAM,QAAQ,MAAM,SAAS,MAAM;AAAA,MACjC,KAAK,SAAS;AAAA,MACd,OAAO,CAAC,UAAU,QAAQ,MAAM;AAAA,IAClC,CAAC;AAED,UAAM,SAAmB,CAAC;AAC1B,UAAM,YAAsB,CAAC;AAC7B,QAAI,WAAW;AAEf,UAAM,QAAQ,WAAW,MAAM;AAC7B,iBAAW;AACX,YAAM,KAAK,SAAS;AAAA,IACtB,GAAG,SAAS,WAAW,GAAM;AAE7B,UAAM,OAAO,GAAG,QAAQ,CAAC,UAAkB,OAAO,KAAK,KAAK,CAAC;AAC7D,UAAM,OAAO,GAAG,QAAQ,CAAC,UAAkB,UAAU,KAAK,KAAK,CAAC;AAEhE,UAAM,GAAG,SAAS,CAAC,QAAQ;AACzB,mBAAa,KAAK;AAClB,aAAO,IAAI,MAAM,mBAAmB,OAAO,KAAK,IAAI,OAAO,EAAE,CAAC;AAAA,IAChE,CAAC;AAED,UAAM,GAAG,SAAS,CAAC,MAAM,WAAW;AAClC,mBAAa,KAAK;AAClB,MAAAA,SAAQ;AAAA,QACN,QAAQ,OAAO,OAAO,MAAM,EAAE,SAAS,OAAO;AAAA,QAC9C,QAAQ,OAAO,OAAO,SAAS,EAAE,SAAS,OAAO;AAAA,QACjD,UAAU;AAAA,QACV;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH,CAAC;AACH;;;AC7CA,IAAM,qBAAqB,oBAAI,IAAI,CAAC,GAAG,CAAC,CAAC;AAMzC,SAAS,QAAQ,MAAsB;AACrC,QAAM,UAAU,KAAK,KAAK;AAC1B,SAAO,QAAQ,SAAS,MAAM,GAAG,QAAQ,MAAM,GAAG,GAAG,CAAC,WAAM;AAC9D;AAEA,SAAS,WAAW,SAAiB,QAAwB;AAC3D,QAAM,SAAS,QAAQ,MAAM;AAC7B,SAAO,SAAS,GAAG,OAAO,KAAK,MAAM,KAAK;AAC5C;AAQO,SAAS,YAAY,MAAgB,KAAoB,SAA0B,CAAC,GAAY;AACrG,QAAM,OAAO,CAAC,MAA2B,aAA8B;AAAA,IACrE,IAAI;AAAA,IACJ,SAAS,EAAE,MAAM,MAAM,SAAS,WAAW,SAAS,IAAI,MAAM,EAAE;AAAA,IAChE,UAAU,IAAI;AAAA,EAChB;AACA,MAAI,IAAI,SAAU,QAAO,KAAK,WAAW,GAAG,IAAI,2BAA2B;AAC3E,MAAI,IAAI,aAAa,MAAM;AACzB,WAAO,KAAK,UAAU,GAAG,IAAI,kBAAkB,IAAI,UAAU,UAAU,EAAE;AAAA,EAC3E;AACA,MAAI,EAAE,OAAO,gBAAgB,oBAAoB,IAAI,IAAI,QAAQ,GAAG;AAClE,WAAO,KAAK,aAAa,GAAG,IAAI,qBAAqB,IAAI,QAAQ,EAAE;AAAA,EACrE;AACA,MAAI,CAAC,OAAO,oBAAoB,CAAC,IAAI,OAAO,KAAK,GAAG;AAClD,WAAO,KAAK,sBAAsB,GAAG,IAAI,WAAW,IAAI,QAAQ,iBAAiB;AAAA,EACnF;AACA,SAAO,EAAE,IAAI,MAAM,QAAQ,IAAI,QAAQ,QAAQ,IAAI,QAAQ,UAAU,IAAI,SAAS;AACpF;AAEA,eAAsB,eACpB,MACA,SACA,MACA,SACA,QACkB;AAClB,MAAI;AACF,WAAO,YAAY,MAAM,MAAM,QAAQ,SAAS,MAAM,OAAO,GAAG,MAAM;AAAA,EACxE,SAAS,KAAK;AACZ,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,WAAO,EAAE,IAAI,OAAO,SAAS,EAAE,MAAM,MAAM,gBAAgB,QAAQ,GAAG,UAAU,KAAK;AAAA,EACvF;AACF;AAEO,SAAS,YACd,MACA,OAC8D;AAC9D,MAAI;AACF,WAAO,EAAE,IAAI,MAAM,OAAO,MAAM,EAAE;AAAA,EACpC,SAAS,KAAK;AACZ,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,WAAO,EAAE,IAAI,OAAO,SAAS,EAAE,MAAM,MAAM,sBAAsB,QAAQ,EAAE;AAAA,EAC7E;AACF;;;ACvEA,SAAS,qBAAqB;AAC9B,SAAS,YAAY;AACrB,SAAS,qBAAqB;AAavB,IAAM,iBAAiD;AAAA,EAC5D,sBAAsB;AAAA,IACpB,EAAE,WAAW,mCAAmC;AAAA,IAChD,EAAE,WAAW,qBAAqB,QAAQ,SAAS;AAAA,EACrD;AAAA,EACA,eAAe,CAAC,EAAE,WAAW,8BAA8B,CAAC;AAAA,EAC5D,SAAS,CAAC,EAAE,WAAW,wBAAwB,CAAC;AAAA,EAChD,OAAO,CAAC,EAAE,WAAW,sBAAsB,CAAC;AAC9C;AAEO,IAAM,iBAAiC;AAAA,EAC5C,EAAE,WAAW,4BAA4B;AAAA,EACzC,EAAE,WAAW,qBAAqB,QAAQ,SAAS;AACrD;AAEO,SAAS,gBAAgB,MAA6B;AAC3D,QAAM,QAAQ,KAAK,YAAY,GAAG;AAClC,SAAO,UAAU,KAAK,OAAO,KAAK,MAAM,GAAG,KAAK;AAClD;AAEO,SAAS,gBAAgB,SAAiC;AAC/D,SAAO,QAAQ,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,MAAM;AACpD;AAEO,SAAS,cACd,SACA,SACuB;AAEvB,QAAMC,WAAU,cAAc,KAAK,SAAS,cAAc,CAAC;AAC3D,aAAW,UAAU,SAAS;AAC5B,QAAI;AACF,YAAM,MAAM,cAAcA,SAAQ,QAAQ,OAAO,SAAS,CAAC,EAAE;AAC7D,aAAO,EAAE,KAAK,QAAQ,OAAO,OAAO;AAAA,IACtC,QAAQ;AACN;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;;;AChCA,SAAS,WAAW,KAAoB,KAA6B;AACnE,MAAI,KAAK,IAAI,IAAI,IAAI,IAAI,GAAG;AAC5B,MAAI,CAAC,IAAI;AACP,SAAK,IAAI,IAAI,IAAI,IAAI;AACrB,QAAI,IAAI,IAAI,IAAI,KAAK,EAAE;AACvB,QAAI,QAAQ,KAAK,UAAU,EAAE,SAAS,KAAK,UAAU,IAAI,GAAG,CAAC,GAAG;AAAA,EAClE;AACA,SAAO,IAAI,SAAS,GAAG,EAAE,IAAI,IAAI,MAAM,KAAK;AAC9C;AAEA,SAAS,cAAc,KAAoB,WAA0C;AACnF,MAAI,CAAC,IAAI,QAAQ,IAAI,SAAS,GAAG;AAC/B,UAAM,UAAU,eAAe,SAAS;AACxC,QAAI,QAAQ,IAAI,WAAW,UAAU,cAAc,SAAS,IAAI,OAAO,IAAI,IAAI;AAAA,EACjF;AACA,SAAO,IAAI,QAAQ,IAAI,SAAS,KAAK;AACvC;AAEA,SAAS,WAAW,KAAoB,WAA2B;AACjE,QAAM,UAAU,eAAe,SAAS;AACxC,MAAI,CAAC,QAAS,QAAO,gDAAgD,SAAS;AAC9E,SAAO,GAAG,gBAAgB,OAAO,CAAC,wBAAwB,IAAI,OAAO;AACvE;AAEA,SAAS,YACP,KACA,OACA,YACA,SACqC;AACrC,QAAM,QAAiC,CAAC;AACxC,QAAM,UAAoB,CAAC;AAC3B,aAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,MAAM,SAAS,CAAC,CAAC,GAAG;AAC7D,UAAM,YAAY,gBAAgB,IAAI;AACtC,UAAM,SAAS,cAAc,OAAO,OAAO,cAAc,KAAK,SAAS;AACvE,QAAI,cAAc,QAAQ,CAAC,QAAQ;AACjC,cAAQ,KAAK,EAAE,MAAM,UAAU,MAAM,QAAQ,WAAW,QAAQ,WAAW,KAAK,SAAS,EAAE,CAAC;AAC5F;AAAA,IACF;AACA,UAAM,IAAI,IAAI;AACd,QAAI,cAAc,QAAQ,OAAQ,SAAQ,KAAK,GAAG,KAAK,UAAU,SAAS,CAAC,KAAK,WAAW,KAAK,MAAM,CAAC,EAAE;AAAA,EAC3G;AACA,QAAM,OAAO,KAAK,UAAU,EAAE,OAAO,MAAM,OAAO,MAAM,CAAC;AACzD,QAAM,OAAO,QAAQ,IAAI,gCAAgC,UAAU,kBAAkB,CAAC,GAAG,IAAI,IAAI,OAAO,CAAC,EAAE,KAAK,IAAI,CAAC;AACrH,SAAO,EAAE,MAAM,WAAW,OAAO,KAAK,KAAK,EAAE,OAAO;AACtD;AAEO,SAAS,wBACd,QACA,SACoB;AACpB,QAAMC,UAAS,cAAc,gBAAgB,OAAO;AACpD,MAAI,CAACA,SAAQ;AACX,UAAM,UAAU,GAAG,gBAAgB,cAAc,CAAC,wBAAwB,OAAO;AACjF,WAAO,EAAE,IAAI,OAAO,SAAS,EAAE,MAAM,UAAU,MAAM,sBAAsB,QAAQ,EAAE;AAAA,EACvF;AACA,QAAM,MAAqB,EAAE,SAAS,SAAS,CAAC,GAAG,KAAK,oBAAI,IAAI,GAAG,SAAS,oBAAI,IAAI,EAAE;AACtF,QAAM,aAAa,WAAW,KAAKA,OAAM;AACzC,QAAM,eAA8B,CAAC;AACrC,QAAM,UAAU,OAAO,IAAI,CAAC,UAAU,YAAY,KAAK,OAAO,YAAY,YAAY,CAAC;AACvF,QAAM,YAAY,QAAQ,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,WAAW,CAAC;AACjE,QAAM,OAAO,QAAQ,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,OAAO;AACpD,QAAM,SAAS,GAAG,IAAI,QAAQ,KAAK,IAAI,CAAC;AAAA;AAAA;AAAA,IAA2B,IAAI;AAAA;AAAA;AACvE,SAAO,EAAE,IAAI,MAAM,QAAQ,WAAW,aAAa;AACrD;;;ACpFA,IAAM,qBAA6C;AAAA,EACjD,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AACL;AAEA,IAAM,uBAA+C;AAAA,EACnD,wCAAwC;AAAA,EACxC,yBAAyB;AAAA,EACzB,8BAA8B;AAAA,EAC9B,gBAAgB;AAAA,EAChB,0BAA0B;AAAA,EAC1B,aAAa;AAAA,EACb,uBAAuB;AACzB;AAEA,SAAS,yBAAyB,gBAA0C;AAC1E,SAAO,mBAAmB,IAAI,UAAU;AAC1C;AAEA,SAAS,qBAAqB,QAAwB;AACpD,MAAI,qBAAqB,MAAM,EAAG,QAAO,qBAAqB,MAAM;AACpE,MAAI,OAAO,WAAW,qBAAqB,EAAG,QAAO;AACrD,MAAI,OAAO,WAAW,QAAQ,EAAG,QAAO;AACxC,MAAI,OAAO,WAAW,UAAU,EAAG,QAAO;AAC1C,MAAI,OAAO,WAAW,SAAS,EAAG,QAAO;AACzC,SAAO;AACT;AAEA,SAAS,mBAAmB,MAAsB;AAChD,QAAM,SAAS,KAAK,QAAQ,UAAU,EAAE;AACxC,SAAO,mBAAmB,MAAM,KAAK;AACvC;AA6BO,SAAS,sBAAsB,SAAoC;AACxE,MAAI;AACJ,MAAI;AACF,cAAU,KAAK,MAAM,OAAO;AAAA,EAC9B,QAAQ;AACN,UAAM,IAAI;AAAA,MACR;AAAA,EAAoD,QAAQ,MAAM,GAAG,GAAG,CAAC;AAAA,IAC3E;AAAA,EACF;AACA,QAAM,cAAiC,CAAC;AAExC,aAAW,SAAS,SAAS;AAC3B,eAAW,OAAO,MAAM,UAAU;AAChC,UAAI,CAAC,IAAI,OAAQ;AACjB,kBAAY,KAAK;AAAA,QACf,MAAM,MAAM;AAAA,QACZ,MAAM,IAAI;AAAA,QACV,QAAQ,IAAI;AAAA,QACZ,UAAU,yBAAyB,IAAI,QAAQ;AAAA,QAC/C,SAAS,IAAI;AAAA,QACb,UAAU,qBAAqB,IAAI,MAAM;AAAA,QACzC,MAAM,IAAI;AAAA,QACV,SAAS,IAAI,OAAO;AAAA,QACpB,KAAK,IAAI,OAAO;AAAA,MAClB,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;AAGO,SAAS,kBAAkB,MAAsD;AACtF,SAAO,SAAS,QAAQ,SAAS;AACnC;AAEO,SAAS,oBAAoB,SAAoC;AACtE,MAAI;AACJ,MAAI;AACF,cAAU,KAAK,MAAM,OAAO;AAAA,EAC9B,QAAQ;AACN,UAAM,IAAI;AAAA,MACR;AAAA,EAAkD,QAAQ,MAAM,GAAG,GAAG,CAAC;AAAA,IACzE;AAAA,EACF;AACA,SAAO,QAAQ,QAAQ,CAAC,UAAU,kBAAkB,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC;AAAA,IACtE,MAAM,MAAM;AAAA,IACZ,MAAM,MAAM,SAAS;AAAA,IACrB,SAAS,MAAM,aAAa;AAAA,IAC5B,QAAQ,MAAM,SAAS;AAAA,IACvB,UAAU;AAAA,IACV,SAAS,MAAM;AAAA,IACf,UAAU,mBAAmB,MAAM,IAAI;AAAA,IACvC,MAAM,MAAM;AAAA,IACZ,SAAS,MAAM,OAAO;AAAA,EACxB,CAAC,CAAC;AACJ;AAEO,SAAS,iBAAiB,GAA4B;AAC3D,SAAO,GAAG,EAAE,IAAI,IAAI,EAAE,IAAI,IAAI,EAAE,MAAM,IAAI,EAAE,QAAQ,IAAI,EAAE,OAAO,KAAK,EAAE,QAAQ,IAAI,EAAE,IAAI;AAC5F;;;AL9GA,SAAS,eAAe,QAA+B;AACrD,QAAM,UAAU,KAAK,MAAM,MAAM;AACjC,SAAO,QAAQ;AAAA,IAAQ,CAAC,UACtB,MAAM,SACH,OAAO,CAAC,MAAM,EAAE,WAAW,SAAS,EAAE,UAAU,QAAQ,EAAE,QAAQ,WAAW,cAAc,EAAE,EAC7F,IAAI,CAAC,OAAO,EAAE,MAAM,UAAmB,MAAM,oBAA6B,MAAM,MAAM,UAAU,SAAS,EAAE,QAAQ,EAAE;AAAA,EAC1H;AACF;AAEA,SAAS,iBAAiB,QAA6E;AACrG,QAAM,SAAS,YAAY,UAAU,OAAO;AAAA,IAC1C,aAAa,sBAAsB,MAAM;AAAA,IACzC,UAAU,eAAe,MAAM;AAAA,EACjC,EAAE;AACF,SAAO,OAAO,KAAK,OAAO,QAAQ,EAAE,aAAa,CAAC,GAAG,UAAU,CAAC,OAAO,OAAO,EAAE;AAClF;AAEA,SAAS,WAAW,YAAoB,OAAiB,KAAyB;AAEhF,SAAO,CAAC,QAAQ,MAAM,UAAU,YAAY,YAAY,YAAY,QAAQ,GAAI,MAAM,CAAC,OAAO,IAAI,CAAC,GAAI,GAAG,KAAK;AACjH;AAEA,eAAsB,UACpB,QACA,OACA,SACuB;AACvB,QAAM,MAAM,SAAS,OAAO,QAAQ,IAAI;AACxC,QAAM,QAAQ,wBAAwB,QAAQ,GAAG;AACjD,MAAI,CAAC,MAAM,GAAI,QAAO,EAAE,aAAa,CAAC,GAAG,UAAU,MAAM,UAAU,CAAC,MAAM,OAAO,GAAG,cAAc,CAAC,EAAE;AACrG,QAAM,EAAE,aAAa,IAAI;AACzB,MAAI,MAAM,cAAc,EAAG,QAAO,EAAE,aAAa,CAAC,GAAG,UAAU,MAAM,UAAU,CAAC,GAAG,aAAa;AAEhG,QAAM,UAAU,YAAYC,MAAK,OAAO,GAAG,mBAAmB,CAAC;AAC/D,QAAM,aAAaA,MAAK,SAAS,mBAAmB;AACpD,MAAI;AACF,kBAAc,YAAY,MAAM,QAAQ,OAAO;AAC/C,UAAM,MAAM,MAAM,eAAe,UAAU,OAAO,WAAW,YAAY,OAAO,SAAS,GAAG,GAAG;AAAA,MAC7F;AAAA,MACA,SAAS,SAAS;AAAA,IACpB,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,QAAO,EAAE,aAAa,CAAC,GAAG,UAAU,IAAI,UAAU,UAAU,CAAC,IAAI,OAAO,GAAG,aAAa;AACrG,WAAO,EAAE,GAAG,iBAAiB,IAAI,MAAM,GAAG,UAAU,IAAI,UAAU,aAAa;AAAA,EACjF,UAAE;AACA,WAAO,SAAS,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,EAClD;AACF;;;AM/DA,SAAS,iBAAAC,gBAAe,eAAAC,cAAa,UAAAC,eAAc;AACnD,SAAS,QAAAC,aAAY;AACrB,SAAS,UAAAC,eAAc;AASvB,SAAS,UAAU,QAA0B;AAC3C,SAAO,IAAI,OAAO,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,EAAE,KAAK,IAAI,CAAC;AACnD;AAEA,SAAS,mBAAmB,MAA0B;AACpD,QAAM,QAAkB,CAAC;AACzB,MAAI,KAAK,OAAQ,OAAM,KAAK,YAAY,UAAU,KAAK,MAAM,CAAC,EAAE;AAChE,MAAI,KAAK,OAAQ,OAAM,KAAK,YAAY,UAAU,KAAK,MAAM,CAAC,EAAE;AAChE,SAAO;AACT;AAEA,SAAS,oBAAoB,MAA0B;AACrD,QAAM,QAAkB,CAAC;AACzB,MAAI,KAAK,QAAQ;AACf,UAAM,KAAK,eAAe;AAC1B,QAAI,KAAK,OAAO,gBAAgB,GAAG;AACjC,YAAM,KAAK,oBAAoB,KAAK,OAAO,gBAAgB,CAAC,EAAE;AAAA,IAChE;AAAA,EACF;AACA,MAAI,KAAK,YAAY;AACnB,UAAM,KAAK,mBAAmB;AAC9B,QAAI,KAAK,WAAW,YAAY;AAC9B,YAAM,KAAK,iBAAiB,KAAK,WAAW,UAAU,GAAG;AAAA,IAC3D;AAAA,EACF;AACA,MAAI,KAAK,OAAO;AACd,UAAM,KAAK,cAAc;AACzB,QAAI,KAAK,MAAM,eAAe,GAAG;AAC/B,YAAM,KAAK,mBAAmB,UAAU,KAAK,MAAM,eAAe,CAAC,CAAC,EAAE;AAAA,IACxE;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,OAAO,QAA4B;AAC1C,QAAM,QAAkB,CAAC;AAEzB,MAAI,OAAO,aAAa,GAAG;AACzB,UAAM,KAAK,iBAAiB,OAAO,aAAa,CAAC,EAAE;AAAA,EACrD;AACA,MAAI,OAAO,QAAS,OAAM,KAAK,gBAAgB;AAE/C,MAAI,OAAO,MAAM;AACf,UAAM,KAAK,UAAU,GAAG,mBAAmB,OAAO,IAAI,GAAG,GAAG,oBAAoB,OAAO,IAAI,CAAC;AAAA,EAC9F;AAEA,SAAO,MAAM,KAAK,IAAI,IAAI;AAC5B;AAQA,SAAS,WAAW,MAAc,SAA8B;AAC9D,SAAO,EAAE,MAAM,QAAQ,MAAM,oBAAoB,MAAM,QAAQ;AACjE;AAGA,SAAS,cAAc,QAA+B;AACpD,QAAM,SAAS,oBAAI,IAAsB;AACzC,aAAW,KAAK,KAAK,MAAM,MAAM,GAAwB;AACvD,QAAI,CAAC,kBAAkB,EAAE,IAAI,EAAG;AAChC,WAAO,IAAI,EAAE,UAAU,CAAC,GAAI,OAAO,IAAI,EAAE,QAAQ,KAAK,CAAC,GAAI,EAAE,OAAO,CAAC;AAAA,EACvE;AACA,SAAO,CAAC,GAAG,MAAM,EAAE,IAAI,CAAC,CAAC,MAAM,QAAQ,MAAM,WAAW,MAAM,SAAS,KAAK,IAAI,CAAC,CAAC;AACpF;AAGA,SAAS,YAAY,QAA+B;AAClD,SAAO,CAAC,GAAG,OAAO,SAAS,yCAAyC,CAAC,EAAE,IAAI,CAAC,MAAM,WAAW,EAAE,CAAC,GAAI,EAAE,CAAC,CAAE,CAAC;AAC5G;AAEA,SAAS,eAAe,QAAgB,QAA6E;AACnH,QAAM,SAAS,YAAY,QAAQ,OAAO;AAAA,IACxC,aAAa,oBAAoB,MAAM;AAAA,IACvC,UAAU,CAAC,GAAG,cAAc,MAAM,GAAG,GAAG,YAAY,MAAM,CAAC;AAAA,EAC7D,EAAE;AACF,SAAO,OAAO,KAAK,OAAO,QAAQ,EAAE,aAAa,CAAC,GAAG,UAAU,CAAC,OAAO,OAAO,EAAE;AAClF;AAEA,eAAsB,QACpB,QACA,OACA,SACuB;AACvB,QAAM,UAAUC,aAAYC,MAAKC,QAAO,GAAG,iBAAiB,CAAC;AAC7D,QAAM,aAAaD,MAAK,SAAS,WAAW;AAE5C,MAAI;AACF,IAAAE,eAAc,YAAY,OAAO,MAAM,GAAG,OAAO;AAEjD,UAAM,OAAO;AAAA,MACX;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAI,SAAS,MAAM,CAAC,OAAO,IAAI,CAAC;AAAA,MAChC,GAAG;AAAA,IACL;AAEA,UAAM,MAAM,MAAM,eAAe,QAAQ,QAAQ,MAAM,EAAE,KAAK,SAAS,KAAK,SAAS,SAAS,QAAQ,CAAC;AACvG,QAAI,CAAC,IAAI,GAAI,QAAO,EAAE,aAAa,CAAC,GAAG,UAAU,IAAI,UAAU,UAAU,CAAC,IAAI,OAAO,GAAG,cAAc,CAAC,EAAE;AACzG,WAAO,EAAE,GAAG,eAAe,IAAI,QAAQ,IAAI,MAAM,GAAG,UAAU,IAAI,UAAU,cAAc,CAAC,EAAE;AAAA,EAC/F,UAAE;AACA,IAAAC,QAAO,SAAS,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,EAClD;AACF;;;AC7GA,SAAS,eAAe,OAAoD;AAC1E,QAAM,UAAU,MAAM,OAAO,CAAC,MAAM,aAAa,KAAK,CAAC,CAAC;AACxD,QAAM,UAAU,MAAM,OAAO,CAAC,MAAM,QAAQ,KAAK,CAAC,CAAC;AACnD,MAAI,QAAQ,SAAS,KAAK,QAAQ,WAAW,EAAG,QAAO;AACvD,MAAI,QAAQ,SAAS,KAAK,QAAQ,WAAW,EAAG,QAAO;AACvD,SAAO;AACT;AAEA,SAAS,aACP,aAC+B;AAC/B,SAAO;AAAA,IACL,OAAO,YAAY;AAAA,IACnB,QAAQ,YAAY,OAAO,CAAC,MAAM,EAAE,aAAa,OAAO,EAAE;AAAA,IAC1D,UAAU,YAAY,OAAO,CAAC,MAAM,EAAE,aAAa,MAAM,EAAE;AAAA,IAC3D,OAAO,YAAY,OAAO,CAAC,MAAM,EAAE,aAAa,MAAM,EAAE;AAAA,IACxD,OAAO;AAAA,EACT;AACF;AAEA,eAAe,SAAS,SAAuD;AAC7E,QAAM,EAAE,SAAS,OAAO,IAAI,IAAI;AAChC,QAAM,WAAW,QAAQ,YAAY,eAAe,KAAK;AACzD,QAAM,UAAU,MAAM,OAAO,CAAC,MAAM,aAAa,KAAK,CAAC,CAAC;AACxD,QAAM,UAAU,MAAM,OAAO,CAAC,MAAM,QAAQ,KAAK,CAAC,CAAC;AACnD,QAAM,UAA0B,CAAC;AAEjC,OAAK,aAAa,gBAAgB,aAAa,YAAY,QAAQ,SAAS,GAAG;AAC7E,UAAM,eAAe,qBAAqB,OAAO;AACjD,QAAI,aAAa,SAAS,EAAG,SAAQ,KAAK,MAAM,UAAU,cAAc,SAAS,EAAE,IAAI,CAAC,CAAC;AAAA,EAC3F;AAEA,OAAK,aAAa,YAAY,aAAa,YAAY,QAAQ,SAAS,GAAG;AACzE,UAAM,aAAa,mBAAmB,OAAO;AAC7C,QAAI,WAAW,MAAM,UAAU,WAAW,KAAK,OAAO,SAAS,GAAG;AAChE,cAAQ,KAAK,MAAM,QAAQ,YAAY,SAAS,EAAE,IAAI,CAAC,CAAC;AAAA,IAC1D;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAsB,YACpB,SAC6B;AAC7B,QAAM,UAAU,MAAM,SAAS,OAAO;AACtC,QAAM,cAAc,QAAQ,QAAQ,CAAC,MAAM,EAAE,WAAW;AACxD,SAAO;AAAA,IACL;AAAA,IACA,UAAU,QAAQ,QAAQ,CAAC,MAAM,EAAE,QAAQ;AAAA,IAC3C,cAAc,QAAQ,QAAQ,CAAC,MAAM,EAAE,YAAY;AAAA,IACnD,SAAS,aAAa,WAAW;AAAA,EACnC;AACF;;;AC5DO,IAAM,mBAAsC;AAAA,EACjD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAGO,SAAS,0BAAsC;AACpD,SAAO,EAAE,SAAS,MAAM,MAAM,EAAE,QAAQ,CAAC,GAAG,gBAAgB,EAAE,EAAE;AAClE;;;AC9BA,SAAS,oBAAoB;AAC7B,SAAS,YAAY,gBAAgB;AAG9B,SAAS,UAAU,KAAsB;AAC9C,SAAO,aAAa,OAAO,QAAQ,IAAI,CAAC;AAC1C;AAEO,SAAS,WAAW,MAAc,MAAsB;AAC7D,SAAO,WAAW,IAAI,IAAI,SAAS,MAAM,IAAI,IAAI;AACnD;;;ACJA,eAAsB,aAAa,OAAiB,SAA6D;AAC/G,QAAM,SAAS,MAAM,QAAQ,wBAAwB,GAAG,OAAO,OAAO;AACtE,QAAM,OAAO,UAAU,SAAS,GAAG;AACnC,QAAM,cAAc,OAAO,YAAY,IAAI,CAAC,OAAO,EAAE,GAAG,GAAG,MAAM,WAAW,MAAM,EAAE,IAAI,EAAE,EAAE;AAC5F,SAAO,EAAE,GAAG,QAAQ,YAAY;AAClC;;;ACVO,IAAM,sBAAyC;AAAA,EACpD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAGA,IAAM,oBAAoB,CAAC,wBAAwB,6BAA6B,yBAAyB;AAKlG,SAAS,2BAA2B,MAA6B;AACtE,QAAM,SAAwB,EAAE,kBAAkB,OAAO,YAAY,CAAC,IAAI,EAAE;AAC5E,aAAW,QAAQ,kBAAmB,QAAO,IAAI,IAAI;AACrD,aAAW,QAAQ,oBAAqB,QAAO,IAAI,IAAI;AACvD,SAAO;AACT;;;ACQO,SAAS,YAAY,UAAyB,QAA+B,CAAC,GAAiB;AACpG,SAAO,EAAE,aAAa,CAAC,GAAG,UAAU,UAAU,CAAC,GAAG,cAAc,CAAC,GAAG,UAAU,CAAC,GAAG,GAAG,MAAM;AAC7F;AAEO,SAAS,mBAAmB,MAA8B;AAC/D,QAAM,OAAO,KAAK,cAAc,KAAK,KAAK,WAAW,MAAM;AAC3D,SAAO,GAAG,KAAK,IAAI,8BAA8B,KAAK,OAAO,KAAK,IAAI;AACxE;AAEA,SAAS,gBAAgB,KAA+C;AACtE,QAAM,EAAE,MAAM,QAAQ,IAAI,IAAI;AAC9B,MAAI,SAAS,kBAAkB,QAAQ,SAAS,QAAQ,EAAG,QAAO;AAClE,SAAO,IAAI,aAAa,OAAO,qBAAqB,KAAK,OAAO;AAClE;AAEA,SAAS,WAAW,MAAc,QAAoC;AACpE,SAAO;AAAA,IACL,aAAa,OAAO,YAAY,IAAI,CAAC,OAAO,EAAE,GAAG,GAAG,MAAM,WAAW,MAAM,EAAE,IAAI,EAAE,EAAE;AAAA,IACrF,UAAU,OAAO,SAAS,IAAI,CAAC,MAAO,EAAE,OAAO,EAAE,GAAG,GAAG,MAAM,WAAW,MAAM,EAAE,IAAI,EAAE,IAAI,CAAE;AAAA,EAC9F;AACF;AAGA,eAAsB,cACpB,MACA,MACA,SACA,OACuB;AACvB,QAAM,aAAa,EAAE,KAAK,SAAS,KAAK,SAAS,SAAS,QAAQ;AAClE,QAAM,MAAM,MAAM,eAAe,KAAK,MAAM,KAAK,SAAS,MAAM,YAAY,KAAK,MAAM;AACvF,MAAI,CAAC,IAAI,MAAM,gBAAgB,GAAG,EAAG,QAAO,YAAY,MAAM,EAAE,UAAU,CAAC,mBAAmB,IAAI,CAAC,EAAE,CAAC;AACtG,MAAI,CAAC,IAAI,GAAI,QAAO,YAAY,IAAI,UAAU,EAAE,UAAU,CAAC,IAAI,OAAO,EAAE,CAAC;AACzE,QAAM,SAAS,YAAY,KAAK,MAAM,MAAM,MAAM,GAAG,CAAC;AACtD,MAAI,CAAC,OAAO,GAAI,QAAO,YAAY,IAAI,UAAU,EAAE,UAAU,CAAC,OAAO,OAAO,EAAE,CAAC;AAC/E,SAAO,YAAY,IAAI,UAAU,WAAW,UAAU,SAAS,GAAG,GAAG,OAAO,KAAK,CAAC;AACpF;AAGO,SAAS,mBAAmB,MAAgB,OAAgC;AACjF,MAAI,MAAM,WAAW,EAAG,QAAO,CAAC;AAChC,QAAM,UAAU,MAAM,KAAK,IAAI;AAC/B,SAAO,CAAC,EAAE,MAAM,MAAM,sBAAsB,SAAS,QAAQ,SAAS,MAAM,GAAG,QAAQ,MAAM,GAAG,GAAG,CAAC,WAAM,QAAQ,CAAC;AACrH;;;AChEA,IAAM,UAA0B;AAAA,EAC9B,MAAM;AAAA,EACN,SAAS;AAAA,EACT,SAAS;AAAA,EACT,QAAQ,EAAE,cAAc,oBAAI,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,kBAAkB,KAAK;AACrE;AAEA,IAAM,UAAU;AAChB,IAAM,gBAAgB;AACtB,IAAM,YAAY;AAQX,SAAS,YAAY,MAAsB;AAChD,QAAM,SAAS,gBAAgB,KAAK,IAAI;AACxC,SAAO,SAAS,UAAU,OAAO,CAAC,CAAC,KAAK;AAC1C;AAEA,SAAS,aAAa,GAAqC;AACzD,QAAM,CAAC,EAAE,MAAM,MAAM,MAAM,UAAU,IAAI;AACzC,SAAO;AAAA,IACL;AAAA,IACA,MAAM,OAAO,IAAI;AAAA,IACjB,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,SAAS,GAAG,IAAI,KAAK,UAAU;AAAA,IAC/B,UAAU;AAAA,IACV,MAAM,WAAW,YAAY,IAAI,CAAC;AAAA,IAClC,SAAS;AAAA,EACX;AACF;AAEA,SAAS,YAAY,QAAmC;AACtD,SAAO,OAAO,MAAM,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,IAAI,CAAC,SAAS;AAC9D,UAAM,IAAI,QAAQ,KAAK,IAAI;AAC3B,QAAI,CAAC,EAAG,OAAM,IAAI,MAAM,8BAA8B,IAAI,EAAE;AAC5D,WAAO,aAAa,CAAC;AAAA,EACvB,CAAC;AACH;AAEA,SAAS,YAAY,QAA+B;AAClD,QAAM,WAA0B,CAAC;AACjC,QAAM,UAAoB,CAAC;AAC3B,aAAW,QAAQ,OAAO,MAAM,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG;AAC7D,UAAM,UAAU,UAAU,KAAK,IAAI;AACnC,UAAM,UAAU,cAAc,KAAK,IAAI;AACvC,QAAI,QAAS,UAAS,KAAK,EAAE,MAAM,WAAW,MAAM,oBAAoB,MAAM,QAAQ,CAAC,GAAI,SAAS,qBAAqB,CAAC;AAAA,aACjH,QAAS,UAAS,KAAK,EAAE,MAAM,WAAW,MAAM,oBAAoB,MAAM,QAAQ,CAAC,GAAI,SAAS,QAAQ,CAAC,EAAG,CAAC;AAAA,QACjH,SAAQ,KAAK,IAAI;AAAA,EACxB;AACA,SAAO,CAAC,GAAG,UAAU,GAAG,mBAAmB,WAAW,OAAO,CAAC;AAChE;AAEO,SAAS,mBAAmB,EAAE,QAAQ,OAAO,GAA6B;AAC/E,SAAO,EAAE,aAAa,YAAY,MAAM,GAAG,UAAU,YAAY,MAAM,EAAE;AAC3E;AAGO,SAAS,gBAAgB,OAAiB,SAAiD;AAChG,QAAM,OAAO,CAAC,oBAAoB,OAAO,SAAS,iBAAiB,EAAE,GAAG,GAAG,KAAK;AAChF,SAAO,cAAc,SAAS,MAAM,SAAS,kBAAkB;AACjE;;;AClEA,IAAM,YAA4B;AAAA,EAChC,MAAM;AAAA,EACN,SAAS;AAAA,EACT,SAAS;AAAA,EACT,QAAQ,EAAE,cAAc,oBAAI,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,kBAAkB,KAAK;AAClE;AAEA,IAAM,YAAY;AAElB,IAAM,eAAe;AAQrB,SAASC,cAAa,MAAc,GAAqC;AACvE,SAAO;AAAA,IACL;AAAA,IACA,MAAM,KAAK,IAAI,GAAG,OAAO,EAAE,CAAC,CAAC,CAAC;AAAA,IAC9B,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,SAAS,EAAE,CAAC;AAAA,IACZ,UAAU;AAAA,IACV,MAAM,aAAa,EAAE,CAAC,CAAC;AAAA,IACvB,SAAS;AAAA,EACX;AACF;AAGO,SAAS,qBAAqB,EAAE,QAAQ,OAAO,GAA6B;AACjF,QAAM,SAAuB,EAAE,aAAa,CAAC,GAAG,UAAU,CAAC,EAAE;AAC7D,MAAI;AACJ,aAAW,QAAQ,GAAG,MAAM;AAAA,EAAK,MAAM,GAAG,MAAM,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG;AAC7E,UAAM,IAAI,UAAU,KAAK,IAAI;AAC7B,QAAI,CAAC,KAAK,MAAM,KAAK,IAAI,EAAG,QAAO,KAAK,KAAK;AAAA,aACpC,CAAC,KAAK,CAAC,KAAM,OAAM,IAAI,MAAM,gCAAgC,IAAI,EAAE;AAAA,aACnE,EAAE,CAAC,MAAM,aAAc,QAAO,SAAS,KAAKC,YAAW,MAAM,EAAE,CAAC,CAAE,CAAC;AAAA,QACvE,QAAO,YAAY,KAAKD,cAAa,MAAM,CAAC,CAAC;AAAA,EACpD;AACA,SAAO;AACT;AAEA,SAASC,YAAW,MAAc,SAA8B;AAC9D,SAAO,EAAE,MAAM,aAAa,MAAM,oBAAoB,MAAM,QAAQ;AACtE;AAGO,SAAS,kBAAkB,OAAiB,SAAmD;AACpG,QAAM,OAAO,CAAC,WAAW,SAAS,SAAS,SAAS,WAAW,GAAG,KAAK;AACvE,SAAO,cAAc,WAAW,MAAM,SAAS,oBAAoB;AACrE;;;ACzDA,SAAS,eAAAC,cAAa,UAAAC,SAAQ,iBAAAC,sBAAqB;AACnD,SAAS,UAAAC,eAAc;AACvB,SAAS,QAAAC,aAAY;AAQrB,IAAM,UAA0B;AAAA,EAC9B,MAAM;AAAA,EACN,SAAS;AAAA,EACT,SAAS;AAAA,EACT,aAAa;AAAA,EACb,QAAQ,EAAE,cAAc,oBAAI,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE;AAC1C;AAeA,SAASC,cAAa,GAA0D;AAC9E,SAAO;AAAA,IACL,MAAM,EAAE;AAAA,IACR,MAAM,EAAE,MAAM,MAAM,OAAO;AAAA,IAC3B,SAAS,EAAE,MAAM,IAAI,OAAO;AAAA,IAC5B,QAAQ,EAAE,MAAM,MAAM,YAAY;AAAA,IAClC,UAAU;AAAA,IACV,SAAS,EAAE;AAAA,IACX,UAAU;AAAA,IACV,MAAM,WAAW,EAAE,IAAI;AAAA,IACvB,SAAS;AAAA,EACX;AACF;AAGA,SAASC,eAAc,SAA6C;AAClE,QAAM,SAAS,oBAAI,IAAsB;AACzC,aAAW,KAAK,QAAQ,OAAO,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,aAAa,OAAO,GAAG;AACxE,WAAO,IAAI,EAAE,MAAM,CAAC,GAAI,OAAO,IAAI,EAAE,IAAI,KAAK,CAAC,GAAI,EAAE,OAAO,CAAC;AAAA,EAC/D;AACA,SAAO,CAAC,GAAG,MAAM,EAAE,IAAI,CAAC,CAAC,MAAM,QAAQ,OAAO,EAAE,MAAM,WAAW,MAAM,oBAAoB,MAAM,SAAS,SAAS,KAAK,IAAI,EAAE,EAAE;AAClI;AAEO,SAAS,mBAAmB,EAAE,OAAO,GAA6B;AACvE,QAAM,UAAW,KAAK,MAAM,MAAM,EAAkD;AACpF,QAAM,UAAU,QAAQ,OAAO,CAAC,MAAiD,oBAAoB,SAAS,EAAE,QAAQ,EAAE,CAAC;AAC3H,SAAO,EAAE,aAAa,QAAQ,IAAID,aAAY,GAAG,UAAUC,eAAc,OAAO,EAAE;AACpF;AAGA,eAAsB,gBAAgB,OAAiB,SAAiD;AACtG,QAAM,UAAUC,aAAYC,MAAKC,QAAO,GAAG,wBAAwB,CAAC;AACpE,MAAI;AACF,UAAM,aAAaD,MAAK,SAAS,oBAAoB;AACrD,IAAAE,eAAc,YAAY,KAAK,UAAU,2BAA2B,UAAU,SAAS,GAAG,CAAC,CAAC,GAAG,OAAO;AACtG,UAAM,SAAS,SAAS,aAAa,CAAC,gBAAgB,QAAQ,UAAU,IAAI,CAAC;AAC7E,WAAO,MAAM,cAAc,SAAS,CAAC,gBAAgB,MAAM,YAAY,GAAG,QAAQ,GAAG,KAAK,GAAG,SAAS,kBAAkB;AAAA,EAC1H,UAAE;AACA,IAAAC,QAAO,SAAS,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,EAClD;AACF;;;ACvEA,SAAS,cAAAC,mBAAkB;AAC3B,SAAS,QAAAC,aAAY;;;ACDrB,SAAS,YAAY,oBAAoB;AACzC,SAAS,QAAAC,aAAY;AASrB,IAAM,eAAe;AAErB,SAAS,eAAe,MAAmC;AACzD,QAAM,MAAM,oBAAI,IAAoB;AACpC,MAAI;AACJ,aAAW,QAAQ,KAAK,MAAM,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG;AACxD,QAAI,KAAK,WAAW,GAAG,EAAG,WAAU,aAAa,KAAK,IAAI,IAAI,CAAC;AAC/D,UAAM,OAAO,WAAW,uBAAuB,KAAK,IAAI;AACxD,QAAI,WAAW,KAAM,KAAI,IAAI,KAAK,CAAC,EAAG,KAAK,GAAG,OAAO;AAAA,EACvD;AACA,SAAO;AACT;AAEA,SAAS,UAAU,OAAe,KAAiC;AACjE,SAAO,IAAI,OAAO,IAAI,GAAG,+BAA+B,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC;AAC9E;AAEA,SAAS,gBAAgB,MAAmC;AAC1D,QAAM,MAAM,oBAAI,IAAoB;AACpC,aAAW,SAAS,KAAK,MAAM,6CAA6C,EAAE,MAAM,CAAC,GAAG;AACtF,UAAM,QAAQ,MAAM,MAAM,MAAM,EAAE,CAAC;AACnC,UAAM,OAAO,UAAU,OAAO,MAAM;AACpC,UAAM,KAAK,UAAU,OAAO,IAAI;AAChC,QAAI,QAAQ,GAAI,KAAI,IAAI,MAAM,EAAE;AAAA,EAClC;AACA,SAAO;AACT;AAEA,SAAS,eAAe,MAAcC,SAAoC;AACxE,MAAI,CAAC,WAAW,IAAI,EAAG,QAAO;AAC9B,QAAM,OAAO,aAAa,MAAM,OAAO;AACvC,SAAOA,QAAO,KAAK,IAAI,IAAI,OAAO;AACpC;AAGO,SAAS,uBAAuB,KAA6C;AAClF,aAAW,QAAQ,CAAC,aAAa,eAAe,GAAG;AACjD,UAAM,MAAM,eAAeD,MAAK,KAAK,IAAI,GAAG,oBAAoB;AAChE,QAAI,QAAQ,OAAW,QAAO,EAAE,MAAMA,MAAK,KAAK,IAAI,GAAG,aAAa,eAAe,GAAG,EAAE;AAAA,EAC1F;AACA,QAAM,OAAO,eAAeA,MAAK,KAAK,gBAAgB,GAAG,0BAA0B;AACnF,MAAI,SAAS,OAAW,QAAO,EAAE,MAAMA,MAAK,KAAK,gBAAgB,GAAG,aAAa,gBAAgB,IAAI,EAAE;AACvG,SAAO;AACT;;;ACvCA,IAAM,OAAO;AACb,IAAM,UAAU;AAEhB,SAAS,YAAY,MAA0B,SAA0B;AACvE,SAAO,SAAS,UAAa,OAAO,KAAK,IAAI,KAAK,KAAK,WAAW,QAAQ;AAC5E;AAEA,SAAS,OAAO,GAAgC;AAC9C,QAAM,QAAQ,EAAE,CAAC,EAAG,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,OAAO,EAAE,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,MAAM,OAAO,UAAU,CAAC,KAAK,IAAI,CAAC;AACtG,SAAO,EAAE,UAAU,EAAE,CAAC,GAAI,UAAU,EAAE,CAAC,GAAI,MAAM;AACnD;AAGO,SAAS,kBAAkB,QAA+B;AAC/D,QAAM,QAAQ,OAAO,MAAM,IAAI;AAC/B,QAAM,QAAQ,MAAM,UAAU,CAAC,GAAG,MAAM,MAAM,sBAAsB,YAAY,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC;AAChG,MAAI,QAAQ,EAAG,QAAO,CAAC;AACvB,QAAM,SAAwB,CAAC;AAC/B,MAAI,WAAW;AACf,MAAI,UAAU;AACd,WAAS,IAAI,QAAQ,GAAG,IAAI,MAAM,QAAQ,KAAK;AAC7C,UAAM,OAAO,MAAM,CAAC;AACpB,UAAM,OAAO,KAAK,KAAK,IAAI;AAC3B,QAAI,YAAY,MAAM,IAAI,CAAC,GAAG,IAAI,KAAK,KAAK,KAAK,EAAG,YAAW;AAAA,aACtD,QAAQ,KAAK,IAAI,EAAG,WAAU,QAAQ,KAAK,IAAI,EAAG,CAAC;AAAA,aACnD,OAAO,CAAC,EAAG,QAAO,KAAK,EAAE,UAAU,SAAS,OAAO,CAAC,OAAO,IAAI,CAAC,EAAE,CAAC;AAAA,aACnE,QAAQ,OAAO,SAAS,EAAG,QAAO,GAAG,EAAE,EAAG,MAAM,KAAK,OAAO,IAAI,CAAC;AAAA,EAC5E;AACA,SAAO;AACT;;;AF9BA,IAAM,gBAAgC;AAAA,EACpC,MAAM;AAAA,EACN,SAAS;AAAA,EACT,SAAS;AAAA,EACT,QAAQ,EAAE,cAAc,oBAAI,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE;AAC1C;AAEO,IAAM,0BAA0B;AAEvC,SAAS,KAAK,MAAsB;AAClC,SAAO,KAAK,YAAY,EAAE,QAAQ,eAAe,GAAG,EAAE,QAAQ,UAAU,EAAE;AAC5E;AAGO,SAAS,WAAW,MAAc,QAAoC;AAC3E,QAAM,OAAO,OAAO,QAAQ,OAAO,GAAG;AACtC,QAAM,aAAa,CAAC,IAAI,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAC,GAAG,GAAG,GAAG,IAAI,OAAO,GAAG,GAAG,GAAG,IAAI,cAAc,CAAC;AAClG,SAAO,WAAW,KAAK,CAAC,MAAME,YAAWC,MAAK,MAAM,CAAC,CAAC,CAAC;AACzD;AAEA,SAASC,cAAa,OAAoB,QAA4B,MAA+B;AACnG,QAAM,QAAQ,MAAM,MAAM,CAAC;AAC3B,QAAM,OAAO,WAAW,MAAM,MAAM,QAAQ;AAC5C,QAAM,OAAO,CAAC,MAAM,UAAU,GAAG,MAAM,MAAM,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,KAAK,MAAM;AAChF,SAAO;AAAA,IACL,MAAM,QAAQ,MAAM;AAAA,IACpB,MAAM,OAAQ,MAAM,MAAM,CAAC,KAAK,IAAK;AAAA,IACrC,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,SAAS,GAAG,MAAM,OAAO,KAAK,IAAI;AAAA,IAClC,UAAU;AAAA,IACV,MAAM,iBAAiB,OAAO,YAAY,IAAI,MAAM,QAAQ,KAAK,KAAK,MAAM,QAAQ,CAAC;AAAA,IACrF,SAAS;AAAA,EACX;AACF;AAEA,SAAS,OAAO,QAA4B,MAAoD;AAC9F,SAAO,CAAC,EAAE,OAAO,MAAM;AACrB,UAAM,SAAS,kBAAkB,MAAM;AACvC,QAAI,OAAO,WAAW,KAAK,aAAa,KAAK,MAAM,EAAG,OAAM,IAAI,MAAM,2EAA2E;AACjJ,WAAO,EAAE,aAAa,OAAO,IAAI,CAAC,MAAMA,cAAa,GAAG,QAAQ,IAAI,CAAC,GAAG,UAAU,CAAC,EAAE;AAAA,EACvF;AACF;AAGO,SAAS,gBAAgB,SAA+C;AAC7E,QAAM,OAAO,UAAU,SAAS,GAAG;AACnC,QAAM,SAAS,uBAAuB,IAAI;AAC1C,MAAI,CAAC,OAAQ,QAAO,QAAQ,QAAQ,YAAY,MAAM,EAAE,UAAU,CAAC,uBAAuB,EAAE,CAAC,CAAC;AAC9F,QAAM,OAAO,CAAC,YAAY,OAAO,MAAM,aAAa,YAAY;AAChE,SAAO,cAAc,eAAe,MAAM,SAAS,OAAO,QAAQ,IAAI,CAAC;AACzE;;;AGhEA,SAAS,gBAAAC,qBAAoB;AAC7B,SAAS,eAAe;AAOxB,IAAM,OAAO;AACb,IAAM,cAAc;AAEb,IAAM,YAAY;AAClB,IAAM,mBAAmB;AAEhC,SAAS,QAAQ,KAAmC;AAClD,SAAO,MAAM,IAAI,MAAM,QAAQ,EAAE,OAAO,OAAO,IAAI,CAAC;AACtD;AAEA,SAAS,OAAO,MAAc,MAAc,GAAqB,MAAc,OAAgC;AAC7G,QAAM,QAAQ,QAAQ,EAAE,CAAC,CAAC;AAC1B,QAAM,UAAU,MAAM,SAAS,GAAG,KAAK,eAAe,MAAM,KAAK,IAAI,CAAC,KAAK,WAAW,KAAK;AAC3F,SAAO,EAAE,MAAM,MAAM,SAAS,EAAE,SAAS,KAAK,GAAG,UAAU,QAAQ,SAAS,UAAU,eAAe,MAAM,SAAS,MAAM;AAC5H;AAGO,SAAS,iBAAiB,MAAc,MAAiC;AAC9E,SAAO,KAAK,MAAM,IAAI,EAAE,QAAQ,CAAC,SAAS,MAAM;AAAA,IAC9C,GAAG,CAAC,GAAG,QAAQ,SAAS,IAAI,CAAC,EAAE,IAAI,CAAC,MAAM,OAAO,MAAM,IAAI,GAAG,GAAG,WAAW,MAAM,CAAC;AAAA,IACnF,GAAG,CAAC,GAAG,QAAQ,SAAS,WAAW,CAAC,EAAE,IAAI,CAAC,MAAM,OAAO,MAAM,IAAI,GAAG,GAAG,kBAAkB,cAAc,CAAC;AAAA,EAC3G,CAAC;AACH;AAGO,SAAS,kBAAkB,OAAiB,SAA0C;AAC3F,QAAM,OAAO,UAAU,SAAS,GAAG;AACnC,QAAM,cAAiC,CAAC;AACxC,QAAM,WAA0B,CAAC;AACjC,aAAW,QAAQ,OAAO;AACxB,UAAM,QAAQ,WAAW,MAAM,QAAQ,MAAM,IAAI,CAAC;AAClD,QAAI;AACF,kBAAY,KAAK,GAAG,iBAAiB,OAAOC,cAAa,QAAQ,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC;AAAA,IACzF,SAAS,KAAK;AACZ,eAAS,KAAK,EAAE,MAAM,gBAAgB,MAAM,oBAAoB,MAAM,OAAO,SAAU,IAAc,QAAQ,CAAC;AAAA,IAChH;AAAA,EACF;AACA,SAAO,YAAY,GAAG,EAAE,aAAa,SAAS,CAAC;AACjD;AAYO,SAAS,kBAAkB,aAAmD;AACnF,QAAM,SAA4B,EAAE,MAAM,GAAG,YAAY,GAAG,QAAQ,CAAC,EAAE;AACvE,aAAW,KAAK,aAAa;AAC3B,UAAM,MAAM,EAAE,SAAS,YAAY,SAAS,EAAE,SAAS,mBAAmB,eAAe;AACzF,QAAI,CAAC,IAAK;AACV,UAAM,OAAQ,OAAO,OAAO,EAAE,IAAI,MAAM,EAAE,MAAM,GAAG,YAAY,EAAE;AACjE,SAAK,GAAG,KAAK;AACb,WAAO,GAAG,KAAK;AAAA,EACjB;AACA,SAAO;AACT;;;ACpEA,SAAS,0BAA0B;AAsBnC,SAAS,YACP,YACA,YAC2B;AAC3B,MAAI,cAAc,WAAW,MAAO,QAAO;AAC3C,MAAI,cAAc,WAAW,KAAM,QAAO;AAC1C,SAAO;AACT;AAEA,SAAS,mBACP,SACA,iBACyD;AACzD,QAAM,CAAC,UAAU,IAAI,IAAI,gBAAgB,MAAM,GAAG;AAClD,MAAI,CAAC,mBAAmB,SAAS,QAA2B,EAAG,QAAO;AACtE,QAAM,UAAU,QAAQ,QAA2B;AACnD,MAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,QAAM,UAAU,SAAS,SAAY,SAAY,QAAQ,IAAI;AAC7D,MAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,MAAI,QAAQ,eAAe,UAAa,QAAQ,eAAe,QAAW;AACxE,WAAO;AAAA,EACT;AACA,SAAO,EAAE,YAAY,QAAQ,YAAY,YAAY,QAAQ,WAAW;AAC1E;AAEO,SAAS,mBACd,SACA,cACY;AACZ,QAAM,aAA0B,CAAC;AACjC,MAAI,WAAW;AAEf,aAAW,OAAO,cAAc;AAC9B,UAAM,cAAc,mBAAmB,SAAS,IAAI,IAAI;AACxD,QAAI,CAAC,YAAa;AAElB,UAAM,WAAW,OAAO,YAAY,UAAU;AAC9C,UAAM,QAAQ,OAAO,IAAI,KAAK;AAE9B,QAAI,UAAU,UAAU;AACtB;AAAA,IACF,OAAO;AACL,iBAAW,KAAK;AAAA,QACd,MAAM,IAAI;AAAA,QACV,MAAM,IAAI;AAAA,QACV,MAAM,IAAI;AAAA,QACV;AAAA,QACA;AAAA,QACA,UAAU;AAAA,UACR,YAAY;AAAA,UACZ,QAAQ;AAAA,QACV;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA,SAAS;AAAA,MACP,OAAO,aAAa;AAAA,MACpB;AAAA,MACA,WAAW,WAAW;AAAA,IACxB;AAAA,EACF;AACF;","names":["join","resolve","require","parser","join","writeFileSync","mkdtempSync","rmSync","join","tmpdir","mkdtempSync","join","tmpdir","writeFileSync","rmSync","toDiagnostic","notChecked","mkdtempSync","rmSync","writeFileSync","tmpdir","join","toDiagnostic","unparsedFiles","mkdtempSync","join","tmpdir","writeFileSync","rmSync","existsSync","join","join","marker","existsSync","join","toDiagnostic","readFileSync","readFileSync"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@titan-design/style-checker",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Run external lint tools (ruff, ESLint) against configs generated from a style profile, normalize their output into one diagnostic shape, and diff observations against a profile",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
"access": "public"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@titan-design/style-analyzer": "^0.1.
|
|
35
|
-
"@titan-design/style-profile": "^0.
|
|
34
|
+
"@titan-design/style-analyzer": "^0.1.1",
|
|
35
|
+
"@titan-design/style-profile": "^0.2.0"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"zod": "^4.0.0"
|