@nodii/dev 0.6.26 → 0.6.28
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 +42 -5
- package/dist/api.js +1 -0
- package/dist/cli.js +62 -15
- package/dist/core.js +6 -2
- package/dist/devops-openapi.json +1 -1
- package/dist/lanes.js +12 -0
- package/dist/managed-hooks.js +30 -27
- package/dist/nodii-dev.js +13 -1
- package/dist/runs.js +341 -0
- package/dist/upload.js +10 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -135,6 +135,8 @@ nodii-dev libraries register-version LIBRARY_ID --version VERSION
|
|
|
135
135
|
nodii-dev state diff
|
|
136
136
|
nodii-dev heartbeat audit [SERVICE]
|
|
137
137
|
|
|
138
|
+
nodii-dev run [--kind build|test_run|subagent|deploy] [--name TEXT] -- COMMAND [ARGS...]
|
|
139
|
+
|
|
138
140
|
nodii-dev design entries [--kind KIND] [--group GROUP] [--q TEXT]
|
|
139
141
|
nodii-dev design entry KEY
|
|
140
142
|
nodii-dev design guide PAGE_TYPE
|
|
@@ -308,6 +310,7 @@ nodii-dev lane step KEY --side backend|frontend --start|--done|--block STEP [--r
|
|
|
308
310
|
# --reason is kept as a lane note; on a step already blocked,
|
|
309
311
|
# --block --reason records the new reason (Dev issue 125 moves it onto the step)
|
|
310
312
|
nodii-dev lane evidence KEY --pr|--commit|--figma|--deploy|--dev-check REF
|
|
313
|
+
nodii-dev lane subagent-quota KEY N
|
|
311
314
|
nodii-dev lane heartbeat KEY [--note TEXT]
|
|
312
315
|
nodii-dev lane review KEY --file REVIEW.json
|
|
313
316
|
nodii-dev lane reviews KEY
|
|
@@ -345,7 +348,7 @@ nodii-dev rulebook diff CODE [--from V] [--to V]
|
|
|
345
348
|
nodii-dev docs check PATH... # local M-05 checks only
|
|
346
349
|
nodii-dev docs validate PATH... --class CLASS # Dev's dry run of the upload
|
|
347
350
|
nodii-dev docs upload PATH... --class ruling|conformance|editorial
|
|
348
|
-
[--base CODE=VERSION ...] [--decision DECISION.json]
|
|
351
|
+
[--base CODE=VERSION ...] [--decision DECISION.json] [--pending]
|
|
349
352
|
[--conformance-ref PR|REPO@SHA] [--meaning-unchanged REASON]
|
|
350
353
|
[--wait [--wait-minutes N]]
|
|
351
354
|
nodii-dev docs ratify --domain KEY [--domain KEY ...] --decision DECISION.json [--wait]
|
|
@@ -542,9 +545,12 @@ Dev's error codes, and nothing is sent until there are none. Dev then runs the
|
|
|
542
545
|
same checks, plus the ones that need its data, in one transaction.
|
|
543
546
|
|
|
544
547
|
- `--class` is required on every write (M05-8). A `ruling` carries its
|
|
545
|
-
decision (`--decision`, with `amends`)
|
|
546
|
-
|
|
547
|
-
|
|
548
|
+
decision (`--decision`, with `amends`). Agents must add `--pending`: Dev
|
|
549
|
+
stores the ruling versions under a pending decision without activating
|
|
550
|
+
them, and a signed-in staff person locks it later. Omitting `--pending`
|
|
551
|
+
requests the staff-only immediate lock-and-amend action. An `editorial`
|
|
552
|
+
write that changes a Rules clause is refused unless
|
|
553
|
+
`--meaning-unchanged REASON` is given (M05-10).
|
|
548
554
|
- A new document is based on nothing. For a document Dev already has, pass
|
|
549
555
|
the version you started from with `--base CODE=VERSION` (M05-11). The CLI
|
|
550
556
|
never fills it in from Dev, so a stale edit is refused instead of
|
|
@@ -686,6 +692,30 @@ Doctor may refresh the access token, as any command does. The permission table
|
|
|
686
692
|
is kept by hand from Dev's route policy until Dev's OpenAPI names each
|
|
687
693
|
operation's permission (requested in Dev).
|
|
688
694
|
|
|
695
|
+
## Watched commands and Results
|
|
696
|
+
|
|
697
|
+
The harness launcher supplies `NODII_DEV_RUN_ID`, the UUID of the current lane,
|
|
698
|
+
coordinator, or subagent run. Use `nodii-dev run -- COMMAND ARGS...` for work
|
|
699
|
+
whose completion must survive the calling agent. The command registers a child
|
|
700
|
+
run with that UUID as its waiter, starts an out-of-process supervisor, and
|
|
701
|
+
preserves the command's exit code. `--kind` defaults to `build`; `--name`
|
|
702
|
+
overrides the executable name shown in Dev without storing command arguments.
|
|
703
|
+
|
|
704
|
+
The supervisor heartbeats once a minute while the command runs. At completion
|
|
705
|
+
it records the executable, exit code, signal when applicable, and the final 8
|
|
706
|
+
KiB of combined stdout/stderr. Credential-shaped values, bearer tokens, private
|
|
707
|
+
keys, and credentials embedded in URLs are redacted before the tail leaves the
|
|
708
|
+
machine. The command is executed directly, never through a shell. Because the
|
|
709
|
+
supervisor is detached from the launching process, the terminal record still
|
|
710
|
+
lands when that process is killed.
|
|
711
|
+
|
|
712
|
+
Every subsequent `nodii-dev` call in the waiter context prints unacknowledged
|
|
713
|
+
Results before doing its requested work. A Result is acknowledged only after
|
|
714
|
+
it renders successfully. Hook calls deliberately leave Results pending because
|
|
715
|
+
their output may be swallowed; the next visible call prints and acknowledges
|
|
716
|
+
them. Unacknowledged amendments will join this preflight when the amendment API
|
|
717
|
+
from AGENT-HARNESS-14 is available.
|
|
718
|
+
|
|
689
719
|
## Build lanes and repository setup
|
|
690
720
|
|
|
691
721
|
`lane start` creates and starts a lane. It infers the repository (by short
|
|
@@ -697,6 +727,12 @@ serves for that step (see Rule delivery). Server refusals,
|
|
|
697
727
|
including operator-only actions, workflow blocks, lane limits and Built items
|
|
698
728
|
that are not on dev, retain Dev's error code/message and exit non-zero.
|
|
699
729
|
|
|
730
|
+
The machine coordinator sets a lane's subagent capacity with `lane
|
|
731
|
+
subagent-quota KEY N` (`0` to `100`). Registering a subagent run consumes one
|
|
732
|
+
slot; Dev refuses another registration with `SUBAGENT_QUOTA_REACHED` when the
|
|
733
|
+
lane is full. A `done`, `handed_off`, or `failed` subagent run no longer counts,
|
|
734
|
+
so the watchdog marking a killed process failed frees the slot.
|
|
735
|
+
|
|
700
736
|
`setup` installs the Claude Code and Codex workflow harnesses in the current repository.
|
|
701
737
|
It writes `.claude/skills/nodii-build-workflow/SKILL.md`, merges SessionStart,
|
|
702
738
|
Stop and tool hooks into `.claude/settings.json` and `.codex/hooks.json`, and maintains one
|
|
@@ -778,7 +814,8 @@ runtime, people, agents, inbox/gates, design, runbooks,
|
|
|
778
814
|
doctrine and exceptions, changelog, health verdicts, approval kinds,
|
|
779
815
|
notification registry, skills, questions, decisions, drift, requests,
|
|
780
816
|
pipelines, runners, routing, usage, audit, reconciliation, cron, composite
|
|
781
|
-
surfaces, service edges, lanes, swaps, ideas, terminology,
|
|
817
|
+
surfaces, service edges, lanes, swaps, ideas, terminology, watched runs,
|
|
818
|
+
releases, operate surfaces, and the AD era: `rulebook`
|
|
782
819
|
(documents, plus `rulebook versions CODE`), `ad-decisions`, `domains`, `standardizations`,
|
|
783
820
|
`archive` (`archive resolve --ref pre:D1366`), and `harness methodology`. `api list` is the authoritative discovery view;
|
|
784
821
|
`api OPERATION_ID` makes every operation callable even without an alias.
|
package/dist/api.js
CHANGED
|
@@ -103,6 +103,7 @@ export const GROUPS = {
|
|
|
103
103
|
swaps: "Swaps", ideas: "Ideas", terminology: "Terminology", credentials: "Credentials",
|
|
104
104
|
rulebook: "ad-documents", "ad-decisions": "ad-decisions", domains: "ad-domains", standardizations: "standardizations",
|
|
105
105
|
archive: "archive", harness: "harness", utils: "utils",
|
|
106
|
+
runs: "Runs", releases: "Releases", operate: "Operate",
|
|
106
107
|
// DEC-0020 rule delivery; `rules core|report|cap` are first-class (upload.js).
|
|
107
108
|
rules: "rulebook",
|
|
108
109
|
};
|
package/dist/cli.js
CHANGED
|
@@ -74,6 +74,11 @@ import {
|
|
|
74
74
|
nextCommand,
|
|
75
75
|
peopleCommand,
|
|
76
76
|
} from "./lanes.js";
|
|
77
|
+
import {
|
|
78
|
+
launchRunSupervisor,
|
|
79
|
+
showWaitingResults,
|
|
80
|
+
superviseRun,
|
|
81
|
+
} from "./runs.js";
|
|
77
82
|
|
|
78
83
|
const HELP = `nodii-dev ${VERSION}
|
|
79
84
|
|
|
@@ -128,7 +133,7 @@ Usage: nodii-dev <command> [options] [--json]
|
|
|
128
133
|
harness check # 0 current, 1 differs from Dev, 2 not installed
|
|
129
134
|
harness update --check # the same check
|
|
130
135
|
docs check|validate|upload <path...> --class ruling|conformance|editorial
|
|
131
|
-
[--base CODE=VERSION] [--decision FILE] [--conformance-ref REF]
|
|
136
|
+
[--base CODE=VERSION] [--decision FILE] [--pending] [--conformance-ref REF]
|
|
132
137
|
[--meaning-unchanged REASON] [--wait]
|
|
133
138
|
docs ratify --domain KEY [--domain KEY ...] --decision FILE [--wait]
|
|
134
139
|
docs parked [CODE] [--status pending|acknowledged]
|
|
@@ -137,10 +142,15 @@ Usage: nodii-dev <command> [options] [--json]
|
|
|
137
142
|
rules report [--section S] [--lane KEY] # delivery totals per document; rules served per lane
|
|
138
143
|
rules cap TOKENS # operator: set the core token cap (default 3000)
|
|
139
144
|
|
|
145
|
+
run [--kind build|test_run|subagent|deploy] [--name TEXT] -- <command> [args...]
|
|
146
|
+
# watched command: registers with NODII_DEV_RUN_ID,
|
|
147
|
+
# heartbeats, and records exit code + redacted log tail
|
|
148
|
+
|
|
140
149
|
lane start <key> --title TEXT --kind KIND --side SIDE [--repo REPO] [--issue ISSUE]
|
|
141
150
|
lane step <key> --side SIDE --start|--done|--block <step> [--reason TEXT]
|
|
142
151
|
# --block --reason on a blocked step records its new reason
|
|
143
152
|
lane evidence <key> --pr|--commit|--figma|--deploy|--dev-check REF
|
|
153
|
+
lane subagent-quota <key> <n> # coordinator assigns 0-100 subagent slots
|
|
144
154
|
lane heartbeat|land|verify|prioritize|deprioritize|stop <key>
|
|
145
155
|
lane review <key> --file REVIEW.json # base/head, outcome and clause-cited findings
|
|
146
156
|
lane reviews <key> # stored review history for the lane
|
|
@@ -164,7 +174,8 @@ contracts, sagas, outbox-topics, tables, libraries, workers, projects, issues,
|
|
|
164
174
|
backlogs, builds, changes, deploy-reports, runtime, people, agents, inbox,
|
|
165
175
|
gate, design, runbooks, doctrine, changelog, health-verdicts, approval-kinds,
|
|
166
176
|
notification-registry, skills, open-questions, decisions, drift, requests,
|
|
167
|
-
rulebook, ad-decisions, domains, standardizations, archive, harness,
|
|
177
|
+
rulebook, ad-decisions, domains, standardizations, archive, harness, utils,
|
|
178
|
+
runs, releases, and operate.
|
|
168
179
|
|
|
169
180
|
Run a command with --help or see the package README for all flags.`;
|
|
170
181
|
|
|
@@ -401,10 +412,51 @@ export async function figmaFileAdd(value, flags, { request = devRequest } = {})
|
|
|
401
412
|
}
|
|
402
413
|
|
|
403
414
|
export async function main(argv = process.argv.slice(2)) {
|
|
415
|
+
if (argv[0] !== "__run-supervisor")
|
|
416
|
+
await showWaitingResults({ acknowledge: argv[0] !== "hook" });
|
|
404
417
|
if (argv.length === 1 && ["--version", "-v"].includes(argv[0]))
|
|
405
418
|
return console.log(VERSION);
|
|
406
419
|
if (argv.length === 1 && ["--help", "-h"].includes(argv[0]))
|
|
407
420
|
return console.log(HELP);
|
|
421
|
+
if (["run", "__run-supervisor"].includes(argv[0])) {
|
|
422
|
+
const boundary = argv.indexOf("--");
|
|
423
|
+
if (boundary === -1 || boundary === argv.length - 1)
|
|
424
|
+
throw new CliError("USAGE", "run needs a command after `--`.", 2);
|
|
425
|
+
const { args: optionArgs, flags: runFlags } = parseArgs(
|
|
426
|
+
argv.slice(1, boundary),
|
|
427
|
+
);
|
|
428
|
+
if (optionArgs.length)
|
|
429
|
+
throw new CliError(
|
|
430
|
+
"USAGE",
|
|
431
|
+
`Unexpected run argument before \`--\`: ${optionArgs.join(" ")}`,
|
|
432
|
+
2,
|
|
433
|
+
);
|
|
434
|
+
const kind = runFlags.kind;
|
|
435
|
+
const name = runFlags.name;
|
|
436
|
+
if (Array.isArray(kind) || kind === true)
|
|
437
|
+
throw new CliError("USAGE", "run --kind needs one value.", 2);
|
|
438
|
+
if (Array.isArray(name) || name === true)
|
|
439
|
+
throw new CliError("USAGE", "run --name needs one value.", 2);
|
|
440
|
+
const command = argv.slice(boundary + 1);
|
|
441
|
+
if (argv[0] === "__run-supervisor") {
|
|
442
|
+
const ignoreBrokenPipe = (error) => {
|
|
443
|
+
if (error?.code !== "EPIPE") throw error;
|
|
444
|
+
};
|
|
445
|
+
stdout.on("error", ignoreBrokenPipe);
|
|
446
|
+
process.stderr.on("error", ignoreBrokenPipe);
|
|
447
|
+
try {
|
|
448
|
+
const result = await superviseRun({ command, kind, name });
|
|
449
|
+
process.exitCode = result.exitCode;
|
|
450
|
+
return;
|
|
451
|
+
} finally {
|
|
452
|
+
stdout.off("error", ignoreBrokenPipe);
|
|
453
|
+
process.stderr.off("error", ignoreBrokenPipe);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
const exitCode = await launchRunSupervisor(command, { kind, name });
|
|
457
|
+
process.exitCode = exitCode;
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
408
460
|
const { args, flags: f } = parseArgs(argv);
|
|
409
461
|
const json = Boolean(f.json);
|
|
410
462
|
const [command, action, ...rest] = args;
|
|
@@ -766,10 +818,6 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
766
818
|
return;
|
|
767
819
|
}
|
|
768
820
|
if (["claude-figma-pre", "figma-pre"].includes(action)) {
|
|
769
|
-
// Fail closed: Claude Code only blocks a tool call on exit code 2, and
|
|
770
|
-
// treats any other non-zero exit as a non-blocking error that lets the
|
|
771
|
-
// write through. Every failure here (bad stdin, missing config, Dev
|
|
772
|
-
// error, lock denied) must therefore exit 2.
|
|
773
821
|
let payload;
|
|
774
822
|
try {
|
|
775
823
|
payload = JSON.parse(await stdinText());
|
|
@@ -778,6 +826,9 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
778
826
|
if (hookRuntime(payload) !== "codex") return emit(result, json, "allowed");
|
|
779
827
|
return;
|
|
780
828
|
} catch (error) {
|
|
829
|
+
// A known policy denial still blocks. Hook/runtime failures fail open
|
|
830
|
+
// and stay silent so a broken environment cannot stop the session.
|
|
831
|
+
if (!["FIGMA_FILE_KEY_MISSING", "FIGMA_LOCK_DENIED"].includes(error?.code)) return;
|
|
781
832
|
if (payload && hookRuntime(payload) === "codex") {
|
|
782
833
|
console.log(JSON.stringify({ hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "deny", permissionDecisionReason: `Blocked Figma write: ${error?.message ?? String(error)}` } }));
|
|
783
834
|
return;
|
|
@@ -791,7 +842,6 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
791
842
|
}
|
|
792
843
|
}
|
|
793
844
|
if (action === "safety-pre") {
|
|
794
|
-
// A malformed or unavailable safety check must not turn into permission.
|
|
795
845
|
let payload;
|
|
796
846
|
try {
|
|
797
847
|
payload = JSON.parse(await stdinText());
|
|
@@ -803,6 +853,9 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
803
853
|
else for (const nudge of nudges) console.log(nudge);
|
|
804
854
|
return;
|
|
805
855
|
} catch (error) {
|
|
856
|
+
// Only an evaluated safety rule may deny. Discovery, parsing, git,
|
|
857
|
+
// config and Dev errors fail open and produce no output.
|
|
858
|
+
if (!String(error?.code ?? "").startsWith("SAFETY_")) return;
|
|
806
859
|
if (payload && hookRuntime(payload) === "codex") {
|
|
807
860
|
console.log(JSON.stringify({ hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "deny", permissionDecisionReason: error?.message ?? String(error) } }));
|
|
808
861
|
return;
|
|
@@ -821,14 +874,7 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
821
874
|
payload = JSON.parse(await stdinText());
|
|
822
875
|
await recordHookFire(payload).catch(() => {});
|
|
823
876
|
await safetyPostHook(payload);
|
|
824
|
-
} catch
|
|
825
|
-
// A post-hook cannot undo a completed command; report state-tracking
|
|
826
|
-
// failure without making Claude treat the completed command as failed.
|
|
827
|
-
const message = `nodii-dev safety post hook: ${error?.code ?? "ERROR"}: ${error?.message ?? String(error)}`;
|
|
828
|
-
if (payload && hookRuntime(payload) === "codex")
|
|
829
|
-
console.log(JSON.stringify({ hookSpecificOutput: { hookEventName: "PostToolUse", additionalContext: message } }));
|
|
830
|
-
else console.error(message);
|
|
831
|
-
}
|
|
877
|
+
} catch {}
|
|
832
878
|
return;
|
|
833
879
|
}
|
|
834
880
|
if (action === "lane-heartbeat") {
|
|
@@ -854,6 +900,7 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
854
900
|
return emit(await postHook(payload), json, "logged");
|
|
855
901
|
} catch (error) {
|
|
856
902
|
const outcome = postHookFailure(error);
|
|
903
|
+
if (!outcome.exitCode) return;
|
|
857
904
|
if (hookRuntime(payload) === "codex")
|
|
858
905
|
console.log(JSON.stringify({ hookSpecificOutput: { hookEventName: "PostToolUse", additionalContext: outcome.message } }));
|
|
859
906
|
else {
|
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.28";
|
|
8
8
|
export const configPath = () =>
|
|
9
9
|
process.env.NODII_DEV_CONFIG_PATH ??
|
|
10
10
|
join(homedir(), ".config/nodii-dev/config.json");
|
|
@@ -199,12 +199,16 @@ export async function checkLogin() {
|
|
|
199
199
|
export const encode = (value) => encodeURIComponent(value);
|
|
200
200
|
// Switches never take the next token as their value, so
|
|
201
201
|
// `docs upload --wait rulebook/` keeps rulebook/ as a path.
|
|
202
|
-
const SWITCHES = new Set(["json", "wait", "dry-run", "check", "all", "fleet", "help", "yes", "mine", "no-browser", "include-drafts", "drafted", "library", "async", "sync", "force", "system"]);
|
|
202
|
+
const SWITCHES = new Set(["json", "wait", "pending", "dry-run", "check", "all", "fleet", "help", "yes", "mine", "no-browser", "include-drafts", "drafted", "library", "async", "sync", "force", "system"]);
|
|
203
203
|
export function parseArgs(argv) {
|
|
204
204
|
const flags = {};
|
|
205
205
|
const args = [];
|
|
206
206
|
for (let i = 0; i < argv.length; i++) {
|
|
207
207
|
const token = argv[i];
|
|
208
|
+
if (token === "--") {
|
|
209
|
+
args.push(...argv.slice(i + 1));
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
208
212
|
if (!token.startsWith("--")) {
|
|
209
213
|
args.push(token);
|
|
210
214
|
continue;
|