@nodii/dev 0.6.24 → 0.6.25

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/lanes.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import { execFile } from "node:child_process";
2
+ import { readFile } from "node:fs/promises";
2
3
  import { promisify } from "node:util";
3
4
  import { CliError, encode, devRequest, query, readConfig, requireFlag } from "./core.js";
4
- import { recordReviewReceipt } from "./safety.js";
5
+ import { recordReviewReceipt, reviewCommitContext } from "./safety.js";
5
6
 
6
7
  const execFileAsync = promisify(execFile);
7
8
  const via = { via: "nodii-dev" };
@@ -53,6 +54,19 @@ function evidence(flags) {
53
54
 
54
55
  const list = (value) => String(value).split(",").map((x) => x.trim()).filter(Boolean);
55
56
 
57
+ export async function reviewBody(flags, read = (path) => readFile(path, "utf8")) {
58
+ const file = requireFlag(flags, "file");
59
+ let body;
60
+ try {
61
+ body = JSON.parse(await read(file));
62
+ } catch (error) {
63
+ throw new CliError("REVIEW_FILE_INVALID", `Cannot read review JSON from ${file}: ${error.message}`, 2);
64
+ }
65
+ if (!body || typeof body !== "object" || Array.isArray(body))
66
+ throw new CliError("REVIEW_FILE_INVALID", "The review file must contain one JSON object.", 2);
67
+ return { ...body, ...via };
68
+ }
69
+
56
70
  /**
57
71
  * DEC-0011 (MW1-28.1): the body for `lane non-issue`. A re-route needs at
58
72
  * least one issue key or an explicit `none`, and the branch work is either
@@ -99,7 +113,7 @@ export async function branchCommits(lane) {
99
113
  }
100
114
  }
101
115
 
102
- export async function laneCommand(action, args, flags, { request = devRequest, config = readConfig, gitContext = inferGitContext, commitsOf = branchCommits, reviewReceipt = recordReviewReceipt } = {}) {
116
+ export async function laneCommand(action, args, flags, { request = devRequest, config = readConfig, gitContext = inferGitContext, commitsOf = branchCommits, read = (path) => readFile(path, "utf8"), reviewContext = reviewCommitContext, reviewReceipt = recordReviewReceipt } = {}) {
103
117
  if (action === "list") {
104
118
  const c = flags.mine ? await config() : null;
105
119
  const payload = await request("GET", `/api/v1/lanes${query({ kind: flags.kind, status: flags.status, machine: flags.machine, agent: flags.mine ? c.agentId : flags.agent, "waiting-on": flags["waiting-on"], stuck: flags.stuck, cursor: flags.cursor, limit: flags.limit })}`);
@@ -170,12 +184,50 @@ export async function laneCommand(action, args, flags, { request = devRequest, c
170
184
  }
171
185
  if (action === "land") return { payload: await request("POST", `/api/v1/lanes/${encode(id)}/land`, via), human: `${ref}: recorded on main.` };
172
186
  if (action === "review") {
173
- const payload = await request("POST", `/api/v1/lanes/${encode(id)}/review`, via);
187
+ const body = await reviewBody(flags, read);
188
+ const current = await reviewContext();
189
+ if (body.head !== current.commit || body.base !== current.base)
190
+ throw new CliError(
191
+ "REVIEW_FILE_STALE",
192
+ `The review file covers base ${body.base ?? "(missing)"} and head ${body.head ?? "(missing)"}, but this worktree is base ${current.base} and head ${current.commit}. Review the current diff and write a new receipt.`,
193
+ 2,
194
+ );
195
+ const payload = await request("POST", `/api/v1/lanes/${encode(id)}/review`, body);
174
196
  try { await reviewReceipt(); }
175
197
  catch (error) {
176
- throw new CliError("REVIEW_RECEIPT_FAILED", `${ref}: Dev recorded the review, but the local push receipt could not be written (${error?.message ?? error}). Re-run nodii-dev lane review ${ref} from its worktree.`, 1);
198
+ throw new CliError("REVIEW_RECEIPT_FAILED", `${ref}: Dev recorded the review, but the local push receipt could not be written (${error?.message ?? error}). Re-run nodii-dev lane review ${ref} --file REVIEW.json from its worktree.`, 1);
177
199
  }
178
- return { payload, human: `${ref}: review recorded for the current commit and origin/main.` };
200
+ return {
201
+ payload,
202
+ human: (p) => {
203
+ const findings = p.data.findings ?? [];
204
+ const missed = findings.filter((finding) => finding.missedTrigger === true).length;
205
+ return `${ref}: review recorded for the current commit and origin/main (${p.data.exitReason}; ${findings.length} finding(s), ${missed} missed trigger(s)).`;
206
+ },
207
+ };
208
+ }
209
+ if (action === "reviews") {
210
+ const payload = await request("GET", `/api/v1/lanes/${encode(id)}`);
211
+ return {
212
+ payload,
213
+ human: (p) => {
214
+ const reviews = p.data.reviews ?? [];
215
+ return reviews.length
216
+ ? reviews
217
+ .map((run) => {
218
+ const missed = run.findings.filter((finding) => finding.missedTrigger === true).length;
219
+ return [
220
+ `${run.createdAt}\t${run.head}\t${run.exitReason}\t${run.findings.length} finding(s)\t${missed} missed trigger(s)`,
221
+ ...run.findings.map(
222
+ (finding) =>
223
+ ` ${finding.severity}\t${finding.perspective}\t${finding.clauseId ?? "-"}\t${finding.summary}${finding.missedTrigger ? "\tMISSED_TRIGGER" : ""}`,
224
+ ),
225
+ ].join("\n");
226
+ })
227
+ .join("\n")
228
+ : `No reviews recorded for ${ref}.`;
229
+ },
230
+ };
179
231
  }
180
232
  if (action === "verify") {
181
233
  const value = requireFlag(flags, "dev-check");
package/dist/safety.js CHANGED
@@ -370,12 +370,19 @@ export async function safetyPostHook(payload) {
370
370
  return { recorded: false };
371
371
  }
372
372
 
373
- /** Pin the exact commit and origin/main base Dev's review transition covers. */
373
+ /** The exact commit and origin/main base a review and its local receipt cover. */
374
+ export async function reviewCommitContext(cwd = process.cwd()) {
375
+ return {
376
+ commit: await git(cwd, "rev-parse", "HEAD"),
377
+ base: await git(cwd, "rev-parse", "origin/main"),
378
+ };
379
+ }
380
+
381
+ /** Pin the exact commit and origin/main base Dev accepted for review. */
374
382
  export async function recordReviewReceipt(cwd = process.cwd()) {
375
383
  const path = await gitPrivatePath(cwd, REVIEW_RECEIPT);
376
384
  const receipt = {
377
- commit: await git(cwd, "rev-parse", "HEAD"),
378
- base: await git(cwd, "rev-parse", "origin/main"),
385
+ ...(await reviewCommitContext(cwd)),
379
386
  recordedAt: new Date().toISOString(),
380
387
  };
381
388
  await mkdir(dirname(path), { recursive: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nodii/dev",
3
- "version": "0.6.24",
3
+ "version": "0.6.25",
4
4
  "description": "Agent CLI for Nodii Dev, reached through the platform edge",
5
5
  "type": "module",
6
6
  "bin": {