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.
Files changed (70) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +166 -0
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +252 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/concurrency.d.ts +1 -0
  7. package/dist/concurrency.js +31 -0
  8. package/dist/concurrency.js.map +1 -0
  9. package/dist/config.d.ts +18 -0
  10. package/dist/config.js +70 -0
  11. package/dist/config.js.map +1 -0
  12. package/dist/discovery.d.ts +3 -0
  13. package/dist/discovery.js +39 -0
  14. package/dist/discovery.js.map +1 -0
  15. package/dist/index.d.ts +9 -0
  16. package/dist/index.js +6 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/init.d.ts +26 -0
  19. package/dist/init.js +50 -0
  20. package/dist/init.js.map +1 -0
  21. package/dist/mcp/aggregator.d.ts +11 -0
  22. package/dist/mcp/aggregator.js +47 -0
  23. package/dist/mcp/aggregator.js.map +1 -0
  24. package/dist/mcp/server.d.ts +14 -0
  25. package/dist/mcp/server.js +105 -0
  26. package/dist/mcp/server.js.map +1 -0
  27. package/dist/mcp/tools.d.ts +33 -0
  28. package/dist/mcp/tools.js +32 -0
  29. package/dist/mcp/tools.js.map +1 -0
  30. package/dist/output/github.d.ts +2 -0
  31. package/dist/output/github.js +36 -0
  32. package/dist/output/github.js.map +1 -0
  33. package/dist/output/json.d.ts +2 -0
  34. package/dist/output/json.js +9 -0
  35. package/dist/output/json.js.map +1 -0
  36. package/dist/output/pretty.d.ts +2 -0
  37. package/dist/output/pretty.js +142 -0
  38. package/dist/output/pretty.js.map +1 -0
  39. package/dist/prompts/init-system.d.ts +4 -0
  40. package/dist/prompts/init-system.js +148 -0
  41. package/dist/prompts/init-system.js.map +1 -0
  42. package/dist/prompts/init-user.d.ts +5 -0
  43. package/dist/prompts/init-user.js +20 -0
  44. package/dist/prompts/init-user.js.map +1 -0
  45. package/dist/prompts/review-system.d.ts +6 -0
  46. package/dist/prompts/review-system.js +61 -0
  47. package/dist/prompts/review-system.js.map +1 -0
  48. package/dist/prompts/review-user.d.ts +2 -0
  49. package/dist/prompts/review-user.js +10 -0
  50. package/dist/prompts/review-user.js.map +1 -0
  51. package/dist/providers/claude-code.d.ts +9 -0
  52. package/dist/providers/claude-code.js +481 -0
  53. package/dist/providers/claude-code.js.map +1 -0
  54. package/dist/providers/registry.d.ts +8 -0
  55. package/dist/providers/registry.js +60 -0
  56. package/dist/providers/registry.js.map +1 -0
  57. package/dist/providers/types.d.ts +70 -0
  58. package/dist/providers/types.js +2 -0
  59. package/dist/providers/types.js.map +1 -0
  60. package/dist/refs.d.ts +9 -0
  61. package/dist/refs.js +81 -0
  62. package/dist/refs.js.map +1 -0
  63. package/dist/runner.d.ts +23 -0
  64. package/dist/runner.js +106 -0
  65. package/dist/runner.js.map +1 -0
  66. package/dist/types.d.ts +68 -0
  67. package/dist/types.js +8 -0
  68. package/dist/types.js.map +1 -0
  69. package/examples/github-workflow.yml +38 -0
  70. package/package.json +73 -0
