@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 +34 -0
- package/dist/cli.js +3 -0
- package/dist/core.js +1 -1
- package/dist/lanes.js +57 -1
- package/package.json +1 -1
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.
|
|
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
|
-
|
|
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
|
|