revu-ai 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +166 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +252 -0
- package/dist/cli.js.map +1 -0
- package/dist/concurrency.d.ts +1 -0
- package/dist/concurrency.js +31 -0
- package/dist/concurrency.js.map +1 -0
- package/dist/config.d.ts +18 -0
- package/dist/config.js +70 -0
- package/dist/config.js.map +1 -0
- package/dist/discovery.d.ts +3 -0
- package/dist/discovery.js +39 -0
- package/dist/discovery.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +26 -0
- package/dist/init.js +50 -0
- package/dist/init.js.map +1 -0
- package/dist/mcp/aggregator.d.ts +11 -0
- package/dist/mcp/aggregator.js +47 -0
- package/dist/mcp/aggregator.js.map +1 -0
- package/dist/mcp/server.d.ts +14 -0
- package/dist/mcp/server.js +105 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools.d.ts +33 -0
- package/dist/mcp/tools.js +32 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/output/github.d.ts +2 -0
- package/dist/output/github.js +36 -0
- package/dist/output/github.js.map +1 -0
- package/dist/output/json.d.ts +2 -0
- package/dist/output/json.js +9 -0
- package/dist/output/json.js.map +1 -0
- package/dist/output/pretty.d.ts +2 -0
- package/dist/output/pretty.js +142 -0
- package/dist/output/pretty.js.map +1 -0
- package/dist/prompts/init-system.d.ts +4 -0
- package/dist/prompts/init-system.js +148 -0
- package/dist/prompts/init-system.js.map +1 -0
- package/dist/prompts/init-user.d.ts +5 -0
- package/dist/prompts/init-user.js +20 -0
- package/dist/prompts/init-user.js.map +1 -0
- package/dist/prompts/review-system.d.ts +6 -0
- package/dist/prompts/review-system.js +61 -0
- package/dist/prompts/review-system.js.map +1 -0
- package/dist/prompts/review-user.d.ts +2 -0
- package/dist/prompts/review-user.js +10 -0
- package/dist/prompts/review-user.js.map +1 -0
- package/dist/providers/claude-code.d.ts +9 -0
- package/dist/providers/claude-code.js +481 -0
- package/dist/providers/claude-code.js.map +1 -0
- package/dist/providers/registry.d.ts +8 -0
- package/dist/providers/registry.js +60 -0
- package/dist/providers/registry.js.map +1 -0
- package/dist/providers/types.d.ts +70 -0
- package/dist/providers/types.js +2 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/refs.d.ts +9 -0
- package/dist/refs.js +81 -0
- package/dist/refs.js.map +1 -0
- package/dist/runner.d.ts +23 -0
- package/dist/runner.js +106 -0
- package/dist/runner.js.map +1 -0
- package/dist/types.d.ts +68 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/examples/github-workflow.yml +38 -0
- package/package.json +73 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 PKWadsworth
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# revu
|
|
2
|
+
|
|
3
|
+
Parallel AI code review for any git repo. You drop Markdown rule files (`*.revu.md`) anywhere in your project; on each run, `revu` spawns a separate Claude agent **per rule file** in parallel. Each agent reviews the current diff *only through the lens of its assigned rules* and reports findings back through a sidecar MCP server. The aggregated findings come out as JSON, pretty terminal output, or GitHub Actions PR annotations.
|
|
4
|
+
|
|
5
|
+
The point: instead of one giant "be a good reviewer" prompt, you write narrow, scoped rule files (dead code, contract enforcement, dependency hygiene, naming conventions, etc.) and they run independently. Most rule agents will silently exit on any given diff because the changes don't touch their scope.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# zero-install (npx / bunx / pnpm dlx — pick your runner)
|
|
11
|
+
npx revu-ai init
|
|
12
|
+
bunx revu-ai init
|
|
13
|
+
pnpm dlx revu-ai init
|
|
14
|
+
|
|
15
|
+
# or install globally
|
|
16
|
+
npm i -g revu-ai
|
|
17
|
+
pnpm add -g revu-ai
|
|
18
|
+
bun add -g revu-ai
|
|
19
|
+
|
|
20
|
+
# or as a project devDep
|
|
21
|
+
npm i -D revu-ai
|
|
22
|
+
pnpm add -D revu-ai
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Set `ANTHROPIC_API_KEY` in your environment.
|
|
26
|
+
|
|
27
|
+
## Quick start
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
cd my-project
|
|
31
|
+
revu-ai init # spawn an agent to inspect the repo and scaffold rule files
|
|
32
|
+
revu-ai list # show what would be reviewed
|
|
33
|
+
revu-ai # run it
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`revu-ai init` spawns a Claude agent that inspects your repo (CLAUDE.md, README, manifests, lint configs, top-level structure) and writes a curated set of `.revu.md` files. Globals go in `.revu/<topic>.revu.md`; rules scoped to a sub-service go alongside it (e.g. `services/auth/openapi.revu.md`). The agent searches for *implicit contracts* first — places where two parts of the codebase must be kept in sync but the type system doesn't enforce it — because those are the highest-value rules.
|
|
37
|
+
|
|
38
|
+
The agent is opinionated: language-aware (uses your project's actual logger / docstring style / convention idioms), refuses to restate things your linter and type-checker already enforce, and writes one concern per file. Re-run with `--force` to overwrite. Calibrate the wall-clock cap with `--timeout-ms` (default 10min).
|
|
39
|
+
|
|
40
|
+
## Writing rule files
|
|
41
|
+
|
|
42
|
+
A rule file is a plain Markdown document that describes a narrow review concern. Anything you'd put in a focused PR-review prompt works. Keep them small and single-purpose.
|
|
43
|
+
|
|
44
|
+
```markdown
|
|
45
|
+
# Logging discipline
|
|
46
|
+
|
|
47
|
+
Flag any newly added log statement that:
|
|
48
|
+
|
|
49
|
+
- Logs PII (email, user IDs, auth tokens)
|
|
50
|
+
- Uses `console.log` instead of the project logger (`src/log.ts`)
|
|
51
|
+
- Lacks structured fields (we want `{ event, ... }`, not freeform strings)
|
|
52
|
+
|
|
53
|
+
## Severity
|
|
54
|
+
|
|
55
|
+
- `critical` for anything that logs auth tokens
|
|
56
|
+
- `high` for anything that logs PII
|
|
57
|
+
- `medium` for `console.log` instead of the project logger
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The agent is told to only report findings that match the rule. If your diff has no logging changes, this rule will silently pass.
|
|
61
|
+
|
|
62
|
+
## How it works
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
revu CLI
|
|
66
|
+
├─ discover *.revu.md (respects .gitignore)
|
|
67
|
+
├─ resolve review target (default: origin/main...HEAD)
|
|
68
|
+
├─ start MCP sidecar on a random localhost port
|
|
69
|
+
├─ for each rule, in parallel:
|
|
70
|
+
│ spawn Claude agent (Claude Agent SDK)
|
|
71
|
+
│ ├─ system prompt = rule contents + reporting protocol
|
|
72
|
+
│ ├─ user prompt = "review the changes between <base> and <head>"
|
|
73
|
+
│ ├─ tools = Read, Grep, Glob, read-only Bash, mcp__revu__report_finding
|
|
74
|
+
│ └─ runs `git diff` itself to inspect the changes
|
|
75
|
+
└─ aggregate findings, emit output, exit
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The runner doesn't pre-compute the diff and stuff it into the prompt — agents inspect the changes via their own `git diff` calls. This sidesteps token-budget and large-file problems that the agent already handles natively.
|
|
79
|
+
|
|
80
|
+
Bash is gated to read-only commands (`git diff/log/show/status`, `cat`, `head`, `tail`, `ls`, `find`, `wc`, etc.). Mutating git operations (`push`, `commit`, `checkout`, …) and shell metacharacters (`>`, `&&`, `;`, backticks) are rejected.
|
|
81
|
+
|
|
82
|
+
## CLI
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
revu-ai [options] # run a review (default command)
|
|
86
|
+
revu-ai init [--dir .revu] # scaffold starter rules + config
|
|
87
|
+
revu-ai list # show discovered rule files
|
|
88
|
+
|
|
89
|
+
Options:
|
|
90
|
+
--base <ref> # diff base; default: auto-detected origin/main
|
|
91
|
+
--working-tree # review uncommitted changes instead of branch
|
|
92
|
+
--staged # review staged changes only
|
|
93
|
+
--pattern <glob> # rule file glob; default: **/*.revu.md
|
|
94
|
+
--provider <name> # default: claude-code
|
|
95
|
+
--model <id> # passed to provider
|
|
96
|
+
--concurrency <n> # max parallel agents; default: min(8, ruleCount)
|
|
97
|
+
--output <fmt> # pretty | json | github (default: auto)
|
|
98
|
+
--output-file <path> # also write output to a file
|
|
99
|
+
--fail-on <severity> # exit-code threshold; default: high
|
|
100
|
+
--force # skip the no-changes pre-flight short-circuit
|
|
101
|
+
--config <path> # config file; default: revu.config.json
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Exit codes: `0` clean, `1` findings ≥ `--fail-on`, `2` runner / agent error.
|
|
105
|
+
|
|
106
|
+
## Configuration
|
|
107
|
+
|
|
108
|
+
`revu.config.json` mirrors the CLI flags (CLI wins on conflict):
|
|
109
|
+
|
|
110
|
+
```json
|
|
111
|
+
{
|
|
112
|
+
"pattern": "**/*.revu.md",
|
|
113
|
+
"provider": "claude-code",
|
|
114
|
+
"model": "claude-sonnet-4-6",
|
|
115
|
+
"concurrency": 8,
|
|
116
|
+
"output": "auto",
|
|
117
|
+
"failOn": "high"
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Output
|
|
122
|
+
|
|
123
|
+
The JSON shape is the stable contract for downstream tooling:
|
|
124
|
+
|
|
125
|
+
```json
|
|
126
|
+
{
|
|
127
|
+
"schemaVersion": 1,
|
|
128
|
+
"runId": "...",
|
|
129
|
+
"startedAt": "...",
|
|
130
|
+
"completedAt": "...",
|
|
131
|
+
"reviewTarget": { "mode": "ref-range", "base": "origin/main", "baseSha": "...",
|
|
132
|
+
"head": "HEAD", "headSha": "...", "changedFiles": ["..."] },
|
|
133
|
+
"rules": [{ "id": "dead-code", "path": ".revu/dead-code.revu.md",
|
|
134
|
+
"ok": true, "durationMs": 12345, "findingCount": 1 }],
|
|
135
|
+
"findings": [{ "ruleId": "dead-code", "severity": "high",
|
|
136
|
+
"path": "src/foo.ts", "line": 42, "lineEnd": 47,
|
|
137
|
+
"message": "...", "category": "unused-export" }]
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## GitHub Actions
|
|
142
|
+
|
|
143
|
+
A starter workflow lives at `examples/github-workflow.yml`. Drop it into `.github/workflows/revu.yml` and add `ANTHROPIC_API_KEY` as a repo secret. With `--output github`, findings render as PR annotations.
|
|
144
|
+
|
|
145
|
+
## Custom providers
|
|
146
|
+
|
|
147
|
+
The default reviewer is Claude Code via `@anthropic-ai/claude-agent-sdk`. The `ReviewAgent` interface in `src/providers/types.ts` is the swap-out boundary:
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
import { registerProvider } from "revu-ai";
|
|
151
|
+
|
|
152
|
+
registerProvider("my-provider", (cfg) => ({
|
|
153
|
+
name: "my-provider",
|
|
154
|
+
async run(input) {
|
|
155
|
+
// Connect to input.mcp.url with Authorization: Bearer <input.mcp.authToken>
|
|
156
|
+
// and X-Revu-Rule-Id: <input.ruleId>. Call mcp__report_finding for each finding.
|
|
157
|
+
// Return { ruleId, ok, durationMs }.
|
|
158
|
+
},
|
|
159
|
+
}));
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Then `revu-ai --provider my-provider` (or set it in `revu.config.json`).
|
|
163
|
+
|
|
164
|
+
## License
|
|
165
|
+
|
|
166
|
+
MIT.
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { Command } from "commander";
|
|
4
|
+
import { findRepoRoot } from "./refs.js";
|
|
5
|
+
const pkg = createRequire(import.meta.url)("../package.json");
|
|
6
|
+
import { loadConfig } from "./config.js";
|
|
7
|
+
import { listRules, run, RevuExit } from "./runner.js";
|
|
8
|
+
import { emitJson } from "./output/json.js";
|
|
9
|
+
import { emitPretty } from "./output/pretty.js";
|
|
10
|
+
import { emitGithub } from "./output/github.js";
|
|
11
|
+
const COLOR = process.stderr.isTTY && !process.env.NO_COLOR;
|
|
12
|
+
const c = {
|
|
13
|
+
reset: "\x1b[0m",
|
|
14
|
+
dim: "\x1b[2m",
|
|
15
|
+
bold: "\x1b[1m",
|
|
16
|
+
red: "\x1b[31m",
|
|
17
|
+
yellow: "\x1b[33m",
|
|
18
|
+
green: "\x1b[32m",
|
|
19
|
+
blue: "\x1b[34m",
|
|
20
|
+
magenta: "\x1b[35m",
|
|
21
|
+
cyan: "\x1b[36m",
|
|
22
|
+
gray: "\x1b[90m",
|
|
23
|
+
};
|
|
24
|
+
const paint = (color, s) => (COLOR ? `${c[color]}${s}${c.reset}` : s);
|
|
25
|
+
const SEV_COLOR = {
|
|
26
|
+
aesthetic: "gray",
|
|
27
|
+
low: "blue",
|
|
28
|
+
medium: "yellow",
|
|
29
|
+
high: "red",
|
|
30
|
+
critical: "magenta",
|
|
31
|
+
};
|
|
32
|
+
const SEV_LABEL = {
|
|
33
|
+
aesthetic: "nit ",
|
|
34
|
+
low: "low ",
|
|
35
|
+
medium: "med ",
|
|
36
|
+
high: "high",
|
|
37
|
+
critical: "CRIT",
|
|
38
|
+
};
|
|
39
|
+
const program = new Command();
|
|
40
|
+
program
|
|
41
|
+
.name(pkg.name)
|
|
42
|
+
.description("Parallel AI code review with per-rule Claude agents")
|
|
43
|
+
.version(pkg.version);
|
|
44
|
+
program
|
|
45
|
+
.command("list")
|
|
46
|
+
.description("List rule files that would be reviewed")
|
|
47
|
+
.option("--pattern <glob>", "rule file glob")
|
|
48
|
+
.option("--config <path>", "config file path")
|
|
49
|
+
.action(async (opts) => {
|
|
50
|
+
const cwd = process.cwd();
|
|
51
|
+
const repoRoot = findRepoRoot(cwd);
|
|
52
|
+
const cfg = loadConfig(repoRoot, { pattern: opts.pattern, config: opts.config });
|
|
53
|
+
const rules = await listRules(cwd, cfg.pattern);
|
|
54
|
+
if (rules.length === 0) {
|
|
55
|
+
console.log(`No rule files found matching ${cfg.pattern}`);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
for (const r of rules) {
|
|
59
|
+
console.log(`${r.ruleId}\t${r.relPath}`);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
program
|
|
63
|
+
.command("init")
|
|
64
|
+
.description("Spawn an agent to inspect the repo and scaffold curated .revu.md rule files")
|
|
65
|
+
.option("--force", "overwrite existing rule files in .revu/")
|
|
66
|
+
.option("--provider <name>", "scaffold provider (default: claude-code)")
|
|
67
|
+
.option("--model <id>", "model id passed to provider")
|
|
68
|
+
.option("--timeout-ms <ms>", "scaffold agent wall-clock timeout (default: 600000 = 10min)", parseIntOpt)
|
|
69
|
+
.action(async (opts) => {
|
|
70
|
+
const { runInit, InitRefusedError } = await import("./init.js");
|
|
71
|
+
const showProgress = process.stderr.isTTY && !process.env.REVU_DEBUG;
|
|
72
|
+
try {
|
|
73
|
+
const result = await runInit({
|
|
74
|
+
cwd: process.cwd(),
|
|
75
|
+
force: opts.force ?? false,
|
|
76
|
+
provider: opts.provider ?? "claude-code",
|
|
77
|
+
...(opts.model !== undefined ? { model: opts.model } : {}),
|
|
78
|
+
timeoutMs: opts.timeoutMs ?? 600_000,
|
|
79
|
+
onStart: ({ repoRoot }) => process.stderr.write(`${paint("cyan", "▶")} ${paint("bold", "scaffolding rule files")} ${paint("dim", repoRoot)}\n`),
|
|
80
|
+
onActivity: showProgress
|
|
81
|
+
? (a) => {
|
|
82
|
+
if (a.kind === "tool" && a.name === "Write") {
|
|
83
|
+
// Each Write gets its own "✱ created" line via onFileWritten — skip the dim activity.
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (a.kind === "tool") {
|
|
87
|
+
process.stderr.write(` ${paint("dim", "↳")} ${paint("cyan", a.name ?? "")}${paint("dim", `(${a.detail})`)}\n`);
|
|
88
|
+
}
|
|
89
|
+
else if (a.detail) {
|
|
90
|
+
process.stderr.write(` ${paint("dim", "…")} ${paint("dim", a.detail)}\n`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
: undefined,
|
|
94
|
+
onFileWritten: showProgress
|
|
95
|
+
? (rel) => process.stderr.write(` ${paint("bold", paint("green", "✱ created"))} ${paint("bold", rel)}\n`)
|
|
96
|
+
: undefined,
|
|
97
|
+
});
|
|
98
|
+
if (!result.ok) {
|
|
99
|
+
const label = result.timedOut ? "⏱ scaffold timed out" : "✗ scaffold failed";
|
|
100
|
+
const color = result.timedOut ? "yellow" : "red";
|
|
101
|
+
process.stderr.write(`${paint(color, paint("bold", label))} ${paint(color, result.errorMessage ?? "?")}\n`);
|
|
102
|
+
if (result.filesWritten.length > 0) {
|
|
103
|
+
process.stderr.write(`${paint("dim", `(${result.filesWritten.length} file(s) written before exit)`)}\n`);
|
|
104
|
+
for (const f of result.filesWritten)
|
|
105
|
+
process.stderr.write(` ${paint("dim", f)}\n`);
|
|
106
|
+
}
|
|
107
|
+
process.exit(2);
|
|
108
|
+
}
|
|
109
|
+
const globals = result.filesWritten.filter((f) => f.startsWith(".revu/")).sort();
|
|
110
|
+
const locals = result.filesWritten.filter((f) => !f.startsWith(".revu/")).sort();
|
|
111
|
+
process.stderr.write(`${paint("bold", paint("green", "✓"))} ${paint("bold", String(result.filesWritten.length))} ${paint("dim", `rule file${result.filesWritten.length === 1 ? "" : "s"} created`)} ${paint("dim", `(${result.durationMs}ms)`)}\n`);
|
|
112
|
+
if (globals.length > 0) {
|
|
113
|
+
process.stderr.write(` ${paint("bold", "globals")}\n`);
|
|
114
|
+
for (const f of globals)
|
|
115
|
+
process.stderr.write(` ${f}\n`);
|
|
116
|
+
}
|
|
117
|
+
if (locals.length > 0) {
|
|
118
|
+
process.stderr.write(` ${paint("bold", "locals")}\n`);
|
|
119
|
+
for (const f of locals)
|
|
120
|
+
process.stderr.write(` ${f}\n`);
|
|
121
|
+
}
|
|
122
|
+
process.exit(0);
|
|
123
|
+
}
|
|
124
|
+
catch (e) {
|
|
125
|
+
if (e instanceof InitRefusedError) {
|
|
126
|
+
process.stderr.write(`${paint("yellow", paint("bold", "revu init:"))} ${e.message.replace(/^revu init: /, "")}\n`);
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}
|
|
129
|
+
process.stderr.write(`${paint("red", paint("bold", "revu init:"))} ${e.message}\n`);
|
|
130
|
+
process.exit(2);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
program
|
|
134
|
+
.command("run", { isDefault: true })
|
|
135
|
+
.description("Run a review")
|
|
136
|
+
.option("--base <ref>", "review base ref (default: auto-detect main)")
|
|
137
|
+
.option("--working-tree", "review uncommitted working-tree changes")
|
|
138
|
+
.option("--staged", "review staged changes only")
|
|
139
|
+
.option("--pattern <glob>", "rule file glob")
|
|
140
|
+
.option("--provider <name>", "review provider (default: claude-code)")
|
|
141
|
+
.option("--model <id>", "model id passed to provider")
|
|
142
|
+
.option("--concurrency <n>", "max parallel agents", parseIntOpt)
|
|
143
|
+
.option("--output <fmt>", "pretty | json | github")
|
|
144
|
+
.option("--output-file <path>", "additionally write output to a file")
|
|
145
|
+
.option("--fail-on <severity>", "exit non-zero threshold (default: high)")
|
|
146
|
+
.option("--timeout-ms <ms>", "per-rule wall-clock timeout in ms (default: 300000 = 5min); 0 disables", parseIntOpt0Allowed)
|
|
147
|
+
.option("--force", "ignore the no-changes pre-flight skip")
|
|
148
|
+
.option("--config <path>", "config file path")
|
|
149
|
+
.action(async (opts) => {
|
|
150
|
+
const cwd = process.cwd();
|
|
151
|
+
const repoRoot = findRepoRoot(cwd);
|
|
152
|
+
const cfg = loadConfig(repoRoot, opts);
|
|
153
|
+
try {
|
|
154
|
+
const showProgress = process.stderr.isTTY && !process.env.REVU_DEBUG;
|
|
155
|
+
const { report, exitCode } = await run(cwd, cfg, {
|
|
156
|
+
onRuleStart: (id) => process.stderr.write(`${paint("cyan", "▶")} ${paint("bold", id)}\n`),
|
|
157
|
+
onActivity: showProgress
|
|
158
|
+
? (id, a) => {
|
|
159
|
+
if (a.kind === "tool" && a.name === "mcp__revu__report_finding") {
|
|
160
|
+
// Findings get their own line via onFinding — skip the tool-use noise.
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
if (a.kind === "tool") {
|
|
164
|
+
process.stderr.write(` ${paint("dim", id)} ${paint("dim", "↳")} ${paint("cyan", a.name ?? "")}${paint("dim", `(${a.detail})`)}\n`);
|
|
165
|
+
}
|
|
166
|
+
else if (a.detail) {
|
|
167
|
+
process.stderr.write(` ${paint("dim", id)} ${paint("dim", "…")} ${paint("dim", a.detail)}\n`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
: undefined,
|
|
171
|
+
onFinding: showProgress
|
|
172
|
+
? (f) => {
|
|
173
|
+
const loc = f.line !== undefined ? `:${f.line}` : "";
|
|
174
|
+
const sev = paint(SEV_COLOR[f.severity], SEV_LABEL[f.severity]);
|
|
175
|
+
process.stderr.write(` ${paint("dim", f.ruleId)} ${paint("bold", paint(SEV_COLOR[f.severity], "✱"))} ${sev} ${paint("bold", f.path)}${paint("dim", loc)}\n`);
|
|
176
|
+
}
|
|
177
|
+
: undefined,
|
|
178
|
+
onRuleEnd: (r) => {
|
|
179
|
+
const dur = paint("dim", `(${r.durationMs}ms)`);
|
|
180
|
+
let status;
|
|
181
|
+
if (r.timedOut) {
|
|
182
|
+
const count = r.findingCount > 0 ? ` ${r.findingCount} partial finding(s)` : "";
|
|
183
|
+
status = `${paint("yellow", "⏱ timed out")}${paint("dim", count)}`;
|
|
184
|
+
}
|
|
185
|
+
else if (!r.ok) {
|
|
186
|
+
status = `${paint("red", "✗ error:")} ${paint("red", r.errorMessage ?? "?")}`;
|
|
187
|
+
}
|
|
188
|
+
else if (r.findingCount === 0) {
|
|
189
|
+
status = paint("green", "✓ clean");
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
status = `${paint("green", "✓")} ${paint("bold", String(r.findingCount))} ${paint("dim", `finding${r.findingCount === 1 ? "" : "s"}`)}`;
|
|
193
|
+
}
|
|
194
|
+
process.stderr.write(` ${paint("bold", r.id)} ${status} ${dur}\n`);
|
|
195
|
+
},
|
|
196
|
+
});
|
|
197
|
+
const fmt = cfg.output === "auto" ? (process.stdout.isTTY ? "pretty" : "json") : cfg.output;
|
|
198
|
+
const outFile = cfg.outputFile;
|
|
199
|
+
if (fmt === "json")
|
|
200
|
+
emitJson(report, outFile);
|
|
201
|
+
else if (fmt === "github")
|
|
202
|
+
emitGithub(report, outFile);
|
|
203
|
+
else
|
|
204
|
+
emitPretty(report, outFile);
|
|
205
|
+
// If every rule errored, emit a stderr line with the actual cause so
|
|
206
|
+
// it's visible even when stdout is being piped/captured.
|
|
207
|
+
const failed = report.rules.filter((r) => !r.ok);
|
|
208
|
+
if (failed.length === report.rules.length && failed.length > 0) {
|
|
209
|
+
const messages = new Set(failed.map((r) => r.errorMessage ?? "unknown"));
|
|
210
|
+
const prefix = paint("red", paint("bold", "revu:"));
|
|
211
|
+
if (messages.size === 1) {
|
|
212
|
+
process.stderr.write(`${prefix} all ${failed.length} rule agents failed: ${paint("red", String([...messages][0]))}\n`);
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
process.stderr.write(`${prefix} all ${failed.length} rule agents failed ${paint("dim", "(mixed errors; see report)")}\n`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
const timedOut = report.rules.filter((r) => r.timedOut);
|
|
219
|
+
if (timedOut.length > 0) {
|
|
220
|
+
process.stderr.write(`${paint("yellow", paint("bold", "revu:"))} ${timedOut.length} rule(s) timed out — partial findings included in the report\n`);
|
|
221
|
+
}
|
|
222
|
+
process.exit(exitCode);
|
|
223
|
+
}
|
|
224
|
+
catch (e) {
|
|
225
|
+
if (e instanceof RevuExit) {
|
|
226
|
+
if (e.exitCode === 0)
|
|
227
|
+
console.log(e.message);
|
|
228
|
+
else
|
|
229
|
+
console.error(e.message);
|
|
230
|
+
process.exit(e.exitCode);
|
|
231
|
+
}
|
|
232
|
+
console.error(e.stack ?? String(e));
|
|
233
|
+
process.exit(2);
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
function parseIntOpt(value) {
|
|
237
|
+
const n = Number.parseInt(value, 10);
|
|
238
|
+
if (!Number.isFinite(n) || n <= 0)
|
|
239
|
+
throw new Error(`Invalid integer: ${value}`);
|
|
240
|
+
return n;
|
|
241
|
+
}
|
|
242
|
+
function parseIntOpt0Allowed(value) {
|
|
243
|
+
const n = Number.parseInt(value, 10);
|
|
244
|
+
if (!Number.isFinite(n) || n < 0)
|
|
245
|
+
throw new Error(`Invalid integer: ${value}`);
|
|
246
|
+
return n;
|
|
247
|
+
}
|
|
248
|
+
program.parseAsync(process.argv).catch((e) => {
|
|
249
|
+
console.error(e.stack ?? String(e));
|
|
250
|
+
process.exit(2);
|
|
251
|
+
});
|
|
252
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAEzC,MAAM,GAAG,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,iBAAiB,CAAsC,CAAC;AACnG,OAAO,EAAE,UAAU,EAAqB,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGhD,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;AAC5D,MAAM,CAAC,GAAG;IACR,KAAK,EAAE,SAAS;IAChB,GAAG,EAAE,SAAS;IACd,IAAI,EAAE,SAAS;IACf,GAAG,EAAE,UAAU;IACf,MAAM,EAAE,UAAU;IAClB,KAAK,EAAE,UAAU;IACjB,IAAI,EAAE,UAAU;IAChB,OAAO,EAAE,UAAU;IACnB,IAAI,EAAE,UAAU;IAChB,IAAI,EAAE,UAAU;CACjB,CAAC;AAEF,MAAM,KAAK,GAAG,CAAC,KAAY,EAAE,CAAS,EAAU,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7F,MAAM,SAAS,GAA4B;IACzC,SAAS,EAAE,MAAM;IACjB,GAAG,EAAE,MAAM;IACX,MAAM,EAAE,QAAQ;IAChB,IAAI,EAAE,KAAK;IACX,QAAQ,EAAE,SAAS;CACpB,CAAC;AACF,MAAM,SAAS,GAA6B;IAC1C,SAAS,EAAE,MAAM;IACjB,GAAG,EAAE,MAAM;IACX,MAAM,EAAE,MAAM;IACd,IAAI,EAAE,MAAM;IACZ,QAAQ,EAAE,MAAM;CACjB,CAAC;AAEF,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;KACd,WAAW,CAAC,qDAAqD,CAAC;KAClE,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AAExB,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,wCAAwC,CAAC;KACrD,MAAM,CAAC,kBAAkB,EAAE,gBAAgB,CAAC;KAC5C,MAAM,CAAC,iBAAiB,EAAE,kBAAkB,CAAC;KAC7C,MAAM,CAAC,KAAK,EAAE,IAA2C,EAAE,EAAE;IAC5D,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC1B,MAAM,QAAQ,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;IACnC,MAAM,GAAG,GAAG,UAAU,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;IACjF,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IAChD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,CAAC,GAAG,CAAC,gCAAgC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QAC3D,OAAO;IACT,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;IAC3C,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,6EAA6E,CAAC;KAC1F,MAAM,CAAC,SAAS,EAAE,yCAAyC,CAAC;KAC5D,MAAM,CAAC,mBAAmB,EAAE,0CAA0C,CAAC;KACvE,MAAM,CAAC,cAAc,EAAE,6BAA6B,CAAC;KACrD,MAAM,CAAC,mBAAmB,EAAE,6DAA6D,EAAE,WAAW,CAAC;KACvG,MAAM,CAAC,KAAK,EAAE,IAAgF,EAAE,EAAE;IACjG,MAAM,EAAE,OAAO,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;IAChE,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;IACrE,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC;YAC3B,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE;YAClB,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,KAAK;YAC1B,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,aAAa;YACxC,GAAG,CAAC,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC1D,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,OAAO;YACpC,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CACxB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,EAAE,wBAAwB,CAAC,IAAI,KAAK,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC;YACtH,UAAU,EAAE,YAAY;gBACtB,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE;oBACJ,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;wBAC5C,sFAAsF;wBACtF,OAAO;oBACT,CAAC;oBACD,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;wBACtB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,GAAG,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC;oBAClH,CAAC;yBAAM,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;wBACpB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;oBAC7E,CAAC;gBACH,CAAC;gBACH,CAAC,CAAC,SAAS;YACb,aAAa,EAAE,YAAY;gBACzB,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,CACN,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC;gBACnG,CAAC,CAAC,SAAS;SACd,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YACf,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,mBAAmB,CAAC;YAC7E,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC;YACjD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,CAAC,YAAY,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC;YAC5G,IAAI,MAAM,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACnC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,KAAK,EAAE,IAAI,MAAM,CAAC,YAAY,CAAC,MAAM,+BAA+B,CAAC,IAAI,CAAC,CAAC;gBACzG,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,YAAY;oBAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;YACtF,CAAC;YACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACjF,MAAM,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACjF,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,GAAG,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK,CAAC,KAAK,EAAE,YAAY,MAAM,CAAC,YAAY,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,IAAI,KAAK,CAAC,KAAK,EAAE,IAAI,MAAM,CAAC,UAAU,KAAK,CAAC,IAAI,CAC9N,CAAC;QACF,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC;YACxD,KAAK,MAAM,CAAC,IAAI,OAAO;gBAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC9D,CAAC;QACD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;YACvD,KAAK,MAAM,CAAC,IAAI,MAAM;gBAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC7D,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,YAAY,gBAAgB,EAAE,CAAC;YAClC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;YACnH,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,IAAK,CAAW,CAAC,OAAO,IAAI,CAAC,CAAC;QAC/F,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;KACnC,WAAW,CAAC,cAAc,CAAC;KAC3B,MAAM,CAAC,cAAc,EAAE,6CAA6C,CAAC;KACrE,MAAM,CAAC,gBAAgB,EAAE,yCAAyC,CAAC;KACnE,MAAM,CAAC,UAAU,EAAE,4BAA4B,CAAC;KAChD,MAAM,CAAC,kBAAkB,EAAE,gBAAgB,CAAC;KAC5C,MAAM,CAAC,mBAAmB,EAAE,wCAAwC,CAAC;KACrE,MAAM,CAAC,cAAc,EAAE,6BAA6B,CAAC;KACrD,MAAM,CAAC,mBAAmB,EAAE,qBAAqB,EAAE,WAAW,CAAC;KAC/D,MAAM,CAAC,gBAAgB,EAAE,wBAAwB,CAAC;KAClD,MAAM,CAAC,sBAAsB,EAAE,qCAAqC,CAAC;KACrE,MAAM,CAAC,sBAAsB,EAAE,yCAAyC,CAAC;KACzE,MAAM,CAAC,mBAAmB,EAAE,wEAAwE,EAAE,mBAAmB,CAAC;KAC1H,MAAM,CAAC,SAAS,EAAE,uCAAuC,CAAC;KAC1D,MAAM,CAAC,iBAAiB,EAAE,kBAAkB,CAAC;KAC7C,MAAM,CAAC,KAAK,EAAE,IAAkB,EAAE,EAAE;IACnC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC1B,MAAM,QAAQ,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;IACnC,MAAM,GAAG,GAAG,UAAU,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IACvC,IAAI,CAAC;QACH,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;QACrE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE;YAC/C,WAAW,EAAE,CAAC,EAAE,EAAE,EAAE,CAClB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC;YACtE,UAAU,EAAE,YAAY;gBACtB,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE;oBACR,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,KAAK,2BAA2B,EAAE,CAAC;wBAChE,uEAAuE;wBACvE,OAAO;oBACT,CAAC;oBACD,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;wBACtB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,KAAK,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,GAAG,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,CAC9G,CAAC;oBACJ,CAAC;yBAAM,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;wBACpB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,KAAK,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,CACzE,CAAC;oBACJ,CAAC;gBACH,CAAC;gBACH,CAAC,CAAC,SAAS;YACb,SAAS,EAAE,YAAY;gBACrB,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE;oBACJ,MAAM,GAAG,GAAG,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBACrD,MAAM,GAAG,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;oBAChE,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,KAAK,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,GAAG,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,CACxI,CAAC;gBACJ,CAAC;gBACH,CAAC,CAAC,SAAS;YACb,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE;gBACf,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,UAAU,KAAK,CAAC,CAAC;gBAChD,IAAI,MAAc,CAAC;gBACnB,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;oBACf,MAAM,KAAK,GAAG,CAAC,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,YAAY,qBAAqB,CAAC,CAAC,CAAC,EAAE,CAAC;oBAChF,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,EAAE,aAAa,CAAC,GAAG,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC;gBACrE,CAAC;qBAAM,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;oBACjB,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,EAAE,UAAU,CAAC,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,YAAY,IAAI,GAAG,CAAC,EAAE,CAAC;gBAChF,CAAC;qBAAM,IAAI,CAAC,CAAC,YAAY,KAAK,CAAC,EAAE,CAAC;oBAChC,MAAM,GAAG,KAAK,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;gBACrC,CAAC;qBAAM,CAAC;oBACN,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,IAAI,KAAK,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC;gBAC1I,CAAC;gBACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,IAAI,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC;YACtE,CAAC;SACF,CAAC,CAAC;QAEH,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC;QAC5F,MAAM,OAAO,GAAG,GAAG,CAAC,UAAU,CAAC;QAC/B,IAAI,GAAG,KAAK,MAAM;YAAE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;aACzC,IAAI,GAAG,KAAK,QAAQ;YAAE,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;;YAClD,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAEjC,qEAAqE;QACrE,yDAAyD;QACzD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACjD,IAAI,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,KAAK,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC/D,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,IAAI,SAAS,CAAC,CAAC,CAAC;YACzE,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;YACpD,IAAI,QAAQ,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;gBACxB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,GAAG,MAAM,QAAQ,MAAM,CAAC,MAAM,wBAAwB,KAAK,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CACjG,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,GAAG,MAAM,QAAQ,MAAM,CAAC,MAAM,uBAAuB,KAAK,CAAC,KAAK,EAAE,4BAA4B,CAAC,IAAI,CACpG,CAAC;YACJ,CAAC;QACH,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QACxD,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,GAAG,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,QAAQ,CAAC,MAAM,gEAAgE,CAC9H,CAAC;QACJ,CAAC;QAED,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACzB,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,YAAY,QAAQ,EAAE,CAAC;YAC1B,IAAI,CAAC,CAAC,QAAQ,KAAK,CAAC;gBAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;;gBACxC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;YAC9B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QAC3B,CAAC;QACD,OAAO,CAAC,KAAK,CAAE,CAAW,CAAC,KAAK,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,SAAS,WAAW,CAAC,KAAa;IAChC,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACrC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,KAAK,EAAE,CAAC,CAAC;IAChF,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,mBAAmB,CAAC,KAAa;IACxC,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACrC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,KAAK,EAAE,CAAC,CAAC;IAC/E,OAAO,CAAC,CAAC;AACX,CAAC;AAED,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE;IAC3C,OAAO,CAAC,KAAK,CAAE,CAAW,CAAC,KAAK,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function createLimiter(max: number): <T>(fn: () => Promise<T>) => Promise<T>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export function createLimiter(max) {
|
|
2
|
+
if (max < 1)
|
|
3
|
+
throw new Error(`concurrency limit must be >= 1, got ${max}`);
|
|
4
|
+
let active = 0;
|
|
5
|
+
const queue = [];
|
|
6
|
+
const next = () => {
|
|
7
|
+
if (active >= max)
|
|
8
|
+
return;
|
|
9
|
+
const job = queue.shift();
|
|
10
|
+
if (job) {
|
|
11
|
+
active++;
|
|
12
|
+
job();
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
return (fn) => new Promise((resolve, reject) => {
|
|
16
|
+
const run = () => {
|
|
17
|
+
fn().then((v) => {
|
|
18
|
+
active--;
|
|
19
|
+
resolve(v);
|
|
20
|
+
next();
|
|
21
|
+
}, (e) => {
|
|
22
|
+
active--;
|
|
23
|
+
reject(e);
|
|
24
|
+
next();
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
queue.push(run);
|
|
28
|
+
next();
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=concurrency.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"concurrency.js","sourceRoot":"","sources":["../src/concurrency.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,IAAI,GAAG,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,uCAAuC,GAAG,EAAE,CAAC,CAAC;IAC3E,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,MAAM,KAAK,GAAsB,EAAE,CAAC;IAEpC,MAAM,IAAI,GAAG,GAAG,EAAE;QAChB,IAAI,MAAM,IAAI,GAAG;YAAE,OAAO;QAC1B,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;QAC1B,IAAI,GAAG,EAAE,CAAC;YACR,MAAM,EAAE,CAAC;YACT,GAAG,EAAE,CAAC;QACR,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,CAAI,EAAoB,EAAc,EAAE,CAC7C,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACjC,MAAM,GAAG,GAAG,GAAG,EAAE;YACf,EAAE,EAAE,CAAC,IAAI,CACP,CAAC,CAAC,EAAE,EAAE;gBACJ,MAAM,EAAE,CAAC;gBACT,OAAO,CAAC,CAAC,CAAC,CAAC;gBACX,IAAI,EAAE,CAAC;YACT,CAAC,EACD,CAAC,CAAC,EAAE,EAAE;gBACJ,MAAM,EAAE,CAAC;gBACT,MAAM,CAAC,CAAC,CAAC,CAAC;gBACV,IAAI,EAAE,CAAC;YACT,CAAC,CACF,CAAC;QACJ,CAAC,CAAC;QACF,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAChB,IAAI,EAAE,CAAC;IACT,CAAC,CAAC,CAAC;AACP,CAAC"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { RevuConfig } from "./types.js";
|
|
2
|
+
export declare const DEFAULT_CONFIG: RevuConfig;
|
|
3
|
+
export interface CliOverrides {
|
|
4
|
+
base?: string;
|
|
5
|
+
workingTree?: boolean;
|
|
6
|
+
staged?: boolean;
|
|
7
|
+
pattern?: string;
|
|
8
|
+
provider?: string;
|
|
9
|
+
model?: string;
|
|
10
|
+
concurrency?: number;
|
|
11
|
+
output?: "pretty" | "json" | "github";
|
|
12
|
+
outputFile?: string;
|
|
13
|
+
failOn?: string;
|
|
14
|
+
force?: boolean;
|
|
15
|
+
config?: string;
|
|
16
|
+
timeoutMs?: number;
|
|
17
|
+
}
|
|
18
|
+
export declare function loadConfig(repoRoot: string, overrides: CliOverrides): RevuConfig;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
const SEVERITIES = new Set([
|
|
4
|
+
"aesthetic",
|
|
5
|
+
"low",
|
|
6
|
+
"medium",
|
|
7
|
+
"high",
|
|
8
|
+
"critical",
|
|
9
|
+
]);
|
|
10
|
+
export const DEFAULT_CONFIG = {
|
|
11
|
+
pattern: "**/*.revu.md",
|
|
12
|
+
workingTree: false,
|
|
13
|
+
staged: false,
|
|
14
|
+
provider: "claude-code",
|
|
15
|
+
output: "auto",
|
|
16
|
+
failOn: "high",
|
|
17
|
+
force: false,
|
|
18
|
+
timeoutMs: 300_000,
|
|
19
|
+
};
|
|
20
|
+
export function loadConfig(repoRoot, overrides) {
|
|
21
|
+
const file = overrides.config
|
|
22
|
+
? resolve(repoRoot, overrides.config)
|
|
23
|
+
: resolve(repoRoot, "revu.config.json");
|
|
24
|
+
let fromFile = {};
|
|
25
|
+
if (existsSync(file)) {
|
|
26
|
+
try {
|
|
27
|
+
fromFile = JSON.parse(readFileSync(file, "utf8"));
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
throw new Error(`Failed to parse ${file}: ${e.message}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const merged = {
|
|
34
|
+
...DEFAULT_CONFIG,
|
|
35
|
+
...fromFile,
|
|
36
|
+
};
|
|
37
|
+
if (overrides.pattern !== undefined)
|
|
38
|
+
merged.pattern = overrides.pattern;
|
|
39
|
+
if (overrides.base !== undefined)
|
|
40
|
+
merged.base = overrides.base;
|
|
41
|
+
if (overrides.workingTree !== undefined)
|
|
42
|
+
merged.workingTree = overrides.workingTree;
|
|
43
|
+
if (overrides.staged !== undefined)
|
|
44
|
+
merged.staged = overrides.staged;
|
|
45
|
+
if (overrides.provider !== undefined)
|
|
46
|
+
merged.provider = overrides.provider;
|
|
47
|
+
if (overrides.model !== undefined)
|
|
48
|
+
merged.model = overrides.model;
|
|
49
|
+
if (overrides.concurrency !== undefined)
|
|
50
|
+
merged.concurrency = overrides.concurrency;
|
|
51
|
+
if (overrides.output !== undefined)
|
|
52
|
+
merged.output = overrides.output;
|
|
53
|
+
if (overrides.outputFile !== undefined)
|
|
54
|
+
merged.outputFile = overrides.outputFile;
|
|
55
|
+
if (overrides.force !== undefined)
|
|
56
|
+
merged.force = overrides.force;
|
|
57
|
+
if (overrides.timeoutMs !== undefined)
|
|
58
|
+
merged.timeoutMs = overrides.timeoutMs;
|
|
59
|
+
if (overrides.failOn !== undefined) {
|
|
60
|
+
if (!SEVERITIES.has(overrides.failOn)) {
|
|
61
|
+
throw new Error(`Invalid --fail-on value: ${overrides.failOn}. Expected one of: ${[...SEVERITIES].join(", ")}`);
|
|
62
|
+
}
|
|
63
|
+
merged.failOn = overrides.failOn;
|
|
64
|
+
}
|
|
65
|
+
if (merged.workingTree && merged.staged) {
|
|
66
|
+
throw new Error("--working-tree and --staged are mutually exclusive");
|
|
67
|
+
}
|
|
68
|
+
return merged;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
2
|
+
import { join, resolve, sep } from "node:path";
|
|
3
|
+
import fg from "fast-glob";
|
|
4
|
+
import ignoreImport from "ignore";
|
|
5
|
+
// `ignore` is published as CJS; under NodeNext its default export is the factory itself.
|
|
6
|
+
const ignore = ignoreImport.default ?? ignoreImport;
|
|
7
|
+
const ALWAYS_IGNORE = ["node_modules/**", ".git/**", "dist/**", "build/**"];
|
|
8
|
+
export async function discoverRules(repoRoot, pattern) {
|
|
9
|
+
const ig = ignore();
|
|
10
|
+
const gitignorePath = join(repoRoot, ".gitignore");
|
|
11
|
+
if (existsSync(gitignorePath)) {
|
|
12
|
+
ig.add(readFileSync(gitignorePath, "utf8"));
|
|
13
|
+
}
|
|
14
|
+
const matches = await fg(pattern, {
|
|
15
|
+
cwd: repoRoot,
|
|
16
|
+
dot: true,
|
|
17
|
+
ignore: ALWAYS_IGNORE,
|
|
18
|
+
onlyFiles: true,
|
|
19
|
+
});
|
|
20
|
+
const filtered = matches.filter((rel) => !ig.ignores(rel));
|
|
21
|
+
return filtered.map((rel) => {
|
|
22
|
+
const abs = resolve(repoRoot, rel);
|
|
23
|
+
return {
|
|
24
|
+
ruleId: deriveRuleId(rel),
|
|
25
|
+
absPath: abs,
|
|
26
|
+
relPath: rel,
|
|
27
|
+
content: readFileSync(abs, "utf8"),
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
function deriveRuleId(relPath) {
|
|
32
|
+
const withoutSuffix = relPath.replace(/\.revu\.md$/i, "");
|
|
33
|
+
const parts = withoutSuffix.split(sep).filter((p) => p && p !== ".");
|
|
34
|
+
return parts.join("/");
|
|
35
|
+
}
|
|
36
|
+
export function _testing_deriveRuleId(rel) {
|
|
37
|
+
return deriveRuleId(rel);
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=discovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery.js","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAY,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AACzD,OAAO,EAAE,MAAM,WAAW,CAAC;AAC3B,OAAO,YAAY,MAAM,QAAQ,CAAC;AAGlC,yFAAyF;AACzF,MAAM,MAAM,GAAI,YAA6D,CAAC,OAAO,IAAI,YAAY,CAAC;AAEtG,MAAM,aAAa,GAAG,CAAC,iBAAiB,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;AAE5E,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,QAAgB,EAAE,OAAe;IACnE,MAAM,EAAE,GAAI,MAAyF,EAAE,CAAC;IACxG,MAAM,aAAa,GAAG,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IACnD,IAAI,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QAC9B,EAAE,CAAC,GAAG,CAAC,YAAY,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC;IAC9C,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,EAAE;QAChC,GAAG,EAAE,QAAQ;QACb,GAAG,EAAE,IAAI;QACT,MAAM,EAAE,aAAa;QACrB,SAAS,EAAE,IAAI;KAChB,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;IAE3D,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,EAAY,EAAE;QACpC,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QACnC,OAAO;YACL,MAAM,EAAE,YAAY,CAAC,GAAG,CAAC;YACzB,OAAO,EAAE,GAAG;YACZ,OAAO,EAAE,GAAG;YACZ,OAAO,EAAE,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC;SACnC,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,YAAY,CAAC,OAAe;IACnC,MAAM,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;IAC1D,MAAM,KAAK,GAAG,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC;IACrE,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,GAAW;IAC/C,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC;AAC3B,CAAC"}
|