@nodii/dev 0.6.5 → 0.6.6

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
@@ -164,6 +164,8 @@ nodii-dev lane list [--kind KIND] [--status STATUS] [--machine UUID]
164
164
  nodii-dev lane show KEY
165
165
  nodii-dev lane prioritize|deprioritize KEY --reason TEXT
166
166
  nodii-dev lane stop KEY --reason TEXT
167
+ nodii-dev lane non-issue KEY --reason TEXT --evidence TEXT --reroute KEY[,KEY]|none
168
+ --branch carried:ISSUE|dropped[:REASON] [--commits SHA[,SHA]]
167
169
  nodii-dev next KEY
168
170
 
169
171
  nodii-dev built list [--cursor CURSOR] [--include-acknowledged true]
@@ -343,6 +345,38 @@ checks. Setup also checks the OAuth login (a token refresh, then the registered
343
345
  agent) and prints `nodii-dev login` guidance without exposing credential
344
346
  details.
345
347
 
348
+ `lane non-issue` ends a lane at its second terminal status, `non_issue`, when
349
+ its investigation proves the issue is not a defect in its repository (DEC-0011,
350
+ MW1-28.1). The lane's own agent or the machine's coordinator runs it; no staff
351
+ person is needed. It needs:
352
+
353
+ - `--reason`: why this is not a defect here;
354
+ - `--evidence`: what was checked, with `file:line` or data;
355
+ - `--reroute`: the issue keys the real cause was re-routed to, comma-separated,
356
+ or `none` only when the evidence shows no defect anywhere;
357
+ - `--branch`: what happens to the lane's branch work. `carried:ISSUE` carries it
358
+ into that open follow-up issue with its commits; `dropped:REASON` drops it
359
+ with a reason. A bare `dropped` is accepted only when the branch has no
360
+ commits beyond `origin/main`, and records that as the reason. Dev records the
361
+ outcome with the lane's branch.
362
+ - `--commits`: the commits (at most 200). Carried work needs at least one.
363
+ Without the flag the CLI reads the lane's branch commits that are not on
364
+ `origin/main` (as last fetched) from the lane's recorded worktree; it refuses
365
+ with `GIT_REQUIRED` when it cannot read them, and with `USAGE` when carried
366
+ work has none (drop it instead).
367
+
368
+ The CLI refuses with `USAGE` (exit 2) before sending anything when one of these
369
+ is missing. Dev then makes the lane `non_issue`, which frees its slot like
370
+ `on_dev`, and moves each linked open issue to `wont_do` with the reason. An
371
+ issue that another active lane still works on stays open. Dev refuses a lane
372
+ that is already terminal (409 `LANE_TERMINAL`), a caller that is neither the
373
+ lane's agent nor its coordinator (403 `LANE_ACTOR_REQUIRED`), a live lease held
374
+ by someone else (409 `CLAIM_HELD_BY_OTHER`), and a re-route or carried key that
375
+ names no issue, the lane's own issue, or (for carried) a closed one (422).
376
+ The command reaches Dev through the edge, so it needs the edge's route for
377
+ `/devops/api/v1/lanes/:id/non-issue`; until that is on an environment, the
378
+ command gets the edge's 404 there.
379
+
346
380
  Heartbeat notes are optional and limited by Dev to 500 characters.
347
381
  Prioritize and deprioritize require a 1–500 character reason, which the CLI
348
382
  sends to Dev. `lane review` records the review gate.
package/dist/cli.js CHANGED
@@ -84,6 +84,9 @@ Usage: nodii-dev <command> [options] [--json]
84
84
  lane step <key> --side SIDE --start|--done|--block <step>
85
85
  lane evidence <key> --pr|--commit|--figma|--deploy|--dev-check REF
86
86
  lane heartbeat|review|land|verify|prioritize|deprioritize|stop <key>
87
+ lane non-issue <key> --reason TEXT --evidence TEXT --reroute KEY[,KEY]|none
88
+ --branch carried:KEY|dropped[:REASON] [--commits SHA[,SHA]]
89
+ # DEC-0011: terminal non_issue; issue -> wont_do; slot frees
87
90
  lane list [--kind KIND] [--status STATUS] [--mine] [--cursor CURSOR]
88
91
  lane show <key> | next <key>
89
92
  built list | built ack <key...>
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.5";
7
+ export const VERSION = "0.6.6";
8
8
  export const configPath = () =>
9
9
  process.env.NODII_DEV_CONFIG_PATH ??
10
10
  join(homedir(), ".config/nodii-dev/config.json");
package/dist/lanes.js CHANGED
@@ -48,7 +48,55 @@ function evidence(flags) {
48
48
  return { kind: flag === "figma" ? "figma_frame" : flag.replace("-", "_"), ...(URL.canParse(value) ? { url: value } : { ref: value }), ...via };
49
49
  }
50
50
 
