nerdsystem 0.0.24 → 0.0.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +13 -3
- package/dist/commands/claude-md-history.d.ts +9 -0
- package/dist/commands/claude-md-history.js +18 -0
- package/dist/commands/deploy.d.ts +6 -0
- package/dist/commands/deploy.js +25 -7
- package/dist/commands/init.js +1 -1
- package/dist/commands/platform-review.d.ts +11 -3
- package/dist/commands/platform-review.js +21 -11
- package/dist/commands/session-review.d.ts +116 -0
- package/dist/commands/session-review.js +268 -0
- package/dist/commands/status.d.ts +3 -2
- package/dist/commands/status.js +15 -10
- package/dist/commands/upgrade.js +10 -2
- package/dist/commands/verify.js +2 -1
- package/dist/gates/design.js +3 -0
- package/dist/gates/review.d.ts +6 -4
- package/dist/gates/review.js +26 -11
- package/dist/gates/rules.js +1 -0
- package/dist/gates/text.d.ts +7 -0
- package/dist/gates/text.js +11 -0
- package/dist/gates/versions.js +3 -1
- package/dist/util.d.ts +2 -0
- package/dist/util.js +8 -0
- package/package.json +1 -1
- package/template/.claude/agents/nerd-reviewer.md +28 -0
- package/template/.claude/skills/nerdsystem/SKILL.md +16 -3
- package/template/.cursor/rules/nerdsystem.mdc +17 -4
- package/template/.github/copilot-instructions.md +17 -4
- package/template/AGENTS.md +17 -4
- package/template/CLAUDE.md +2 -1
- package/template/GEMINI.md +17 -4
package/dist/cli.js
CHANGED
|
@@ -9,6 +9,7 @@ import { cron, health, rollback, secretList, secretRemove, secretSet, transfer,
|
|
|
9
9
|
import { adminHelp, adminSuspend, adminUnsuspend } from "./commands/admin.js";
|
|
10
10
|
import { agentsSync } from "./commands/agents.js";
|
|
11
11
|
import { upgrade } from "./commands/upgrade.js";
|
|
12
|
+
import { reviewChecklist, reviewReport } from "./commands/session-review.js";
|
|
12
13
|
import { review, status } from "./commands/status.js";
|
|
13
14
|
import { billingPortal, billingShow, billingUpgrade } from "./commands/billing.js";
|
|
14
15
|
import { auditExport, clientsCreate, clientsList, clientsRelease, domainsAdd, domainsList, domainsRemove, logs, membersInvite, membersList, membersRemove } from "./commands/org.js";
|
|
@@ -61,7 +62,9 @@ const HELP = `nerd: start, check, and deploy a web app on nerdsystem
|
|
|
61
62
|
nerd agents sync Regenerate AGENTS.md, Cursor, Copilot, Gemini files from the skill
|
|
62
63
|
nerd upgrade Bring this app up to the current SDK, agent instructions, and test harness
|
|
63
64
|
nerd status What the platform has found for this app that needs attention (updates, review findings, vulnerable packages)
|
|
64
|
-
nerd review
|
|
65
|
+
nerd review What is open from the latest review of this app, with the fix for each
|
|
66
|
+
nerd review checklist What to look for when reviewing this app's code before a deploy, and which files changed
|
|
67
|
+
nerd review report [--agent name] Send the review written to .nerd/review.json (see the checklist for the format)
|
|
65
68
|
nerd billing [--org slug] Plan, limits, and usage for the org
|
|
66
69
|
nerd billing upgrade --plan pro|studio [--annual] Start checkout
|
|
67
70
|
nerd billing portal Manage cards, invoices, and plan
|
|
@@ -143,8 +146,15 @@ export async function main(argv) {
|
|
|
143
146
|
return status({ json: Boolean(values.json), hook: Boolean(values.hook) });
|
|
144
147
|
}
|
|
145
148
|
case "review": {
|
|
146
|
-
const { values } =
|
|
147
|
-
|
|
149
|
+
const { values, positionals } = parseArgs({ args: rest, options: { json: BOOL, file: STR, agent: STR }, allowPositionals: true });
|
|
150
|
+
const json = Boolean(values.json);
|
|
151
|
+
if (positionals[0] === "checklist")
|
|
152
|
+
return reviewChecklist({ json });
|
|
153
|
+
if (positionals[0] === "report")
|
|
154
|
+
return reviewReport({ json, ...(values.file ? { file: String(values.file) } : {}), ...(values.agent ? { agent: String(values.agent) } : {}) });
|
|
155
|
+
if (positionals.length)
|
|
156
|
+
throw new CliError("Usage: nerd review | nerd review checklist | nerd review report [--file path] [--agent name]");
|
|
157
|
+
return review({ json });
|
|
148
158
|
}
|
|
149
159
|
case "upgrade": {
|
|
150
160
|
const { values } = parse({}, false);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The sha256 of every version of the starter app's CLAUDE.md this project has shipped (line endings as LF).
|
|
3
|
+
* `nerd upgrade` uses it to tell a CLAUDE.md that is still exactly the platform's text, which it replaces
|
|
4
|
+
* without ceremony, from one a person or an agent has added to, which it keeps beside the new one.
|
|
5
|
+
*
|
|
6
|
+
* When packages/template/CLAUDE.md changes, add the new hash here; a test fails until it is listed.
|
|
7
|
+
* To print it: `tr -d '\\r' < packages/template/CLAUDE.md | sha256sum`.
|
|
8
|
+
*/
|
|
9
|
+
export declare const KNOWN_CLAUDE_MD: Set<string>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The sha256 of every version of the starter app's CLAUDE.md this project has shipped (line endings as LF).
|
|
3
|
+
* `nerd upgrade` uses it to tell a CLAUDE.md that is still exactly the platform's text, which it replaces
|
|
4
|
+
* without ceremony, from one a person or an agent has added to, which it keeps beside the new one.
|
|
5
|
+
*
|
|
6
|
+
* When packages/template/CLAUDE.md changes, add the new hash here; a test fails until it is listed.
|
|
7
|
+
* To print it: `tr -d '\\r' < packages/template/CLAUDE.md | sha256sum`.
|
|
8
|
+
*/
|
|
9
|
+
export const KNOWN_CLAUDE_MD = new Set([
|
|
10
|
+
"02eb1063cb99fca0106b0a3052872a7839782564d285527ac74a08b1ca19b9ed",
|
|
11
|
+
"30b969614ba066a6faa930d35df67da48c3a692aba66ce009543e7c0d58114ba",
|
|
12
|
+
"31b9481c380810c87dff5e6a0812274cfb7068a35e11941867ad5bd4b2dc8dbf",
|
|
13
|
+
"440751d827c9f112f584157ea08495b56c8ff111024cc52177e5aa98b07622b8",
|
|
14
|
+
"4dc0c5234ad2115e1b932396db2a6efb0329bd93cb4f8ddc9968d978ddd48389",
|
|
15
|
+
"65f97940d79e9d657bc117d1dbbf13f21e84c578301b20b13c726e00739a305b",
|
|
16
|
+
"6f21b6478d50bd599bc8833068ffa82bc7ad5434439636a81b5b35d9d8554947",
|
|
17
|
+
"887cebefb74b2187ccaea027a40074d0cd163c3e6ed6fcdb0edbcbe766c0ccdd",
|
|
18
|
+
]);
|
|
@@ -11,6 +11,12 @@ export interface BuildOutput {
|
|
|
11
11
|
compatibilityFlags: string[];
|
|
12
12
|
}
|
|
13
13
|
/** Locates the Vite build output: dist/<worker>/wrangler.json names the entry and the assets dir. */
|
|
14
|
+
/**
|
|
15
|
+
* The Vite plugin copies `.dev.vars` next to the built Worker so a local preview can read it. A deploy
|
|
16
|
+
* never uploads it, and it is rewritten by every build, so the copy is removed here instead of being
|
|
17
|
+
* reported on every run with nothing for anyone to do about it. The file in the app's own folder stays.
|
|
18
|
+
*/
|
|
19
|
+
export declare function dropLocalSecretCopies(build: BuildOutput): void;
|
|
14
20
|
export declare function readBuildOutput(root: string): BuildOutput;
|
|
15
21
|
export declare function applyRemoteMigrations(root: string, api: ApiClient): Promise<{
|
|
16
22
|
applied: string[];
|
package/dist/commands/deploy.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
|
-
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { existsSync, readdirSync, readFileSync, rmSync, statSync } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { ApiClient } from "../api.js";
|
|
5
5
|
import { findProjectRoot, projectPath, readDevVars, readNerdJson } from "../project.js";
|
|
@@ -8,6 +8,7 @@ import { CliError, log, printJson, runOrThrow, table, dashboardUrl, ensureDeps }
|
|
|
8
8
|
import { runChecks } from "./check.js";
|
|
9
9
|
import { appendLineage, cliVersion, LOCAL_ONLY_NOTE, packProject, readLineage, uploadSnapshot, uploadStopMessage } from "./source.js";
|
|
10
10
|
import { lockedGetPaths } from "../gates/tests.js";
|
|
11
|
+
import { ENV_FILES } from "../gates/text.js";
|
|
11
12
|
import { platformScan } from "./platform-scan.js";
|
|
12
13
|
import { platformReview, printReview } from "./platform-review.js";
|
|
13
14
|
import { BUILD_GATES, gateSummary, knownSecretsFrom, probeDeployment, runGates, skippedProbe, SOURCE_GATES, waitForDeployment } from "../gates/index.js";
|
|
@@ -24,6 +25,18 @@ function walk(dir, base = dir) {
|
|
|
24
25
|
return out;
|
|
25
26
|
}
|
|
26
27
|
/** Locates the Vite build output: dist/<worker>/wrangler.json names the entry and the assets dir. */
|
|
28
|
+
/**
|
|
29
|
+
* The Vite plugin copies `.dev.vars` next to the built Worker so a local preview can read it. A deploy
|
|
30
|
+
* never uploads it, and it is rewritten by every build, so the copy is removed here instead of being
|
|
31
|
+
* reported on every run with nothing for anyone to do about it. The file in the app's own folder stays.
|
|
32
|
+
*/
|
|
33
|
+
export function dropLocalSecretCopies(build) {
|
|
34
|
+
if (!existsSync(build.workerDir))
|
|
35
|
+
return;
|
|
36
|
+
for (const name of readdirSync(build.workerDir))
|
|
37
|
+
if (ENV_FILES.test(name))
|
|
38
|
+
rmSync(path.join(build.workerDir, name), { force: true });
|
|
39
|
+
}
|
|
27
40
|
export function readBuildOutput(root) {
|
|
28
41
|
const dist = path.join(root, "dist");
|
|
29
42
|
if (!existsSync(dist))
|
|
@@ -76,10 +89,11 @@ async function probeAfterDeploy(root, deployment, url, skip) {
|
|
|
76
89
|
return skippedProbe("the app had not started answering within 30 seconds; open the URL to check, or run nerd deploy again to probe it");
|
|
77
90
|
return probeDeployment(url, undefined, { lockedGets: lockedGetPaths(root) });
|
|
78
91
|
}
|
|
79
|
-
function printProbe(url, probes) {
|
|
92
|
+
function printProbe(url, probes, ownDomain) {
|
|
80
93
|
if (probes.status === "skip")
|
|
81
94
|
return log.info(`Live URL not probed: ${probes.summary}`);
|
|
82
|
-
|
|
95
|
+
// With the owner's domain on the app, the address above is theirs and this one is not: say why it is the one tried.
|
|
96
|
+
const line = `Probed ${url}${ownDomain ? " (the app's platform address, which keeps answering whatever happens to the domain's DNS)" : ""}: ${probes.status === "pass" ? "all probes passed" : probes.summary}`;
|
|
83
97
|
if (probes.status === "pass")
|
|
84
98
|
log.ok(line);
|
|
85
99
|
else
|
|
@@ -224,18 +238,19 @@ export async function deploy(opts) {
|
|
|
224
238
|
await runOrThrow("npx", ["vite", "build"], { cwd: root, stdoutToStderr: true });
|
|
225
239
|
}
|
|
226
240
|
const build = readBuildOutput(root);
|
|
241
|
+
dropLocalSecretCopies(build);
|
|
227
242
|
const built = await runGates(BUILD_GATES, { root, build, knownSecrets, log: (l) => log.info(l) }, { stopOnFail: true });
|
|
228
243
|
if (!built.ok)
|
|
229
244
|
throw new CliError(`Blocked by: ${built.failed.map((g) => g.title).join(", ")}.`, 2);
|
|
230
245
|
const r = await shipBuild({ root, nerd, api, build, results: [...gates.results, ...built.results], opts });
|
|
231
|
-
// The app is live at this point. The
|
|
246
|
+
// The app is live at this point. The platform says what the review on file holds, and now and then reads the code itself.
|
|
232
247
|
const reviewPage = `${dashboardUrl(api.apiUrl)}/app/${nerd.org}/projects/${nerd.project}/review`;
|
|
233
248
|
const wantReview = r.deployment.status === "live" && Boolean(r.source);
|
|
234
249
|
if (opts.json) {
|
|
235
250
|
const review = wantReview ? await platformReview(api, `${projectPath(nerd)}/sources/${r.source.id}`, reviewPage) : null;
|
|
236
251
|
return printJson({ deployment: r.deployment, url: r.url, customUrl: r.customUrl, migrations: r.migrations, probes: r.probes, source: r.source, sourceSkipped: r.sourceSkipped, lineage: r.lineage.passed, warnings: r.warnings, review });
|
|
237
252
|
}
|
|
238
|
-
printProbe(r.url, r.probes);
|
|
253
|
+
printProbe(r.url, r.probes, Boolean(r.customUrl));
|
|
239
254
|
if (r.deployment.status === "live") {
|
|
240
255
|
if (r.deployment.sourceId)
|
|
241
256
|
log.ok(sourceLine(r));
|
|
@@ -243,10 +258,13 @@ export async function deploy(opts) {
|
|
|
243
258
|
log.warn(sourceLine(r));
|
|
244
259
|
}
|
|
245
260
|
if (wantReview) {
|
|
246
|
-
log.step("Your app is live.
|
|
261
|
+
log.step("Your app is live. Checking in on its review (now and then nerdsystem reads the code itself, which takes a minute or two; safe to stop waiting)");
|
|
247
262
|
printReview(await platformReview(api, `${projectPath(nerd)}/sources/${r.source.id}`, reviewPage));
|
|
248
263
|
}
|
|
249
|
-
|
|
264
|
+
// Said once, after the app's first deploy, which is when it is news.
|
|
265
|
+
const earlier = await api.get(projectPath(nerd, "/deployments?limit=2")).catch(() => null);
|
|
266
|
+
if (earlier && earlier.deployments.length <= 1)
|
|
267
|
+
log.info(`First time? Sign up on your app at ${r.customUrl ?? r.url}, then make yourself admin in the dashboard's Auth tab: ${dashboardUrl(api.apiUrl)}/app/${nerd.org}/projects/${nerd.project}/auth`);
|
|
250
268
|
}
|
|
251
269
|
export async function deployments(opts) {
|
|
252
270
|
const nerd = readNerdJson(findProjectRoot());
|
package/dist/commands/init.js
CHANGED
|
@@ -192,4 +192,4 @@ export async function init(dir, args) {
|
|
|
192
192
|
log.ok(`Ready. Next:\n cd ${dir}\n pnpm install\n nerd dev\n\nThen open the folder in your coding agent and ask it to build the first feature; the nerdsystem skill walks it through.\nDeploys go to ${project.url}`);
|
|
193
193
|
}
|
|
194
194
|
// Kept in sync with packages/sdk/package.json by the release script.
|
|
195
|
-
const SDK_VERSION = "0.0.
|
|
195
|
+
const SDK_VERSION = "0.0.26";
|
|
@@ -12,16 +12,24 @@ export type ReviewOutcome = {
|
|
|
12
12
|
status: "done" | "declined";
|
|
13
13
|
findings: ReviewFindingOut[];
|
|
14
14
|
dashboard: string;
|
|
15
|
+
/** Whether the platform read the code itself just now. False means these are the findings already on file. */
|
|
16
|
+
platformRead: boolean;
|
|
17
|
+
/** Whose review the findings on file come from. */
|
|
18
|
+
kind: "platform" | "session";
|
|
15
19
|
} | {
|
|
16
20
|
status: "skipped";
|
|
17
21
|
reason: string;
|
|
18
22
|
};
|
|
19
23
|
/**
|
|
20
|
-
* After a deploy is live,
|
|
24
|
+
* After a deploy is live, checks in with the platform about the review of the stored code.
|
|
25
|
+
*
|
|
26
|
+
* The platform reads the code itself only now and then (an app's first deploy, and about weekly on paid
|
|
27
|
+
* plans when the code has changed), which takes a minute or two; otherwise it answers at once with the
|
|
28
|
+
* review on file, which is usually the one this app's agent reported with `nerd review report`.
|
|
21
29
|
*
|
|
22
30
|
* Only the findings come back into the terminal, and only as a rule id with a file and a line: the words
|
|
23
|
-
* printed are this CLI's own catalog, looked up by id, so nothing
|
|
24
|
-
* the agent reading this output. The
|
|
31
|
+
* printed are this CLI's own catalog, looked up by id, so nothing a model wrote is ever put in front of
|
|
32
|
+
* the agent reading this output. The plain-language summary is for the person and stays in the
|
|
25
33
|
* dashboard. Any failure, limit, or missing configuration is a skip with a reason; a review never changes
|
|
26
34
|
* whether the deploy succeeded.
|
|
27
35
|
*/
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { RULES } from "../gates/rules.js";
|
|
2
2
|
import { CliError, log } from "../util.js";
|
|
3
3
|
/**
|
|
4
|
-
* After a deploy is live,
|
|
4
|
+
* After a deploy is live, checks in with the platform about the review of the stored code.
|
|
5
|
+
*
|
|
6
|
+
* The platform reads the code itself only now and then (an app's first deploy, and about weekly on paid
|
|
7
|
+
* plans when the code has changed), which takes a minute or two; otherwise it answers at once with the
|
|
8
|
+
* review on file, which is usually the one this app's agent reported with `nerd review report`.
|
|
5
9
|
*
|
|
6
10
|
* Only the findings come back into the terminal, and only as a rule id with a file and a line: the words
|
|
7
|
-
* printed are this CLI's own catalog, looked up by id, so nothing
|
|
8
|
-
* the agent reading this output. The
|
|
11
|
+
* printed are this CLI's own catalog, looked up by id, so nothing a model wrote is ever put in front of
|
|
12
|
+
* the agent reading this output. The plain-language summary is for the person and stays in the
|
|
9
13
|
* dashboard. Any failure, limit, or missing configuration is a skip with a reason; a review never changes
|
|
10
14
|
* whether the deploy succeeded.
|
|
11
15
|
*/
|
|
@@ -13,18 +17,23 @@ export async function platformReview(api, sourcePath, dashboard) {
|
|
|
13
17
|
if (process.env.NERD_NO_REVIEW)
|
|
14
18
|
return { status: "skipped", reason: "turned off with NERD_NO_REVIEW" };
|
|
15
19
|
let review;
|
|
20
|
+
let platformRead = true;
|
|
16
21
|
try {
|
|
17
|
-
|
|
22
|
+
const got = await api.post(`${sourcePath}/review`);
|
|
23
|
+
review = got.review;
|
|
24
|
+
// A platform that does not say is an older one, which read the code on every call.
|
|
25
|
+
platformRead = got.read ? got.read.ran : true;
|
|
18
26
|
}
|
|
19
27
|
catch (err) {
|
|
20
28
|
const status = err instanceof CliError ? err.status : undefined;
|
|
21
29
|
// The 402's wording is the platform's own (plan and allowance), safe to repeat.
|
|
22
30
|
if (status === 402 && err instanceof Error)
|
|
23
31
|
return { status: "skipped", reason: err.message.replace(/\s*\[[^\]]*\]\s*$/, "") };
|
|
24
|
-
return { status: "skipped", reason: status === 503 ? "the
|
|
32
|
+
return { status: "skipped", reason: status === 503 ? "the platform's own review is not available here yet" : "the review could not be completed this time" };
|
|
25
33
|
}
|
|
34
|
+
const kind = review.kind === "session" ? "session" : "platform";
|
|
26
35
|
if (review.status !== "done")
|
|
27
|
-
return { status: "declined", findings: [], dashboard };
|
|
36
|
+
return { status: "declined", findings: [], dashboard, platformRead, kind };
|
|
28
37
|
const findings = [];
|
|
29
38
|
for (const f of review.findings ?? []) {
|
|
30
39
|
const rule = typeof f.rule_id === "string" && f.rule_id.startsWith("review/") ? RULES[f.rule_id] : undefined;
|
|
@@ -33,22 +42,23 @@ export async function platformReview(api, sourcePath, dashboard) {
|
|
|
33
42
|
continue;
|
|
34
43
|
findings.push({ rule_id: f.rule_id, severity: rule.severity, audience: rule.audience, file: f.file, line: f.line, risk: rule.risk, fix: rule.fix });
|
|
35
44
|
}
|
|
36
|
-
return { status: "done", findings, dashboard };
|
|
45
|
+
return { status: "done", findings, dashboard, platformRead, kind };
|
|
37
46
|
}
|
|
38
47
|
export function printReview(r) {
|
|
39
48
|
if (r.status === "skipped") {
|
|
40
|
-
log.info(`
|
|
49
|
+
log.info(`Review by nerdsystem: not run (${r.reason}). Your own review before each deploy (\`nerd review checklist\`) is not affected.`);
|
|
41
50
|
return;
|
|
42
51
|
}
|
|
43
52
|
if (r.status === "declined") {
|
|
44
|
-
log.info("
|
|
53
|
+
log.info("Review by nerdsystem: the reviewer gave no answer for this app.");
|
|
45
54
|
return;
|
|
46
55
|
}
|
|
56
|
+
const whose = r.platformRead ? "nerdsystem read the code itself after this deploy" : r.kind === "session" ? "from the review this app's agent last reported" : "from nerdsystem's last own read of the code";
|
|
47
57
|
if (!r.findings.length) {
|
|
48
|
-
log.ok(`
|
|
58
|
+
log.ok(`Review: nothing open (${whose}). A plain-language summary for the person is at ${r.dashboard}`);
|
|
49
59
|
return;
|
|
50
60
|
}
|
|
51
|
-
log.warn(`
|
|
61
|
+
log.warn(`Review: ${r.findings.length} thing(s) open (${whose}). A review is a second opinion and can be wrong, so check each one against the code before changing anything. Say what you did about them in the next \`nerd review report\`.`);
|
|
52
62
|
for (const f of r.findings) {
|
|
53
63
|
log.info(` ${f.severity === "info" ? "·" : "!"} ${f.rule_id} ${f.file}:${f.line}${f.audience === "human" ? " (for the person to decide)" : ""}`);
|
|
54
64
|
log.info(` ${f.risk}`);
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The session review: the app's coding agent reads its own changes against the platform's `review/`
|
|
3
|
+
* catalog before a deploy, and reports what it found.
|
|
4
|
+
*
|
|
5
|
+
* `nerd review checklist` prints what to look for, which files changed since the last report from this
|
|
6
|
+
* folder, the findings still open from earlier reviews, and the report's format. `nerd review report`
|
|
7
|
+
* stores a snapshot of the code as reviewed and sends the report with it. The platform checks every
|
|
8
|
+
* finding against that stored code and keeps the report as the agent's own word; it does its own
|
|
9
|
+
* independent read only now and then, and compares the two.
|
|
10
|
+
*
|
|
11
|
+
* What changed is worked out on this computer, from a list of file hashes kept in `.nerd/reviewed.json`
|
|
12
|
+
* at the last report. A fresh folder has no list, so its first review covers everything.
|
|
13
|
+
*/
|
|
14
|
+
/** Where the agent writes its report for `nerd review report` to send. */
|
|
15
|
+
export declare const REPORT_FILE = ".nerd/review.json";
|
|
16
|
+
export declare const REVIEW_RULES: string[];
|
|
17
|
+
type Hashes = Record<string, string>;
|
|
18
|
+
/** Every file a review reads in this folder, with a hash of its contents. */
|
|
19
|
+
export declare function reviewFiles(root: string): Hashes;
|
|
20
|
+
/** Records the code as it stands as reviewed. */
|
|
21
|
+
export declare function writeReviewed(root: string, files: Hashes): void;
|
|
22
|
+
export interface ReviewDue {
|
|
23
|
+
/** Whether code a review reads has changed since the last report from this folder (styles alone do not count). */
|
|
24
|
+
due: boolean;
|
|
25
|
+
/** No report has been made from this folder: everything is unreviewed as far as this computer knows. */
|
|
26
|
+
first: boolean;
|
|
27
|
+
/** Files to read: the ones added or changed since the last report, or all of them the first time. */
|
|
28
|
+
changed: string[];
|
|
29
|
+
}
|
|
30
|
+
export declare function reviewDue(root: string): ReviewDue;
|
|
31
|
+
interface OpenFinding {
|
|
32
|
+
rule_id: string;
|
|
33
|
+
file: string;
|
|
34
|
+
line: number;
|
|
35
|
+
origin: "platform" | "session";
|
|
36
|
+
/** Recorded earlier as real and deliberately left as it is. */
|
|
37
|
+
leftAsIs: boolean;
|
|
38
|
+
}
|
|
39
|
+
declare const FORMAT: {
|
|
40
|
+
summary: string;
|
|
41
|
+
does: string[];
|
|
42
|
+
missing: string[];
|
|
43
|
+
findings: {
|
|
44
|
+
rule: string;
|
|
45
|
+
file: string;
|
|
46
|
+
line: number;
|
|
47
|
+
}[];
|
|
48
|
+
leftAsIs: {
|
|
49
|
+
rule: string;
|
|
50
|
+
file: string;
|
|
51
|
+
line: number;
|
|
52
|
+
}[];
|
|
53
|
+
fixed: {
|
|
54
|
+
rule: string;
|
|
55
|
+
file: string;
|
|
56
|
+
}[];
|
|
57
|
+
dismissed: {
|
|
58
|
+
rule: string;
|
|
59
|
+
file: string;
|
|
60
|
+
}[];
|
|
61
|
+
};
|
|
62
|
+
export interface Checklist {
|
|
63
|
+
app: string;
|
|
64
|
+
first: boolean;
|
|
65
|
+
changed: string[];
|
|
66
|
+
open: OpenFinding[];
|
|
67
|
+
rules: {
|
|
68
|
+
rule_id: string;
|
|
69
|
+
title: string;
|
|
70
|
+
audience: "agent" | "human";
|
|
71
|
+
lookFor: string;
|
|
72
|
+
fix: string;
|
|
73
|
+
}[];
|
|
74
|
+
reportFile: string;
|
|
75
|
+
format: typeof FORMAT;
|
|
76
|
+
}
|
|
77
|
+
export declare function checklistText(c: Checklist): string;
|
|
78
|
+
/** `nerd review checklist`: what the agent reads before it reviews. */
|
|
79
|
+
export declare function reviewChecklist(opts: {
|
|
80
|
+
json: boolean;
|
|
81
|
+
}): Promise<void>;
|
|
82
|
+
export interface Report {
|
|
83
|
+
findings: {
|
|
84
|
+
rule: string;
|
|
85
|
+
file: string;
|
|
86
|
+
line: number;
|
|
87
|
+
}[];
|
|
88
|
+
leftAsIs: {
|
|
89
|
+
rule: string;
|
|
90
|
+
file: string;
|
|
91
|
+
line: number;
|
|
92
|
+
}[];
|
|
93
|
+
fixed: {
|
|
94
|
+
rule: string;
|
|
95
|
+
file: string;
|
|
96
|
+
}[];
|
|
97
|
+
dismissed: {
|
|
98
|
+
rule: string;
|
|
99
|
+
file: string;
|
|
100
|
+
}[];
|
|
101
|
+
summary?: string;
|
|
102
|
+
does?: string[];
|
|
103
|
+
missing?: string[];
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Reads the agent's report strictly, so a slip is corrected here with a sentence that says what to change,
|
|
107
|
+
* instead of being dropped without a word by the platform. `files` is what a review reads in this folder.
|
|
108
|
+
*/
|
|
109
|
+
export declare function parseReport(text: string, files: Hashes, lineCount: (file: string) => number): Report;
|
|
110
|
+
/** `nerd review report`: store the code as reviewed, and send the agent's report with it. */
|
|
111
|
+
export declare function reviewReport(opts: {
|
|
112
|
+
json: boolean;
|
|
113
|
+
file?: string;
|
|
114
|
+
agent?: string;
|
|
115
|
+
}): Promise<void>;
|
|
116
|
+
export {};
|