@@ -0,0 +1,9 @@
1
+ export { run, listRules, RevuExit } from "./runner.js";
2
+ export type { RunnerResult, RunHooks } from "./runner.js";
3
+ export { registerProvider, unregisterProvider, getProviderFactory, listProviders } from "./providers/registry.js";
4
+ export type { ReviewAgent, ReviewAgentFactory, ReviewInput, ReviewResult } from "./providers/types.js";
5
+ export { startSidecar } from "./mcp/server.js";
6
+ export type { SidecarHandle } from "./mcp/server.js";
7
+ export { FindingsAggregator } from "./mcp/aggregator.js";
8
+ export { loadConfig, DEFAULT_CONFIG } from "./config.js";
9
+ export type { Finding, Severity, RuleFile, ReviewTarget, ResolvedTarget, RuleResult, RunReport, RevuConfig, } from "./types.js";
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export { run, listRules, RevuExit } from "./runner.js";
2
+ export { registerProvider, unregisterProvider, getProviderFactory, listProviders } from "./providers/registry.js";
3
+ export { startSidecar } from "./mcp/server.js";
4
+ export { FindingsAggregator } from "./mcp/aggregator.js";
5
+ export { loadConfig, DEFAULT_CONFIG } from "./config.js";
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvD,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAElH,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC"}
package/dist/init.d.ts ADDED
@@ -0,0 +1,26 @@
1
+ import type { ReviewActivity } from "./providers/types.js";
2
+ export interface InitOptions {
3
+ cwd: string;
4
+ /** Overwrite existing rule files. */
5
+ force: boolean;
6
+ provider: string;
7
+ model?: string;
8
+ timeoutMs: number;
9
+ onActivity?: (activity: ReviewActivity) => void;
10
+ onFileWritten?: (relPath: string) => void;
11
+ onStart?: (info: {
12
+ repoRoot: string;
13
+ }) => void;
14
+ }
15
+ export interface InitResult {
16
+ ok: boolean;
17
+ repoRoot: string;
18
+ filesWritten: string[];
19
+ durationMs: number;
20
+ errorMessage?: string;
21
+ timedOut?: boolean;
22
+ }
23
+ export declare class InitRefusedError extends Error {
24
+ constructor(message: string);
25
+ }
26
+ export declare function runInit(opts: InitOptions): Promise<InitResult>;
package/dist/init.js ADDED
@@ -0,0 +1,50 @@
1
+ import { existsSync, readdirSync } from "node:fs";
2
+ import { join, resolve } from "node:path";
3
+ import { findRepoRoot } from "./refs.js";
4
+ import { getScaffoldFactory } from "./providers/registry.js";
5
+ export class InitRefusedError extends Error {
6
+ constructor(message) {
7
+ super(message);
8
+ this.name = "InitRefusedError";
9
+ }
10
+ }
11
+ export async function runInit(opts) {
12
+ const repoRoot = findRepoRoot(opts.cwd);
13
+ if (!opts.force) {
14
+ const existing = listExistingGlobalRules(repoRoot);
15
+ if (existing.length > 0) {
16
+ throw new InitRefusedError(`revu init: \`.revu/\` already contains rule files (${existing.join(", ")}). Pass --force to overwrite.`);
17
+ }
18
+ }
19
+ opts.onStart?.({ repoRoot });
20
+ const factory = getScaffoldFactory(opts.provider);
21
+ const agent = factory({ ...(opts.model ? { model: opts.model } : {}) });
22
+ const result = await agent.run({
23
+ repoRoot,
24
+ force: opts.force,
25
+ timeoutMs: opts.timeoutMs,
26
+ ...(opts.onActivity ? { onActivity: opts.onActivity } : {}),
27
+ ...(opts.onFileWritten ? { onFileWritten: opts.onFileWritten } : {}),
28
+ });
29
+ return {
30
+ ok: result.ok,
31
+ repoRoot,
32
+ filesWritten: result.filesWritten,
33
+ durationMs: result.durationMs,
34
+ ...(result.errorMessage ? { errorMessage: result.errorMessage } : {}),
35
+ ...(result.timedOut ? { timedOut: true } : {}),
36
+ };
37
+ }
38
+ /** Returns the *.revu.md filenames already present in `<repoRoot>/.revu/`, or []. */
39
+ function listExistingGlobalRules(repoRoot) {
40
+ const dir = resolve(repoRoot, ".revu");
41
+ if (!existsSync(dir))
42
+ return [];
43
+ try {
44
+ return readdirSync(dir).filter((n) => n.endsWith(".revu.md")).map((n) => join(".revu", n));
45
+ }
46
+ catch {
47
+ return [];
48
+ }
49
+ }
50
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { Finding, Severity } from "../types.js";
2
+ export declare class FindingsAggregator {
3
+ private byRule;
4
+ private dedupKeys;
5
+ private listeners;
6
+ onAdd(listener: (f: Finding) => void): () => void;
7
+ add(finding: Finding): boolean;
8
+ countFor(ruleId: string): number;
9
+ all(): Finding[];
10
+ maxSeverity(): Severity | undefined;
11
+ }
@@ -0,0 +1,47 @@
1
+ export class FindingsAggregator {
2
+ byRule = new Map();
3
+ dedupKeys = new Set();
4
+ listeners = new Set();
5
+ onAdd(listener) {
6
+ this.listeners.add(listener);
7
+ return () => this.listeners.delete(listener);
8
+ }
9
+ add(finding) {
10
+ const key = `${finding.ruleId}\0${finding.severity}\0${finding.path}\0${finding.line ?? ""}\0${finding.lineEnd ?? ""}\0${finding.message}`;
11
+ if (this.dedupKeys.has(key))
12
+ return false;
13
+ this.dedupKeys.add(key);
14
+ const list = this.byRule.get(finding.ruleId);
15
+ if (list)
16
+ list.push(finding);
17
+ else
18
+ this.byRule.set(finding.ruleId, [finding]);
19
+ for (const l of this.listeners) {
20
+ try {
21
+ l(finding);
22
+ }
23
+ catch { /* listener errors must not affect aggregation */ }
24
+ }
25
+ return true;
26
+ }
27
+ countFor(ruleId) {
28
+ return this.byRule.get(ruleId)?.length ?? 0;
29
+ }
30
+ all() {
31
+ const out = [];
32
+ for (const list of this.byRule.values())
33
+ out.push(...list);
34
+ return out;
35
+ }
36
+ maxSeverity() {
37
+ const order = ["aesthetic", "low", "medium", "high", "critical"];
38
+ let max = -1;
39
+ for (const f of this.all()) {
40
+ const idx = order.indexOf(f.severity);
41
+ if (idx > max)
42
+ max = idx;
43
+ }
44
+ return max < 0 ? undefined : order[max];
45
+ }
46
+ }
47
+ //# sourceMappingURL=aggregator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aggregator.js","sourceRoot":"","sources":["../../src/mcp/aggregator.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,kBAAkB;IACrB,MAAM,GAAG,IAAI,GAAG,EAAqB,CAAC;IACtC,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,SAAS,GAAG,IAAI,GAAG,EAAwB,CAAC;IAEpD,KAAK,CAAC,QAA8B;QAClC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC7B,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC/C,CAAC;IAED,GAAG,CAAC,OAAgB;QAClB,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,MAAM,KAAK,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,IAAI,IAAI,EAAE,KAAK,OAAO,CAAC,OAAO,IAAI,EAAE,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC;QAC3I,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;QAC1C,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAExB,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC7C,IAAI,IAAI;YAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;;YACxB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;QAChD,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YAC/B,IAAI,CAAC;gBAAC,CAAC,CAAC,OAAO,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,iDAAiD,CAAC,CAAC;QACjF,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,QAAQ,CAAC,MAAc;QACrB,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC,CAAC;IAC9C,CAAC;IAED,GAAG;QACD,MAAM,GAAG,GAAc,EAAE,CAAC;QAC1B,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE;YAAE,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;QAC3D,OAAO,GAAG,CAAC;IACb,CAAC;IAED,WAAW;QACT,MAAM,KAAK,GAAe,CAAC,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;QAC7E,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC;QACb,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YAC3B,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;YACtC,IAAI,GAAG,GAAG,GAAG;gBAAE,GAAG,GAAG,GAAG,CAAC;QAC3B,CAAC;QACD,OAAO,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC1C,CAAC;CACF"}
@@ -0,0 +1,14 @@
1
+ import { FindingsAggregator } from "./aggregator.js";
2
+ export interface SidecarHandle {
3
+ url: string;
4
+ authToken: string;
5
+ aggregator: FindingsAggregator;
6
+ shutdown: () => Promise<void>;
7
+ }
8
+ export interface StartSidecarOptions {
9
+ repoRoot: string;
10
+ host?: string;
11
+ /** If omitted, an ephemeral free port is chosen. */
12
+ port?: number;
13
+ }
14
+ export declare function startSidecar(opts: StartSidecarOptions): Promise<SidecarHandle>;
@@ -0,0 +1,105 @@
1
+ import { createServer } from "node:http";
2
+ import { randomBytes } from "node:crypto";
3
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
5
+ import { isAbsolute, relative } from "node:path";
6
+ import { FindingsAggregator } from "./aggregator.js";
7
+ import { ReportFindingShape, REPORT_FINDING_DESCRIPTION } from "./tools.js";
8
+ export async function startSidecar(opts) {
9
+ const aggregator = new FindingsAggregator();
10
+ const authToken = randomBytes(24).toString("base64url");
11
+ const host = opts.host ?? "127.0.0.1";
12
+ const httpServer = createServer((req, res) => {
13
+ handle(req, res, { authToken, aggregator, repoRoot: opts.repoRoot }).catch((e) => {
14
+ if (!res.headersSent) {
15
+ res.statusCode = 500;
16
+ res.end(`internal error: ${e.message}`);
17
+ }
18
+ });
19
+ });
20
+ await new Promise((resolve, reject) => {
21
+ httpServer.once("error", reject);
22
+ httpServer.listen(opts.port ?? 0, host, () => {
23
+ httpServer.off("error", reject);
24
+ resolve();
25
+ });
26
+ });
27
+ const addr = httpServer.address();
28
+ const url = `http://${host}:${addr.port}/mcp`;
29
+ const shutdown = () => new Promise((resolve, reject) => {
30
+ httpServer.close((err) => (err ? reject(err) : resolve()));
31
+ });
32
+ return { url, authToken, aggregator, shutdown };
33
+ }
34
+ async function handle(req, res, ctx) {
35
+ if (!req.url?.startsWith("/mcp")) {
36
+ res.statusCode = 404;
37
+ res.end("not found");
38
+ return;
39
+ }
40
+ const auth = req.headers["authorization"];
41
+ if (typeof auth !== "string" || auth !== `Bearer ${ctx.authToken}`) {
42
+ res.statusCode = 401;
43
+ res.end("unauthorized");
44
+ return;
45
+ }
46
+ const ruleIdHeader = req.headers["x-revu-rule-id"];
47
+ const ruleId = typeof ruleIdHeader === "string" && ruleIdHeader.length > 0 ? ruleIdHeader : "unknown";
48
+ const mcp = buildMcpServer(ruleId, ctx);
49
+ const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
50
+ res.on("close", () => {
51
+ void transport.close().catch(() => { });
52
+ void mcp.close().catch(() => { });
53
+ });
54
+ await mcp.connect(transport);
55
+ await transport.handleRequest(req, res);
56
+ }
57
+ function buildMcpServer(ruleId, ctx) {
58
+ const server = new McpServer({ name: "revu-sidecar", version: "0.0.1" });
59
+ server.registerTool("report_finding", {
60
+ description: REPORT_FINDING_DESCRIPTION,
61
+ inputSchema: ReportFindingShape,
62
+ }, async (args) => {
63
+ if (args.lineEnd !== undefined && args.line === undefined) {
64
+ return errorResult("`line` is required when `lineEnd` is set.");
65
+ }
66
+ if (args.lineEnd !== undefined && args.line !== undefined && args.lineEnd < args.line) {
67
+ return errorResult("`lineEnd` must be >= `line`.");
68
+ }
69
+ const finding = {
70
+ ruleId,
71
+ severity: args.severity,
72
+ path: normalizePath(args.path, ctx.repoRoot),
73
+ message: args.message,
74
+ ...(args.line !== undefined ? { line: args.line } : {}),
75
+ ...(args.lineEnd !== undefined ? { lineEnd: args.lineEnd } : {}),
76
+ ...(args.category !== undefined ? { category: args.category } : {}),
77
+ };
78
+ const accepted = ctx.aggregator.add(finding);
79
+ return {
80
+ content: [
81
+ {
82
+ type: "text",
83
+ text: accepted
84
+ ? `Recorded ${finding.severity} finding for ${finding.path}.`
85
+ : `Duplicate finding for ${finding.path}; ignored.`,
86
+ },
87
+ ],
88
+ };
89
+ });
90
+ return server;
91
+ }
92
+ function normalizePath(input, repoRoot) {
93
+ if (isAbsolute(input)) {
94
+ const rel = relative(repoRoot, input);
95
+ return rel.split("\\").join("/");
96
+ }
97
+ return input.split("\\").join("/");
98
+ }
99
+ function errorResult(message) {
100
+ return {
101
+ isError: true,
102
+ content: [{ type: "text", text: message }],
103
+ };
104
+ }
105
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import { z } from "zod";
2
+ export declare const ReportFindingShape: {
3
+ readonly severity: z.ZodEnum<["aesthetic", "low", "medium", "high", "critical"]>;
4
+ readonly path: z.ZodString;
5
+ readonly line: z.ZodOptional<z.ZodNumber>;
6
+ readonly lineEnd: z.ZodOptional<z.ZodNumber>;
7
+ readonly message: z.ZodString;
8
+ readonly category: z.ZodOptional<z.ZodString>;
9
+ };
10
+ export declare const ReportFindingObject: z.ZodObject<{
11
+ readonly severity: z.ZodEnum<["aesthetic", "low", "medium", "high", "critical"]>;
12
+ readonly path: z.ZodString;
13
+ readonly line: z.ZodOptional<z.ZodNumber>;
14
+ readonly lineEnd: z.ZodOptional<z.ZodNumber>;
15
+ readonly message: z.ZodString;
16
+ readonly category: z.ZodOptional<z.ZodString>;
17
+ }, "strip", z.ZodTypeAny, {
18
+ path: string;
19
+ message: string;
20
+ severity: "aesthetic" | "low" | "medium" | "high" | "critical";
21
+ line?: number | undefined;
22
+ lineEnd?: number | undefined;
23
+ category?: string | undefined;
24
+ }, {
25
+ path: string;
26
+ message: string;
27
+ severity: "aesthetic" | "low" | "medium" | "high" | "critical";
28
+ line?: number | undefined;
29
+ lineEnd?: number | undefined;
30
+ category?: string | undefined;
31
+ }>;
32
+ export type ReportFindingInput = z.infer<typeof ReportFindingObject>;
33
+ export declare const REPORT_FINDING_DESCRIPTION = "Report a code-review finding to the revu runner.\nCall this tool once for each issue you find. The runner aggregates findings across all reviewers.\nSeverity guidance:\n aesthetic = nit / style preference\n low = minor smell, easy to live with\n medium = should fix; not a bug yet but degrades the codebase\n high = clearly wrong; will cause bugs or regressions\n critical = will break production, security issue, or data loss\nDo NOT report findings outside the scope of your assigned rules.";
@@ -0,0 +1,32 @@
1
+ import { z } from "zod";
2
+ export const ReportFindingShape = {
3
+ severity: z
4
+ .enum(["aesthetic", "low", "medium", "high", "critical"])
5
+ .describe("Severity of the finding."),
6
+ path: z.string().min(1).describe("Repo-relative file path, forward-slash separated."),
7
+ line: z
8
+ .number()
9
+ .int()
10
+ .positive()
11
+ .optional()
12
+ .describe("1-indexed line number where the issue starts."),
13
+ lineEnd: z
14
+ .number()
15
+ .int()
16
+ .positive()
17
+ .optional()
18
+ .describe("1-indexed line number where the issue ends. If set, line must also be set."),
19
+ message: z.string().min(1).describe("Human-readable description of the issue."),
20
+ category: z.string().optional().describe("Optional free-form category tag."),
21
+ };
22
+ export const ReportFindingObject = z.object(ReportFindingShape);
23
+ export const REPORT_FINDING_DESCRIPTION = `Report a code-review finding to the revu runner.
24
+ Call this tool once for each issue you find. The runner aggregates findings across all reviewers.
25
+ Severity guidance:
26
+ aesthetic = nit / style preference
27
+ low = minor smell, easy to live with
28
+ medium = should fix; not a bug yet but degrades the codebase
29
+ high = clearly wrong; will cause bugs or regressions
30
+ critical = will break production, security issue, or data loss
31
+ Do NOT report findings outside the scope of your assigned rules.`;
32
+ //# sourceMappingURL=tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../../src/mcp/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,QAAQ,EAAE,CAAC;SACR,IAAI,CAAC,CAAC,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;SACxD,QAAQ,CAAC,0BAA0B,CAAC;IACvC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,mDAAmD,CAAC;IACrF,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,GAAG,EAAE;SACL,QAAQ,EAAE;SACV,QAAQ,EAAE;SACV,QAAQ,CAAC,+CAA+C,CAAC;IAC5D,OAAO,EAAE,CAAC;SACP,MAAM,EAAE;SACR,GAAG,EAAE;SACL,QAAQ,EAAE;SACV,QAAQ,EAAE;SACV,QAAQ,CAAC,4EAA4E,CAAC;IACzF,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,0CAA0C,CAAC;IAC/E,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,kCAAkC,CAAC;CACpE,CAAC;AAEX,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC;AAGhE,MAAM,CAAC,MAAM,0BAA0B,GAAG;;;;;;;;iEAQuB,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { RunReport } from "../types.js";
2
+ export declare function emitGithub(report: RunReport, outputFile?: string): void;
@@ -0,0 +1,36 @@
1
+ import { writeFileSync } from "node:fs";
2
+ const SEV_COMMAND = {
3
+ aesthetic: "notice",
4
+ low: "notice",
5
+ medium: "warning",
6
+ high: "error",
7
+ critical: "error",
8
+ };
9
+ export function emitGithub(report, outputFile) {
10
+ const lines = [];
11
+ for (const f of report.findings) {
12
+ lines.push(formatLine(f));
13
+ }
14
+ const text = lines.length ? lines.join("\n") + "\n" : "";
15
+ process.stdout.write(text);
16
+ if (outputFile) {
17
+ writeFileSync(outputFile, text, "utf8");
18
+ }
19
+ }
20
+ function formatLine(f) {
21
+ const command = SEV_COMMAND[f.severity];
22
+ const params = [`file=${escape(f.path)}`];
23
+ if (f.line !== undefined)
24
+ params.push(`line=${f.line}`);
25
+ if (f.lineEnd !== undefined)
26
+ params.push(`endLine=${f.lineEnd}`);
27
+ params.push(`title=${escape(`revu / ${f.ruleId} (${f.severity})`)}`);
28
+ return `::${command} ${params.join(",")}::${escapeMessage(f.message)}`;
29
+ }
30
+ function escape(value) {
31
+ return value.replace(/%/g, "%25").replace(/\r/g, "%0D").replace(/\n/g, "%0A").replace(/:/g, "%3A").replace(/,/g, "%2C");
32
+ }
33
+ function escapeMessage(value) {
34
+ return value.replace(/%/g, "%25").replace(/\r/g, "%0D").replace(/\n/g, "%0A");
35
+ }
36
+ //# sourceMappingURL=github.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.js","sourceRoot":"","sources":["../../src/output/github.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAGxC,MAAM,WAAW,GAAqD;IACpE,SAAS,EAAE,QAAQ;IACnB,GAAG,EAAE,QAAQ;IACb,MAAM,EAAE,SAAS;IACjB,IAAI,EAAE,OAAO;IACb,QAAQ,EAAE,OAAO;CAClB,CAAC;AAEF,MAAM,UAAU,UAAU,CAAC,MAAiB,EAAE,UAAmB;IAC/D,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QAChC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5B,CAAC;IACD,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IACzD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC3B,IAAI,UAAU,EAAE,CAAC;QACf,aAAa,CAAC,UAAU,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IAC1C,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,CAAU;IAC5B,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IACxC,MAAM,MAAM,GAAa,CAAC,QAAQ,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACpD,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS;QAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IACxD,IAAI,CAAC,CAAC,OAAO,KAAK,SAAS;QAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;IACjE,MAAM,CAAC,IAAI,CAAC,SAAS,MAAM,CAAC,UAAU,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,QAAQ,GAAG,CAAC,EAAE,CAAC,CAAC;IACrE,OAAO,KAAK,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;AACzE,CAAC;AAED,SAAS,MAAM,CAAC,KAAa;IAC3B,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AAC1H,CAAC;AAED,SAAS,aAAa,CAAC,KAAa;IAClC,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAChF,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { RunReport } from "../types.js";
2
+ export declare function emitJson(report: RunReport, outputFile?: string): void;
@@ -0,0 +1,9 @@
1
+ import { writeFileSync } from "node:fs";
2
+ export function emitJson(report, outputFile) {
3
+ const text = JSON.stringify(report, null, 2);
4
+ process.stdout.write(text + "\n");
5
+ if (outputFile) {
6
+ writeFileSync(outputFile, text + "\n", "utf8");
7
+ }
8
+ }
9
+ //# sourceMappingURL=json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.js","sourceRoot":"","sources":["../../src/output/json.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAGxC,MAAM,UAAU,QAAQ,CAAC,MAAiB,EAAE,UAAmB;IAC7D,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC7C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IAClC,IAAI,UAAU,EAAE,CAAC;QACf,aAAa,CAAC,UAAU,EAAE,IAAI,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;IACjD,CAAC;AACH,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { RunReport } from "../types.js";
2
+ export declare function emitPretty(report: RunReport, outputFile?: string): void;
@@ -0,0 +1,142 @@
1
+ import { writeFileSync } from "node:fs";
2
+ const COLOR_ENABLED = process.stdout.isTTY && !process.env.NO_COLOR;
3
+ const c = {
4
+ reset: "\x1b[0m",
5
+ dim: "\x1b[2m",
6
+ bold: "\x1b[1m",
7
+ red: "\x1b[31m",
8
+ yellow: "\x1b[33m",
9
+ blue: "\x1b[34m",
10
+ green: "\x1b[32m",
11
+ magenta: "\x1b[35m",
12
+ cyan: "\x1b[36m",
13
+ gray: "\x1b[90m",
14
+ };
15
+ function paint(color, s) {
16
+ return COLOR_ENABLED ? `${c[color]}${s}${c.reset}` : s;
17
+ }
18
+ const SEV_COLOR = {
19
+ aesthetic: "gray",
20
+ low: "blue",
21
+ medium: "yellow",
22
+ high: "red",
23
+ critical: "magenta",
24
+ };
25
+ const SEV_LABEL = {
26
+ aesthetic: "nit ",
27
+ low: "low ",
28
+ medium: "med ",
29
+ high: "high",
30
+ critical: "CRIT",
31
+ };
32
+ export function emitPretty(report, outputFile) {
33
+ const lines = [];
34
+ lines.push(paint("bold", `revu ${report.runId.slice(0, 8)}`));
35
+ lines.push(paint("dim", `target: ${formatTarget(report)} rules: ${report.rules.length} findings: ${report.findings.length}`));
36
+ lines.push("");
37
+ const timedOut = report.rules.filter((r) => r.timedOut);
38
+ if (timedOut.length > 0) {
39
+ lines.push(paint("yellow", `⏱ ${timedOut.length} rule(s) timed out — results below are partial:`));
40
+ for (const r of timedOut) {
41
+ lines.push(paint("yellow", ` ${r.id} (${r.findingCount} partial finding(s), ${r.durationMs}ms)`));
42
+ }
43
+ lines.push("");
44
+ }
45
+ const systemic = detectSystemicFailure(report);
46
+ if (systemic) {
47
+ lines.push(paint("bold", paint("red", `✗ ${systemic.scope} agent(s) failed: ${systemic.message}`)));
48
+ const hint = hintFor(systemic.message);
49
+ if (hint)
50
+ lines.push(paint("yellow", ` → ${hint}`));
51
+ lines.push("");
52
+ }
53
+ if (report.findings.length === 0) {
54
+ lines.push(paint("green", " no findings"));
55
+ }
56
+ else {
57
+ const byPath = new Map();
58
+ for (const f of report.findings) {
59
+ const list = byPath.get(f.path) ?? [];
60
+ list.push(f);
61
+ byPath.set(f.path, list);
62
+ }
63
+ for (const [path, findings] of byPath) {
64
+ lines.push(paint("bold", path));
65
+ for (const f of findings) {
66
+ const sev = paint(SEV_COLOR[f.severity], SEV_LABEL[f.severity]);
67
+ const loc = f.line !== undefined
68
+ ? `:${f.line}${f.lineEnd && f.lineEnd !== f.line ? `-${f.lineEnd}` : ""}`
69
+ : "";
70
+ const cat = f.category ? paint("dim", ` [${f.category}]`) : "";
71
+ const ruleTag = paint("dim", `(${f.ruleId})`);
72
+ lines.push(` ${sev} ${path}${loc} ${ruleTag}${cat}`);
73
+ for (const ml of f.message.split("\n")) {
74
+ lines.push(` ${ml}`);
75
+ }
76
+ }
77
+ lines.push("");
78
+ }
79
+ }
80
+ const failed = report.rules.filter((r) => !r.ok);
81
+ if (failed.length > 0) {
82
+ lines.push(paint("red", `${failed.length} rule(s) errored:`));
83
+ for (const r of failed) {
84
+ lines.push(` ${r.id}: ${r.errorMessage ?? "(unknown error)"}`);
85
+ }
86
+ lines.push("");
87
+ }
88
+ const text = lines.join("\n") + "\n";
89
+ process.stdout.write(text);
90
+ if (outputFile) {
91
+ // Strip ANSI codes for file output.
92
+ writeFileSync(outputFile, text.replace(/\x1b\[[0-9;]*m/g, ""), "utf8");
93
+ }
94
+ }
95
+ function detectSystemicFailure(report) {
96
+ // Timeouts are reported in their own banner above; don't double-count.
97
+ const failed = report.rules.filter((r) => !r.ok && !r.timedOut);
98
+ if (failed.length === 0)
99
+ return undefined;
100
+ const messages = new Set(failed.map((r) => r.errorMessage ?? "unknown"));
101
+ if (messages.size === 1 && failed.length === report.rules.length) {
102
+ return { scope: `All ${failed.length}`, message: [...messages][0] ?? "unknown" };
103
+ }
104
+ if (messages.size === 1 && failed.length > 1) {
105
+ return { scope: `${failed.length}/${report.rules.length}`, message: [...messages][0] ?? "unknown" };
106
+ }
107
+ return undefined;
108
+ }
109
+ function hintFor(message) {
110
+ const m = message.toLowerCase();
111
+ if (m.includes("credit balance") || m.includes("credit_balance")) {
112
+ return "Anthropic API account has no credits — top up at https://console.anthropic.com/settings/billing";
113
+ }
114
+ if (m.includes("invalid api key") || m.includes("authentication") || m.includes("401")) {
115
+ return "Check ANTHROPIC_API_KEY in your environment.";
116
+ }
117
+ if (m.includes("rate limit") || m.includes("429")) {
118
+ return "Rate-limited by the Anthropic API — try a lower --concurrency or a different model.";
119
+ }
120
+ if (m.includes("overloaded") || m.includes("529")) {
121
+ return "Anthropic API is overloaded; retry in a moment.";
122
+ }
123
+ if (m.includes("model") && (m.includes("not found") || m.includes("does not exist"))) {
124
+ return "Model identifier was rejected — pass --model with a valid id.";
125
+ }
126
+ return undefined;
127
+ }
128
+ function formatTarget(report) {
129
+ const t = report.reviewTarget;
130
+ if (t.mode === "ref-range") {
131
+ const target = t.target;
132
+ if (target.mode === "ref-range") {
133
+ return `${target.base}...${target.head} (${t.changedFiles.length} files)`;
134
+ }
135
+ }
136
+ if (t.mode === "working-tree")
137
+ return `working tree (${t.changedFiles.length} files)`;
138
+ if (t.mode === "staged")
139
+ return `staged (${t.changedFiles.length} files)`;
140
+ return t.mode;
141
+ }
142
+ //# sourceMappingURL=pretty.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pretty.js","sourceRoot":"","sources":["../../src/output/pretty.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export interface InitSystemPromptInput {
2
+ force: boolean;
3
+ }
4
+ export declare function buildInitSystemPrompt(_input: InitSystemPromptInput): string;