51
- export async function laneCommand(action, args, flags, { request = devRequest, config = readConfig, gitContext = inferGitContext } = {}) {
51
+ const list = (value) => String(value).split(",").map((x) => x.trim()).filter(Boolean);
52
+
53
+ /**
54
+ * DEC-0011 (MW1-28.1): the body for `lane non-issue`. A re-route needs at
55
+ * least one issue key or an explicit `none`, and the branch work is either
56
+ * carried into a named issue with its commits or dropped with a reason, never
57
+ * left unsaid. Without --commits, the commits are the lane's branch commits
58
+ * that are not on origin/main, read from the lane's recorded worktree.
59
+ */
60
+ export async function nonIssueBody(flags, laneCommits) {
61
+ const reason = requireFlag(flags, "reason");
62
+ const evidence = requireFlag(flags, "evidence");
63
+ const rerouteFlag = requireFlag(flags, "reroute");
64
+ const reroute = rerouteFlag === "none" ? "none" : list(rerouteFlag);
65
+ if (reroute !== "none" && !reroute.length) throw new CliError("USAGE", "--reroute needs at least one issue key, or none when the evidence shows no defect anywhere.", 2);
66
+ const branchFlag = flags.branch;
67
+ const usage = "--branch must say what happens to the lane's branch work: carried:ISSUE_KEY or dropped[:REASON].";
68
+ if (typeof branchFlag !== "string" || !branchFlag) throw new CliError("USAGE", usage, 2);
69
+ const at = branchFlag.indexOf(":");
70
+ const disposition = at === -1 ? branchFlag : branchFlag.slice(0, at);
71
+ const detail = at === -1 ? "" : branchFlag.slice(at + 1).trim();
72
+ if (!(disposition === "carried" && detail) && disposition !== "dropped") throw new CliError("USAGE", usage, 2);
73
+ const given = flags.commits !== undefined ? list(requireFlag(flags, "commits")) : undefined;
74
+ // dropped:REASON states its own reason, so it needs no commit lookup.
75
+ const commits = given ?? (disposition === "dropped" && detail ? undefined : await laneCommits());
76
+ if (commits && commits.length > MAX_COMMITS) throw new CliError("USAGE", `That is ${commits.length} commits; Dev records at most ${MAX_COMMITS}. Pass --commits with the ones that matter, or --branch dropped:REASON.`, 2);
77
+ let branch;
78
+ if (disposition === "carried") {
79
+ if (!commits.length) throw new CliError("USAGE", "Carried work needs its commits: the lane's branch has none beyond origin/main. Pass --commits SHA[,SHA], or --branch dropped:REASON.", 2);
80
+ branch = { disposition, issueKey: detail, commits };
81
+ } else if (detail) branch = { disposition, reason: detail, ...(commits ? { commits } : {}) };
82
+ else if (!commits.length) branch = { disposition, reason: "no commits beyond origin/main" };
83
+ else throw new CliError("USAGE", `The lane's branch has ${commits.length} commits beyond origin/main. Say why they are dropped: --branch dropped:REASON.`, 2);
84
+ return { reason, evidence, reroute, branch, ...via };
85
+ }
86
+
87
+ const MAX_COMMITS = 200;
88
+
89
+ /** The lane's branch commits not on origin/main (as last fetched), from its recorded worktree. */
90
+ export async function branchCommits(lane) {
91
+ try {
92
+ const out = (await execFileAsync("git", ["-C", lane.worktree, "rev-list", `origin/main..${lane.branch}`], { encoding: "utf8" })).stdout.trim();
93
+ return out ? out.split("\n") : [];
94
+ } catch {
95
+ throw new CliError("GIT_REQUIRED", `Cannot read the commits of ${lane.branch} in ${lane.worktree}. Pass --commits SHA[,SHA], or --branch dropped:REASON.`, 2);
96
+ }
97
+ }
98
+
99
+ export async function laneCommand(action, args, flags, { request = devRequest, config = readConfig, gitContext = inferGitContext, commitsOf = branchCommits } = {}) {
52
100
  if (action === "list") {
53
101
  const c = flags.mine ? await config() : null;
54
102
  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 })}`);
@@ -108,6 +156,14 @@ export async function laneCommand(action, args, flags, { request = devRequest, c
108
156
  return { payload, human: `${ref}: ${action}d.` };
109
157
  }
110
158
  if (action === "stop") return { payload: await request("POST", `/api/v1/lanes/${encode(id)}/stop`, { reason: requireFlag(flags, "reason"), ...via }), human: `${ref}: stopped.` };
159
+ if (action === "non-issue") {
160
+ const body = await nonIssueBody(flags, async () => commitsOf((await request("GET", `/api/v1/lanes/${encode(id)}`)).data));
161
+ const payload = await request("POST", `/api/v1/lanes/${encode(id)}/non-issue`, body);
162
+ const { closedIssues = [], openIssues = [] } = payload?.data?.nonIssue ?? {};
163
+ const closed = closedIssues.length ? ` ${closedIssues.join(", ")} moved to wont_do.` : "";
164
+ const open = openIssues.length ? ` ${openIssues.join(", ")} left open: another active lane works on it.` : "";
165
+ return { payload, human: `${ref}: non_issue; slot freed.${closed}${open}` };
166
+ }
111
167
  throw new CliError("USAGE", `Unknown lane action: ${action}`, 2);
112
168
  }
113
169
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nodii/dev",
3
- "version": "0.6.5",
3
+ "version": "0.6.6",
4
4
  "description": "Agent CLI for Nodii Dev, reached through the platform edge",
5
5
  "type": "module",
6
6
  "bin": {