@nodii/dev 0.5.0 → 0.6.1
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 +83 -1
- package/dist/api.js +10 -2
- package/dist/cli.js +24 -2
- package/dist/core.js +24 -6
- package/dist/devops-openapi.json +1 -1
- package/dist/docs.js +204 -0
- package/dist/harness.js +343 -0
- package/dist/upload.js +291 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -36,6 +36,11 @@ refuses those flags. The first command you run after upgrading deletes the old
|
|
|
36
36
|
config and its stored secret (Keychain item or file) without printing it, then
|
|
37
37
|
asks you to run `nodii-dev login`.
|
|
38
38
|
|
|
39
|
+
`nodii-dev` is only the Dev control CLI. The local-stack CLI is `./nodii-stack`,
|
|
40
|
+
run from the `nodii-local-stack` checkout (renamed per operator ruling G3-6,
|
|
41
|
+
with no alias under the old name). Typing one of its verbs here, such as
|
|
42
|
+
`nodii-dev doctor`, fails and names the `./nodii-stack` command instead.
|
|
43
|
+
|
|
39
44
|
Every command accepts `--json`; without it, output is formatted for humans.
|
|
40
45
|
Dev failures print Dev's error code to stderr and exit non-zero.
|
|
41
46
|
|
|
@@ -137,8 +142,83 @@ nodii-dev people refresh-names
|
|
|
137
142
|
nodii-dev setup # preview files that would change
|
|
138
143
|
nodii-dev setup --yes # merge and write the harness
|
|
139
144
|
nodii-dev setup --check # exit non-zero when it has drifted
|
|
145
|
+
|
|
146
|
+
nodii-dev rulebook list [--section rulebook|services] [--kind KIND] [--q TEXT]
|
|
147
|
+
nodii-dev rulebook show CODE [--version V] # the current text, from Dev
|
|
148
|
+
nodii-dev rulebook diff CODE [--from V] [--to V]
|
|
149
|
+
|
|
150
|
+
nodii-dev docs check PATH... # local M-05 checks only
|
|
151
|
+
nodii-dev docs validate PATH... --class CLASS # Dev's dry run of the upload
|
|
152
|
+
nodii-dev docs upload PATH... --class ruling|conformance|editorial
|
|
153
|
+
[--base CODE=VERSION ...] [--decision DECISION.json]
|
|
154
|
+
[--conformance-ref PR|REPO@SHA] [--meaning-unchanged REASON]
|
|
155
|
+
[--wait [--wait-minutes N]]
|
|
156
|
+
nodii-dev docs ratify --domain KEY [--domain KEY ...] --decision DECISION.json [--wait]
|
|
157
|
+
nodii-dev docs parked [CODE] [--status pending|acknowledged]
|
|
158
|
+
|
|
159
|
+
nodii-dev harness install [--include-drafts] [--adopt PATH,...] [--dry-run]
|
|
160
|
+
nodii-dev harness update [--include-drafts] [--adopt PATH,...] [--dry-run]
|
|
161
|
+
nodii-dev harness update --check # exit 1 when Dev would write anything different
|
|
140
162
|
```
|
|
141
163
|
|
|
164
|
+
## AD documents
|
|
165
|
+
|
|
166
|
+
AD documents (the rulebook and per-service feature docs) live in Dev, one
|
|
167
|
+
current text per document with its version history (M-05). `docs upload`
|
|
168
|
+
takes files or folders (folders are searched for `.md` files, skipping `_`
|
|
169
|
+
and `.` entries) and checks every document locally before sending anything:
|
|
170
|
+
front matter, the section order for its kind, unique clause ids that use the
|
|
171
|
+
document's code, no bare decision ids (`D123`; cite `pre:D123`), and the
|
|
172
|
+
parked (`Undecided`) items, which it lists. All problems are printed with
|
|
173
|
+
Dev's error codes, and nothing is sent until there are none. Dev then runs the
|
|
174
|
+
same checks, plus the ones that need its data, in one transaction.
|
|
175
|
+
|
|
176
|
+
- `--class` is required on every write (M05-8). A `ruling` carries its
|
|
177
|
+
decision (`--decision`, with `amends`), and the decision locks with the new
|
|
178
|
+
versions or nothing is written. An `editorial` write that changes a Rules
|
|
179
|
+
clause is refused unless `--meaning-unchanged REASON` is given (M05-10).
|
|
180
|
+
- A new document is based on nothing. For a document Dev already has, pass
|
|
181
|
+
the version you started from with `--base CODE=VERSION` (M05-11). The CLI
|
|
182
|
+
never fills it in from Dev, so a stale edit is refused instead of
|
|
183
|
+
overwriting newer text.
|
|
184
|
+
- **Parked items:** when a document has parked items the operator has not
|
|
185
|
+
acknowledged, Dev refuses the upload and records the list. The CLI prints
|
|
186
|
+
it and exits `4`. The operator acknowledges it in Dev (this CLI is an OAuth
|
|
187
|
+
app and cannot). Re-run the upload then, or pass `--wait` to poll and retry
|
|
188
|
+
once it is acknowledged.
|
|
189
|
+
- `docs ratify` is the bulk ratification mode: one decision locks v1.0 of
|
|
190
|
+
every draft document in the named domains, which then become AD (M05-2).
|
|
191
|
+
Upload the drafts first (`--class conformance`), then ratify domain by
|
|
192
|
+
domain. The decision file holds `title`, `context`, `options`, `ruling`,
|
|
193
|
+
`consequences` and `provenance`; Dev mints the `DEC-nnnn` id and generates
|
|
194
|
+
the amends. A domain that is already AD can be named again to ratify drafts
|
|
195
|
+
added to it since (a new feature doc in a ratified service).
|
|
196
|
+
- `docs validate` also prints Dev's warnings for each document (they never
|
|
197
|
+
block) and any problems with the decision's amends.
|
|
198
|
+
|
|
199
|
+
Like every other command, these reach Dev only through the platform edge
|
|
200
|
+
(`/devops/api/v1/documents`, `/domains`, `/harness`, …) with the OAuth login.
|
|
201
|
+
|
|
202
|
+
## Agent harness
|
|
203
|
+
|
|
204
|
+
`harness install` writes the repository's agent harness as Dev generates it
|
|
205
|
+
from the ratified methodology: the `CLAUDE.md` and `AGENTS.md` pointer
|
|
206
|
+
blocks, one pointer skill per methodology document, the Claude Code hooks
|
|
207
|
+
merged into `.claude/settings.json`, and Dev's `.nodii/harness.json`. Every
|
|
208
|
+
generated file carries the methodology version it came from. Nothing about
|
|
209
|
+
the rules is authored locally. Run it in the service's own repository: it
|
|
210
|
+
installs one service at a time, and there is no fleet mode. `--include-drafts`
|
|
211
|
+
uses draft methodology while none is ratified.
|
|
212
|
+
|
|
213
|
+
The CLI records what it wrote in `.nodii/harness-state.json` and never touches
|
|
214
|
+
anything else. An existing file it did not write is refused, unless the
|
|
215
|
+
operator names it with `--adopt PATH` (a `CLAUDE.md` then keeps its text and
|
|
216
|
+
gains the managed block; `settings.json` keeps its other keys and hooks).
|
|
217
|
+
`harness update` re-syncs: it prints the methodology version change, which
|
|
218
|
+
source documents moved, and each file created, updated or removed. It
|
|
219
|
+
removes only files, blocks and hook entries it wrote, and it refuses (writing
|
|
220
|
+
nothing) when one of its files was edited by hand.
|
|
221
|
+
|
|
142
222
|
## Build lanes and repository setup
|
|
143
223
|
|
|
144
224
|
`lane start` creates and starts a lane. It infers the repository, branch and
|
|
@@ -190,7 +270,9 @@ runtime, people, agents, inbox/gates, design, runbooks,
|
|
|
190
270
|
doctrine and exceptions, changelog, health verdicts, approval kinds,
|
|
191
271
|
notification registry, skills, questions, decisions, drift, requests,
|
|
192
272
|
pipelines, runners, routing, usage, audit, reconciliation, cron, composite
|
|
193
|
-
surfaces, service edges, lanes, swaps, ideas,
|
|
273
|
+
surfaces, service edges, lanes, swaps, ideas, terminology, and the AD era: `rulebook`
|
|
274
|
+
(documents, plus `rulebook versions CODE`), `ad-decisions`, `domains`, `standardizations`,
|
|
275
|
+
`archive` (`archive resolve --ref pre:D1366`), and `harness methodology`. `api list` is the authoritative discovery view;
|
|
194
276
|
`api OPERATION_ID` makes every operation callable even without an alias.
|
|
195
277
|
|
|
196
278
|
`figma check` exits `0` when allowed and `3` when denied. The pre-hook permits
|
package/dist/api.js
CHANGED
|
@@ -95,12 +95,14 @@ export const GROUPS = {
|
|
|
95
95
|
sagas: "Sagas", "outbox-topics": ["Outbox", "OutboxTopics"], tables: "Tables", libraries: "Libraries", workers: "Workers",
|
|
96
96
|
projects: "Projects", issues: "Issues", backlog: "Backlogs", backlogs: "Backlogs", builds: "Delivery", changes: "Delivery",
|
|
97
97
|
runtime: "Runtime", people: "People", agents: "Agents", inbox: "Inbox", gate: "Inbox", design: "Design", runbooks: "Runbooks",
|
|
98
|
-
doctrine: ["Doctrine", "DoctrineExceptions"],
|
|
98
|
+
doctrine: ["Doctrine", "DoctrineExceptions"], changelog: "Changelog", "health-verdicts": "HealthVerdicts",
|
|
99
99
|
"approval-kinds": "ApprovalKinds", "notification-registry": "Notification registry", skills: "Skills", "open-questions": "OpenQuestions",
|
|
100
100
|
decisions: "Decisions", drift: "Drift", requests: "Requests", pipelines: "Pipelines", slices: "Slices", "sql-lookups": "SqlLookups",
|
|
101
101
|
runners: "Runners", routing: "Routing", usage: "Usage", audit: "AuditLog", reconciliation: "Reconciliation", cron: "Cron", health: "Health",
|
|
102
102
|
composite: "Composite", "service-edges": "ServiceEdges", lanes: "Lanes",
|
|
103
103
|
swaps: "Swaps", ideas: "Ideas", terminology: "Terminology",
|
|
104
|
+
rulebook: "ad-documents", "ad-decisions": "ad-decisions", domains: "ad-domains", standardizations: "standardizations",
|
|
105
|
+
archive: "archive", harness: "harness",
|
|
104
106
|
};
|
|
105
107
|
|
|
106
108
|
export const ALIASES = {
|
|
@@ -129,7 +131,7 @@ export const ALIASES = {
|
|
|
129
131
|
"gate:fleet": "getFleetGate", "gate:service": "getServiceGate", "gate:dry-run": "getGateDryRun",
|
|
130
132
|
"runbooks:list": "listRunbooks", "runbooks:show": "getRunbook", "runbooks:create": "registerRunbook", "runbooks:update": "updateRunbook",
|
|
131
133
|
"doctrine:list": "listDoctrine", "doctrine:show": "getDoctrine", "doctrine:create": "createDoctrine", "doctrine:revise": "reviseDoctrine",
|
|
132
|
-
"
|
|
134
|
+
"changelog:list": "listChangelog", "changelog:append": "appendChangelog",
|
|
133
135
|
"health-verdicts:list": "listHealthVerdicts", "health-verdicts:post": "postHealthVerdict",
|
|
134
136
|
"approval-kinds:list": "listApprovalKinds", "approval-kinds:show": "getApprovalKind", "approval-kinds:create": "createApprovalKind",
|
|
135
137
|
"notification-registry:list": "listNotificationEvents", "notification-registry:show": "getNotificationEvent", "notification-registry:propose": "proposeNotificationEvent",
|
|
@@ -143,6 +145,12 @@ export const ALIASES = {
|
|
|
143
145
|
"service-edges:list": "listServiceEdges", "service-edges:show": "getServiceEdgesForService",
|
|
144
146
|
"swaps:list": "listSwaps", "swaps:show": "getSwap", "swaps:create": "createSwap", "swaps:close": "closeSwap",
|
|
145
147
|
"ideas:list": "listIdeas", "ideas:show": "getIdea", "ideas:create": "createIdea", "ideas:status": "updateIdeaStatus",
|
|
148
|
+
"rulebook:versions": "listAdDocumentVersions", "rulebook:parked": "getAdDocumentParked",
|
|
149
|
+
"ad-decisions:list": "listAdDecisions", "ad-decisions:show": "getAdDecision", "ad-decisions:lock": "lockAndAmendAdDecision",
|
|
150
|
+
"domains:list": "listAdDomains", "domains:show": "getAdDomain",
|
|
151
|
+
"standardizations:list": "listStandardizations", "standardizations:show": "getStandardization", "standardizations:update": "updateStandardization",
|
|
152
|
+
"archive:search": "searchPreDevArchive", "archive:resolve": "resolvePreCitation",
|
|
153
|
+
"harness:show": "generateHarness", "harness:methodology": "getHarnessMethodologyVersion",
|
|
146
154
|
"terminology:list": "listTerminology", "terminology:show": "getTerminologyTerm", "terminology:create": "createTerminologyTerm", "terminology:update": "updateTerminologyTerm",
|
|
147
155
|
};
|
|
148
156
|
|
package/dist/cli.js
CHANGED
|
@@ -24,6 +24,8 @@ import {
|
|
|
24
24
|
} from "./oauth.js";
|
|
25
25
|
import { heartbeatCurrentLane, postHook, preHook, settingsSnippet, workflowSuggestion } from "./hooks.js";
|
|
26
26
|
import { setupCommand } from "./setup.js";
|
|
27
|
+
import { harnessCommand } from "./harness.js";
|
|
28
|
+
import { docsCommand, rulebookCommand } from "./upload.js";
|
|
27
29
|
import {
|
|
28
30
|
ALIASES,
|
|
29
31
|
GROUPS,
|
|
@@ -59,6 +61,14 @@ Usage: nodii-dev <command> [options] [--json]
|
|
|
59
61
|
figma lock|check|log-write|submit|apply|published|abandon|release
|
|
60
62
|
hook claude-figma-pre|claude-figma-post|print-settings
|
|
61
63
|
setup [--yes|--check] # dry-run by default
|
|
64
|
+
harness install|update [--include-drafts[=false]] [--adopt PATH,...] [--dry-run]
|
|
65
|
+
harness update --check # exit 1 when Dev would write anything different
|
|
66
|
+
docs check|validate|upload <path...> --class ruling|conformance|editorial
|
|
67
|
+
[--base CODE=VERSION] [--decision FILE] [--conformance-ref REF]
|
|
68
|
+
[--meaning-unchanged REASON] [--wait]
|
|
69
|
+
docs ratify --domain KEY [--domain KEY ...] --decision FILE [--wait]
|
|
70
|
+
docs parked [CODE] [--status pending|acknowledged]
|
|
71
|
+
rulebook list|show CODE [--version V]|diff CODE
|
|
62
72
|
|
|
63
73
|
lane start <key> --title TEXT --kind KIND --side SIDE [--repo REPO] [--issue ISSUE]
|
|
64
74
|
lane step <key> --side SIDE --start|--done|--block <step>
|
|
@@ -79,10 +89,15 @@ Resource commands cover every OpenAPI tag, including services, feature-docs,
|
|
|
79
89
|
contracts, sagas, outbox-topics, tables, libraries, workers, projects, issues,
|
|
80
90
|
backlogs, builds, changes, deploy-reports, runtime, people, agents, inbox,
|
|
81
91
|
gate, design, runbooks, doctrine, changelog, health-verdicts, approval-kinds,
|
|
82
|
-
notification-registry, skills, open-questions, decisions, drift,
|
|
92
|
+
notification-registry, skills, open-questions, decisions, drift, requests,
|
|
93
|
+
rulebook, ad-decisions, domains, standardizations, archive, and harness.
|
|
83
94
|
|
|
84
95
|
Run a command with --help or see the package README for all flags.`;
|
|
85
96
|
|
|
97
|
+
// Verbs of the local-stack CLI, which shared this name until it became
|
|
98
|
+
// ./nodii-stack (operator G3-6, no alias). Named here only to point there.
|
|
99
|
+
const STACK_VERBS = new Set(["up", "down", "doctor", "sync", "seed", "wipe", "reset", "rebuild", "restart", "smoke", "urls", "peek", "trace", "stats", "bootstrap", "provenance", "stale", "status", "logs", "world", "totp", "token", "bench"]);
|
|
100
|
+
|
|
86
101
|
async function stdinText() {
|
|
87
102
|
let value = "";
|
|
88
103
|
for await (const chunk of stdin) value += chunk;
|
|
@@ -248,6 +263,13 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
248
263
|
}
|
|
249
264
|
const emitCommand = async (result) => emit(result.payload, json, result.human);
|
|
250
265
|
if (command === "setup") return emitCommand(await setupCommand(f));
|
|
266
|
+
if (command === "harness" && ["install", "update"].includes(action)) {
|
|
267
|
+
// `--include-drafts` is a switch: `--include-drafts false` would leave `false` here and turn drafts on.
|
|
268
|
+
if (rest.length) throw new CliError("USAGE", `harness ${action} takes no arguments, got: ${rest.join(" ")}. Write a flag value as --include-drafts=false.`, 2);
|
|
269
|
+
return emitCommand(await harnessCommand(action, f));
|
|
270
|
+
}
|
|
271
|
+
if (command === "docs") return emitCommand(await docsCommand(action, rest, f));
|
|
272
|
+
if (command === "rulebook" && ["list", "show", "diff"].includes(action)) return emitCommand(await rulebookCommand(action, rest, f));
|
|
251
273
|
if (command === "lane") return emitCommand(await laneCommand(action, rest, f));
|
|
252
274
|
if (command === "next") return emitCommand(await nextCommand(action, f));
|
|
253
275
|
if (command === "built") return emitCommand(await builtCommand(action, rest, f));
|
|
@@ -568,7 +590,7 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
568
590
|
}
|
|
569
591
|
throw new CliError(
|
|
570
592
|
"USAGE",
|
|
571
|
-
`Unknown command: ${[command, action].filter(Boolean).join(" ")}`,
|
|
593
|
+
`Unknown command: ${[command, action].filter(Boolean).join(" ")}${STACK_VERBS.has(command) ? `. The local-stack CLI is \`./nodii-stack ${command}\`, run from the nodii-local-stack checkout.` : ""}`,
|
|
572
594
|
2,
|
|
573
595
|
);
|
|
574
596
|
}
|
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.
|
|
7
|
+
export const VERSION = "0.6.1";
|
|
8
8
|
export const configPath = () =>
|
|
9
9
|
process.env.NODII_DEV_CONFIG_PATH ??
|
|
10
10
|
join(homedir(), ".config/nodii-dev/config.json");
|
|
@@ -105,10 +105,10 @@ export function requestUrl(config, path) {
|
|
|
105
105
|
return `${config.edgeUrl.replace(/\/$/, "")}/devops${path}`;
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
async function devFetch(method, path, bodyText, contentType, accept) {
|
|
109
109
|
const config = await readConfig();
|
|
110
110
|
const auth = await authProvider();
|
|
111
|
-
const headers = { accept
|
|
111
|
+
const headers = { accept };
|
|
112
112
|
if (bodyText !== undefined) headers["content-type"] = contentType;
|
|
113
113
|
Object.assign(headers, await auth.headers({ method, path, body: bodyText ?? "" }));
|
|
114
114
|
const url = requestUrl(config, path);
|
|
@@ -118,7 +118,11 @@ export async function devRawRequest(method, path, bodyText, contentType = "appli
|
|
|
118
118
|
} catch (error) {
|
|
119
119
|
throw new CliError("DEV_UNREACHABLE", `Cannot reach Dev at ${new URL(url).origin}: ${error.message}`);
|
|
120
120
|
}
|
|
121
|
-
|
|
121
|
+
return { response, raw: await response.text() };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export async function devRawRequest(method, path, bodyText, contentType = "application/json") {
|
|
125
|
+
const { response, raw } = await devFetch(method, path, bodyText, contentType, "application/json");
|
|
122
126
|
let payload;
|
|
123
127
|
try {
|
|
124
128
|
payload = raw ? JSON.parse(raw) : {};
|
|
@@ -135,6 +139,15 @@ export async function devRawRequest(method, path, bodyText, contentType = "appli
|
|
|
135
139
|
return payload;
|
|
136
140
|
}
|
|
137
141
|
|
|
142
|
+
/** A Dev route that answers with text (e.g. a document body as text/markdown). Errors are Dev's JSON. */
|
|
143
|
+
export async function devTextRequest(method, path) {
|
|
144
|
+
const { response, raw } = await devFetch(method, path, undefined, undefined, "text/markdown, text/plain, application/json");
|
|
145
|
+
if (response.ok) return raw;
|
|
146
|
+
let payload = {};
|
|
147
|
+
try { payload = JSON.parse(raw); } catch { payload = { message: raw }; }
|
|
148
|
+
throw new CliError(payload.code ?? `HTTP_${response.status}`, payload.message ?? `Dev returned HTTP ${response.status}`, 1, payload.details);
|
|
149
|
+
}
|
|
150
|
+
|
|
138
151
|
export const devRequest = (method, path, body) =>
|
|
139
152
|
devRawRequest(method, path, body === undefined ? undefined : JSON.stringify(body));
|
|
140
153
|
|
|
@@ -147,6 +160,9 @@ export async function checkLogin() {
|
|
|
147
160
|
}
|
|
148
161
|
|
|
149
162
|
export const encode = (value) => encodeURIComponent(value);
|
|
163
|
+
// Switches never take the next token as their value, so
|
|
164
|
+
// `docs upload --wait rulebook/` keeps rulebook/ as a path.
|
|
165
|
+
const SWITCHES = new Set(["json", "wait", "dry-run", "check", "all", "fleet", "help", "yes", "mine", "no-browser", "include-drafts"]);
|
|
150
166
|
export function parseArgs(argv) {
|
|
151
167
|
const flags = {};
|
|
152
168
|
const args = [];
|
|
@@ -156,11 +172,13 @@ export function parseArgs(argv) {
|
|
|
156
172
|
args.push(token);
|
|
157
173
|
continue;
|
|
158
174
|
}
|
|
159
|
-
const
|
|
175
|
+
const at = token.indexOf("=");
|
|
176
|
+
const key = at === -1 ? token.slice(2) : token.slice(2, at);
|
|
177
|
+
const inline = at === -1 ? undefined : token.slice(at + 1);
|
|
160
178
|
const value =
|
|
161
179
|
inline !== undefined
|
|
162
180
|
? inline
|
|
163
|
-
: argv[i + 1] && !argv[i + 1].startsWith("--")
|
|
181
|
+
: !SWITCHES.has(key) && argv[i + 1] && !argv[i + 1].startsWith("--")
|
|
164
182
|
? argv[++i]
|
|
165
183
|
: true;
|
|
166
184
|
flags[key] =
|