@nodii/dev 0.6.42 → 0.6.44

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/README.md CHANGED
@@ -357,6 +357,9 @@ nodii-dev lane step KEY --side backend|frontend --start|--done|--block STEP [--r
357
357
  # --reason is kept as a lane note; on a step already blocked,
358
358
  # --block --reason records the new reason (Dev issue 125 moves it onto the step)
359
359
  nodii-dev lane evidence KEY --pr|--commit|--figma|--deploy|--dev-check REF
360
+ nodii-dev lane done-check KEY --evidence TEXT|URL
361
+ # the lane's done check is met (NODII-DEV-CLI-4); a launched
362
+ # run that exits after it ends finished, not orphaned
360
363
  nodii-dev lane subagent-quota KEY N
361
364
  nodii-dev lane heartbeat KEY [--note TEXT]
362
365
  nodii-dev lane resume KEY
@@ -922,8 +925,9 @@ Dev identity.
922
925
  A launched or resumed runtime runs under a detached lane supervisor (an
923
926
  internal `nodii-dev __lane-supervisor` process), which sees its exit code and
924
927
  reports it to Dev as the lane's identity (NODII-DEV-CLI-1). It first reads the
925
- lane's status. A lane whose done check is recorded (its `dev_check` step done,
926
- or the lane `on_dev`, `non_issue` or `stopped`) has finished. On it, exit 0 with a clean worktree ends the run
928
+ lane's status. A lane whose done check is recorded (`nodii-dev lane done-check
929
+ KEY --evidence TEXT|URL`, its `dev_check` step done, or the lane `on_dev`,
930
+ `non_issue` or `stopped`) has finished. On it, exit 0 with a clean worktree ends the run
927
931
  `done`, exit 0 with uncommitted or untracked files ends it `handed_off` and names
928
932
  them, and any other exit ends it `failed`. None of these counts as a death. A
929
933
  process that stops before the done check is recorded is unfinished, whatever
package/dist/cli.js CHANGED
@@ -219,6 +219,7 @@ Usage: nodii-dev <command> [options] [--json]
219
219
  lane step <key> --side SIDE --not-applicable gather --reason-code pre_harness_lane [--note TEXT]
220
220
  # coordinator only: Gather never applied (pre-Gather lane); never "done"
221
221
  lane evidence <key> --pr|--commit|--figma|--deploy|--dev-check REF
222
+ lane done-check <key> --evidence TEXT|URL # the done check is met; a launched run that exits after it ends finished
222
223
  lane subagent-quota <key> <n> # coordinator assigns 0-100 subagent slots
223
224
  lane heartbeat|resume|land|verify|prioritize|deprioritize|stop <key>
224
225
  lane review <key> --file REVIEW.json # base/head, outcome and clause-cited findings
@@ -1049,9 +1050,10 @@ export async function main(argv = process.argv.slice(2)) {
1049
1050
  await recordHookFire(payload).catch(() => {});
1050
1051
  const result = await safetyPreHook(payload);
1051
1052
  const nudges = result.nudges ?? [];
1052
- if (nudges.length && hookRuntime(payload) === "codex")
1053
+ // Both runtimes show the model a PreToolUse nudge only as additionalContext;
1054
+ // Claude keeps plain stdout of an exit-0 hook out of the model's context.
1055
+ if (nudges.length)
1053
1056
  console.log(JSON.stringify({ hookSpecificOutput: { hookEventName: "PreToolUse", additionalContext: nudges.join("\n") } }));
1054
- else for (const nudge of nudges) console.log(nudge);
1055
1057
  return;
1056
1058
  } catch (error) {
1057
1059
  // Only an evaluated safety rule may deny. Discovery, parsing, git,
package/dist/core.js CHANGED
@@ -4,7 +4,7 @@ import { chmod, mkdir, readFile, rm, writeFile } from "node:fs/promises";
4
4
  import { homedir, platform } from "node:os";
5
5
  import { dirname, join } from "node:path";
6
6
 
7
- export const VERSION = "0.6.42";
7
+ export const VERSION = "0.6.44";
8
8
  export const configPath = () =>
9
9
  process.env.NODII_DEV_CONFIG_PATH ??
10
10
  join(homedir(), ".config/nodii-dev/config.json");
@@ -10,8 +10,8 @@ import { saveWatchedRun, watchdogRunDir } from "./watchdog.js";
10
10
  * NODII-DEV-CLI-1: a launched or resumed lane's runtime is the child of this
11
11
  * supervisor, so its exit code is known. The supervisor reports it to Dev
12
12
  * with the worktree's clean/dirty state and the lane's status. A lane whose
13
- * done check is recorded (its dev_check step done, or the lane on_dev,
14
- * non_issue or stopped) has finished: its run
13
+ * done check is recorded (`lane done-check`, NODII-DEV-CLI-4; its dev_check
14
+ * step done; or the lane on_dev, non_issue or stopped) has finished: its run
15
15
  * ends, never as a death. A process that stops before that is unfinished,
16
16
  * whatever its exit code: it is reported as a process exit, which Dev orphans
17
17
  * so `lane resume` relaunches it (hub-58 scope add). Before this, the
@@ -76,14 +76,15 @@ async function send(request, path, body, attempts = 3) {
76
76
  }
77
77
 
78
78
  /**
79
- * The lane's Dev status and whether its done check is recorded: a dev_check
80
- * step done, or a terminal lane. Unreadable reads as unfinished.
79
+ * The lane's Dev status and whether its done check is recorded: a recorded
80
+ * done check, a dev_check step done, or a terminal lane. Unreadable reads as
81
+ * unfinished.
81
82
  */
82
83
  async function laneProgress(request, laneKey) {
83
84
  try {
84
85
  const lane = (await request("GET", `/api/v1/lanes/${encode(laneKey)}`)).data;
85
86
  const devChecked = (lane?.steps ?? []).some((step) => step.stepKey === "dev_check" && step.state === "done");
86
- return { status: lane?.status ?? null, finished: devChecked || FINISHED_LANE.has(lane?.status) };
87
+ return { status: lane?.status ?? null, finished: Boolean(lane?.doneCheckRecorded) || devChecked || FINISHED_LANE.has(lane?.status) };
87
88
  } catch {
88
89
  return { status: null, finished: false };
89
90
  }
package/dist/lanes.js CHANGED
@@ -120,8 +120,10 @@ async function laneContract(id, request, options) {
120
120
  }
121
121
  }
122
122
  // Gather never ends on a bare "finished" (hub-58): the next step is the task.
123
+ // Dev has moved the lane past gather by now (NODII-DEV-CLI-3); recording the
124
+ // done check is what lets a launched run end finished (NODII-DEV-CLI-4).
123
125
  const nextStepText = (ref, doneCheck) =>
124
- `Gather is finished. Next: Task. Work the objective until ${doneCheck} is met, then run \`nodii-dev lane land ${ref}\` (or the lane's finish command). Don't stop before that.`;
126
+ `Gather is finished. Next: Task. Work the objective until ${doneCheck} is met, record it with \`nodii-dev lane done-check ${ref} --evidence TEXT|URL\`, then run \`nodii-dev lane land ${ref}\` (or the lane's finish command). Don't stop before that.`;
125
127
  const agentShortlistText = (ref, data, flags) => [
126
128
  ...contractLines(data),
127
129
  `Trigger shortlist ${data.shortlistId} · ${JSON.stringify(data.objective)}`,
@@ -516,6 +518,11 @@ export async function laneCommand(action, args, flags, { request = devRequest, c
516
518
  const lane = (await request("GET", `/api/v1/lanes/${encode(id)}`)).data;
517
519
  const step = lane.steps.find((x) => x.side === side && (x.id === stepRef || x.stepKey === stepRef));
518
520
  if (!step) throw new CliError("STEP_NOT_FOUND", `No ${side} step ${stepRef} on ${ref}.`, 1);
521
+ // Dev finishes gather and starts the next step itself when Gather's Freeze
522
+ // is written (NODII-DEV-CLI-3), so asking for that again is already true.
523
+ const already = { done: "done", start: "current" }[selected[0]];
524
+ if (already && step.state === already)
525
+ return { payload: { data: step }, human: `${ref}: ${side}/${step.stepKey} is already ${already}.` };
519
526
  // hub-58: Gather is the only step a coordinator may rule not applicable.
520
527
  if (ruling && step.stepKey !== "gather") throw new CliError("USAGE", "--not-applicable covers only the gather step.", 2);
521
528
  // GATHER-NA: coordinator only; Dev refuses a lane identity (COORDINATOR_REQUIRED).
@@ -549,6 +556,17 @@ export async function laneCommand(action, args, flags, { request = devRequest, c
549
556
  const payload = await request("POST", `/api/v1/lanes/${encode(id)}/evidence`, body);
550
557
  return { payload, human: `${ref}: recorded ${body.kind} evidence.` };
551
558
  }
559
+ if (action === "done-check") {
560
+ // NODII-DEV-CLI-4: the agent met the lane's done check. A launched run that
561
+ // exits after this ends finished instead of orphaned (lane-supervisor.js).
562
+ const value = typeof flags.evidence === "string" ? flags.evidence.trim() : "";
563
+ if (!value) throw new CliError("USAGE", "lane done-check needs --evidence TEXT|URL: what shows the lane's done check is met.", 2);
564
+ const body = { kind: "done_check", ...(/^https?:\/\//i.test(value) && URL.canParse(value) ? { url: value } : { ref: value }), ...via };
565
+ const payload = await request("POST", `/api/v1/lanes/${encode(id)}/evidence`, body);
566
+ if (!payload?.data?.doneCheckRecorded)
567
+ throw new CliError("DONE_CHECK_NOT_RECORDED", `Dev took the evidence but does not show ${ref}'s done check as recorded; it predates NODII-DEV-CLI-4. The run would still end orphaned.`, 1);
568
+ return { payload, human: `${ref}: done check recorded. The lane's run ends finished when its process exits.` };
569
+ }
552
570
  if (action === "heartbeat") {
553
571
  return { payload: await request("POST", `/api/v1/lanes/${encode(id)}/heartbeat`, { ...(flags.note !== undefined ? { note: flags.note } : {}), ...via }), human: `${ref}: heartbeat recorded.` };
554
572
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nodii/dev",
3
- "version": "0.6.42",
3
+ "version": "0.6.44",
4
4
  "description": "Agent CLI for Nodii Dev, reached through the platform edge",
5
5
  "type": "module",
6
6
  "bin": {