co-maintainer 0.4.13 → 0.5.0-beta.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/LICENSE +21 -21
- package/README.md +57 -38
- package/dist/main.js +2 -3
- package/dist/package.json +21 -15
- package/dist/src/ai/batch.d.ts +24 -1
- package/dist/src/ai/batch.js +75 -23
- package/dist/src/ai/estimate.d.ts +40 -0
- package/dist/src/ai/estimate.js +113 -0
- package/dist/src/ai/fake.d.ts +1 -1
- package/dist/src/ai/fake.js +1 -1
- package/dist/src/ai/hetzner.js +1 -1
- package/dist/src/ai/openrouter.d.ts +13 -0
- package/dist/src/ai/openrouter.js +66 -12
- package/dist/src/ai/pricing.d.ts +14 -0
- package/dist/src/ai/pricing.js +77 -0
- package/dist/src/ai/provider.js +8 -0
- package/dist/src/ai/verify.d.ts +25 -0
- package/dist/src/ai/verify.js +78 -0
- package/dist/src/cli/args.js +25 -29
- package/dist/src/cli/commands/config.d.ts +44 -0
- package/dist/src/cli/commands/config.js +220 -0
- package/dist/src/cli/commands/probe.js +70 -103
- package/dist/src/cli/commands/registry.d.ts +57 -0
- package/dist/src/cli/commands/registry.js +713 -0
- package/dist/src/cli/commands/review.js +66 -43
- package/dist/src/cli/commands/serve.d.ts +4 -2
- package/dist/src/cli/commands/serve.js +14 -10
- package/dist/src/cli/commands/set.d.ts +2 -1
- package/dist/src/cli/commands/set.js +63 -17
- package/dist/src/cli/commands/view.d.ts +1 -0
- package/dist/src/cli/commands/view.js +180 -0
- package/dist/src/cli/error.d.ts +39 -0
- package/dist/src/cli/error.js +64 -0
- package/dist/src/cli/main.d.ts +7 -0
- package/dist/src/cli/main.js +82 -1
- package/dist/src/cli/prompt.js +21 -7
- package/dist/src/cli/review_args.d.ts +4 -0
- package/dist/src/cli/review_args.js +22 -9
- package/dist/src/cli/review_output.d.ts +2 -2
- package/dist/src/cli/review_output.js +7 -6
- package/dist/src/cli/review_result.d.ts +59 -8
- package/dist/src/cli/review_result.js +145 -103
- package/dist/src/config.d.ts +15 -0
- package/dist/src/config.js +31 -0
- package/dist/src/github/app.d.ts +13 -0
- package/dist/src/github/app.js +23 -0
- package/dist/src/github/app_manifest.d.ts +50 -0
- package/dist/src/github/app_manifest.js +138 -0
- package/dist/src/github/client.js +7 -1
- package/dist/src/github/collect.js +1 -1
- package/dist/src/github/gh.js +75 -18
- package/dist/src/knowledge/facts.js +8 -3
- package/dist/src/knowledge/guide.d.ts +8 -0
- package/dist/src/knowledge/guide.js +32 -8
- package/dist/src/knowledge/probe.js +13 -3
- package/dist/src/knowledge/sections.d.ts +9 -0
- package/dist/src/knowledge/sections.js +16 -0
- package/dist/src/knowledge/skill.d.ts +5 -0
- package/dist/src/knowledge/skill.js +68 -21
- package/dist/src/knowledge/synthesis.d.ts +17 -1
- package/dist/src/knowledge/synthesis.js +70 -15
- package/dist/src/knowledge/types.d.ts +5 -0
- package/dist/src/local/codegraph_prepare.d.ts +3 -2
- package/dist/src/local/codegraph_prepare.js +4 -2
- package/dist/src/local/git_ops.d.ts +5 -4
- package/dist/src/local/git_ops.js +8 -9
- package/dist/src/local/review_local.js +36 -14
- package/dist/src/pr/checkout.js +14 -3
- package/dist/src/pr/codegraph_tools.js +3 -3
- package/dist/src/pr/diff_summary.js +3 -3
- package/dist/src/pr/findings.js +16 -5
- package/dist/src/pr/findings_json.d.ts +26 -0
- package/dist/src/pr/findings_json.js +187 -0
- package/dist/src/pr/review_copy.d.ts +20 -0
- package/dist/src/pr/review_copy.js +40 -0
- package/dist/src/pr/reviewer.d.ts +2 -1
- package/dist/src/pr/reviewer.js +84 -91
- package/dist/src/remote/client.js +42 -42
- package/dist/src/remote/http.d.ts +5 -0
- package/dist/src/remote/http.js +49 -0
- package/dist/src/remote/server/guides.d.ts +22 -0
- package/dist/src/remote/server/guides.js +81 -0
- package/dist/src/remote/server/routes.js +24 -0
- package/dist/src/review/blocking.d.ts +35 -0
- package/dist/src/review/blocking.js +45 -0
- package/dist/src/review/carry_over.d.ts +12 -1
- package/dist/src/review/carry_over.js +35 -9
- package/dist/src/review/engine.d.ts +1 -0
- package/dist/src/review/guides.js +28 -1
- package/dist/src/server/api/installations.js +1 -1
- package/dist/src/server/api/repos.js +86 -3
- package/dist/src/server/api/settings.js +3 -3
- package/dist/src/server/app.d.ts +3 -0
- package/dist/src/server/app.js +12 -5
- package/dist/src/server/pages/activity.d.ts +1 -0
- package/dist/src/server/pages/activity.js +2 -2
- package/dist/src/server/pages/add_repo.js +73 -5
- package/dist/src/server/pages/client.d.ts +1 -1
- package/dist/src/server/pages/client.js +1 -1
- package/dist/src/server/pages/home.d.ts +6 -1
- package/dist/src/server/pages/home.js +25 -2
- package/dist/src/server/pages/knowledge.js +1 -1
- package/dist/src/server/pages/layout.js +2 -2
- package/dist/src/server/pages/repo.d.ts +1 -1
- package/dist/src/server/pages/repo.js +21 -2
- package/dist/src/server/pages/repo_prs.d.ts +5 -1
- package/dist/src/server/pages/repo_prs.js +36 -1
- package/dist/src/server/pages/repo_remote.js +2 -2
- package/dist/src/server/pages/repo_settings.js +2 -2
- package/dist/src/server/pages/router.d.ts +6 -0
- package/dist/src/server/pages/router.js +129 -9
- package/dist/src/server/pages/settings.d.ts +1 -1
- package/dist/src/server/pages/settings.js +87 -9
- package/dist/src/server/pages/styles.d.ts +1 -1
- package/dist/src/server/pages/styles.js +1 -1
- package/dist/src/services/probe.d.ts +28 -0
- package/dist/src/services/probe.js +133 -0
- package/dist/src/services/remake_cron.d.ts +1 -1
- package/dist/src/services/remake_cron.js +3 -3
- package/dist/src/services/remote_review.js +14 -4
- package/dist/src/services/review.d.ts +11 -0
- package/dist/src/services/review.js +39 -11
- package/dist/src/services/setup.js +37 -19
- package/dist/src/services/setup_checklist.d.ts +10 -0
- package/dist/src/services/setup_checklist.js +87 -0
- package/dist/src/store/app_db.js +5 -2
- package/dist/src/store/cache_db.d.ts +4 -0
- package/dist/src/store/cache_db.js +27 -4
- package/dist/src/store/deliveries.d.ts +7 -0
- package/dist/src/store/deliveries.js +15 -0
- package/dist/src/tools/codegraph.d.ts +30 -0
- package/dist/src/tools/codegraph.js +49 -12
- package/dist/src/types.d.ts +10 -0
- package/dist/src/util/log.d.ts +3 -0
- package/dist/src/util/log.js +12 -0
- package/dist/src/util/run_summary.d.ts +33 -0
- package/dist/src/util/run_summary.js +47 -0
- package/dist/src/util/webhook_reachability.d.ts +11 -0
- package/dist/src/util/webhook_reachability.js +64 -0
- package/package.json +21 -15
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/** One error shape for every CLI failure (CORE-10).
|
|
2
|
+
*
|
|
3
|
+
* The message is the first line the user reads, `hint` is an optional
|
|
4
|
+
* follow-up line printed as `Hint: ...`, and `exitCode` is the documented
|
|
5
|
+
* contract: 1 only for blocking review findings, 2 for usage and precondition
|
|
6
|
+
* errors, 3 for runtime failures. Anything thrown that is not a `CliError` is
|
|
7
|
+
* treated as a runtime failure (3), which is the safe default for the network,
|
|
8
|
+
* provider and filesystem errors the CLI does not classify yet.
|
|
9
|
+
*/
|
|
10
|
+
export const EXIT_FINDINGS = 1;
|
|
11
|
+
export const EXIT_USAGE = 2;
|
|
12
|
+
export const EXIT_RUNTIME = 3;
|
|
13
|
+
export class CliError extends Error {
|
|
14
|
+
code;
|
|
15
|
+
hint;
|
|
16
|
+
exitCode;
|
|
17
|
+
constructor(code, message, hint, exitCode = EXIT_USAGE) {
|
|
18
|
+
super(message);
|
|
19
|
+
this.name = "CliError";
|
|
20
|
+
this.code = code;
|
|
21
|
+
this.hint = hint;
|
|
22
|
+
this.exitCode = exitCode;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/** Throws a usage or precondition error. Kept as a helper so the existing
|
|
26
|
+
* `die(...)` call sites read the same as before. */
|
|
27
|
+
export function die(message, code = "usage") {
|
|
28
|
+
throw new CliError(code, message, undefined, EXIT_USAGE);
|
|
29
|
+
}
|
|
30
|
+
/** Ends the CLI with `code`, letting the event loop drain first.
|
|
31
|
+
*
|
|
32
|
+
* `process.exit` while undici's fetch connection pool is still open asserts in
|
|
33
|
+
* libuv on Windows (`Assertion failed: !(handle->flags & UV_HANDLE_CLOSING),
|
|
34
|
+
* file src\win\async.c, line 94`) and the process dies with 0xC0000409 instead
|
|
35
|
+
* of the intended code. Every provider and remote call uses fetch, so this is
|
|
36
|
+
* reachable from any error path after a request. Setting `exitCode` and letting
|
|
37
|
+
* the loop empty is what the probe confirms is crash-free and immediate (the
|
|
38
|
+
* undici pool does not keep the process alive). CORE-11. */
|
|
39
|
+
export function exitWith(code) {
|
|
40
|
+
process.exitCode = code;
|
|
41
|
+
}
|
|
42
|
+
/** Renders an error the way the CLI prints it: the message, then `Hint: ...`
|
|
43
|
+
* when there is one. Used where an error becomes a log line rather than the
|
|
44
|
+
* top-level exit (CORE-12), so a hint is not lost just because the failure was
|
|
45
|
+
* quarantined. */
|
|
46
|
+
export function formatError(error) {
|
|
47
|
+
if (error instanceof CliError) {
|
|
48
|
+
return error.hint ? `${error.message} Hint: ${error.hint}` : error.message;
|
|
49
|
+
}
|
|
50
|
+
return error instanceof Error ? error.message : String(error);
|
|
51
|
+
}
|
|
52
|
+
/** A failed `fetch` as a message that names the host and the cause (CORE-12).
|
|
53
|
+
* Node's own text is only `fetch failed`, which says neither. */
|
|
54
|
+
export function networkFailure(url, error) {
|
|
55
|
+
const code = error.cause?.code;
|
|
56
|
+
let host = url;
|
|
57
|
+
try {
|
|
58
|
+
host = new URL(url).host;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// Keep the raw value when it does not parse.
|
|
62
|
+
}
|
|
63
|
+
return new CliError("network_failed", `Could not reach ${host}${code ? `: ${code}` : "."}`, undefined, EXIT_RUNTIME);
|
|
64
|
+
}
|
package/dist/src/cli/main.d.ts
CHANGED
|
@@ -1 +1,8 @@
|
|
|
1
1
|
export declare function run(args: string[]): Promise<void>;
|
|
2
|
+
/** The single place a CLI error turns into output and an exit code (CORE-10).
|
|
3
|
+
* The message's first line and the optional `Hint:` line keep the 0.4.13
|
|
4
|
+
* shape. With `--json` the error goes to stdout as `schemaVersion 1` JSON, the
|
|
5
|
+
* same shape local review already used. CORE-11 later replaces the direct
|
|
6
|
+
* `process.exit` with `exitCode` plus handle cleanup, which is why that work is
|
|
7
|
+
* separate. */
|
|
8
|
+
export declare function reportCliError(error: unknown): void;
|
package/dist/src/cli/main.js
CHANGED
|
@@ -3,12 +3,55 @@
|
|
|
3
3
|
import { parseArgs } from "./args.js";
|
|
4
4
|
import { runServe } from "./commands/serve.js";
|
|
5
5
|
import { runSet } from "./commands/set.js";
|
|
6
|
+
import { runConfig } from "./commands/config.js";
|
|
7
|
+
import { runView } from "./commands/view.js";
|
|
6
8
|
import { runProbe } from "./commands/probe.js";
|
|
7
9
|
import { runReviewFromCli } from "./commands/review.js";
|
|
8
10
|
import { runInitOrRemake } from "../services/setup.js";
|
|
9
11
|
import { VERSION } from "../version.js";
|
|
12
|
+
import { CliError, EXIT_RUNTIME, exitWith } from "./error.js";
|
|
13
|
+
import { findCommand, renderCommandHelp, renderGlobalHelp, unknownCommandMessage, } from "./commands/registry.js";
|
|
14
|
+
/** Every command's help is rendered from the registry, so a new flag cannot
|
|
15
|
+
* drift out of it. `help <command>`, `<command> --help` and `<command> -h` all
|
|
16
|
+
* land here and all exit 0 (CORE-20). */
|
|
17
|
+
function printHelpFor(name) {
|
|
18
|
+
const help = renderCommandHelp(name);
|
|
19
|
+
console.log(help ?? renderGlobalHelp());
|
|
20
|
+
exitWith(0);
|
|
21
|
+
}
|
|
22
|
+
/** True when the help was printed and `run` should stop. */
|
|
23
|
+
function handleHelp(args) {
|
|
24
|
+
const first = args[0];
|
|
25
|
+
if (first === "help") {
|
|
26
|
+
const target = args.find((arg) => !arg.startsWith("-") && arg !== "help");
|
|
27
|
+
if (target)
|
|
28
|
+
printHelpFor(target);
|
|
29
|
+
else {
|
|
30
|
+
console.log(renderGlobalHelp());
|
|
31
|
+
exitWith(0);
|
|
32
|
+
}
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
if (first === "--help" || first === "-h") {
|
|
36
|
+
console.log(renderGlobalHelp());
|
|
37
|
+
exitWith(0);
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
// `<command> --help` without a repo: `serve --help` and `set --help` must not
|
|
41
|
+
// reach their handlers, which would start a server or demand a flag (CORE-20).
|
|
42
|
+
if (first && (args.includes("--help") || args.includes("-h"))) {
|
|
43
|
+
const spec = findCommand(first);
|
|
44
|
+
if (spec) {
|
|
45
|
+
printHelpFor(first);
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
10
51
|
export async function run(args) {
|
|
11
|
-
if (args
|
|
52
|
+
if (handleHelp(args))
|
|
53
|
+
return;
|
|
54
|
+
if (args[0] === "-v" || args[0] === "--version" || args[0] === "version") {
|
|
12
55
|
console.log(VERSION);
|
|
13
56
|
return;
|
|
14
57
|
}
|
|
@@ -16,6 +59,14 @@ export async function run(args) {
|
|
|
16
59
|
await runSet(args.slice(1));
|
|
17
60
|
return;
|
|
18
61
|
}
|
|
62
|
+
if (args[0] === "config") {
|
|
63
|
+
await runConfig(args.slice(1));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (args[0] === "view") {
|
|
67
|
+
await runView(args.slice(1));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
19
70
|
if (args[0] === "serve") {
|
|
20
71
|
await runServe(args.slice(1));
|
|
21
72
|
return;
|
|
@@ -24,9 +75,39 @@ export async function run(args) {
|
|
|
24
75
|
await runReviewFromCli(args.slice(1));
|
|
25
76
|
return;
|
|
26
77
|
}
|
|
78
|
+
// An unknown command is a usage error with a suggestion, and it must resolve
|
|
79
|
+
// through the registry so hidden aliases still work (CORE-20).
|
|
80
|
+
if (args[0] && !args[0].startsWith("-") && !findCommand(args[0])) {
|
|
81
|
+
throw new CliError("usage", unknownCommandMessage(args[0]));
|
|
82
|
+
}
|
|
27
83
|
const options = await parseArgs(args);
|
|
28
84
|
if (options.command === "probe")
|
|
29
85
|
await runProbe(options);
|
|
30
86
|
else
|
|
31
87
|
await runInitOrRemake(options);
|
|
32
88
|
}
|
|
89
|
+
/** The single place a CLI error turns into output and an exit code (CORE-10).
|
|
90
|
+
* The message's first line and the optional `Hint:` line keep the 0.4.13
|
|
91
|
+
* shape. With `--json` the error goes to stdout as `schemaVersion 1` JSON, the
|
|
92
|
+
* same shape local review already used. CORE-11 later replaces the direct
|
|
93
|
+
* `process.exit` with `exitCode` plus handle cleanup, which is why that work is
|
|
94
|
+
* separate. */
|
|
95
|
+
export function reportCliError(error) {
|
|
96
|
+
const cli = error instanceof CliError
|
|
97
|
+
? error
|
|
98
|
+
: new CliError("runtime", error instanceof Error ? error.message : String(error), undefined, EXIT_RUNTIME);
|
|
99
|
+
if (process.argv.includes("--json")) {
|
|
100
|
+
console.log(JSON.stringify({
|
|
101
|
+
schemaVersion: 1,
|
|
102
|
+
ok: false,
|
|
103
|
+
error: { code: cli.code, message: cli.message, hint: cli.hint },
|
|
104
|
+
exitCode: cli.exitCode,
|
|
105
|
+
}));
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
console.error(`[error] ${cli.message}`);
|
|
109
|
+
if (cli.hint)
|
|
110
|
+
console.error(`Hint: ${cli.hint}`);
|
|
111
|
+
}
|
|
112
|
+
exitWith(cli.exitCode);
|
|
113
|
+
}
|
package/dist/src/cli/prompt.js
CHANGED
|
@@ -4,16 +4,30 @@
|
|
|
4
4
|
* more than a couple of questions, and an interface left open keeps stdin
|
|
5
5
|
* referenced, which would hang a non-interactive pipeline. */
|
|
6
6
|
import { createInterface } from "node:readline/promises";
|
|
7
|
+
/** Asks one question and always settles.
|
|
8
|
+
*
|
|
9
|
+
* `rl.question` never settles when stdin reaches EOF before an answer, which
|
|
10
|
+
* is the F01 hang: `co-maintainer review </dev/null` awaited the answer
|
|
11
|
+
* forever, printed `Warning: Detected unsettled top-level await`, and exited
|
|
12
|
+
* 13. Racing the question against `close`/`end` and treating those as "no
|
|
13
|
+
* answer" keeps the caller's fallback path alive instead. */
|
|
7
14
|
async function ask(question) {
|
|
8
15
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
9
16
|
try {
|
|
10
|
-
return (await
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
return (await new Promise((resolve) => {
|
|
18
|
+
let settled = false;
|
|
19
|
+
const done = (value) => {
|
|
20
|
+
if (settled)
|
|
21
|
+
return;
|
|
22
|
+
settled = true;
|
|
23
|
+
resolve(value);
|
|
24
|
+
};
|
|
25
|
+
// `question` rejects when stdin closes mid-prompt (Ctrl-D, a detached
|
|
26
|
+
// pipeline, a killed parent); treat that as "no answer" too.
|
|
27
|
+
void rl.question(question).then(done, () => done(""));
|
|
28
|
+
rl.once("close", () => done(""));
|
|
29
|
+
process.stdin.once("end", () => done(""));
|
|
30
|
+
})).trim();
|
|
17
31
|
}
|
|
18
32
|
finally {
|
|
19
33
|
rl.close();
|
|
@@ -10,6 +10,10 @@ type ReviewFlags = {
|
|
|
10
10
|
branch?: string;
|
|
11
11
|
repoOverride?: string;
|
|
12
12
|
remakeBeforeReview: boolean;
|
|
13
|
+
/** `--remote-host` override for this run only (CORE-25). */
|
|
14
|
+
remoteHost?: string;
|
|
15
|
+
/** `--remote-token` override for this run only (CORE-25). */
|
|
16
|
+
remoteToken?: string;
|
|
13
17
|
};
|
|
14
18
|
export type ReviewCliArgs = ({
|
|
15
19
|
mode: "pr";
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { parseArgs, setCliInteractive } from "./args.js";
|
|
2
|
-
|
|
3
|
-
throw new Error(message);
|
|
4
|
-
}
|
|
2
|
+
import { die } from "./error.js";
|
|
5
3
|
function reviewFlags(rest) {
|
|
6
4
|
const text = (name) => rest.find((item) => item.startsWith(`--${name}=`))?.slice(name.length + 3);
|
|
5
|
+
// `--remake-before-review` is the 0.4.13 spelling; both names mean the same
|
|
6
|
+
// thing, and only the new one is documented (CORE-21).
|
|
7
|
+
const syncBeforeReview = rest.some((arg) => arg === "--sync-before-review" || arg === "--remake-before-review");
|
|
7
8
|
for (const arg of rest) {
|
|
8
9
|
if (arg === "--codegraph") {
|
|
9
10
|
die("Unknown option: --codegraph");
|
|
10
11
|
}
|
|
11
|
-
if (arg === "--remote" &&
|
|
12
|
-
|
|
13
|
-
die("--remake-before-review cannot be used with --remote");
|
|
12
|
+
if (arg === "--remote" && syncBeforeReview) {
|
|
13
|
+
die("--sync-before-review cannot be used with --remote");
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
return {
|
|
@@ -22,7 +22,9 @@ function reviewFlags(rest) {
|
|
|
22
22
|
toBranch: text("to-branch"),
|
|
23
23
|
branch: text("branch"),
|
|
24
24
|
repoOverride: text("repo"),
|
|
25
|
-
remakeBeforeReview:
|
|
25
|
+
remakeBeforeReview: syncBeforeReview,
|
|
26
|
+
remoteHost: text("remote-host"),
|
|
27
|
+
remoteToken: text("remote-token"),
|
|
26
28
|
};
|
|
27
29
|
}
|
|
28
30
|
const LOCAL_ONLY_FLAGS = new Set([
|
|
@@ -32,6 +34,7 @@ const LOCAL_ONLY_FLAGS = new Set([
|
|
|
32
34
|
"--allow-tool-install",
|
|
33
35
|
"--fresh",
|
|
34
36
|
"--remake-before-review",
|
|
37
|
+
"--sync-before-review",
|
|
35
38
|
]);
|
|
36
39
|
/** Strip local-only flags before `parseArgs` for PR-style options. */
|
|
37
40
|
export function filterReviewConfigArgs(raw) {
|
|
@@ -40,7 +43,9 @@ export function filterReviewConfigArgs(raw) {
|
|
|
40
43
|
return false;
|
|
41
44
|
if (arg.startsWith("--to-branch=") ||
|
|
42
45
|
arg.startsWith("--branch=") ||
|
|
43
|
-
arg.startsWith("--repo=")
|
|
46
|
+
arg.startsWith("--repo=") ||
|
|
47
|
+
arg.startsWith("--remote-host=") ||
|
|
48
|
+
arg.startsWith("--remote-token=")) {
|
|
44
49
|
return false;
|
|
45
50
|
}
|
|
46
51
|
return true;
|
|
@@ -50,6 +55,11 @@ export function filterReviewConfigArgs(raw) {
|
|
|
50
55
|
export async function parseReviewArgs(args) {
|
|
51
56
|
const positional = args.filter((a) => !a.startsWith("--"));
|
|
52
57
|
const flags = reviewFlags(args);
|
|
58
|
+
// `--remote-host` / `--remote-token` only mean something together with
|
|
59
|
+
// `--remote`; silently dropping them is exactly the F33 surprise (CORE-25).
|
|
60
|
+
if ((flags.remoteHost || flags.remoteToken) && !flags.remote) {
|
|
61
|
+
die("--remote-host and --remote-token require --remote");
|
|
62
|
+
}
|
|
53
63
|
// `--json` must never prompt (plan §8.7). `parseArgs` only receives the
|
|
54
64
|
// filtered args, and `--json` is stripped before it sees them, so the flag
|
|
55
65
|
// has to be applied here rather than after the parse returns.
|
|
@@ -58,6 +68,9 @@ export async function parseReviewArgs(args) {
|
|
|
58
68
|
/^[^/]+\/[^/]+$/.test(positional[0]) &&
|
|
59
69
|
/^\d+$/.test(positional[1]);
|
|
60
70
|
if (isPr) {
|
|
71
|
+
if (flags.remoteHost || flags.remoteToken) {
|
|
72
|
+
die("--remote-host is only for remote review without a PR number");
|
|
73
|
+
}
|
|
61
74
|
const options = await parseArgs([
|
|
62
75
|
"review",
|
|
63
76
|
positional[0],
|
|
@@ -67,7 +80,7 @@ export async function parseReviewArgs(args) {
|
|
|
67
80
|
if (flags.remote)
|
|
68
81
|
die("--remote is only for local review without a PR number");
|
|
69
82
|
if (flags.remakeBeforeReview) {
|
|
70
|
-
die("--
|
|
83
|
+
die("--sync-before-review is not supported for PR review");
|
|
71
84
|
}
|
|
72
85
|
return {
|
|
73
86
|
mode: "pr",
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
export declare function
|
|
1
|
+
import { type ReviewBlocking } from "../review/blocking.ts";
|
|
2
|
+
export declare function reviewExitCode(markdown: string, mode?: ReviewBlocking): number;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { parseFindings } from "../pr/findings.js";
|
|
2
|
-
import {
|
|
3
|
-
export function reviewExitCode(markdown) {
|
|
2
|
+
import { DEFAULT_REVIEW_BLOCKING, isBlocking, } from "../review/blocking.js";
|
|
3
|
+
export function reviewExitCode(markdown, mode = DEFAULT_REVIEW_BLOCKING) {
|
|
4
4
|
const findings = parseFindings(markdown);
|
|
5
|
-
const blocking = findings.some((f) =>
|
|
5
|
+
const blocking = findings.some((f) => isBlocking(mode, {
|
|
6
|
+
severity: f.severity,
|
|
7
|
+
blocked: f.blocking,
|
|
8
|
+
text: f.heading,
|
|
9
|
+
}));
|
|
6
10
|
return blocking ? 1 : 0;
|
|
7
11
|
}
|
|
8
|
-
export function printLocalReview(header, markdown) {
|
|
9
|
-
console.log(`\n${header}\n\n${humanCopy(markdown)}\n`);
|
|
10
|
-
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ParsedFinding } from "../pr/findings.ts";
|
|
2
2
|
import { type ResolvedFinding } from "../review/carry_over.ts";
|
|
3
|
+
import { type ReviewBlocking } from "../review/blocking.ts";
|
|
3
4
|
import type { Revision } from "../review/revision.ts";
|
|
4
5
|
export type ReviewWarning = {
|
|
5
6
|
code: string;
|
|
@@ -22,29 +23,77 @@ export type JsonReviewFinding = {
|
|
|
22
23
|
text: string;
|
|
23
24
|
} | null;
|
|
24
25
|
};
|
|
25
|
-
export declare function isBlockingFinding(title: string, severity: string): boolean;
|
|
26
|
+
export declare function isBlockingFinding(title: string, severity: string, mode?: ReviewBlocking): boolean;
|
|
26
27
|
export declare function resolvedFromFirstReview(parsed: ParsedFinding[], filesByPath: Map<string, {
|
|
27
28
|
patch: string;
|
|
28
29
|
}>): ResolvedFinding[];
|
|
29
30
|
export declare function sortResolvedFindings(findings: ResolvedFinding[]): ResolvedFinding[];
|
|
30
|
-
export declare function toJsonFinding(row: ResolvedFinding): JsonReviewFinding;
|
|
31
|
+
export declare function toJsonFinding(row: ResolvedFinding, mode?: ReviewBlocking): JsonReviewFinding;
|
|
31
32
|
export declare function revisionStats(revision: Revision): {
|
|
32
33
|
files: number;
|
|
33
34
|
additions: number;
|
|
34
35
|
deletions: number;
|
|
35
36
|
truncated: boolean;
|
|
36
37
|
};
|
|
37
|
-
export declare function summaryCounts(findings: ResolvedFinding[]): {
|
|
38
|
+
export declare function summaryCounts(findings: ResolvedFinding[], mode?: ReviewBlocking): {
|
|
38
39
|
new: number;
|
|
39
40
|
open: number;
|
|
40
41
|
closed: number;
|
|
41
42
|
blocking: number;
|
|
42
43
|
};
|
|
43
|
-
export declare function reviewExitCodeFromResolved(findings: ResolvedFinding[]): number;
|
|
44
|
-
/**
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
export
|
|
44
|
+
export declare function reviewExitCodeFromResolved(findings: ResolvedFinding[], mode?: ReviewBlocking): number;
|
|
45
|
+
/** One finding as the human renderer prints it. Local, PR and remote reviews
|
|
46
|
+
* all normalize into this shape before printing, so the three modes cannot
|
|
47
|
+
* drift into different products (CORE-43 / F21, F22). */
|
|
48
|
+
export type HumanFinding = {
|
|
49
|
+
state: "new" | "open" | "closed";
|
|
50
|
+
severity: string;
|
|
51
|
+
blocking: boolean;
|
|
52
|
+
path: string | null;
|
|
53
|
+
lineFrom: number | null;
|
|
54
|
+
lineTo: number | null;
|
|
55
|
+
title: string;
|
|
56
|
+
body: string;
|
|
57
|
+
suggestion: {
|
|
58
|
+
lineFrom: number;
|
|
59
|
+
lineTo: number;
|
|
60
|
+
text: string;
|
|
61
|
+
} | null;
|
|
62
|
+
};
|
|
63
|
+
/** Local and PR findings: blocking is decided here from the configured rule,
|
|
64
|
+
* exactly as the exit code decides it (CORE-41). */
|
|
65
|
+
export declare function humanFindingsFromResolved(findings: ResolvedFinding[], mode?: ReviewBlocking): HumanFinding[];
|
|
66
|
+
/** Remote findings already carry the server's blocking decision, so the label
|
|
67
|
+
* uses that field rather than re-deriving the rule on a client that may not
|
|
68
|
+
* share the server's config (CORE-41). */
|
|
69
|
+
export declare function humanFindingsFromJson(findings: JsonReviewFinding[]): HumanFinding[];
|
|
70
|
+
export type HumanReviewInput = {
|
|
71
|
+
/** Header line: repo, subject and mode. */
|
|
72
|
+
title: string;
|
|
73
|
+
/** Optional `N files · +a −b` fragment, present for a local review. */
|
|
74
|
+
stats?: string;
|
|
75
|
+
guideBuiltAt: string | null;
|
|
76
|
+
/** `null` when the run cannot say (a server older than 0.5.0 sends no
|
|
77
|
+
* codegraph block). A wrong "used" is what F23 was about, so unknown is
|
|
78
|
+
* reported as unknown rather than guessed. */
|
|
79
|
+
codegraphState: "used" | "disabled" | "unavailable" | null;
|
|
80
|
+
findings: HumanFinding[];
|
|
81
|
+
warnings?: ReviewWarning[];
|
|
82
|
+
};
|
|
83
|
+
/** The single human-readable review format (CORE-43 / F21, F22, F23). Local,
|
|
84
|
+
* PR and remote reviews print this: a header naming the guide build date and
|
|
85
|
+
* the codegraph state, the findings grouped by state, then one summary line.
|
|
86
|
+
*
|
|
87
|
+
* The severity legend and the "ask for more detail" sentence are deliberately
|
|
88
|
+
* absent: a terminal has nobody to ask, and the legend is fixed noise. Both
|
|
89
|
+
* stay in the GitHub comment, which is a different reader. */
|
|
90
|
+
export declare function formatHumanReview(input: HumanReviewInput): string;
|
|
91
|
+
/** Local review output: the shared format plus the working-tree file stats. */
|
|
92
|
+
export declare function formatHumanLocalReview(header: string, revision: Revision, guideBuiltAt: string | null, codegraphState: "used" | "disabled" | "unavailable", findings: ResolvedFinding[], warnings: ReviewWarning[], mode?: ReviewBlocking): string;
|
|
93
|
+
/** The exit code for a remote review (CORE-41). The server already decided
|
|
94
|
+
* each finding's `blocking` under its own configured rule, so the client
|
|
95
|
+
* trusts that field instead of re-deriving the rule from a title: the client
|
|
96
|
+
* may not even have the same config as the server. */
|
|
48
97
|
export declare function reviewExitCodeFromJsonFindings(findings: JsonReviewFinding[]): number;
|
|
49
98
|
export type LocalReviewJsonInput = {
|
|
50
99
|
repo: string;
|
|
@@ -62,6 +111,7 @@ export type LocalReviewJsonInput = {
|
|
|
62
111
|
costUsd: number | null;
|
|
63
112
|
};
|
|
64
113
|
durationMs: number;
|
|
114
|
+
reviewBlocking?: ReviewBlocking;
|
|
65
115
|
};
|
|
66
116
|
export type PrReviewJsonInput = {
|
|
67
117
|
repo: string;
|
|
@@ -76,6 +126,7 @@ export type PrReviewJsonInput = {
|
|
|
76
126
|
costUsd: number | null;
|
|
77
127
|
};
|
|
78
128
|
durationMs: number;
|
|
129
|
+
reviewBlocking?: ReviewBlocking;
|
|
79
130
|
};
|
|
80
131
|
export declare function buildPrReviewJson(input: PrReviewJsonInput): string;
|
|
81
132
|
export declare function buildLocalReviewJson(input: LocalReviewJsonInput): string;
|